import fs from 'node:fs'; import assert from 'node:assert/strict'; const html = fs.readFileSync(new URL('../www/index.html', import.meta.url), 'utf8'); const main = fs.readFileSync(new URL('../www/main.js', import.meta.url), 'utf8'); const audio = fs.readFileSync(new URL('../www/core/audio.js', import.meta.url), 'utf8'); const styleSelect = html.match(/]*\bid="styleSel"[^>]*>/)?.[0] || ''; assert(styleSelect, 'style selector is missing'); assert(!/\bopt-w-\d+\b/.test(styleSelect), 'style selector must not have a fixed-width utility class'); assert(/adjustStyleDropdownWidth\(\)/.test(main), 'dynamic style selector sizing is missing'); assert(/styleSel\.style\.width\s*=/.test(main), 'dynamic style selector width assignment is missing'); assert(/currentMetricsSeq\s*=\s*Number\.isFinite\(env\.audio\?\.phoenixMetricsSeq\)/.test(main), 'render loop must track the metrics sequence used by the RTA'); assert(/renderRequestGeneration\s*\+=\s*1/.test(main), 'render requests must carry a generation that survives asynchronous rendering'); assert(/renderRequestGeneration\s*===\s*renderGeneration\)\s*renderDirty\s*=\s*false/.test(main), 'an in-flight render must not clear a newer render request'); assert(/const frameDue\s*=\s*audioOk\s*\|\|\s*elapsed\s*>=\s*target/.test(main), 'live displays must use requestAnimationFrame directly instead of a second 60 Hz gate'); assert(/await updateActiveMeters\(env, d, CONFIG\);\s*[^]*?env\.requestRender\?\.\('audio'\)/.test(audio), 'audio rendering must be requested after active meters finish updating'); assert(!/phoenixPacketRaf\s*=\s*requestAnimationFrame/.test(audio), 'metrics packets must not wait for a competing requestAnimationFrame pump'); console.log('GUI contract regression tests passed');