first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user