@newrelic/browser-agent 1.301.0 → 1.302.0-rc.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 (142) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/cjs/common/config/init-types.js +1 -1
  3. package/dist/cjs/common/config/init.js +7 -1
  4. package/dist/cjs/common/config/runtime.js +1 -1
  5. package/dist/cjs/common/constants/agent-constants.js +11 -2
  6. package/dist/cjs/common/constants/env.cdn.js +1 -1
  7. package/dist/cjs/common/constants/env.npm.js +1 -1
  8. package/dist/cjs/common/drain/drain.js +1 -1
  9. package/dist/cjs/common/harvest/harvester.js +30 -39
  10. package/dist/cjs/common/util/mfe.js +45 -0
  11. package/dist/cjs/features/ajax/aggregate/index.js +5 -1
  12. package/dist/cjs/features/generic_events/aggregate/index.js +9 -8
  13. package/dist/cjs/features/generic_events/constants.js +3 -1
  14. package/dist/cjs/features/generic_events/instrument/index.js +38 -32
  15. package/dist/cjs/features/jserrors/aggregate/index.js +18 -17
  16. package/dist/cjs/features/logging/aggregate/index.js +19 -15
  17. package/dist/cjs/features/logging/shared/utils.js +3 -3
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +3 -33
  19. package/dist/cjs/features/session_replay/aggregate/index.js +13 -13
  20. package/dist/cjs/features/session_replay/shared/recorder.js +3 -2
  21. package/dist/cjs/features/session_trace/aggregate/index.js +0 -2
  22. package/dist/cjs/features/soft_navigations/aggregate/index.js +1 -2
  23. package/dist/cjs/features/utils/aggregate-base.js +45 -47
  24. package/dist/cjs/loaders/api/addPageAction.js +2 -2
  25. package/dist/cjs/loaders/api/log.js +2 -2
  26. package/dist/cjs/loaders/api/noticeError.js +2 -2
  27. package/dist/cjs/loaders/api/register-api-types.js +5 -5
  28. package/dist/cjs/loaders/api/register.js +80 -97
  29. package/dist/esm/common/config/init-types.js +1 -1
  30. package/dist/esm/common/config/init.js +7 -1
  31. package/dist/esm/common/config/runtime.js +1 -1
  32. package/dist/esm/common/constants/agent-constants.js +9 -1
  33. package/dist/esm/common/constants/env.cdn.js +1 -1
  34. package/dist/esm/common/constants/env.npm.js +1 -1
  35. package/dist/esm/common/drain/drain.js +1 -1
  36. package/dist/esm/common/harvest/harvester.js +30 -39
  37. package/dist/esm/common/util/mfe.js +38 -0
  38. package/dist/esm/features/ajax/aggregate/index.js +5 -1
  39. package/dist/esm/features/generic_events/aggregate/index.js +9 -8
  40. package/dist/esm/features/generic_events/constants.js +3 -1
  41. package/dist/esm/features/generic_events/instrument/index.js +38 -32
  42. package/dist/esm/features/jserrors/aggregate/index.js +18 -17
  43. package/dist/esm/features/logging/aggregate/index.js +19 -15
  44. package/dist/esm/features/logging/shared/utils.js +3 -3
  45. package/dist/esm/features/page_view_event/aggregate/index.js +3 -33
  46. package/dist/esm/features/session_replay/aggregate/index.js +13 -13
  47. package/dist/esm/features/session_replay/shared/recorder.js +3 -2
  48. package/dist/esm/features/session_trace/aggregate/index.js +0 -2
  49. package/dist/esm/features/soft_navigations/aggregate/index.js +1 -2
  50. package/dist/esm/features/utils/aggregate-base.js +46 -48
  51. package/dist/esm/loaders/api/addPageAction.js +2 -2
  52. package/dist/esm/loaders/api/log.js +2 -2
  53. package/dist/esm/loaders/api/noticeError.js +2 -2
  54. package/dist/esm/loaders/api/register-api-types.js +5 -5
  55. package/dist/esm/loaders/api/register.js +80 -97
  56. package/dist/tsconfig.tsbuildinfo +1 -1
  57. package/dist/types/common/config/init-types.d.ts +4 -1
  58. package/dist/types/common/config/init-types.d.ts.map +1 -1
  59. package/dist/types/common/config/init.d.ts.map +1 -1
  60. package/dist/types/common/constants/agent-constants.d.ts +7 -4
  61. package/dist/types/common/constants/agent-constants.d.ts.map +1 -1
  62. package/dist/types/common/harvest/harvester.d.ts +2 -2
  63. package/dist/types/common/harvest/harvester.d.ts.map +1 -1
  64. package/dist/types/common/util/mfe.d.ts +20 -0
  65. package/dist/types/common/util/mfe.d.ts.map +1 -0
  66. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  67. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -2
  68. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  69. package/dist/types/features/generic_events/constants.d.ts +1 -0
  70. package/dist/types/features/generic_events/instrument/index.d.ts.map +1 -1
  71. package/dist/types/features/jserrors/aggregate/index.d.ts +3 -3
  72. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  73. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  74. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  75. package/dist/types/features/logging/shared/utils.d.ts +2 -2
  76. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  77. package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -4
  78. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  79. package/dist/types/features/session_replay/aggregate/index.d.ts +13 -4
  80. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  81. package/dist/types/features/session_replay/shared/recorder.d.ts +1 -0
  82. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  83. package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
  84. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  85. package/dist/types/features/utils/aggregate-base.d.ts +13 -5
  86. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  87. package/dist/types/loaders/api/addPageAction.d.ts +1 -1
  88. package/dist/types/loaders/api/addPageAction.d.ts.map +1 -1
  89. package/dist/types/loaders/api/log.d.ts +1 -1
  90. package/dist/types/loaders/api/log.d.ts.map +1 -1
  91. package/dist/types/loaders/api/noticeError.d.ts +1 -1
  92. package/dist/types/loaders/api/noticeError.d.ts.map +1 -1
  93. package/dist/types/loaders/api/register-api-types.d.ts +4 -4
  94. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -1
  95. package/dist/types/loaders/api/register.d.ts +8 -1
  96. package/dist/types/loaders/api/register.d.ts.map +1 -1
  97. package/package.json +2 -2
  98. package/src/common/config/init-types.js +1 -1
  99. package/src/common/config/init.js +3 -1
  100. package/src/common/config/runtime.js +1 -1
  101. package/src/common/constants/agent-constants.js +10 -0
  102. package/src/common/drain/drain.js +1 -1
  103. package/src/common/harvest/harvester.js +27 -32
  104. package/src/common/util/mfe.js +35 -0
  105. package/src/features/ajax/aggregate/index.js +7 -1
  106. package/src/features/generic_events/aggregate/index.js +9 -8
  107. package/src/features/generic_events/constants.js +3 -1
  108. package/src/features/generic_events/instrument/index.js +44 -35
  109. package/src/features/jserrors/aggregate/index.js +17 -17
  110. package/src/features/logging/aggregate/index.js +20 -13
  111. package/src/features/logging/shared/utils.js +3 -3
  112. package/src/features/page_view_event/aggregate/index.js +3 -28
  113. package/src/features/session_replay/aggregate/index.js +12 -10
  114. package/src/features/session_replay/shared/recorder.js +3 -2
  115. package/src/features/session_trace/aggregate/index.js +0 -2
  116. package/src/features/soft_navigations/aggregate/index.js +1 -2
  117. package/src/features/utils/aggregate-base.js +47 -42
  118. package/src/loaders/api/addPageAction.js +2 -2
  119. package/src/loaders/api/log.js +2 -2
  120. package/src/loaders/api/noticeError.js +2 -2
  121. package/src/loaders/api/register-api-types.js +5 -5
  122. package/src/loaders/api/register.js +62 -89
  123. package/dist/cjs/common/util/target.js +0 -34
  124. package/dist/cjs/features/utils/entity-manager.js +0 -46
  125. package/dist/cjs/features/utils/event-store-manager.js +0 -174
  126. package/dist/cjs/loaders/api/register-api.js +0 -165
  127. package/dist/esm/common/util/target.js +0 -27
  128. package/dist/esm/features/utils/entity-manager.js +0 -39
  129. package/dist/esm/features/utils/event-store-manager.js +0 -166
  130. package/dist/esm/loaders/api/register-api.js +0 -159
  131. package/dist/types/common/util/target.d.ts +0 -18
  132. package/dist/types/common/util/target.d.ts.map +0 -1
  133. package/dist/types/features/utils/entity-manager.d.ts +0 -11
  134. package/dist/types/features/utils/entity-manager.d.ts.map +0 -1
  135. package/dist/types/features/utils/event-store-manager.d.ts +0 -85
  136. package/dist/types/features/utils/event-store-manager.d.ts.map +0 -1
  137. package/dist/types/loaders/api/register-api.d.ts +0 -14
  138. package/dist/types/loaders/api/register-api.d.ts.map +0 -1
  139. package/src/common/util/target.js +0 -27
  140. package/src/features/utils/entity-manager.js +0 -45
  141. package/src/features/utils/event-store-manager.js +0 -165
  142. package/src/loaders/api/register-api.js +0 -152
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
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.302.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.301.0...v1.302.0) (2025-10-24)
7
+
8
+
9
+ ### Features
10
+
11
+ * Prepare agent to utilize v2 harvests for MFE registrations ([#1495](https://github.com/newrelic/newrelic-browser-agent/issues/1495)) ([a7ac827](https://github.com/newrelic/newrelic-browser-agent/commit/a7ac82751dd8dade8294a66a7894c1295679265b))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * Improve accuracy in standalone AjaxRequest event start times ([#1598](https://github.com/newrelic/newrelic-browser-agent/issues/1598)) ([b41de5c](https://github.com/newrelic/newrelic-browser-agent/commit/b41de5cbcd9d75fa8bd3c67ad1270fde4f788733))
17
+ * User frustrations logic should be gated ([#1600](https://github.com/newrelic/newrelic-browser-agent/issues/1600)) ([984b05e](https://github.com/newrelic/newrelic-browser-agent/commit/984b05e8920be79d7c3d1c3d7d56c75fa2cfe253))
18
+
6
19
  ## [1.301.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.300.0...v1.301.0) (2025-10-21)
7
20
 
8
21
 
@@ -40,7 +40,7 @@ exports.default = void 0;
40
40
  * @property {Object} [metrics]
41
41
  * @property {boolean} [metrics.enabled] - Turn on/off the metrics feature (on by default).
42
42
  * @property {boolean} [metrics.autoStart] - If true, the agent will automatically start the metrics feature. Otherwise, it will be in a deferred state until the `start` API method is called.
43
- * @property {Array<Object>} [obfuscate] - Array of regexp and corresponding replacement patterns for obfuscating data.
43
+ * @property {{regex: RegExp | string, replacement: string}[]} [obfuscate] - Array of regexp and corresponding replacement patterns for obfuscating data.
44
44
  * @property {Object} [page_action]
45
45
  * @property {boolean} [page_action.enabled] - Must be true to allow PageAction events to be captured.
46
46
  * @property {Object} [page_view_event]
@@ -27,6 +27,7 @@ const InitModelFn = () => {
27
27
  const hiddenState = {
28
28
  feature_flags: [],
29
29
  experimental: {
30
+ allow_registered_children: false,
30
31
  resources: false
31
32
  },
32
33
  mask_selector: '*',
@@ -59,7 +60,12 @@ const InitModelFn = () => {
59
60
  autoStart: true
60
61
  },
61
62
  api: {
62
- allow_registered_children: true,
63
+ get allow_registered_children() {
64
+ return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.REGISTER) || hiddenState.experimental.allow_registered_children;
65
+ },
66
+ set allow_registered_children(val) {
67
+ hiddenState.experimental.allow_registered_children = val;
68
+ },
63
69
  duplicate_registered_data: false
64
70
  },
65
71
  distributed_tracing: {
@@ -29,7 +29,6 @@ const RuntimeModel = {
29
29
  customTransaction: undefined,
30
30
  denyList: undefined,
31
31
  disabled: false,
32
- entityManager: undefined,
33
32
  harvester: undefined,
34
33
  isolatedBacklog: false,
35
34
  isRecording: false,
@@ -42,6 +41,7 @@ const RuntimeModel = {
42
41
  releaseIds: {},
43
42
  session: undefined,
44
43
  timeKeeper: undefined,
44
+ registeredEntities: [],
45
45
  /** a proxy is set in agent-session to track jsAttributes changes for harvesting mechanics */
46
46
  jsAttributesMetadata: {
47
47
  bytes: 0
@@ -3,12 +3,21 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.SESSION_ERROR = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.DEFAULT_KEY = void 0;
6
+ exports.SUPPORTS_REGISTERED_ENTITIES = exports.SESSION_ERROR = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.DEFAULT_KEY = void 0;
7
+ var _features = require("../../loaders/features/features");
7
8
  /**
8
9
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
9
10
  * SPDX-License-Identifier: Apache-2.0
10
11
  */
12
+
11
13
  const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 16000;
12
14
  const MAX_PAYLOAD_SIZE = exports.MAX_PAYLOAD_SIZE = 1000000;
13
15
  const DEFAULT_KEY = exports.DEFAULT_KEY = 'NR_CONTAINER_AGENT';
14
- const SESSION_ERROR = exports.SESSION_ERROR = 'SESSION_ERROR';
16
+ const SESSION_ERROR = exports.SESSION_ERROR = 'SESSION_ERROR';
17
+ const SUPPORTS_REGISTERED_ENTITIES = exports.SUPPORTS_REGISTERED_ENTITIES = {
18
+ [_features.FEATURE_NAMES.logging]: true,
19
+ // flip other features here when they are supported by DEM consumers
20
+ [_features.FEATURE_NAMES.genericEvents]: false,
21
+ [_features.FEATURE_NAMES.jserrors]: false,
22
+ [_features.FEATURE_NAMES.ajax]: false
23
+ };
@@ -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.301.0";
20
+ const VERSION = exports.VERSION = "1.302.0-rc.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.301.0";
20
+ const VERSION = exports.VERSION = "1.302.0-rc.0";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -130,7 +130,7 @@ function drainGroup(agentIdentifier, group, activateGroup = true) {
130
130
  }
131
131
  if (!baseEE.isolatedBacklog) delete handlers[group];
132
132
  baseEE.backlog[group] = null;
133
- baseEE.emit('drain-' + group, []); // TODO: Code exists purely for a unit test and needs to be refined
133
+ baseEE.emit('drain-' + group, []); // Informs the feature that it has drained its backlog of events, this kicks off an immediate harvest to capture any load-driven events that were buffered.
134
134
  }
135
135
 
136
136
  /**
@@ -61,35 +61,30 @@ class Harvester {
61
61
  * @returns {boolean} True if 1+ network call was made. Note that this does not mean or guarantee that it was successful (or that all were in the case of more than 1).
62
62
  */
63
63
  triggerHarvestFor(aggregateInst, localOpts = {}) {
64
- if (aggregateInst.blocked) return false;
64
+ const output = {
65
+ ranSend: false,
66
+ payload: undefined,
67
+ endpointVersion: aggregateInst.harvestEndpointVersion || 1
68
+ };
69
+ if (aggregateInst.blocked) return output;
65
70
  const submitMethod = (0, _submitData.getSubmitMethod)(localOpts);
66
- if (!submitMethod) return false;
71
+ if (!submitMethod) return output;
67
72
  const shouldRetryOnFail = !localOpts.isFinalHarvest && submitMethod === _submitData.xhr; // always retry all features harvests except for final
68
- let dataToSendArr;
69
- let ranSend = false;
70
- if (!localOpts.directSend) {
71
- // primarily used by rum call to bypass makeHarvestPayload by providing payload directly
72
- dataToSendArr = aggregateInst.makeHarvestPayload(shouldRetryOnFail, localOpts); // be sure the 'this' of makeHarvestPayload is the aggregate w/ access to its harvestOpts
73
- if (!dataToSendArr) return false; // can be undefined if storage is empty or preharvest checks failed
74
- } else dataToSendArr = [localOpts.directSend];
75
- dataToSendArr.forEach(({
76
- targetApp,
77
- payload
78
- }) => {
79
- if (!payload) return;
80
- send(this.agentRef, {
81
- endpoint: _features.FEATURE_TO_ENDPOINT[aggregateInst.featureName],
82
- targetApp,
83
- payload,
84
- localOpts,
85
- submitMethod,
86
- cbFinished,
87
- raw: aggregateInst.harvestOpts.raw,
88
- featureName: aggregateInst.featureName
89
- });
90
- ranSend = true;
73
+ output.payload = !localOpts.directSend ? aggregateInst.makeHarvestPayload(shouldRetryOnFail, localOpts) : localOpts.directSend?.payload; // features like PVE can define the payload directly, bypassing the makeHarvestPayload logic
74
+
75
+ if (!output.payload) return output;
76
+ send(this.agentRef, {
77
+ endpoint: _features.FEATURE_TO_ENDPOINT[aggregateInst.featureName],
78
+ payload: output.payload,
79
+ localOpts,
80
+ submitMethod,
81
+ cbFinished,
82
+ raw: aggregateInst.harvestOpts.raw,
83
+ featureName: aggregateInst.featureName,
84
+ endpointVersion: output.endpointVersion
91
85
  });
92
- return ranSend;
86
+ output.ranSend = true;
87
+ return output;
93
88
 
94
89
  /**
95
90
  * This is executed immediately after harvest sends the data via XHR, or if there's nothing to send. Note that this excludes on unloading / sendBeacon.
@@ -122,13 +117,13 @@ const warnings = {};
122
117
  */
123
118
  function send(agentRef, {
124
119
  endpoint,
125
- targetApp,
126
120
  payload,
127
121
  localOpts = {},
128
122
  submitMethod,
129
123
  cbFinished,
130
124
  raw,
131
- featureName
125
+ featureName,
126
+ endpointVersion = 1
132
127
  }) {
133
128
  if (!agentRef.info.errorBeacon) return false;
134
129
  let {
@@ -138,15 +133,14 @@ function send(agentRef, {
138
133
  if (Object.keys(body).length === 0 && !localOpts.sendEmptyBody) {
139
134
  // if there's no body to send, just run onfinish stuff and return
140
135
  if (cbFinished) cbFinished({
141
- sent: false,
142
- targetApp
136
+ sent: false
143
137
  });
144
138
  return false;
145
139
  }
146
140
  const protocol = agentRef.init.ssl === false ? 'http' : 'https';
147
141
  const perceivedBeacon = agentRef.init.proxy.beacon || agentRef.info.errorBeacon;
148
- const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(endpoint !== _features.RUM ? '/' + endpoint : '', "/1/").concat(targetApp.licenseKey);
149
- const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint, targetApp.applicationID) : '';
142
+ const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(endpoint !== _features.RUM ? '/' + endpoint : '', "/").concat(endpointVersion, "/").concat(agentRef.info.licenseKey);
143
+ const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint) : '';
150
144
  let payloadParams = (0, _encode.obj)(qs, agentRef.runtime.maxBytes);
151
145
  if (baseParams === '' && payloadParams.startsWith('&')) {
152
146
  payloadParams = payloadParams.substring(1);
@@ -189,8 +183,7 @@ function send(agentRef, {
189
183
  status: this.status,
190
184
  retry: shouldRetry(this.status),
191
185
  fullUrl,
192
- xhr: this,
193
- targetApp
186
+ xhr: this
194
187
  };
195
188
  if (localOpts.needResponse) cbResult.responseText = this.responseText;
196
189
  cbFinished(cbResult);
@@ -206,8 +199,7 @@ function send(agentRef, {
206
199
  status,
207
200
  retry: shouldRetry(status),
208
201
  fullUrl,
209
- fetchResponse: response,
210
- targetApp
202
+ fetchResponse: response
211
203
  };
212
204
  if (localOpts.needResponse) cbResult.responseText = await response.text();
213
205
  cbFinished(cbResult);
@@ -248,7 +240,6 @@ function send(agentRef, {
248
240
  data: {
249
241
  endpoint,
250
242
  headers,
251
- targetApp,
252
243
  payload,
253
244
  submitMethod: getSubmitMethodName(),
254
245
  raw,
@@ -298,12 +289,12 @@ function cleanPayload(payload = {}) {
298
289
  }
299
290
 
300
291
  // The stuff that gets sent every time.
301
- function baseQueryString(agentRef, qs, endpoint, applicationID) {
292
+ function baseQueryString(agentRef, qs, endpoint) {
302
293
  const ref = agentRef.runtime.obfuscator.obfuscateString((0, _cleanUrl.cleanURL)('' + _runtime.globalScope.location));
303
294
  const session = agentRef.runtime.session;
304
295
  const hr = !!session?.state.sessionReplaySentFirstChunk && session?.state.sessionReplayMode === 1 && endpoint !== _features.JSERRORS;
305
296
  const ht = !!session?.state.traceHarvestStarted && session?.state.sessionTraceMode === 1 && ![_features.LOGS, _features.BLOBS].includes(endpoint);
306
- const qps = ['a=' + 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',
297
+ 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',
307
298
  // ck param DEPRECATED - still expected by backend
308
299
  '&s=' + (session?.state.value || '0'),
309
300
  // the 0 id encaps all untrackable and default traffic
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getVersion2Attributes = getVersion2Attributes;
7
+ exports.isValidMFETarget = isValidMFETarget;
8
+ /**
9
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
10
+ * SPDX-License-Identifier: Apache-2.0
11
+ */
12
+
13
+ /**
14
+ * @param {Object} [target] - the target to be validated
15
+ * @returns {boolean}
16
+ */
17
+ function isValidMFETarget(target = {}) {
18
+ return !!(target.id && target.name);
19
+ }
20
+
21
+ /**
22
+ * When given a valid target, returns an object with the MFE payload attributes. Returns an empty object otherwise.
23
+ * @param {Object} [target] the registered target
24
+ * @param {AggregateInstance} [aggregateInstance] the aggregate instance calling the method
25
+ * @returns {{'mfe.id': *, 'mfe.name': String}|{}} returns an empty object if args are not supplied or the aggregate instance is not supporting version 2
26
+ */
27
+ function getVersion2Attributes(target, aggregateInstance) {
28
+ if (aggregateInstance?.harvestEndpointVersion !== 2) return {};
29
+ const containerAgentEntityGuid = aggregateInstance.agentRef.runtime.appMetadata.agents[0].entityGuid;
30
+ if (!isValidMFETarget(target)) {
31
+ return {
32
+ 'entity.guid': containerAgentEntityGuid,
33
+ appId: aggregateInstance.agentRef.info.applicationID
34
+ };
35
+ }
36
+ return {
37
+ 'mfe.id': target.id,
38
+ // these field names may change as the schema is finalized
39
+ 'mfe.name': target.name,
40
+ // these field names may change as the schema is finalized
41
+ eventSource: 'MicroFrontendBrowserAgent',
42
+ // these field names may change as the schema is finalized
43
+ 'parent.id': containerAgentEntityGuid
44
+ };
45
+ }
@@ -131,9 +131,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
131
131
  if (!eventBuffer.length) return;
132
132
  const addString = (0, _belSerializer.getAddStringContext)(this.agentRef.runtime.obfuscator);
133
133
  let payload = 'bel.7;';
134
+ let firstTimestamp = 0;
134
135
  for (let i = 0; i < eventBuffer.length; i++) {
135
136
  const event = eventBuffer[i];
136
- const fields = [(0, _belSerializer.numeric)(event.startTime), (0, _belSerializer.numeric)(event.endTime - event.startTime), (0, _belSerializer.numeric)(0),
137
+ // ajax nodes are relative to the first node (or page origin if no previous node), so we need to calculate the relative start time
138
+ const relativeStartTime = event.startTime - firstTimestamp;
139
+ if (i === 0) firstTimestamp = event.startTime;
140
+ const fields = [(0, _belSerializer.numeric)(relativeStartTime), (0, _belSerializer.numeric)(event.endTime - event.startTime), (0, _belSerializer.numeric)(0),
137
141
  // callbackEnd
138
142
  (0, _belSerializer.numeric)(0),
139
143
  // no callbackDuration for non-SPA events
@@ -16,6 +16,7 @@ var _traverse = require("../../../common/util/traverse");
16
16
  var _userActionsAggregator = require("./user-actions/user-actions-aggregator");
17
17
  var _iframe = require("../../../common/dom/iframe");
18
18
  var _typeCheck = require("../../../common/util/type-check");
19
+ var _mfe = require("../../../common/util/mfe");
19
20
  /**
20
21
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
21
22
  * SPDX-License-Identifier: Apache-2.0
@@ -43,8 +44,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
43
44
  });
44
45
  }, this.featureName, this.ee);
45
46
  if (agentRef.init.page_action.enabled) {
46
- (0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes, targetEntityGuid) => {
47
- if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return (0, _console.warn)(56, this.featureName);
47
+ (0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes, target) => {
48
48
  this.addEvent({
49
49
  ...attributes,
50
50
  eventType: 'PageAction',
@@ -56,7 +56,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
56
56
  browserWidth: window.document.documentElement?.clientWidth,
57
57
  browserHeight: window.document.documentElement?.clientHeight
58
58
  })
59
- }, targetEntityGuid);
59
+ }, target);
60
60
  }, this.featureName, this.ee);
61
61
  }
62
62
  let addUserAction = () => {/** no-op */};
@@ -254,7 +254,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
254
254
  };
255
255
  this.addEvent(event);
256
256
  }, this.featureName, this.ee);
257
- agentRef.runtime.harvester.triggerHarvestFor(this);
258
257
  this.drain();
259
258
  });
260
259
  }
@@ -270,10 +269,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
270
269
  * * sessionTraceId: set by the `ptid=` query param
271
270
  * * userAgent*: set by the userAgent header
272
271
  * @param {object=} obj the event object for storing in the event buffer
273
- * @param {string=} targetEntityGuid the target entity guid for the event to scope buffering and harvesting. Defaults to agent config if undefined
272
+ * @param {string=} target the target metadata for the event to scope buffering and harvesting. Defaults to container agent config if undefined
274
273
  * @returns void
275
274
  */
276
- addEvent(obj = {}, targetEntityGuid) {
275
+ addEvent(obj = {}, target) {
277
276
  if (!obj || !Object.keys(obj).length) return;
278
277
  if (!obj.eventType) {
279
278
  (0, _console.warn)(44);
@@ -288,7 +287,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
288
287
  timestamp: Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp((0, _now.now)())),
289
288
  /** all generic events require pageUrl(s) */
290
289
  pageUrl: (0, _cleanUrl.cleanURL)('' + _runtime.initialLocation),
291
- currentUrl: (0, _cleanUrl.cleanURL)('' + location)
290
+ currentUrl: (0, _cleanUrl.cleanURL)('' + location),
291
+ /** Specific attributes only supplied if harvesting to endpoint version 2 */
292
+ ...(0, _mfe.getVersion2Attributes)(target, this)
292
293
  };
293
294
  const eventAttributes = {
294
295
  /** Agent-level custom attributes */
@@ -298,7 +299,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
298
299
  /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
299
300
  ...obj
300
301
  };
301
- this.events.add(eventAttributes, targetEntityGuid);
302
+ this.events.add(eventAttributes);
302
303
  }
303
304
  serializer(eventBuffer) {
304
305
  return (0, _traverse.applyFnToProps)({
@@ -18,5 +18,7 @@ const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
18
18
  const FRUSTRATION_TIMEOUT_MS = exports.FRUSTRATION_TIMEOUT_MS = 2000;
19
19
  const RESERVED_EVENT_TYPES = exports.RESERVED_EVENT_TYPES = ['PageAction', 'UserAction', 'BrowserPerformance'];
20
20
  const FEATURE_FLAGS = exports.FEATURE_FLAGS = {
21
- RESOURCES: 'experimental.resources'
21
+ RESOURCES: 'experimental.resources',
22
+ REGISTER: 'register'
23
+ // register.jserrors and register.generic_events are also used, but not referenced directly so no need to represent here
22
24
  };
@@ -38,6 +38,13 @@ class Instrument extends _instrumentBase.InstrumentBase {
38
38
  (0, _finished.setupFinishedAPI)(agentRef);
39
39
  (0, _register.setupRegisterAPI)(agentRef);
40
40
  (0, _measure.setupMeasureAPI)(agentRef);
41
+ const ufEnabled = agentRef.init.feature_flags.includes('user_frustrations');
42
+ let historyEE;
43
+ if (_runtime.isBrowserScope && ufEnabled) {
44
+ (0, _wrapFetch.wrapFetch)(this.ee);
45
+ (0, _wrapXhr.wrapXhr)(this.ee);
46
+ historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
47
+ }
41
48
  if (_runtime.isBrowserScope) {
42
49
  if (agentRef.init.user_actions.enabled) {
43
50
  _constants.OBSERVED_EVENTS.forEach(eventType => (0, _eventListenerOpts.windowAddEventListener)(eventType, evt => (0, _handle.handle)('ua', [evt], undefined, this.featureName, this.ee), true));
@@ -51,6 +58,37 @@ class Instrument extends _instrumentBase.InstrumentBase {
51
58
  }
52
59
  // Capture is not used here so that we don't get element focus/blur events, only the window's as they do not bubble. They are also not cancellable, so no worries about being front of line.
53
60
  );
61
+ if (ufEnabled) {
62
+ _runtime.globalScope.addEventListener('error', () => {
63
+ (0, _handle.handle)('uaErr', [], undefined, _features.FEATURE_NAMES.genericEvents, this.ee);
64
+ }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
65
+ this.ee.on('open-xhr-start', (args, xhr) => {
66
+ if (!isInternalTraffic(args[1])) {
67
+ xhr.addEventListener('readystatechange', () => {
68
+ if (xhr.readyState === 2) {
69
+ // HEADERS_RECEIVED
70
+ (0, _handle.handle)('uaXhr', [], undefined, _features.FEATURE_NAMES.genericEvents, this.ee);
71
+ }
72
+ });
73
+ }
74
+ });
75
+ this.ee.on('fetch-start', fetchArguments => {
76
+ if (fetchArguments.length >= 1 && !isInternalTraffic((0, _extractUrl.extractUrl)(fetchArguments[0]))) {
77
+ (0, _handle.handle)('uaXhr', [], undefined, _features.FEATURE_NAMES.genericEvents, this.ee);
78
+ }
79
+ });
80
+ function isInternalTraffic(url) {
81
+ const parsedUrl = (0, _parseUrl.parseUrl)(url);
82
+ return agentRef.beacons.includes(parsedUrl.hostname + ':' + parsedUrl.port);
83
+ }
84
+ historyEE.on('pushState-end', navigationChange);
85
+ historyEE.on('replaceState-end', navigationChange);
86
+ window.addEventListener('hashchange', navigationChange, (0, _eventListenerOpts.eventListenerOpts)(true, this.removeOnAbort?.signal));
87
+ window.addEventListener('popstate', navigationChange, (0, _eventListenerOpts.eventListenerOpts)(true, this.removeOnAbort?.signal));
88
+ function navigationChange() {
89
+ historyEE.emit('navChange');
90
+ }
91
+ }
54
92
  }
55
93
  if (agentRef.init.performance.resources.enabled && _runtime.globalScope.PerformanceObserver?.supportedEntryTypes.includes('resource')) {
56
94
  const observer = new PerformanceObserver(list => {
@@ -63,14 +101,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
63
101
  buffered: true
64
102
  });
65
103
  }
66
- const historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
67
- historyEE.on('pushState-end', navigationChange);
68
- historyEE.on('replaceState-end', navigationChange);
69
- window.addEventListener('hashchange', navigationChange, (0, _eventListenerOpts.eventListenerOpts)(true, this.removeOnAbort?.signal));
70
- window.addEventListener('popstate', navigationChange, (0, _eventListenerOpts.eventListenerOpts)(true, this.removeOnAbort?.signal));
71
- function navigationChange() {
72
- historyEE.emit('navChange');
73
- }
74
104
  }
75
105
  try {
76
106
  this.removeOnAbort = new AbortController();
@@ -79,30 +109,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
79
109
  this.removeOnAbort?.abort();
80
110
  this.abortHandler = undefined; // weakly allow this abort op to run only once
81
111
  };
82
- _runtime.globalScope.addEventListener('error', () => {
83
- (0, _handle.handle)('uaErr', [], undefined, _features.FEATURE_NAMES.genericEvents, this.ee);
84
- }, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
85
- (0, _wrapFetch.wrapFetch)(this.ee);
86
- (0, _wrapXhr.wrapXhr)(this.ee);
87
- this.ee.on('open-xhr-start', (args, xhr) => {
88
- if (!isInternalTraffic(args[1])) {
89
- xhr.addEventListener('readystatechange', () => {
90
- if (xhr.readyState === 2) {
91
- // HEADERS_RECEIVED
92
- (0, _handle.handle)('uaXhr', [], undefined, _features.FEATURE_NAMES.genericEvents, this.ee);
93
- }
94
- });
95
- }
96
- });
97
- this.ee.on('fetch-start', fetchArguments => {
98
- if (fetchArguments.length >= 1 && !isInternalTraffic((0, _extractUrl.extractUrl)(fetchArguments[0]))) {
99
- (0, _handle.handle)('uaXhr', [], undefined, _features.FEATURE_NAMES.genericEvents, this.ee);
100
- }
101
- });
102
- function isInternalTraffic(url) {
103
- const parsedUrl = (0, _parseUrl.parseUrl)(url);
104
- return agentRef.beacons.includes(parsedUrl.hostname + ':' + parsedUrl.port);
105
- }
106
112
 
107
113
  /** If any of the sources are active, import the aggregator. otherwise deregister */
108
114
  if (genericEventSourceConfigs.some(x => x)) this.importAggregator(agentRef, () => Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "generic_events-aggregate" */'../aggregate'))));else this.deregisterDrain();
@@ -18,8 +18,7 @@ var _aggregateBase = require("../../utils/aggregate-base");
18
18
  var _now = require("../../../common/timing/now");
19
19
  var _traverse = require("../../../common/util/traverse");
20
20
  var _internalErrors = require("./internal-errors");
21
- var _target = require("../../../common/util/target");
22
- var _console = require("../../../common/util/console");
21
+ var _mfe = require("../../../common/util/mfe");
23
22
  var _causeString = require("./cause-string");
24
23
  /**
25
24
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
@@ -34,6 +33,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
34
33
  static featureName = _constants.FEATURE_NAME;
35
34
  constructor(agentRef) {
36
35
  super(agentRef, _constants.FEATURE_NAME);
36
+
37
+ /** set up agg-level behaviors specific to this feature */
38
+ this.harvestOpts.aggregatorTypes = ['err', 'ierr', 'xhr']; // the types in EventAggregator this feature cares about
37
39
  this.stackReported = {};
38
40
  this.observedAt = {};
39
41
  this.pageviewReported = {};
@@ -46,8 +48,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
46
48
  (0, _registerHandler.registerHandler)('ierr', (...args) => this.storeError(...args), this.featureName, this.ee);
47
49
  (0, _registerHandler.registerHandler)('softNavFlush', (interactionId, wasFinished, softNavAttrs, interactionEndTime) => this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs, interactionEndTime), this.featureName, this.ee); // when an ixn is done or cancelled
48
50
 
49
- this.harvestOpts.aggregatorTypes = ['err', 'ierr', 'xhr']; // the types in EventAggregator this feature cares about
50
-
51
51
  // 0 == off, 1 == on
52
52
  this.waitForFlags(['err']).then(([errFlag]) => {
53
53
  if (errFlag) {
@@ -107,10 +107,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
107
107
  * @param {object=} target the target to buffer and harvest to, if undefined the default configuration target is used
108
108
  * @returns
109
109
  */
110
- storeError(err, time, internal, customAttributes, hasReplay, swallowReason, targetEntityGuid) {
110
+ storeError(err, time, internal, customAttributes, hasReplay, swallowReason, target) {
111
111
  if (!err) return;
112
- const target = this.agentRef.runtime.entityManager.get(targetEntityGuid);
113
- if (!target) return (0, _console.warn)(56, this.featureName);
114
112
  // are we in an interaction
115
113
  time = time || (0, _now.now)();
116
114
  let filterOutput;
@@ -148,7 +146,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
148
146
  if (filterOutput?.group) params.errorGroup = filterOutput.group;
149
147
 
150
148
  // Should only decorate "hasReplay" for the container agent, so check if the target matches the config
151
- if (hasReplay && (0, _target.isContainerAgentTarget)(target, this.agentRef)) params.hasReplay = hasReplay;
149
+ if (hasReplay && !target) params.hasReplay = hasReplay;
152
150
  /**
153
151
  * The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
154
152
  * stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
@@ -183,14 +181,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
183
181
 
184
182
  // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
185
183
  const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
186
- if (this.shouldAllowMainAgentToCapture(targetEntityGuid)) (0, _handle.handle)('trace-jserror', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
184
+ if (this.shouldAllowMainAgentToCapture(target)) (0, _handle.handle)('trace-jserror', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
187
185
  // still send EE events for other features such as above, but stop this one from aggregating internal data
188
186
  if (this.blocked) return;
189
187
  if (err?.__newrelic?.[this.agentIdentifier]) {
190
188
  params._interactionId = err.__newrelic[this.agentIdentifier].interactionId;
191
189
  params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId;
192
190
  }
193
- if (this.shouldAllowMainAgentToCapture(targetEntityGuid)) {
191
+ if (this.shouldAllowMainAgentToCapture(target)) {
194
192
  const softNavInUse = Boolean(this.agentRef.features?.[_features.FEATURE_NAMES.softNav]);
195
193
  // Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
196
194
  // They each will also tack on their respective properties to the params object as part of the decision flow.
@@ -211,11 +209,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
211
209
  }
212
210
 
213
211
  // always add directly if scoped to a sub-entity, the other pathways above will be deterministic if the main agent should procede
214
- if (targetEntityGuid) this.#storeJserrorForHarvest([...jsErrorEvent, targetEntityGuid], false, params._softNavAttributes);
212
+ if (target) this.#storeJserrorForHarvest([...jsErrorEvent, target], false, params._softNavAttributes);
215
213
  }
216
214
  #storeJserrorForHarvest(errorInfoArr, softNavOccurredFinished, softNavCustomAttrs = {}) {
217
- let [type, bucketHash, params, newMetrics, localAttrs, targetEntityGuid] = errorInfoArr;
218
- const allCustomAttrs = {};
215
+ let [type, bucketHash, params, newMetrics, localAttrs, target] = errorInfoArr;
216
+ const allCustomAttrs = {
217
+ /** MFE specific attributes if in "multiple" mode (ie consumer version 2) */
218
+ ...(0, _mfe.getVersion2Attributes)(target, this)
219
+ };
219
220
  if (softNavOccurredFinished) {
220
221
  Object.entries(softNavCustomAttrs).forEach(([k, v]) => setCustom(k, v)); // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
221
222
  bucketHash += params.browserInteractionId;
@@ -230,7 +231,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
230
231
 
231
232
  const jsAttributesHash = (0, _stringHashCode.stringHashCode)((0, _stringify.stringify)(allCustomAttrs));
232
233
  const aggregateHash = bucketHash + ':' + jsAttributesHash;
233
- this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs], targetEntityGuid);
234
+ this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs]);
234
235
  function setCustom(key, val) {
235
236
  allCustomAttrs[key] = val && typeof val === 'object' ? (0, _stringify.stringify)(val) : val;
236
237
  }
@@ -239,11 +240,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
239
240
  /**
240
241
  * If the event lacks an entityGuid (the default behavior), the main agent should capture the data. If the data is assigned to a sub-entity target
241
242
  * the main agent should not capture events unless it is configured to do so.
242
- * @param {string} entityGuid - the context object for the event
243
+ * @param {string} target - the context object for the event
243
244
  * @returns {boolean} - whether the main agent should capture the event to its internal target
244
245
  */
245
- shouldAllowMainAgentToCapture(entityGuid) {
246
- return !entityGuid || this.agentRef.init.api.duplicate_registered_data;
246
+ shouldAllowMainAgentToCapture(target) {
247
+ return !target || this.agentRef.init.api.duplicate_registered_data;
247
248
  }
248
249
 
249
250
  // TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.