@newrelic/browser-agent 1.283.2 → 1.284.1

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 (51) hide show
  1. package/CHANGELOG.md +23 -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/util/event-origin.js +36 -0
  6. package/dist/cjs/common/vitals/constants.js +1 -1
  7. package/dist/cjs/features/ajax/instrument/index.js +3 -2
  8. package/dist/cjs/features/jserrors/shared/cast-error.js +5 -5
  9. package/dist/cjs/features/metrics/aggregate/index.js +3 -0
  10. package/dist/cjs/features/metrics/instrument/index.js +7 -0
  11. package/dist/cjs/features/page_view_timing/aggregate/index.js +31 -4
  12. package/dist/cjs/features/session_replay/shared/stylesheet-evaluator.js +1 -1
  13. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +4 -28
  14. package/dist/esm/common/constants/env.cdn.js +2 -2
  15. package/dist/esm/common/constants/env.npm.js +2 -2
  16. package/dist/esm/common/util/event-origin.js +30 -0
  17. package/dist/esm/common/vitals/constants.js +1 -1
  18. package/dist/esm/features/ajax/instrument/index.js +3 -2
  19. package/dist/esm/features/jserrors/shared/cast-error.js +5 -5
  20. package/dist/esm/features/metrics/aggregate/index.js +3 -0
  21. package/dist/esm/features/metrics/instrument/index.js +12 -2
  22. package/dist/esm/features/page_view_timing/aggregate/index.js +31 -4
  23. package/dist/esm/features/session_replay/shared/stylesheet-evaluator.js +1 -1
  24. package/dist/esm/features/session_trace/aggregate/trace/storage.js +4 -28
  25. package/dist/tsconfig.tsbuildinfo +1 -1
  26. package/dist/types/common/util/event-origin.d.ts +13 -0
  27. package/dist/types/common/util/event-origin.d.ts.map +1 -0
  28. package/dist/types/common/vitals/constants.d.ts +1 -1
  29. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  30. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  31. package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
  32. package/dist/types/features/page_view_timing/aggregate/index.d.ts +12 -1
  33. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  34. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts +0 -1
  35. package/dist/types/features/session_trace/aggregate/trace/storage.d.ts.map +1 -1
  36. package/package.json +3 -3
  37. package/src/common/util/event-origin.js +36 -0
  38. package/src/common/vitals/constants.js +1 -1
  39. package/src/common/vitals/interaction-to-next-paint.js +1 -1
  40. package/src/features/ajax/instrument/index.js +3 -2
  41. package/src/features/jserrors/shared/cast-error.js +5 -5
  42. package/src/features/metrics/aggregate/index.js +3 -0
  43. package/src/features/metrics/instrument/index.js +14 -2
  44. package/src/features/page_view_timing/aggregate/index.js +33 -4
  45. package/src/features/session_replay/shared/stylesheet-evaluator.js +1 -1
  46. package/src/features/session_trace/aggregate/trace/storage.js +4 -33
  47. package/dist/cjs/common/vitals/first-input-delay.js +0 -36
  48. package/dist/esm/common/vitals/first-input-delay.js +0 -29
  49. package/dist/types/common/vitals/first-input-delay.d.ts +0 -3
  50. package/dist/types/common/vitals/first-input-delay.d.ts.map +0 -1
  51. package/src/common/vitals/first-input-delay.js +0 -28
package/CHANGELOG.md CHANGED
@@ -3,6 +3,29 @@
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.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.284.0...v1.284.1) (2025-03-11)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Console error on some cross-origin requests without NR CAT header ([#1407](https://github.com/newrelic/newrelic-browser-agent/issues/1407)) ([6660c44](https://github.com/newrelic/newrelic-browser-agent/commit/6660c4455f73bdd90da9946f48ca6c6b377a866f))
12
+ * Obtain FirstInteraction directly from performance API ([#1410](https://github.com/newrelic/newrelic-browser-agent/issues/1410)) ([22ef4ff](https://github.com/newrelic/newrelic-browser-agent/commit/22ef4ffaef72729f99ee615d4851870f51a129f7))
13
+
14
+ ## [1.284.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.283.2...v1.284.0) (2025-03-04)
15
+
16
+
17
+ ### Features
18
+
19
+ * 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))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * 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))
25
+ * 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))
26
+ * 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))
27
+ * Upgrade rrweb to 18 ([#1383](https://github.com/newrelic/newrelic-browser-agent/issues/1383)) ([46b691b](https://github.com/newrelic/newrelic-browser-agent/commit/46b691b671d2090f47707eea6dfa5ae8f31d0c7b))
28
+
6
29
  ## [1.283.2](https://github.com/newrelic/newrelic-browser-agent/compare/v1.283.1...v1.283.2) (2025-02-21)
7
30
 
8
31
 
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.1";
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.1";
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";
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.eventOrigin = eventOrigin;
7
+ /**
8
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
+ * SPDX-License-Identifier: Apache-2.0
10
+ */
11
+
12
+ /**
13
+ * Returns a string representing the origin of an event target. Used by SessionTrace and PageViewTiming features to assign a "better" target to events
14
+ * @param {*} t The target to derive the origin from.
15
+ * @param {*} [target] A known target to compare to. If supplied, and a derived origin could not be reached, this will be referenced.
16
+ * @param {*} [ee] An event emitter instance to use for context retrieval, which only applies to XMLHttpRequests.
17
+ * @returns {string} The derived origin of the event target.
18
+ */
19
+ function eventOrigin(t, target, ee) {
20
+ let origin = 'unknown';
21
+ if (t && t instanceof XMLHttpRequest) {
22
+ const params = ee.context(t).params;
23
+ if (!params || !params.status || !params.method || !params.host || !params.pathname) return 'xhrOriginMissing';
24
+ origin = params.status + ' ' + params.method + ': ' + params.host + params.pathname;
25
+ } else if (t && typeof t.tagName === 'string') {
26
+ origin = t.tagName.toLowerCase();
27
+ if (t.id) origin += '#' + t.id;
28
+ if (t.className) {
29
+ for (let i = 0; i < t.classList.length; i++) origin += '.' + t.classList[i];
30
+ }
31
+ }
32
+ if (origin === 'unknown') {
33
+ if (typeof target === 'string') origin = target;else if (target === document) origin = 'document';else if (target === window) origin = 'window';else if (target instanceof FileReader) origin = 'FileReader';
34
+ }
35
+ return origin;
36
+ }
@@ -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',
@@ -30,6 +30,7 @@ var handlers = ['load', 'error', 'abort', 'timeout'];
30
30
  var handlersLen = handlers.length;
31
31
  var origRequest = (0, _nreum.gosNREUMOriginals)().o.REQ;
32
32
  var origXHR = (0, _nreum.gosNREUMOriginals)().o.XHR;
33
+ const NR_CAT_HEADER = 'X-NewRelic-App-Data';
33
34
  class Instrument extends _instrumentBase.InstrumentBase {
34
35
  static featureName = _constants.FEATURE_NAME;
35
36
  constructor(agentRef, auto = true) {
@@ -360,8 +361,8 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
360
361
  ctx.params.status = xhr.status;
361
362
  var size = (0, _responseSize.responseSizeFromXhr)(xhr, ctx.lastSize);
362
363
  if (size) ctx.metrics.rxSize = size;
363
- if (ctx.sameOrigin) {
364
- var header = xhr.getResponseHeader('X-NewRelic-App-Data');
364
+ if (ctx.sameOrigin && xhr.getAllResponseHeaders().indexOf(NR_CAT_HEADER) >= 0) {
365
+ var header = xhr.getResponseHeader(NR_CAT_HEADER);
365
366
  if (header) {
366
367
  (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC, ['Ajax/CrossApplicationTracing/Header/Seen'], undefined, _features.FEATURE_NAMES.metrics, ee);
367
368
  ctx.params.cat = header.split(', ').pop();
@@ -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,13 +12,14 @@ 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");
19
18
  var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
20
19
  var _pageVisibility = require("../../../common/window/page-visibility");
21
20
  var _constants2 = require("../../../common/vitals/constants");
21
+ var _runtime = require("../../../common/constants/runtime");
22
+ var _eventOrigin = require("../../../common/util/event-origin");
22
23
  /**
23
24
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
24
25
  * SPDX-License-Identifier: Apache-2.0
@@ -36,13 +37,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
36
37
  constructor(agentRef) {
37
38
  super(agentRef, _constants.FEATURE_NAME);
38
39
  this.curSessEndRecorded = false;
40
+ this.firstIxnRecorded = false;
39
41
  (0, _registerHandler.registerHandler)('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
40
42
  // Add the time of _window pagehide event_ firing to the next PVT harvest == NRDB windowUnload attr:
41
43
  (0, _registerHandler.registerHandler)('winPagehide', msTimestamp => this.addTiming('unload', msTimestamp, null), this.featureName, this.ee);
42
44
  this.waitForFlags([]).then(() => {
43
45
  _firstPaint.firstPaint.subscribe(this.#handleVitalMetric);
44
46
  _firstContentfulPaint.firstContentfulPaint.subscribe(this.#handleVitalMetric);
45
- _firstInputDelay.firstInputDelay.subscribe(this.#handleVitalMetric);
46
47
  _largestContentfulPaint.largestContentfulPaint.subscribe(this.#handleVitalMetric);
47
48
  _interactionToNextPaint.interactionToNextPaint.subscribe(this.#handleVitalMetric);
48
49
  _timeToFirstByte.timeToFirstByte.subscribe(({
@@ -93,12 +94,34 @@ class Aggregate extends _aggregateBase.AggregateBase {
93
94
  if (name !== _constants2.VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT && _cumulativeLayoutShift.cumulativeLayoutShift.current.value >= 0) {
94
95
  attrs.cls = _cumulativeLayoutShift.cumulativeLayoutShift.current.value;
95
96
  }
96
- this.events.add({
97
+ const timing = {
97
98
  name,
98
99
  value,
99
100
  attrs
100
- });
101
+ };
102
+ this.events.add(timing);
101
103
  (0, _handle.handle)('pvtAdded', [name, value, attrs], undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
104
+ this.checkForFirstInteraction();
105
+
106
+ // makes testing easier
107
+ return timing;
108
+ }
109
+
110
+ /**
111
+ * Checks the performance API to see if the agent can set a first interaction event value
112
+ * @returns {void}
113
+ */
114
+ checkForFirstInteraction() {
115
+ // preserve the original behavior where FID is not reported if the page is hidden before the first interaction
116
+ if (this.firstIxnRecorded || _runtime.initiallyHidden || !performance) return;
117
+ const firstInput = performance.getEntriesByType('first-input')[0];
118
+ if (!firstInput) return;
119
+ this.firstIxnRecorded = true;
120
+ this.addTiming('fi', firstInput.startTime, {
121
+ type: firstInput.name,
122
+ eventTarget: (0, _eventOrigin.eventOrigin)(firstInput.target),
123
+ loadState: document.readyState
124
+ });
102
125
  }
103
126
  appendGlobalCustomAttributes(timing) {
104
127
  var timingAttributes = timing.attrs || {};
@@ -109,6 +132,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
109
132
  }
110
133
  });
111
134
  }
135
+ preHarvestChecks() {
136
+ this.checkForFirstInteraction();
137
+ return super.preHarvestChecks();
138
+ }
112
139
 
113
140
  // serialize array of timing data
114
141
  serializer(eventBuffer) {
@@ -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
@@ -8,6 +8,7 @@ var _runtime = require("../../../../common/constants/runtime");
8
8
  var _constants = require("../../../../common/session/constants");
9
9
  var _now = require("../../../../common/timing/now");
10
10
  var _parseUrl = require("../../../../common/url/parse-url");
11
+ var _eventOrigin = require("../../../../common/util/event-origin");
11
12
  var _constants2 = require("../../constants");
12
13
  var _node = require("./node");
13
14
  /**
@@ -144,13 +145,6 @@ class TraceStorage {
144
145
  this.storeTiming({
145
146
  [name]: value
146
147
  });
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
148
  }
155
149
  storeTiming(timingEntry, isAbsoluteTimestamp = false) {
156
150
  if (!timingEntry) return;
@@ -183,14 +177,14 @@ class TraceStorage {
183
177
  try {
184
178
  // webcomponents-lite.js can trigger an exception on currentEvent.target getter because
185
179
  // it does not check currentEvent.currentTarget before calling getRootNode() on it
186
- evt.o = this.evtOrigin(currentEvent.target, target);
180
+ evt.o = (0, _eventOrigin.eventOrigin)(currentEvent.target, target, this.parent.ee);
187
181
  } catch (e) {
188
- evt.o = this.evtOrigin(null, target);
182
+ evt.o = (0, _eventOrigin.eventOrigin)(null, target, this.parent.ee);
189
183
  }
190
184
  this.storeSTN(evt);
191
185
  }
192
186
  shouldIgnoreEvent(event, target) {
193
- const origin = this.evtOrigin(event.target, target);
187
+ const origin = (0, _eventOrigin.eventOrigin)(event.target, target, this.parent.ee);
194
188
  if (event.type in ignoredEvents.global) return true;
195
189
  if (!!ignoredEvents[origin] && ignoredEvents[origin].ignoreAll) return true;
196
190
  return !!(!!ignoredEvents[origin] && event.type in ignoredEvents[origin]);
@@ -220,24 +214,6 @@ class TraceStorage {
220
214
  return type;
221
215
  }
222
216
  }
223
- evtOrigin(t, target) {
224
- let origin = 'unknown';
225
- if (t && t instanceof XMLHttpRequest) {
226
- const params = this.parent.ee.context(t).params;
227
- if (!params || !params.status || !params.method || !params.host || !params.pathname) return 'xhrOriginMissing';
228
- origin = params.status + ' ' + params.method + ': ' + params.host + params.pathname;
229
- } else if (t && typeof t.tagName === 'string') {
230
- origin = t.tagName.toLowerCase();
231
- if (t.id) origin += '#' + t.id;
232
- if (t.className) {
233
- for (let i = 0; i < t.classList.length; i++) origin += '.' + t.classList[i];
234
- }
235
- }
236
- if (origin === 'unknown') {
237
- if (typeof target === 'string') origin = target;else if (target === document) origin = 'document';else if (target === window) origin = 'window';else if (target instanceof FileReader) origin = 'FileReader';
238
- }
239
- return origin;
240
- }
241
217
 
242
218
  // Tracks when the window history API specified by wrap-history is used.
243
219
  storeHist(path, old, time) {
@@ -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.1";
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.1";
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";
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * Returns a string representing the origin of an event target. Used by SessionTrace and PageViewTiming features to assign a "better" target to events
8
+ * @param {*} t The target to derive the origin from.
9
+ * @param {*} [target] A known target to compare to. If supplied, and a derived origin could not be reached, this will be referenced.
10
+ * @param {*} [ee] An event emitter instance to use for context retrieval, which only applies to XMLHttpRequests.
11
+ * @returns {string} The derived origin of the event target.
12
+ */
13
+ export function eventOrigin(t, target, ee) {
14
+ let origin = 'unknown';
15
+ if (t && t instanceof XMLHttpRequest) {
16
+ const params = ee.context(t).params;
17
+ if (!params || !params.status || !params.method || !params.host || !params.pathname) return 'xhrOriginMissing';
18
+ origin = params.status + ' ' + params.method + ': ' + params.host + params.pathname;
19
+ } else if (t && typeof t.tagName === 'string') {
20
+ origin = t.tagName.toLowerCase();
21
+ if (t.id) origin += '#' + t.id;
22
+ if (t.className) {
23
+ for (let i = 0; i < t.classList.length; i++) origin += '.' + t.classList[i];
24
+ }
25
+ }
26
+ if (origin === 'unknown') {
27
+ if (typeof target === 'string') origin = target;else if (target === document) origin = 'document';else if (target === window) origin = 'window';else if (target instanceof FileReader) origin = 'FileReader';
28
+ }
29
+ return origin;
30
+ }
@@ -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',
@@ -23,6 +23,7 @@ var handlers = ['load', 'error', 'abort', 'timeout'];
23
23
  var handlersLen = handlers.length;
24
24
  var origRequest = gosNREUMOriginals().o.REQ;
25
25
  var origXHR = gosNREUMOriginals().o.XHR;
26
+ const NR_CAT_HEADER = 'X-NewRelic-App-Data';
26
27
  export class Instrument extends InstrumentBase {
27
28
  static featureName = FEATURE_NAME;
28
29
  constructor(agentRef, auto = true) {
@@ -352,8 +353,8 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
352
353
  ctx.params.status = xhr.status;
353
354
  var size = responseSizeFromXhr(xhr, ctx.lastSize);
354
355
  if (size) ctx.metrics.rxSize = size;
355
- if (ctx.sameOrigin) {
356
- var header = xhr.getResponseHeader('X-NewRelic-App-Data');
356
+ if (ctx.sameOrigin && xhr.getAllResponseHeaders().indexOf(NR_CAT_HEADER) >= 0) {
357
+ var header = xhr.getResponseHeader(NR_CAT_HEADER);
357
358
  if (header) {
358
359
  handle(SUPPORTABILITY_METRIC, ['Ajax/CrossApplicationTracing/Header/Seen'], undefined, FEATURE_NAMES.metrics, ee);
359
360
  ctx.params.cat = header.split(', ').pop();
@@ -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,13 +11,14 @@ 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
15
  import { 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';
20
19
  import { VITAL_NAMES } from '../../../common/vitals/constants';
20
+ import { initiallyHidden } from '../../../common/constants/runtime';
21
+ import { eventOrigin } from '../../../common/util/event-origin';
21
22
  export class Aggregate extends AggregateBase {
22
23
  static featureName = FEATURE_NAME;
23
24
  #handleVitalMetric = ({
@@ -30,13 +31,13 @@ export class Aggregate extends AggregateBase {
30
31
  constructor(agentRef) {
31
32
  super(agentRef, FEATURE_NAME);
32
33
  this.curSessEndRecorded = false;
34
+ this.firstIxnRecorded = false;
33
35
  registerHandler('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
34
36
  // Add the time of _window pagehide event_ firing to the next PVT harvest == NRDB windowUnload attr:
35
37
  registerHandler('winPagehide', msTimestamp => this.addTiming('unload', msTimestamp, null), this.featureName, this.ee);
36
38
  this.waitForFlags([]).then(() => {
37
39
  firstPaint.subscribe(this.#handleVitalMetric);
38
40
  firstContentfulPaint.subscribe(this.#handleVitalMetric);
39
- firstInputDelay.subscribe(this.#handleVitalMetric);
40
41
  largestContentfulPaint.subscribe(this.#handleVitalMetric);
41
42
  interactionToNextPaint.subscribe(this.#handleVitalMetric);
42
43
  timeToFirstByte.subscribe(({
@@ -87,12 +88,34 @@ export class Aggregate extends AggregateBase {
87
88
  if (name !== VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT && cumulativeLayoutShift.current.value >= 0) {
88
89
  attrs.cls = cumulativeLayoutShift.current.value;
89
90
  }
90
- this.events.add({
91
+ const timing = {
91
92
  name,
92
93
  value,
93
94
  attrs
94
- });
95
+ };
96
+ this.events.add(timing);
95
97
  handle('pvtAdded', [name, value, attrs], undefined, FEATURE_NAMES.sessionTrace, this.ee);
98
+ this.checkForFirstInteraction();
99
+
100
+ // makes testing easier
101
+ return timing;
102
+ }
103
+
104
+ /**
105
+ * Checks the performance API to see if the agent can set a first interaction event value
106
+ * @returns {void}
107
+ */
108
+ checkForFirstInteraction() {
109
+ // preserve the original behavior where FID is not reported if the page is hidden before the first interaction
110
+ if (this.firstIxnRecorded || initiallyHidden || !performance) return;
111
+ const firstInput = performance.getEntriesByType('first-input')[0];
112
+ if (!firstInput) return;
113
+ this.firstIxnRecorded = true;
114
+ this.addTiming('fi', firstInput.startTime, {
115
+ type: firstInput.name,
116
+ eventTarget: eventOrigin(firstInput.target),
117
+ loadState: document.readyState
118
+ });
96
119
  }
97
120
  appendGlobalCustomAttributes(timing) {
98
121
  var timingAttributes = timing.attrs || {};
@@ -103,6 +126,10 @@ export class Aggregate extends AggregateBase {
103
126
  }
104
127
  });
105
128
  }
129
+ preHarvestChecks() {
130
+ this.checkForFirstInteraction();
131
+ return super.preHarvestChecks();
132
+ }
106
133
 
107
134
  // serialize array of timing data
108
135
  serializer(eventBuffer) {
@@ -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