From d61e3d7aa6ac03a8c67469935fd1af167ca877a8 Mon Sep 17 00:00:00 2001 From: Egor Aristov Date: Sun, 16 Nov 2025 11:55:04 +0300 Subject: [PATCH] finally support blobs (go->ts as arg and return) --- example/ts/src/index.ts | 7 +++---- example/ts/src/remote.ts | 6 ++++-- kitcom/internal/api/api.go | 17 +++++++++++++++++ kitcom/internal/ts/tsgen.go | 2 +- kitcom/internal/ts/tsgen.tmpl | 30 ++++++++++++++++++------------ lib/golang/lib.go | 25 ++++++++++++++++++++----- lib/ts/src/lib.ts | 4 ++-- 7 files changed, 65 insertions(+), 26 deletions(-) diff --git a/example/ts/src/index.ts b/example/ts/src/index.ts index 8603660..c8f93cb 100644 --- a/example/ts/src/index.ts +++ b/example/ts/src/index.ts @@ -22,10 +22,9 @@ async function main() { console.log(`call result ts->go Div = ${await remoteApi.Div(10, 2)}`); - // todo check empty array - const data1 = new Uint8Array(100).fill(0b10101010, 0, 100); - const data2 = new Uint8Array(100).fill(0b11110000, 0, 100); - console.log(`call result ts->go XorData = ${await remoteApi.XorData(data1, data2)}`); + const data1 = Buffer.alloc(10, 0b10101010); + const data2 = Buffer.alloc(10, 0b11110000); + console.log(`call result ts->go XorData = ${(await remoteApi.XorData(data1, data2)).toString('hex')}`); await ipc.wait(); } diff --git a/example/ts/src/remote.ts b/example/ts/src/remote.ts index f00d9e0..11cbc8e 100644 --- a/example/ts/src/remote.ts +++ b/example/ts/src/remote.ts @@ -1,6 +1,7 @@ // Code generated by kitcom. DO NOT EDIT. import { ParentIPC, ChildIPC } from "kitten-ipc"; + export default class GoIpcApi { protected ipc: ParentIPC | ChildIPC; @@ -13,8 +14,9 @@ export default class GoIpcApi { return results[0] as number; } - async XorData(data1: Uint8Array, data2: Uint8Array): Promise { + async XorData(data1: Buffer, data2: Buffer): Promise { const results = await this.ipc.call("GoIpcApi.XorData", data1, data2); - return results[0] as Uint8Array; + results[0] = Buffer.from(results[0], "base64"); + return results[0] as Buffer; } } diff --git a/kitcom/internal/api/api.go b/kitcom/internal/api/api.go index 8d27360..e299700 100644 --- a/kitcom/internal/api/api.go +++ b/kitcom/internal/api/api.go @@ -13,6 +13,23 @@ const ( TArray ValType = 5 ) +func (v ValType) String() string { + switch v { + case TInt: + return "int" + case TString: + return "string" + case TBool: + return "bool" + case TBlob: + return "blob" + case TArray: + return "array" + default: + panic("unreachable code") + } +} + type Val struct { Name string Type ValType diff --git a/kitcom/internal/ts/tsgen.go b/kitcom/internal/ts/tsgen.go index 5b1d763..92d903a 100644 --- a/kitcom/internal/ts/tsgen.go +++ b/kitcom/internal/ts/tsgen.go @@ -35,7 +35,7 @@ func (g *TypescriptApiGenerator) Generate(apis *api.Api, destFile string) error api.TInt: "number", api.TString: "string", api.TBool: "boolean", - api.TBlob: "Uint8Array", + api.TBlob: "Buffer", }[t] if !ok { return "", fmt.Errorf("cannot generate type %v", t) diff --git a/kitcom/internal/ts/tsgen.tmpl b/kitcom/internal/ts/tsgen.tmpl index 5432731..61abca7 100644 --- a/kitcom/internal/ts/tsgen.tmpl +++ b/kitcom/internal/ts/tsgen.tmpl @@ -1,25 +1,31 @@ +{{- /*gotype: efprojects.com/kitten-ipc/kitcom/internal/ts.tsGenData*/ -}} + // Code generated by kitcom. DO NOT EDIT. import {ParentIPC, ChildIPC} from 'kitten-ipc'; -{{- /*gotype: efprojects.com/kitten-ipc/kitcom/ts.tsGenData*/ -}} -{{range $e := .Api.Endpoints}} -export default class {{$e.Name}} { +{{ range $e := .Api.Endpoints }} +export default class {{ $e.Name }} { protected ipc: ParentIPC | ChildIPC; constructor(ipc: ParentIPC | ChildIPC) { this.ipc = ipc; } -{{range $mtd := $e.Methods}} - async {{ $mtd.Name }}( - {{ range $par := $mtd.Params }}{{$par.Name}}: {{$par.Type | typedef }}, {{end}} - ): Promise<{{if len $mtd.Ret}}{{(index $mtd.Ret 0).Type | typedef }}{{else}}void{{end}}> { - const results = await this.ipc.call('{{$e.Name}}.{{$mtd.Name}}', - {{range $par := $mtd.Params}}{{$par.Name}}, {{end}} +{{ range $mtd := $e.Methods }} + async {{ $mtd.Name }}( + {{ range $par := $mtd.Params }}{{ $par.Name }}: {{ $par.Type | typedef }}, {{ end }} + ): Promise<{{ if len $mtd.Ret }}{{ (index $mtd.Ret 0).Type | typedef }}{{ else }}void{{ end }}> { + const results = await this.ipc.call('{{ $e.Name }}.{{ $mtd.Name }}', + {{ range $par := $mtd.Params }}{{ $par.Name }}, {{ end }} ); - return {{range $i, $ret := $mtd.Ret}}{{if $i}}, {{end}}results[{{$i}}] as {{$ret.Type | typedef}}{{end}} + + {{- if eq (index $mtd.Ret 0).Type.String "blob" -}} + results[0] = Buffer.from(results[0], 'base64'); + {{- end -}} + + return {{ range $i, $ret := $mtd.Ret }}{{ if $i }}, {{ end }}results[{{ $i }}] as {{ $ret.Type | typedef }}{{ end }} } -{{end}} +{{ end }} } -{{end}} +{{ end }} diff --git a/lib/golang/lib.go b/lib/golang/lib.go index abb1ae9..791f15d 100644 --- a/lib/golang/lib.go +++ b/lib/golang/lib.go @@ -2,6 +2,7 @@ package golang import ( "bufio" + "encoding/base64" "encoding/json" "errors" "flag" @@ -154,11 +155,25 @@ func (ipc *ipcCommon) handleCall(msg Message) { } func (ipc *ipcCommon) convType(needType reflect.Type, gotType reflect.Type, arg any) any { - // JSON decodes any number to float64. If we need int, we should check and convert - if needType.Kind() == reflect.Int && gotType.Kind() == reflect.Float64 { - floatArg := arg.(float64) - if float64(int64(floatArg)) == floatArg && !needType.OverflowInt(int64(floatArg)) { - arg = int(floatArg) + switch needType.Kind() { + case reflect.Int: + // JSON decodes any number to float64. If we need int, we should check and convert + if gotType.Kind() == reflect.Float64 { + floatArg := arg.(float64) + if float64(int64(floatArg)) == floatArg && !needType.OverflowInt(int64(floatArg)) { + arg = int(floatArg) + } + } + case reflect.Slice: + switch needType.Elem().Kind() { + case reflect.Uint8: + if gotType.Kind() == reflect.String { + var err error + arg, err = base64.StdEncoding.DecodeString(arg.(string)) + if err != nil { + panic(fmt.Sprintf("decode base64: %s", err)) + } + } } } return arg diff --git a/lib/ts/src/lib.ts b/lib/ts/src/lib.ts index e6d53b1..907ec30 100644 --- a/lib/ts/src/lib.ts +++ b/lib/ts/src/lib.ts @@ -200,8 +200,8 @@ abstract class IPCCommon { return arg; // @ts-expect-error TS7029 case 'object': - if(arg instanceof Uint8Array) { - return Buffer.from(arg).toString('base64'); + if(arg instanceof Buffer) { + return arg.toString('base64'); } default: throw new Error(`arg type ${typeof arg} is not supported`);