From 491debaabfe255b3c046c79db34defee146e5eec Mon Sep 17 00:00:00 2001 From: Connor Clark Date: Fri, 10 Oct 2025 09:46:44 -0700 Subject: [PATCH] Add onEachInteraction to onINP options This adds an "onEachInteraction" option to onINP, which exposes to us each interaction. It also calls attributeINP on each value given to the callback. --- dist/modules/attribution/onINP.js | 13 +++++++++++-- src/attribution/onINP.ts | 47 +++++++++++++++++++++++++++++++++++++++- src/types/inp.ts | 1 + 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/dist/modules/attribution/onINP.js b/dist/modules/attribution/onINP.js index 479a00f..d295263 100644 --- a/dist/modules/attribution/onINP.js +++ b/dist/modules/attribution/onINP.js @@ -121,7 +121,11 @@ group.startTime = Math.min(entry.startTime, group.startTime); group.processingStart = Math.min(entry.processingStart, group.processingStart); group.processingEnd = Math.max(entry.processingEnd, group.processingEnd); - group.entries.push(entry); + // Entries are not needed in DevTools since we're only displaying the + // summary information, and also emitting events as they come in. Stop + // holding a reference to avoid memory issues. + // See https://crbug.com/484342204 + // group.entries.push(entry); break; } } @@ -135,7 +139,12 @@ group = { startTime: entry.startTime, processingStart: entry.processingStart, processingEnd: entry.processingEnd, renderTime, - entries: [entry], + // Entries are not needed in DevTools since we're only displaying the + // summary information, and also emiting events as they come in. Stop + // holding a reference to avoid memory issues. + // See https://crbug.com/484342204 + // entries: [entry], + entries: [], }; pendingEntriesGroups.push(group); } diff --git a/src/attribution/onINP.ts b/src/attribution/onINP.ts index 479a00f..d295263 100644 --- a/src/attribution/onINP.ts +++ b/src/attribution/onINP.ts @@ -165,7 +165,11 @@ group.processingEnd = Math.max( entry.processingEnd, group.processingEnd, ); - group.entries.push(entry); + // Entries are not needed in DevTools since we're only displaying the + // summary information, and also emitting events as they come in. Stop + // holding a reference to avoid memory issues. + // See https://crbug.com/484342204 + // group.entries.push(entry); break; } @@ -186,7 +190,12 @@ processingStart: entry.processingStart, processingEnd: entry.processingEnd, renderTime, - entries: [entry], + // Entries are not needed in DevTools since we're only displaying the + // summary information, and also emiting events as they come in. Stop + // holding a reference to avoid memory issues. + // See https://crbug.com/484342204 + // entries: [entry], + entries: [], }; pendingEntriesGroups.push(group); @@ -240,7 +240,40 @@ export const onINP = ( cleanupPending = false; }; - interactionManager._onBeforeProcessingEntry = groupEntriesByRenderTime; + async function handleOnEachInteractionCallback( + entry: PerformanceEventTiming, + ) { + if (!opts.onEachInteraction) { + return; + } + + // Wait a microtask so this "pre" processing callback actually + // becomes a "post" processing callback. + void (await Promise.resolve()); + if (!entry.interactionId) { + return; + } + + const interaction = attributeINP({ + entries: [entry], + // The only value we really need for `attributeINP` is `entries` + // Everything else is included to fill out the type. + name: 'INP', + rating: 'good', + value: entry.duration, + delta: entry.duration, + navigationType: 'navigate', + id: 'N/A', + }); + opts.onEachInteraction(interaction); + } + + interactionManager._onBeforeProcessingEntry = ( + entry: PerformanceEventTiming, + ) => { + void handleOnEachInteractionCallback(entry); + groupEntriesByRenderTime(entry); + }; interactionManager._onAfterProcessingINPCandidate = saveInteractionTarget; const getIntersectingLoAFs = ( diff --git a/src/types/inp.ts b/src/types/inp.ts index 7dfa9bb..f87a2c5 100644 --- a/src/types/inp.ts +++ b/src/types/inp.ts @@ -27,6 +27,7 @@ export interface INPReportOpts extends ReportOpts { export interface INPAttributionReportOpts extends AttributionReportOpts { durationThreshold?: number; + onEachInteraction?: (interaction: INPMetricWithAttribution) => void; } /** -- 2.51.0.760.g7b8bcc2412-goog