import { type CLSAttribution, type BeaconMetricData, BeaconMetricKey } from "../beacon"; import type { UserConfig } from "../config"; import { getNodeSelector } from "../dom"; import { max } from "../math"; import * as PROPS from "../minification"; import { processTimeMetric } from "../timing"; let sessionValue = 0; let sessionEntries: LayoutShift[] = []; let sessionAttributions: CLSAttribution[] = []; let largestEntry: LayoutShift | undefined; let maximumSessionValue = 0; export function processEntry(entry: LayoutShift): void { if (!entry.hadRecentInput) { const firstEntry = sessionEntries[0]; const latestEntry = sessionEntries[sessionEntries[PROPS.length] - 1]; const sources = entry.sources ? entry.sources .filter((source) => source.node) .map((source) => ({ value: entry.value, startTime: processTimeMetric(entry[PROPS.startTime]), elementSelector: getNodeSelector(source.node!), elementType: source.node!.nodeName, })) : []; if ( sessionEntries[PROPS.length] && (entry[PROPS.startTime] - latestEntry[PROPS.startTime] >= 1000 || entry[PROPS.startTime] - firstEntry[PROPS.startTime] >= 5000) ) { sessionValue = entry.value; sessionEntries = [entry]; sessionAttributions = sources; largestEntry = entry; } else { sessionValue += entry.value; sessionEntries[PROPS.push](entry); sessionAttributions = sessionAttributions.concat(sources); if (!largestEntry || entry.value > largestEntry.value) { largestEntry = entry; } } maximumSessionValue = max(maximumSessionValue, sessionValue); } } export function reset(): void { sessionValue = 0; sessionEntries = []; maximumSessionValue = 0; largestEntry = undefined; } export function getData(config: UserConfig): BeaconMetricData[BeaconMetricKey.CLS] | undefined { if (!("LayoutShift" in self)) { return undefined; } return { value: maximumSessionValue, startTime: sessionEntries[0] ? processTimeMetric(sessionEntries[0][PROPS.startTime]) : null, largestEntry: largestEntry ? { value: largestEntry.value, startTime: processTimeMetric(largestEntry[PROPS.startTime]), } : null, sources: sessionAttributions[PROPS.length] ? sessionAttributions.slice(0, config.maxAttributionEntries) : null, }; }