Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update types to support more generic usage #471

Merged
merged 16 commits into from
May 3, 2024
Merged
31 changes: 16 additions & 15 deletions src/attribution/onCLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import {getLoadState} from '../lib/getLoadState.js';
import {getSelector} from '../lib/getSelector.js';
import {onCLS as unattributedOnCLS} from '../onCLS.js';
import {
CLSReportCallback,
CLSReportCallbackWithAttribution,
CLSAttribution,
CLSMetric,
CLSMetricWithAttribution,
ReportOpts,
Expand All @@ -33,26 +32,31 @@ 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 => {
// Use an empty object if no other attribution has been set.
let attribution: CLSAttribution = {};

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 = {
attribution = {
largestShiftTarget: getSelector(largestSource.node),
largestShiftTime: largestEntry.startTime,
largestShiftValue: largestEntry.value,
largestShiftSource: largestSource,
largestShiftEntry: largestEntry,
loadState: getLoadState(largestEntry.startTime),
};
return;
}
}
}
// Set an empty object if no other attribution has been set.
(metric as CLSMetricWithAttribution).attribution = {};

// Cast to attribution metric so it can be populated.
const metricWithAttribution = metric as CLSMetricWithAttribution;
brendankenny marked this conversation as resolved.
Show resolved Hide resolved
metricWithAttribution.attribution = attribution;
return metricWithAttribution;
};

/**
Expand All @@ -77,14 +81,11 @@ const attributeCLS = (metric: CLSMetric): void => {
* during the same page load._
*/
export const onCLS = (
onReport: CLSReportCallbackWithAttribution,
onReport: (metric: CLSMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
unattributedOnCLS(
((metric: CLSMetricWithAttribution) => {
attributeCLS(metric);
onReport(metric);
}) as CLSReportCallback,
opts,
);
unattributedOnCLS((metric: CLSMetric) => {
const metricWithAttribution = attributeCLS(metric);
onReport(metricWithAttribution);
}, opts);
};
59 changes: 30 additions & 29 deletions src/attribution/onFCP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,41 +20,45 @@ import {getNavigationEntry} from '../lib/getNavigationEntry.js';
import {isInvalidTimestamp} from '../lib/isInvalidTimestamp.js';
import {onFCP as unattributedOnFCP} from '../onFCP.js';
import {
FCPAttribution,
FCPMetric,
FCPMetricWithAttribution,
FCPReportCallback,
FCPReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';

const attributeFCP = (metric: FCPMetric): void => {
const attributeFCP = (metric: FCPMetric): FCPMetricWithAttribution => {
// Use a default object if no other attribution has been set.
let attribution: FCPAttribution = {
timeToFirstByte: 0,
firstByteToFCP: metric.value,
loadState: getLoadState(getBFCacheRestoreTime()),
};

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;

const activationStart = navigationEntry.activationStart || 0;
const ttfb = Math.max(0, responseStart - activationStart);
if (!isInvalidTimestamp(responseStart)) {
const activationStart = navigationEntry.activationStart || 0;
const ttfb = Math.max(0, responseStart - activationStart);

(metric as FCPMetricWithAttribution).attribution = {
timeToFirstByte: ttfb,
firstByteToFCP: metric.value - ttfb,
loadState: getLoadState(metric.entries[0].startTime),
navigationEntry,
fcpEntry,
};
return;
attribution = {
timeToFirstByte: ttfb,
firstByteToFCP: metric.value - ttfb,
loadState: getLoadState(metric.entries[0].startTime),
navigationEntry,
fcpEntry,
};
}
}
}
// Set an empty object if no other attribution has been set.
(metric as FCPMetricWithAttribution).attribution = {
timeToFirstByte: 0,
firstByteToFCP: metric.value,
loadState: getLoadState(getBFCacheRestoreTime()),
};

// Cast to attribution metric so it can be populated.
const metricWithAttribution = metric as FCPMetricWithAttribution;
metricWithAttribution.attribution = attribution;
return metricWithAttribution;
};

/**
Expand All @@ -64,14 +68,11 @@ const attributeFCP = (metric: FCPMetric): void => {
* value is a `DOMHighResTimeStamp`.
*/
export const onFCP = (
onReport: FCPReportCallbackWithAttribution,
onReport: (metric: FCPMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
unattributedOnFCP(
((metric: FCPMetricWithAttribution) => {
attributeFCP(metric);
onReport(metric);
}) as FCPReportCallback,
opts,
);
unattributedOnFCP((metric: FCPMetric) => {
const metricWithAttribution = attributeFCP(metric);
onReport(metricWithAttribution);
}, opts);
};
25 changes: 13 additions & 12 deletions src/attribution/onFID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,26 @@ import {getLoadState} from '../lib/getLoadState.js';
import {getSelector} from '../lib/getSelector.js';
import {onFID as unattributedOnFID} from '../onFID.js';
import {
FIDAttribution,
FIDMetric,
FIDMetricWithAttribution,
FIDReportCallback,
FIDReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';

const attributeFID = (metric: FIDMetric): void => {
const attributeFID = (metric: FIDMetric): FIDMetricWithAttribution => {
const fidEntry = metric.entries[0];
(metric as FIDMetricWithAttribution).attribution = {
const attribution: FIDAttribution = {
eventTarget: getSelector(fidEntry.target),
eventType: fidEntry.name,
eventTime: fidEntry.startTime,
eventEntry: fidEntry,
loadState: getLoadState(fidEntry.startTime),
};

// Cast to attribution metric so it can be populated.
const metricWithAttribution = metric as FIDMetricWithAttribution;
metricWithAttribution.attribution = attribution;
return metricWithAttribution;
};

/**
Expand All @@ -46,14 +50,11 @@ const attributeFID = (metric: FIDMetric): void => {
* page, it's possible that it will not be reported for some page loads._
*/
export const onFID = (
onReport: FIDReportCallbackWithAttribution,
onReport: (metric: FIDMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
unattributedOnFID(
((metric: FIDMetricWithAttribution) => {
attributeFID(metric);
onReport(metric);
}) as FIDReportCallback,
opts,
);
unattributedOnFID((metric: FIDMetric) => {
const metricWithAttribution = attributeFID(metric);
onReport(metricWithAttribution);
}, opts);
};
41 changes: 21 additions & 20 deletions src/attribution/onINP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import {observe} from '../lib/observe.js';
import {whenIdle} from '../lib/whenIdle.js';
import {onINP as unattributedOnINP} from '../onINP.js';
import {
INPAttribution,
INPMetric,
INPMetricWithAttribution,
INPReportCallback,
INPReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';

Expand Down Expand Up @@ -192,7 +191,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)!;
Expand Down Expand Up @@ -226,7 +225,7 @@ const attributeINP = (metric: INPMetric): void => {

const nextPaintTime = Math.max.apply(Math, nextPaintTimeCandidates);

(metric as INPMetricWithAttribution).attribution = {
const attribution: INPAttribution = {
interactionTarget: getSelector(
firstEntryWithTarget && firstEntryWithTarget.target,
),
Expand All @@ -240,6 +239,11 @@ const attributeINP = (metric: INPMetric): void => {
presentationDelay: Math.max(nextPaintTime - processingEnd, 0),
loadState: getLoadState(firstEntry.startTime),
};

// Cast to attribution metric so it can be populated.
const metricWithAttribution = metric as INPMetricWithAttribution;
metricWithAttribution.attribution = attribution;
return metricWithAttribution;
};

/**
Expand Down Expand Up @@ -270,25 +274,22 @@ const attributeINP = (metric: INPMetric): void => {
* during the same page load._
*/
export const onINP = (
onReport: INPReportCallbackWithAttribution,
onReport: (metric: INPMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
if (!loafObserver) {
loafObserver = observe('long-animation-frame', handleLoAFEntries);
}
unattributedOnINP(
((metric: INPMetricWithAttribution) => {
// Queue attribution and reporting in the next idle task.
// This is needed to increase the chances that all event entries that
// occurred between the user interaction and the next paint
// have been dispatched. Note: there is currently an experiment
// running in Chrome (EventTimingKeypressAndCompositionInteractionId)
// 123+ that if rolled out fully would make this no longer necessary.
whenIdle(() => {
attributeINP(metric);
onReport(metric);
});
}) as INPReportCallback,
opts,
);
unattributedOnINP((metric: INPMetric) => {
// Queue attribution and reporting in the next idle task.
// This is needed to increase the chances that all event entries that
// occurred between the user interaction and the next paint
// have been dispatched. Note: there is currently an experiment
// running in Chrome (EventTimingKeypressAndCompositionInteractionId)
// 123+ that if rolled out fully would make this no longer necessary.
whenIdle(() => {
const metricWithAttribution = attributeINP(metric);
onReport(metricWithAttribution);
});
}, opts);
};
51 changes: 23 additions & 28 deletions src/attribution/onLCP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ import {
LCPAttribution,
LCPMetric,
LCPMetricWithAttribution,
LCPReportCallback,
LCPReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';

const attributeLCP = (metric: LCPMetric) => {
if (metric.entries.length) {
const navigationEntry = getNavigationEntry();

if (navigationEntry) {
const responseStart = navigationEntry.responseStart;
if (isInvalidTimestamp(responseStart)) return;
brendankenny marked this conversation as resolved.
Show resolved Hide resolved
const attributeLCP = (metric: LCPMetric): LCPMetricWithAttribution => {
// Use a default object if no other attribution has been set.
let attribution: LCPAttribution = {
timeToFirstByte: 0,
resourceLoadDelay: 0,
resourceLoadDuration: 0,
elementRenderDelay: metric.value,
};

const navigationEntry = getNavigationEntry();
if (metric.entries.length && navigationEntry) {
const responseStart = navigationEntry.responseStart;
if (!isInvalidTimestamp(responseStart)) {
philipwalton marked this conversation as resolved.
Show resolved Hide resolved
const activationStart = navigationEntry.activationStart || 0;
const lcpEntry = metric.entries[metric.entries.length - 1];
const lcpResourceEntry =
Expand Down Expand Up @@ -62,7 +65,7 @@ const attributeLCP = (metric: LCPMetric) => {
lcpEntry.startTime - activationStart,
);

const attribution: LCPAttribution = {
attribution = {
element: getSelector(lcpEntry.element),
timeToFirstByte: ttfb,
resourceLoadDelay: lcpRequestStart - ttfb,
Expand All @@ -79,18 +82,13 @@ const attributeLCP = (metric: LCPMetric) => {
if (lcpResourceEntry) {
attribution.lcpResourceEntry = lcpResourceEntry;
}

(metric as LCPMetricWithAttribution).attribution = attribution;
return;
}
}
// Set an empty object if no other attribution has been set.
(metric as LCPMetricWithAttribution).attribution = {
timeToFirstByte: 0,
resourceLoadDelay: 0,
resourceLoadDuration: 0,
elementRenderDelay: metric.value,
};

// Cast to attribution metric so it can be populated.
const metricWithAttribution = metric as LCPMetricWithAttribution;
metricWithAttribution.attribution = attribution;
return metricWithAttribution;
};

/**
Expand All @@ -105,14 +103,11 @@ const attributeLCP = (metric: LCPMetric) => {
* been determined.
*/
export const onLCP = (
onReport: LCPReportCallbackWithAttribution,
onReport: (metric: LCPMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
unattributedOnLCP(
((metric: LCPMetricWithAttribution) => {
attributeLCP(metric);
onReport(metric);
}) as LCPReportCallback,
opts,
);
unattributedOnLCP((metric: LCPMetric) => {
const metricWithAttribution = attributeLCP(metric);
onReport(metricWithAttribution);
}, opts);
};
Loading
Loading