# Poiesis ## Module Name `reichard.io/poiesis` ## Overview Go tool that transpiles TypeScript to JavaScript using esbuild API and executes it with github.com/fastschema/qjs. Features a flexible builtin system for exposing Go functions to TypeScript with support for both synchronous and asynchronous (Promise-based) operations. ## Build & Test ```bash go build ./cmd/poiesis go test ./... golangci-lint run ``` ## Project Structure ``` reichard.io/poiesis/ ├── cmd/poiesis/ # CLI entry point │ └── main.go ├── internal/ │ ├── runtime/ # Runtime management, transpilation, execution │ │ ├── runtime.go │ │ ├── runtime_test.go │ │ └── options.go │ ├── functions/ # Function registration framework │ │ ├── registry.go │ │ ├── types.go │ │ ├── typescript_test.go │ │ └── functions_test.go │ ├── tsconvert/ # Go-to-TypeScript type conversion utilities │ │ ├── convert.go │ │ ├── types.go │ │ └── convert_test.go │ └── stdlib/ # Standard library implementations │ ├── fetch.go │ └── fetch_test.go ``` ## Key Packages - `reichard.io/poiesis/internal/runtime` - Runtime management, TypeScript transpilation, JavaScript execution, per-runtime type management - `reichard.io/poiesis/internal/functions` - Generic function registration framework (sync/async wrappers, automatic JS/Go conversion via JSON) - `reichard.io/poiesis/internal/tsconvert` - Go-to-TypeScript type conversion utilities and type declaration generation - `reichard.io/poiesis/internal/stdlib` - Standard library implementations (fetch) ## Function System ### Registration Two types of functions: - **Sync**: `RegisterFunction[T, R](name, fn)` - executes synchronously, returns value - **Async**: `RegisterAsyncFunction[T, R](name, fn)` - runs in goroutine, returns Promise ### Requirements - Args must be a struct implementing `Args` interface with `Validate() error` method - Use JSON tags for TypeScript type definitions - Async functions automatically generate `Promise` return types ### Calling Convention **Important**: Functions have different calling conventions on Go vs JavaScript sides: - **Go side**: Function receives a **single argument struct** with all parameters - **JavaScript side**: Function is called with **individual arguments** matching the struct fields (in field order) Fields are ordered by their position in the struct, with the generated TypeScript signature using those field names as argument names. ### Example ```go type AddArgs struct { A int `json:"a"` B int `json:"b"` } func (a AddArgs) Validate() error { return nil } func Add(_ context.Context, args AddArgs) (int, error) { return args.A + args.B, nil } // Register sync function functions.RegisterFunction[AddArgs, int]("add", Add) // Register async function functions.RegisterAsyncFunction[FetchArgs, *FetchResult]("fetch", Fetch) ``` ## Testing Patterns - **Test framework**: Go's built-in `testing` package - **Assertions**: `github.com/stretchr/testify/assert` and `require` - **Linting**: `golangci-lint run` - must pass before committing - **Test organization**: Test files use `_test.go` suffix, test functions prefixed with `Test` - **TypeScript test files**: Tests that require TypeScript files should create them inline using `os.CreateTemp()` instead of relying on external test files ## Dependencies - `github.com/evanw/esbuild/pkg/api` - TypeScript transpilation - `github.com/fastschema/qjs` - JavaScript execution (CGO-free QuickJS runtime) - `github.com/stretchr/testify/assert` - Test assertions ## Code Conventions - Handle all return values from external functions (enforced by golangci-lint) - Use `os` package instead of deprecated `io/ioutil` - Error logging uses `_, _ = fmt.Fprintf(stderr, ...)` pattern - Package structure follows standard Go project layout with internal packages ### Comment Style Code blocks (even within functions) should be separated with title-cased comments describing what the block does: ```go // Create Runtime r := &Runtime{opts: qjs.Option{Context: ctx}} // Create QuickJS Context rt, err := qjs.New(r.opts) // Populate Globals if err := r.populateGlobals(); err != nil { return nil, err } ``` For more complex blocks, use a hyphen to add elaboration: ```go // Does Thing - We do this here because we need to do xyz... ```