frontend-patterns/research/experiments/boundary-recovery/isolation.mjs
tegwick 85ae4f62af Isolate nested dismissal ownership across browsers and builds
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-26 22:35:12 +02:00

91 lines
7.4 KiB
JavaScript

// 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(`<!doctype html><html lang="en"><title>Nested isolation</title><body><div id="root"></div><script>window.layerTrace=[];</script><script src="/bundle.js?key=${u.searchParams.get('key')}"></script></body></html>`);}
});
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.