export const PERFORMANCE_BUDGETS = Object.freeze({ initialHtmlCriticalCss: 100 * 1024, initialApplicationJs: 120 * 1024, heroPoster: 220 * 1024, initialFonts: 120 * 1024, firstLiveScene: 650 * 1024, projectVideo: 2.5 * 1024 * 1024, projectVideoDurationSeconds: 10, projectVideoWidth: 1280, projectVideoHeight: 720, projectStill: 400 * 1024, chart: 100 * 1024, deferredProjectVisuals: 2.5 * 1024 * 1024, highTierExtras: 750 * 1024, deferredNonVideo: 3.5 * 1024 * 1024, imageSceneConcurrency: 2, videoConcurrency: 1, mobileTextureDimension: 2048, lowTierGpuEstimate: 96 * 1024 * 1024, highTierGpuEstimate: 192 * 1024 * 1024, }); export const classifyArtifact = (path, source = '') => { if (path === 'index.html') return 'initial-html'; if (/^assets\/index-[^/]+\.css$/u.test(path)) return 'critical-css'; if (/^assets\/index-[^/]+\.js$/u.test(path) && /import\(/u.test(source)) return 'initial-application-js'; if (/^assets\/environment-[^/]+\.js$/u.test(path)) return 'first-live-scene'; if (/\.(?:woff2?|ttf|otf)$/u.test(path)) return 'initial-fonts'; if (/^assets\/hero-[^/]+\.(?:avif|webp|png|jpe?g)$/u.test(path)) return 'hero-poster'; if (/^assets\/project-[^/]+\.(?:webm|mp4)$/u.test(path)) return 'project-video'; if (/^assets\/project-[^/]*chart[^/]*\.(?:avif|webp|png|jpe?g)$/u.test(path)) return 'chart'; if (/^assets\/project-[^/]+\.(?:avif|webp|png|jpe?g|svg)$/u.test(path)) return 'project-still'; if (/^(?:robots\.txt|sitemap\.xml|_headers\.example|_redirects\.example)$/u.test(path)) return 'release-metadata'; throw new Error(`Artifact is unclassified: ${path}`); }; const thresholdByTotal = Object.freeze({ initialHtmlCriticalCss: PERFORMANCE_BUDGETS.initialHtmlCriticalCss, initialApplicationJs: PERFORMANCE_BUDGETS.initialApplicationJs, heroPoster: PERFORMANCE_BUDGETS.heroPoster, initialFonts: PERFORMANCE_BUDGETS.initialFonts, firstLiveScene: PERFORMANCE_BUDGETS.firstLiveScene, deferredProjectVisuals: PERFORMANCE_BUDGETS.deferredProjectVisuals, deferredNonVideo: PERFORMANCE_BUDGETS.deferredNonVideo, }); export const evaluateBudgetTotals = (totals) => ({ violations: Object.entries(thresholdByTotal) .filter(([budget, threshold]) => (totals[budget] ?? 0) > threshold) .map(([budget, threshold]) => ({ budget, actual: totals[budget], threshold })), });