3.7 KiB
3.7 KiB
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
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
│ ├── functions/ # Function registration framework
│ │ ├── collector.go
│ │ ├── registry.go
│ │ ├── types.go
│ │ ├── typescript.go
│ │ ├── typescript_test.go
│ │ └── functions_test.go
│ └── stdlib/ # Standard library implementations
│ ├── fetch.go
│ └── fetch_test.go
Key Packages
reichard.io/poiesis/internal/runtime- Runtime management, TypeScript transpilation, JavaScript executionreichard.io/poiesis/internal/functions- Generic function registration framework (sync/async wrappers, automatic JS/Go conversion via JSON, type definition 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
Argsinterface withValidate() errormethod - Use JSON tags for TypeScript type definitions
- Async functions automatically generate
Promise<R>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
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
testingpackage - Assertions:
github.com/stretchr/testify/assertandrequire - Linting:
golangci-lint run- must pass before committing - Test organization: Test files use
_test.gosuffix, test functions prefixed withTest - 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 transpilationgithub.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
ospackage instead of deprecatedio/ioutil - Error logging uses
_, _ = fmt.Fprintf(stderr, ...)pattern - Package structure follows standard Go project layout with internal packages