diff --git a/src/attribution/onCLS.ts b/src/attribution/onCLS.ts index 5fca98c..b10c5a8 100644 --- a/src/attribution/onCLS.ts +++ b/src/attribution/onCLS.ts @@ -27,13 +27,15 @@ const getLargestLayoutShiftSource = (sources: LayoutShiftAttribution[]) => { return sources.find((s) => s.node && s.node.nodeType === 1) || sources[0]; }; -const attributeCLS = (metric: CLSMetric): void => { +const attributeCLS = (metric: CLSMetric): CLSMetricWithAttribution => { + const metricWithAttribution = metric as CLSMetricWithAttribution; + if (metric.entries.length) { const largestEntry = getLargestLayoutShiftEntry(metric.entries); if (largestEntry && largestEntry.sources && largestEntry.sources.length) { const largestSource = getLargestLayoutShiftSource(largestEntry.sources); if (largestSource) { - (metric as CLSMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { largestShiftTarget: getSelector(largestSource.node), largestShiftTime: largestEntry.startTime, largestShiftValue: largestEntry.value, @@ -41,12 +43,13 @@ const attributeCLS = (metric: CLSMetric): void => { largestShiftEntry: largestEntry, loadState: getLoadState(largestEntry.startTime), }; - return; + return metricWithAttribution; } } } // Set an empty object if no other attribution has been set. - (metric as CLSMetricWithAttribution).attribution = {}; + metricWithAttribution.attribution = {}; + return metricWithAttribution; }; /** @@ -75,7 +78,7 @@ export const onCLS = ( opts?: ReportOpts, ) => { unattributedOnCLS((metric: CLSMetric) => { - attributeCLS(metric); - onReport(metric as CLSMetricWithAttribution); + const metricWithAttribution = attributeCLS(metric); + onReport(metricWithAttribution); }, opts); }; diff --git a/src/attribution/onFCP.ts b/src/attribution/onFCP.ts index 649f96e..16df0ec 100644 --- a/src/attribution/onFCP.ts +++ b/src/attribution/onFCP.ts @@ -21,34 +21,38 @@ import {isInvalidTimestamp} from '../lib/isInvalidTimestamp.js'; import {onFCP as unattributedOnFCP} from '../onFCP.js'; import {FCPMetric, FCPMetricWithAttribution, ReportOpts} from '../types.js'; -const attributeFCP = (metric: FCPMetric): void => { +const attributeFCP = (metric: FCPMetric): FCPMetricWithAttribution => { + const metricWithAttribution = metric as FCPMetricWithAttribution; + if (metric.entries.length) { const navigationEntry = getNavigationEntry(); const fcpEntry = metric.entries[metric.entries.length - 1]; if (navigationEntry) { const responseStart = navigationEntry.responseStart; - if (isInvalidTimestamp(responseStart)) return; + // TODO(bckenny): this is wrong. + if (isInvalidTimestamp(responseStart)) return metricWithAttribution; const activationStart = navigationEntry.activationStart || 0; const ttfb = Math.max(0, responseStart - activationStart); - (metric as FCPMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { timeToFirstByte: ttfb, firstByteToFCP: metric.value - ttfb, loadState: getLoadState(metric.entries[0].startTime), navigationEntry, fcpEntry, }; - return; + return metricWithAttribution; } } // Set an empty object if no other attribution has been set. - (metric as FCPMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { timeToFirstByte: 0, firstByteToFCP: metric.value, loadState: getLoadState(getBFCacheRestoreTime()), }; + return metricWithAttribution; }; /** @@ -62,7 +66,7 @@ export const onFCP = ( opts?: ReportOpts, ) => { unattributedOnFCP((metric: FCPMetric) => { - attributeFCP(metric); - onReport(metric as FCPMetricWithAttribution); + const metricWithAttribution = attributeFCP(metric); + onReport(metricWithAttribution); }, opts); }; diff --git a/src/attribution/onFID.ts b/src/attribution/onFID.ts index a75a257..7a5143e 100644 --- a/src/attribution/onFID.ts +++ b/src/attribution/onFID.ts @@ -19,15 +19,18 @@ import {getSelector} from '../lib/getSelector.js'; import {onFID as unattributedOnFID} from '../onFID.js'; import {FIDMetric, FIDMetricWithAttribution, ReportOpts} from '../types.js'; -const attributeFID = (metric: FIDMetric): void => { +const attributeFID = (metric: FIDMetric): FIDMetricWithAttribution => { + const metricWithAttribution = metric as FIDMetricWithAttribution; + const fidEntry = metric.entries[0]; - (metric as FIDMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { eventTarget: getSelector(fidEntry.target), eventType: fidEntry.name, eventTime: fidEntry.startTime, eventEntry: fidEntry, loadState: getLoadState(fidEntry.startTime), }; + return metricWithAttribution; }; /** @@ -44,7 +47,7 @@ export const onFID = ( opts?: ReportOpts, ) => { unattributedOnFID((metric: FIDMetric) => { - attributeFID(metric); - onReport(metric as FIDMetricWithAttribution); + const metricWithAttribution = attributeFID(metric); + onReport(metricWithAttribution); }, opts); }; diff --git a/src/attribution/onINP.ts b/src/attribution/onINP.ts index 378ad2c..e198330 100644 --- a/src/attribution/onINP.ts +++ b/src/attribution/onINP.ts @@ -186,7 +186,7 @@ const getIntersectingLoAFs = ( return intersectingLoAFs; }; -const attributeINP = (metric: INPMetric): void => { +const attributeINP = (metric: INPMetric): INPMetricWithAttribution => { const firstEntry = metric.entries[0]; const renderTime = entryToRenderTimeMap.get(firstEntry)!; const group = pendingEntriesGroupMap.get(renderTime)!; @@ -220,7 +220,8 @@ const attributeINP = (metric: INPMetric): void => { const nextPaintTime = Math.max.apply(Math, nextPaintTimeCandidates); - (metric as INPMetricWithAttribution).attribution = { + const metricWithAttribution = metric as INPMetricWithAttribution; + metricWithAttribution.attribution = { interactionTarget: getSelector( firstEntryWithTarget && firstEntryWithTarget.target, ), @@ -234,6 +235,7 @@ const attributeINP = (metric: INPMetric): void => { presentationDelay: Math.max(nextPaintTime - processingEnd, 0), loadState: getLoadState(firstEntry.startTime), }; + return metricWithAttribution; }; /** @@ -278,8 +280,8 @@ export const onINP = ( // running in Chrome (EventTimingKeypressAndCompositionInteractionId) // 123+ that if rolled out fully would make this no longer necessary. whenIdle(() => { - attributeINP(metric); - onReport(metric as INPMetricWithAttribution); + const metricWithAttribution = attributeINP(metric); + onReport(metricWithAttribution); }); }, opts); }; diff --git a/src/attribution/onLCP.ts b/src/attribution/onLCP.ts index aeba48f..3e5b21b 100644 --- a/src/attribution/onLCP.ts +++ b/src/attribution/onLCP.ts @@ -25,13 +25,16 @@ import { ReportOpts, } from '../types.js'; -const attributeLCP = (metric: LCPMetric) => { +const attributeLCP = (metric: LCPMetric): LCPMetricWithAttribution => { + const metricWithAttribution = metric as LCPMetricWithAttribution; + if (metric.entries.length) { const navigationEntry = getNavigationEntry(); if (navigationEntry) { const responseStart = navigationEntry.responseStart; - if (isInvalidTimestamp(responseStart)) return; + // TODO(bckenny): this is wrong. + if (isInvalidTimestamp(responseStart)) return metricWithAttribution; const activationStart = navigationEntry.activationStart || 0; const lcpEntry = metric.entries[metric.entries.length - 1]; @@ -78,17 +81,18 @@ const attributeLCP = (metric: LCPMetric) => { attribution.lcpResourceEntry = lcpResourceEntry; } - (metric as LCPMetricWithAttribution).attribution = attribution; - return; + metricWithAttribution.attribution = attribution; + return metricWithAttribution; } } // Set an empty object if no other attribution has been set. - (metric as LCPMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { timeToFirstByte: 0, resourceLoadDelay: 0, resourceLoadDuration: 0, elementRenderDelay: metric.value, }; + return metricWithAttribution; }; /** @@ -107,7 +111,7 @@ export const onLCP = ( opts?: ReportOpts, ) => { unattributedOnLCP((metric: LCPMetric) => { - attributeLCP(metric); - onReport(metric as LCPMetricWithAttribution); + const metricWithAttribution = attributeLCP(metric); + onReport(metricWithAttribution); }, opts); }; diff --git a/src/attribution/onTTFB.ts b/src/attribution/onTTFB.ts index 3f267c0..51c92a4 100644 --- a/src/attribution/onTTFB.ts +++ b/src/attribution/onTTFB.ts @@ -17,7 +17,9 @@ import {onTTFB as unattributedOnTTFB} from '../onTTFB.js'; import {TTFBMetric, TTFBMetricWithAttribution, ReportOpts} from '../types.js'; -const attributeTTFB = (metric: TTFBMetric): void => { +const attributeTTFB = (metric: TTFBMetric): TTFBMetricWithAttribution => { + const metricWithAttribution = metric as TTFBMetricWithAttribution; + if (metric.entries.length) { const navigationEntry = metric.entries[0]; const activationStart = navigationEntry.activationStart || 0; @@ -43,7 +45,7 @@ const attributeTTFB = (metric: TTFBMetric): void => { 0, ); - (metric as TTFBMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { waitingDuration: waitEnd, cacheDuration: dnsStart - waitEnd, // dnsEnd usually equals connectStart but use connectStart over dnsEnd @@ -57,16 +59,17 @@ const attributeTTFB = (metric: TTFBMetric): void => { requestDuration: metric.value - connectEnd, navigationEntry: navigationEntry, }; - return; + return metricWithAttribution; } // Set an empty object if no other attribution has been set. - (metric as TTFBMetricWithAttribution).attribution = { + metricWithAttribution.attribution = { waitingDuration: 0, cacheDuration: 0, dnsDuration: 0, connectionDuration: 0, requestDuration: 0, }; + return metricWithAttribution; }; /** @@ -89,7 +92,7 @@ export const onTTFB = ( opts?: ReportOpts, ) => { unattributedOnTTFB((metric: TTFBMetric) => { - attributeTTFB(metric); - onReport(metric as TTFBMetricWithAttribution); + const metricWithAttribution = attributeTTFB(metric); + onReport(metricWithAttribution); }, opts); };