📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
// Thresholds are Google's "Poor" band (https://web.dev/articles/vitals): LCP p75 > 2500ms, INP > 200ms, CLS > 0.1.
|
||||
// When Speed Insights isn't wired up the metrics come back empty and the gate is a no-op.
|
||||
import { withRouteShapeWarnings } from '../route-normalize.mjs';
|
||||
|
||||
export const metadata = {
|
||||
id: 'cwv_poor',
|
||||
threshold: 'LCP p75>2500 OR INP p75>200 OR CLS p75>0.1, AND speed_insights count > 50',
|
||||
billingDimension: 'speed-insights',
|
||||
scope: 'route',
|
||||
sourceCitation: 'https://web.dev/articles/vitals',
|
||||
description:
|
||||
'Routes where Core Web Vitals fall into Google\'s "Poor" band on real-user traffic. LCP > 2500ms, INP > 200ms, or CLS > 0.1 each hurt SEO and conversion. Surfaces one candidate per (route, metric) pair to keep recommendations focused.',
|
||||
};
|
||||
|
||||
// Below this floor p75 is too noisy to act on.
|
||||
const MIN_PER_ROUTE_SAMPLES = 50;
|
||||
|
||||
export function gate(signals) {
|
||||
const totalSamples = sumRows(signals.metrics?.cwvCount?.rows);
|
||||
if (totalSamples === 0) return [];
|
||||
|
||||
const countByRoute = byRoute(signals.metrics?.cwvCountByRoute?.rows);
|
||||
const lcpBy = byRoute(signals.metrics?.cwvLcpByRoute?.rows);
|
||||
const inpBy = byRoute(signals.metrics?.cwvInpByRoute?.rows);
|
||||
const clsBy = byRoute(signals.metrics?.cwvClsByRoute?.rows);
|
||||
|
||||
const routes = new Set([...lcpBy.keys(), ...inpBy.keys(), ...clsBy.keys()]);
|
||||
const out = [];
|
||||
for (const route of routes) {
|
||||
const routeSamples = countByRoute.get(route) ?? 0;
|
||||
if (routeSamples < MIN_PER_ROUTE_SAMPLES) continue;
|
||||
const lcp = lcpBy.get(route);
|
||||
const inp = inpBy.get(route);
|
||||
const cls = clsBy.get(route);
|
||||
const issues = [];
|
||||
if (lcp != null && lcp > 2500) issues.push({ metric: 'LCP', value: Math.round(lcp), threshold: 2500, unit: 'ms' });
|
||||
if (inp != null && inp > 200) issues.push({ metric: 'INP', value: Math.round(inp), threshold: 200, unit: 'ms' });
|
||||
if (cls != null && cls > 0.1) issues.push({ metric: 'CLS', value: round2(cls), threshold: 0.1, unit: '' });
|
||||
if (issues.length === 0) continue;
|
||||
|
||||
const summary = issues.map((i) => `${i.metric}=${i.value}${i.unit}`).join(',');
|
||||
out.push(withRouteShapeWarnings({
|
||||
kind: metadata.id,
|
||||
scope: 'route',
|
||||
route,
|
||||
files: [],
|
||||
priority: issues.reduce((s, i) => s + ratioOverThreshold(i), 0) * 10,
|
||||
confidence: 0.82,
|
||||
o11ySignal: summary,
|
||||
reason: 'real-user Core Web Vitals in poor band',
|
||||
question: `On ${route}, ${summary}. Which client-side work (bundle weight, blocking scripts, layout shifts, hydration) is responsible, and which change would land first?`,
|
||||
evidence: {
|
||||
metric: 'cwv',
|
||||
route,
|
||||
lcpMs: lcp != null ? Math.round(lcp) : null,
|
||||
inpMs: inp != null ? Math.round(inp) : null,
|
||||
cls: cls != null ? round2(cls) : null,
|
||||
issues,
|
||||
totalSpeedInsightsSamples: totalSamples,
|
||||
routeSpeedInsightsSamples: routeSamples,
|
||||
},
|
||||
}, signals));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function byRoute(rows) {
|
||||
const m = new Map();
|
||||
for (const r of rows ?? []) {
|
||||
if (!r.route || r.value == null) continue;
|
||||
m.set(r.route, r.value);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
function sumRows(rows) {
|
||||
if (!Array.isArray(rows)) return 0;
|
||||
return rows.reduce((s, r) => s + (r.value ?? 0), 0);
|
||||
}
|
||||
|
||||
function round2(n) {
|
||||
return Math.round(n * 100) / 100;
|
||||
}
|
||||
|
||||
function ratioOverThreshold(i) {
|
||||
return i.value / (i.threshold || 1);
|
||||
}
|
||||
Reference in New Issue
Block a user