1717import  {  WINDOW  }  from  '../../types' ; 
1818import  {  bindReporter  }  from  './lib/bindReporter' ; 
1919import  {  initMetric  }  from  './lib/initMetric' ; 
20- import  {  DEFAULT_DURATION_THRESHOLD ,  estimateP98LongestInteraction ,  processInteractionEntry  }  from  './lib/interactions' ; 
20+ import  {  initUnique  }  from  './lib/initUnique' ; 
21+ import  {  InteractionManager  }  from  './lib/InteractionManager' ; 
2122import  {  observe  }  from  './lib/observe' ; 
22- import  {  onHidden  }  from  './lib/onHidden' ; 
2323import  {  initInteractionCountPolyfill  }  from  './lib/polyfills/interactionCountPolyfill' ; 
2424import  {  whenActivated  }  from  './lib/whenActivated' ; 
25- import  {  whenIdle  }  from  './lib/whenIdle ' ; 
26- import  type  {  INPMetric ,  MetricRatingThresholds ,   ReportOpts  }  from  './types' ; 
25+ import  {  whenIdleOrHidden  }  from  './lib/whenIdleOrHidden ' ; 
26+ import  type  {  INPMetric ,  INPReportOpts ,   MetricRatingThresholds  }  from  './types' ; 
2727
2828/** Thresholds for INP. See https://web.dev/articles/inp#what_is_a_good_inp_score */ 
2929export  const  INPThresholds : MetricRatingThresholds  =  [ 200 ,  500 ] ; 
3030
31+ // The default `durationThreshold` used across this library for observing 
32+ // `event` entries via PerformanceObserver. 
33+ const  DEFAULT_DURATION_THRESHOLD  =  40 ; 
34+ 
3135/** 
3236 * Calculates the [INP](https://web.dev/articles/inp) value for the current 
3337 * page and calls the `callback` function once the value is ready, along with 
3438 * the `event` performance entries reported for that interaction. The reported 
3539 * value is a `DOMHighResTimeStamp`. 
3640 * 
37-  * A custom `durationThreshold` configuration option can optionally be passed to 
38-  * control what `event-timing` entries are considered for INP reporting. The 
39-  * default threshold is `40`, which means INP scores of less than 40 are 
40-  * reported as 0. Note that this will not affect your 75th percentile INP value 
41-  * unless that value is also less than 40 (well below the recommended 
41+  * A custom `durationThreshold` configuration option can optionally be passed 
42+  * to control what `event-timing` entries are considered for INP reporting. The 
43+  * default threshold is `40`, which means INP scores of less than 40 will not 
44+  * be reported. To avoid reporting no interactions in these cases, the library 
45+  * will fall back to the input delay of the first interaction. Note that this 
46+  * will not affect your 75th percentile INP value unless that value is also 
47+  * less than 40 (well below the recommended 
4248 * [good](https://web.dev/articles/inp#what_is_a_good_inp_score) threshold). 
4349 * 
4450 * If the `reportAllChanges` configuration option is set to `true`, the 
@@ -55,9 +61,9 @@ export const INPThresholds: MetricRatingThresholds = [200, 500];
5561 * hidden. As a result, the `callback` function might be called multiple times 
5662 * during the same page load._ 
5763 */ 
58- export  const  onINP  =  ( onReport : ( metric : INPMetric )  =>  void ,  opts : ReportOpts  =  { } )  =>  { 
64+ export  const  onINP  =  ( onReport : ( metric : INPMetric )  =>  void ,  opts : INPReportOpts  =  { } )  =>  { 
5965  // Return if the browser doesn't support all APIs needed to measure INP. 
60-   if  ( ! ( ' PerformanceEventTiming'   in   WINDOW  &&  'interactionId'  in  PerformanceEventTiming . prototype ) )  { 
66+   if  ( ! ( globalThis . PerformanceEventTiming  &&  'interactionId'  in  PerformanceEventTiming . prototype ) )  { 
6167    return ; 
6268  } 
6369
@@ -69,20 +75,24 @@ export const onINP = (onReport: (metric: INPMetric) => void, opts: ReportOpts =
6975    // eslint-disable-next-line prefer-const 
7076    let  report : ReturnType < typeof  bindReporter > ; 
7177
78+     const  interactionManager  =  initUnique ( opts ,  InteractionManager ) ; 
79+ 
7280    const  handleEntries  =  ( entries : INPMetric [ 'entries' ] )  =>  { 
7381      // Queue the `handleEntries()` callback in the next idle task. 
7482      // This is needed to increase the chances that all event entries that 
7583      // occurred between the user interaction and the next paint 
7684      // have been dispatched. Note: there is currently an experiment 
7785      // running in Chrome (EventTimingKeypressAndCompositionInteractionId) 
7886      // 123+ that if rolled out fully may make this no longer necessary. 
79-       whenIdle ( ( )  =>  { 
80-         entries . forEach ( processInteractionEntry ) ; 
87+       whenIdleOrHidden ( ( )  =>  { 
88+         for  ( const  entry  of  entries )  { 
89+           interactionManager . _processEntry ( entry ) ; 
90+         } 
8191
82-         const  inp  =  estimateP98LongestInteraction ( ) ; 
92+         const  inp  =  interactionManager . _estimateP98LongestInteraction ( ) ; 
8393
84-         if  ( inp  &&  inp . latency  !==  metric . value )  { 
85-           metric . value  =  inp . latency ; 
94+         if  ( inp  &&  inp . _latency  !==  metric . value )  { 
95+           metric . value  =  inp . _latency ; 
8696          metric . entries  =  inp . entries ; 
8797          report ( ) ; 
8898        } 
@@ -96,7 +106,7 @@ export const onINP = (onReport: (metric: INPMetric) => void, opts: ReportOpts =
96106      // and performance. Running this callback for any interaction that spans 
97107      // just one or two frames is likely not worth the insight that could be 
98108      // gained. 
99-       durationThreshold : opts . durationThreshold  !=   null  ?  opts . durationThreshold  :  DEFAULT_DURATION_THRESHOLD , 
109+       durationThreshold : opts . durationThreshold  ??  DEFAULT_DURATION_THRESHOLD , 
100110    } ) ; 
101111
102112    report  =  bindReporter ( onReport ,  metric ,  INPThresholds ,  opts . reportAllChanges ) ; 
@@ -106,9 +116,11 @@ export const onINP = (onReport: (metric: INPMetric) => void, opts: ReportOpts =
106116      // where the first interaction is less than the `durationThreshold`. 
107117      po . observe ( {  type : 'first-input' ,  buffered : true  } ) ; 
108118
109-       onHidden ( ( )  =>  { 
110-         handleEntries ( po . takeRecords ( )  as  INPMetric [ 'entries' ] ) ; 
111-         report ( true ) ; 
119+       WINDOW . document ?. addEventListener ( 'visibilitychange' ,  ( )  =>  { 
120+         if  ( WINDOW . document ?. visibilityState  ===  'hidden' )  { 
121+           handleEntries ( po . takeRecords ( )  as  INPMetric [ 'entries' ] ) ; 
122+           report ( true ) ; 
123+         } 
112124      } ) ; 
113125    } 
114126  } ) ; 
0 commit comments