diff --git a/cmd/generate/main-generate.go b/cmd/generate/main-generate.go
index d7548ac8..a0944250 100644
--- a/cmd/generate/main-generate.go
+++ b/cmd/generate/main-generate.go
@@ -14,14 +14,13 @@ import (
"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
)
-func generateTypesFile() error {
+func generateTypesFile(tsTypesMap map[reflect.Type]string) error {
fd, err := os.Create("frontend/types/gotypes.d.ts")
if err != nil {
return err
}
defer fd.Close()
fmt.Fprintf(os.Stderr, "generating types file to %s\n", fd.Name())
- tsTypesMap := make(map[reflect.Type]string)
tsgen.GenerateWaveObjTypes(tsTypesMap)
err = tsgen.GenerateServiceTypes(tsTypesMap)
if err != nil {
@@ -37,8 +36,8 @@ func generateTypesFile() error {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
- iname, _ := tsgen.TypeToTSType(keys[i])
- jname, _ := tsgen.TypeToTSType(keys[j])
+ iname, _ := tsgen.TypeToTSType(keys[i], tsTypesMap)
+ jname, _ := tsgen.TypeToTSType(keys[j], tsTypesMap)
return iname < jname
})
for _, key := range keys {
@@ -51,7 +50,7 @@ func generateTypesFile() error {
return nil
}
-func generateServicesFile() error {
+func generateServicesFile(tsTypesMap map[reflect.Type]string) error {
fd, err := os.Create("frontend/app/store/services.ts")
if err != nil {
return err
@@ -65,7 +64,7 @@ func generateServicesFile() error {
orderedKeys := utilfn.GetOrderedMapKeys(service.ServiceMap)
for _, serviceName := range orderedKeys {
serviceObj := service.ServiceMap[serviceName]
- svcStr := tsgen.GenerateServiceClass(serviceName, serviceObj)
+ svcStr := tsgen.GenerateServiceClass(serviceName, serviceObj, tsTypesMap)
fmt.Fprint(fd, svcStr)
fmt.Fprint(fd, "\n")
}
@@ -78,12 +77,13 @@ func main() {
fmt.Fprintf(os.Stderr, "Error validating service map: %v\n", err)
os.Exit(1)
}
- err = generateTypesFile()
+ tsTypesMap := make(map[reflect.Type]string)
+ err = generateTypesFile(tsTypesMap)
if err != nil {
fmt.Fprintf(os.Stderr, "Error generating types file: %v\n", err)
os.Exit(1)
}
- err = generateServicesFile()
+ err = generateServicesFile(tsTypesMap)
if err != nil {
fmt.Fprintf(os.Stderr, "Error generating services file: %v\n", err)
os.Exit(1)
diff --git a/frontend/app/block/block.tsx b/frontend/app/block/block.tsx
index 6dc99bb1..12d154ae 100644
--- a/frontend/app/block/block.tsx
+++ b/frontend/app/block/block.tsx
@@ -49,7 +49,7 @@ const Block = ({ blockId, onClose }: BlockProps) => {
} else if (blockData.view === "plot") {
blockElem = ;
} else if (blockData.view === "codeedit") {
- blockElem = ;
+ blockElem = ;
}
return (
diff --git a/frontend/app/store/services.ts b/frontend/app/store/services.ts
index d3a14152..132a306b 100644
--- a/frontend/app/store/services.ts
+++ b/frontend/app/store/services.ts
@@ -8,7 +8,7 @@ import * as WOS from "./wos";
// blockservice.BlockService (block)
class BlockServiceType {
// send command to block
- SendCommand(blockid: string, command: MetaType): Promise {
+ SendCommand(blockid: string, cmd: BlockCommand): Promise {
return WOS.callBackendService("block", "SendCommand", Array.from(arguments))
}
}
diff --git a/frontend/app/view/term.tsx b/frontend/app/view/term.tsx
index 347bea65..2e5a743c 100644
--- a/frontend/app/view/term.tsx
+++ b/frontend/app/view/term.tsx
@@ -49,7 +49,10 @@ function handleResize(fitAddon: FitAddon, blockId: string, term: Terminal) {
const oldCols = term.cols;
fitAddon.fit();
if (oldRows !== term.rows || oldCols !== term.cols) {
- const resizeCommand = { command: "controller:input", termsize: { rows: term.rows, cols: term.cols } };
+ const resizeCommand: BlockInputCommand = {
+ command: "controller:input",
+ termsize: { rows: term.rows, cols: term.cols },
+ };
services.BlockService.SendCommand(blockId, resizeCommand);
}
}
@@ -78,17 +81,13 @@ const TerminalView = ({ blockId }: { blockId: string }) => {
newTerm.loadAddon(newFitAddon);
newTerm.open(connectElemRef.current);
newFitAddon.fit();
- // BlockService.SendCommand(blockId, {
- // command: "controller:input",
- // termsize: { rows: newTerm.rows, cols: newTerm.cols },
- // });
services.BlockService.SendCommand(blockId, {
command: "controller:input",
termsize: { rows: newTerm.rows, cols: newTerm.cols },
});
newTerm.onData((data) => {
const b64data = btoa(data);
- const inputCmd = { command: "controller:input", blockid: blockId, inputdata64: b64data };
+ const inputCmd: BlockInputCommand = { command: "controller:input", inputdata64: b64data };
services.BlockService.SendCommand(blockId, inputCmd);
});
diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts
index e505a95d..4e28ee4f 100644
--- a/frontend/types/gotypes.d.ts
+++ b/frontend/types/gotypes.d.ts
@@ -14,6 +14,10 @@ declare global {
meta: MetaType;
};
+ type BlockCommand = {
+ command: string;
+ } & ( BlockMessageCommand | BlockInputCommand | BlockSetViewCommand | BlockSetMetaCommand );
+
// wstore.BlockDef
type BlockDef = {
controller?: string;
@@ -22,6 +26,32 @@ declare global {
meta?: MetaType;
};
+ // blockcontroller.BlockInputCommand
+ type BlockInputCommand = {
+ command: "controller:input";
+ inputdata64?: string;
+ signame?: string;
+ termsize?: TermSize;
+ };
+
+ // blockcontroller.BlockMessageCommand
+ type BlockMessageCommand = {
+ command: "message";
+ message: string;
+ };
+
+ // blockcontroller.BlockSetMetaCommand
+ type BlockSetMetaCommand = {
+ command: "setmeta";
+ meta: MetaType;
+ };
+
+ // blockcontroller.BlockSetViewCommand
+ type BlockSetViewCommand = {
+ command: "setview";
+ view: string;
+ };
+
// wstore.Client
type Client = WaveObj & {
mainwindowid: string;
@@ -62,7 +92,7 @@ declare global {
type MetaType = {[key: string]: any}
- // servicemeta.MethodMeta
+ // tsgenmeta.MethodMeta
type MethodMeta = {
Desc: string;
ArgNames: string[];
diff --git a/pkg/blockcontroller/blockcommand.go b/pkg/blockcontroller/blockcommand.go
index fb0a65fe..6b662809 100644
--- a/pkg/blockcontroller/blockcommand.go
+++ b/pkg/blockcontroller/blockcommand.go
@@ -9,6 +9,7 @@ import (
"reflect"
"github.com/wavetermdev/thenextwave/pkg/shellexec"
+ "github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
)
const CommandKey = "command"
@@ -21,16 +22,33 @@ const (
)
var CommandToTypeMap = map[string]reflect.Type{
- BlockCommand_Message: reflect.TypeOf(MessageCommand{}),
- BlockCommand_Input: reflect.TypeOf(InputCommand{}),
- BlockCommand_SetView: reflect.TypeOf(SetViewCommand{}),
- BlockCommand_SetMeta: reflect.TypeOf(SetMetaCommand{}),
+ BlockCommand_Message: reflect.TypeOf(BlockMessageCommand{}),
+ BlockCommand_Input: reflect.TypeOf(BlockInputCommand{}),
+ BlockCommand_SetView: reflect.TypeOf(BlockSetViewCommand{}),
+ BlockCommand_SetMeta: reflect.TypeOf(BlockSetMetaCommand{}),
+}
+
+func CommandTypeUnionMeta() tsgenmeta.TypeUnionMeta {
+ return tsgenmeta.TypeUnionMeta{
+ BaseType: reflect.TypeOf((*BlockCommand)(nil)).Elem(),
+ TypeFieldName: "command",
+ Types: []reflect.Type{
+ reflect.TypeOf(BlockMessageCommand{}),
+ reflect.TypeOf(BlockInputCommand{}),
+ reflect.TypeOf(BlockSetViewCommand{}),
+ reflect.TypeOf(BlockSetMetaCommand{}),
+ },
+ }
}
type BlockCommand interface {
GetCommand() string
}
+type BlockCommandWrapper struct {
+ BlockCommand
+}
+
func ParseCmdMap(cmdMap map[string]any) (BlockCommand, error) {
cmdType, ok := cmdMap[CommandKey].(string)
if !ok {
@@ -52,40 +70,40 @@ func ParseCmdMap(cmdMap map[string]any) (BlockCommand, error) {
return cmd.(BlockCommand), nil
}
-type MessageCommand struct {
- Command string `json:"command"`
+type BlockMessageCommand struct {
+ Command string `json:"command" tstype:"\"message\""`
Message string `json:"message"`
}
-func (mc *MessageCommand) GetCommand() string {
+func (mc *BlockMessageCommand) GetCommand() string {
return BlockCommand_Message
}
-type InputCommand struct {
- Command string `json:"command"`
- InputData64 string `json:"inputdata64"`
+type BlockInputCommand struct {
+ Command string `json:"command" tstype:"\"controller:input\""`
+ InputData64 string `json:"inputdata64,omitempty"`
SigName string `json:"signame,omitempty"`
TermSize *shellexec.TermSize `json:"termsize,omitempty"`
}
-func (ic *InputCommand) GetCommand() string {
+func (ic *BlockInputCommand) GetCommand() string {
return BlockCommand_Input
}
-type SetViewCommand struct {
- Command string `json:"command"`
+type BlockSetViewCommand struct {
+ Command string `json:"command" tstype:"\"setview\""`
View string `json:"view"`
}
-func (svc *SetViewCommand) GetCommand() string {
+func (svc *BlockSetViewCommand) GetCommand() string {
return BlockCommand_SetView
}
-type SetMetaCommand struct {
- Command string `json:"command"`
+type BlockSetMetaCommand struct {
+ Command string `json:"command" tstype:"\"setmeta\""`
Meta map[string]any `json:"meta"`
}
-func (smc *SetMetaCommand) GetCommand() string {
+func (smc *BlockSetMetaCommand) GetCommand() string {
return BlockCommand_SetMeta
}
diff --git a/pkg/blockcontroller/blockcontroller.go b/pkg/blockcontroller/blockcontroller.go
index e9fafb26..007190bd 100644
--- a/pkg/blockcontroller/blockcontroller.go
+++ b/pkg/blockcontroller/blockcontroller.go
@@ -40,7 +40,7 @@ type BlockController struct {
Status string
ShellProc *shellexec.ShellProc
- ShellInputCh chan *InputCommand
+ ShellInputCh chan *BlockInputCommand
}
func (bc *BlockController) WithLock(f func()) {
@@ -149,7 +149,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts) error {
bc.ShellProc.Close()
return err
}
- shellInputCh := make(chan *InputCommand)
+ shellInputCh := make(chan *BlockInputCommand)
bc.ShellInputCh = shellInputCh
go func() {
defer func() {
@@ -234,7 +234,7 @@ func (bc *BlockController) Run(bdata *wstore.Block) {
for genCmd := range bc.InputCh {
switch cmd := genCmd.(type) {
- case *InputCommand:
+ case *BlockInputCommand:
fmt.Printf("INPUT: %s | %q\n", bc.BlockId, cmd.InputData64)
if bc.ShellInputCh != nil {
bc.ShellInputCh <- cmd
@@ -293,10 +293,10 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) error {
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancelFn()
switch cmd := cmdGen.(type) {
- case *MessageCommand:
+ case *BlockMessageCommand:
log.Printf("MESSAGE: %s | %q\n", blockId, cmd.Message)
return nil
- case *SetViewCommand:
+ case *BlockSetViewCommand:
log.Printf("SETVIEW: %s | %q\n", blockId, cmd.View)
block, err := wstore.DBGet[*wstore.Block](ctx, blockId)
if err != nil {
@@ -308,7 +308,7 @@ func ProcessStaticCommand(blockId string, cmdGen BlockCommand) error {
return fmt.Errorf("error updating block: %w", err)
}
return nil
- case *SetMetaCommand:
+ case *BlockSetMetaCommand:
log.Printf("SETMETA: %s | %v\n", blockId, cmd.Meta)
block, err := wstore.DBGet[*wstore.Block](ctx, blockId)
if err != nil {
diff --git a/pkg/service/blockservice/blockservice.go b/pkg/service/blockservice/blockservice.go
index b9b14548..76e91ccc 100644
--- a/pkg/service/blockservice/blockservice.go
+++ b/pkg/service/blockservice/blockservice.go
@@ -9,25 +9,21 @@ import (
"time"
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
- "github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
+ "github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
)
type BlockService struct{}
const DefaultTimeout = 2 * time.Second
-func (bs *BlockService) SendCommand_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (bs *BlockService) SendCommand_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
Desc: "send command to block",
- ArgNames: []string{"blockid", "command"},
+ ArgNames: []string{"blockid", "cmd"},
}
}
-func (bs *BlockService) SendCommand(blockId string, cmdMap map[string]any) error {
- cmd, err := blockcontroller.ParseCmdMap(cmdMap)
- if err != nil {
- return fmt.Errorf("error parsing command map: %w", err)
- }
+func (bs *BlockService) SendCommand(blockId string, cmd blockcontroller.BlockCommand) error {
if strings.HasPrefix(cmd.GetCommand(), "controller:") {
bc := blockcontroller.GetBlockController(blockId)
if bc == nil {
diff --git a/pkg/service/objectservice/objectservice.go b/pkg/service/objectservice/objectservice.go
index 8d5e0d25..a65db3c7 100644
--- a/pkg/service/objectservice/objectservice.go
+++ b/pkg/service/objectservice/objectservice.go
@@ -12,7 +12,7 @@ import (
"time"
"github.com/wavetermdev/thenextwave/pkg/blockcontroller"
- "github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
+ "github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
@@ -29,8 +29,8 @@ func parseORef(oref string) (*waveobj.ORef, error) {
return &waveobj.ORef{OType: fields[0], OID: fields[1]}, nil
}
-func (svc *ObjectService) GetObject_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) GetObject_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
Desc: "get wave object by oref",
ArgNames: []string{"oref"},
}
@@ -50,8 +50,8 @@ func (svc *ObjectService) GetObject(orefStr string) (waveobj.WaveObj, error) {
return obj, nil
}
-func (svc *ObjectService) GetObjects_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) GetObjects_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"orefs"},
ReturnDesc: "objects",
}
@@ -92,8 +92,8 @@ func updatesRtn(ctx context.Context, rtnVal map[string]any) (any, error) {
return rtnVal, nil
}
-func (svc *ObjectService) AddTabToWorkspace_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) AddTabToWorkspace_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "tabName", "activateTab"},
ReturnDesc: "tabId",
}
@@ -120,8 +120,8 @@ func (svc *ObjectService) AddTabToWorkspace(uiContext wstore.UIContext, tabName
return tab.OID, wstore.ContextGetUpdatesRtn(ctx), nil
}
-func (svc *ObjectService) SetActiveTab_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) SetActiveTab_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "tabId"},
}
}
@@ -158,8 +158,8 @@ func (svc *ObjectService) SetActiveTab(uiContext wstore.UIContext, tabId string)
return updates, nil
}
-func (svc *ObjectService) CreateBlock_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) CreateBlock_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "blockDef", "rtOpts"},
ReturnDesc: "blockId",
}
@@ -185,8 +185,8 @@ func (svc *ObjectService) CreateBlock(uiContext wstore.UIContext, blockDef *wsto
return blockData.OID, wstore.ContextGetUpdatesRtn(ctx), nil
}
-func (svc *ObjectService) DeleteBlock_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) DeleteBlock_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "blockId"},
}
}
@@ -203,8 +203,8 @@ func (svc *ObjectService) DeleteBlock(uiContext wstore.UIContext, blockId string
return wstore.ContextGetUpdatesRtn(ctx), nil
}
-func (svc *ObjectService) CloseTab_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) CloseTab_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "tabId"},
}
}
@@ -244,8 +244,8 @@ func (svc *ObjectService) CloseTab(uiContext wstore.UIContext, tabId string) (ws
return wstore.ContextGetUpdatesRtn(ctx), nil
}
-func (svc *ObjectService) UpdateObjectMeta_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) UpdateObjectMeta_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "oref", "meta"},
}
}
@@ -265,8 +265,8 @@ func (svc *ObjectService) UpdateObjectMeta(uiContext wstore.UIContext, orefStr s
return wstore.ContextGetUpdatesRtn(ctx), nil
}
-func (svc *ObjectService) UpdateObject_Meta() servicemeta.MethodMeta {
- return servicemeta.MethodMeta{
+func (svc *ObjectService) UpdateObject_Meta() tsgenmeta.MethodMeta {
+ return tsgenmeta.MethodMeta{
ArgNames: []string{"uiContext", "waveObj", "returnUpdates"},
}
}
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 5ddfc156..bc886b15 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -9,11 +9,12 @@ import (
"reflect"
"strings"
+ "github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/service/blockservice"
"github.com/wavetermdev/thenextwave/pkg/service/clientservice"
"github.com/wavetermdev/thenextwave/pkg/service/fileservice"
"github.com/wavetermdev/thenextwave/pkg/service/objectservice"
- "github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
+ "github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
@@ -31,9 +32,10 @@ var updatesRType = reflect.TypeOf(([]wstore.WaveObjUpdate{}))
var waveObjRType = reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
var waveObjSliceRType = reflect.TypeOf([]waveobj.WaveObj{})
var waveObjMapRType = reflect.TypeOf(map[string]waveobj.WaveObj{})
-var methodMetaRType = reflect.TypeOf(servicemeta.MethodMeta{})
+var methodMetaRType = reflect.TypeOf(tsgenmeta.MethodMeta{})
var waveObjUpdateRType = reflect.TypeOf(wstore.WaveObjUpdate{})
var uiContextRType = reflect.TypeOf((*wstore.UIContext)(nil)).Elem()
+var blockCommandRType = reflect.TypeOf((*blockcontroller.BlockCommand)(nil)).Elem()
type WebCallType struct {
Service string `json:"service"`
@@ -89,12 +91,25 @@ func convertComplex(argType reflect.Type, jsonArg any) (any, error) {
}
func isSpecialWaveArgType(argType reflect.Type) bool {
- return argType == waveObjRType || argType == waveObjSliceRType || argType == waveObjMapRType
+ return argType == waveObjRType || argType == waveObjSliceRType || argType == waveObjMapRType || argType == blockCommandRType
+}
+
+func convertBlockCommand(argType reflect.Type, jsonArg any) (any, error) {
+ if _, ok := jsonArg.(map[string]any); !ok {
+ return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
+ }
+ cmd, err := blockcontroller.ParseCmdMap(jsonArg.(map[string]any))
+ if err != nil {
+ return nil, fmt.Errorf("error parsing command map: %w", err)
+ }
+ return cmd, nil
}
func convertSpecial(argType reflect.Type, jsonArg any) (any, error) {
jsonType := reflect.TypeOf(jsonArg)
- if argType == waveObjRType {
+ if argType == blockCommandRType {
+ return convertBlockCommand(argType, jsonArg)
+ } else if argType == waveObjRType {
if jsonType.Kind() != reflect.Map {
return nil, fmt.Errorf("cannot convert %T to %s", jsonArg, argType)
}
diff --git a/pkg/service/servicemeta/servicemeta.go b/pkg/service/servicemeta/servicemeta.go
deleted file mode 100644
index a6a59fa2..00000000
--- a/pkg/service/servicemeta/servicemeta.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2024, Command Line Inc.
-// SPDX-License-Identifier: Apache-2.0
-
-package servicemeta
-
-type MethodMeta struct {
- Desc string
- ArgNames []string
- ReturnDesc string
-}
diff --git a/pkg/tsgen/tsgen.go b/pkg/tsgen/tsgen.go
index 4d794bc1..100e3cdb 100644
--- a/pkg/tsgen/tsgen.go
+++ b/pkg/tsgen/tsgen.go
@@ -10,9 +10,10 @@ import (
"reflect"
"strings"
+ "github.com/wavetermdev/thenextwave/pkg/blockcontroller"
"github.com/wavetermdev/thenextwave/pkg/eventbus"
"github.com/wavetermdev/thenextwave/pkg/service"
- "github.com/wavetermdev/thenextwave/pkg/service/servicemeta"
+ "github.com/wavetermdev/thenextwave/pkg/tsgen/tsgenmeta"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
"github.com/wavetermdev/thenextwave/pkg/wstore"
)
@@ -69,7 +70,7 @@ func isFieldOmitEmpty(field reflect.StructField) bool {
return false
}
-func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
+func TypeToTSType(t reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
switch t.Kind() {
case reflect.String:
return "string", nil
@@ -80,7 +81,7 @@ func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
case reflect.Bool:
return "boolean", nil
case reflect.Slice, reflect.Array:
- elemType, subTypes := TypeToTSType(t.Elem())
+ elemType, subTypes := TypeToTSType(t.Elem(), tsTypesMap)
if elemType == "" {
return "", nil
}
@@ -92,7 +93,7 @@ func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
if t == metaRType {
return "MetaType", nil
}
- elemType, subTypes := TypeToTSType(t.Elem())
+ elemType, subTypes := TypeToTSType(t.Elem(), tsTypesMap)
if elemType == "" {
return "", nil
}
@@ -100,10 +101,10 @@ func TypeToTSType(t reflect.Type) (string, []reflect.Type) {
case reflect.Struct:
return t.Name(), []reflect.Type{t}
case reflect.Ptr:
- return TypeToTSType(t.Elem())
+ return TypeToTSType(t.Elem(), tsTypesMap)
case reflect.Interface:
- if t == waveObjRType {
- return "WaveObj", nil
+ if _, ok := tsTypesMap[t]; ok {
+ return t.Name(), nil
}
return "any", nil
default:
@@ -115,7 +116,7 @@ var tsRenameMap = map[string]string{
"Window": "WaveWindow",
}
-func generateTSTypeInternal(rtype reflect.Type) (string, []reflect.Type) {
+func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
var buf bytes.Buffer
waveObjType := reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
tsTypeName := rtype.Name()
@@ -149,10 +150,10 @@ func generateTSTypeInternal(rtype reflect.Type) (string, []reflect.Type) {
}
tsTypeTag := field.Tag.Get("tstype")
if tsTypeTag != "" {
- buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
+ buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
continue
}
- tsType, fieldSubTypes := TypeToTSType(field.Type)
+ tsType, fieldSubTypes := TypeToTSType(field.Type, tsTypesMap)
if tsType == "" {
continue
}
@@ -179,7 +180,32 @@ func GenerateWaveObjTSType() string {
func GenerateMetaType() string {
return "type MetaType = {[key: string]: any}\n"
+}
+func GenerateTSTypeUnion(unionMeta tsgenmeta.TypeUnionMeta, tsTypeMap map[reflect.Type]string) {
+ rtn := generateTSTypeUnionInternal(unionMeta)
+ tsTypeMap[unionMeta.BaseType] = rtn
+ for _, rtype := range unionMeta.Types {
+ GenerateTSType(rtype, tsTypeMap)
+ }
+}
+
+func generateTSTypeUnionInternal(unionMeta tsgenmeta.TypeUnionMeta) string {
+ var buf bytes.Buffer
+ if unionMeta.Desc != "" {
+ buf.WriteString(fmt.Sprintf("// %s\n", unionMeta.Desc))
+ }
+ buf.WriteString(fmt.Sprintf("type %s = {\n", unionMeta.BaseType.Name()))
+ buf.WriteString(fmt.Sprintf(" %s: string;\n", unionMeta.TypeFieldName))
+ buf.WriteString("} & ( ")
+ for idx, rtype := range unionMeta.Types {
+ if idx > 0 {
+ buf.WriteString(" | ")
+ }
+ buf.WriteString(rtype.Name())
+ }
+ buf.WriteString(" );\n")
+ return buf.String()
}
func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
@@ -212,7 +238,7 @@ func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
if rtype.Kind() != reflect.Struct {
return
}
- tsType, subTypes := generateTSTypeInternal(rtype)
+ tsType, subTypes := generateTSTypeInternal(rtype, tsTypesMap)
tsTypesMap[rtype] = tsType
for _, subType := range subTypes {
GenerateTSType(subType, tsTypesMap)
@@ -229,7 +255,7 @@ func hasUpdatesReturn(method reflect.Method) bool {
return false
}
-func GenerateMethodSignature(serviceName string, method reflect.Method, meta servicemeta.MethodMeta, isFirst bool) string {
+func GenerateMethodSignature(serviceName string, method reflect.Method, meta tsgenmeta.MethodMeta, isFirst bool, tsTypesMap map[reflect.Type]string) string {
var sb strings.Builder
mayReturnUpdates := hasUpdatesReturn(method)
if (meta.Desc != "" || meta.ReturnDesc != "" || mayReturnUpdates) && !isFirst {
@@ -260,7 +286,7 @@ func GenerateMethodSignature(serviceName string, method reflect.Method, meta ser
if inType == contextRType || inType == uiContextRType {
continue
}
- tsTypeName, _ := TypeToTSType(inType)
+ tsTypeName, _ := TypeToTSType(inType, tsTypesMap)
var argName string
if idx-1 < len(meta.ArgNames) {
argName = meta.ArgNames[idx-1] // subtract 1 for receiver
@@ -280,7 +306,7 @@ func GenerateMethodSignature(serviceName string, method reflect.Method, meta ser
if outType == updatesRtnRType {
continue
}
- tsTypeName, _ := TypeToTSType(outType)
+ tsTypeName, _ := TypeToTSType(outType, tsTypesMap)
sb.WriteString(fmt.Sprintf("Promise<%s>", tsTypeName))
wroteRtn = true
}
@@ -291,11 +317,11 @@ func GenerateMethodSignature(serviceName string, method reflect.Method, meta ser
return sb.String()
}
-func GenerateMethodBody(serviceName string, method reflect.Method, meta servicemeta.MethodMeta) string {
+func GenerateMethodBody(serviceName string, method reflect.Method, meta tsgenmeta.MethodMeta) string {
return fmt.Sprintf(" return WOS.callBackendService(%q, %q, Array.from(arguments))\n", serviceName, method.Name)
}
-func GenerateServiceClass(serviceName string, serviceObj any) string {
+func GenerateServiceClass(serviceName string, serviceObj any, tsTypesMap map[reflect.Type]string) string {
serviceType := reflect.TypeOf(serviceObj)
var sb strings.Builder
tsServiceName := serviceType.Elem().Name()
@@ -309,14 +335,14 @@ func GenerateServiceClass(serviceName string, serviceObj any) string {
if strings.HasSuffix(method.Name, "_Meta") {
continue
}
- var meta servicemeta.MethodMeta
+ var meta tsgenmeta.MethodMeta
metaMethod, found := serviceType.MethodByName(method.Name + "_Meta")
if found {
serviceObjVal := reflect.ValueOf(serviceObj)
metaVal := metaMethod.Func.Call([]reflect.Value{serviceObjVal})
- meta = metaVal[0].Interface().(servicemeta.MethodMeta)
+ meta = metaVal[0].Interface().(tsgenmeta.MethodMeta)
}
- sb.WriteString(GenerateMethodSignature(serviceName, method, meta, isFirst))
+ sb.WriteString(GenerateMethodSignature(serviceName, method, meta, isFirst, tsTypesMap))
sb.WriteString(GenerateMethodBody(serviceName, method, meta))
sb.WriteString(" }\n")
isFirst = false
@@ -327,6 +353,7 @@ func GenerateServiceClass(serviceName string, serviceObj any) string {
}
func GenerateWaveObjTypes(tsTypesMap map[reflect.Type]string) {
+ GenerateTSTypeUnion(blockcontroller.CommandTypeUnionMeta(), tsTypesMap)
GenerateTSType(reflect.TypeOf(waveobj.ORef{}), tsTypesMap)
GenerateTSType(reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem(), tsTypesMap)
GenerateTSType(reflect.TypeOf(map[string]any{}), tsTypesMap)
diff --git a/pkg/tsgen/tsgenmeta/tsgenmeta.go b/pkg/tsgen/tsgenmeta/tsgenmeta.go
new file mode 100644
index 00000000..8e17164a
--- /dev/null
+++ b/pkg/tsgen/tsgenmeta/tsgenmeta.go
@@ -0,0 +1,19 @@
+// Copyright 2024, Command Line Inc.
+// SPDX-License-Identifier: Apache-2.0
+
+package tsgenmeta
+
+import "reflect"
+
+type MethodMeta struct {
+ Desc string
+ ArgNames []string
+ ReturnDesc string
+}
+
+type TypeUnionMeta struct {
+ BaseType reflect.Type
+ Desc string
+ TypeFieldName string
+ Types []reflect.Type
+}