WASM Target
Devlish runs in WebAssembly through a bytecode-in-WASM approach: the Devlish VM itself is compiled to WASM, and it interprets Devlish bytecode at runtime. This gives browser and Node.js embedding without per-workflow code generation.
Architecture
Section titled “Architecture”.dvl source -> Rust compiler -> .dvlc.json bytecode | v Devlish VM (Rust) -> WASM binary -> JavaScript hostThe WASM binary is a generic VM. It accepts any bytecode package and executes it. The reviewed artifact is always the .dvl source plus its inspectable bytecode, not generated JavaScript.
WASM Exports
Section titled “WASM Exports”The runner exports these functions:
| Export | Purpose |
|---|---|
devlish_init |
Initialize the VM with a bytecode package |
devlish_run |
Execute the loaded program |
devlish_resume |
Resume after a checkpoint |
devlish_result_ptr |
Get pointer to result in linear memory |
devlish_result_len |
Get length of result data |
devlish_free |
Free allocated memory |
Host Imports
Section titled “Host Imports”The WASM module imports callbacks from the JavaScript host:
| Import | Signature | Purpose |
|---|---|---|
emit_event |
(ptr, len) |
Send execution events to the host |
request_input |
(ptr, len) |
Request user input (for Ask) |
write_file |
(ptr, len) |
Request file write from host |
Values cross the WASM boundary as JSON strings in linear memory. This is not the final ABI, but it enables full integration without blocking on the WebAssembly Component Model.
devlish-runtime npm Package
Section titled “devlish-runtime npm Package”The devlish-runtime package wraps the low-level WASM interface with an ergonomic JavaScript API:
import { loadTool, runTool } from "devlish-runtime";
// One-shotconst result = await runTool(bytecode, { amount: 1200 });
// Reusable (Web Worker)const tool = await loadTool({ bytecode });const r1 = await tool.run({ project: "A" });const r2 = await tool.run({ project: "B" });tool.dispose();Base64 Inlining Strategy
Section titled “Base64 Inlining Strategy”The WASM binary is inlined as base64 within the npm package. This ensures:
- Zero-config bundler compatibility (webpack, vite, esbuild)
- No separate
.wasmfile to serve or configure - Works with CDN imports (
esm.sh,unpkg,skypack) - No async WASM loading path required in simple cases
The tradeoff is a larger package size, acceptable for the current VM binary.
Permission Check at Load Time
Section titled “Permission Check at Load Time”When loadTool receives bytecode with a manifest, it checks permissions before creating the VM:
const tool = await loadTool({ bytecode: programWithHttpPermission });// Throws: "Tool requires HTTP requests permission, which is not available in the WASM sandbox"The WASM sandbox cannot perform HTTP requests or filesystem operations. Tools that declare these permissions are rejected immediately, preventing confusing runtime errors.
Browser and Node Compatibility
Section titled “Browser and Node Compatibility”The same package works in all JavaScript environments:
| Environment | Worker Support | Notes |
|---|---|---|
| Browser | Web Worker | Default async execution |
| Node.js | worker_threads | Same API |
| Deno | Web Worker | Standard imports |
| Cloudflare Workers | Main thread only | Use mainThread: true |
| Bun | worker_threads | Same as Node |
Building the WASM Runner
Section titled “Building the WASM Runner”From the Devlish repo:
./scripts/build_wasm_runner.shThis compiles the devlish_wasm_runner crate to wasm32-unknown-unknown and produces the binary that gets base64-inlined into the npm package.
Direct WASM Access
Section titled “Direct WASM Access”For advanced use cases, the low-level wrapper is available at crates/devlish_wasm_runner/js/index.mjs:
import { loadDevlishWorkflow } from "./index.mjs";
const workflow = await loadDevlishWorkflow({ wasmUrl: "/devlish_runner.wasm", bytecodeUrl: "/program.dvlc.json", host: { writeFile: req => { files.push(req); return true; }, emitEvent: evt => events.push(evt) }});
const result = await workflow.run({ input_field: "value" });Most users should use devlish-runtime instead.