// Follow-up only: original fixture, runner and result receipts remain unchanged. import {build} from 'esbuild'; import {chromium, firefox} from 'playwright'; import {createServer} from 'node:http'; import {once} from 'node:events'; import {readFile, writeFile} from 'node:fs/promises'; import {createHash} from 'node:crypto'; import {fileURLToPath} from 'node:url'; const dir = fileURLToPath(new URL('.', import.meta.url)); const hash = data => createHash('sha256').update(data).digest('hex'); const repetitions = 3; const layerPath = dir + 'node_modules/@radix-ui/react-dismissable-layer/dist/index.mjs'; const layerSource = await readFile(layerPath, 'utf8'); const expectedLayerHash = JSON.parse(await readFile(dir + 'provenance.json')).inspected_files['@radix-ui/react-dismissable-layer/dist/index.mjs'].sha256; if (hash(layerSource) !== expectedLayerHash) throw new Error('Inspected dependency changed'); const bundles = new Map(); function instrument(source, guard) { const replace = (from, to) => { if (source.split(from).length !== 2) throw new Error('Ambiguous instrumentation locator: ' + from); source = source.replace(from, to); }; replace('const isHighestLayer = node ? index === layers.length - 1 : false;', `const isHighestLayer = node ? index === layers.length - 1 : false; const trace = (kind) => window.layerTrace.push({kind, t: performance.now(), node: node?.getAttribute('aria-labelledby'), active: document.activeElement?.id, layers: [...context.layers].map(n => n.getAttribute('aria-labelledby'))});`); replace(' onEscapeKeyDown?.(event);', ` trace('escape'); ${guard ? "if ([...context.layers].at(-1) !== node) { trace('guard-rejected'); return; }" : ''} onEscapeKeyDown?.(event);`); replace(' ownerDocument.addEventListener("keydown", handleKeyDown, { capture: true });', ' trace("listen");\n ownerDocument.addEventListener("keydown", handleKeyDown, { capture: true });'); replace(' return () => ownerDocument.removeEventListener("keydown", handleKeyDown, { capture: true });', ' return () => { trace("unlisten"); ownerDocument.removeEventListener("keydown", handleKeyDown, { capture: true }); };'); replace(' context.layers.add(node);', ' context.layers.add(node); trace("layer-add");'); replace(' context.layers.delete(node);', ' trace("layer-remove"); context.layers.delete(node);'); return source; } for (const mode of ['development', 'production']) { for (const variant of ['raw', 'traced', 'ready', 'guarded']) { const b = await build({entryPoints: [dir + 'fixture.jsx'], bundle: true, write: false, define: {'process.env.NODE_ENV': JSON.stringify(mode)}, format: 'iife', plugins: variant === 'raw' ? [] : [{name: 'layer-observation', setup(build) { build.onLoad({filter: /@radix-ui\/react-dismissable-layer\/dist\/index\.mjs$/}, () => ({contents: instrument(layerSource, variant === 'guarded'), loader: 'js', resolveDir: dir + 'node_modules/@radix-ui/react-dismissable-layer/dist'})); }}]}); bundles.set(mode + '-' + variant, b.outputFiles[0].contents); } } const server = createServer((req, res) => { const u = new URL(req.url, 'http://localhost'); if (u.pathname === '/bundle.js') {res.setHeader('Content-Type','application/javascript'); res.end(bundles.get(u.searchParams.get('key')));} else {res.setHeader('Content-Type', 'text/html');res.end(`Nested isolation
`);} }); server.listen(0, '127.0.0.1');await once(server, 'listening'); const results = []; try { for (const [engine, launcher] of [['chromium', chromium], ['firefox', firefox]]) { const browser = await launcher.launch({headless: true, ...(engine === 'chromium' && process.env.FEP_CHROMIUM_PATH ? {executablePath: process.env.FEP_CHROMIUM_PATH} : {})}); try { for (const mode of ['development', 'production']) for (const variant of ['raw', 'traced', 'ready', 'guarded']) for (let trial = 1; trial <= repetitions; trial++) { const page = await browser.newPage();page.setDefaultTimeout(2000); const r = {engine, browser: browser.version(), mode, variant, trial, status: 'pass'}; try { await page.goto(`http://127.0.0.1:${server.address().port}/?mode=focus&key=${mode}-${variant}`); await page.locator('#invoker').focus();await page.keyboard.press('Enter'); await page.waitForFunction(() => document.activeElement?.id === 'remove'); await page.keyboard.press('Tab');await page.waitForFunction(() => document.activeElement?.id === 'nested-invoker'); await page.keyboard.press('Enter');await page.waitForFunction(() => document.activeElement?.id === 'nested-close'); if (variant === 'ready') await page.waitForFunction(() => { const inner = document.getElementById('nested-close').closest('[role=dialog]').getAttribute('aria-labelledby'); const listeners = new Set();for (const e of window.layerTrace) {if (e.kind === 'listen') listeners.add(e.node);if(e.kind === 'unlisten') listeners.delete(e.node);} return listeners.size === 1 && listeners.has(inner); }); r.before = await page.evaluate(() => ({active: document.activeElement.id, dialogs: [...document.querySelectorAll('[role=dialog]')].map(n => ({id: n.getAttribute('aria-labelledby'), title: document.getElementById(n.getAttribute('aria-labelledby'))?.textContent}))})); await page.keyboard.press('Escape'); await page.waitForFunction(() => document.activeElement?.id === 'nested-invoker' && document.querySelectorAll('[role=dialog]').length === 1, null, {timeout: 1500}); await page.keyboard.press('Escape'); await page.waitForFunction(() => document.activeElement?.id === 'invoker', null, {timeout: 1500}); } catch (e) {r.status = 'fail';r.error = e.message;} r.after = await page.evaluate(() => ({active: document.activeElement?.id, dialogs: document.querySelectorAll('[role=dialog]').length, transitions: window.observed.dialogTransitions, trace: window.layerTrace})); results.push(r); await page.close(); await writeFile('/tmp/fep-isolation-checkpoint.json', JSON.stringify({status: 'incomplete', results}, null, 2) + '\n'); } } finally {await browser.close();} } } finally {await new Promise(resolve => server.close(resolve));} const files = {}; for (const name of ['isolation.mjs','fixture.jsx','package.json','package-lock.json']) files[name] = hash(await readFile(dir + name)); const summary = []; for (const engine of ['chromium','firefox']) for (const mode of ['development','production']) for (const variant of ['raw','traced','ready','guarded']) { const group=results.filter(r => r.engine===engine && r.mode===mode && r.variant===variant); summary.push({engine,mode,variant,passed:group.filter(r=>r.status==='pass').length,total:group.length}); } const output = process.env.FEP_RESULT_PATH || '/tmp/fep-isolation.json'; await writeFile(output, JSON.stringify({date:new Date().toISOString(), node:process.version, repetitions, files, layerSourceSha256:hash(layerSource), bundleHashes:Object.fromEntries([...bundles].map(([k,v])=>[k,hash(v)])), summary,results},null,2)+'\n'); console.log(JSON.stringify({output,summary},null,2)); // Findings are retained, including failed obligations; not a CI acceptance gate.