# `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`. - `memoryRef` thunk pattern — callers wire the imports BEFORE the instance is constructed; the thunk resolves to the bound memory once the runner has called `bindMemory`. - `random_get` uses the host's `crypto.getRandomValues`, the same source as `@suaveplan/api-sandbox-wasm`'s default snapshot. - `clock_time_get` returns wall-clock nanoseconds (`Date.now() * 1_000_000`). Test consumers wanting a deterministic clock build their own imports table. ## Usage: Basic ```ts 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 ```ts 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 | undefined` signature is the union of `proc_exit` (void return) with every other entry (number return). The `as never` cast at each call site is structurally safe — WASI host imports are typed at the JS API by argument arity, not return shape. - `path_open` returns `2` (`ENOENT`) so a guest's `path_open` request fails synchronously instead of trapping. Operators wanting real preopens compose `extraImports` on the runner. - `fd_prestat_get` returns `8` (`BADF`) — the host has zero preopened fds beyond stdin/stdout/stderr. The matching `fd_prestat_dir_name` is wired for symmetry; both return BADF. - `clock_time_get` writes to the supplied `timePtr` then returns `0`; the value is little-endian as required by the WASI ABI.