Files
skills/docs/api-stack/api/wasm-compiler-javy/src/validate/abi-validate.md
2026-05-29 08:20:56 +01:00

3.4 KiB

validateAbi — post-Javy module reflection

Purpose

validateAbi is step 5 of the TS->WASM compilation pipeline: it runs WebAssembly.compile(bytes) against Javy's output and reflects the resulting module to assert the WASI-stdio variant's surface contract. The contract has two halves: required exports (memory + _start) and required imports (wasi_snapshot_preview1.{fd_read, fd_write, environ_get, environ_sizes_get, proc_exit}).

Mirroring the host bridge's REQUIRED_WASI_STDIO_IMPORTS list intentionally: the bridge instantiates the module against its own snapshot of those imports, and any future divergence between the lists would cause one half of the system to ship validations the other cannot satisfy. Single-sourcing the list in spec ensures the bridge and compiler stay in lock-step; this module hard-codes its own copy because cycling the import via @suaveplan/api-sandbox-wasm-host-bridge would pull a 30+ file graph into the compiler's static surface.

Features

  • One-shot WebAssembly.compile returns both the validation verdict AND a reusable Module — the orchestrator threads the same Module into the route-extract step so we never compile twice.
  • Failure mode compile_failed (WebAssembly.compile threw) is separated from ABI_VALIDATION_FAILED (compile succeeded, exports/imports incomplete) via context.reason.
  • Per-missing-item recordAbiFailure increments thread through the optional instruments so dashboards can split the failure rate by which symbol vanished — useful when a Javy upgrade silently drops clock_time_get or similar.
  • Missing list is sorted for diagnostic stability.

Usage: Basic

import { validateAbi } from "@suaveplan/wasm-compiler-javy";
import { readFile } from "node:fs/promises";

const bytes = await readFile("/tmp/plugin.wasm");
const { module } = await validateAbi({ wasmBytes: bytes });

Usage: Advanced

import { validateAbi, createWasmCompilerInstruments } from "@suaveplan/wasm-compiler-javy";

const instr = createWasmCompilerInstruments();
try {
    const { module } = await validateAbi({ wasmBytes, instruments: instr });
    // forward `module` to route-extract
} catch (err) {
    // `recordAbiFailure` has already counted the missing exports for the dashboard.
    throw err;
}

API Reference

  • validateAbi(options: ValidateAbiOptions): Promise<ValidateAbiResult> — entrypoint.
  • ValidateAbiOptions: { wasmBytes: Uint8Array; instruments?: WasmCompilerInstruments }.
  • ValidateAbiResult: { module: WebAssembly.Module }.
  • REQUIRED_WASI_IMPORTS / REQUIRED_EXPORTS — frozen string-array registries.

Implementation Notes

WebAssembly.compile is the only piece of the validator that crosses the JS/WASM boundary. The reflection methods WebAssembly.Module.imports(module) and WebAssembly.Module.exports(module) are pure JS — they walk parsed metadata without touching memory or invoking the runtime. The compiled Module is opaque to the validator but the orchestrator hands it to the route-extract step, which instantiates it against a host-built WASI snapshot to fish the routes out. The implementation deliberately does NOT cycle through @suaveplan/api-sandbox-wasm-host-bridge so the compiler's static graph stays compact; the bridge re-validates anyway when its buildWasiStdioInstall runs, so a one-pass divergence cannot leak past the host's installPlugin call.