@newrelic/browser-agent 1.283.2 → 1.284.0

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 (44) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +1 -1
  3. package/dist/cjs/common/constants/env.cdn.js +2 -2
  4. package/dist/cjs/common/constants/env.npm.js +2 -2
  5. package/dist/cjs/common/vitals/constants.js +1 -1
  6. package/dist/cjs/common/vitals/interaction-to-next-paint.js +20 -1
  7. package/dist/cjs/features/jserrors/shared/cast-error.js +5 -5
  8. package/dist/cjs/features/metrics/aggregate/index.js +3 -0
  9. package/dist/cjs/features/metrics/instrument/index.js +7 -0
  10. package/dist/cjs/features/page_view_timing/aggregate/index.js +1 -2
  11. package/dist/cjs/features/session_replay/shared/stylesheet-evaluator.js +1 -1
  12. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +0 -7
  13. package/dist/esm/common/constants/env.cdn.js +2 -2
  14. package/dist/esm/common/constants/env.npm.js +2 -2
  15. package/dist/esm/common/vitals/constants.js +1 -1
  16. package/dist/esm/common/vitals/interaction-to-next-paint.js +20 -1
  17. package/dist/esm/features/jserrors/shared/cast-error.js +5 -5
  18. package/dist/esm/features/metrics/aggregate/index.js +3 -0
  19. package/dist/esm/features/metrics/instrument/index.js +12 -2
  20. package/dist/esm/features/page_view_timing/aggregate/index.js +2 -3
  21. package/dist/esm/features/session_replay/shared/stylesheet-evaluator.js +1 -1
  22. package/dist/esm/features/session_trace/aggregate/trace/storage.js +0 -7
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/dist/types/common/vitals/constants.d.ts +1 -1
  25. package/dist/types/common/vitals/interaction-to-next-paint.d.ts +1 -0
  26. package/dist/types/common/vitals/interaction-to-next-paint.d.ts.map +1 -1
  27. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  28. package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
  29. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  30. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  31. package/package.json +3 -3
  32. package/src/common/vitals/constants.js +1 -1
  33. package/src/common/vitals/interaction-to-next-paint.js +21 -2
  34. package/src/features/jserrors/shared/cast-error.js +5 -5
  35. package/src/features/metrics/aggregate/index.js +3 -0
  36. package/src/features/metrics/instrument/index.js +14 -2
  37. package/src/features/page_view_timing/aggregate/index.js +2 -3
  38. package/src/features/session_replay/shared/stylesheet-evaluator.js +1 -1
  39. package/src/features/session_trace/aggregate/trace/storage.js +0 -5
  40. package/dist/cjs/common/vitals/first-input-delay.js +0 -36
  41. package/dist/esm/common/vitals/first-input-delay.js +0 -29
  42. package/dist/types/common/vitals/first-input-delay.d.ts +0 -3
  43. package/dist/types/common/vitals/first-input-delay.d.ts.map +0 -1
  44. package/src/common/vitals/first-input-delay.js +0 -28
package/CHANGELOG.md CHANGED
@@ -3,6 +3,21 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [1.284.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.283.2...v1.284.0) (2025-03-04)
7
+
8
+
9
+ ### Features
10
+
11
+ * Remove FID, replace first interaction detection with INP ([#1395](https://github.com/newrelic/newrelic-browser-agent/issues/1395)) ([436ce94](https://github.com/newrelic/newrelic-browser-agent/commit/436ce94eac45e3e28d6b3be6fe9f9fcb15ac1e9b))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * bump web-vitals from 4.2.3 to 4.2.4 ([#1379](https://github.com/newrelic/newrelic-browser-agent/issues/1379)) ([d0b5b83](https://github.com/newrelic/newrelic-browser-agent/commit/d0b5b83ec50755771e9d4afbf984937f5ab00458))
17
+ * Deduplicate Prefixes on Unhandled Promise Rejection Messages ([#1397](https://github.com/newrelic/newrelic-browser-agent/issues/1397)) ([474fc4f](https://github.com/newrelic/newrelic-browser-agent/commit/474fc4fbc6f7b3fe6b396ce86acaaa1bc712e5fd))
18
+ * Guard against non-CSSStyleSheet when fixing sheets for replay ([#1396](https://github.com/newrelic/newrelic-browser-agent/issues/1396)) ([b929aba](https://github.com/newrelic/newrelic-browser-agent/commit/b929abafb5ab04287fc1f0a07397ff739f314c38))
19
+ * Upgrade rrweb to 18 ([#1383](https://github.com/newrelic/newrelic-browser-agent/issues/1383)) ([46b691b](https://github.com/newrelic/newrelic-browser-agent/commit/46b691b671d2090f47707eea6dfa5ae8f31d0c7b))
20
+
6
21
  ## [1.283.2](https://github.com/newrelic/newrelic-browser-agent/compare/v1.283.1...v1.283.2) (2025-02-21)
7
22
 
8
23
 
package/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright [yyyy] [name of copyright owner]
189
+ Copyright New Relic, Inc. All rights reserved.
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.283.2";
20
+ const VERSION = exports.VERSION = "1.284.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -33,4 +33,4 @@ const DIST_METHOD = exports.DIST_METHOD = 'CDN';
33
33
  /**
34
34
  * Exposes the lib version of rrweb
35
35
  */
36
- const RRWEB_VERSION = exports.RRWEB_VERSION = "^2.0.0-alpha.17";
36
+ const RRWEB_VERSION = exports.RRWEB_VERSION = "^2.0.0-alpha.18";
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.283.2";
20
+ const VERSION = exports.VERSION = "1.284.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -34,4 +34,4 @@ const DIST_METHOD = exports.DIST_METHOD = 'NPM';
34
34
  /**
35
35
  * Exposes the lib version of rrweb
36
36
  */
37
- const RRWEB_VERSION = exports.RRWEB_VERSION = "^2.0.0-alpha.17";
37
+ const RRWEB_VERSION = exports.RRWEB_VERSION = "^2.0.0-alpha.18";
@@ -11,7 +11,7 @@ exports.VITAL_NAMES = void 0;
11
11
  const VITAL_NAMES = exports.VITAL_NAMES = {
12
12
  FIRST_PAINT: 'fp',
13
13
  FIRST_CONTENTFUL_PAINT: 'fcp',
14
- FIRST_INPUT_DELAY: 'fi',
14
+ FIRST_INTERACTION: 'fi',
15
15
  LARGEST_CONTENTFUL_PAINT: 'lcp',
16
16
  CUMULATIVE_LAYOUT_SHIFT: 'cls',
17
17
  INTERACTION_TO_NEXT_PAINT: 'inp',
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.interactionToNextPaint = void 0;
6
+ exports.interactionToNextPaint = exports.firstInteraction = void 0;
7
7
  var _attribution = require("web-vitals/attribution");
8
8
  var _vitalMetric = require("./vital-metric");
9
9
  var _constants = require("./constants");
@@ -14,7 +14,21 @@ var _runtime = require("../constants/runtime");
14
14
  */
15
15
 
16
16
  const interactionToNextPaint = exports.interactionToNextPaint = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.INTERACTION_TO_NEXT_PAINT);
17
+ // Note: First Interaction is a legacy NR timing event, not an actual CWV metric
18
+ // ('fi' used to be detected via FID. It is now represented by the first INP)
19
+ const firstInteraction = exports.firstInteraction = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.FIRST_INTERACTION);
17
20
  if (_runtime.isBrowserScope) {
21
+ const recordFirstInteraction = attribution => {
22
+ firstInteraction.update({
23
+ value: attribution.interactionTime,
24
+ attrs: {
25
+ type: attribution.interactionType,
26
+ eventTarget: attribution.interactionTarget,
27
+ loadState: attribution.loadState
28
+ }
29
+ });
30
+ };
31
+
18
32
  /* Interaction-to-Next-Paint */
19
33
  (0, _attribution.onINP)(({
20
34
  value,
@@ -40,5 +54,10 @@ if (_runtime.isBrowserScope) {
40
54
  value,
41
55
  attrs
42
56
  });
57
+
58
+ // preserve the original behavior where FID is not reported if the page is hidden before the first interaction
59
+ if (!firstInteraction.isValid && !_runtime.initiallyHidden) {
60
+ recordFirstInteraction(attribution);
61
+ }
43
62
  });
44
63
  }
@@ -37,7 +37,7 @@ function castError(error) {
37
37
  * @returns {Error} An Error object with the message as the casted reason
38
38
  */
39
39
  function castPromiseRejectionEvent(promiseRejectionEvent) {
40
- const prefix = 'Unhandled Promise Rejection';
40
+ const prefix = 'Unhandled Promise Rejection: ';
41
41
 
42
42
  /**
43
43
  * If the casted return value is falsy like this, it will get dropped and not produce an error event for harvest.
@@ -47,14 +47,14 @@ function castPromiseRejectionEvent(promiseRejectionEvent) {
47
47
  if (!promiseRejectionEvent?.reason) return;
48
48
  if (canTrustError(promiseRejectionEvent.reason)) {
49
49
  try {
50
- promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
51
- return castError(promiseRejectionEvent.reason);
50
+ if (!promiseRejectionEvent.reason.message.startsWith(prefix)) promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message;
52
51
  } catch (e) {
53
- return castError(promiseRejectionEvent.reason);
52
+ // failed to modify the message, do nothing else
54
53
  }
54
+ return castError(promiseRejectionEvent.reason);
55
55
  }
56
56
  const error = castError(promiseRejectionEvent.reason);
57
- error.message = prefix + ': ' + error?.message;
57
+ if (!(error.message || '').startsWith(prefix)) error.message = prefix + error.message;
58
58
  return error;
59
59
  }
60
60
 
@@ -135,6 +135,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
135
135
  });
136
136
  }
137
137
 
138
+ // webdriver detection
139
+ if (navigator.webdriver) this.storeSupportabilityMetrics('Generic/WebDriver/Detected');
140
+
138
141
  // WATCHABLE_WEB_SOCKET_EVENTS.forEach(tag => {
139
142
  // registerHandler('buffered-' + WEBSOCKET_TAG + tag, (...args) => {
140
143
  // handleWebsocketEvents(this.storeSupportabilityMetrics.bind(this), tag, ...args)
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.Metrics = exports.Instrument = void 0;
7
+ var _runtime = require("../../../common/constants/runtime");
8
+ var _handle = require("../../../common/event-emitter/handle");
7
9
  var _instrumentBase = require("../../utils/instrument-base");
8
10
  var _constants = require("../constants");
9
11
  /**
@@ -26,6 +28,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
26
28
  // })
27
29
  // })
28
30
 
31
+ if (_runtime.isBrowserScope) {
32
+ document.addEventListener('securitypolicyviolation', e => {
33
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/CSPViolation/Detected'], undefined, this.featureName, this.ee);
34
+ });
35
+ }
29
36
  this.importAggregator(agentRef);
30
37
  }
31
38
  }
@@ -12,7 +12,6 @@ var _features = require("../../../loaders/features/features");
12
12
  var _aggregateBase = require("../../utils/aggregate-base");
13
13
  var _cumulativeLayoutShift = require("../../../common/vitals/cumulative-layout-shift");
14
14
  var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
15
- var _firstInputDelay = require("../../../common/vitals/first-input-delay");
16
15
  var _firstPaint = require("../../../common/vitals/first-paint");
17
16
  var _interactionToNextPaint = require("../../../common/vitals/interaction-to-next-paint");
18
17
  var _largestContentfulPaint = require("../../../common/vitals/largest-contentful-paint");
@@ -42,8 +41,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
42
41
  this.waitForFlags([]).then(() => {
43
42
  _firstPaint.firstPaint.subscribe(this.#handleVitalMetric);
44
43
  _firstContentfulPaint.firstContentfulPaint.subscribe(this.#handleVitalMetric);
45
- _firstInputDelay.firstInputDelay.subscribe(this.#handleVitalMetric);
46
44
  _largestContentfulPaint.largestContentfulPaint.subscribe(this.#handleVitalMetric);
45
+ _interactionToNextPaint.firstInteraction.subscribe(this.#handleVitalMetric);
47
46
  _interactionToNextPaint.interactionToNextPaint.subscribe(this.#handleVitalMetric);
48
47
  _timeToFirstByte.timeToFirstByte.subscribe(({
49
48
  attrs
@@ -30,7 +30,7 @@ class StylesheetEvaluator {
30
30
  this.#brokenSheets = [];
31
31
  if (_runtime.isBrowserScope) {
32
32
  for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
33
- if (!this.#evaluated.has(document.styleSheets[i])) {
33
+ if (!this.#evaluated.has(document.styleSheets[i]) && document.styleSheets[i] instanceof CSSStyleSheet) {
34
34
  this.#evaluated.add(document.styleSheets[i]);
35
35
  try {
36
36
  // eslint-disable-next-line
@@ -144,13 +144,6 @@ class TraceStorage {
144
144
  this.storeTiming({
145
145
  [name]: value
146
146
  });
147
- if (hasFID(name, attrs)) this.storeEvent({
148
- type: 'fid',
149
- target: 'document'
150
- }, 'document', value, value + attrs.fid);
151
- function hasFID(name, attrs) {
152
- return name === 'fi' && !!attrs && typeof attrs.fid === 'number';
153
- }
154
147
  }
155
148
  storeTiming(timingEntry, isAbsoluteTimestamp = false) {
156
149
  if (!timingEntry) return;
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.283.2";
14
+ export const VERSION = "1.284.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -27,4 +27,4 @@ export const DIST_METHOD = 'CDN';
27
27
  /**
28
28
  * Exposes the lib version of rrweb
29
29
  */
30
- export const RRWEB_VERSION = "^2.0.0-alpha.17";
30
+ export const RRWEB_VERSION = "^2.0.0-alpha.18";
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.283.2";
14
+ export const VERSION = "1.284.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -28,4 +28,4 @@ export const DIST_METHOD = 'NPM';
28
28
  /**
29
29
  * Exposes the lib version of rrweb
30
30
  */
31
- export const RRWEB_VERSION = "^2.0.0-alpha.17";
31
+ export const RRWEB_VERSION = "^2.0.0-alpha.18";
@@ -5,7 +5,7 @@
5
5
  export const VITAL_NAMES = {
6
6
  FIRST_PAINT: 'fp',
7
7
  FIRST_CONTENTFUL_PAINT: 'fcp',
8
- FIRST_INPUT_DELAY: 'fi',
8
+ FIRST_INTERACTION: 'fi',
9
9
  LARGEST_CONTENTFUL_PAINT: 'lcp',
10
10
  CUMULATIVE_LAYOUT_SHIFT: 'cls',
11
11
  INTERACTION_TO_NEXT_PAINT: 'inp',
@@ -5,9 +5,23 @@
5
5
  import { onINP } from 'web-vitals/attribution';
6
6
  import { VitalMetric } from './vital-metric';
7
7
  import { VITAL_NAMES } from './constants';
8
- import { isBrowserScope } from '../constants/runtime';
8
+ import { initiallyHidden, isBrowserScope } from '../constants/runtime';
9
9
  export const interactionToNextPaint = new VitalMetric(VITAL_NAMES.INTERACTION_TO_NEXT_PAINT);
10
+ // Note: First Interaction is a legacy NR timing event, not an actual CWV metric
11
+ // ('fi' used to be detected via FID. It is now represented by the first INP)
12
+ export const firstInteraction = new VitalMetric(VITAL_NAMES.FIRST_INTERACTION);
10
13
  if (isBrowserScope) {
14
+ const recordFirstInteraction = attribution => {
15
+ firstInteraction.update({
16
+ value: attribution.interactionTime,
17
+ attrs: {
18
+ type: attribution.interactionType,
19
+ eventTarget: attribution.interactionTarget,
20
+ loadState: attribution.loadState
21
+ }
22
+ });
23
+ };
24
+
11
25
  /* Interaction-to-Next-Paint */
12
26
  onINP(({
13
27
  value,
@@ -33,5 +47,10 @@ if (isBrowserScope) {
33
47
  value,
34
48
  attrs
35
49
  });
50
+
51
+ // preserve the original behavior where FID is not reported if the page is hidden before the first interaction
52
+ if (!firstInteraction.isValid && !initiallyHidden) {
53
+ recordFirstInteraction(attribution);
54
+ }
36
55
  });
37
56
  }
@@ -29,7 +29,7 @@ export function castError(error) {
29
29
  * @returns {Error} An Error object with the message as the casted reason
30
30
  */
31
31
  export function castPromiseRejectionEvent(promiseRejectionEvent) {
32
- const prefix = 'Unhandled Promise Rejection';
32
+ const prefix = 'Unhandled Promise Rejection: ';
33
33
 
34
34
  /**
35
35
  * If the casted return value is falsy like this, it will get dropped and not produce an error event for harvest.
@@ -39,14 +39,14 @@ export function castPromiseRejectionEvent(promiseRejectionEvent) {
39
39
  if (!promiseRejectionEvent?.reason) return;
40
40
  if (canTrustError(promiseRejectionEvent.reason)) {
41
41
  try {
42
- promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message;
43
- return castError(promiseRejectionEvent.reason);
42
+ if (!promiseRejectionEvent.reason.message.startsWith(prefix)) promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message;
44
43
  } catch (e) {
45
- return castError(promiseRejectionEvent.reason);
44
+ // failed to modify the message, do nothing else
46
45
  }
46
+ return castError(promiseRejectionEvent.reason);
47
47
  }
48
48
  const error = castError(promiseRejectionEvent.reason);
49
- error.message = prefix + ': ' + error?.message;
49
+ if (!(error.message || '').startsWith(prefix)) error.message = prefix + error.message;
50
50
  return error;
51
51
  }
52
52
 
@@ -128,6 +128,9 @@ export class Aggregate extends AggregateBase {
128
128
  });
129
129
  }
130
130
 
131
+ // webdriver detection
132
+ if (navigator.webdriver) this.storeSupportabilityMetrics('Generic/WebDriver/Detected');
133
+
131
134
  // WATCHABLE_WEB_SOCKET_EVENTS.forEach(tag => {
132
135
  // registerHandler('buffered-' + WEBSOCKET_TAG + tag, (...args) => {
133
136
  // handleWebsocketEvents(this.storeSupportabilityMetrics.bind(this), tag, ...args)
@@ -3,10 +3,15 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
+ import { isBrowserScope } from '../../../common/constants/runtime';
7
+ import { handle } from '../../../common/event-emitter/handle';
8
+ import { InstrumentBase } from '../../utils/instrument-base';
9
+ import { FEATURE_NAME,
10
+ // WATCHABLE_WEB_SOCKET_EVENTS,
11
+ SUPPORTABILITY_METRIC_CHANNEL } from '../constants';
6
12
  // import { handle } from '../../../common/event-emitter/handle'
7
13
  // import { WEBSOCKET_TAG, wrapWebSocket } from '../../../common/wrap/wrap-websocket'
8
- import { InstrumentBase } from '../../utils/instrument-base';
9
- import { FEATURE_NAME } from '../constants';
14
+
10
15
  export class Instrument extends InstrumentBase {
11
16
  static featureName = FEATURE_NAME;
12
17
  constructor(agentRef, auto = true) {
@@ -19,6 +24,11 @@ export class Instrument extends InstrumentBase {
19
24
  // })
20
25
  // })
21
26
 
27
+ if (isBrowserScope) {
28
+ document.addEventListener('securitypolicyviolation', e => {
29
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/CSPViolation/Detected'], undefined, this.featureName, this.ee);
30
+ });
31
+ }
22
32
  this.importAggregator(agentRef);
23
33
  }
24
34
  }
@@ -11,9 +11,8 @@ import { FEATURE_NAMES } from '../../../loaders/features/features';
11
11
  import { AggregateBase } from '../../utils/aggregate-base';
12
12
  import { cumulativeLayoutShift } from '../../../common/vitals/cumulative-layout-shift';
13
13
  import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint';
14
- import { firstInputDelay } from '../../../common/vitals/first-input-delay';
15
14
  import { firstPaint } from '../../../common/vitals/first-paint';
16
- import { interactionToNextPaint } from '../../../common/vitals/interaction-to-next-paint';
15
+ import { firstInteraction, interactionToNextPaint } from '../../../common/vitals/interaction-to-next-paint';
17
16
  import { largestContentfulPaint } from '../../../common/vitals/largest-contentful-paint';
18
17
  import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte';
19
18
  import { subscribeToVisibilityChange } from '../../../common/window/page-visibility';
@@ -36,8 +35,8 @@ export class Aggregate extends AggregateBase {
36
35
  this.waitForFlags([]).then(() => {
37
36
  firstPaint.subscribe(this.#handleVitalMetric);
38
37
  firstContentfulPaint.subscribe(this.#handleVitalMetric);
39
- firstInputDelay.subscribe(this.#handleVitalMetric);
40
38
  largestContentfulPaint.subscribe(this.#handleVitalMetric);
39
+ firstInteraction.subscribe(this.#handleVitalMetric);
41
40
  interactionToNextPaint.subscribe(this.#handleVitalMetric);
42
41
  timeToFirstByte.subscribe(({
43
42
  attrs
@@ -23,7 +23,7 @@ class StylesheetEvaluator {
23
23
  this.#brokenSheets = [];
24
24
  if (isBrowserScope) {
25
25
  for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
26
- if (!this.#evaluated.has(document.styleSheets[i])) {
26
+ if (!this.#evaluated.has(document.styleSheets[i]) && document.styleSheets[i] instanceof CSSStyleSheet) {
27
27
  this.#evaluated.add(document.styleSheets[i]);
28
28
  try {
29
29
  // eslint-disable-next-line
@@ -137,13 +137,6 @@ export class TraceStorage {
137
137
  this.storeTiming({
138
138
  [name]: value
139
139
  });
140
- if (hasFID(name, attrs)) this.storeEvent({
141
- type: 'fid',
142
- target: 'document'
143
- }, 'document', value, value + attrs.fid);
144
- function hasFID(name, attrs) {
145
- return name === 'fi' && !!attrs && typeof attrs.fid === 'number';
146
- }
147
140
  }
148
141
  storeTiming(timingEntry, isAbsoluteTimestamp = false) {
149
142
  if (!timingEntry) return;
@@ -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.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/runtime.js","../src/common/constants/shared-channel.js","../src/common/context/shared-context.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/harvester.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/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.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/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/obfuscate.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/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-input-delay.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/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-jsonp.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-mutation.js","../src/common/wrap/wrap-promise.js","../src/common/wrap/wrap-timer.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/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/index.js","../src/features/metrics/aggregate/websocket-detection.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/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/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/spa/constants.js","../src/features/spa/index.js","../src/features/spa/aggregate/index.js","../src/features/spa/aggregate/interaction-node.js","../src/features/spa/aggregate/interaction.js","../src/features/spa/aggregate/serializer.js","../src/features/spa/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/event-store-manager.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/features/utils/lazy-feature-loader.js","../src/features/utils/nr1-debugger.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/api-methods.js","../src/loaders/api/api.js","../src/loaders/api/apiAsync.js","../src/loaders/api/interaction-types.js","../src/loaders/configure/configure.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.7.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.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/runtime.js","../src/common/constants/shared-channel.js","../src/common/context/shared-context.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/harvester.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/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.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/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/obfuscate.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/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/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-jsonp.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-mutation.js","../src/common/wrap/wrap-promise.js","../src/common/wrap/wrap-timer.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/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/index.js","../src/features/metrics/aggregate/websocket-detection.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/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/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/spa/constants.js","../src/features/spa/index.js","../src/features/spa/aggregate/index.js","../src/features/spa/aggregate/interaction-node.js","../src/features/spa/aggregate/interaction.js","../src/features/spa/aggregate/serializer.js","../src/features/spa/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/event-store-manager.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/features/utils/lazy-feature-loader.js","../src/features/utils/nr1-debugger.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/api-methods.js","../src/loaders/api/api.js","../src/loaders/api/apiAsync.js","../src/loaders/api/interaction-types.js","../src/loaders/configure/configure.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.7.3"}
@@ -1,7 +1,7 @@
1
1
  export namespace VITAL_NAMES {
2
2
  let FIRST_PAINT: string;
3
3
  let FIRST_CONTENTFUL_PAINT: string;
4
- let FIRST_INPUT_DELAY: string;
4
+ let FIRST_INTERACTION: string;
5
5
  let LARGEST_CONTENTFUL_PAINT: string;
6
6
  let CUMULATIVE_LAYOUT_SHIFT: string;
7
7
  let INTERACTION_TO_NEXT_PAINT: string;
@@ -1,3 +1,4 @@
1
1
  export const interactionToNextPaint: VitalMetric;
2
+ export const firstInteraction: VitalMetric;
2
3
  import { VitalMetric } from './vital-metric';
3
4
  //# sourceMappingURL=interaction-to-next-paint.d.ts.map
@@ -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":"AASA,iDAA4F;AAG5F,2CAA8E;4BAPlD,gBAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/metrics/aggregate/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,2BAoBC;IAED,iCAAsE;IAEtE,wDAKC;IAED,iDAKC;IAED,qBAmEC;IAED,0BAOC;CACF;8BA1H6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/metrics/aggregate/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,2BAoBC;IAED,iCAAsE;IAEtE,wDAKC;IAED,iDAKC;IAED,qBAsEC;IAED,0BAOC;CACF;8BA7H6B,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/metrics/instrument/index.js"],"names":[],"mappings":"AAUA;IACE,2BAAiC;IACjC,2CAWC;CACF;AAED,wCAAiC;+BAnBF,6BAA6B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/metrics/instrument/index.js"],"names":[],"mappings":"AAgBA;IACE,2BAAiC;IACjC,2CAiBC;CACF;AAED,wCAAiC;+BA/BF,6BAA6B"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AAqBA;IACE,2BAAiC;IAMjC,2BA4BC;IA1BC,4BAA+B;IA4BjC;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED,mDAuBC;IAED,gDAUC;IAGD,qCAuBC;;CACF;8BAzH6B,4BAA4B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IAMjC,2BA4BC;IA1BC,4BAA+B;IA4BjC;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED,mDAuBC;IAED,gDAUC;IAGD,qCAuBC;;CACF;8BAxH6B,4BAA4B"}
@@ -1 +1 @@
1
- {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AA6BA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,2BAA4B;IAI1B,YAAoB;IAGtB,gGAAgG;IAChG,yBAcC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;MAgBC;IAED,mEA6BC;IAED,oDAOC;IAED,mEAuBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAGC;IAID,mBAEC;IAED,aAEC;IAED;;;;;;;QAEC;IAED,cAMC;IAED,mBAEC;IAED,kBAEC;;CACF"}
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../../src/features/session_trace/aggregate/trace/storage.js"],"names":[],"mappings":"AA6BA,+HAA+H;AAC/H;IAQE,yBAEC;IATD,kBAAa;IACb,UAAU;IACV,0BAA4B;IAC5B,wBAAmB;IACnB,2BAA4B;IAI1B,YAAoB;IAGtB,gGAAgG;IAChG,yBAcC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAED,oEAAoE;IACpE;;;;MAgBC;IAED,mEA6BC;IAED,oDAEC;IAED,mEAuBC;IAGD,uEAcC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDAEC;IAID,qCAaC;IAGD,qEAGC;IAGD,mEAGC;IAID,mBAEC;IAED,aAEC;IAED;;;;;;;QAEC;IAED,cAMC;IAED,mBAEC;IAED,kBAEC;;CACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newrelic/browser-agent",
3
- "version": "1.283.2",
3
+ "version": "1.284.0",
4
4
  "private": false,
5
5
  "author": "New Relic Browser Agent Team <browser-agent@newrelic.com>",
6
6
  "description": "New Relic Browser Agent",
@@ -207,8 +207,8 @@
207
207
  },
208
208
  "dependencies": {
209
209
  "fflate": "0.8.2",
210
- "rrweb": "^2.0.0-alpha.17",
211
- "web-vitals": "4.2.3"
210
+ "rrweb": "^2.0.0-alpha.18",
211
+ "web-vitals": "4.2.4"
212
212
  },
213
213
  "devDependencies": {
214
214
  "@babel/cli": "^7.23.4",
@@ -5,7 +5,7 @@
5
5
  export const VITAL_NAMES = {
6
6
  FIRST_PAINT: 'fp',
7
7
  FIRST_CONTENTFUL_PAINT: 'fcp',
8
- FIRST_INPUT_DELAY: 'fi',
8
+ FIRST_INTERACTION: 'fi',
9
9
  LARGEST_CONTENTFUL_PAINT: 'lcp',
10
10
  CUMULATIVE_LAYOUT_SHIFT: 'cls',
11
11
  INTERACTION_TO_NEXT_PAINT: 'inp',
@@ -5,12 +5,26 @@
5
5
  import { onINP } from 'web-vitals/attribution'
6
6
  import { VitalMetric } from './vital-metric'
7
7
  import { VITAL_NAMES } from './constants'
8
- import { isBrowserScope } from '../constants/runtime'
8
+ import { initiallyHidden, isBrowserScope } from '../constants/runtime'
9
9
 
10
10
  export const interactionToNextPaint = new VitalMetric(VITAL_NAMES.INTERACTION_TO_NEXT_PAINT)
11
+ // Note: First Interaction is a legacy NR timing event, not an actual CWV metric
12
+ // ('fi' used to be detected via FID. It is now represented by the first INP)
13
+ export const firstInteraction = new VitalMetric(VITAL_NAMES.FIRST_INTERACTION)
11
14
 
12
15
  if (isBrowserScope) {
13
- /* Interaction-to-Next-Paint */
16
+ const recordFirstInteraction = (attribution) => {
17
+ firstInteraction.update({
18
+ value: attribution.interactionTime,
19
+ attrs: {
20
+ type: attribution.interactionType,
21
+ eventTarget: attribution.interactionTarget,
22
+ loadState: attribution.loadState
23
+ }
24
+ })
25
+ }
26
+
27
+ /* Interaction-to-Next-Paint */
14
28
  onINP(({ value, attribution, id }) => {
15
29
  const attrs = {
16
30
  metricId: id,
@@ -26,5 +40,10 @@ if (isBrowserScope) {
26
40
  loadState: attribution.loadState
27
41
  }
28
42
  interactionToNextPaint.update({ value, attrs })
43
+
44
+ // preserve the original behavior where FID is not reported if the page is hidden before the first interaction
45
+ if (!firstInteraction.isValid && !initiallyHidden) {
46
+ recordFirstInteraction(attribution)
47
+ }
29
48
  })
30
49
  }
@@ -35,7 +35,7 @@ export function castError (error) {
35
35
  * @returns {Error} An Error object with the message as the casted reason
36
36
  */
37
37
  export function castPromiseRejectionEvent (promiseRejectionEvent) {
38
- const prefix = 'Unhandled Promise Rejection'
38
+ const prefix = 'Unhandled Promise Rejection: '
39
39
 
40
40
  /**
41
41
  * If the casted return value is falsy like this, it will get dropped and not produce an error event for harvest.
@@ -46,15 +46,15 @@ export function castPromiseRejectionEvent (promiseRejectionEvent) {
46
46
 
47
47
  if (canTrustError(promiseRejectionEvent.reason)) {
48
48
  try {
49
- promiseRejectionEvent.reason.message = prefix + ': ' + promiseRejectionEvent.reason.message
50
- return castError(promiseRejectionEvent.reason)
49
+ if (!promiseRejectionEvent.reason.message.startsWith(prefix)) promiseRejectionEvent.reason.message = prefix + promiseRejectionEvent.reason.message
51
50
  } catch (e) {
52
- return castError(promiseRejectionEvent.reason)
51
+ // failed to modify the message, do nothing else
53
52
  }
53
+ return castError(promiseRejectionEvent.reason)
54
54
  }
55
55
 
56
56
  const error = castError(promiseRejectionEvent.reason)
57
- error.message = prefix + ': ' + error?.message
57
+ if (!((error.message || '').startsWith(prefix))) error.message = prefix + error.message
58
58
  return error
59
59
  }
60
60
 
@@ -116,6 +116,9 @@ export class Aggregate extends AggregateBase {
116
116
  mo.observe(window.document.body, { childList: true, subtree: true })
117
117
  }
118
118
 
119
+ // webdriver detection
120
+ if (navigator.webdriver) this.storeSupportabilityMetrics('Generic/WebDriver/Detected')
121
+
119
122
  // WATCHABLE_WEB_SOCKET_EVENTS.forEach(tag => {
120
123
  // registerHandler('buffered-' + WEBSOCKET_TAG + tag, (...args) => {
121
124
  // handleWebsocketEvents(this.storeSupportabilityMetrics.bind(this), tag, ...args)
@@ -3,10 +3,16 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
+ import { isBrowserScope } from '../../../common/constants/runtime'
7
+ import { handle } from '../../../common/event-emitter/handle'
8
+ import { InstrumentBase } from '../../utils/instrument-base'
9
+ import {
10
+ FEATURE_NAME,
11
+ // WATCHABLE_WEB_SOCKET_EVENTS,
12
+ SUPPORTABILITY_METRIC_CHANNEL
13
+ } from '../constants'
6
14
  // import { handle } from '../../../common/event-emitter/handle'
7
15
  // import { WEBSOCKET_TAG, wrapWebSocket } from '../../../common/wrap/wrap-websocket'
8
- import { InstrumentBase } from '../../utils/instrument-base'
9
- import { FEATURE_NAME } from '../constants'
10
16
 
11
17
  export class Instrument extends InstrumentBase {
12
18
  static featureName = FEATURE_NAME
@@ -20,6 +26,12 @@ export class Instrument extends InstrumentBase {
20
26
  // })
21
27
  // })
22
28
 
29
+ if (isBrowserScope) {
30
+ document.addEventListener('securitypolicyviolation', (e) => {
31
+ handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/CSPViolation/Detected'], undefined, this.featureName, this.ee)
32
+ })
33
+ }
34
+
23
35
  this.importAggregator(agentRef)
24
36
  }
25
37
  }
@@ -11,9 +11,8 @@ import { FEATURE_NAMES } from '../../../loaders/features/features'
11
11
  import { AggregateBase } from '../../utils/aggregate-base'
12
12
  import { cumulativeLayoutShift } from '../../../common/vitals/cumulative-layout-shift'
13
13
  import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint'
14
- import { firstInputDelay } from '../../../common/vitals/first-input-delay'
15
14
  import { firstPaint } from '../../../common/vitals/first-paint'
16
- import { interactionToNextPaint } from '../../../common/vitals/interaction-to-next-paint'
15
+ import { firstInteraction, interactionToNextPaint } from '../../../common/vitals/interaction-to-next-paint'
17
16
  import { largestContentfulPaint } from '../../../common/vitals/largest-contentful-paint'
18
17
  import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte'
19
18
  import { subscribeToVisibilityChange } from '../../../common/window/page-visibility'
@@ -37,8 +36,8 @@ export class Aggregate extends AggregateBase {
37
36
  this.waitForFlags(([])).then(() => {
38
37
  firstPaint.subscribe(this.#handleVitalMetric)
39
38
  firstContentfulPaint.subscribe(this.#handleVitalMetric)
40
- firstInputDelay.subscribe(this.#handleVitalMetric)
41
39
  largestContentfulPaint.subscribe(this.#handleVitalMetric)
40
+ firstInteraction.subscribe(this.#handleVitalMetric)
42
41
  interactionToNextPaint.subscribe(this.#handleVitalMetric)
43
42
  timeToFirstByte.subscribe(({ attrs }) => {
44
43
  this.addTiming('load', Math.round(attrs.navigationEntry.loadEventEnd))
@@ -24,7 +24,7 @@ class StylesheetEvaluator {
24
24
  this.#brokenSheets = []
25
25
  if (isBrowserScope) {
26
26
  for (let i = 0; i < Object.keys(document.styleSheets).length; i++) {
27
- if (!this.#evaluated.has(document.styleSheets[i])) {
27
+ if (!this.#evaluated.has(document.styleSheets[i]) && document.styleSheets[i] instanceof CSSStyleSheet) {
28
28
  this.#evaluated.add(document.styleSheets[i])
29
29
  try {
30
30
  // eslint-disable-next-line
@@ -136,11 +136,6 @@ export class TraceStorage {
136
136
 
137
137
  processPVT (name, value, attrs) {
138
138
  this.storeTiming({ [name]: value })
139
- if (hasFID(name, attrs)) this.storeEvent({ type: 'fid', target: 'document' }, 'document', value, value + attrs.fid)
140
-
141
- function hasFID (name, attrs) {
142
- return name === 'fi' && !!attrs && typeof attrs.fid === 'number'
143
- }
144
139
  }
145
140
 
146
141
  storeTiming (timingEntry, isAbsoluteTimestamp = false) {
@@ -1,36 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.firstInputDelay = void 0;
7
- var _attribution = require("web-vitals/attribution");
8
- var _vitalMetric = require("./vital-metric");
9
- var _constants = require("./constants");
10
- var _runtime = require("../constants/runtime");
11
- /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
- * SPDX-License-Identifier: Apache-2.0
14
- */
15
-
16
- const firstInputDelay = exports.firstInputDelay = new _vitalMetric.VitalMetric(_constants.VITAL_NAMES.FIRST_INPUT_DELAY);
17
- if (_runtime.isBrowserScope) {
18
- (0, _attribution.onFID)(({
19
- value,
20
- attribution
21
- }) => {
22
- if (_runtime.initiallyHidden || firstInputDelay.isValid) return;
23
- const attrs = {
24
- type: attribution.eventType,
25
- fid: Math.round(value),
26
- eventTarget: attribution.eventTarget,
27
- loadState: attribution.loadState
28
- };
29
-
30
- // CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
31
- firstInputDelay.update({
32
- value: attribution.eventTime,
33
- attrs
34
- });
35
- });
36
- }
@@ -1,29 +0,0 @@
1
- /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import { onFID } from 'web-vitals/attribution';
6
- import { VitalMetric } from './vital-metric';
7
- import { VITAL_NAMES } from './constants';
8
- import { initiallyHidden, isBrowserScope } from '../constants/runtime';
9
- export const firstInputDelay = new VitalMetric(VITAL_NAMES.FIRST_INPUT_DELAY);
10
- if (isBrowserScope) {
11
- onFID(({
12
- value,
13
- attribution
14
- }) => {
15
- if (initiallyHidden || firstInputDelay.isValid) return;
16
- const attrs = {
17
- type: attribution.eventType,
18
- fid: Math.round(value),
19
- eventTarget: attribution.eventTarget,
20
- loadState: attribution.loadState
21
- };
22
-
23
- // CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
24
- firstInputDelay.update({
25
- value: attribution.eventTime,
26
- attrs
27
- });
28
- });
29
- }
@@ -1,3 +0,0 @@
1
- export const firstInputDelay: VitalMetric;
2
- import { VitalMetric } from './vital-metric';
3
- //# sourceMappingURL=first-input-delay.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"first-input-delay.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/first-input-delay.js"],"names":[],"mappings":"AASA,0CAA6E;4BAJjD,gBAAgB"}
@@ -1,28 +0,0 @@
1
- /**
2
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
- * SPDX-License-Identifier: Apache-2.0
4
- */
5
- import { onFID } from 'web-vitals/attribution'
6
- import { VitalMetric } from './vital-metric'
7
- import { VITAL_NAMES } from './constants'
8
- import { initiallyHidden, isBrowserScope } from '../constants/runtime'
9
-
10
- export const firstInputDelay = new VitalMetric(VITAL_NAMES.FIRST_INPUT_DELAY)
11
-
12
- if (isBrowserScope) {
13
- onFID(({ value, attribution }) => {
14
- if (initiallyHidden || firstInputDelay.isValid) return
15
- const attrs = {
16
- type: attribution.eventType,
17
- fid: Math.round(value),
18
- eventTarget: attribution.eventTarget,
19
- loadState: attribution.loadState
20
- }
21
-
22
- // CWV will only report one (THE) first-input entry to us; fid isn't reported if there are no user interactions occurs before the *first* page hiding.
23
- firstInputDelay.update({
24
- value: attribution.eventTime,
25
- attrs
26
- })
27
- })
28
- }