// Debug probe: isolate which host-fs RPCs hang, bypassing the UI entirely via // the window.__runtime debug hook. Not an acceptance test — a diagnostic. // node scripts/probe-fs-rpc.mjs (starts its own server on :8932) import { createServer } from "node:http"; import { readFile } from "node:fs/promises"; import { extname, join, normalize, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { chromium } from "playwright"; const ROOT = resolve(fileURLToPath(new URL("..", import.meta.url))); const PORT = 8932; const MIME = { ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".mjs": "text/javascript; charset=utf-8", ".cjs": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".txt": "text/plain; charset=utf-8", ".json": "application/json", ".wasm": "application/wasm", }; const server = createServer(async (req, res) => { let path = decodeURIComponent(new URL(req.url ?? "/", `http://localhost:${PORT}`).pathname); if (path === "/") path = "/demo/index.html"; try { const data = await readFile(join(ROOT, normalize(path).replace(/^(\.\.[/\\])+/, ""))); res.writeHead(200, { "content-type": MIME[extname(path)] ?? "application/octet-stream", "cross-origin-opener-policy": "same-origin", "cross-origin-embedder-policy": "require-corp", }); res.end(data); } catch { res.writeHead(404).end(); } }); await new Promise((r) => server.listen(PORT, r)); console.log(`probe server on :${PORT}`); const browser = await chromium.launch(); const page = await browser.newPage(); const logs = []; page.on("console", (m) => logs.push(`[console.${m.type()}] ${m.text()}`)); page.on("pageerror", (e) => logs.push(`[pageerror] ${e.message}`)); await page.goto(`http://localhost:${PORT}/demo/harness.html`); await page.waitForSelector('#tree [data-path="/app"]', { timeout: 20_000 }); console.log("harness booted"); const withTimeout = (p, ms, label) => Promise.race([p.then(() => `${label}: RESOLVED`, (e) => `${label}: REJECTED ${e?.message ?? e}`), new Promise((r) => setTimeout(() => r(`${label}: HUNG (>${ms}ms)`), ms))]); console.log(await page.evaluate(async ({ withTimeoutSrc }) => { const out = []; const withTimeout = eval(withTimeoutSrc); const runtime = window.__runtime; out.push(await withTimeout(runtime.fs.writeFile("/app/probe-empty.txt", ""), 4000, "writeFile empty")); out.push(await withTimeout(runtime.fs.writeFile("/app/probe-full.txt", "hello"), 4000, "writeFile non-empty")); out.push(await withTimeout(runtime.fs.mkdir("/app/probe-dir", { recursive: true }), 4000, "mkdir")); out.push(await withTimeout(runtime.fs.rm("/app/probe-dir", { recursive: true }), 4000, "rm dir recursive")); out.push(await withTimeout(runtime.fs.writeFile("/app/probe2.txt", ""), 4000, "writeFile empty #2")); out.push(await withTimeout(runtime.fs.rm("/app/probe-empty.txt"), 4000, "rm file")); return out; }, { withTimeoutSrc: withTimeout.toString() })); console.log("--- page logs ---"); for (const l of logs) console.log(l); await browser.close(); server.close();