wip rename

This commit is contained in:
2026-01-28 14:22:35 -05:00
parent 604178341d
commit a613283539
11 changed files with 112 additions and 158 deletions

View File

@@ -1 +0,0 @@
package builtin

View File

@@ -1,4 +1,4 @@
package builtin
package functions
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package builtin
package functions
import (
"context"
@@ -18,21 +18,21 @@ func (t TestArgs) Validate() error {
return nil
}
func TestAsyncBuiltin(t *testing.T) {
RegisterAsyncBuiltin("testAsync", func(_ context.Context, args TestArgs) (string, error) {
func TestAsyncFunction(t *testing.T) {
RegisterAsyncFunction("testAsync", func(_ context.Context, args TestArgs) (string, error) {
return "result: " + args.Field1, nil
})
registryMutex.RLock()
builtin, ok := builtinRegistry["testAsync"]
fn, ok := functionRegistry["testAsync"]
registryMutex.RUnlock()
require.True(t, ok, "testAsync should be registered")
assert.Contains(t, builtin.Definition(), "Promise<string>", "definition should include Promise<string>")
assert.Contains(t, fn.Definition(), "Promise<string>", "definition should include Promise<string>")
}
func TestAsyncBuiltinResolution(t *testing.T) {
RegisterAsyncBuiltin("resolveTest", func(_ context.Context, args TestArgs) (string, error) {
func TestAsyncFunctionResolution(t *testing.T) {
RegisterAsyncFunction("resolveTest", func(_ context.Context, args TestArgs) (string, error) {
return "test-result", nil
})
@@ -43,15 +43,15 @@ func TestAsyncBuiltinResolution(t *testing.T) {
}()
vm.SetCanBlock(true)
RegisterBuiltins(context.Background(), vm)
RegisterFunctions(context.Background(), vm)
result, err := vm.Eval(`resolveTest("hello")`, quickjs.EvalGlobal)
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestAsyncBuiltinRejection(t *testing.T) {
RegisterAsyncBuiltin("rejectTest", func(_ context.Context, args TestArgs) (string, error) {
func TestAsyncFunctionRejection(t *testing.T) {
RegisterAsyncFunction("rejectTest", func(_ context.Context, args TestArgs) (string, error) {
return "", assert.AnError
})
@@ -62,7 +62,7 @@ func TestAsyncBuiltinRejection(t *testing.T) {
}()
vm.SetCanBlock(true)
RegisterBuiltins(context.Background(), vm)
RegisterFunctions(context.Background(), vm)
result, err := vm.Eval(`rejectTest({field1: "hello"})`, quickjs.EvalGlobal)
require.NoError(t, err)
@@ -70,7 +70,7 @@ func TestAsyncBuiltinRejection(t *testing.T) {
}
func TestNonPromise(t *testing.T) {
RegisterBuiltin("nonPromiseTest", func(_ context.Context, args TestArgs) (string, error) {
RegisterFunction("nonPromiseTest", func(_ context.Context, args TestArgs) (string, error) {
return "sync-result", nil
})
@@ -81,7 +81,7 @@ func TestNonPromise(t *testing.T) {
}()
vm.SetCanBlock(true)
RegisterBuiltins(context.Background(), vm)
RegisterFunctions(context.Background(), vm)
result, err := vm.Eval(`nonPromiseTest({field1: "hello"})`, quickjs.EvalGlobal)
require.NoError(t, err)

View File

@@ -1,4 +1,4 @@
package builtin
package functions
import (
"fmt"
@@ -8,12 +8,12 @@ import (
)
var (
builtinRegistry = make(map[string]Builtin)
registryMutex sync.RWMutex
collector *typeCollector
functionRegistry = make(map[string]Function)
registryMutex sync.RWMutex
collector *typeCollector
)
func registerBuiltin[A Args, R any](name string, isAsync bool, fn Func[A, R]) {
func registerFunction[A Args, R any](name string, isAsync bool, fn RawFunc[A, R]) {
registryMutex.Lock()
defer registryMutex.Unlock()
@@ -23,14 +23,14 @@ func registerBuiltin[A Args, R any](name string, isAsync bool, fn Func[A, R]) {
tType := reflect.TypeFor[A]()
if tType.Kind() != reflect.Struct {
panic(fmt.Sprintf("builtin %s: argument must be a struct type, got %v", name, tType))
panic(fmt.Sprintf("function %s: argument must be a struct type, got %v", name, tType))
}
fnType := reflect.TypeOf(fn)
types := collector.collectTypes(tType, fnType)
paramTypes := collector.getParamTypes()
builtinRegistry[name] = &builtinImpl[A, R]{
functionRegistry[name] = &functionImpl[A, R]{
name: name,
fn: fn,
types: types,
@@ -38,7 +38,7 @@ func registerBuiltin[A Args, R any](name string, isAsync bool, fn Func[A, R]) {
}
}
func GetBuiltinsDeclarations() string {
func GetFunctionDeclarations() string {
registryMutex.RLock()
defer registryMutex.RUnlock()
@@ -46,14 +46,14 @@ func GetBuiltinsDeclarations() string {
var typeDefs []string
var functionDecls []string
for _, builtin := range builtinRegistry {
for _, t := range builtin.Types() {
for _, fn := range functionRegistry {
for _, t := range fn.Types() {
if !typeDefinitions[t] {
typeDefinitions[t] = true
typeDefs = append(typeDefs, t)
}
}
functionDecls = append(functionDecls, builtin.Definition())
functionDecls = append(functionDecls, fn.Definition())
}
result := strings.Join(typeDefs, "\n\n")
@@ -65,14 +65,14 @@ func GetBuiltinsDeclarations() string {
return result
}
func RegisterBuiltin[T Args, R any](name string, fn Func[T, R]) {
registerBuiltin(name, false, fn)
func GetRegisteredFunctions() map[string]Function {
return functionRegistry
}
func RegisterAsyncBuiltin[T Args, R any](name string, fn Func[T, R]) {
registerBuiltin(name, true, fn)
func RegisterFunction[T Args, R any](name string, fn RawFunc[T, R]) {
registerFunction(name, false, fn)
}
func GetBuiltins() map[string]Builtin {
return builtinRegistry
func RegisterAsyncFunction[T Args, R any](name string, fn RawFunc[T, R]) {
registerFunction(name, true, fn)
}

View File

@@ -1,4 +1,4 @@
package builtin
package functions
import (
"context"
@@ -6,39 +6,39 @@ import (
"reflect"
)
type Builtin interface {
type Function interface {
Name() string
Types() []string
Definition() string
WrapFn(context.Context) func(...any) (any, error)
}
type Func[A Args, R any] func(ctx context.Context, args A) (R, error)
type RawFunc[A Args, R any] func(context.Context, A) (R, error)
type Args interface {
Validate() error
}
type builtinImpl[A Args, R any] struct {
type functionImpl[A Args, R any] struct {
name string
fn Func[A, R]
fn RawFunc[A, R]
definition string
types []string
}
func (b *builtinImpl[A, R]) Name() string {
func (b *functionImpl[A, R]) Name() string {
return b.name
}
func (b *builtinImpl[A, R]) Types() []string {
func (b *functionImpl[A, R]) Types() []string {
return b.types
}
func (b *builtinImpl[A, R]) Definition() string {
func (b *functionImpl[A, R]) Definition() string {
return b.definition
}
func (b *builtinImpl[A, R]) WrapFn(ctx context.Context) func(...any) (any, error) {
func (b *functionImpl[A, R]) WrapFn(ctx context.Context) func(...any) (any, error) {
return func(allArgs ...any) (any, error) {
// Populate Arguments
var fnArgs A

View File

@@ -1,4 +1,4 @@
package builtin
package functions
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package builtin
package functions
import (
"context"
@@ -18,11 +18,11 @@ func (t TestBasicArgs) Validate() error { return nil }
func TestBasicType(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestBasicArgs, string]("basic", func(ctx context.Context, args TestBasicArgs) (string, error) {
RegisterFunction[TestBasicArgs, string]("basic", func(ctx context.Context, args TestBasicArgs) (string, error) {
return args.Name, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function basic(name: string, age: number): string;")
assert.Contains(t, defs, "interface TestBasicArgs")
}
@@ -30,7 +30,7 @@ func TestBasicType(t *testing.T) {
func resetRegistry() {
registryLock.Lock()
defer registryLock.Unlock()
builtinRegistry = make(map[string]Builtin)
functionRegistry = make(map[string]Function)
}
var (
@@ -47,11 +47,11 @@ func (t TestComplexArgs) Validate() error { return nil }
func TestComplexTypes(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestComplexArgs, bool]("complex", func(ctx context.Context, args TestComplexArgs) (bool, error) {
RegisterFunction[TestComplexArgs, bool]("complex", func(ctx context.Context, args TestComplexArgs) (bool, error) {
return args.Flag, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function complex(items: number[], data: Record<string, any>, flag: boolean): boolean;")
}
@@ -66,11 +66,11 @@ func (t TestNestedArgs) Validate() error { return nil }
func TestNestedStruct(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestNestedArgs, string]("nested", func(ctx context.Context, args TestNestedArgs) (string, error) {
RegisterFunction[TestNestedArgs, string]("nested", func(ctx context.Context, args TestNestedArgs) (string, error) {
return args.User.FirstName, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function nested(user: {}): string;")
}
@@ -84,11 +84,11 @@ func (t TestOptionalArgs) Validate() error { return nil }
func TestOptionalFields(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestOptionalArgs, string]("optional", func(ctx context.Context, args TestOptionalArgs) (string, error) {
RegisterFunction[TestOptionalArgs, string]("optional", func(ctx context.Context, args TestOptionalArgs) (string, error) {
return args.Name, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function optional(name: string, age?: number | null, score?: number | null): string;")
}
@@ -105,11 +105,11 @@ func (t TestResultArgs) Validate() error { return nil }
func TestResultStruct(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestResultArgs, TestResult]("result", func(ctx context.Context, args TestResultArgs) (TestResult, error) {
RegisterFunction[TestResultArgs, TestResult]("result", func(ctx context.Context, args TestResultArgs) (TestResult, error) {
return TestResult{ID: 1}, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function result(input: string): TestResult;")
assert.Contains(t, defs, "interface TestResult {id: number; data: number[]}")
}
@@ -126,11 +126,11 @@ type TestAsyncResult struct {
func TestAsyncPromise(t *testing.T) {
resetRegistry()
RegisterAsyncBuiltin[TestAsyncArgs, *TestAsyncStatus]("async", func(ctx context.Context, args TestAsyncArgs) (*TestAsyncStatus, error) {
RegisterAsyncFunction[TestAsyncArgs, *TestAsyncStatus]("async", func(ctx context.Context, args TestAsyncArgs) (*TestAsyncStatus, error) {
return &TestAsyncStatus{Code: 200}, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function async(url: string): Promise<TestAsyncStatus | null>;")
assert.Contains(t, defs, "interface TestAsyncStatus")
}
@@ -151,11 +151,11 @@ func (t TestNestedPointerArgs) Validate() error { return nil }
func TestNestedPointerInResult(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestNestedPointerArgs, *TestNestedPointerResult]("pointerResult", func(ctx context.Context, args TestNestedPointerArgs) (*TestNestedPointerResult, error) {
RegisterFunction[TestNestedPointerArgs, *TestNestedPointerResult]("pointerResult", func(ctx context.Context, args TestNestedPointerArgs) (*TestNestedPointerResult, error) {
return &TestNestedPointerResult{Value: "test"}, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function pointerResult(id: number): TestNestedPointerResult | null;")
}
@@ -167,11 +167,11 @@ func (t TestUintArgs) Validate() error { return nil }
func TestUintType(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestUintArgs, uint]("uint", func(ctx context.Context, args TestUintArgs) (uint, error) {
RegisterFunction[TestUintArgs, uint]("uint", func(ctx context.Context, args TestUintArgs) (uint, error) {
return args.Value, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function uint(value: number): number;")
}
@@ -183,11 +183,11 @@ func (t TestFloatArgs) Validate() error { return nil }
func TestFloatType(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestFloatArgs, float32]("float", func(ctx context.Context, args TestFloatArgs) (float32, error) {
RegisterFunction[TestFloatArgs, float32]("float", func(ctx context.Context, args TestFloatArgs) (float32, error) {
return float32(args.Amount), nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function float(amount: number): number;")
}
@@ -201,11 +201,11 @@ func (t TestPointerInArgs) Validate() error { return nil }
func TestNestedPointerStruct(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestPointerInArgs, string]("nestedPointer", func(ctx context.Context, args TestPointerInArgs) (string, error) {
RegisterFunction[TestPointerInArgs, string]("nestedPointer", func(ctx context.Context, args TestPointerInArgs) (string, error) {
return "test", nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function nestedPointer(user?: {} | null): string;")
}
@@ -217,11 +217,11 @@ func (t TestErrorOnlyArgs) Validate() error { return nil }
func TestErrorOnlyReturn(t *testing.T) {
resetRegistry()
RegisterBuiltin[TestErrorOnlyArgs, struct{}]("errorOnly", func(ctx context.Context, args TestErrorOnlyArgs) (struct{}, error) {
RegisterFunction[TestErrorOnlyArgs, struct{}]("errorOnly", func(ctx context.Context, args TestErrorOnlyArgs) (struct{}, error) {
return struct{}{}, nil
})
defs := GetBuiltinsDeclarations()
defs := GetFunctionDeclarations()
assert.Contains(t, defs, "declare function errorOnly(input: string): {};")
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/evanw/esbuild/pkg/api"
"modernc.org/quickjs"
"reichard.io/poiesis/internal/builtin"
"reichard.io/poiesis/internal/functions"
)
type Runtime struct {
@@ -59,7 +59,7 @@ func (r *Runtime) populateGlobals() error {
}
// Register Custom Functions
for name, builtin := range builtin.GetBuiltins() {
for name, builtin := range functions.GetRegisteredFunctions() {
// Register Main Function
if err := r.vm.RegisterFunc(name, builtin.WrapFn(r.ctx), false); err != nil {
return err

View File

@@ -1,60 +0,0 @@
package standard
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"modernc.org/quickjs"
"reichard.io/poiesis/internal/builtin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFetchReturnsPromise(t *testing.T) {
vm, err := quickjs.NewVM()
require.NoError(t, err)
defer func() {
_ = vm.Close()
}()
vm.SetCanBlock(true)
builtin.RegisterBuiltins(context.Background(), vm)
result, err := vm.Eval(`fetch({input: "https://example.com"})`, quickjs.EvalGlobal)
require.NoError(t, err)
assert.NotNil(t, result)
}
func TestFetchAsyncAwait(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"status":"ok"}`))
}))
defer server.Close()
vm, err := quickjs.NewVM()
require.NoError(t, err)
defer func() {
_ = vm.Close()
}()
vm.SetCanBlock(true)
builtin.RegisterBuiltins(context.Background(), vm)
result, err := vm.Eval(`fetch({input: "`+server.URL+`"})`, quickjs.EvalGlobal)
require.NoError(t, err)
if obj, ok := result.(*quickjs.Object); ok {
var arr []any
if err := obj.Into(&arr); err == nil && len(arr) > 0 {
if response, ok := arr[0].(map[string]any); ok {
assert.True(t, response["ok"].(bool))
}
}
}
}

View File

@@ -1,4 +1,4 @@
package standard
package stdlib
import (
"context"
@@ -8,9 +8,13 @@ import (
"net/http"
"strings"
"reichard.io/poiesis/internal/builtin"
"reichard.io/poiesis/internal/functions"
)
func init() {
functions.RegisterAsyncFunction("fetch", Fetch)
}
type FetchArgs struct {
Input string `json:"input"`
Init *RequestInit `json:"init,omitempty"`
@@ -106,17 +110,3 @@ func Fetch(_ context.Context, args FetchArgs) (Response, error) {
Headers: resultHeaders,
}, nil
}
func Add(_ context.Context, args AddArgs) (int, error) {
return args.A + args.B, nil
}
func Greet(_ context.Context, args GreetArgs) (string, error) {
return fmt.Sprintf("Hello, %s!", args.Name), nil
}
func init() {
builtin.RegisterAsyncBuiltin("fetch", Fetch)
builtin.RegisterBuiltin("add", Add)
builtin.RegisterBuiltin("greet", Greet)
}

View File

@@ -1,4 +1,4 @@
package standard
package stdlib
import (
"context"
@@ -8,6 +8,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"modernc.org/quickjs"
"reichard.io/poiesis/internal/functions"
)
func TestFetch(t *testing.T) {
@@ -97,24 +99,47 @@ func TestFetchDefaults(t *testing.T) {
assert.True(t, result.OK)
}
func TestAdd(t *testing.T) {
ctx := context.Background()
result, err := Add(ctx, AddArgs{A: 5, B: 10})
func TestFetchReturnsPromise(t *testing.T) {
vm, err := quickjs.NewVM()
require.NoError(t, err)
assert.Equal(t, 15, result)
defer func() {
_ = vm.Close()
}()
vm.SetCanBlock(true)
result, err = Add(ctx, AddArgs{A: -3, B: 7})
functions.RegisterBuiltins(context.Background(), vm)
result, err := vm.Eval(`fetch({input: "https://example.com"})`, quickjs.EvalGlobal)
require.NoError(t, err)
assert.Equal(t, 4, result)
assert.NotNil(t, result)
}
func TestGreet(t *testing.T) {
ctx := context.Background()
result, err := Greet(ctx, GreetArgs{Name: "World"})
require.NoError(t, err)
assert.Equal(t, "Hello, World!", result)
func TestFetchAsyncAwait(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"status":"ok"}`))
}))
defer server.Close()
result, err = Greet(ctx, GreetArgs{Name: "Alice"})
vm, err := quickjs.NewVM()
require.NoError(t, err)
assert.Equal(t, "Hello, Alice!", result)
defer func() {
_ = vm.Close()
}()
vm.SetCanBlock(true)
functions.RegisterBuiltins(context.Background(), vm)
result, err := vm.Eval(`fetch({input: "`+server.URL+`"})`, quickjs.EvalGlobal)
require.NoError(t, err)
if obj, ok := result.(*quickjs.Object); ok {
var arr []any
if err := obj.Into(&arr); err == nil && len(arr) > 0 {
if response, ok := arr[0].(map[string]any); ok {
assert.True(t, response["ok"].(bool))
}
}
}
}