97 lines
2.9 KiB
JavaScript
97 lines
2.9 KiB
JavaScript
/**
|
|
* @param {{
|
|
* initialJsGzipBytes: number,
|
|
* lazyChunks: Array<{ path: string, gzipBytes: number }>
|
|
* }} measurements
|
|
* @param {{ initialJsGzipBytes: number, lazyChunkGzipBytes: number }} thresholds
|
|
*/
|
|
export function evaluateBundleBudget(measurements, thresholds) {
|
|
const initialPassed =
|
|
measurements.initialJsGzipBytes <= thresholds.initialJsGzipBytes;
|
|
const lazyResults = measurements.lazyChunks.map((chunk) => ({
|
|
...chunk,
|
|
threshold: thresholds.lazyChunkGzipBytes,
|
|
passed: chunk.gzipBytes <= thresholds.lazyChunkGzipBytes,
|
|
}));
|
|
return Object.freeze({
|
|
initialPassed,
|
|
lazyResults: Object.freeze(lazyResults),
|
|
passed: initialPassed && lazyResults.every((chunk) => chunk.passed),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @param {{
|
|
* context?: Record<string, unknown>,
|
|
* metrics: { lcpMs: number, cls: number, namedInteractionMs: number }
|
|
* }} report
|
|
* @param {{ lcpMs: number, cls: number, namedInteractionMs: number }} thresholds
|
|
*/
|
|
export function evaluateLabBudget(report, thresholds) {
|
|
const requiredContext = [
|
|
"runner",
|
|
"browser",
|
|
"viewport",
|
|
"network",
|
|
"cpu",
|
|
"cache",
|
|
"build",
|
|
];
|
|
const missingContext = requiredContext.filter(
|
|
(field) => report.context?.[field] === undefined,
|
|
);
|
|
const results = {
|
|
lcp: report.metrics.lcpMs <= thresholds.lcpMs,
|
|
cls: report.metrics.cls <= thresholds.cls,
|
|
namedInteraction:
|
|
report.metrics.namedInteractionMs <= thresholds.namedInteractionMs,
|
|
};
|
|
return Object.freeze({
|
|
missingContext: Object.freeze(missingContext),
|
|
results: Object.freeze(results),
|
|
passed: missingContext.length === 0 && Object.values(results).every(Boolean),
|
|
});
|
|
}
|
|
|
|
/** @param {number[]} values */
|
|
export function percentile75(values) {
|
|
if (values.length === 0) return null;
|
|
const sorted = [...values].sort((left, right) => left - right);
|
|
return sorted[Math.ceil(sorted.length * 0.75) - 1];
|
|
}
|
|
|
|
/**
|
|
* @param {{
|
|
* metrics: { p75LcpMs: number | null, p75Cls: number | null, p75InpMs: number | null },
|
|
* eligibleSamples: number
|
|
* }} report
|
|
* @param {{
|
|
* p75LcpMs: number,
|
|
* p75Cls: number,
|
|
* p75InpMs: number,
|
|
* minimumEligibleSamples: number | null
|
|
* }} thresholds
|
|
*/
|
|
export function evaluateFieldBudget(report, thresholds) {
|
|
if (
|
|
thresholds.minimumEligibleSamples === null ||
|
|
report.eligibleSamples < thresholds.minimumEligibleSamples ||
|
|
Object.values(report.metrics).some((value) => value === null)
|
|
) {
|
|
return Object.freeze({
|
|
status: /** @type {const} */ ("FAIL_UNVERIFIED"),
|
|
passed: false,
|
|
});
|
|
}
|
|
const passed =
|
|
/** @type {number} */ (report.metrics.p75LcpMs) <= thresholds.p75LcpMs &&
|
|
/** @type {number} */ (report.metrics.p75Cls) <= thresholds.p75Cls &&
|
|
/** @type {number} */ (report.metrics.p75InpMs) <= thresholds.p75InpMs;
|
|
return Object.freeze({
|
|
status: passed
|
|
? /** @type {const} */ ("PASS")
|
|
: /** @type {const} */ ("FAIL_THRESHOLD"),
|
|
passed,
|
|
});
|
|
}
|