// Browser leg of statevector-equivalence. // // The native and `wasm32-wasip2` legs are checked by `wai_quantum_vectors verify`. // This checks the third surface the extension claims — a browser — against the // same pinned corpus, so all three are held to one reference rather than to each // other. // // node browser_verify.mjs [path/to/quantum-ml/pkg] // // Needs Playwright with its bundled Chromium. `statevector_hash_hex()` is empty // until `run()` is called: the circuit is built eagerly, the state is not. import http from "node:http"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { createRequire } from "node:module"; const HERE = path.dirname(fileURLToPath(import.meta.url)); const PKG = process.argv[2] || "/Users/dcharlot/data-share/vibe-coding/wai-transaction-science-web/public/quantum-ml/pkg"; const PLAYWRIGHT_HOST = process.env.PLAYWRIGHT_HOST || "/Users/dcharlot/data-share/vibe-coding/joulesperbit/"; const { chromium } = createRequire(PLAYWRIGHT_HOST)("playwright"); const EXPECTED = JSON.parse(fs.readFileSync(path.join(HERE, "expected_hashes.json"), "utf8")); // vector name -> how the catalog builds that circuit const CASES = [ ["bell.wqc", "bell", 2, 0], ["ghz3.wqc", "ghz", 3, 0], ["qft3.wqc", "qft", 3, 0], ["qft5.wqc", "qft", 5, 0], ]; const MIME = { ".js": "text/javascript", ".wasm": "application/wasm", ".html": "text/html" }; const PORT = Number(process.env.PORT || 8794); const server = http.createServer((req, res) => { const url = req.url.split("?")[0]; if (url === "/") { res.writeHead(200, { "content-type": "text/html" }); return res.end('conformanceready'); } const f = path.join(PKG, path.basename(url)); if (!fs.existsSync(f)) { res.writeHead(404); return res.end("not found"); } res.writeHead(200, { "content-type": MIME[path.extname(f)] || "application/octet-stream" }); fs.createReadStream(f).pipe(res); }); let browser; process.on("unhandledRejection", (e) => { console.error("UNHANDLED:", e); process.exitCode = 1; }); try { if (!fs.existsSync(path.join(PKG, "wai_quantum_ml.js"))) { console.error(`no bundle at ${PKG} — pass the pkg directory as argv[1]`); process.exit(2); } await new Promise((r) => server.listen(PORT, "127.0.0.1", r)); browser = await chromium.launch(); const page = await browser.newPage(); const errs = []; page.on("console", (m) => { if (m.type() === "error") errs.push(m.text()); }); page.on("pageerror", (e) => errs.push(String(e))); // domcontentloaded, never networkidle: the page holds no long-lived connections // but networkidle still stalls behind them on some hosts. await page.goto(`http://127.0.0.1:${PORT}/`, { waitUntil: "domcontentloaded", timeout: 30000 }); const got = await page.evaluate(async ({ cases, port }) => { const mod = await import(`http://127.0.0.1:${port}/wai_quantum_ml.js`); await mod.default(); const out = {}; for (const [name, id, n, param] of cases) { const ide = new mod.QuantumIde(n); const ok = ide.load_algorithm(id, n, param); if (ok) ide.run(); // the state is not computed until this out[name] = ok ? { circuit: ide.circuit_hash_hex(), statevector: ide.statevector_hash_hex() } : { error: "load_algorithm returned false" }; ide.free(); } return out; }, { cases: CASES, port: PORT }); let fail = 0; for (const [name] of CASES) { const e = EXPECTED[name], g = got[name]; if (g.circuit === e.circuit_hash && g.statevector === e.statevector_hash) { console.log(`PASS ${name}`); } else { fail++; console.log(`FAIL ${name}`); console.log(` circuit browser=${g.circuit}\n corpus =${e.circuit_hash}`); console.log(` statevector browser=${g.statevector}\n corpus =${e.statevector_hash}`); } } console.log(errs.length ? `console errors: ${errs.length}` : "console errors: 0"); if (fail === 0 && errs.length === 0) { console.log("all vectors pass: statevector-equivalence holds in the browser"); } process.exitCode = fail || errs.length ? 1 : 0; } catch (e) { console.error("HARNESS ERROR:", e); process.exitCode = 1; } finally { if (browser) await browser.close(); server.close(); }