diff --git a/scripts/lib/browser-module-e2e.mjs b/scripts/lib/browser-module-e2e.mjs index 2bb656adb..5c6a154f3 100644 --- a/scripts/lib/browser-module-e2e.mjs +++ b/scripts/lib/browser-module-e2e.mjs @@ -14,36 +14,42 @@ import { webModuleTraceResultSchema } from "../../dist/domain/webModuleTrace.js" import { NativeModuleResolver } from "../../dist/browser/modules/NativeModuleResolver.js"; import { startBrowserModuleSite } from "../fixtures/browser-module-site.mjs"; import { mcpTextValue } from "./mcp-verifier-results.mjs"; +import { closeRuntimeFixtureResources } from "./browser-runtime-fixture-lifecycle.mjs"; /** Check native URL resolution against an independently loaded module graph through CLI/MCP. */ export async function verifyBrowserModules( executable, entrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url)), ) { - const site = await startBrowserModuleSite(); - const root = await mkdtemp(join(tmpdir(), "rea-browser-modules-")); - const env = { - ...process.env, - REA_LOG_LEVEL: "silent", - REA_BROWSER_EXECUTABLE: executable, - HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper", - }; - const cli = async (...args) => { - const { stdout } = await promisify(execFile)( - process.execPath, - [entrypoint, ...args, "--json"], - { env, timeout: 45000, maxBuffer: 16 * 1024 * 1024 }, - ); - return parseEvidence(JSON.parse(stdout)); - }; - const transport = new StdioClientTransport({ - command: process.execPath, - args: [entrypoint, "mcp"], - env, - stderr: "pipe", - }); - const client = new Client({ name: "module-real-e2e", version: "1" }); + let site; + let root; + let transport; + let client; + let primaryError; try { + site = await startBrowserModuleSite(); + root = await mkdtemp(join(tmpdir(), "rea-browser-modules-")); + const env = { + ...process.env, + REA_LOG_LEVEL: "silent", + REA_BROWSER_EXECUTABLE: executable, + HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper", + }; + const cli = async (...args) => { + const { stdout } = await promisify(execFile)( + process.execPath, + [entrypoint, ...args, "--json"], + { env, timeout: 45000, maxBuffer: 16 * 1024 * 1024 }, + ); + return parseEvidence(JSON.parse(stdout)); + }; + transport = new StdioClientTransport({ + command: process.execPath, + args: [entrypoint, "mcp"], + env, + stderr: "pipe", + }); + client = new Client({ name: "module-real-e2e", version: "1" }); const oracle = await loadingOracle(executable, site); const scenario = { browser: { mode: "launch", executable_path: executable }, @@ -298,11 +304,17 @@ export async function verifyBrowserModules( map_identity_verified: true, implicit_refetches: 0, }; + } catch (cause) { + primaryError = cause; + throw cause; } finally { - await client.close(); - await transport.close(); - await site.close(); - await rm(root, { recursive: true, force: true }); + const cleanup = []; + if (client !== undefined) cleanup.push(() => client.close()); + if (transport !== undefined) cleanup.push(() => transport.close()); + if (site !== undefined) cleanup.push(() => site.close()); + if (root !== undefined) + cleanup.push(() => rm(root, { recursive: true, force: true })); + await closeRuntimeFixtureResources(cleanup, primaryError); } } diff --git a/scripts/lib/browser-network-e2e.mjs b/scripts/lib/browser-network-e2e.mjs index 4cf369f63..33aad95c7 100644 --- a/scripts/lib/browser-network-e2e.mjs +++ b/scripts/lib/browser-network-e2e.mjs @@ -14,6 +14,7 @@ import { } from "../fixtures/browser-network-site.mjs"; import { assertBrowserNetworkEvidence } from "./browser-network-assertions.mjs"; import { scenarioProfiles } from "./browser-scenario-verifier.mjs"; +import { closeRuntimeFixtureResources } from "./browser-runtime-fixture-lifecycle.mjs"; /** Exercise selected network content through real CLI and stdio MCP, one browser at a time. */ export async function verifyBrowserNetworkEvidence( @@ -21,9 +22,14 @@ export async function verifyBrowserNetworkEvidence( entrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url)), attachment, ) { - const site = await startBrowserNetworkSite(); - const profilesBefore = await scenarioProfiles(); + let site; + let profilesBefore; + let transport; + let client; + let primaryError; try { + site = await startBrowserNetworkSite(); + profilesBefore = await scenarioProfiles(); const scenario = browserScenarioSchema.parse({ browser: { mode: "launch", executable_path: executable }, start_url: { url: site.origin }, @@ -97,58 +103,50 @@ export async function verifyBrowserNetworkEvidence( () => assertBrowserNetworkEvidence(swapped, site.origin), /associated with a different same-URL request/, ); - const transport = new StdioClientTransport({ + transport = new StdioClientTransport({ command: process.execPath, args: [entrypoint, "mcp"], env, stderr: "pipe", }); - const client = new Client({ name: "network-real-e2e", version: "1" }); + client = new Client({ name: "network-real-e2e", version: "1" }); let mcpProof; - try { - await client.connect(transport); - const result = await client.callTool( - { name: "capture_browser_scenario", arguments: scenario }, + await client.connect(transport); + const result = await client.callTool( + { name: "capture_browser_scenario", arguments: scenario }, + { timeout: 60_000 }, + ); + assert.notEqual(result.isError, true, JSON.stringify(result)); + const evidence = parseEvidence(result.structuredContent); + mcpProof = assertBrowserNetworkEvidence( + browserScenarioCaptureSchema.parse(evidence.normalized_result), + site.origin, + ); + if (attachment !== undefined) { + const connectedScenario = browserScenarioSchema.parse({ + ...scenario, + browser: { mode: "connect", ...attachment }, + }); + const connected = await client.callTool( + { name: "capture_browser_scenario", arguments: connectedScenario }, { timeout: 60_000 }, ); - assert.notEqual(result.isError, true, JSON.stringify(result)); - const evidence = parseEvidence(result.structuredContent); - mcpProof = assertBrowserNetworkEvidence( - browserScenarioCaptureSchema.parse(evidence.normalized_result), - site.origin, - ); - if (attachment !== undefined) { - const connectedScenario = browserScenarioSchema.parse({ - ...scenario, - browser: { mode: "connect", ...attachment }, - }); - const connected = await client.callTool( - { name: "capture_browser_scenario", arguments: connectedScenario }, - { timeout: 60_000 }, - ); - assert.notEqual(connected.isError, true, JSON.stringify(connected)); - const connectedEvidence = parseEvidence(connected.structuredContent); - assert.deepEqual( - assertBrowserNetworkEvidence( - browserScenarioCaptureSchema.parse( - connectedEvidence.normalized_result, - ), - site.origin, - "disconnected-external", + assert.notEqual(connected.isError, true, JSON.stringify(connected)); + const connectedEvidence = parseEvidence(connected.structuredContent); + assert.deepEqual( + assertBrowserNetworkEvidence( + browserScenarioCaptureSchema.parse( + connectedEvidence.normalized_result, ), - cliProof, - ); - assert.ok( - (await fetch(`${attachment.cdp_endpoint}/json/version`)).ok, - "Network capture closed the external browser", - ); - } - } finally { - try { - await client.close(); - } finally { - await transport.close(); - } + site.origin, + "disconnected-external", + ), + cliProof, + ); + assert.ok( + (await fetch(`${attachment.cdp_endpoint}/json/version`)).ok, + "Network capture closed the external browser", + ); } assert.deepEqual(mcpProof, cliProof); for (const path of [ @@ -180,7 +178,14 @@ export async function verifyBrowserNetworkEvidence( profile_cleanup: true, connected_network_verified: attachment !== undefined, }; + } catch (cause) { + primaryError = cause; + throw cause; } finally { - await site.close(); + const cleanup = []; + if (client !== undefined) cleanup.push(() => client.close()); + if (transport !== undefined) cleanup.push(() => transport.close()); + if (site !== undefined) cleanup.push(() => site.close()); + await closeRuntimeFixtureResources(cleanup, primaryError); } } diff --git a/scripts/lib/browser-script-export-e2e.mjs b/scripts/lib/browser-script-export-e2e.mjs index 17fe25551..ce1599e17 100644 --- a/scripts/lib/browser-script-export-e2e.mjs +++ b/scripts/lib/browser-script-export-e2e.mjs @@ -15,6 +15,7 @@ import { assertExportedScriptAnalysis, } from "./browser-script-export-assertions.mjs"; import { mcpTextValue } from "./mcp-verifier-results.mjs"; +import { closeRuntimeFixtureResources } from "./browser-runtime-fixture-lifecycle.mjs"; /** Verify capture → export → existing analysis through real CLI and stdio MCP. */ export async function verifyBrowserScriptExport( @@ -22,24 +23,29 @@ export async function verifyBrowserScriptExport( entrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url)), attachment, ) { - const site = await startBrowserScriptSite(); - const root = await mkdtemp(join(tmpdir(), "rea-browser-script-export-")); - const env = { - ...process.env, - REA_LOG_LEVEL: "silent", - HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper", - }; - const cli = exportCli(entrypoint, env); - const scenario = scriptCaptureScenario(executable, site.origin); - const save = (name, capture) => saveCapture(root, name, capture); - const transport = new StdioClientTransport({ - command: process.execPath, - args: [entrypoint, "mcp"], - env, - stderr: "pipe", - }); - const client = new Client({ name: "web-script-real-e2e", version: "1" }); + let site; + let root; + let transport; + let client; + let primaryError; try { + site = await startBrowserScriptSite(); + root = await mkdtemp(join(tmpdir(), "rea-browser-script-export-")); + const env = { + ...process.env, + REA_LOG_LEVEL: "silent", + HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper", + }; + const cli = exportCli(entrypoint, env); + const scenario = scriptCaptureScenario(executable, site.origin); + const save = (name, capture) => saveCapture(root, name, capture); + transport = new StdioClientTransport({ + command: process.execPath, + args: [entrypoint, "mcp"], + env, + stderr: "pipe", + }); + client = new Client({ name: "web-script-real-e2e", version: "1" }); const cliCapture = await cli( "capture-browser-scenario", JSON.stringify(scenario), @@ -103,11 +109,17 @@ export async function verifyBrowserScriptExport( query_variants_isolated: true, implicit_refetches: 0, }; + } catch (cause) { + primaryError = cause; + throw cause; } finally { - await client.close(); - await transport.close(); - await site.close(); - await rm(root, { recursive: true, force: true }); + const cleanup = []; + if (client !== undefined) cleanup.push(() => client.close()); + if (transport !== undefined) cleanup.push(() => transport.close()); + if (site !== undefined) cleanup.push(() => site.close()); + if (root !== undefined) + cleanup.push(() => rm(root, { recursive: true, force: true })); + await closeRuntimeFixtureResources(cleanup, primaryError); } } diff --git a/scripts/lib/browser-source-map-e2e.mjs b/scripts/lib/browser-source-map-e2e.mjs index efd12e648..635dcb5e6 100644 --- a/scripts/lib/browser-source-map-e2e.mjs +++ b/scripts/lib/browser-source-map-e2e.mjs @@ -12,35 +12,41 @@ import { parseEvidence } from "../../dist/domain/evidence.js"; import { webSourceLocationResultSchema } from "../../dist/domain/webSourceLocation.js"; import { startBrowserSourceMapSite } from "../fixtures/browser-source-map-site.mjs"; import { mcpTextValue } from "./mcp-verifier-results.mjs"; +import { closeRuntimeFixtureResources } from "./browser-runtime-fixture-lifecycle.mjs"; /** Verify real Chrome capture → export → original compiler point through packaged CLI/MCP. */ export async function verifyBrowserSourceMap( executable, entrypoint = fileURLToPath(new URL("../rea.mjs", import.meta.url)), ) { - const site = await startBrowserSourceMapSite(); - const root = await mkdtemp(join(tmpdir(), "rea-browser-source-map-")); - const env = { - ...process.env, - REA_LOG_LEVEL: "silent", - HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper", - }; - const cli = async (...args) => { - const { stdout } = await promisify(execFile)( - process.execPath, - [entrypoint, ...args, "--json"], - { env, timeout: 45000, maxBuffer: 16 * 1024 * 1024 }, - ); - return parseEvidence(JSON.parse(stdout)); - }; - const transport = new StdioClientTransport({ - command: process.execPath, - args: [entrypoint, "mcp"], - env, - stderr: "pipe", - }); - const client = new Client({ name: "source-map-real-e2e", version: "1" }); + let site; + let root; + let transport; + let client; + let primaryError; try { + site = await startBrowserSourceMapSite(); + root = await mkdtemp(join(tmpdir(), "rea-browser-source-map-")); + const env = { + ...process.env, + REA_LOG_LEVEL: "silent", + HOPPER_LAUNCHER_PATH: "/rea-unconfigured-provider/hopper", + }; + const cli = async (...args) => { + const { stdout } = await promisify(execFile)( + process.execPath, + [entrypoint, ...args, "--json"], + { env, timeout: 45000, maxBuffer: 16 * 1024 * 1024 }, + ); + return parseEvidence(JSON.parse(stdout)); + }; + transport = new StdioClientTransport({ + command: process.execPath, + args: [entrypoint, "mcp"], + env, + stderr: "pipe", + }); + client = new Client({ name: "source-map-real-e2e", version: "1" }); const capture = await cli( "capture-browser-scenario", JSON.stringify({ @@ -126,11 +132,17 @@ export async function verifyBrowserSourceMap( selected_inert_userinfo_context_preserved: true, public_point_cases: 4, }; + } catch (cause) { + primaryError = cause; + throw cause; } finally { - await client.close(); - await transport.close(); - await site.close(); - await rm(root, { recursive: true, force: true }); + const cleanup = []; + if (client !== undefined) cleanup.push(() => client.close()); + if (transport !== undefined) cleanup.push(() => transport.close()); + if (site !== undefined) cleanup.push(() => site.close()); + if (root !== undefined) + cleanup.push(() => rm(root, { recursive: true, force: true })); + await closeRuntimeFixtureResources(cleanup, primaryError); } } function assertPoint(evidence, { site, input, capture, identity }) { diff --git a/scripts/verify-browser-dom-destinations.mjs b/scripts/verify-browser-dom-destinations.mjs index 31b5d1e17..78ee670cb 100644 --- a/scripts/verify-browser-dom-destinations.mjs +++ b/scripts/verify-browser-dom-destinations.mjs @@ -6,15 +6,21 @@ import { join } from "node:path"; import { chromium } from "playwright-core"; import { startBrowserVerifierSite } from "./fixtures/browser-verifier-site.mjs"; import { verifyBrowserDomDestinations } from "./lib/browser-dom-destinations-e2e.mjs"; +import { closeRuntimeFixtureResources } from "./lib/browser-runtime-fixture-lifecycle.mjs"; +import { requireOrdinaryWindowsVerifierCaller } from "./lib/windows-ordinary-user-verifier.mjs"; const executable = process.env.REA_BROWSER_EXECUTABLE; if (!executable) throw new Error("verify:browser:dom requires REA_BROWSER_EXECUTABLE"); -const profile = await mkdtemp(join(tmpdir(), "rea-browser-dom-")); -const site = await startBrowserVerifierSite(); +await requireOrdinaryWindowsVerifierCaller(); +let profile; +let site; let context; let report; +let primaryError; try { + profile = await mkdtemp(join(tmpdir(), "rea-browser-dom-")); + site = await startBrowserVerifierSite(); context = await chromium.launchPersistentContext(profile, { executablePath: executable, headless: true, @@ -50,9 +56,15 @@ try { native_oracle: true, verified: true, }; +} catch (cause) { + primaryError = cause; + throw cause; } finally { - await context?.close(); - await site.close(); - await rm(profile, { recursive: true, force: true }); + const cleanup = []; + if (context !== undefined) cleanup.push(() => context.close()); + if (site !== undefined) cleanup.push(() => site.close()); + if (profile !== undefined) + cleanup.push(() => rm(profile, { recursive: true, force: true })); + await closeRuntimeFixtureResources(cleanup, primaryError); } process.stdout.write(`${JSON.stringify(report)}\n`); diff --git a/scripts/verify-browser-modules.mjs b/scripts/verify-browser-modules.mjs index 23f2f9a76..df195938e 100644 --- a/scripts/verify-browser-modules.mjs +++ b/scripts/verify-browser-modules.mjs @@ -2,12 +2,14 @@ import { access } from "node:fs/promises"; import { verifyBrowserModules } from "./lib/browser-module-e2e.mjs"; import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs"; +import { requireOrdinaryWindowsVerifierCaller } from "./lib/windows-ordinary-user-verifier.mjs"; const executable = process.env.REA_BROWSER_EXECUTABLE; if (!executable) throw new Error( "verify:browser:modules requires REA_BROWSER_EXECUTABLE for caller-supplied Chromium", ); +await requireOrdinaryWindowsVerifierCaller(); await access(executable); const run = createVerifierRun(); const proof = await verifyBrowserModules(executable, process.argv[2]); diff --git a/scripts/verify-browser-network.mjs b/scripts/verify-browser-network.mjs index f7fa0222b..d7c0200da 100644 --- a/scripts/verify-browser-network.mjs +++ b/scripts/verify-browser-network.mjs @@ -2,12 +2,14 @@ import { access } from "node:fs/promises"; import { verifyBrowserNetworkEvidence } from "./lib/browser-network-e2e.mjs"; import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs"; +import { requireOrdinaryWindowsVerifierCaller } from "./lib/windows-ordinary-user-verifier.mjs"; const executable = process.env.REA_BROWSER_EXECUTABLE; if (executable === undefined || executable === "") throw new Error( "verify:browser:network requires REA_BROWSER_EXECUTABLE pointing to an installed Chrome-family browser", ); +await requireOrdinaryWindowsVerifierCaller(); await access(executable); const run = createVerifierRun(); const proof = await verifyBrowserNetworkEvidence(executable, process.argv[2]); diff --git a/scripts/verify-browser-scripts.mjs b/scripts/verify-browser-scripts.mjs index 30fba281b..0c5661379 100644 --- a/scripts/verify-browser-scripts.mjs +++ b/scripts/verify-browser-scripts.mjs @@ -2,12 +2,14 @@ import { access } from "node:fs/promises"; import { verifyBrowserScriptExport } from "./lib/browser-script-export-e2e.mjs"; import { createVerifierRun, completeVerifierRun } from "./lib/verifier-run.mjs"; +import { requireOrdinaryWindowsVerifierCaller } from "./lib/windows-ordinary-user-verifier.mjs"; const executable = process.env.REA_BROWSER_EXECUTABLE; if (executable === undefined || executable === "") throw new Error( "verify:browser:scripts requires REA_BROWSER_EXECUTABLE pointing to an installed Chrome-family browser", ); +await requireOrdinaryWindowsVerifierCaller(); await access(executable); const run = createVerifierRun(); const proof = await verifyBrowserScriptExport(executable, process.argv[2]);