#!/usr/bin/env node /** * check-research-gallery-embed.mjs — the cap-5 Research Gallery () must embed on * SOMEONE ELSE'S page, in both embed modes, with every nested map painting. * * WHAT THIS PROTECTS. The gallery is going onto the DX-life research page. Every other gate loads * the gallery on its OWN generated leaf page, which bakes the three things a nested map needs — * window.MAP_APP_API_URL, window.MAP_TOKEN and a correct assets path — and pins the tag * position:fixed. A foreign page supplies none of that, so those gates cannot see the embed failing. * Measured 2026-09-24 before the fix, from a second origin: the gallery drew OVER the host's header * (the element had no :host box, so the layout's position:absolute root anchored to the viewport), * and 8 of 13 tabs showed an error card because every data call hit the host's origin. * * HOW. Serves the built tree with internal/serve-strict.mjs (production parity: strict directories, * ACAO:*) on 127.0.0.1, and a stand-in host page on `localhost` — a DIFFERENT ORIGIN, so CORS is * genuinely exercised. The host page has its own header and footer around the embed. Then, per mode: * js — `` return `host
HOST HEADER
${embed}
` } const tree = spawn(process.execPath, ['internal/serve-strict.mjs', '--port', String(TREE_PORT)], { cwd: ROOT, stdio: 'ignore' }) const host = createServer((_, r) => { r.writeHead(200, { 'content-type': 'text/html' }); r.end(hostPage()) }).listen(HOST_PORT, 'localhost') const cleanup = () => { try { tree.kill() } catch {} try { host.close() } catch {} } await new Promise(r => setTimeout(r, 800)) const browser = await chromium.launch({ args: ['--enable-unsafe-swiftshader', '--use-gl=angle', '--use-angle=swiftshader'] }) const fails = [] try { for (mode of MODES) { const page = await browser.newPage({ viewport: { width: 1400, height: 900 } }) const own = new Set(), foreign = new Set() const note = (url, why) => (url.startsWith(`http://127.0.0.1:${TREE_PORT}/`) ? own : foreign).add(why + ' ' + url.split('?')[0]) page.on('response', r => { if (r.status() >= 400) note(r.url(), String(r.status())) }) page.on('requestfailed', q => { const e = (q.failure() && q.failure().errorText) || ''; if (!/ABORTED/.test(e)) note(q.url(), e) }) await page.goto(`http://localhost:${HOST_PORT}/`) const frame = mode === 'iframe' ? await (await page.waitForSelector('iframe')).contentFrame() : page.mainFrame() await frame.waitForFunction(() => { const s = document.querySelector('doxa-maps-spa'); return s && s._instance && s._instance.proxy.tabs.some(t => t.resolved) }, null, { timeout: 30000 }) if (mode === 'js') { const g = await page.evaluate(() => { const s = document.querySelector('doxa-maps-spa'), v = s.shadowRoot.querySelector('.vtl') const a = s.getBoundingClientRect(), b = v.getBoundingClientRect() return { host: [a.top, a.left, a.width, a.height].map(Math.round), vtl: [b.top, b.left, b.width, b.height].map(Math.round) } }) const inside = g.vtl[0] >= g.host[0] - 1 && g.vtl[1] >= g.host[1] - 1 && g.vtl[2] <= g.host[2] + 1 && g.vtl[3] <= g.host[3] + 1 && g.host[3] >= 400 console.log(`${inside ? 'PASS' : 'FAIL'} [js] layout inside the host box — host ${g.host} layout ${g.vtl}`) if (!inside) fails.push(`[js] layout escaped the box (host ${g.host}, layout ${g.vtl})`) } const tabs = await frame.evaluate(() => document.querySelector('doxa-maps-spa')._instance.proxy.tabs.map(t => ({ id: t.id, discovered: !!t.discovered }))) for (const { id, discovered } of tabs) { if (ONLY && !ONLY.includes(id)) continue await frame.evaluate(id => document.querySelector('doxa-maps-spa')._instance.proxy.onSelect(id), id) let v for (let k = 0; k < 50; k++) { await page.waitForTimeout(400) v = await frame.evaluate(id => { const vm = document.querySelector('doxa-maps-spa')._instance.proxy const i = vm.tabs.findIndex(x => x.id === id), t = vm.tabs[i] const el = vm._slots && vm._slots[i] && vm._slots[i].firstElementChild let canvas = 0 const walk = r => { for (const n of r.querySelectorAll('*')) { if (n.tagName === 'CANVAS') canvas++; if (n.shadowRoot) walk(n.shadowRoot) } } if (el) { walk(el); if (el.shadowRoot) walk(el.shadowRoot) } return { state: t.state, error: t.error, canvas } }, id) if (v.state === 'error' || (v.state === 'ready' && (v.canvas || discovered && k > 5))) break } const needCanvas = !discovered const ok = v.state === 'ready' && (!needCanvas || v.canvas > 0) const why = v.state !== 'ready' ? `state ${v.state}${v.error ? ' — ' + v.error : ''}` : (needCanvas && !v.canvas ? 'mounted but painted no map canvas' : '') console.log(`${ok ? 'PASS' : 'FAIL'} [${mode}] ${id}${why ? ' — ' + why : ''}`) if (!ok) fails.push(`[${mode}] ${id}: ${why}`) } for (const f of own) { console.log(`FAIL [${mode}] tree request failed: ${f}`); fails.push(`[${mode}] ${f}`) } for (const f of foreign) console.log(`note [${mode}] third-party request failed (not gated): ${f}`) await page.close() } } finally { await browser.close(); cleanup() } if (fails.length) { console.log(`\ncheck-research-gallery-embed: FAIL (${fails.length})`); for (const f of fails) console.log(' - ' + f) process.exit(1) } console.log('\ncheck-research-gallery-embed: PASS — the Research Gallery embeds on a foreign origin in ' + MODES.join(' + ') + ' mode')