159 lines
4.5 KiB
Markdown
159 lines
4.5 KiB
Markdown
# 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
|
|
```
|
|
|
|
## Testing
|
|
|
|
```bash
|
|
go test ./...
|
|
golangci-lint run
|
|
```
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
poiesis <typescript-file>
|
|
poiesis -print-types
|
|
```
|
|
|
|
## 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
|