@newrelic/browser-agent 1.320.1 → 1.321.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 (169) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/common/config/runtime.js +3 -0
  3. package/dist/cjs/common/constants/env.cdn.js +1 -1
  4. package/dist/cjs/common/constants/env.npm.js +1 -1
  5. package/dist/cjs/common/dom/selector-path.js +1 -1
  6. package/dist/cjs/common/event-listener/event-listener-opts.js +1 -1
  7. package/dist/cjs/common/harvest/connector.js +202 -0
  8. package/dist/cjs/common/harvest/harvester.js +5 -8
  9. package/dist/cjs/common/harvest/send.js +8 -7
  10. package/dist/cjs/common/session/session-entity.js +1 -1
  11. package/dist/cjs/common/timer/interaction-timer.js +2 -2
  12. package/dist/cjs/common/unload/eol.js +3 -3
  13. package/dist/cjs/common/util/console.js +2 -1
  14. package/dist/cjs/common/util/feature-flags.js +2 -1
  15. package/dist/cjs/common/v2/constants.js +21 -0
  16. package/dist/cjs/common/v2/utils.js +25 -23
  17. package/dist/cjs/common/window/page-visibility.js +1 -1
  18. package/dist/cjs/common/wrap/wrap-events.js +4 -3
  19. package/dist/cjs/common/wrap/wrap-fetch.js +2 -2
  20. package/dist/cjs/common/wrap/wrap-function.js +1 -1
  21. package/dist/cjs/common/wrap/wrap-history.js +3 -2
  22. package/dist/cjs/common/wrap/wrap-xhr.js +2 -2
  23. package/dist/cjs/features/ajax/aggregate/index.js +1 -1
  24. package/dist/cjs/features/generic_events/aggregate/index.js +1 -1
  25. package/dist/cjs/features/generic_events/instrument/index.js +1 -1
  26. package/dist/cjs/features/jserrors/aggregate/index.js +5 -5
  27. package/dist/cjs/features/logging/instrument/index.js +1 -1
  28. package/dist/cjs/features/page_view_event/aggregate/index.js +2 -6
  29. package/dist/cjs/features/page_view_event/aggregate_v2/index.js +109 -0
  30. package/dist/cjs/features/page_view_event/instrument/index.js +6 -1
  31. package/dist/cjs/features/page_view_timing/aggregate/index.js +5 -1
  32. package/dist/cjs/features/session_replay/instrument/index.js +9 -0
  33. package/dist/cjs/features/session_replay/shared/recorder.js +2 -1
  34. package/dist/cjs/features/session_replay/shared/utils.js +3 -5
  35. package/dist/cjs/features/session_trace/instrument/index.js +3 -3
  36. package/dist/cjs/features/soft_navigations/instrument/index.js +2 -2
  37. package/dist/cjs/features/utils/aggregate-base.js +0 -12
  38. package/dist/cjs/features/utils/instrument-base.js +72 -29
  39. package/dist/cjs/loaders/agent.js +2 -1
  40. package/dist/cjs/loaders/api/consent.js +2 -2
  41. package/dist/cjs/loaders/api/register.js +26 -13
  42. package/dist/cjs/loaders/configure/configure.js +14 -0
  43. package/dist/cjs/loaders/features/features.js +2 -1
  44. package/dist/esm/common/config/runtime.js +3 -0
  45. package/dist/esm/common/constants/env.cdn.js +1 -1
  46. package/dist/esm/common/constants/env.npm.js +1 -1
  47. package/dist/esm/common/dom/selector-path.js +1 -1
  48. package/dist/esm/common/event-listener/event-listener-opts.js +1 -1
  49. package/dist/esm/common/harvest/connector.js +195 -0
  50. package/dist/esm/common/harvest/harvester.js +5 -8
  51. package/dist/esm/common/harvest/send.js +10 -9
  52. package/dist/esm/common/session/session-entity.js +1 -1
  53. package/dist/esm/common/timer/interaction-timer.js +2 -2
  54. package/dist/esm/common/unload/eol.js +3 -3
  55. package/dist/esm/common/util/console.js +2 -1
  56. package/dist/esm/common/util/feature-flags.js +2 -1
  57. package/dist/esm/common/v2/constants.js +15 -0
  58. package/dist/esm/common/v2/utils.js +24 -22
  59. package/dist/esm/common/window/page-visibility.js +1 -1
  60. package/dist/esm/common/wrap/wrap-events.js +4 -3
  61. package/dist/esm/common/wrap/wrap-fetch.js +2 -2
  62. package/dist/esm/common/wrap/wrap-function.js +1 -1
  63. package/dist/esm/common/wrap/wrap-history.js +3 -2
  64. package/dist/esm/common/wrap/wrap-xhr.js +2 -2
  65. package/dist/esm/features/ajax/aggregate/index.js +2 -2
  66. package/dist/esm/features/generic_events/aggregate/index.js +1 -1
  67. package/dist/esm/features/generic_events/instrument/index.js +1 -1
  68. package/dist/esm/features/jserrors/aggregate/index.js +6 -6
  69. package/dist/esm/features/logging/instrument/index.js +1 -1
  70. package/dist/esm/features/page_view_event/aggregate/index.js +2 -6
  71. package/dist/esm/features/page_view_event/aggregate_v2/index.js +100 -0
  72. package/dist/esm/features/page_view_event/instrument/index.js +6 -1
  73. package/dist/esm/features/page_view_timing/aggregate/index.js +5 -1
  74. package/dist/esm/features/session_replay/instrument/index.js +9 -0
  75. package/dist/esm/features/session_replay/shared/recorder.js +2 -1
  76. package/dist/esm/features/session_replay/shared/utils.js +3 -5
  77. package/dist/esm/features/session_trace/instrument/index.js +3 -3
  78. package/dist/esm/features/soft_navigations/instrument/index.js +2 -2
  79. package/dist/esm/features/utils/aggregate-base.js +0 -12
  80. package/dist/esm/features/utils/instrument-base.js +71 -28
  81. package/dist/esm/loaders/agent.js +2 -1
  82. package/dist/esm/loaders/api/consent.js +2 -2
  83. package/dist/esm/loaders/api/register.js +19 -6
  84. package/dist/esm/loaders/configure/configure.js +14 -0
  85. package/dist/esm/loaders/features/features.js +1 -0
  86. package/dist/tsconfig.tsbuildinfo +1 -1
  87. package/dist/types/common/config/runtime.d.ts.map +1 -1
  88. package/dist/types/common/event-listener/event-listener-opts.d.ts +1 -1
  89. package/dist/types/common/harvest/connector.d.ts +6 -0
  90. package/dist/types/common/harvest/connector.d.ts.map +1 -0
  91. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  92. package/dist/types/common/harvest/send.d.ts.map +1 -1
  93. package/dist/types/common/unload/eol.d.ts +1 -1
  94. package/dist/types/common/unload/eol.d.ts.map +1 -1
  95. package/dist/types/common/util/console.d.ts +2 -1
  96. package/dist/types/common/util/console.d.ts.map +1 -1
  97. package/dist/types/common/v2/constants.d.ts +9 -0
  98. package/dist/types/common/v2/constants.d.ts.map +1 -0
  99. package/dist/types/common/v2/utils.d.ts +36 -11
  100. package/dist/types/common/v2/utils.d.ts.map +1 -1
  101. package/dist/types/common/wrap/wrap-events.d.ts +2 -1
  102. package/dist/types/common/wrap/wrap-events.d.ts.map +1 -1
  103. package/dist/types/common/wrap/wrap-history.d.ts +2 -1
  104. package/dist/types/common/wrap/wrap-history.d.ts.map +1 -1
  105. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  106. package/dist/types/features/page_view_event/aggregate_v2/index.d.ts +20 -0
  107. package/dist/types/features/page_view_event/aggregate_v2/index.d.ts.map +1 -0
  108. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  109. package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
  110. package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
  111. package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
  112. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  113. package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
  114. package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
  115. package/dist/types/features/utils/aggregate-base.d.ts +0 -5
  116. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  117. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  118. package/dist/types/loaders/agent.d.ts.map +1 -1
  119. package/dist/types/loaders/api/register.d.ts.map +1 -1
  120. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  121. package/dist/types/loaders/features/features.d.ts +1 -0
  122. package/dist/types/loaders/features/features.d.ts.map +1 -1
  123. package/package.json +1 -1
  124. package/src/common/config/runtime.js +3 -0
  125. package/src/common/dom/selector-path.js +1 -1
  126. package/src/common/event-listener/event-listener-opts.js +1 -1
  127. package/src/common/harvest/connector.js +170 -0
  128. package/src/common/harvest/harvester.js +7 -9
  129. package/src/common/harvest/send.js +9 -7
  130. package/src/common/session/session-entity.js +1 -1
  131. package/src/common/timer/interaction-timer.js +2 -2
  132. package/src/common/unload/eol.js +3 -3
  133. package/src/common/util/console.js +2 -1
  134. package/src/common/util/feature-flags.js +1 -1
  135. package/src/common/v2/constants.js +15 -0
  136. package/src/common/v2/utils.js +23 -21
  137. package/src/common/window/page-visibility.js +1 -1
  138. package/src/common/wrap/wrap-events.js +4 -3
  139. package/src/common/wrap/wrap-fetch.js +2 -2
  140. package/src/common/wrap/wrap-function.js +1 -1
  141. package/src/common/wrap/wrap-history.js +3 -2
  142. package/src/common/wrap/wrap-xhr.js +2 -2
  143. package/src/features/ajax/aggregate/index.js +2 -2
  144. package/src/features/generic_events/aggregate/index.js +1 -1
  145. package/src/features/generic_events/instrument/index.js +1 -1
  146. package/src/features/jserrors/aggregate/index.js +6 -6
  147. package/src/features/logging/instrument/index.js +1 -1
  148. package/src/features/page_view_event/aggregate/index.js +2 -6
  149. package/src/features/page_view_event/aggregate_v2/index.js +97 -0
  150. package/src/features/page_view_event/instrument/index.js +6 -1
  151. package/src/features/page_view_timing/aggregate/index.js +5 -1
  152. package/src/features/session_replay/instrument/index.js +9 -0
  153. package/src/features/session_replay/shared/recorder.js +2 -1
  154. package/src/features/session_replay/shared/utils.js +3 -5
  155. package/src/features/session_trace/instrument/index.js +3 -3
  156. package/src/features/soft_navigations/instrument/index.js +2 -2
  157. package/src/features/utils/aggregate-base.js +0 -12
  158. package/src/features/utils/instrument-base.js +71 -25
  159. package/src/loaders/agent.js +2 -1
  160. package/src/loaders/api/consent.js +2 -2
  161. package/src/loaders/api/register.js +16 -6
  162. package/src/loaders/configure/configure.js +12 -0
  163. package/src/loaders/features/features.js +1 -0
  164. package/dist/cjs/common/url/location.js +0 -18
  165. package/dist/esm/common/url/location.js +0 -12
  166. package/dist/types/common/url/location.d.ts +0 -10
  167. package/dist/types/common/url/location.d.ts.map +0 -1
  168. package/src/common/url/__mocks__/location.js +0 -5
  169. package/src/common/url/location.js +0 -12
@@ -42,8 +42,8 @@ function wrapXhr(sharedEE, agentRef) {
42
42
  if (wrapped[ee.debugId]++) return ee;
43
43
  wrapped[ee.debugId] = 1; // otherwise, first feature to wrap XHR
44
44
 
45
- (0, _wrapEvents.wrapEvents)(baseEE); // wrap-events patches XMLHttpRequest.prototype.addEventListener for us
46
- var wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee);
45
+ (0, _wrapEvents.wrapEvents)(baseEE, agentRef); // wrap-events patches XMLHttpRequest.prototype.addEventListener for us
46
+ var wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee, undefined, agentRef);
47
47
  var OrigXHR = _runtime.globalScope.XMLHttpRequest;
48
48
  var MutationObserver = _runtime.globalScope.MutationObserver;
49
49
  var Promise = _runtime.globalScope.Promise;
@@ -126,7 +126,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
126
126
  if (event.gql) this.reportSupportabilityMetric('Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length);
127
127
 
128
128
  /** make a copy of the event for the MFE target if it exists */
129
- if (target) {
129
+ if ((0, _utils.isMfeTarget)(target)) {
130
130
  this.events.add({
131
131
  ...event,
132
132
  targetAttributes: (0, _utils.getVersion2Attributes)(target, this)
@@ -262,7 +262,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
262
262
  this.addEvent(event, target);
263
263
  }, this.featureName, this.ee);
264
264
  if (agentRef.init.feature_flags.includes('websockets') || agentRef.init.web_sockets?.enabled) {
265
- (0, _registerHandler.registerHandler)('ws-complete', (nrData, targets = [undefined]) => {
265
+ (0, _registerHandler.registerHandler)('ws-complete', (nrData, targets = [agentRef.runtime.v2Target]) => {
266
266
  targets.forEach(target => {
267
267
  const event = {
268
268
  ...nrData,
@@ -63,7 +63,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
63
63
  if (_runtime.isBrowserScope) {
64
64
  (0, _wrapFetch.wrapFetch)(this.ee, agentRef);
65
65
  (0, _wrapXhr.wrapXhr)(this.ee, agentRef);
66
- historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
66
+ historyEE = (0, _wrapHistory.wrapHistory)(this.ee, agentRef);
67
67
  if (agentRef.init.user_actions.enabled) {
68
68
  _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
69
69
  _constants.OBSERVED_WINDOW_EVENTS.forEach(eventType => {
@@ -210,7 +210,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
210
210
  if (targets.length) break;
211
211
  }
212
212
  }
213
- if (!targets.length) targets.push(undefined);
213
+ if (!targets.length) targets.push(this.agentRef.runtime.v2Target);
214
214
  }
215
215
  return targets;
216
216
  }
@@ -254,7 +254,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
254
254
  if (filterOutput?.group) params.errorGroup = filterOutput.group;
255
255
 
256
256
  // Should only decorate "hasReplay" for the container agent, so check if the target matches the config
257
- if (hasReplay && !target) params.hasReplay = hasReplay;
257
+ if (hasReplay && !(0, _utils.isMfeTarget)(target)) params.hasReplay = hasReplay;
258
258
  /**
259
259
  * The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
260
260
  * stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
@@ -289,13 +289,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
289
289
 
290
290
  // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
291
291
  const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
292
- if (!target) (0, _handle.handle)('trace-jserror', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
292
+ if (!(0, _utils.isMfeTarget)(target)) (0, _handle.handle)('trace-jserror', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
293
293
  // still send EE events for other features such as above, but stop this one from aggregating internal data
294
294
  if (this.blocked) return;
295
295
  if (err.__newrelic?.socketId) {
296
296
  customAttributes.socketId = err.__newrelic.socketId;
297
297
  }
298
- if (!target) {
298
+ if (!(0, _utils.isMfeTarget)(target)) {
299
299
  const softNavInUse = Boolean(this.agentRef.features?.[_features.FEATURE_NAMES.softNav]);
300
300
  if (softNavInUse) {
301
301
  // pass the error to soft nav for evaluation - it will return it via 'returnJserror' when interaction is resolved
@@ -306,7 +306,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
306
306
  }
307
307
 
308
308
  // always add directly if scoped to a sub-entity, the other pathways above will be deterministic if the main agent should procede
309
- if (target) this.#storeJserrorForHarvest(jsErrorEvent, {}, target);
309
+ if ((0, _utils.isMfeTarget)(target)) this.#storeJserrorForHarvest(jsErrorEvent, {}, target);
310
310
  }
311
311
 
312
312
  /**
@@ -36,7 +36,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
36
36
  });
37
37
 
38
38
  /** emitted by wrap-logger function */
39
- this.ee.on('wrap-logger-end', function handleLog([message], _, __, targets = []) {
39
+ this.ee.on('wrap-logger-end', function handleLog([message], _, __, targets = [agentRef.runtime.v2Target]) {
40
40
  const {
41
41
  level,
42
42
  customAttributes,
@@ -140,9 +140,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
140
140
  return queryParameters;
141
141
  };
142
142
  this.events.add(body);
143
- if (this.agentRef.runtime.harvester.triggerHarvestFor(this, {
144
- sendEmptyBody: true
145
- }).ranSend) this.sentRum = true;
143
+ if (this.agentRef.runtime.harvester.triggerHarvestFor(this).ranSend) this.sentRum = true;
146
144
  }
147
145
  serializer(eventBuffer) {
148
146
  // this is necessary because PVE sends a single item rather than an array; in the case of undefined, this prevents sending [null] as body
@@ -183,9 +181,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
183
181
 
184
182
  if (this.isRetrying && this.retries++ < 1) {
185
183
  // Only retry once
186
- setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this, {
187
- sendEmptyBody: true
188
- }), 5000); // Retry sending the RUM event after 5 seconds
184
+ setTimeout(() => this.agentRef.runtime.harvester.triggerHarvestFor(this), 5000); // Retry sending the RUM event after 5 seconds
189
185
  return;
190
186
  }
191
187
  if (status >= 400 || status === 0) {
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Aggregate = void 0;
7
+ var _runtime = require("../../../common/constants/runtime");
8
+ var _navTiming = require("../../../common/timing/nav-timing");
9
+ var _stringify = require("../../../common/util/stringify");
10
+ var CONSTANTS = _interopRequireWildcard(require("../constants"));
11
+ var _initializedFeatures = require("../aggregate/initialized-features");
12
+ var _aggregateBase = require("../../utils/aggregate-base");
13
+ var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
14
+ var _firstPaint = require("../../../common/vitals/first-paint");
15
+ var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
16
+ var _obfuscate = require("../../../common/util/obfuscate");
17
+ var _webdriverDetection = require("../../../common/util/webdriver-detection");
18
+ var _events = require("../../../common/constants/events");
19
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
20
+ /**
21
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
22
+ * SPDX-License-Identifier: Apache-2.0
23
+ */
24
+
25
+ class Aggregate extends _aggregateBase.AggregateBase {
26
+ static featureName = CONSTANTS.FEATURE_NAME;
27
+ #timeToFirstByte = 0;
28
+ #firstByteToWindowLoad = 0; // our "frontend" duration
29
+ #firstByteToDomContent = 0; // our "dom processing" duration
30
+ #obfuscator;
31
+ constructor(agentRef) {
32
+ super(agentRef, CONSTANTS.FEATURE_NAME);
33
+ this.#obfuscator = new _obfuscate.Obfuscator(agentRef, _events.EVENT_TYPES.PVE);
34
+ if (_runtime.isBrowserScope) {
35
+ _timeToFirstByte.timeToFirstByte.subscribe(({
36
+ value,
37
+ attrs
38
+ }) => {
39
+ const navEntry = attrs.navigationEntry;
40
+ this.#timeToFirstByte = Math.max(value, this.#timeToFirstByte);
41
+ this.#firstByteToWindowLoad = Math.max(Math.round(navEntry.loadEventEnd - this.#timeToFirstByte), this.#firstByteToWindowLoad); // our "frontend" duration
42
+ this.#firstByteToDomContent = Math.max(Math.round(navEntry.domContentLoadedEventEnd - this.#timeToFirstByte), this.#firstByteToDomContent); // our "dom processing" duration
43
+ });
44
+ }
45
+ this.waitForFlags([]).then(() => {
46
+ const body = this.#obfuscator.traverseAndObfuscateEvents({
47
+ ja: {
48
+ ...agentRef.info.jsAttributes,
49
+ webdriverDetected: _webdriverDetection.webdriverDetected
50
+ }
51
+ });
52
+ this.events.add(body);
53
+ this.drain();
54
+ });
55
+ }
56
+ queryStringsBuilder() {
57
+ const info = this.agentRef.info;
58
+ const measures = {};
59
+ if (info.queueTime) measures.qt = info.queueTime;
60
+ if (info.applicationTime) measures.ap = info.applicationTime;
61
+
62
+ // These 3 values should've been recorded after load and before this func runs. They are part of the minimum required for PageView events to be created.
63
+ // Following PR #428, which demands that the BA must drive entity indexing, these need to be sent even outside of the main window context where PerformanceTiming
64
+ // or PerformanceNavigationTiming do not exists. Hence, they'll be filled in by 0s instead in, for example, worker threads that still init the PVE module.
65
+ measures.be = this.#timeToFirstByte;
66
+ measures.fe = this.#firstByteToWindowLoad;
67
+ measures.dc = this.#firstByteToDomContent;
68
+ const queryParameters = {
69
+ at: info.atts,
70
+ af: (0, _initializedFeatures.getActivatedFeaturesFlags)(this.agentRef).join(','),
71
+ igp: this.agentRef.runtime.appMetadata.igp,
72
+ ...measures,
73
+ fp: _firstPaint.firstPaint.current.value,
74
+ fcp: _firstContentfulPaint.firstContentfulPaint.current.value
75
+ };
76
+ if (_runtime.globalScope.performance) {
77
+ const navTimingEntry = (0, _runtime.getNavigationEntry)();
78
+ if (navTimingEntry) {
79
+ // Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
80
+ const perf = {
81
+ timing: (0, _navTiming.addPT)(_runtime.originTime, navTimingEntry, {}),
82
+ navigation: (0, _navTiming.addPN)(navTimingEntry, {})
83
+ };
84
+ queryParameters.perf = (0, _stringify.stringify)(perf);
85
+ } else if (typeof PerformanceTiming !== 'undefined') {
86
+ // Modern Safari iFrames and Safari pre-15 do not support level 2 timing.
87
+ const perf = {
88
+ timing: (0, _navTiming.addPT)(_runtime.originTime, _runtime.globalScope.performance.timing, {}, true),
89
+ navigation: (0, _navTiming.addPN)(_runtime.globalScope.performance.navigation, {})
90
+ };
91
+ queryParameters.perf = (0, _stringify.stringify)(perf);
92
+ }
93
+ }
94
+ const {
95
+ correctedOriginTime
96
+ } = this.agentRef.runtime.timeKeeper;
97
+ if (correctedOriginTime) queryParameters.timestamp = correctedOriginTime; // a "PageView" is set to represent the page's (server adjusted) origin time rather than when event is recorded/harvested
98
+
99
+ return queryParameters;
100
+ }
101
+ get harvestEndpointVersion() {
102
+ return 2;
103
+ }
104
+ serializer(eventBuffer) {
105
+ // this is necessary because PVE sends a single item rather than an array; in the case of undefined, this prevents sending [null] as body
106
+ return eventBuffer[0];
107
+ }
108
+ }
109
+ exports.Aggregate = Aggregate;
@@ -24,7 +24,12 @@ class Instrument extends _instrumentBase.InstrumentBase {
24
24
 
25
25
  /** feature specific APIs */
26
26
  (0, _setPageViewName.setupSetPageViewNameAPI)(agentRef);
27
- this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "page_view_event-aggregate" */'../aggregate'))));
27
+ this.importAggregator(agentRef, () => {
28
+ if (agentRef.init.feature_flags.includes('rum_v2')) {
29
+ return Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "page_view_event-aggregate" */'../aggregate_v2')));
30
+ }
31
+ return Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "page_view_event-aggregate" */'../aggregate')));
32
+ });
28
33
  }
29
34
  setupInspectionEvents() {
30
35
  const dispatch = (evt, name) => {
@@ -72,7 +72,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
72
72
  } = _cumulativeLayoutShift.cumulativeLayoutShift.current;
73
73
  if (value === undefined) return;
74
74
  this.addTiming(name, value * 1000, attrs);
75
- }, true, true); // CLS node should only reports on vis change rather than on every change
75
+ }, true, true); // CLS node should only report on vis change rather than on every change.
76
+ /* NOTE: the capture=true is required -- empirically verified against a real browser (Chrome) for CLS
77
+ timing node to reliably be in the same final harvest as the rest, alongside Harvester#startTimer's
78
+ queueMicrotask deferral of the EOL harvest itself. Dropping this flag caused CLS to be split into its own,
79
+ separate, later harvest instead of going out together with pageHide/INP. */
76
80
 
77
81
  this.drain();
78
82
  });
@@ -44,6 +44,10 @@ class Instrument extends _instrumentBase.InstrumentBase {
44
44
  recorder.startRecording(_constants2.TRIGGERS.PRELOAD, session?.sessionReplayMode);
45
45
  }); // could handle specific fail-state behaviors with a .catch block here
46
46
  }
47
+
48
+ // If the aggregate never successfully loads (missing session prerequisite, or the aggregate module itself
49
+ // fails to import/construct), any preload recording that already started needs to be stopped.
50
+ this.abortHandler = this.#abort;
47
51
  this.importAggregator(this.agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "session_replay-aggregate" */'../aggregate'))), this);
48
52
 
49
53
  /** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
@@ -108,6 +112,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
108
112
  }); // could handle specific fail-state behaviors with a .catch block here
109
113
  }
110
114
  }
115
+
116
+ /** Stops any preload recording that already started if the aggregate never ends up successfully loading. */
117
+ #abort() {
118
+ this.recorder?.stopRecording();
119
+ }
111
120
  }
112
121
  exports.Instrument = Instrument;
113
122
  const SessionReplay = exports.SessionReplay = Instrument;
@@ -216,7 +216,8 @@ class Recorder {
216
216
 
217
217
  // We are making an effort to try to keep payloads manageable for unloading. If they reach the unload limit before their interval,
218
218
  // it will send immediately. This often happens on the first snapshot, which can be significantly larger than the other payloads.
219
- if ((this.events.hasSnapshot && this.events.hasMeta || payloadSize > _agentConstants.IDEAL_PAYLOAD_SIZE) && !this.isErrorMode) {
219
+ // (the aggregate may not exist yet/at all if its own bootstrap hasn't finished or failed -- nothing to harvest for in that case.)
220
+ if ((this.events.hasSnapshot && this.events.hasMeta || payloadSize > _agentConstants.IDEAL_PAYLOAD_SIZE) && !this.isErrorMode && this.srInstrument.featAggregate) {
220
221
  // if we've made it to the ideal size of ~16kb before the interval timer, we should send early.
221
222
  this.agentRef.runtime.harvester.triggerHarvestFor(this.srInstrument.featAggregate);
222
223
  }
@@ -9,7 +9,7 @@ exports.isPreloadAllowed = isPreloadAllowed;
9
9
  var _nreum = require("../../../common/window/nreum");
10
10
  var _featureGates = require("../../utils/feature-gates");
11
11
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
12
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
13
  * SPDX-License-Identifier: Apache-2.0
14
14
  */
15
15
 
@@ -25,10 +25,8 @@ function isPreloadAllowed(agentInit) {
25
25
  }
26
26
  function customMasker(text, element) {
27
27
  try {
28
- if (typeof element?.type === 'string') {
29
- if (element.type.toLowerCase() === 'password') return '*'.repeat(text?.length || 0);
30
- if (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask')) return text;
31
- }
28
+ if (typeof element?.type === 'string' && element.type.toLowerCase() === 'password') return '*'.repeat(text?.length || 0);
29
+ if (element?.dataset?.nrUnmask !== undefined || element?.classList?.contains('nr-unmask')) return text;
32
30
  } catch (err) {
33
31
  // likely an element was passed to this handler that was invalid and was missing attributes or methods
34
32
  }
@@ -15,7 +15,7 @@ var _now = require("../../../common/timing/now");
15
15
  var _addToTrace = require("../../../loaders/api/addToTrace");
16
16
  var _finished = require("../../../loaders/api/finished");
17
17
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
18
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
18
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
19
19
  * SPDX-License-Identifier: Apache-2.0
20
20
  */
21
21
  const {
@@ -42,8 +42,8 @@ class Instrument extends _instrumentBase.InstrumentBase {
42
42
  return;
43
43
  }
44
44
  const thisInstrumentEE = this.ee;
45
- (0, _wrapHistory.wrapHistory)(thisInstrumentEE);
46
- this.eventsEE = (0, _wrapEvents.wrapEvents)(thisInstrumentEE);
45
+ (0, _wrapHistory.wrapHistory)(thisInstrumentEE, agentRef);
46
+ this.eventsEE = (0, _wrapEvents.wrapEvents)(thisInstrumentEE, agentRef);
47
47
  this.eventsEE.on(FN_START, function (args, target) {
48
48
  this.bstStart = (0, _now.now)();
49
49
  });
@@ -15,7 +15,7 @@ var _constants = require("../constants");
15
15
  var _now = require("../../../common/timing/now");
16
16
  var _interaction = require("../../../loaders/api/interaction");
17
17
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
18
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
18
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
19
19
  * SPDX-License-Identifier: Apache-2.0
20
20
  */
21
21
  /**
@@ -34,7 +34,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
34
34
  (0, _interaction.setupInteractionAPI)(agentRef);
35
35
  if (!_runtime.isBrowserScope || !(0, _nreum.gosNREUMOriginals)().o.MO) return; // soft navigations is not supported outside web env or browsers without the mutation observer API
36
36
 
37
- const historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
37
+ const historyEE = (0, _wrapHistory.wrapHistory)(this.ee, agentRef);
38
38
  try {
39
39
  this.removeOnAbort = new AbortController();
40
40
  } catch (e) {}
@@ -7,7 +7,6 @@ exports.AggregateBase = void 0;
7
7
  var _featureBase = require("./feature-base");
8
8
  var _drain = require("../../common/drain/drain");
9
9
  var _features = require("../../loaders/features/features");
10
- var _harvester = require("../../common/harvest/harvester");
11
10
  var _eventBuffer = require("./event-buffer");
12
11
  var _handle = require("../../common/event-emitter/handle");
13
12
  var _constants = require("../metrics/constants");
@@ -27,7 +26,6 @@ class AggregateBase extends _featureBase.FeatureBase {
27
26
  */
28
27
  constructor(agentRef, featureName) {
29
28
  super(agentRef, featureName);
30
- this.doOnceForAllAggregate(agentRef);
31
29
 
32
30
  /** @type {Boolean} indicates if custom attributes are combined in each event payload for size estimation purposes. this is set to true in derived classes that need to evaluate custom attributes separately from the event payload */
33
31
  this.customAttributesAreSeparate = false;
@@ -179,16 +177,6 @@ class AggregateBase extends _featureBase.FeatureBase {
179
177
  this.events.clearSave(this.harvestOpts);
180
178
  }
181
179
 
182
- /**
183
- * These are actions related to shared resources that should be initialized once by whichever feature Aggregate subclass loads first.
184
- * This method should run after checkConfiguration, which may reset the agent's info/runtime object that is used here.
185
- */
186
- doOnceForAllAggregate(agentRef) {
187
- // Note: obfuscator is now created per-feature for their specific event types
188
-
189
- if (!agentRef.runtime.harvester) agentRef.runtime.harvester = new _harvester.Harvester(agentRef);
190
- }
191
-
192
180
  /**
193
181
  * Report a supportability metric
194
182
  * @param {*} metricName The tag of the name matching the Angler aggregation tag
@@ -27,6 +27,7 @@ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r
27
27
  * Inherits `blocked` behavior from [FeatureBase]{@link ./feature-base}.
28
28
  */
29
29
  const checkedAgents = new WeakSet();
30
+ const runtimeBootstrapPromises = new WeakMap();
30
31
 
31
32
  /**
32
33
  * Base class for instrumenting a feature.
@@ -102,32 +103,14 @@ class InstrumentBase extends _featureBase.FeatureBase {
102
103
  this.loadedSuccessfully(false);
103
104
  return;
104
105
  }
105
- let session;
106
106
  try {
107
- if ((0, _featureGates.canEnableSessionTracking)(agentRef.init)) {
108
- // would require some setup before certain features start
109
- const {
110
- setupAgentSession
111
- } = await Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "session-manager" */'./agent-session')));
112
- session = setupAgentSession(agentRef);
113
- }
107
+ // in the interest of keeping loader file size small, some modules are lazy-loaded as part of the larger async chunk
108
+ await ensureRuntimeBootstrap(agentRef, this.ee, this.featureName);
114
109
  } catch (e) {
115
- (0, _console.warn)(20, e);
116
- this.ee.emit('internal-error', [e]);
117
- (0, _handle.handle)(_agentConstants.SESSION_ERROR, [e], undefined, this.featureName, this.ee);
118
- }
119
- if (agentRef.init.api.register.allow_iframe_bridge) {
120
- try {
121
- // This chunk doesn't exist in the lite build (see webpack IgnorePlugin config) since none
122
- // of lite's features wire up agent.register -- guard against that rather than letting an
123
- // unhandled rejection surface if this flag is ever set on a lite page.
124
- const {
125
- setupIframeMFEMessageListener
126
- } = await Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "iframe-message-handler" */'../../loaders/configure/iframe-message-handler')));
127
- setupIframeMFEMessageListener(agentRef);
128
- } catch (e) {
129
- (0, _console.warn)(23, e);
130
- }
110
+ (0, _console.warn)(79, e);
111
+ this.ee.abort(); // failed Connector or Harvester will cause entire agent to shutdown
112
+ this.loadedSuccessfully(false);
113
+ return;
131
114
  }
132
115
 
133
116
  /**
@@ -135,7 +118,8 @@ class InstrumentBase extends _featureBase.FeatureBase {
135
118
  * it's only responsible for aborting its one specific feature, rather than all.
136
119
  */
137
120
  try {
138
- if (!this.#shouldImportAgg(this.featureName, session, agentRef.init)) {
121
+ if (!this.#shouldImportAgg(this.featureName, agentRef.runtime.session, agentRef.init)) {
122
+ this.abortHandler?.();
139
123
  (0, _drain.drain)(this.agentRef, this.featureName);
140
124
  this.loadedSuccessfully(false); // aggregate module isn't loaded at all
141
125
  return;
@@ -144,15 +128,14 @@ class InstrumentBase extends _featureBase.FeatureBase {
144
128
  Aggregate
145
129
  } = await fetchAggregator();
146
130
  this.featAggregate = new Aggregate(agentRef, argsObjFromInstrument);
147
- agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate); // "subscribe" the feature to future harvest intervals (PVE will start the timer)
131
+ agentRef.runtime.harvester.initializedAggregates.push(this.featAggregate); // "subscribe" the feature to future harvest intervals
148
132
  this.loadedSuccessfully(true);
149
133
  } catch (e) {
150
134
  (0, _console.warn)(34, e);
151
- this.abortHandler?.(); // undo any important alterations made to the page
135
+ this.abortHandler?.();
152
136
  // not supported yet but nice to do: "abort" this agent's EE for this feature specifically
153
137
  (0, _drain.drain)(this.agentRef, this.featureName, true);
154
138
  this.loadedSuccessfully(false);
155
- if (this.ee) this.ee.abort();
156
139
  }
157
140
  };
158
141
 
@@ -212,4 +195,64 @@ class InstrumentBase extends _featureBase.FeatureBase {
212
195
  }
213
196
  }
214
197
  }
215
- exports.InstrumentBase = InstrumentBase;
198
+
199
+ /**
200
+ * Lazily initializes the shared runtime bootstrap for a given agent exactly once. This loads session setup, Connector, and
201
+ * Harvester through shared async chunks and returns the same in-flight Promise to any concurrent callers.
202
+ *
203
+ * Session setup failures are reported but do not stop bootstrap. Connector or Harvester failures are allowed to bubble so the caller can abort the agent.
204
+ *
205
+ * @param {Object} agentRef - The agent reference object.
206
+ * @param {Object} ee - The feature event emitter used for error reporting.
207
+ * @param {string} featureName - The feature name used when reporting session setup errors.
208
+ * @returns {Promise<void>} Resolves when the shared bootstrap completes.
209
+ */
210
+ exports.InstrumentBase = InstrumentBase;
211
+ async function ensureRuntimeBootstrap(agentRef, ee, featureName) {
212
+ if (runtimeBootstrapPromises.has(agentRef)) return runtimeBootstrapPromises.get(agentRef);
213
+ const bootstrapPromise = (async () => {
214
+ if ((0, _featureGates.canEnableSessionTracking)(agentRef.init)) {
215
+ try {
216
+ const {
217
+ setupAgentSession
218
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "session-manager" */'./agent-session')));
219
+ setupAgentSession(agentRef); // sets agentRef.runtime.session, if successful
220
+ } catch (e) {
221
+ (0, _console.warn)(20, e);
222
+ ee.emit('internal-error', [e]);
223
+ (0, _handle.handle)(_agentConstants.SESSION_ERROR, [e], undefined, featureName, ee);
224
+ }
225
+ }
226
+ if (agentRef.init.api.register.allow_iframe_bridge) {
227
+ try {
228
+ // This chunk doesn't exist in the lite build (see webpack IgnorePlugin config) since none
229
+ // of lite's features wire up agent.register -- guard against that rather than letting an
230
+ // unhandled rejection surface if this flag is ever set on a lite page.
231
+ const {
232
+ setupIframeMFEMessageListener
233
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "iframe-message-handler" */'../../loaders/configure/iframe-message-handler')));
234
+ setupIframeMFEMessageListener(agentRef);
235
+ } catch (e) {
236
+ (0, _console.warn)(23, e);
237
+ }
238
+ }
239
+ const [{
240
+ Connector
241
+ }, {
242
+ Harvester
243
+ }] = await Promise.all([Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "connector" */'../../common/harvest/connector'))), Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "harvester" */'../../common/harvest/harvester')))]);
244
+ agentRef.runtime.connector = new Connector(agentRef);
245
+ agentRef.runtime.harvester = new Harvester(agentRef);
246
+
247
+ /* Wait for auto-started features' aggregates to load before starting the harvest timer, so Harvester's EOL
248
+ listener registers after aggregate-level listeners like web-vitals' CWV APIs. Not awaited here to avoid
249
+ deadlock, since every feature awaits this same bootstrap promise before its own aggregate load.
250
+ Only auto-started features are waited on: an autoStart:false feature only registers in drainRegistry once
251
+ `start` is called, so filtering on that registry excludes any feature that's never started instead of
252
+ blocking startTimer() forever. */
253
+ const autoStartedFeatures = Object.values(agentRef.features).filter(feature => agentRef.runtime.drainRegistry.has(feature.featureName));
254
+ Promise.all(autoStartedFeatures.map(feature => feature.onAggregateImported)).then(() => agentRef.runtime.harvester.startTimer());
255
+ })();
256
+ runtimeBootstrapPromises.set(agentRef, bootstrapPromise);
257
+ await bootstrapPromise;
258
+ }
@@ -96,9 +96,10 @@ class Agent extends _agentBase.AgentBase {
96
96
  try {
97
97
  const enabledFeatures = (0, _enabledFeatures.getEnabledFeatures)(this.init);
98
98
  const featuresToStart = [...this.desiredFeatures];
99
+ const isRumV2Enabled = this.init.feature_flags.includes('rum_v2');
99
100
  featuresToStart.sort((a, b) => _features.featurePriority[a.featureName] - _features.featurePriority[b.featureName]);
100
101
  featuresToStart.forEach(InstrumentCtor => {
101
- if (!enabledFeatures[InstrumentCtor.featureName] && InstrumentCtor.featureName !== _features.FEATURE_NAMES.pageViewEvent) return; // PVE is required to run even if it's marked disabled
102
+ if (!enabledFeatures[InstrumentCtor.featureName] && (isRumV2Enabled || InstrumentCtor.featureName !== _features.FEATURE_NAMES.pageViewEvent)) return; // PVE is required to run even if it's marked disabled unless rum_v2 is enabled
102
103
 
103
104
  const dependencies = (0, _featureDependencies.getFeatureDependencyNames)(InstrumentCtor.featureName);
104
105
  const missingDependencies = dependencies.filter(featName => !(featName in this.features)); // any other feature(s) this depends on should've been initialized on prior iterations by priority order
@@ -9,7 +9,7 @@ var _sharedHandlers = require("./sharedHandlers");
9
9
  var _handle = require("../../common/event-emitter/handle");
10
10
  var _console = require("../../common/util/console");
11
11
  /**
12
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
12
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
13
13
  * SPDX-License-Identifier: Apache-2.0
14
14
  */
15
15
 
@@ -29,7 +29,7 @@ function setupConsentAPI(agent) {
29
29
  pveInst.onAggregateImported.then(loaded => {
30
30
  const pveAgg = pveInst.featAggregate;
31
31
  if (loaded && !pveAgg.sentRum) {
32
- pveAgg.sendRum();
32
+ pveAgg.sendRum?.(); // only the old v1 aggregate has a sendRum method that acts as a blocker for the rest of the agent; v2 aggregate will auto report when harvester becomes unblocked
33
33
  }
34
34
  });
35
35
  }