gots-engine
v1.0.2
Published
Write Go inside TypeScript. Compile-time speed, runtime magic. Run Go functions directly from TypeScript via WebAssembly or native binaries.
Maintainers
Readme
gots-engine — Go inside TypeScript
Write Go functions directly in TypeScript files. Compile-time speed, runtime magic.
What is gots-engine?
gots-engine lets you embed Go code inside TypeScript using tagged template literals. It automatically compiles, caches, and runs your Go code via WebAssembly or a native binary — whichever is faster on your platform.
import { go } from 'gots-engine';
// ① Define Go functions inline
const math = await go`
func SumN(n int) int {
total := 0
for i := 0; i < n; i++ { total += i }
return total
}
`;
// ② Call them like normal TypeScript async functions
const result = await math.call('SumN', 1_000_000);
console.log(result); // 499999500000 — runs at Go speedNo microservices. No gRPC. No child processes to manage. One file.
Why?
JavaScript/TypeScript is fast for I/O but slow for CPU-intensive work:
| Operation | JavaScript | gots-engine (WASM) | gots-engine (native) | |-----------|-----------|-------------|----------------| | Sum 1B iterations | ~2,100 ms | ~180 ms | ~95 ms | | gzip 10MB | ~890 ms | ~75 ms | ~40 ms | | Sort 1M elements | ~450 ms | ~85 ms | ~50 ms | | SHA-256 100MB | ~1,200 ms | ~95 ms | ~55 ms |
gots-engine closes this gap without leaving your TypeScript project.
Installation
npm install gots-engineRequirements:
- Node.js >= 18
- Go >= 1.21 (for WASM or native backend)
- TinyGo (optional — produces smaller WASM binaries)
Check your environment:
npx gots-engine infoQuick Start
Basic computation
import { go } from 'gots-engine';
const calc = await go`
func Fibonacci(n int) int {
if n <= 1 { return n }
a, b := 0, 1
for i := 2; i <= n; i++ { a, b = b, a+b }
return b
}
`;
console.log(await calc.call('Fibonacci', 45)); // 1134903170Multiple functions in one block
const text = await go`
import "strings"
func CountWords(s string) int {
return len(strings.Fields(s))
}
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
`;
const wordCount = await text.call('CountWords', 'Hello World from Go');
const reversed = await text.call('Reverse', 'gots-engine');Error handling
import { go, GoError } from 'gots-engine';
const safe = await go`
import "fmt"
func Divide(a, b float64) (float64, error) {
if b == 0 { return 0, fmt.Errorf("division by zero") }
return a / b, nil
}
`;
try {
await safe.call('Divide', 10, 0);
} catch (err) {
if (err instanceof GoError) {
console.log(err.goMessage); // "division by zero"
}
}Structs (marshalled automatically)
const sorter = await go`
import "sort"
type User struct {
Name string \`json:"name"\`
Score float64 \`json:"score"\`
}
func SortByScore(users []User) []User {
sort.Slice(users, func(i, j int) bool {
return users[i].Score > users[j].Score
})
return users
}
`;
const sorted = await sorter.call('SortByScore', [
{ name: 'Alice', score: 95.5 },
{ name: 'Bob', score: 87.2 },
{ name: 'Carol', score: 99.1 },
]);
// [{ name: 'Carol', score: 99.1 }, { name: 'Alice', ... }, ...]Binary data (Uint8Array ↔ []byte)
const compress = await go`
import (
"bytes"
"compress/gzip"
)
func GzipCompress(data []byte) ([]byte, error) {
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
if _, err := w.Write(data); err != nil { return nil, err }
w.Close()
return buf.Bytes(), nil
}
`;
const data = new TextEncoder().encode('Hello!'.repeat(1000));
const compressed = await compress.call('GzipCompress', data);
// compressed is Uint8Array — gzip bytesReusable function handles
// Get a named function as a standalone callable
const crunch = await go`
func Crunch(n int) int {
sum := 0
for i := 0; i < n; i++ { sum += i * i }
return sum
}
`;
const crunchFn = crunch.fn('Crunch'); // () => Promise<unknown>
const results = await Promise.all([1000, 2000, 3000].map(crunchFn));Configuration
import { configure } from 'gots-engine';
configure({
backend: 'auto', // 'auto' | 'wasm-js' | 'wasm-wasip1' | 'tinygo-wasm' | 'native'
preferNative: true, // Use native binary on Node.js (fastest)
compileTimeout: 60_000, // Max compilation time (ms)
optimize: false, // Run wasm-opt on WASM output
logLevel: 'warn', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
noCache: false, // Disable disk cache
// Security — restrict what Go imports are allowed
deniedImports: ['os/exec', 'plugin'],
allowedImports: [], // If set, only these external imports are allowed
});Backend Selection
| Backend | Where it works | Speed | WASM size | Requires |
|---------|---------------|-------|-----------|----------|
| native | Node.js only | ⚡⚡⚡ Fastest | N/A | Go |
| tinygo-wasm | Browser + Node | ⚡⚡ | ~50-300KB | TinyGo |
| wasm-wasip1 | Node 20+ / Deno / Bun | ⚡⚡ | ~2-8MB | Go 1.21+ |
| wasm-js | Browser + Node | ⚡ | ~2-8MB | Go 1.11+ |
'auto' (default) picks the best backend for your environment automatically.
Build Plugins
Vite
// vite.config.ts
import { defineConfig } from 'vite';
import { gots-enginePlugin } from 'gots-engine/vite';
export default defineConfig({
plugins: [
gots-enginePlugin({
preferTinyGo: true, // Smaller WASM for browser
precompile: true, // Compile at build time, not runtime
typegen: true, // Generate .d.ts from Go structs
typegenOutput: 'src/generated/gots-engine.d.ts',
}),
],
});esbuild
import esbuild from 'esbuild';
import { gots-engineEsbuildPlugin } from 'gots-engine/esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
plugins: [gots-engineEsbuildPlugin({ precompile: true })],
});webpack
// webpack.config.js
const { gots-engineWebpackPlugin } = require('gots-engine/webpack');
module.exports = {
plugins: [new gots-engineWebpackPlugin({ precompile: true, typegen: true })],
};Next.js
// next.config.ts
import { withgots-engine } from 'gots-engine/next';
export default withgots-engine({
// your Next.js config
}, {
serverBackend: 'native', // Fast on Node.js server
clientBackend: 'tinygo-wasm', // Small for browser
precompile: true,
});CLI
# Show Go toolchain, TinyGo, cache stats, and recommended backend
npx gots-engine info
# Pre-compile all Go blocks in TypeScript files (for CI/production)
npx gots-engine build
npx gots-engine build "src/**/*.ts" --backend tinygo-wasm --optimize
npx gots-engine build --typegen --typegen-output src/generated/gots-engine.d.ts
# Validate Go blocks without compiling (for linting/CI)
npx gots-engine check src/index.ts
npx gots-engine check # scans all src/**/*.ts files
# Clear the compiled WASM/binary cache
npx gots-engine clean
npx gots-engine clean --forceType Safety
gots-engine automatically generates TypeScript types from your Go structs and function signatures:
npx gots-engine build --typegenThis produces a src/generated/gots-engine.d.ts file:
// Auto-generated by gots-engine
export interface User {
name: string;
score: number;
age: number;
}
export type FnSortByScore = (users: User[]) => Promise<User[]>;Caching
gots-engine uses a two-level cache to make repeated runs instant:
- Level 1 (Memory): Zero-overhead lookups within a process session
- Level 2 (Disk):
~/.gots-engine/cache/— persists across restarts
Each cache entry = <hash>.wasm + <hash>.meta.json
The hash is computed from: Go source code + Go version + gots-engine cache version. Any change to your Go code automatically triggers recompilation.
Security
By default, gots-engine blocks these import paths:
os/exec— prevents shell command executionplugin— prevents loading arbitrary Go plugins
Add custom restrictions via gots-engineConfig:
configure({
deniedImports: ['net/http'], // Block outbound HTTP from Go
allowedImports: ['fmt', 'strings', 'encoding/json'], // Allowlist mode
});Architecture
TypeScript Go Runtime
──────────────── ────────────────────────────
await go` ──→ GoParser.parse()
func Foo(...) ... GoCache.get(hash)
` GoCompiler.compile()
↓
[js/wasm] ← syscall/js bridge
[wasip1] ← JSON-RPC via stdin/stdout
[native] ← JSON-RPC via stdin/stdout
↓
handle.call('Foo', ...) ──→ GoBridge.marshalArgs()
runtime.call()
GoBridge.unmarshalResult()
←── TypeScript valueProject Structure
gots-engine/
├── src/
│ ├── index.ts # Public API: go(), GoError, configure()
│ ├── core/
│ │ ├── types.ts # All TypeScript type definitions
│ │ ├── errors.ts # GoError, GoCompileError, etc.
│ │ ├── parser.ts # Go AST parser (regex + mini-parser)
│ │ ├── compiler.ts # go/tinygo build orchestrator
│ │ ├── template-generator.ts # Wraps user code in compilable Go
│ │ ├── cache.ts # Two-level cache (memory + disk)
│ │ ├── bridge.ts # Type marshalling (TS ↔ Go)
│ │ ├── runtime.ts # go() tagged template implementation
│ │ ├── config.ts # Global gots-engineConfig
│ │ └── logger.ts # Internal logger
│ ├── backends/
│ │ ├── backend-selector.ts # Picks best backend automatically
│ │ ├── wasm/wasm-backend.ts # js/wasm + WASI WASM runtime
│ │ └── native/native-backend.ts # Native binary JSON-RPC runtime
│ ├── toolchain/
│ │ ├── go-detector.ts # Detects Go installation + version
│ │ └── tinygo-detector.ts # Detects TinyGo installation
│ ├── type-system/
│ │ ├── type-mapper.ts # Go ↔ TypeScript type mapping
│ │ └── validator.ts # Runtime type validation
│ ├── build-plugin/
│ │ ├── vite-plugin.ts # Vite integration
│ │ ├── esbuild-plugin.ts # esbuild integration
│ │ ├── webpack-plugin.ts # webpack 5 integration
│ │ ├── rollup-plugin.ts # Rollup integration
│ │ └── next-plugin.ts # Next.js withgots-engine() wrapper
│ └── cli/
│ ├── index.ts # CLI entry point
│ └── commands/ # info, build, check, clean
├── go/
│ ├── runtime/ # Go-side RPC + marshalling helpers
│ └── templates/ # Go code templates
├── wasm-runtime/
│ └── wasm_exec.cjs # Bundled Go WASM exec shim
└── test/
└── unit/ # 67 passing unit testsKnown Limitations
- Goroutines in WASM — Limited support. Use native backend for goroutine-heavy code.
- TinyGo compatibility — Not all stdlib packages work with TinyGo.
gots-engineauto-detects incompatible imports. - First-run compilation — Initial
await go\...`` takes 5–30s. Subsequent runs use the cache. - Browser WASM size — Standard Go WASM is 2–8MB. Use
preferTinyGo: truefor 50–300KB output. - Edge runtime — Native backend not available; use
wasm-wasip1for Cloudflare Workers.
Roadmap
| Version | Feature |
|---------|---------|
| 1.0 | ✅ Core runtime, all backends, CLI, build plugins |
| 1.1 | chan T → AsyncIterable<T> — streaming from Go to TypeScript |
| 1.2 | VSCode extension with Go LSP inside TypeScript files |
| 2.0 | Reverse direction — call TypeScript from Go via callbacks |
License
MIT © BRAHIM TIMEZGHINE TIMSoftDZ gots-engine contributors
