This commit is contained in:
2026-01-27 20:04:36 -05:00
parent c3a16c9e92
commit f9d3753806
13 changed files with 247 additions and 352 deletions

View File

@@ -2,71 +2,80 @@ package builtin
import (
"context"
"encoding/json"
"fmt"
"reflect"
"github.com/dop251/goja"
"modernc.org/quickjs"
)
func createWrapper[T Args, R any](fn Func[T, R], isAsync bool) func(*goja.Runtime) func(goja.FunctionCall) goja.Value {
return func(vm *goja.Runtime) func(goja.FunctionCall) goja.Value {
return func(call goja.FunctionCall) goja.Value {
var args T
argsValue := reflect.ValueOf(&args).Elem()
func createWrapper[T Args, R any](fn Func[T, R], isAsync bool) interface{} {
if !isAsync {
return createSyncWrapper[T, R](fn)
}
return createAsyncWrapper[T, R](fn)
}
for i := 0; i < argsValue.NumField() && i < len(call.Arguments); i++ {
jsArg := call.Arguments[i]
field := argsValue.Field(i)
if goja.IsUndefined(jsArg) || goja.IsNull(jsArg) {
if field.Kind() == reflect.Pointer {
continue
}
}
converted, err := convertJSValueToGo(vm, jsArg, field.Type())
if err != nil {
panic(fmt.Sprintf("argument %d (%s): %v", i, getFieldName(argsValue.Type().Field(i)), err))
}
if converted != nil {
field.Set(reflect.ValueOf(converted))
}
}
if err := args.Validate(); err != nil {
panic(fmt.Sprintf("argument validation failed: %v", err))
}
if isAsync {
return createAsyncPromise(vm, fn, args)
}
ctx := context.Background()
result, err := fn(ctx, args)
func createSyncWrapper[T Args, R any](fn Func[T, R]) interface{} {
return func(rawArgs any) (R, error) {
var zero R
var args T
obj, ok := rawArgs.(*quickjs.Object)
if ok {
jsonData, err := obj.MarshalJSON()
if err != nil {
panic(err)
return zero, fmt.Errorf("failed to marshal args: %w", err)
}
if err := json.Unmarshal(jsonData, &args); err != nil {
return zero, fmt.Errorf("failed to unmarshal args: %w", err)
}
} else if rawArgs != nil && rawArgs != quickjs.UndefinedValue {
jsonData, err := json.Marshal(rawArgs)
if err != nil {
return zero, fmt.Errorf("failed to marshal args: %w", err)
}
if err := json.Unmarshal(jsonData, &args); err != nil {
return zero, fmt.Errorf("failed to unmarshal args: %w", err)
}
return convertGoValueToJS(vm, reflect.ValueOf(result))
}
if err := args.Validate(); err != nil {
return zero, fmt.Errorf("argument validation failed: %w", err)
}
ctx := context.Background()
return fn(ctx, args)
}
}
func createAsyncPromise[T Args, R any](vm *goja.Runtime, fn Func[T, R], args T) goja.Value {
promise, resolve, reject := vm.NewPromise()
func createAsyncWrapper[T Args, R any](fn Func[T, R]) interface{} {
return func(rawArgs any) (any, error) {
var args T
go func() {
ctx := context.Background()
result, err := fn(ctx, args)
if err != nil {
_ = reject(vm.ToValue(err.Error()))
} else {
_ = resolve(convertGoValueToJS(vm, reflect.ValueOf(result)))
obj, ok := rawArgs.(*quickjs.Object)
if ok {
jsonData, err := obj.MarshalJSON()
if err != nil {
return nil, fmt.Errorf("failed to marshal args: %w", err)
}
if err := json.Unmarshal(jsonData, &args); err != nil {
return nil, fmt.Errorf("failed to unmarshal args: %w", err)
}
} else if rawArgs != nil && rawArgs != quickjs.UndefinedValue {
jsonData, err := json.Marshal(rawArgs)
if err != nil {
return nil, fmt.Errorf("failed to marshal args: %w", err)
}
if err := json.Unmarshal(jsonData, &args); err != nil {
return nil, fmt.Errorf("failed to unmarshal args: %w", err)
}
}
}()
return vm.ToValue(promise)
if err := args.Validate(); err != nil {
return nil, fmt.Errorf("argument validation failed: %w", err)
}
ctx := context.Background()
return fn(ctx, args)
}
}