import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; import { WP4_07_PERFORMANCE_BUDGETS } from "../tests/visual-performance/wp4-07-fixture.mjs"; const evidenceIndex = process.argv.indexOf("--evidence"); const phaseIndex = process.argv.indexOf("--phase"); const evidenceDirectory = resolve(evidenceIndex >= 0 ? process.argv[evidenceIndex + 1] : ""); const phase = phaseIndex >= 0 ? process.argv[phaseIndex + 1] : "green"; if (evidenceIndex < 0 || !process.argv[evidenceIndex + 1] || !["green", "red"].includes(phase)) throw new Error("Usage: --evidence [--phase red|green]"); function read(browser, filename) { const path = resolve(evidenceDirectory, browser, filename); if (!existsSync(path)) throw new Error(`missing ${browser}/${filename}`); return JSON.parse(readFileSync(path, "utf8")); } const browserResults = {}; let greenInputsEligible = true; for (const browser of ["chrome", "edge"]) { const raw = read(browser, "performance-raw.json"); const memory = read(browser, "memory.json"); const dom = read(browser, "dom-count.json"); const environment = read(browser, "environment.json"); greenInputsEligible = greenInputsEligible && raw.eligible_for_green === true && raw.harness_mode === "real"; if (raw.interaction.length !== 5 || raw.autosave_serialization.length !== 5 || raw.export_1080x1920.length !== 5 || raw.editor_reopen.samples_ms.length !== 5) { throw new Error(`${browser} did not retain exactly five measured samples per budget`); } if (raw.interaction.some((run) => run.duration_ms < WP4_07_PERFORMANCE_BUDGETS.interaction_duration_ms)) { throw new Error(`${browser} shortened a ten-second interaction measurement`); } const checks = { autosave_serialization: raw.autosave_serialization.every((run) => run.p95_ms <= WP4_07_PERFORMANCE_BUDGETS.autosave_serialization_p95_ms_max), canvas_frame: raw.interaction.every((run) => run.frame_p95_ms <= WP4_07_PERFORMANCE_BUDGETS.canvas_frame_p95_ms_max), continuous_unresponsive: raw.interaction.every((run) => run.frame_max_ms < WP4_07_PERFORMANCE_BUDGETS.continuous_unresponsive_ms_max_exclusive), dom_bounded: dom.bounded_by_viewport_and_two_screens === true && dom.top_count < dom.catalog_count && dom.bottom_count < dom.catalog_count, editor_reopen: raw.editor_reopen.max_ms <= WP4_07_PERFORMANCE_BUDGETS.editor_reopen_ms_max, export_duration: raw.export_1080x1920.every((run) => run.duration_ms <= WP4_07_PERFORMANCE_BUDGETS.export_1080x1920_ms_max), export_failure_isolated: raw.export_failure.observed_error === "export_asset_unavailable" && JSON.stringify(raw.export_failure.saves_before) === JSON.stringify(raw.export_failure.saves_after), export_memory: memory.export_peak_additional_bytes.every((value) => value <= WP4_07_PERFORMANCE_BUDGETS.export_peak_additional_bytes_max), long_task: raw.interaction.every((run) => run.long_task_max_ms <= WP4_07_PERFORMANCE_BUDGETS.long_task_ms_max), pointer_to_frame: raw.interaction.every((run) => run.pointer_to_frame_p95_ms <= WP4_07_PERFORMANCE_BUDGETS.pointer_to_frame_p95_ms_max), }; browserResults[browser] = { checks, environment, fixture_sha256: raw.fixture_sha256, metrics: { autosave_serialization: raw.autosave_serialization, editor_reopen: raw.editor_reopen, export_1080x1920: raw.export_1080x1920, interaction: raw.interaction, }, status: Object.values(checks).every(Boolean) ? "within_budget" : "budget_exceeded", }; } const passed = Object.values(browserResults).every((result) => result.status === "within_budget"); const report = { browsers: browserResults, eligible_for_green: phase === "green" && greenInputsEligible, normative_budgets: WP4_07_PERFORMANCE_BUDGETS, phase, status: passed ? "within_budget" : "budget_exceeded", }; writeFileSync(resolve(evidenceDirectory, "performance.json"), `${JSON.stringify(report, null, 2)}\n`); writeFileSync(resolve(evidenceDirectory, "memory.json"), `${JSON.stringify({ browsers: Object.fromEntries(["chrome", "edge"].map((browser) => [browser, read(browser, "memory.json")])), limit_bytes: WP4_07_PERFORMANCE_BUDGETS.export_peak_additional_bytes_max, }, null, 2)}\n`); writeFileSync(resolve(evidenceDirectory, "dom-count.json"), `${JSON.stringify({ browsers: Object.fromEntries(["chrome", "edge"].map((browser) => [browser, read(browser, "dom-count.json")])), required_catalog_count: 1_407, }, null, 2)}\n`); writeFileSync(resolve(evidenceDirectory, "environment.json"), `${JSON.stringify({ browsers: Object.fromEntries(["chrome", "edge"].map((browser) => [browser, read(browser, "environment.json")])), fixture_hashes_match: browserResults.chrome.fixture_sha256 === browserResults.edge.fixture_sha256, }, null, 2)}\n`); console.log(JSON.stringify({ browser_statuses: Object.fromEntries(Object.entries(browserResults).map(([name, result]) => [name, result.status])), phase, status: report.status }, null, 2)); if (phase === "green" && (!greenInputsEligible || !passed)) process.exit(1);