Integrate the Blender grand tourer with a cohesive shader-driven road, terrain, atmosphere, lighting, and post-processing pass. Refine responsive drive dynamics and sign lifecycle behavior, and add durable visual, accessibility, and performance verification evidence.
226 lines
12 KiB
JavaScript
226 lines
12 KiB
JavaScript
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 resolveBudgetGateMode = ({ transferOnly, runtimeTracePath }) => {
|
|
if (transferOnly && runtimeTracePath !== null) throw new Error('Transfer-only mode cannot be combined with runtime evidence.');
|
|
if (transferOnly) return { kind: 'transfer-only' };
|
|
if (runtimeTracePath === null) throw new Error('A runtime trace is required for the full performance budget gate. Use --transfer-only only for a non-gating static build check.');
|
|
return { kind: 'full', runtimeTracePath };
|
|
};
|
|
|
|
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-high|high-tier)-[^/]+\.(?:js|avif|webp|png|jpe?g)$/u.test(path)) return 'high-tier-extras';
|
|
if (/^assets\/environment-[^/]+\.js$/u.test(path)) return 'first-live-scene';
|
|
if (/^assets\/nightdrive-grand-tourer-[^/]+\.glb$/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,
|
|
highTierExtras: PERFORMANCE_BUDGETS.highTierExtras,
|
|
});
|
|
|
|
export const evaluateBudgetTotals = (totals) => ({
|
|
violations: Object.entries(thresholdByTotal)
|
|
.filter(([budget, threshold]) => (totals[budget] ?? 0) > threshold)
|
|
.map(([budget, threshold]) => ({ budget, actual: totals[budget], threshold })),
|
|
});
|
|
|
|
const violation = (budget, actual, threshold, id) => ({ budget, actual, threshold, ...(id === undefined ? {} : { id }) });
|
|
const finitePositiveInteger = (value) => Number.isInteger(value) && value > 0;
|
|
|
|
export const evaluateMediaAssetBudget = (asset, probe, transferBytes, assetClass = undefined) => {
|
|
if (probe === null || typeof probe !== 'object') throw new Error(`Media probe evidence is required for ${asset?.id ?? 'unknown asset'}.`);
|
|
if (!finitePositiveInteger(asset?.width) || !finitePositiveInteger(asset?.height)
|
|
|| !finitePositiveInteger(probe.width) || !finitePositiveInteger(probe.height)) {
|
|
throw new Error(`Verified dimensions are required for ${asset?.id ?? 'unknown asset'}.`);
|
|
}
|
|
if (asset.width !== probe.width || asset.height !== probe.height || asset.mediaType !== probe.mediaType) {
|
|
throw new Error(`Manifest and probe evidence disagree for ${asset.id}.`);
|
|
}
|
|
const violations = [];
|
|
if (asset.mediaType.startsWith('video/')) {
|
|
if (!finitePositiveInteger(asset.durationMs) || !finitePositiveInteger(probe.durationMs)
|
|
|| Math.abs(asset.durationMs - probe.durationMs) > 50) {
|
|
throw new Error(`Verified duration is required for ${asset.id}.`);
|
|
}
|
|
if (transferBytes > PERFORMANCE_BUDGETS.projectVideo) violations.push(violation('projectVideo', transferBytes, PERFORMANCE_BUDGETS.projectVideo, asset.id));
|
|
if (probe.durationMs > PERFORMANCE_BUDGETS.projectVideoDurationSeconds * 1_000) violations.push(violation('projectVideoDurationSeconds', probe.durationMs / 1_000, PERFORMANCE_BUDGETS.projectVideoDurationSeconds, asset.id));
|
|
if (probe.width > PERFORMANCE_BUDGETS.projectVideoWidth) violations.push(violation('projectVideoWidth', probe.width, PERFORMANCE_BUDGETS.projectVideoWidth, asset.id));
|
|
if (probe.height > PERFORMANCE_BUDGETS.projectVideoHeight) violations.push(violation('projectVideoHeight', probe.height, PERFORMANCE_BUDGETS.projectVideoHeight, asset.id));
|
|
} else {
|
|
if (asset.durationMs !== null || probe.durationMs !== null) throw new Error(`Still media cannot declare duration for ${asset.id}.`);
|
|
const budget = assetClass === 'chart' ? 'chart' : 'projectStill';
|
|
const threshold = PERFORMANCE_BUDGETS[budget];
|
|
if (transferBytes > threshold) violations.push(violation(budget, transferBytes, threshold, asset.id));
|
|
}
|
|
return { violations };
|
|
};
|
|
|
|
const allocationBytes = (allocation, fields, label) => {
|
|
for (const field of fields) {
|
|
if (!finitePositiveInteger(allocation?.[field])) throw new Error(`${label} ${allocation?.id ?? 'unknown'} has invalid ${field}.`);
|
|
}
|
|
return fields.reduce((total, field) => total * allocation[field], 1);
|
|
};
|
|
|
|
export const evaluateRuntimeBudgetEvidence = (evidence) => {
|
|
if (evidence?.schema !== 'portfolio-runtime-budget-evidence/v1' || !Array.isArray(evidence.tiers)) {
|
|
throw new Error('Runtime budget evidence must use portfolio-runtime-budget-evidence/v1.');
|
|
}
|
|
const tierNames = evidence.tiers.map((tier) => tier?.tier).sort();
|
|
if (JSON.stringify(tierNames) !== JSON.stringify(['high', 'low'])) {
|
|
throw new Error('Runtime evidence must contain exactly one Low and High tier allocation ledger.');
|
|
}
|
|
const imageScene = evidence.peakRequests?.imageScene;
|
|
const video = evidence.peakRequests?.video;
|
|
if (!Number.isInteger(imageScene) || imageScene < 0 || !Number.isInteger(video) || video < 0) {
|
|
throw new Error('Runtime request concurrency evidence is incomplete.');
|
|
}
|
|
const violations = [];
|
|
if (imageScene > PERFORMANCE_BUDGETS.imageSceneConcurrency) violations.push(violation('imageSceneConcurrency', imageScene, PERFORMANCE_BUDGETS.imageSceneConcurrency));
|
|
if (video > PERFORMANCE_BUDGETS.videoConcurrency) violations.push(violation('videoConcurrency', video, PERFORMANCE_BUDGETS.videoConcurrency));
|
|
const tiers = evidence.tiers.map((tier) => {
|
|
if (!['low', 'high'].includes(tier?.tier) || !Array.isArray(tier.textures) || !Array.isArray(tier.renderTargets)
|
|
|| !Number.isInteger(tier.geometryBytes) || tier.geometryBytes < 0) {
|
|
throw new Error('Runtime tier allocation evidence is incomplete.');
|
|
}
|
|
let textureBytes = 0;
|
|
for (const texture of tier.textures) {
|
|
const base = allocationBytes(texture, ['width', 'height', 'bytesPerPixel', 'layers'], 'Texture');
|
|
if (typeof texture.mipmapped !== 'boolean') throw new Error(`Texture ${texture?.id ?? 'unknown'} is missing mipmap evidence.`);
|
|
textureBytes += Math.ceil(base * (texture.mipmapped ? 4 / 3 : 1));
|
|
if (tier.tier === 'low' && (texture.width > PERFORMANCE_BUDGETS.mobileTextureDimension || texture.height > PERFORMANCE_BUDGETS.mobileTextureDimension)) {
|
|
violations.push(violation('mobileTextureDimension', Math.max(texture.width, texture.height), PERFORMANCE_BUDGETS.mobileTextureDimension, texture.id));
|
|
}
|
|
}
|
|
let renderTargetBytes = 0;
|
|
for (const target of tier.renderTargets) {
|
|
renderTargetBytes += allocationBytes(target, ['width', 'height', 'bytesPerPixel', 'attachments', 'samples'], 'Render target');
|
|
}
|
|
const runtimeAllowanceBytes = 32 * 1024 * 1024;
|
|
const totalBytes = textureBytes + tier.geometryBytes + renderTargetBytes + runtimeAllowanceBytes;
|
|
const budget = tier.tier === 'low' ? 'lowTierGpuEstimate' : 'highTierGpuEstimate';
|
|
const threshold = PERFORMANCE_BUDGETS[budget];
|
|
if (totalBytes > threshold) violations.push(violation(budget, totalBytes, threshold));
|
|
return { tier: tier.tier, textureBytes, geometryBytes: tier.geometryBytes, renderTargetBytes, runtimeAllowanceBytes, totalBytes };
|
|
});
|
|
return { tiers, violations };
|
|
};
|
|
|
|
export const evaluateRuntimeTraceBudgetEvidence = (evidence) => {
|
|
if (evidence?.schema !== 'portfolio-runtime-trace-evidence/v1' || evidence.allocation === null || typeof evidence.allocation !== 'object') {
|
|
throw new Error('Runtime trace evidence must use portfolio-runtime-trace-evidence/v1.');
|
|
}
|
|
const evaluated = evaluateRuntimeBudgetEvidence({
|
|
schema: 'portfolio-runtime-budget-evidence/v1',
|
|
peakRequests: evidence.peakRequests,
|
|
tiers: [
|
|
evidence.allocation,
|
|
evidence.allocation.tier === 'low'
|
|
? { tier: 'high', textures: [], geometryBytes: 0, renderTargets: [] }
|
|
: { tier: 'low', textures: [], geometryBytes: 0, renderTargets: [] },
|
|
],
|
|
});
|
|
const tier = evaluated.tiers.find((item) => item.tier === evidence.allocation.tier);
|
|
if (tier === undefined) throw new Error('Runtime trace tier could not be evaluated.');
|
|
return {
|
|
tier,
|
|
violations: evaluated.violations,
|
|
};
|
|
};
|
|
|
|
export const validateRuntimeBudgetTraceReport = (report) => {
|
|
if (report?.schema !== 'runtime-budget-trace.v1'
|
|
|| report.evidenceKind !== 'authenticated-test-build-production-path'
|
|
|| report.gatingReferenceResult !== false) {
|
|
throw new Error('Runtime budget verification requires an authenticated production-path capture.');
|
|
}
|
|
const expectedProfile = report.tier === 'high' ? 'desktop-high' : report.tier === 'low' ? 'mobile-low' : null;
|
|
if (expectedProfile === null || report.profile !== expectedProfile) {
|
|
throw new Error('Runtime budget capture profile and tier disagree.');
|
|
}
|
|
const viewport = report.observed?.viewport;
|
|
const devicePixelRatio = report.observed?.devicePixelRatio;
|
|
const rendererPixelRatio = report.observed?.rendererPixelRatio;
|
|
const expectedRendererPixelRatio = report.tier === 'high' ? 1.5 : 1.25;
|
|
if (!finitePositiveInteger(viewport?.width) || !finitePositiveInteger(viewport?.height)
|
|
|| !(Number.isFinite(devicePixelRatio) && devicePixelRatio >= expectedRendererPixelRatio)
|
|
|| rendererPixelRatio !== expectedRendererPixelRatio) {
|
|
throw new Error('Runtime budget capture did not exercise the named tier DPR cap.');
|
|
}
|
|
const evidence = report.runtimeBudgetEvidence;
|
|
if (evidence?.allocation?.tier !== report.tier || !Array.isArray(evidence.allocation.renderTargets)) {
|
|
throw new Error('Runtime budget capture allocation and tier disagree.');
|
|
}
|
|
const framebuffers = evidence.allocation.renderTargets.filter((target) => target?.id === 'default-framebuffer');
|
|
const expectedWidth = Math.round(viewport.width * rendererPixelRatio);
|
|
const expectedHeight = Math.round(viewport.height * rendererPixelRatio);
|
|
if (framebuffers.length !== 1 || framebuffers[0].width !== expectedWidth || framebuffers[0].height !== expectedHeight) {
|
|
throw new Error('Runtime budget capture framebuffer dimensions do not match the observed viewport and DPR cap.');
|
|
}
|
|
return evaluateRuntimeTraceBudgetEvidence(evidence);
|
|
};
|
|
|
|
const requestClass = (entry) => {
|
|
if (entry.initiatorType === 'video' || /\.(?:mp4|webm)(?:[?#]|$)/iu.test(entry.name)) return 'video';
|
|
if (entry.initiatorType === 'img' || /\/assets\/(?:environment-|high-tier-)/u.test(entry.name)) return 'imageScene';
|
|
return null;
|
|
};
|
|
|
|
export const calculatePeakRequestConcurrency = (entries) => {
|
|
if (!Array.isArray(entries)) throw new Error('Resource timing entries are required.');
|
|
const peaks = { imageScene: 0, video: 0 };
|
|
for (const kind of ['imageScene', 'video']) {
|
|
const events = [];
|
|
for (const entry of entries) {
|
|
if (requestClass(entry) !== kind) continue;
|
|
if (!Number.isFinite(entry.startTime) || !Number.isFinite(entry.responseEnd) || entry.responseEnd < entry.startTime) {
|
|
throw new Error('Tracked resource timing is incomplete.');
|
|
}
|
|
events.push({ at: entry.startTime, delta: 1 }, { at: entry.responseEnd, delta: -1 });
|
|
}
|
|
events.sort((left, right) => left.at - right.at || left.delta - right.delta);
|
|
let active = 0;
|
|
for (const event of events) {
|
|
active += event.delta;
|
|
peaks[kind] = Math.max(peaks[kind], active);
|
|
}
|
|
}
|
|
return peaks;
|
|
};
|