3.8 KiB
wasi-stdio-imports
Purpose
Builds the wasi_snapshot_preview1 imports table the WASI-stdio
runner threads into WebAssembly.instantiate. Centralises the WASI
shim implementations so the runner stays focused on protocol
orchestration; testing each WASI hook in isolation becomes a unit
test against this module rather than an integration test that needs
a hand-encoded fixture per hook.
The five hooks the bridge's ABI validator requires (fd_read,
fd_write, environ_get, environ_sizes_get, proc_exit) are
always present. Additional common WASI hooks (fd_close,
clock_time_get, random_get, path_open, fd_seek, fd_fdstat_get,
fd_prestat_get, fd_prestat_dir_name) are wired with safe defaults
so a guest that calls them does not abort instantiation. The
defaults are intentionally restrictive — path_open returns ENOENT
(2), fd_prestat_get returns BADF (8) — to keep the wasi-stdio
variant focused on its stdin/stdout protocol surface.
Features
- Single-call builder; returns a frozen-shape record ready to feed
into
WebAssembly.Instance. memoryRefthunk pattern — callers wire the imports BEFORE the instance is constructed; the thunk resolves to the bound memory once the runner has calledbindMemory.random_getuses the host'scrypto.getRandomValues, the same source as@suaveplan/api-sandbox-wasm's default snapshot.clock_time_getreturns wall-clock nanoseconds (Date.now() * 1_000_000). Test consumers wanting a deterministic clock build their own imports table.
Usage: Basic
import { buildWasiStdioImports } from
"@suaveplan/api-sandbox-wasm-host-bridge/wasi-stdio";
import { createWasiStdioStreams } from
"@suaveplan/api-sandbox-wasm-host-bridge/wasi-stdio";
const streams = createWasiStdioStreams({ pluginName: "ex" });
let memory: WebAssembly.Memory | undefined;
const imports = buildWasiStdioImports({
streams,
memoryRef: () => {
if (memory === undefined) throw new Error("memory not bound");
return memory;
},
});
void imports;
Usage: Advanced
import { buildWasiStdioImports } from
"@suaveplan/api-sandbox-wasm-host-bridge/wasi-stdio";
declare const streams: import("./wasi-stdio-streams.js").WasiStdioStreams;
declare const memory: WebAssembly.Memory;
const imports = buildWasiStdioImports({
streams,
memoryRef: () => memory,
});
// Verify the shape — every guarded WASI hook is present.
for (const name of [
"fd_read",
"fd_write",
"environ_get",
"environ_sizes_get",
"proc_exit",
"fd_close",
"clock_time_get",
"random_get",
"path_open",
"fd_seek",
] as const) {
if (typeof imports[name] !== "function") {
throw new Error(`missing import ${name}`);
}
}
API Reference
buildWasiStdioImports({ streams, memoryRef })
Returns a record keyed by WASI snapshot-preview1 function name. Every
entry is a function with the WASI ABI signature. Pass the returned
record under the wasi_snapshot_preview1 namespace when
instantiating a module.
Implementation Notes
- The
(...args: never[]) => number | undefinedsignature is the union ofproc_exit(void return) with every other entry (number return). Theas nevercast at each call site is structurally safe — WASI host imports are typed at the JS API by argument arity, not return shape. path_openreturns2(ENOENT) so a guest'spath_openrequest fails synchronously instead of trapping. Operators wanting real preopens composeextraImportson the runner.fd_prestat_getreturns8(BADF) — the host has zero preopened fds beyond stdin/stdout/stderr. The matchingfd_prestat_dir_nameis wired for symmetry; both return BADF.clock_time_getwrites to the suppliedtimePtrthen returns0; the value is little-endian as required by the WASI ABI.