@newrelic/browser-agent 1.321.0-rc.6 → 1.321.0-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cjs/common/constants/env.cdn.js +1 -1
  2. package/dist/cjs/common/constants/env.npm.js +1 -1
  3. package/dist/cjs/common/vitals/cumulative-layout-shift.js +6 -4
  4. package/dist/cjs/common/vitals/first-contentful-paint.js +5 -3
  5. package/dist/cjs/common/vitals/interaction-to-next-paint.js +10 -4
  6. package/dist/cjs/common/vitals/largest-contentful-paint.js +5 -3
  7. package/dist/cjs/common/vitals/register-vital.js +25 -0
  8. package/dist/cjs/common/vitals/time-to-first-byte.js +4 -2
  9. package/dist/cjs/common/vitals/vital-metric.js +3 -2
  10. package/dist/cjs/interfaces/registered-iframe-entity.js +9 -3
  11. package/dist/esm/common/constants/env.cdn.js +1 -1
  12. package/dist/esm/common/constants/env.npm.js +1 -1
  13. package/dist/esm/common/vitals/cumulative-layout-shift.js +6 -4
  14. package/dist/esm/common/vitals/first-contentful-paint.js +5 -3
  15. package/dist/esm/common/vitals/interaction-to-next-paint.js +10 -4
  16. package/dist/esm/common/vitals/largest-contentful-paint.js +5 -3
  17. package/dist/esm/common/vitals/register-vital.js +19 -0
  18. package/dist/esm/common/vitals/time-to-first-byte.js +4 -2
  19. package/dist/esm/common/vitals/vital-metric.js +3 -2
  20. package/dist/esm/interfaces/registered-iframe-entity.js +9 -3
  21. package/dist/tsconfig.tsbuildinfo +1 -1
  22. package/dist/types/common/vitals/cumulative-layout-shift.d.ts.map +1 -1
  23. package/dist/types/common/vitals/first-contentful-paint.d.ts.map +1 -1
  24. package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -1
  25. package/dist/types/common/vitals/largest-contentful-paint.d.ts.map +1 -1
  26. package/dist/types/common/vitals/register-vital.d.ts +13 -0
  27. package/dist/types/common/vitals/register-vital.d.ts.map +1 -0
  28. package/dist/types/common/vitals/time-to-first-byte.d.ts.map +1 -1
  29. package/dist/types/common/vitals/vital-metric.d.ts +1 -1
  30. package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
  31. package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -1
  32. package/package.json +2 -2
  33. package/src/common/vitals/cumulative-layout-shift.js +5 -3
  34. package/src/common/vitals/first-contentful-paint.js +5 -3
  35. package/src/common/vitals/interaction-to-next-paint.js +8 -3
  36. package/src/common/vitals/largest-contentful-paint.js +5 -3
  37. package/src/common/vitals/register-vital.js +19 -0
  38. package/src/common/vitals/time-to-first-byte.js +4 -2
  39. package/src/common/vitals/vital-metric.js +3 -2
  40. package/src/interfaces/registered-iframe-entity.js +7 -2
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.321.0-rc.6";
20
+ const VERSION = exports.VERSION = "1.321.0-rc.8";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.321.0-rc.6";
20
+ const VERSION = exports.VERSION = "1.321.0-rc.8";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -7,15 +7,16 @@ exports.cumulativeLayoutShift = void 0;
7
7
  var _attribution = require("web-vitals/attribution");
8
8
  var _constants = require("./constants");
9
9
  var _vitalMetric = require("./vital-metric");
10
+ var _registerVital = require("./register-vital");
10
11
  var _runtime = require("../constants/runtime");
11
12
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
14
  * SPDX-License-Identifier: Apache-2.0
14
15
  */
15
16
 
16
17
  const cumulativeLayoutShift = exports.cumulativeLayoutShift = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, x => x);
17
18
  if (_runtime.isBrowserScope) {
18
- (0, _attribution.onCLS)(({
19
+ const handleCLS = ({
19
20
  value,
20
21
  attribution,
21
22
  id
@@ -31,7 +32,8 @@ if (_runtime.isBrowserScope) {
31
32
  value,
32
33
  attrs
33
34
  });
34
- }, {
35
+ };
36
+ (0, _registerVital.registerVital)(() => (0, _attribution.onCLS)(handleCLS, {
35
37
  reportAllChanges: true
36
- });
38
+ }));
37
39
  }
@@ -8,8 +8,9 @@ var _attribution = require("web-vitals/attribution");
8
8
  var _runtime = require("../constants/runtime");
9
9
  var _constants = require("./constants");
10
10
  var _vitalMetric = require("./vital-metric");
11
+ var _registerVital = require("./register-vital");
11
12
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
14
  * SPDX-License-Identifier: Apache-2.0
14
15
  */
15
16
 
@@ -37,7 +38,7 @@ if (_runtime.isBrowserScope) {
37
38
  // ignore
38
39
  }
39
40
  } else {
40
- (0, _attribution.onFCP)(({
41
+ const handleFCP = ({
41
42
  value,
42
43
  attribution
43
44
  }) => {
@@ -51,6 +52,7 @@ if (_runtime.isBrowserScope) {
51
52
  value,
52
53
  attrs
53
54
  });
54
- });
55
+ };
56
+ (0, _registerVital.registerVital)(() => (0, _attribution.onFCP)(handleFCP));
55
57
  }
56
58
  }
@@ -7,20 +7,25 @@ exports.interactionToNextPaint = void 0;
7
7
  var _attribution = require("web-vitals/attribution");
8
8
  var _vitalMetric = require("./vital-metric");
9
9
  var _constants = require("./constants");
10
+ var _registerVital = require("./register-vital");
10
11
  var _runtime = require("../constants/runtime");
11
12
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
14
  * SPDX-License-Identifier: Apache-2.0
14
15
  */
15
16
 
16
17
  const interactionToNextPaint = exports.interactionToNextPaint = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.INTERACTION_TO_NEXT_PAINT);
17
18
  if (_runtime.isBrowserScope) {
18
19
  /* Interaction-to-Next-Paint */
19
- (0, _attribution.onINP)(({
20
+ const handleINP = ({
20
21
  value,
21
22
  attribution,
22
- id
23
+ id,
24
+ entries
23
25
  }) => {
26
+ /* web-vitals v6 reports a synthetic INP (value 8, no entries, no interaction attribution) after bfcache restores when
27
+ every interaction stayed below the duration threshold; skip those so only measured interactions are reported, as in v4 */
28
+ if (!entries?.length) return;
24
29
  const attrs = {
25
30
  metricId: id,
26
31
  eventTarget: attribution.interactionTarget,
@@ -40,5 +45,6 @@ if (_runtime.isBrowserScope) {
40
45
  value,
41
46
  attrs
42
47
  });
43
- });
48
+ };
49
+ (0, _registerVital.registerVital)(() => (0, _attribution.onINP)(handleINP));
44
50
  }
@@ -7,6 +7,7 @@ exports.largestContentfulPaint = void 0;
7
7
  var _attribution = require("web-vitals/attribution");
8
8
  var _vitalMetric = require("./vital-metric");
9
9
  var _constants = require("./constants");
10
+ var _registerVital = require("./register-vital");
10
11
  var _runtime = require("../constants/runtime");
11
12
  var _cleanUrl = require("../url/clean-url");
12
13
  /**
@@ -16,7 +17,7 @@ var _cleanUrl = require("../url/clean-url");
16
17
 
17
18
  const largestContentfulPaint = exports.largestContentfulPaint = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.LARGEST_CONTENTFUL_PAINT);
18
19
  if (_runtime.isBrowserScope) {
19
- (0, _attribution.onLCP)(({
20
+ const handleLCP = ({
20
21
  value,
21
22
  attribution
22
23
  }) => {
@@ -36,11 +37,12 @@ if (_runtime.isBrowserScope) {
36
37
  attrs.eid = lcpEntry.id;
37
38
  if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
38
39
  }
39
- if (attribution.element) attrs.element = attribution.element;
40
+ if (attribution.target) attrs.element = attribution.target; // renamed from `element` in web-vitals v4->v5; NR attr name kept for backwards compatibility
40
41
  if (attribution.url) attrs.elUrl = (0, _cleanUrl.cleanURL)(attribution.url);
41
42
  largestContentfulPaint.update({
42
43
  value,
43
44
  attrs
44
45
  });
45
- });
46
+ };
47
+ (0, _registerVital.registerVital)(() => (0, _attribution.onLCP)(handleLCP));
46
48
  }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.registerVital = registerVital;
7
+ /**
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
+ * SPDX-License-Identifier: Apache-2.0
10
+ */
11
+
12
+ /**
13
+ * Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
14
+ * throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
15
+ * metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
16
+ * the feature that imported it.
17
+ * @param {() => void} register
18
+ */
19
+ function registerVital(register) {
20
+ try {
21
+ register();
22
+ } catch (e) {
23
+ // metric unavailable in this environment
24
+ }
25
+ }
@@ -7,6 +7,7 @@ exports.timeToFirstByte = void 0;
7
7
  var _runtime = require("../constants/runtime");
8
8
  var _constants = require("./constants");
9
9
  var _vitalMetric = require("./vital-metric");
10
+ var _registerVital = require("./register-vital");
10
11
  var _attribution = require("web-vitals/attribution");
11
12
  /**
12
13
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
@@ -24,7 +25,7 @@ const timeToFirstByte = exports.timeToFirstByte = new _vitalMetric.VitalMetric(_
24
25
  * - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
25
26
  */
26
27
  if (_runtime.isBrowserScope && (0, _runtime.getNavigationEntry)() && !_runtime.isiOS && window === window.parent) {
27
- (0, _attribution.onTTFB)(({
28
+ const handleTTFB = ({
28
29
  value,
29
30
  attribution
30
31
  }) => {
@@ -35,7 +36,8 @@ if (_runtime.isBrowserScope && (0, _runtime.getNavigationEntry)() && !_runtime.i
35
36
  navigationEntry: attribution.navigationEntry
36
37
  }
37
38
  });
38
- });
39
+ };
40
+ (0, _registerVital.registerVital)(() => (0, _attribution.onTTFB)(handleTTFB));
39
41
  } else {
40
42
  if (!timeToFirstByte.isValid) {
41
43
  const entry = {};
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.VitalMetric = void 0;
7
7
  /**
8
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
9
  * SPDX-License-Identifier: Apache-2.0
10
10
  */
11
11
  class VitalMetric {
@@ -24,7 +24,8 @@ class VitalMetric {
24
24
  const state = {
25
25
  value: this.roundingMethod(value),
26
26
  name: this.name,
27
- attrs
27
+ // optional web-vitals attribution fields are omitted rather than being serialized as null attributes
28
+ attrs: Object.fromEntries(Object.entries(attrs).filter(([, attrValue]) => attrValue !== undefined))
28
29
  };
29
30
  this.history.push(state);
30
31
  this.#subscribers.forEach(cb => {
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.RegisteredIframeEntity = void 0;
7
7
  var _webVitals = require("web-vitals");
8
8
  var _runtime = require("../common/constants/runtime");
9
+ var _registerVital = require("../common/vitals/register-vital");
9
10
  var _iframe = require("../common/dom/iframe");
10
11
  var _now = require("../common/timing/now");
11
12
  var _console = require("../common/util/console");
@@ -191,9 +192,14 @@ class RegisteredIframeEntity {
191
192
  */
192
193
  #setupVitalsListeners() {
193
194
  VITALS.forEach(([vitalFn, property]) => {
194
- vitalFn(({
195
- value
195
+ (0, _registerVital.registerVital)(() => vitalFn(({
196
+ value,
197
+ entries: vitalEntries
196
198
  }) => {
199
+ /* web-vitals v6 reports a synthetic INP (value 8, no entries) after bfcache restores when
200
+ every interaction stayed below the duration threshold; skip those so only measured
201
+ interactions are reported, matching the guard in src/common/vitals/interaction-to-next-paint.js */
202
+ if (property === 'inp' && !vitalEntries?.length) return;
197
203
  this.metadata.vitals[property].value = value;
198
204
  this.#postMessageToParent(_iframeConstants.IFRAME_VITALS_UPDATE, {
199
205
  entries: [{
@@ -203,7 +209,7 @@ class RegisteredIframeEntity {
203
209
  });
204
210
  }, {
205
211
  reportAllChanges: property === 'cls' || property === 'inp'
206
- });
212
+ }));
207
213
  });
208
214
  }
209
215
 
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.321.0-rc.6";
14
+ export const VERSION = "1.321.0-rc.8";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.321.0-rc.6";
14
+ export const VERSION = "1.321.0-rc.8";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -1,14 +1,15 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onCLS } from 'web-vitals/attribution';
6
6
  import { VITAL_NAMES } from './constants';
7
7
  import { VitalMetric } from './vital-metric';
8
+ import { registerVital } from './register-vital';
8
9
  import { isBrowserScope } from '../constants/runtime';
9
10
  export const cumulativeLayoutShift = new VitalMetric(VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, x => x);
10
11
  if (isBrowserScope) {
11
- onCLS(({
12
+ const handleCLS = ({
12
13
  value,
13
14
  attribution,
14
15
  id
@@ -24,7 +25,8 @@ if (isBrowserScope) {
24
25
  value,
25
26
  attrs
26
27
  });
27
- }, {
28
+ };
29
+ registerVital(() => onCLS(handleCLS, {
28
30
  reportAllChanges: true
29
- });
31
+ }));
30
32
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onFCP } from 'web-vitals/attribution';
@@ -7,6 +7,7 @@ import { onFCP } from 'web-vitals/attribution';
7
7
  import { iOSBelow16, initiallyHidden, isBrowserScope } from '../constants/runtime';
8
8
  import { VITAL_NAMES } from './constants';
9
9
  import { VitalMetric } from './vital-metric';
10
+ import { registerVital } from './register-vital';
10
11
  export const firstContentfulPaint = new VitalMetric(VITAL_NAMES.FIRST_CONTENTFUL_PAINT);
11
12
 
12
13
  /* First Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
@@ -29,7 +30,7 @@ if (isBrowserScope) {
29
30
  // ignore
30
31
  }
31
32
  } else {
32
- onFCP(({
33
+ const handleFCP = ({
33
34
  value,
34
35
  attribution
35
36
  }) => {
@@ -43,6 +44,7 @@ if (isBrowserScope) {
43
44
  value,
44
45
  attrs
45
46
  });
46
- });
47
+ };
48
+ registerVital(() => onFCP(handleFCP));
47
49
  }
48
50
  }
@@ -1,19 +1,24 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onINP } from 'web-vitals/attribution';
6
6
  import { VitalMetric } from './vital-metric';
7
7
  import { VITAL_NAMES } from './constants';
8
+ import { registerVital } from './register-vital';
8
9
  import { isBrowserScope } from '../constants/runtime';
9
10
  export const interactionToNextPaint = new VitalMetric(VITAL_NAMES.INTERACTION_TO_NEXT_PAINT);
10
11
  if (isBrowserScope) {
11
12
  /* Interaction-to-Next-Paint */
12
- onINP(({
13
+ const handleINP = ({
13
14
  value,
14
15
  attribution,
15
- id
16
+ id,
17
+ entries
16
18
  }) => {
19
+ /* web-vitals v6 reports a synthetic INP (value 8, no entries, no interaction attribution) after bfcache restores when
20
+ every interaction stayed below the duration threshold; skip those so only measured interactions are reported, as in v4 */
21
+ if (!entries?.length) return;
17
22
  const attrs = {
18
23
  metricId: id,
19
24
  eventTarget: attribution.interactionTarget,
@@ -33,5 +38,6 @@ if (isBrowserScope) {
33
38
  value,
34
39
  attrs
35
40
  });
36
- });
41
+ };
42
+ registerVital(() => onINP(handleINP));
37
43
  }
@@ -5,11 +5,12 @@
5
5
  import { onLCP } from 'web-vitals/attribution';
6
6
  import { VitalMetric } from './vital-metric';
7
7
  import { VITAL_NAMES } from './constants';
8
+ import { registerVital } from './register-vital';
8
9
  import { initiallyHidden, isBrowserScope } from '../constants/runtime';
9
10
  import { cleanURL } from '../url/clean-url';
10
11
  export const largestContentfulPaint = new VitalMetric(VITAL_NAMES.LARGEST_CONTENTFUL_PAINT);
11
12
  if (isBrowserScope) {
12
- onLCP(({
13
+ const handleLCP = ({
13
14
  value,
14
15
  attribution
15
16
  }) => {
@@ -29,11 +30,12 @@ if (isBrowserScope) {
29
30
  attrs.eid = lcpEntry.id;
30
31
  if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
31
32
  }
32
- if (attribution.element) attrs.element = attribution.element;
33
+ if (attribution.target) attrs.element = attribution.target; // renamed from `element` in web-vitals v4->v5; NR attr name kept for backwards compatibility
33
34
  if (attribution.url) attrs.elUrl = cleanURL(attribution.url);
34
35
  largestContentfulPaint.update({
35
36
  value,
36
37
  attrs
37
38
  });
38
- });
39
+ };
40
+ registerVital(() => onLCP(handleLCP));
39
41
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
8
+ * throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
9
+ * metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
10
+ * the feature that imported it.
11
+ * @param {() => void} register
12
+ */
13
+ export function registerVital(register) {
14
+ try {
15
+ register();
16
+ } catch (e) {
17
+ // metric unavailable in this environment
18
+ }
19
+ }
@@ -5,6 +5,7 @@
5
5
  import { globalScope, isBrowserScope, isiOS, originTime, getNavigationEntry } from '../constants/runtime';
6
6
  import { VITAL_NAMES } from './constants';
7
7
  import { VitalMetric } from './vital-metric';
8
+ import { registerVital } from './register-vital';
8
9
  import { onTTFB } from 'web-vitals/attribution';
9
10
  export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE);
10
11
 
@@ -17,7 +18,7 @@ export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE);
17
18
  * - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
18
19
  */
19
20
  if (isBrowserScope && getNavigationEntry() && !isiOS && window === window.parent) {
20
- onTTFB(({
21
+ const handleTTFB = ({
21
22
  value,
22
23
  attribution
23
24
  }) => {
@@ -28,7 +29,8 @@ if (isBrowserScope && getNavigationEntry() && !isiOS && window === window.parent
28
29
  navigationEntry: attribution.navigationEntry
29
30
  }
30
31
  });
31
- });
32
+ };
33
+ registerVital(() => onTTFB(handleTTFB));
32
34
  } else {
33
35
  if (!timeToFirstByte.isValid) {
34
36
  const entry = {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export class VitalMetric {
@@ -18,7 +18,8 @@ export class VitalMetric {
18
18
  const state = {
19
19
  value: this.roundingMethod(value),
20
20
  name: this.name,
21
- attrs
21
+ // optional web-vitals attribution fields are omitted rather than being serialized as null attributes
22
+ attrs: Object.fromEntries(Object.entries(attrs).filter(([, attrValue]) => attrValue !== undefined))
22
23
  };
23
24
  this.history.push(state);
24
25
  this.#subscribers.forEach(cb => {
@@ -7,6 +7,7 @@
7
7
  import { onCLS, onFCP, onINP, onLCP } from 'web-vitals';
8
8
  // internal
9
9
  import { globalScope, isBrowserScope } from '../common/constants/runtime';
10
+ import { registerVital } from '../common/vitals/register-vital';
10
11
  import { isIFrameWindow } from '../common/dom/iframe';
11
12
  import { now } from '../common/timing/now';
12
13
  import { warn } from '../common/util/console';
@@ -183,9 +184,14 @@ export class RegisteredIframeEntity {
183
184
  */
184
185
  #setupVitalsListeners() {
185
186
  VITALS.forEach(([vitalFn, property]) => {
186
- vitalFn(({
187
- value
187
+ registerVital(() => vitalFn(({
188
+ value,
189
+ entries: vitalEntries
188
190
  }) => {
191
+ /* web-vitals v6 reports a synthetic INP (value 8, no entries) after bfcache restores when
192
+ every interaction stayed below the duration threshold; skip those so only measured
193
+ interactions are reported, matching the guard in src/common/vitals/interaction-to-next-paint.js */
194
+ if (property === 'inp' && !vitalEntries?.length) return;
189
195
  this.metadata.vitals[property].value = value;
190
196
  this.#postMessageToParent(IFRAME_VITALS_UPDATE, {
191
197
  entries: [{
@@ -195,7 +201,7 @@ export class RegisteredIframeEntity {
195
201
  });
196
202
  }, {
197
203
  reportAllChanges: property === 'cls' || property === 'inp'
198
- });
204
+ }));
199
205
  });
200
206
  }
201
207
 
@@ -1 +1 @@
1
- {"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/events.js","../src/common/constants/iframe-constants.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/connector.js","../src/common/harvest/harvester.js","../src/common/harvest/send.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/payloads/payloads.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/session/session-key.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/add-url.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/browser-stack-matchers.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/short-circuit.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/webdriver-detection.js","../src/common/v2/constants.js","../src/common/v2/mfe-vitals.js","../src/common/v2/script-correlation.js","../src/common/v2/script-tracker-constants.js","../src/common/v2/script-tracker.js","../src/common/v2/timing-factory.js","../src/common/v2/utils.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/aggregate_v2/index.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/interfaces/registered-entity.js","../src/interfaces/registered-iframe-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/iframe-message-handler.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
1
+ {"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/events.js","../src/common/constants/iframe-constants.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/connector.js","../src/common/harvest/harvester.js","../src/common/harvest/send.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/payloads/payloads.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/session/session-key.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/add-url.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/browser-stack-matchers.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/short-circuit.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/webdriver-detection.js","../src/common/v2/constants.js","../src/common/v2/mfe-vitals.js","../src/common/v2/script-correlation.js","../src/common/v2/script-tracker-constants.js","../src/common/v2/script-tracker.js","../src/common/v2/timing-factory.js","../src/common/v2/utils.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/register-vital.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/aggregate_v2/index.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/interfaces/registered-entity.js","../src/interfaces/registered-iframe-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/iframe-message-handler.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
@@ -1 +1 @@
1
- {"version":3,"file":"cumulative-layout-shift.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/cumulative-layout-shift.js"],"names":[],"mappings":"AASA,gDAAmG;4BAHvE,gBAAgB"}
1
+ {"version":3,"file":"cumulative-layout-shift.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/cumulative-layout-shift.js"],"names":[],"mappings":"AAUA,gDAAmG;4BAJvE,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"first-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/first-contentful-paint.js"],"names":[],"mappings":"AAUA,+CAAuF;4BAF3D,gBAAgB"}
1
+ {"version":3,"file":"first-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/first-contentful-paint.js"],"names":[],"mappings":"AAWA,+CAAuF;4BAH3D,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"interaction-to-next-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/interaction-to-next-paint.js"],"names":[],"mappings":"AASA,iDAA4F;4BAJhE,gBAAgB"}
1
+ {"version":3,"file":"interaction-to-next-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/interaction-to-next-paint.js"],"names":[],"mappings":"AAUA,iDAA4F;4BALhE,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"largest-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/largest-contentful-paint.js"],"names":[],"mappings":"AAUA,iDAA2F;4BAL/D,gBAAgB"}
1
+ {"version":3,"file":"largest-contentful-paint.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/largest-contentful-paint.js"],"names":[],"mappings":"AAWA,iDAA2F;4BAN/D,gBAAgB"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
7
+ * throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
8
+ * metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
9
+ * the feature that imported it.
10
+ * @param {() => void} register
11
+ */
12
+ export function registerVital(register: () => void): void;
13
+ //# sourceMappingURL=register-vital.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-vital.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/register-vital.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,wCAFW,MAAM,IAAI,QAQpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"time-to-first-byte.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/time-to-first-byte.js"],"names":[],"mappings":"AASA,0CAA8E;4BAHlD,gBAAgB"}
1
+ {"version":3,"file":"time-to-first-byte.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/time-to-first-byte.js"],"names":[],"mappings":"AAUA,0CAA8E;4BAJlD,gBAAgB"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export class VitalMetric {
@@ -1 +1 @@
1
- {"version":3,"file":"vital-metric.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/vital-metric.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IAIE,4CAIC;IAND,eAAY;IAGV,UAAgB;IAChB,UAAe;IACf,oBAAwF;IAG1F;;;aAgBC;IAED,mBAMC;IAED,uBAEC;IAED,uEAMC;;CACF"}
1
+ {"version":3,"file":"vital-metric.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/vital-metric.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;IAIE,4CAIC;IAND,eAAY;IAGV,UAAgB;IAChB,UAAe;IACf,oBAAwF;IAG1F;;;aAiBC;IAED,mBAMC;IAED,uBAEC;IAED,uEAMC;;CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"AAsBA;;;;GAIG;AAEH;;;;;;GAMG;AACH;IAkDE;;;OAGG;IACH,kBAFW,sBAAsB,EAsDhC;IAzGD,kCAAkC;IAClC,UADW,mBAAmB,CAW7B;IAyBD,mBANa,OAAO,EAQnB;IAbD;;;;;;OAMG;IACH,eAFa,OAAO,CAInB;IAgTD;;;;;OAKG;IACH,oBAHW,MAAM,eACN,MAAM,QAIhB;IAED;;;;;;;OAOG;IACH,cAFa,OAAO,CAAC,IAAI,CAAC,CAgBzB;IAED;;;;;SAKK;IACL,6BAHa,MAAM,eACN,MAAM,QAIlB;IAED;;;;;;OAMG;IACH,cAJW,MAAM,YACN;QAAC,KAAK,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC,GACvF,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAAC,CAI7F;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,IAAI,YAC1B,OAAO,QAIjB;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,qBACZ,MAAM,QAIhB;IAED;;;;;OAKG;IACH,iBAHW,MAAM,GAAC,IAAI,iBACX,OAAO,QAIjB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;;MAKE;IACF,aAHW,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,QAIpF;;CACF;0BA1cY,OAAO,mCAAmC,EAAE,WAAW;kCACvD,OAAO,mCAAmC,EAAE,mBAAmB;qCAC/D,OAAO,mCAAmC,EAAE,sBAAsB"}
1
+ {"version":3,"file":"registered-iframe-entity.d.ts","sourceRoot":"","sources":["../../../src/interfaces/registered-iframe-entity.js"],"names":[],"mappings":"AAuBA;;;;GAIG;AAEH;;;;;;GAMG;AACH;IAkDE;;;OAGG;IACH,kBAFW,sBAAsB,EAsDhC;IAzGD,kCAAkC;IAClC,UADW,mBAAmB,CAW7B;IAyBD,mBANa,OAAO,EAQnB;IAbD;;;;;;OAMG;IACH,eAFa,OAAO,CAInB;IAoTD;;;;;OAKG;IACH,oBAHW,MAAM,eACN,MAAM,QAIhB;IAED;;;;;;;OAOG;IACH,cAFa,OAAO,CAAC,IAAI,CAAC,CAgBzB;IAED;;;;;SAKK;IACL,6BAHa,MAAM,eACN,MAAM,QAIlB;IAED;;;;;;OAMG;IACH,cAJW,MAAM,YACN;QAAC,KAAK,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,GAAC,eAAe,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAC,GACvF,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAC,CAAC,CAI7F;IAED;;;;;;OAMG;IACH,yBAJW,MAAM,SACN,MAAM,GAAC,MAAM,GAAC,OAAO,GAAC,IAAI,YAC1B,OAAO,QAIjB;IAED;;;;;OAKG;IACH,mBAHW,KAAK,GAAC,MAAM,qBACZ,MAAM,QAIhB;IAED;;;;;OAKG;IACH,iBAHW,MAAM,GAAC,IAAI,iBACX,OAAO,QAIjB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,IAAI,QAMrB;IAED;;;;;MAKE;IACF,aAHW,MAAM,YACN;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,GAAC,OAAO,GAAC,OAAO,GAAC,MAAM,GAAC,MAAM,CAAA;KAAC,QAIpF;;CACF;0BA9cY,OAAO,mCAAmC,EAAE,WAAW;kCACvD,OAAO,mCAAmC,EAAE,mBAAmB;qCAC/D,OAAO,mCAAmC,EAAE,sBAAsB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.321.0-rc.6",
3
+ "version": "1.321.0-rc.8",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -225,7 +225,7 @@
225
225
  "dependencies": {
226
226
  "@newrelic/rrweb": "^1.1.2",
227
227
  "fflate": "0.8.2",
228
- "web-vitals": "4.2.4"
228
+ "web-vitals": "6.2.1"
229
229
  },
230
230
  "devDependencies": {
231
231
  "@babel/cli": "^7.23.4",
@@ -1,16 +1,17 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onCLS } from 'web-vitals/attribution'
6
6
  import { VITAL_NAMES } from './constants'
7
7
  import { VitalMetric } from './vital-metric'
8
+ import { registerVital } from './register-vital'
8
9
  import { isBrowserScope } from '../constants/runtime'
9
10
 
10
11
  export const cumulativeLayoutShift = new VitalMetric(VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT, (x) => x)
11
12
 
12
13
  if (isBrowserScope) {
13
- onCLS(({ value, attribution, id }) => {
14
+ const handleCLS = ({ value, attribution, id }) => {
14
15
  const attrs = {
15
16
  metricId: id,
16
17
  largestShiftTarget: attribution.largestShiftTarget,
@@ -19,5 +20,6 @@ if (isBrowserScope) {
19
20
  loadState: attribution.loadState
20
21
  }
21
22
  cumulativeLayoutShift.update({ value, attrs })
22
- }, { reportAllChanges: true })
23
+ }
24
+ registerVital(() => onCLS(handleCLS, { reportAllChanges: true }))
23
25
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onFCP } from 'web-vitals/attribution'
@@ -7,6 +7,7 @@ import { onFCP } from 'web-vitals/attribution'
7
7
  import { iOSBelow16, initiallyHidden, isBrowserScope } from '../constants/runtime'
8
8
  import { VITAL_NAMES } from './constants'
9
9
  import { VitalMetric } from './vital-metric'
10
+ import { registerVital } from './register-vital'
10
11
 
11
12
  export const firstContentfulPaint = new VitalMetric(VITAL_NAMES.FIRST_CONTENTFUL_PAINT)
12
13
 
@@ -27,7 +28,7 @@ if (isBrowserScope) {
27
28
  // ignore
28
29
  }
29
30
  } else {
30
- onFCP(({ value, attribution }) => {
31
+ const handleFCP = ({ value, attribution }) => {
31
32
  if (initiallyHidden || firstContentfulPaint.isValid) return
32
33
  const attrs = {
33
34
  timeToFirstByte: attribution.timeToFirstByte,
@@ -35,6 +36,7 @@ if (isBrowserScope) {
35
36
  loadState: attribution.loadState
36
37
  }
37
38
  firstContentfulPaint.update({ value, attrs })
38
- })
39
+ }
40
+ registerVital(() => onFCP(handleFCP))
39
41
  }
40
42
  }
@@ -1,17 +1,21 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  import { onINP } from 'web-vitals/attribution'
6
6
  import { VitalMetric } from './vital-metric'
7
7
  import { VITAL_NAMES } from './constants'
8
+ import { registerVital } from './register-vital'
8
9
  import { isBrowserScope } from '../constants/runtime'
9
10
 
10
11
  export const interactionToNextPaint = new VitalMetric(VITAL_NAMES.INTERACTION_TO_NEXT_PAINT)
11
12
 
12
13
  if (isBrowserScope) {
13
14
  /* Interaction-to-Next-Paint */
14
- onINP(({ value, attribution, id }) => {
15
+ const handleINP = ({ value, attribution, id, entries }) => {
16
+ /* web-vitals v6 reports a synthetic INP (value 8, no entries, no interaction attribution) after bfcache restores when
17
+ every interaction stayed below the duration threshold; skip those so only measured interactions are reported, as in v4 */
18
+ if (!entries?.length) return
15
19
  const attrs = {
16
20
  metricId: id,
17
21
  eventTarget: attribution.interactionTarget, // event* attrs deprecated in v4, kept for NR backwards compatibility
@@ -26,5 +30,6 @@ if (isBrowserScope) {
26
30
  loadState: attribution.loadState
27
31
  }
28
32
  interactionToNextPaint.update({ value, attrs })
29
- })
33
+ }
34
+ registerVital(() => onINP(handleINP))
30
35
  }
@@ -5,13 +5,14 @@
5
5
  import { onLCP } from 'web-vitals/attribution'
6
6
  import { VitalMetric } from './vital-metric'
7
7
  import { VITAL_NAMES } from './constants'
8
+ import { registerVital } from './register-vital'
8
9
  import { initiallyHidden, isBrowserScope } from '../constants/runtime'
9
10
  import { cleanURL } from '../url/clean-url'
10
11
 
11
12
  export const largestContentfulPaint = new VitalMetric(VITAL_NAMES.LARGEST_CONTENTFUL_PAINT)
12
13
 
13
14
  if (isBrowserScope) {
14
- onLCP(({ value, attribution }) => {
15
+ const handleLCP = ({ value, attribution }) => {
15
16
  /* Largest Contentful Paint - As of WV v3, it still imperfectly tries to detect document vis state asap and isn't supposed to report if page starts hidden. */
16
17
  if (initiallyHidden || largestContentfulPaint.isValid) return
17
18
 
@@ -28,9 +29,10 @@ if (isBrowserScope) {
28
29
  attrs.eid = lcpEntry.id
29
30
  if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName
30
31
  }
31
- if (attribution.element) attrs.element = attribution.element
32
+ if (attribution.target) attrs.element = attribution.target // renamed from `element` in web-vitals v4->v5; NR attr name kept for backwards compatibility
32
33
  if (attribution.url) attrs.elUrl = cleanURL(attribution.url)
33
34
 
34
35
  largestContentfulPaint.update({ value, attrs })
35
- })
36
+ }
37
+ registerVital(() => onLCP(handleLCP))
36
38
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Invokes a web-vitals registration (e.g. `onLCP(callback)`), tolerating environments where the registration itself
8
+ * throws. web-vitals >= 5 reads Performance Timeline APIs such as `performance.getEntriesByType` unguarded when a
9
+ * metric is registered; where those are missing the metric is simply never reported, instead of the throw failing
10
+ * the feature that imported it.
11
+ * @param {() => void} register
12
+ */
13
+ export function registerVital (register) {
14
+ try {
15
+ register()
16
+ } catch (e) {
17
+ // metric unavailable in this environment
18
+ }
19
+ }
@@ -5,6 +5,7 @@
5
5
  import { globalScope, isBrowserScope, isiOS, originTime, getNavigationEntry } from '../constants/runtime'
6
6
  import { VITAL_NAMES } from './constants'
7
7
  import { VitalMetric } from './vital-metric'
8
+ import { registerVital } from './register-vital'
8
9
  import { onTTFB } from 'web-vitals/attribution'
9
10
 
10
11
  export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE)
@@ -18,10 +19,11 @@ export const timeToFirstByte = new VitalMetric(VITAL_NAMES.TIME_TO_FIRST_BYTE)
18
19
  * - onTTFB relies on a truthy `responseStart` value, should ensure that exists before relying on it (seen to be falsy in certain Electron.js cases for instance)
19
20
  */
20
21
  if (isBrowserScope && getNavigationEntry() && !isiOS && window === window.parent) {
21
- onTTFB(({ value, attribution }) => {
22
+ const handleTTFB = ({ value, attribution }) => {
22
23
  if (timeToFirstByte.isValid) return
23
24
  timeToFirstByte.update({ value, attrs: { navigationEntry: attribution.navigationEntry } })
24
- })
25
+ }
26
+ registerVital(() => onTTFB(handleTTFB))
25
27
  } else {
26
28
  if (!timeToFirstByte.isValid) {
27
29
  const entry = {}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
2
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
  export class VitalMetric {
@@ -17,7 +17,8 @@ export class VitalMetric {
17
17
  const state = {
18
18
  value: this.roundingMethod(value),
19
19
  name: this.name,
20
- attrs
20
+ // optional web-vitals attribution fields are omitted rather than being serialized as null attributes
21
+ attrs: Object.fromEntries(Object.entries(attrs).filter(([, attrValue]) => attrValue !== undefined))
21
22
  }
22
23
 
23
24
  this.history.push(state)
@@ -7,6 +7,7 @@
7
7
  import { onCLS, onFCP, onINP, onLCP } from 'web-vitals'
8
8
  // internal
9
9
  import { globalScope, isBrowserScope } from '../common/constants/runtime'
10
+ import { registerVital } from '../common/vitals/register-vital'
10
11
  import { isIFrameWindow } from '../common/dom/iframe'
11
12
  import { now } from '../common/timing/now'
12
13
  import { warn } from '../common/util/console'
@@ -179,12 +180,16 @@ export class RegisteredIframeEntity {
179
180
  */
180
181
  #setupVitalsListeners () {
181
182
  VITALS.forEach(([vitalFn, property]) => {
182
- vitalFn(({ value }) => {
183
+ registerVital(() => vitalFn(({ value, entries: vitalEntries }) => {
184
+ /* web-vitals v6 reports a synthetic INP (value 8, no entries) after bfcache restores when
185
+ every interaction stayed below the duration threshold; skip those so only measured
186
+ interactions are reported, matching the guard in src/common/vitals/interaction-to-next-paint.js */
187
+ if (property === 'inp' && !vitalEntries?.length) return
183
188
  this.metadata.vitals[property].value = value
184
189
  this.#postMessageToParent(IFRAME_VITALS_UPDATE, {
185
190
  entries: [{ property, value }]
186
191
  })
187
- }, { reportAllChanges: property === 'cls' || property === 'inp' })
192
+ }, { reportAllChanges: property === 'cls' || property === 'inp' }))
188
193
  })
189
194
  }
190
195