first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
# `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.