Files
poiesis/AGENTS.md
2026-01-29 21:31:49 -05:00

139 lines
4.5 KiB
Markdown

# 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<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
```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...
```