#!/usr/bin/env node // eval_wasm_probes.js — W14/F8: evaluate a file of probe expressions (one // per line, '#'-comments allowed) on the SHIPPED browser kernel and print // PROBE // per line, for diffing against the native server (scripts/test-differential.sh). // Boot stubs mirror test_wasm_native.js / run_wasm_corpus.js. const fs = require('fs'); const path = require('path'); const PROJECT_ROOT = path.resolve(__dirname, '../../..'); const WASM_DIR = path.join(PROJECT_ROOT, 'shared/static/wasm'); const probeFile = process.argv[2]; if (!probeFile) { console.error('usage: eval_wasm_probes.js '); process.exit(2); } global.window = global; global.document = { createElement: () => ({ style: {}, setAttribute() {}, appendChild() {}, children: [] }), createDocumentFragment: () => ({ appendChild() {}, children: [], childNodes: [] }), head: { appendChild() {} }, body: { appendChild() {} }, querySelector: () => null, querySelectorAll: () => [], createTextNode: s => ({ textContent: s }), addEventListener() {}, createComment: s => ({ textContent: s || '' }), getElementsByTagName: () => [], }; global.localStorage = { getItem: () => null, setItem() {}, removeItem() {} }; global.CustomEvent = class { constructor(n, o) { this.type = n; this.detail = (o || {}).detail || {}; } }; global.MutationObserver = class { observe() {} disconnect() {} }; global.requestIdleCallback = fn => setTimeout(fn, 0); global.matchMedia = () => ({ matches: false }); global.navigator = { serviceWorker: { register: () => Promise.resolve() } }; global.location = { href: '', pathname: '/', hostname: 'localhost' }; global.history = { pushState() {}, replaceState() {} }; global.fetch = () => Promise.resolve({ ok: true, text: () => Promise.resolve('') }); async function main() { require(path.join(WASM_DIR, 'sx_browser.bc.wasm.js')); const K = await new Promise((resolve, reject) => { let tries = 0; const poll = setInterval(() => { if (globalThis.SxKernel) { clearInterval(poll); resolve(globalThis.SxKernel); } else if (++tries > 200) { clearInterval(poll); reject(new Error('SxKernel not found')); } }, 50); }); const lines = fs.readFileSync(probeFile, 'utf8').split('\n'); let n = 0; for (const raw of lines) { const line = raw.trim(); if (!line || line.startsWith('#')) continue; n++; let out; try { // Serialize through the kernel's own printer so both hosts emit SX // text (K.eval returns raw JS values otherwise — [object Object]). const r = K.eval(`(sx-serialize ${line})`); out = (typeof r === 'string') ? r : String(r); } catch (e) { out = 'ERROR'; } // errors normalized: kernel returns "Error: ..." strings for eval errors if (typeof out === 'string' && out.startsWith('Error')) out = 'ERROR'; console.log(`PROBE ${n} ${out.replace(/\n/g, '\\n')}`); } } main().catch(e => { console.error('FATAL:', e.message); process.exit(1); });