import { verifySignedReferenceRun } from './reference-evidence.mjs'; const median = (values) => { if (values.length === 0) throw new Error('At least one value is required.'); const sorted = [...values].sort((left, right) => left - right); return sorted[Math.floor(sorted.length / 2)]; }; export const aggregateFiveDriveTraces = (runs, trustPolicy) => { if (!Array.isArray(runs) || runs.length !== 5) throw new Error('Exactly five drive-trace.v1 runs are required.'); for (const run of runs) verifySignedReferenceRun(run, trustPolicy); const profile = runs[0]?.profile; if (typeof profile !== 'string' || runs.some((run) => run?.schema !== 'drive-trace.v1' || run.profile !== profile)) { throw new Error('All runs must use one drive-trace.v1 profile.'); } const identities = runs.map((run) => run.referenceRun); const indexes = identities.map((identity) => identity?.runIndex).sort((left, right) => left - right); if (JSON.stringify(indexes) !== JSON.stringify([1, 2, 3, 4, 5])) { throw new Error('Reference evidence must contain unique run indexes 1 through 5.'); } const runIds = new Set(identities.map((identity) => identity?.runId)); const digests = new Set(identities.map((identity) => identity?.attestationDigest)); if (runIds.size !== 5 || digests.size !== 5 || identities.some((identity) => !/^[a-f0-9]{64}$/u.test(identity?.attestationDigest ?? ''))) { throw new Error('Reference evidence must contain five unique authenticated identities.'); } const lcp = runs.map((run) => run.metrics.lcp); const tbt = runs.map((run) => run.metrics.tbt); const everyPerRunBudgetPass = runs.every((run) => Object.values(run.budgetResults).every(Boolean)); const referenceEvidence = runs.every((run) => run.gatingReferenceResult === true && run.validReferenceCadence === true); const summary = { lcpWorst: Math.max(...lcp), lcpMedian: median(lcp), tbtWorst: Math.max(...tbt), tbtMedian: median(tbt), everyPerRunBudgetPass, referenceEvidence, }; return { schema: 'drive-trace-aggregation/v1', profile, runCount: 5, summary, passes: referenceEvidence && everyPerRunBudgetPass && summary.lcpWorst <= 3_000 && summary.lcpMedian <= 2_500 && summary.tbtWorst <= 300 && summary.tbtMedian <= 200, }; };