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
32 changes: 13 additions & 19 deletions src/attribution/onCLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@
import {getLoadState} from '../lib/getLoadState.js';
import {getSelector} from '../lib/getSelector.js';
import {onCLS as unattributedOnCLS} from '../onCLS.js';
import {
CLSReportCallback,
CLSReportCallbackWithAttribution,
CLSMetric,
CLSMetricWithAttribution,
ReportOpts,
} from '../types.js';
import {CLSMetric, CLSMetricWithAttribution, ReportOpts} from '../types.js';

const getLargestLayoutShiftEntry = (entries: LayoutShift[]) => {
return entries.reduce((a, b) => (a && a.value > b.value ? a : b));
Expand All @@ -33,26 +27,29 @@ 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,
largestShiftSource: largestSource,
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;
};

/**
Expand All @@ -77,14 +74,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);
};
35 changes: 15 additions & 20 deletions src/attribution/onFCP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,40 @@ import {getLoadState} from '../lib/getLoadState.js';
import {getNavigationEntry} from '../lib/getNavigationEntry.js';
import {isInvalidTimestamp} from '../lib/isInvalidTimestamp.js';
import {onFCP as unattributedOnFCP} from '../onFCP.js';
import {
FCPMetric,
FCPMetricWithAttribution,
FCPReportCallback,
FCPReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';
import {FCPMetric, FCPMetricWithAttribution, ReportOpts} from '../types.js';

const attributeFCP = (metric: FCPMetric): FCPMetricWithAttribution => {
const metricWithAttribution = metric as FCPMetricWithAttribution;
philipwalton marked this conversation as resolved.
Show resolved Hide resolved

const attributeFCP = (metric: FCPMetric): void => {
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;
philipwalton marked this conversation as resolved.
Show resolved Hide resolved

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;
};

/**
Expand All @@ -64,14 +62,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);
};
28 changes: 11 additions & 17 deletions src/attribution/onFID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,20 @@
import {getLoadState} from '../lib/getLoadState.js';
import {getSelector} from '../lib/getSelector.js';
import {onFID as unattributedOnFID} from '../onFID.js';
import {
FIDMetric,
FIDMetricWithAttribution,
FIDReportCallback,
FIDReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';
import {FIDMetric, FIDMetricWithAttribution, ReportOpts} from '../types.js';

const attributeFID = (metric: FIDMetric): FIDMetricWithAttribution => {
const metricWithAttribution = metric as FIDMetricWithAttribution;

const attributeFID = (metric: FIDMetric): void => {
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;
};

/**
Expand All @@ -46,14 +43,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);
};
43 changes: 18 additions & 25 deletions src/attribution/onINP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@ import {
import {observe} from '../lib/observe.js';
import {whenIdle} from '../lib/whenIdle.js';
import {onINP as unattributedOnINP} from '../onINP.js';
import {
INPMetric,
INPMetricWithAttribution,
INPReportCallback,
INPReportCallbackWithAttribution,
ReportOpts,
} from '../types.js';
import {INPMetric, INPMetricWithAttribution, ReportOpts} from '../types.js';

interface pendingEntriesGroup {
startTime: DOMHighResTimeStamp;
Expand Down Expand Up @@ -192,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)!;
Expand Down Expand Up @@ -226,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,
),
Expand All @@ -240,6 +235,7 @@ const attributeINP = (metric: INPMetric): void => {
presentationDelay: Math.max(nextPaintTime - processingEnd, 0),
loadState: getLoadState(firstEntry.startTime),
};
return metricWithAttribution;
};

/**
Expand Down Expand Up @@ -270,25 +266,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);
};
29 changes: 14 additions & 15 deletions src/attribution/onLCP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ import {
LCPAttribution,
LCPMetric,
LCPMetricWithAttribution,
LCPReportCallback,
LCPReportCallbackWithAttribution,
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;
brendankenny marked this conversation as resolved.
Show resolved Hide resolved
// TODO(bckenny): this is wrong.
if (isInvalidTimestamp(responseStart)) return metricWithAttribution;

const activationStart = navigationEntry.activationStart || 0;
const lcpEntry = metric.entries[metric.entries.length - 1];
Expand Down Expand Up @@ -80,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;
};

/**
Expand All @@ -105,14 +107,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