Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116// Real wasm32-wasip1 programs (tests/fixtures/wasi/src/*.rs, tests/fixtures/wasi/*.wasm)// run through our node:wasi and checked against output recorded from real Node 24.16// under the identical options (see the worktree's scratch probes for how each golden// was captured: `node run.cjs <wasm> <args...>` with the pinned Node on PATH).import { readFileSync } from "node:fs";import { test, expect, type Page } from "@playwright/test";import type { Kernel } from "../src/host/index";
declare global { interface Window { __runtime?: Kernel } }
function wasmBytes(name: string): Uint8Array { return new Uint8Array(readFileSync(`tests/fixtures/wasi/${name}`));}
interface RunResult { code: number; stdout: string; stdoutBytes: number[]; stderr: string }
async function runWasi(page: Page, wasmName: string, main: string[], files: Record<string, string> = {}, stdin = ""): Promise<RunResult> { await page.goto("/demo/fixture.html"); await page.waitForFunction(() => window.__runtime !== undefined, undefined, { timeout: 20_000 }); const wasm = wasmBytes(wasmName); return await page.evaluate(async ({ wasm, main, files, wasmName, stdin }) => { const runtime = window.__runtime!; runtime.writeTree({ [`/app/${wasmName}`]: wasm, "/app/main.cjs": main.join("\n"), ...files }); const proc = runtime.exec("/app/main.cjs", { cwd: "/app" }); const writer = proc.stdin.getWriter(); if (stdin) void writer.write(new TextEncoder().encode(stdin)); void writer.close(); const dec = new TextDecoder(); const stdoutBytes: number[] = []; let stderr = ""; await Promise.all([ proc.stdout.pipeTo(new WritableStream({ write: (c) => { stdoutBytes.push(...c); } })), proc.stderr.pipeTo(new WritableStream({ write: (c) => { stderr += dec.decode(c); } })), ]); return { code: await proc.exited, stdout: dec.decode(new Uint8Array(stdoutBytes)), stdoutBytes, stderr }; }, { wasm, main, files, wasmName, stdin });}
const RUN_PREAMBLE = (wasmName: string, optionsJson: string) => [ 'const { WASI } = require("node:wasi");', `const wasm = require("fs").readFileSync("/app/${wasmName}");`, `const wasi = new WASI({ version: "preview1", returnOnExit: true, ...${optionsJson} });`, "const mod = new WebAssembly.Module(wasm);", "const inst = new WebAssembly.Instance(mod, wasi.getImportObject());", "const code = wasi.start(inst);", // fs.writeSync(1, ...) isn't a generic path this kernel supports outside the // process.stdout object (only our own fd_write routes raw bytes there directly); // console.log goes through the supported path for this marker. "console.log(`__EXIT__${code}`);",];
test("args_get/environ_get match real Node's encoding and ordering", async ({ page }) => { const res = await runWasi(page, "args_env.wasm", RUN_PREAMBLE("args_env.wasm", '{ args: ["/app/args_env.wasm", "hello", "world"], env: { FOO: "bar", BAZ: "qux" } }')); // Recorded from real Node 24.16: `node run.cjs fixtures/args_env.wasm x // '{"args":["hello","world"],"env":{"FOO":"bar","BAZ":"qux"}}'` (run.cjs prepends the // wasm path itself as args[0], matching what we pass here). expect(res.stdout).toBe("args: /app/args_env.wasm|hello|world\nenv: BAZ=qux|FOO=bar\n__EXIT__0\n"); expect(res.code).toBe(0);});
test("file create/read/seek/rename/readdir/remove under a preopen match real Node", async ({ page }) => { const res = await runWasi(page, "fs_ops.wasm", RUN_PREAMBLE("fs_ops.wasm", '{ preopens: { ".": "/app" } }')); // Recorded from real Node 24.16 with cwd = the fixture's own directory and the same // preopen shape (".": cwd). expect(res.stdout).toBe("read: hello world\nseek-read: world\nsize: 11\ndir: b.txt\ndone\n__EXIT__0\n"); expect(res.code).toBe(0);});
test("fd_write is raw and unbuffered: no trailing newline, embedded NUL and non-UTF8 bytes survive", async ({ page }) => { const res = await runWasi(page, "binary_stdio.wasm", RUN_PREAMBLE("binary_stdio.wasm", "{}")); // Real Node 24.16's stdout, hex-dumped from a file: "no-newline-here", then bytes // 00 01 02 ff fe, then the exit marker, with no newline inserted or byte decoded. const enc = new TextEncoder(); expect(res.stdoutBytes).toEqual([...enc.encode("no-newline-here"), 0, 1, 2, 255, 254, ...enc.encode("__EXIT__0\n")]); expect(res.stderr).toContain("err-no-newline"); expect(res.stderr.startsWith("err-no-newline")).toBe(true); // no inserted newline before the ExperimentalWarning either expect(res.code).toBe(0);});
test("path escape above a preopen is rejected (ENOTCAPABLE), matching real Node", async ({ page }) => { const res = await runWasi(page, "escape.wasm", RUN_PREAMBLE("escape.wasm", '{ preopens: { ".": "/app" } }')); // Real Node 24.16's whole stdout (preopens: {".": dir}, no canary.txt in it): both // escapes raise ENOTCAPABLE (76); the in-sandbox lookup is ENOENT (44). expect(res.stdout).toBe("../canary.txt: err raw_os_error=Some(76)\ncanary.txt: err raw_os_error=Some(44)\n../../canary.txt: err raw_os_error=Some(76)\n__EXIT__0\n"); expect(res.code).toBe(0);});
test("fd_read on stdin sees data piped in before the guest starts (closed, then read to EOF)", async ({ page }) => { const res = await runWasi(page, "stdin.wasm", RUN_PREAMBLE("stdin.wasm", "{}"), {}, "hi there"); // Recorded from real Node 24.16: `echo -n "hi there" | node run.cjs fixtures/stdin.wasm`. // This is the one syscall this worktree's WASI can't fully match uvwasi on: a read // issued before any data has arrived returns 0 bytes instead of blocking for more, // since stdin is delivered by postMessage rather than the Atomics mailbox fs reads // use (see wasi.ts's module comment). Piping-then-closing before the guest starts, as // this test and every other guest-stdin test in this suite already do, sidesteps // that gap entirely: the data is in the inbox before fd_read is ever called. expect(res.stdout).toBe('got: 8 bytes: "hi there"\n__EXIT__0\n'); expect(res.code).toBe(0);});
test("fd_fdstat_set_rights rejects escalating past an fd's own rights (ENOTCAPABLE)", async ({ page }) => { // tests/fixtures/wasi/src/fdstat-rights-escalation.wat calls // fd_fdstat_set_rights(1, -1, -1) then proc_exit(result). Recorded from real Node // 24.16: `node run.cjs fdstat-rights-escalation.wasm` exits 76 (ENOTCAPABLE). const res = await runWasi(page, "fdstat-rights-escalation.wasm", [ 'const { WASI } = require("node:wasi");', 'const wasm = require("fs").readFileSync("/app/fdstat-rights-escalation.wasm");', 'const wasi = new WASI({ version: "preview1", returnOnExit: true });', "const mod = new WebAssembly.Module(wasm);", "const inst = new WebAssembly.Instance(mod, wasi.getImportObject());", "process.exit(wasi.start(inst));", ]); expect(res.code).toBe(76);});