# Poiesis A Go tool that transpiles TypeScript to JavaScript using esbuild and executes it with qjs, with an extensible function system. ## Project Structure ``` reichard.io/poiesis/ ├── cmd/ │ └── poiesis/ # CLI application entry point │ └── main.go ├── internal/ │ ├── runtime/ # Runtime management, transpilation, execution │ │ ├── runtime.go # Core runtime, transpilation, execution │ │ └── runtime_test.go # Runtime tests │ ├── functions/ # Function registration framework │ │ ├── registry.go # Registration system │ │ ├── types.go # Core interfaces and types │ │ ├── typescript.go # TypeScript definition generation │ │ ├── collector.go # Type collection utilities │ │ └── typescript_test.go # Type system tests │ └── stdlib/ # Standard library implementations │ ├── fetch.go # HTTP fetch implementation │ └── fetch_test.go # Fetch tests ``` ## Architecture The project is cleanly separated into three packages: 1. **`internal/runtime`** - Runtime management - TypeScript transpilation with esbuild - JavaScript execution with qjs - Automatic function registration and execution 2. **`internal/functions`** - Generic function registration framework - Type-safe registration with generics - Bidirectional Go ↔ JavaScript type conversion - Automatic TypeScript declaration generation 3. **`internal/stdlib`** - Standard library implementations - `fetch` - HTTP requests - Extensible for additional standard functions ## Installation & Build ```bash go build ./cmd/poiesis ``` ## CLI Options - `[file]` - Path to TypeScript file to execute (optional) - `--print-types` - Print TypeScript type declarations for all registered functions - `--help` - Show help information ## Testing ```bash go test ./... golangci-lint run ``` ## Usage ```bash poiesis execute [file] # Run TypeScript file poiesis types # Print TypeScript type declarations poiesis --help # Show help ``` ## Function System The function system allows you to easily expose Go functions to TypeScript/JavaScript. ### Adding a Function Just write a Go function and register it: ```go package mystdlib import ( "context" "reichard.io/poiesis/internal/functions" ) 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 } func init() { functions.RegisterFunction[AddArgs, int]("add", Add) } ``` That's it! The framework automatically: - Converts JavaScript values to Go types - Handles errors (panics as JS errors) - Generates TypeScript definitions - Manages the qjs integration ### Calling Convention **Important**: There's an important difference between how functions are defined in Go versus how they're called in JavaScript: - **Go side**: The function receives a **single argument struct** containing all parameters - **JavaScript side**: The function is called with the **struct fields as individual arguments** (in the order they appear in the struct) ```go // Go: Single struct argument type AddArgs struct { A int `json:"a"` B int `json:"b"` } func Add(_ context.Context, args AddArgs) (int, error) { return args.A + args.B, nil } ``` ```typescript // JavaScript: Individual arguments (not an object!) const result = add(5, 10); // NOT add({ a: 5, b: 10 }) ``` The framework extracts the JSON tags from the struct fields and uses them to generate the correct TypeScript function signature. ### Example ```typescript // TypeScript code - call with individual arguments matching struct fields const response = fetch("https://httpbin.org/get"); console.log("OK:", response.ok); console.log("Status:", response.status); console.log("Body:", response.body); ``` ### Built-in Functions - `fetch(options)` - HTTP requests - `options.input` (string) - URL to fetch - `options.init` (object) - Optional init object with `method`, `headers`, `body` ## Dependencies - `github.com/evanw/esbuild/pkg/api` - TypeScript transpilation - `github.com/fastschema/qjs` - JavaScript execution (QuickJS) - `github.com/stretchr/testify/assert` - Test assertions ## Development - **Test framework**: Go's built-in `testing` package - **Code style**: Follow standard Go conventions - **Linting**: `golangci-lint run` - must pass before committing - **TypeScript test files**: Tests that require TypeScript files should create them inline using `os.CreateTemp()` instead of relying on external test files