@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
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.321.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.320.1...v1.321.0) (2026-08-31)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add instrumentation to track races between tabs ([#1838](https://github.com/newrelic/newrelic-browser-agent/issues/1838)) ([f678c3a](https://github.com/newrelic/newrelic-browser-agent/commit/f678c3a60f53455eb5d43eab90ab28e081b18a83))
12
+ * improve v2 target standardization ([#1826](https://github.com/newrelic/newrelic-browser-agent/issues/1826)) ([1a18e10](https://github.com/newrelic/newrelic-browser-agent/commit/1a18e10f4754f6516fdac82020fe5cc5eed489a4))
13
+ * Introduce v2 PageView & decouple connect service call ([#1824](https://github.com/newrelic/newrelic-browser-agent/issues/1824)) ([a07609a](https://github.com/newrelic/newrelic-browser-agent/commit/a07609a36f9891d85001711ba2d723fef096421b))
14
+ * MicroFrontEndTiming attributes changes ([#1837](https://github.com/newrelic/newrelic-browser-agent/issues/1837)) ([a282ba0](https://github.com/newrelic/newrelic-browser-agent/commit/a282ba0afbc9291aaaef165050da813bfc79f8b6))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * Ensure Session Replay's custom masker applies to non-inputs ([#1840](https://github.com/newrelic/newrelic-browser-agent/issues/1840)) ([db9faa5](https://github.com/newrelic/newrelic-browser-agent/commit/db9faa52730585c8ecbd1100e00a3b6fe94c2ab9))
20
+
6
21
  ## [1.320.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.320.0...v1.320.1) (2026-08-12)
7
22
 
8
23
 
@@ -44,7 +44,10 @@ const RuntimeModel = {
44
44
  disabled: false,
45
45
  /** @type {Map<string, {staged: boolean, priority: number}>} */
46
46
  drainRegistry: new Map(),
47
+ connector: undefined,
47
48
  harvester: undefined,
49
+ /** eagerly-created in configure() -- the v2 target representing the container (main) agent, see common/v2/utils#V2Target */
50
+ v2Target: undefined,
48
51
  isolatedBacklog: false,
49
52
  isRecording: false,
50
53
  // true when actively recording, false when paused or stopped
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.320.1";
20
+ const VERSION = exports.VERSION = "1.321.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.RRWEB_PACKAGE_NAME = exports.D
17
17
  /**
18
18
  * Exposes the version of the agent
19
19
  */
20
- const VERSION = exports.VERSION = "1.320.1";
20
+ const VERSION = exports.VERSION = "1.321.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -27,7 +27,7 @@ const analyzeElemPath = (elem, targetFields = [], agentRef) => {
27
27
  path: undefined,
28
28
  nearestFields: {},
29
29
  get targets() {
30
- return targets.length ? targets : [undefined];
30
+ return targets.length ? targets : [agentRef.runtime.v2Target];
31
31
  },
32
32
  hasButton: false,
33
33
  hasLink: false
@@ -7,7 +7,7 @@ exports.documentAddEventListener = documentAddEventListener;
7
7
  exports.eventListenerOpts = eventListenerOpts;
8
8
  exports.windowAddEventListener = windowAddEventListener;
9
9
  /**
10
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
11
11
  * SPDX-License-Identifier: Apache-2.0
12
12
  */
13
13
  function eventListenerOpts(useCapture, abortSignal) {
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Connector = void 0;
7
+ var _runtime = require("../constants/runtime");
8
+ var _send = require("./send");
9
+ var _timeKeeper = require("../timing/time-keeper");
10
+ var _featureFlags = require("../util/feature-flags");
11
+ var _now = require("../timing/now");
12
+ var _console = require("../util/console");
13
+ var _env = require("../constants/env.npm");
14
+ var _features = require("../../loaders/features/features");
15
+ var _handle = require("../event-emitter/handle");
16
+ var _constants = require("../../features/metrics/constants");
17
+ /**
18
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
19
+ * SPDX-License-Identifier: Apache-2.0
20
+ */
21
+
22
+ const MAX_CONNECT_ATTEMPTS = 3;
23
+ const CONNECT_RETRY_BASE_MS = 3000; // affects each retry window proportionally (attempt 2: 0-3s, 3: 0-6s, 4: 0-12s, etc)
24
+
25
+ class Connector {
26
+ #agentRef;
27
+ #connectStartTime;
28
+ #numOfAttempts = 0;
29
+ constructor(agentRef) {
30
+ if (!agentRef.init.feature_flags.includes('rum_v2')) return; // the Connector does not run in v1; legacy PVE has its own BCS handling
31
+ this.#agentRef = agentRef;
32
+ agentRef.runtime.timeKeeper = new _timeKeeper.TimeKeeper(agentRef.runtime.session);
33
+ if (_runtime.isBrowserScope) {
34
+ const cached = agentRef.runtime.session && agentRef.runtime.session.state.cachedRumResponse;
35
+ if (cached) {
36
+ // `cachedRumResponse` is stored flat (`{ app, ...flags }`, matching the old v1 RUM response), so re-nest it before applying.
37
+ const {
38
+ app,
39
+ ...config
40
+ } = cached;
41
+ this.#applyConnectResponse({
42
+ app,
43
+ config
44
+ });
45
+ return;
46
+ }
47
+ }
48
+ this.makeConnectRequest();
49
+ }
50
+ makeConnectRequest(retryHeaders) {
51
+ this.#connectStartTime = (0, _now.now)();
52
+ return (0, _send.send)(this.#agentRef, {
53
+ endpoint: "browser/connect/2/".concat(this.#agentRef.info.licenseKey),
54
+ payload: {
55
+ body: {},
56
+ qs: {
57
+ a: this.#agentRef.info.applicationID,
58
+ v: _env.VERSION,
59
+ s: this.#agentRef.runtime.session?.state.value || '0'
60
+ }
61
+ },
62
+ localOpts: {
63
+ sendEmptyBody: true,
64
+ headers: retryHeaders
65
+ },
66
+ raw: true,
67
+ featureName: _features.CONNECT,
68
+ cbFinished: this.#processConnectResponse.bind(this)
69
+ });
70
+ }
71
+ #applyConnectResponse({
72
+ app,
73
+ config
74
+ }) {
75
+ if (!Object.keys(this.#agentRef.runtime.appMetadata).length) this.#agentRef.runtime.appMetadata = app;
76
+ (0, _featureFlags.activateFeatures)(config, this.#agentRef);
77
+ }
78
+ #processConnectResponse({
79
+ sent,
80
+ status,
81
+ retry,
82
+ xhr,
83
+ responseText
84
+ }) {
85
+ const connectEndTime = (0, _now.now)();
86
+ const session = this.#agentRef.runtime.session;
87
+ if (session) {
88
+ // Double check that there isn't a response saved (e.g. from another agent/browser tab) btwn the async time of the request and now in a race-condition.
89
+ const cachedResp = session.state.cachedRumResponse;
90
+ if (cachedResp) {
91
+ // Report that we've seen a race condition on updating session state
92
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Session/RaceCondition/Seen'], undefined, _features.FEATURE_NAMES.metrics, this.#agentRef.ee);
93
+ // `cachedRumResponse` is stored flat (`{ app, ...flags }`, matching the old v1 RUM response), so re-nest it before applying.
94
+ const {
95
+ app,
96
+ ...config
97
+ } = cachedResp;
98
+ this.#applyConnectResponse({
99
+ app,
100
+ config
101
+ });
102
+ return;
103
+ }
104
+ }
105
+ const shouldRetry = sent && retry; // important to note: no retry if HTTP status is 0
106
+ if (shouldRetry && ++this.#numOfAttempts < MAX_CONNECT_ATTEMPTS) {
107
+ // We use an exponential backoff calculation for the retry to avoid thundering herd, skipping any further processing.
108
+ const delay = Math.floor(Math.random() * CONNECT_RETRY_BASE_MS * 2 ** (this.#numOfAttempts - 1));
109
+ const retryHeaders = [{
110
+ key: 'X-Retry-Count',
111
+ value: this.#numOfAttempts
112
+ }, {
113
+ key: 'X-Previous-Status',
114
+ value: status
115
+ }];
116
+ setTimeout(() => this.makeConnectRequest(retryHeaders), delay);
117
+ return;
118
+ }
119
+ if (status >= 400 || status === 0) {
120
+ (0, _console.warn)(18, status);
121
+
122
+ // Get estimated payload size of our backlog.
123
+ const textEncoder = new TextEncoder();
124
+ const payloadSize = Object.values(newrelic.ee.backlog).reduce((acc, value) => {
125
+ if (!value) return acc;
126
+ const encoded = textEncoder.encode(value);
127
+ return acc + encoded.byteLength;
128
+ }, 0);
129
+
130
+ // Send SMs about the (last) failed connect request, then abort agent.
131
+ const BCSError = 'BCS/Error/';
132
+ const sm = [{
133
+ params: {
134
+ name: BCSError + status
135
+ },
136
+ stats: {
137
+ c: 1
138
+ }
139
+ }, {
140
+ params: {
141
+ name: BCSError + 'Duration/Ms'
142
+ },
143
+ stats: {
144
+ c: 1,
145
+ t: connectEndTime - this.#connectStartTime
146
+ }
147
+ }, {
148
+ params: {
149
+ name: BCSError + 'Dropped/Bytes'
150
+ },
151
+ stats: {
152
+ c: 1,
153
+ t: payloadSize
154
+ }
155
+ }];
156
+ (0, _send.send)(this.#agentRef, {
157
+ // note we have to send it this way because the Metrics feature will never be harvested under this branch of code (BCS failure)
158
+ endpoint: _features.FEATURE_TO_ENDPOINT[_features.FEATURE_NAMES.metrics],
159
+ payload: {
160
+ body: {
161
+ sm
162
+ }
163
+ },
164
+ featureName: _features.FEATURE_NAMES.metrics
165
+ });
166
+ this.#agentRef.ee.abort();
167
+ return;
168
+ }
169
+ let resp;
170
+ try {
171
+ resp = JSON.parse(responseText);
172
+ } catch (error) {
173
+ (0, _console.warn)(53, error);
174
+ this.#agentRef.ee.abort();
175
+ return;
176
+ }
177
+ // store the response for other pages later in same session or in parallel tabs; flatten `.config` into the old v1 RUM response shape
178
+ if (session) session.write({
179
+ cachedRumResponse: {
180
+ app: resp.app,
181
+ ...resp.config
182
+ }
183
+ });
184
+ try {
185
+ const wasReady = this.#agentRef.runtime.timeKeeper.ready;
186
+ this.#agentRef.runtime.timeKeeper.processRumRequest(xhr, this.#connectStartTime, connectEndTime, resp.app.nrServerTime);
187
+ if (!this.#agentRef.runtime.timeKeeper.ready) throw new Error('TimeKeeper not ready');
188
+
189
+ // If timeKeeper's origin time is ahead of nrServerTime, then the timestamp is invalid. Report a supportability metric.
190
+ const timeDiff = this.#agentRef.runtime.timeKeeper.correctedOriginTime - resp.app.nrServerTime;
191
+ if (wasReady && timeDiff > 0) {
192
+ (0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/TimeKeeper/InvalidTimestamp/Seen', timeDiff], undefined, _features.FEATURE_NAMES.metrics, this.#agentRef.ee);
193
+ }
194
+ } catch (error) {
195
+ (0, _console.warn)(17, error);
196
+ this.#agentRef.ee.abort();
197
+ return;
198
+ }
199
+ this.#applyConnectResponse(resp);
200
+ }
201
+ }
202
+ exports.Connector = Connector;
@@ -26,11 +26,13 @@ class Harvester {
26
26
  constructor(agentRef) {
27
27
  this.agentRef = agentRef;
28
28
 
29
- // Create obfuscator for harvest metadata (referrer URL, etc.)
30
29
  // No event type specified - applies to all harvests regardless of feature
31
30
  this.obfuscator = new _obfuscate.Obfuscator(agentRef);
31
+ }
32
+ startTimer(harvestInterval = this.agentRef.init.harvest.interval) {
33
+ if (this.#started) return;
34
+ this.#started = true;
32
35
  (0, _eol.subscribeToEOL)(() => {
33
- // do one last harvest round or check
34
36
  this.initializedAggregates.forEach(aggregateInst => {
35
37
  // let all features wrap up things needed to do before ANY harvest in case there's last minute cross-feature data dependencies
36
38
  if (typeof aggregateInst.harvestOpts.beforeUnload === 'function') aggregateInst.harvestOpts.beforeUnload();
@@ -38,12 +40,7 @@ class Harvester {
38
40
  this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst, {
39
41
  isFinalHarvest: true
40
42
  }));
41
- /* This callback should run in bubble phase, so that CWV api, like "onLCP", is called before the final harvest so that emitted timings are part of last outgoing. */
42
- }, false);
43
- }
44
- startTimer(harvestInterval = this.agentRef.init.harvest.interval) {
45
- if (this.#started) return;
46
- this.#started = true;
43
+ });
47
44
  const onHarvestInterval = () => {
48
45
  this.initializedAggregates.forEach(aggregateInst => this.triggerHarvestFor(aggregateInst));
49
46
  setTimeout(onHarvestInterval, harvestInterval * 1000); // repeat in X seconds
@@ -37,7 +37,7 @@ function send(agentRef, {
37
37
  endpoint,
38
38
  payload,
39
39
  localOpts = {},
40
- submitMethod,
40
+ submitMethod = (0, _submitData.getSubmitMethod)(),
41
41
  cbFinished,
42
42
  raw,
43
43
  featureName,
@@ -58,13 +58,15 @@ function send(agentRef, {
58
58
  }
59
59
  const protocol = agentRef.init.ssl === false ? 'http' : 'https';
60
60
  const perceivedBeacon = agentRef.init.proxy.beacon || agentRef.info.errorBeacon;
61
- const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(endpoint !== _features.RUM ? '/' + endpoint : '', "/").concat(endpointVersion, "/").concat(agentRef.info.licenseKey);
61
+ const includeEndpointName = endpoint !== _features.RUM || endpointVersion !== 1;
62
+ const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(includeEndpointName ? '/' + endpoint : '', "/").concat(endpointVersion, "/").concat(agentRef.info.licenseKey);
62
63
  const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint, harvesterObfuscator) : '';
63
64
  let payloadParams = (0, _encode.obj)(qs, agentRef.runtime.maxBytes);
64
65
  if (baseParams === '' && payloadParams.startsWith('&')) {
65
66
  payloadParams = payloadParams.substring(1);
66
67
  }
67
- const fullUrl = "".concat(url, "?").concat(baseParams).concat(payloadParams);
68
+ const queryString = "".concat(baseParams).concat(payloadParams);
69
+ const fullUrl = queryString ? "".concat(url, "?").concat(queryString) : url; // omit the '?' if there are no query params
68
70
  const gzip = !!qs?.attributes?.includes('gzip');
69
71
 
70
72
  // all gzipped data is already in the correct format and needs no transformation
@@ -79,7 +81,7 @@ function send(agentRef, {
79
81
  const headers = [{
80
82
  key: 'content-type',
81
83
  value: 'text/plain'
82
- }];
84
+ }, ...(localOpts.headers || [])];
83
85
 
84
86
  /* Since workers don't support sendBeacon right now, they can only use XHR method.
85
87
  Because they still do permit synch XHR, the idea is that at final harvest time (worker is closing),
@@ -128,6 +130,7 @@ function send(agentRef, {
128
130
  }
129
131
  function trackHarvestMetadata() {
130
132
  try {
133
+ if (featureName === _features.CONNECT) return;
131
134
  if (featureName === _features.FEATURE_NAMES.jserrors && !body?.err) return;
132
135
  const hasReplay = baseParams.includes('hr=1');
133
136
  const hasTrace = baseParams.includes('ht=1');
@@ -213,9 +216,7 @@ function baseQueryString(agentRef, qs, endpoint, harvesterObfuscator) {
213
216
  const session = agentRef.runtime.session;
214
217
  const hr = !!session?.state.sessionReplaySentFirstChunk && session?.state.sessionReplayMode === 1 && endpoint !== _features.JSERRORS;
215
218
  const ht = !!session?.state.traceHarvestStarted && session?.state.sessionTraceMode === 1 && ![_features.LOGS, _features.BLOBS].includes(endpoint);
216
- const qps = ['a=' + agentRef.info.applicationID, (0, _encode.param)('sa', agentRef.info.sa ? '' + agentRef.info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(), (0, _encode.param)('ct', agentRef.runtime.customTransaction), '&rst=' + (0, _now.now)(), '&ck=0',
217
- // ck param DEPRECATED - still expected by backend
218
- '&s=' + (session?.state.value || '0'),
219
+ const qps = ['a=' + agentRef.info.applicationID, (0, _encode.param)('sa', agentRef.info.sa ? '' + agentRef.info.sa : ''), (0, _encode.param)('v', _env.VERSION), transactionNameParam(), (0, _encode.param)('ct', agentRef.runtime.customTransaction), '&rst=' + (0, _now.now)(), '&s=' + (session?.state.value || '0'),
219
220
  // the 0 id encaps all untrackable and default traffic
220
221
  (0, _encode.param)('ref', ref), (0, _encode.param)('ptid', agentRef.runtime.ptid ? '' + agentRef.runtime.ptid : '')];
221
222
  if (hr) qps.push((0, _encode.param)('hr', '1', qs));
@@ -66,7 +66,7 @@ class SessionEntity {
66
66
  // key is intended to act as the k=v pair
67
67
  this.key = key;
68
68
  this.ee = agentRef.ee;
69
- (0, _wrapEvents.wrapEvents)(this.ee);
69
+ (0, _wrapEvents.wrapEvents)(this.ee, agentRef);
70
70
  this.setup(opts);
71
71
 
72
72
  /**
@@ -9,7 +9,7 @@ var _pageVisibility = require("../window/page-visibility");
9
9
  var _invoke = require("../util/invoke");
10
10
  var _runtime = require("../constants/runtime");
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
 
@@ -59,7 +59,7 @@ class InteractionTimer extends _timer.Timer {
59
59
  if (state === 'hidden') this.pause();
60
60
  // vis change --> visible is treated like a new interaction with the page
61
61
  else this.resume();
62
- }, false, false, this.abortController?.signal);
62
+ }, false, this.abortController?.signal);
63
63
  }
64
64
  }
65
65
  abort() {
@@ -7,7 +7,7 @@ exports.subscribeToEOL = subscribeToEOL;
7
7
  var _runtime = require("../constants/runtime");
8
8
  var _pageVisibility = require("../window/page-visibility");
9
9
  /**
10
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
11
11
  * SPDX-License-Identifier: Apache-2.0
12
12
  */
13
13
 
@@ -29,9 +29,9 @@ if (_runtime.isWorkerScope) {
29
29
  * This is used, for example, to submit a final harvest and send all remaining data on best-effort.
30
30
  * @param {function} cb - func to run before or during the last reliable event or time of an env's life span
31
31
  */
32
- function subscribeToEOL(cb, capturePhase) {
32
+ function subscribeToEOL(cb) {
33
33
  if (_runtime.isBrowserScope) {
34
- (0, _pageVisibility.subscribeToVisibilityChange)(cb, true, capturePhase); // when user switches tab or hides window, esp. mobile scenario
34
+ (0, _pageVisibility.subscribeToVisibilityChange)(cb, true); // when user switches tab or hides window, esp. mobile scenario
35
35
  } else if (_runtime.isWorkerScope) {
36
36
  _runtime.globalScope.cleanupTasks.push(cb); // close() should run these tasks before quitting thread
37
37
  }
@@ -73,7 +73,7 @@ var _globalEvent = require("../dispatch/global-event");
73
73
  * | 50 | Failed to connect. Cannot allow registered API. |
74
74
  * | 51 | Container agent is not available to register with. Can not connect. |
75
75
  * | 52 | Unexpected problem encountered. There should be at least one app for harvest! |
76
- * | 53 | Did not receive a valid entityGuid from connection response. |
76
+ * | 53 | Failed to parse connect response. |
77
77
  * | 54 | An experimental feature is being used. Support can not be offered for issues. |
78
78
  * | 55 | Register API has been disabled on the container agent. |
79
79
  * | 56 | Could not find a matching entity to store data. |
@@ -99,6 +99,7 @@ var _globalEvent = require("../dispatch/global-event");
99
99
  * | 76 | Agent rejected post message, could not match with existing entity. |
100
100
  * | 77 | Agent rejected post message, could not validate origin. |
101
101
  * | 78 | RegisteredIframeEntity could not determine parent origin and will not register, to avoid trusting messages from any origin. |
102
+ * | 79 | Unable to initialize Connector and/or Harvester. |
102
103
  *
103
104
  * @param {number} code The warning code to emit, which will be used to link to the warning code documentation
104
105
  * @param {*} [secondary] Secondary data to include, usually an extra message, error or object
@@ -25,7 +25,8 @@ function activateFeatures(flags, agentRef) {
25
25
  // let any window level subscribers know that the agent is running, per install docs
26
26
  (0, _globalEvent.dispatchGlobalEvent)({
27
27
  loaded: true,
28
- drained: true,
28
+ drained: !agentRef.init.feature_flags.includes('rum_v2'),
29
+ // v2 RUM always call activateFeatures before any group is drained (in waitForFlags)
29
30
  type: 'lifecycle',
30
31
  name: 'load',
31
32
  feature: undefined,
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.V2_TYPES = void 0;
7
+ /**
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
+ * SPDX-License-Identifier: Apache-2.0
10
+ */
11
+
12
+ /**
13
+ * @enum {string}
14
+ * @readonly
15
+ */
16
+ const V2_TYPES = exports.V2_TYPES = {
17
+ /** Micro Frontend */
18
+ MFE: 'MFE',
19
+ /** Browser Application */
20
+ BA: 'BA'
21
+ };
@@ -3,7 +3,6 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.V2_TYPES = void 0;
7
6
  exports.dedupeRegisteredEntitiesByAsset = dedupeRegisteredEntitiesByAsset;
8
7
  exports.dedupeTargetsByInstance = dedupeTargetsByInstance;
9
8
  exports.findTargetsFromStackTrace = findTargetsFromStackTrace;
@@ -12,23 +11,31 @@ exports.getRegisteredTargetsFromFilename = getRegisteredTargetsFromFilename;
12
11
  exports.getRegisteredTargetsFromId = getRegisteredTargetsFromId;
13
12
  exports.getVersion2Attributes = getVersion2Attributes;
14
13
  exports.getVersion2DuplicationAttributes = getVersion2DuplicationAttributes;
14
+ exports.isMfeTarget = isMfeTarget;
15
15
  exports.shouldDuplicate = shouldDuplicate;
16
16
  var _scriptTracker = require("./script-tracker");
17
+ var _constants = require("./constants");
17
18
  /**
18
19
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
19
20
  * SPDX-License-Identifier: Apache-2.0
20
21
  */
21
22
 
22
23
  /**
23
- * @enum {string}
24
- * @readonly
24
+ * @typedef {Object} V2Target
25
+ * @property {'BA'} type always V2_TYPES.BA -- identifies this as the container (main) agent, not a registered MFE
26
+ * @property {string} instance the container agent's agentIdentifier
27
+ * @property {string} [id] the container agent's entity guid, once known (getter, resolved asynchronously)
28
+ * @property {{[key:string]: any}} attributes the V2 payload attributes for the container agent (getter)
25
29
  */
26
- const V2_TYPES = exports.V2_TYPES = {
27
- /** Micro Frontend */
28
- MFE: 'MFE',
29
- /** Browser Application */
30
- BA: 'BA'
31
- };
30
+
31
+ /**
32
+ * Determines if a given target represents a registered MFE (as opposed to the container agent).
33
+ * @param {*} target the target to check
34
+ * @returns {boolean} true if the target is a registered MFE target
35
+ */
36
+ function isMfeTarget(target) {
37
+ return target?.type === _constants.V2_TYPES.MFE;
38
+ }
32
39
 
33
40
  /**
34
41
  * Returns a single registered entity associated with a given iframe interface ID. Returns undefined if no entity is found.
@@ -124,14 +131,8 @@ function dedupeRegisteredEntitiesByAsset(entities) {
124
131
  */
125
132
  function getVersion2Attributes(target, aggregateInstance) {
126
133
  if (!supportsV2(aggregateInstance)) return {};
127
- const containerAgentEntityGuid = aggregateInstance.agentRef.runtime.appMetadata.agents[0].entityGuid;
128
- /** if there's no target, but we are in v2 mode, this means the data belongs to the container agent */
129
- if (!target) {
130
- return {
131
- 'entity.guid': containerAgentEntityGuid,
132
- appId: aggregateInstance.agentRef.info.applicationID
133
- };
134
- }
134
+ /** if the target isn't a registered MFE, but we are in v2 mode, this means the data belongs to the container agent */
135
+ if (!isMfeTarget(target)) return aggregateInstance.agentRef.runtime.v2Target.attributes;
135
136
  /** otherwise, the data belongs to the target (MFE) and should be attributed as such */
136
137
  return target.attributes;
137
138
  }
@@ -161,16 +162,17 @@ function getVersion2DuplicationAttributes(target, aggregateInstance) {
161
162
  * @returns {boolean} returns true if the event should be duplicated for the target, false otherwise
162
163
  */
163
164
  function shouldDuplicate(target, aggregateInstance) {
164
- return !!target && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.register.duplicate_data_to_container;
165
+ return isMfeTarget(target) && !!supportsV2(aggregateInstance) && aggregateInstance.agentRef.init.api.register.duplicate_data_to_container;
165
166
  }
166
167
 
167
168
  /**
168
169
  * Finds the registered targets from the stack trace for a given agent reference.
169
170
  * @param {*} agentRef The agent reference to use for finding targets.
170
- * @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array with undefined.
171
+ * @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array containing the container target.
171
172
  */
172
173
  function findTargetsFromStackTrace(agentRef) {
173
- if (!isValid(true, agentRef) || !agentRef?.runtime?.registeredEntities?.length) return [undefined];
174
+ if (!agentRef?.runtime) return [];
175
+ if (!isValid(true, agentRef) || !agentRef.runtime.registeredEntities?.length) return [agentRef.runtime.v2Target];
174
176
  const targets = [];
175
177
  try {
176
178
  var urls = (0, _scriptTracker.extractUrlsFromStack)((0, _scriptTracker.getDeepStackTrace)());
@@ -182,15 +184,15 @@ function findTargetsFromStackTrace(agentRef) {
182
184
  // Silent catch to prevent errors from propagating
183
185
  }
184
186
  const deduped = dedupeTargetsByInstance(targets);
185
- if (!deduped.length) deduped.push(undefined); // if we can't find any targets from the stack trace, return an array with undefined to signify the container agent is the target
187
+ if (!deduped.length) deduped.push(agentRef.runtime.v2Target); // if we can't find any targets from the stack trace, the container agent is the target
186
188
  return deduped;
187
189
  }
188
190
 
189
191
  /**
190
192
  * Removes duplicate targets from an array, keyed by `target.instance`. This guards against the same canonical
191
193
  * target re-entering the array via multiple matched stack-frame URLs (e.g. a recursive call whose stack contains the
192
- * same file at multiple depths). Entries with no `instance` (i.e. `undefined`, meaning "the container agent")
193
- * naturally collapse to a single entry too, which is the desired behavior.
194
+ * same file at multiple depths). Multiple container-target entries (whose `instance` is the container agent's stable
195
+ * `agentIdentifier`) naturally collapse to a single entry too, which is the desired behavior.
194
196
  * @param {Array} targets
195
197
  * @returns {Array} deduped list of targets, preserving relative order of first occurrence
196
198
  */
@@ -7,7 +7,7 @@ exports.subscribeToPageUnload = subscribeToPageUnload;
7
7
  exports.subscribeToVisibilityChange = subscribeToVisibilityChange;
8
8
  var _eventListenerOpts = require("../event-listener/event-listener-opts");
9
9
  /**
10
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
11
11
  * SPDX-License-Identifier: Apache-2.0
12
12
  */
13
13
 
@@ -10,7 +10,7 @@ var _wrapFunction = require("./wrap-function");
10
10
  var _getOrSet = require("../util/get-or-set");
11
11
  var _runtime = require("../constants/runtime");
12
12
  /**
13
- * Copyright 2020-2025 New Relic, Inc. All rights reserved.
13
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
14
14
  * SPDX-License-Identifier: Apache-2.0
15
15
  */
16
16
 
@@ -31,16 +31,17 @@ const flag = "nr@wrapped:".concat(_contextualEe.contextId);
31
31
  * `document` (if in a browser scope). Adds custom events in context of a new emitter scoped only to these methods.
32
32
  * @param {Object} sharedEE - The shared event emitter on which a new scoped
33
33
  * event emitter will be based.
34
+ * @param {*} [agentRef] - The agent reference, used to attribute wrapped calls to the correct v2 target.
34
35
  * @returns {Object} Scoped event emitter with a debug ID of `events`.
35
36
  */
36
- function wrapEvents(sharedEE) {
37
+ function wrapEvents(sharedEE, agentRef) {
37
38
  var ee = scopedEE(sharedEE);
38
39
 
39
40
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
40
41
  // then we increment the count to track # of feats using this at runtime.
41
42
  if (wrapped[ee.debugId]++) return ee;
42
43
  wrapped[ee.debugId] = 1; // otherwise, first feature to wrap events
43
- var wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee, true);
44
+ var wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee, true, agentRef);
44
45
 
45
46
  // Guard against instrumenting environments w/o necessary features
46
47
  if ('getPrototypeOf' in Object) {
@@ -52,8 +52,8 @@ function wrapFetch(sharedEE, agentRef) {
52
52
  wrapPromiseMethod(_runtime.globalScope, 'fetch', prefix);
53
53
  ee.on(prefix + 'end', function (err, res, targets) {
54
54
  var ctx = this;
55
- // undefined target reports to container
56
- ctx.targets = targets || [undefined];
55
+ // default target reports to container
56
+ ctx.targets = targets || [agentRef.runtime.v2Target];
57
57
  if (res) {
58
58
  var size = res.headers.get('content-length');
59
59
  if (size !== null) {
@@ -89,7 +89,7 @@ function createWrapperWithEmitter(emitter, always, agentRef) {
89
89
 
90
90
  // certain wrappers can inform the function wrapper to evaluate the stack of the executed wrapped function to find targets of the execution
91
91
  // (e.g. wrap-logger can inform this method to find try to find the MFE source of a console.log)
92
- targets = evaluateStack ? (0, _utils.findTargetsFromStackTrace)(agentRef) : [undefined]; // undefined target always maps to the container agent
92
+ targets = evaluateStack ? (0, _utils.findTargetsFromStackTrace)(agentRef) : [agentRef?.runtime?.v2Target]; // default target always maps to the container agent
93
93
 
94
94
  if (typeof getContext === 'function') {
95
95
  ctx = getContext(args, originalThis);
@@ -25,9 +25,10 @@ const HISTORY_FNS = ['pushState', 'replaceState'];
25
25
  * Wraps the `pushState` and `replaceState` methods of `window.history` and returns a corresponding event emitter
26
26
  * scoped to the history object.
27
27
  * @param {Object} sharedEE - The shared event emitter on which a new scoped event emitter will be based.
28
+ * @param {*} [agentRef] - The agent reference, used to attribute wrapped calls to the correct v2 target.
28
29
  * @returns {Object} Scoped event emitter with a debug ID of `history`.
29
30
  */
30
- function wrapHistory(sharedEE) {
31
+ function wrapHistory(sharedEE, agentRef) {
31
32
  const ee = scopedEE(sharedEE);
32
33
 
33
34
  // Notice if our wrapping never ran yet, the falsy NaN will not early return; but if it has,
@@ -36,7 +37,7 @@ function wrapHistory(sharedEE) {
36
37
  if (!_runtime.isBrowserScope || wrapped[ee.debugId]++) return ee;
37
38
  wrapped[ee.debugId] = 1; // otherwise, first feature to wrap history
38
39
 
39
- var wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee);
40
+ var wrapFn = (0, _wrapFunction.createWrapperWithEmitter)(ee, undefined, agentRef);
40
41
  /*
41
42
  * For objects that will be instantiated more than once, we wrap the object's prototype methods. The history object
42
43
  * is instantiated only once, so we can wrap its methods directly--and we must wrap the history methods directly as