https://github.com/aperturerobotics/go-quickjs-wasi-reactor
Run quickjs within Go wazero with WASI with reentrant scheduling.
https://github.com/aperturerobotics/go-quickjs-wasi-reactor
node quickjs wasi
Last synced: 2 months ago
JSON representation
Run quickjs within Go wazero with WASI with reentrant scheduling.
- Host: GitHub
- URL: https://github.com/aperturerobotics/go-quickjs-wasi-reactor
- Owner: aperturerobotics
- License: mit
- Created: 2025-07-04T01:35:56.000Z (11 months ago)
- Default Branch: master
- Last Pushed: 2026-03-29T11:42:55.000Z (2 months ago)
- Last Synced: 2026-03-29T14:26:04.920Z (2 months ago)
- Topics: node, quickjs, wasi
- Language: Go
- Homepage: https://github.com/quickjs-ng/quickjs
- Size: 3.22 MB
- Stars: 14
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# go-quickjs-wasi-reactor
[![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card]
> A Go module that embeds the QuickJS-NG WASI WebAssembly runtime (reactor model).
[GoDoc]: https://godoc.org/github.com/aperturerobotics/go-quickjs-wasi-reactor
[GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/go-quickjs-wasi-reactor?status.svg
[Go Report Card Widget]: https://goreportcard.com/badge/github.com/aperturerobotics/go-quickjs-wasi-reactor
[Go Report Card]: https://goreportcard.com/report/github.com/aperturerobotics/go-quickjs-wasi-reactor
## Related Projects
- [js-quickjs-wasi-reactor](https://github.com/aperturerobotics/js-quickjs-wasi-reactor) - JavaScript/TypeScript harness for browser environments
- [go-quickjs-wasi](https://github.com/paralin/go-quickjs-wasi) - Standard WASI command model with blocking `_start()` entry point
- [QuickJS-NG](https://github.com/quickjs-ng/quickjs) - Upstream QuickJS-NG (includes WASI reactor build)
## Variants
This repository provides the **reactor model** WASM binary for re-entrant execution. If you only need simple blocking execution where QuickJS runs to completion in `_start()`, see the **command model** variant above.
## About QuickJS-NG
QuickJS is a small and embeddable JavaScript engine. It aims to support the latest ECMAScript specification.
This project uses [QuickJS-NG], a community-driven fork of the original
[QuickJS project] by Fabrice Bellard and Charlie Gordon. Both projects are
actively maintained. The WASI reactor build target is part of upstream QuickJS-NG,
and the WASM binary is pulled directly from QuickJS-NG GitHub release artifacts.
[QuickJS-NG]: https://github.com/quickjs-ng/quickjs
[QuickJS project]: https://bellard.org/quickjs/
## Purpose
This module provides easy access to the QuickJS-NG JavaScript engine compiled to
WebAssembly with WASI support using the **reactor model**. The WASM binary is
embedded directly in the Go module, making it easy to use QuickJS in Go
applications without external dependencies.
### Reactor Model
Unlike the standard WASI "command" model that blocks in `_start()`, the reactor
model exports the raw QuickJS C API functions, enabling full control over the
JavaScript runtime lifecycle from the host environment.
The reactor exports the complete QuickJS C API including:
**Core Runtime:**
- `JS_NewRuntime`, `JS_FreeRuntime` - Runtime lifecycle
- `JS_NewContext`, `JS_FreeContext` - Context lifecycle
- `JS_Eval` - Evaluate JavaScript code
- `JS_Call` - Call JavaScript functions
**Standard Library (quickjs-libc.h):**
- `js_init_module_std`, `js_init_module_os`, `js_init_module_bjson` - Module initialization
- `js_std_init_handlers`, `js_std_free_handlers` - I/O handler setup
- `js_std_add_helpers` - Add console.log, print, etc.
- `js_std_loop_once` - Run one iteration of the event loop (non-blocking)
- `js_std_poll_io` - Poll for I/O events
**Memory Management:**
- `malloc`, `free`, `realloc`, `calloc` - For host to allocate memory
## Features
- Embeds the QuickJS-NG WASI reactor WebAssembly binary
- Provides version information about the embedded QuickJS release
- High-level Go API via the `wazero-quickjs` subpackage
- Update script to build and copy from local QuickJS checkout
## Packages
### Root Package (`github.com/aperturerobotics/go-quickjs-wasi-reactor`)
Provides the embedded WASM binary and version information:
```go
package main
import (
"fmt"
quickjswasi "github.com/aperturerobotics/go-quickjs-wasi-reactor"
)
func main() {
// Access the embedded WASM binary
wasmBytes := quickjswasi.QuickJSWASM
fmt.Printf("QuickJS WASM size: %d bytes\n", len(wasmBytes))
// Get version information
fmt.Printf("QuickJS version: %s\n", quickjswasi.Version)
fmt.Printf("Download URL: %s\n", quickjswasi.DownloadURL)
}
```
### Wazero QuickJS Library (`github.com/aperturerobotics/go-quickjs-wasi-reactor/wazero-quickjs`)
High-level Go API for running JavaScript with wazero:
```go
package main
import (
"context"
"embed"
"os"
quickjs "github.com/aperturerobotics/go-quickjs-wasi-reactor/wazero-quickjs"
"github.com/tetratelabs/wazero"
)
//go:embed scripts
var scriptsFS embed.FS
func main() {
ctx := context.Background()
r := wazero.NewRuntime(ctx)
defer r.Close(ctx)
config := wazero.NewModuleConfig().
WithStdout(os.Stdout).
WithStderr(os.Stderr).
WithFS(scriptsFS) // Mount embedded filesystem
qjs, _ := quickjs.NewQuickJS(ctx, r, config)
defer qjs.Close(ctx)
// Option 1: Initialize with CLI args to load script via WASI filesystem
qjs.Init(ctx, []string{"qjs", "--std", "scripts/main.js"})
// Option 2: Initialize with --std and eval code directly
// qjs.Init(ctx, []string{"qjs", "--std"})
// qjs.Eval(ctx, `console.log("Hello from QuickJS!");`, false)
// Run event loop until idle
qjs.RunLoop(ctx)
}
```
### Initialization Options
```go
// Basic runtime with std modules available for import
qjs.Init(ctx, nil)
qjs.Eval(ctx, `import * as std from 'qjs:std'; std.printf("Hello\n")`, true)
// With --std flag to expose std, os, bjson as globals
qjs.Init(ctx, []string{"qjs", "--std"})
qjs.Eval(ctx, `std.printf("Hello\n")`, false) // std is already global
// With script args accessible via scriptArgs global
qjs.Init(ctx, []string{"qjs", "script.js", "--verbose"})
qjs.Eval(ctx, `console.log(scriptArgs)`, false) // ['qjs', 'script.js', '--verbose']
```
### Non-Blocking Event Loop
The reactor model enables cooperative scheduling with the host:
```go
// Instead of blocking RunLoop(), use LoopOnce() for fine-grained control
for {
result, err := qjs.LoopOnce(ctx)
if err != nil {
return err
}
switch {
case result == quickjs.LoopIdle:
// No pending work - done
return nil
case result == quickjs.LoopError:
return errors.New("JavaScript error")
case result == 0:
// More microtasks pending - continue immediately
continue
case result > 0:
// Timer pending - host can do other work before next iteration
time.Sleep(time.Duration(result) * time.Millisecond)
}
}
```
### I/O Polling
For scripts that use `os.setReadHandler()`, the host must poll for I/O:
```go
// Poll for I/O events (non-blocking)
result, _ := qjs.PollIO(ctx, 0)
// Poll with timeout (blocking up to 100ms)
result, _ := qjs.PollIO(ctx, 100)
```
See the [wazero-quickjs README](./wazero-quickjs/README.md) for more details.
## Command-Line REPL
A command-line JavaScript runner with interactive REPL mode is provided:
```bash
# Install
go install github.com/aperturerobotics/go-quickjs-wasi-reactor/wazero-quickjs/repl@master
# Interactive REPL
repl
# Run a JavaScript file
repl script.js
# Run as ES module
repl script.mjs --module
```
## Updating
To update to the latest QuickJS-NG reactor build from upstream releases:
```bash
# Download latest release automatically
./update-quickjs.bash
# Or specify a release tag
./update-quickjs.bash v0.12.1
```
This script will:
1. Fetch the latest release tag from `quickjs-ng/quickjs` (or use the provided tag)
2. Download the `qjs-wasi-reactor.wasm` release artifact
3. Generate `version.go` with version and download URL constants
A GitHub Actions workflow (`.github/workflows/update-quickjs.yml`) also runs daily to automatically check for new upstream releases and publish updated versions.
### Building the WASM file from source
To build the WASM file from source:
```bash
cd /path/to/quickjs
# Create build directory
mkdir -p build-wasi-reactor && cd build-wasi-reactor
# Configure with WASI SDK
cmake .. \
-DCMAKE_TOOLCHAIN_FILE=/opt/wasi-sdk/share/cmake/wasi-sdk.cmake \
-DQJS_WASI_REACTOR=ON \
-DCMAKE_BUILD_TYPE=Release
# Build
make -j
# Copy output
cp qjs.wasm ../qjs-wasi-reactor.wasm
```
## Testing
```bash
go test ./...
cd wazero-quickjs && go test ./...
```
## License
This module is released under the same license as the embedded QuickJS-NG project.
MIT