// 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();
}