@newrelic/browser-agent 1.285.0 → 1.287.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 (180) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +6 -0
  3. package/dist/cjs/common/config/init-types.js +96 -0
  4. package/dist/cjs/common/config/init.js +9 -79
  5. package/dist/cjs/common/config/runtime.js +7 -6
  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/dom/selector-path.js +1 -1
  9. package/dist/cjs/common/harvest/harvester.js +3 -3
  10. package/dist/cjs/common/util/feature-flags.js +6 -6
  11. package/dist/cjs/common/util/target.js +34 -0
  12. package/dist/cjs/features/ajax/aggregate/index.js +2 -1
  13. package/dist/cjs/features/generic_events/aggregate/index.js +10 -6
  14. package/dist/cjs/features/jserrors/aggregate/index.js +44 -22
  15. package/dist/cjs/features/logging/aggregate/index.js +17 -11
  16. package/dist/cjs/features/logging/shared/utils.js +3 -2
  17. package/dist/cjs/features/metrics/aggregate/index.js +6 -4
  18. package/dist/cjs/features/page_view_event/aggregate/index.js +60 -11
  19. package/dist/cjs/features/page_view_event/instrument/index.js +4 -0
  20. package/dist/cjs/features/session_replay/aggregate/index.js +8 -6
  21. package/dist/cjs/features/session_replay/instrument/index.js +1 -1
  22. package/dist/cjs/features/session_replay/shared/recorder-events.js +4 -2
  23. package/dist/cjs/features/session_replay/shared/recorder.js +17 -11
  24. package/dist/cjs/features/session_trace/aggregate/trace/storage.js +4 -3
  25. package/dist/cjs/features/soft_navigations/aggregate/index.js +12 -2
  26. package/dist/cjs/features/spa/aggregate/index.js +3 -3
  27. package/dist/cjs/features/spa/aggregate/interaction.js +6 -9
  28. package/dist/cjs/features/utils/aggregate-base.js +51 -24
  29. package/dist/cjs/features/utils/entity-manager.js +47 -0
  30. package/dist/cjs/features/utils/event-store-manager.js +79 -54
  31. package/dist/cjs/features/utils/nr1-debugger.js +1 -1
  32. package/dist/cjs/interfaces/registered-entity.js +114 -0
  33. package/dist/cjs/loaders/agent-base.js +1 -1
  34. package/dist/cjs/loaders/agent.js +3 -1
  35. package/dist/cjs/loaders/api/api-methods.js +1 -1
  36. package/dist/cjs/loaders/api/api.js +97 -69
  37. package/dist/cjs/loaders/api/apiAsync.js +19 -22
  38. package/dist/cjs/loaders/api/register-api-types.js +35 -0
  39. package/dist/cjs/loaders/api/register-api.js +165 -0
  40. package/dist/cjs/loaders/configure/configure.js +12 -15
  41. package/dist/cjs/loaders/micro-agent-base.js +17 -1
  42. package/dist/cjs/loaders/micro-agent.js +4 -1
  43. package/dist/esm/common/config/init-types.js +92 -0
  44. package/dist/esm/common/config/init.js +9 -79
  45. package/dist/esm/common/config/runtime.js +7 -6
  46. package/dist/esm/common/constants/env.cdn.js +1 -1
  47. package/dist/esm/common/constants/env.npm.js +1 -1
  48. package/dist/esm/common/dom/selector-path.js +1 -1
  49. package/dist/esm/common/harvest/harvester.js +3 -3
  50. package/dist/esm/common/util/feature-flags.js +6 -6
  51. package/dist/esm/common/util/target.js +27 -0
  52. package/dist/esm/features/ajax/aggregate/index.js +2 -1
  53. package/dist/esm/features/generic_events/aggregate/index.js +10 -6
  54. package/dist/esm/features/jserrors/aggregate/index.js +44 -22
  55. package/dist/esm/features/logging/aggregate/index.js +17 -11
  56. package/dist/esm/features/logging/shared/utils.js +3 -2
  57. package/dist/esm/features/metrics/aggregate/index.js +6 -4
  58. package/dist/esm/features/page_view_event/aggregate/index.js +60 -11
  59. package/dist/esm/features/page_view_event/instrument/index.js +4 -0
  60. package/dist/esm/features/session_replay/aggregate/index.js +8 -6
  61. package/dist/esm/features/session_replay/instrument/index.js +1 -1
  62. package/dist/esm/features/session_replay/shared/recorder-events.js +4 -2
  63. package/dist/esm/features/session_replay/shared/recorder.js +17 -11
  64. package/dist/esm/features/session_trace/aggregate/trace/storage.js +4 -3
  65. package/dist/esm/features/soft_navigations/aggregate/index.js +12 -2
  66. package/dist/esm/features/spa/aggregate/index.js +3 -3
  67. package/dist/esm/features/spa/aggregate/interaction.js +6 -9
  68. package/dist/esm/features/utils/aggregate-base.js +51 -24
  69. package/dist/esm/features/utils/entity-manager.js +40 -0
  70. package/dist/esm/features/utils/event-store-manager.js +79 -54
  71. package/dist/esm/features/utils/nr1-debugger.js +1 -1
  72. package/dist/esm/interfaces/registered-entity.js +107 -0
  73. package/dist/esm/loaders/agent-base.js +1 -1
  74. package/dist/esm/loaders/agent.js +3 -1
  75. package/dist/esm/loaders/api/api-methods.js +1 -1
  76. package/dist/esm/loaders/api/api.js +95 -67
  77. package/dist/esm/loaders/api/apiAsync.js +14 -17
  78. package/dist/esm/loaders/api/register-api-types.js +33 -0
  79. package/dist/esm/loaders/api/register-api.js +159 -0
  80. package/dist/esm/loaders/configure/configure.js +13 -16
  81. package/dist/esm/loaders/micro-agent-base.js +17 -1
  82. package/dist/esm/loaders/micro-agent.js +4 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/dist/types/common/config/init-types.d.ts +275 -0
  85. package/dist/types/common/config/init-types.d.ts.map +1 -0
  86. package/dist/types/common/config/init.d.ts +1 -262
  87. package/dist/types/common/config/init.d.ts.map +1 -1
  88. package/dist/types/common/config/runtime.d.ts.map +1 -1
  89. package/dist/types/common/dom/selector-path.d.ts.map +1 -1
  90. package/dist/types/common/util/feature-flags.d.ts +1 -1
  91. package/dist/types/common/util/feature-flags.d.ts.map +1 -1
  92. package/dist/types/common/util/target.d.ts +18 -0
  93. package/dist/types/common/util/target.d.ts.map +1 -0
  94. package/dist/types/features/ajax/aggregate/index.d.ts +1 -1
  95. package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
  96. package/dist/types/features/generic_events/aggregate/index.d.ts +2 -1
  97. package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
  98. package/dist/types/features/jserrors/aggregate/index.d.ts +9 -1
  99. package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
  100. package/dist/types/features/logging/aggregate/index.d.ts +3 -3
  101. package/dist/types/features/logging/aggregate/index.d.ts.map +1 -1
  102. package/dist/types/features/logging/shared/utils.d.ts +2 -1
  103. package/dist/types/features/logging/shared/utils.d.ts.map +1 -1
  104. package/dist/types/features/metrics/aggregate/index.d.ts +1 -0
  105. package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
  106. package/dist/types/features/page_view_event/aggregate/index.d.ts +10 -2
  107. package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
  108. package/dist/types/features/page_view_event/instrument/index.d.ts.map +1 -1
  109. package/dist/types/features/session_replay/aggregate/index.d.ts +2 -11
  110. package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
  111. package/dist/types/features/session_replay/shared/recorder-events.d.ts +1 -0
  112. package/dist/types/features/session_replay/shared/recorder-events.d.ts.map +1 -1
  113. package/dist/types/features/session_replay/shared/recorder.d.ts +4 -4
  114. package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
  115. package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
  116. package/dist/types/features/spa/aggregate/interaction.d.ts +3 -4
  117. package/dist/types/features/spa/aggregate/interaction.d.ts.map +1 -1
  118. package/dist/types/features/utils/aggregate-base.d.ts +22 -5
  119. package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
  120. package/dist/types/features/utils/entity-manager.d.ts +15 -0
  121. package/dist/types/features/utils/entity-manager.d.ts.map +1 -0
  122. package/dist/types/features/utils/event-store-manager.d.ts +48 -24
  123. package/dist/types/features/utils/event-store-manager.d.ts.map +1 -1
  124. package/dist/types/interfaces/registered-entity.d.ts +72 -0
  125. package/dist/types/interfaces/registered-entity.d.ts.map +1 -0
  126. package/dist/types/loaders/agent.d.ts +2 -1
  127. package/dist/types/loaders/agent.d.ts.map +1 -1
  128. package/dist/types/loaders/api/api.d.ts +1 -20
  129. package/dist/types/loaders/api/api.d.ts.map +1 -1
  130. package/dist/types/loaders/api/apiAsync.d.ts +1 -1
  131. package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
  132. package/dist/types/loaders/api/register-api-types.d.ts +56 -0
  133. package/dist/types/loaders/api/register-api-types.d.ts.map +1 -0
  134. package/dist/types/loaders/api/register-api.d.ts +14 -0
  135. package/dist/types/loaders/api/register-api.d.ts.map +1 -0
  136. package/dist/types/loaders/configure/configure.d.ts +1 -0
  137. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  138. package/dist/types/loaders/micro-agent-base.d.ts +17 -0
  139. package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
  140. package/dist/types/loaders/micro-agent.d.ts +2 -0
  141. package/dist/types/loaders/micro-agent.d.ts.map +1 -1
  142. package/package.json +10 -1
  143. package/src/common/config/init-types.js +92 -0
  144. package/src/common/config/init.js +9 -79
  145. package/src/common/config/runtime.js +7 -6
  146. package/src/common/dom/selector-path.js +1 -3
  147. package/src/common/harvest/harvester.js +3 -3
  148. package/src/common/util/feature-flags.js +6 -6
  149. package/src/common/util/target.js +27 -0
  150. package/src/features/ajax/aggregate/index.js +2 -1
  151. package/src/features/generic_events/aggregate/index.js +8 -6
  152. package/src/features/jserrors/aggregate/index.js +42 -20
  153. package/src/features/logging/aggregate/index.js +14 -10
  154. package/src/features/logging/shared/utils.js +3 -2
  155. package/src/features/metrics/aggregate/index.js +7 -5
  156. package/src/features/page_view_event/aggregate/index.js +50 -8
  157. package/src/features/page_view_event/instrument/index.js +4 -0
  158. package/src/features/session_replay/aggregate/index.js +6 -3
  159. package/src/features/session_replay/instrument/index.js +1 -1
  160. package/src/features/session_replay/shared/recorder-events.js +5 -2
  161. package/src/features/session_replay/shared/recorder.js +17 -11
  162. package/src/features/session_trace/aggregate/trace/storage.js +3 -3
  163. package/src/features/soft_navigations/aggregate/index.js +11 -2
  164. package/src/features/spa/aggregate/index.js +3 -3
  165. package/src/features/spa/aggregate/interaction.js +6 -9
  166. package/src/features/utils/aggregate-base.js +56 -24
  167. package/src/features/utils/entity-manager.js +45 -0
  168. package/src/features/utils/event-store-manager.js +72 -49
  169. package/src/features/utils/nr1-debugger.js +1 -1
  170. package/src/interfaces/registered-entity.js +107 -0
  171. package/src/loaders/agent-base.js +2 -2
  172. package/src/loaders/agent.js +4 -1
  173. package/src/loaders/api/api-methods.js +1 -1
  174. package/src/loaders/api/api.js +94 -62
  175. package/src/loaders/api/apiAsync.js +14 -18
  176. package/src/loaders/api/register-api-types.js +33 -0
  177. package/src/loaders/api/register-api.js +152 -0
  178. package/src/loaders/configure/configure.js +12 -12
  179. package/src/loaders/micro-agent-base.js +18 -2
  180. package/src/loaders/micro-agent.js +5 -1
@@ -23,7 +23,7 @@ class MicroAgentBase {
23
23
  * @param {...any} args
24
24
  */
25
25
  #callMethod(methodName, ...args) {
26
- if (typeof this.api?.[methodName] !== 'function') (0, _console.warn)(35, methodName);else return this.api[methodName](...args);
26
+ if (this[methodName] === MicroAgentBase.prototype[methodName]) (0, _console.warn)(35, methodName);else return this[methodName](...args);
27
27
  }
28
28
 
29
29
  // MicroAgent class custom defines its own start
@@ -38,6 +38,22 @@ class MicroAgentBase {
38
38
  return this.#callMethod('addPageAction', name, attributes);
39
39
  }
40
40
 
41
+ /**
42
+ * @experimental
43
+ * IMPORTANT: This feature is being developed for use internally and is not in a public-facing production-ready state.
44
+ * It is not recommended for use in production environments and will not receive support for issues.
45
+ *
46
+ * Registers an external caller to report through the base agent to a different target than the base agent.
47
+ * @param {object} target the target object to report data to
48
+ * @param {string} target.licenseKey The licenseKey to report data to
49
+ * @param {string} target.applicationID The applicationID to report data to
50
+ * @param {string=} target.entityGuid The entityGuid to report data to
51
+ * @returns {object} Returns an object that contains the available API methods and configurations to use with the external caller. See loaders/api/api.js for more information.
52
+ */
53
+ register(target) {
54
+ return this.#callMethod('register', target);
55
+ }
56
+
41
57
  /**
42
58
  * Records a custom event with a specified eventType and attributes.
43
59
  * {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
@@ -20,6 +20,7 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
20
20
  const nonAutoFeatures = [_features.FEATURE_NAMES.jserrors, _features.FEATURE_NAMES.genericEvents, _features.FEATURE_NAMES.metrics, _features.FEATURE_NAMES.logging];
21
21
 
22
22
  /**
23
+ * @deprecated This feature has been deprecated and will be removed in a future release. A future product centralizing around a single agent instance will be released as a replacement, at which time this loader will be removed.
23
24
  * A minimal agent class designed to only respond to manual user input. As such, this class does not
24
25
  * automatically instrument. Instead, each MicroAgent instance will lazy load the required features and can support loading multiple instances on one page.
25
26
  * Out of the box, it can manually handle and report Page View, Page Action, and Error events.
@@ -38,7 +39,6 @@ class MicroAgent extends _microAgentBase.MicroAgentBase {
38
39
  isolatedBacklog: true
39
40
  }
40
41
  }, options.loaderType || 'micro-agent');
41
- Object.assign(this, this.api); // the APIs should be available at the class level for micro-agent
42
42
 
43
43
  /**
44
44
  * Starts a set of agent features if not running in "autoStart" mode
@@ -91,5 +91,8 @@ class MicroAgent extends _microAgentBase.MicroAgentBase {
91
91
  runtime: this.runtime
92
92
  };
93
93
  }
94
+ get api() {
95
+ return this;
96
+ }
94
97
  }
95
98
  exports.MicroAgent = MicroAgent;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ *
5
+ */
6
+
7
+ /**
8
+ * @typedef {Object} Init
9
+ * @property {Object} [ajax]
10
+ * @property {Array<string>} [ajax.deny_list] - List of domain URLs to be excluded from AjaxRequest collection.
11
+ * @property {boolean} [ajax.block_internal] - If true, agent requests going to harvest endpoint are treated as on deny list. In other words, agent will not self-report AJAX.
12
+ * @property {boolean} [ajax.enabled] - Turn on/off the ajax feature (on by default).
13
+ * @property {boolean} [ajax.autoStart] - If true, the agent will automatically start the ajax feature. Otherwise, it will be in a deferred state until the `start` API method is called.
14
+ * @property {Object} [api]
15
+ * @property {boolean} [api.allow_registered_children] - If true, the agent will allow registered children to be sent to the server.
16
+ * @property {boolean} [api.duplicate_registered_data] - If true, the agent will capture registered child data to the main agent as well as the registered child.
17
+ * @property {Object} [distributed_tracing]
18
+ * @property {boolean} [distributed_tracing.enabled] - If true, distributed tracing headers will be added to outgoing requests. Requires ajax feature to be running.
19
+ * @property {boolean} [distributed_tracing.exclude_newrelic_header]
20
+ * @property {boolean} [distributed_tracing.cors_use_newrelic_header]
21
+ * @property {boolean} [distributed_tracing.cors_use_tracecontext_headers]
22
+ * @property {Array<string>} [distributed_tracing.allowed_origins]
23
+ * @property {Array<string>} [feature_flags] - An array of feature flags to enable experimental features.
24
+ * @property {Object} [generic_events]
25
+ * @property {boolean} [generic_events.enabled] - Turn on/off the generic events feature (on by default). This is required for `PageAction`, `UserAction`, and `BrowserPerformance` events.
26
+ * @property {boolean} [generic_events.autoStart] - If true, the agent will automatically start the generic events feature. Otherwise, it will be in a deferred state until the `start` API method is called.
27
+ * @property {Object} [harvest]
28
+ * @property {number} [harvest.interval] - The interval in seconds at which the agent will send out data. It's not recommended to change this value.
29
+ * @property {Object} [jserrors]
30
+ * @property {boolean} [jserrors.enabled] - Turn on/off the jserrors feature (on by default).
31
+ * @property {boolean} [jserrors.autoStart] - If true, the agent will automatically start the jserrors feature. Otherwise, it will be in a deferred state until the `start` API method is called.
32
+ * @property {Object} [logging]
33
+ * @property {boolean} [logging.enabled] - Turn on/off the logging feature (on by default).
34
+ * @property {boolean} [logging.autoStart] - If true, the agent will automatically start the logging feature. Otherwise, it will be in a deferred state until the `start` API method is called.
35
+ * @property {Object} [metrics]
36
+ * @property {boolean} [metrics.enabled] - Turn on/off the metrics feature (on by default).
37
+ * @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.
38
+ * @property {Array<Object>} [obfuscate] - Array of regexp and corresponding replacement patterns for obfuscating data.
39
+ * @property {Object} [page_action]
40
+ * @property {boolean} [page_action.enabled] - Must be true to allow PageAction events to be captured.
41
+ * @property {Object} [page_view_event]
42
+ * @property {boolean} [page_view_event.enabled] - This setting is ignored! PageViewEvent is always enabled by force.
43
+ * @property {boolean} [page_view_event.autoStart] - If true, the agent will automatically send the RUM request. Otherwise, it will be in a deferred state until the `start` API method is called.
44
+ * @property {Object} [page_view_timing]
45
+ * @property {boolean} [page_view_timing.enabled] - Turn on/off the page view timing feature (on by default).
46
+ * @property {boolean} [page_view_timing.autoStart] - If true, the agent will automatically start the page view timing feature. Otherwise, it will be in a deferred state until the `start` API method is called.
47
+ * @property {Object} [performance]
48
+ * @property {boolean} [performance.capture_marks] - If true, the agent will capture PerformanceMark events.
49
+ * @property {boolean} [performance.capture_measures] - If true, the agent will capture PerformanceMeasure events.
50
+ * @property {boolean} [performance.capture_detail] - If true, `BrowserPerformance` events from marks and measures will include, as attribute(s), the `detail` metadata provided to `markOptions` and `measureOptions`.
51
+ * @property {Object} [performance.resources]
52
+ * @property {boolean} [performance.resources.enabled] - If true, the agent will capture PerformanceResourceTiming entries.
53
+ * @property {Array<string>} [performance.resources.asset_types] - Array of `initiatorType` strings to filter the desired ResourceTiming entries. By default, all resource types are captured.
54
+ * @property {Array<string>} [performance.resources.first_party_domains] - Each resource URL will be checked against this list to determine if it should be labeled "first party" in the resulting `BrowserPerformance` event.
55
+ * @property {boolean} [performance.resources.ignore_newrelic] - When true (default), resource entries associated with New Relic domains will be ignored.
56
+ * @property {Object} [privacy]
57
+ * @property {boolean} [privacy.cookies_enabled] - If true (default), session tracking of users across page loads is enabled in the agent. This is required for session trace, replay, and session-related features.
58
+ * @property {Object} [proxy]
59
+ * @property {string} [proxy.assets] - Set value will be used to overwrite the webpack asset path used to fetch agent assets.
60
+ * @property {string} [proxy.beacon] - Set value will be used to overwrite the endpoint URL to which we send analytics.
61
+ * @property {Object} [session]
62
+ * @property {number} [session.expiresMs] - When session tracking is on, this determines how long a session will last before expiring. Modifying this value is not recommended.
63
+ * @property {number} [session.inactiveMs] - When session tracking is on, this determines how long a session will last without user activity before expiring. Modifying this value is not recommended.
64
+ * @property {Object} [session_replay]
65
+ * @property {boolean} [session_replay.autoStart] - If true, the agent will automatically start the session replay feature. Otherwise, it will be in a deferred state until the `start` API method is called.
66
+ * @property {boolean} [session_replay.enabled] - Turn on/off the session replay feature (off by default).
67
+ * @property {boolean} [session_replay.preload] - If true, allow the agent to run rrweb recorder immediately instead of waiting until after the window.load event, for new sessions. Existing sessions ignore this setting.
68
+ * @property {number} [session_replay.sampling_rate] - This setting is deprecated and ineffective. Sampling is controlled in New Relic by server-side configuration.
69
+ * @property {number} [session_replay.error_sampling_rate] - This setting is deprecated and ineffective.
70
+ * @property {boolean} [session_replay.collect_fonts] - When true, serialize fonts for collection without public asset url. This is currently broken -- https://github.com/rrweb-io/rrweb/issues/1304.
71
+ * @property {boolean} [session_replay.inline_images] - When true, serialize images for collection without public asset url. Not recommended for use. This is currently for TESTING as it easily generates payloads too large to be harvested.
72
+ * @property {boolean} [session_replay.fix_stylesheets] - When true, tries to fetch any missing stylesheets again to inline in replayer.
73
+ * @property {boolean} [session_replay.mask_all_inputs] - If true, all input content will be masked with asterisks.
74
+ * @property {string} [session_replay.mask_text_selector] - Set value should be in CSS selector syntax and is used to identify matching elements to mask.
75
+ * @property {string} [session_replay.block_selector] - Set value should be in CSS selector syntax and is used to identify matching elements to block.
76
+ * @property {Object} [session_replay.mask_input_options] - If mask_all_inputs is not true, this object will be used to select what input to mask. Passwords are forcibly always masked.
77
+ * @property {Object} [session_trace]
78
+ * @property {boolean} [session_trace.enabled] - Turn on/off the session trace feature (on by default).
79
+ * @property {boolean} [session_trace.autoStart] - If true, the agent will automatically start the session trace feature. Otherwise, it will be in a deferred state until the `start` API method is called.
80
+ * @property {Object} [soft_navigations]
81
+ * @property {boolean} [soft_navigations.enabled] - Turn on/off the soft navigations feature (on by default).
82
+ * @property {boolean} [soft_navigations.autoStart] - If true, the agent will automatically start the soft navigations feature. Otherwise, it will be in a deferred state until the `start` API method is called.
83
+ * @property {Object} [spa]
84
+ * @property {boolean} [spa.enabled] - Turn on/off the single page application feature (on by default). NOTE: the SPA feature is deprecated and under removal procedure.
85
+ * @property {boolean} [spa.autoStart] - If true, the agent will automatically start the single page application feature. Otherwise, it will be in a deferred state until the `start` API method is called.
86
+ * @property {boolean} [ssl] - If explicitly false, the agent will use HTTP instead of HTTPS. This setting should NOT be used.
87
+ * @property {Object} [user_actions]
88
+ * @property {boolean} [user_actions.enabled] - Must be true to allow UserAction events to be captured.
89
+ * @property {Array<string>} [user_actions.elementAttributes] - List of HTML Element properties to be captured with UserAction events' target elements. This may help to identify the source element being interacted with in the UI.
90
+ */
91
+
92
+ export default {};
@@ -10,88 +10,14 @@ import { getNREUMInitializedAgent } from '../window/nreum';
10
10
  import { getModeledObject } from './configurable';
11
11
 
12
12
  /**
13
- * @typedef {Object} Init
14
- * @property {Object} [ajax]
15
- * @property {Array<string>} [ajax.deny_list] - List of domain URLs to be excluded from AjaxRequest collection.
16
- * @property {boolean} [ajax.block_internal] - If true, agent requests going to harvest endpoint are treated as on deny list. In other words, agent will not self-report AJAX.
17
- * @property {boolean} [ajax.enabled] - Turn on/off the ajax feature (on by default).
18
- * @property {boolean} [ajax.autoStart] - If true, the agent will automatically start the ajax feature. Otherwise, it will be in a deferred state until the `start` API method is called.
19
- * @property {Object} [distributed_tracing]
20
- * @property {boolean} [distributed_tracing.enabled] - If true, distributed tracing headers will be added to outgoing requests. Requires ajax feature to be running.
21
- * @property {boolean} [distributed_tracing.exclude_newrelic_header]
22
- * @property {boolean} [distributed_tracing.cors_use_newrelic_header]
23
- * @property {boolean} [distributed_tracing.cors_use_tracecontext_headers]
24
- * @property {Array<string>} [distributed_tracing.allowed_origins]
25
- * @property {Array<string>} [feature_flags] - An array of feature flags to enable experimental features.
26
- * @property {Object} [generic_events]
27
- * @property {boolean} [generic_events.enabled] - Turn on/off the generic events feature (on by default). This is required for `PageAction`, `UserAction`, and `BrowserPerformance` events.
28
- * @property {boolean} [generic_events.autoStart] - If true, the agent will automatically start the generic events feature. Otherwise, it will be in a deferred state until the `start` API method is called.
29
- * @property {Object} [harvest]
30
- * @property {number} [harvest.interval] - The interval in seconds at which the agent will send out data. It's not recommended to change this value.
31
- * @property {Object} [jserrors]
32
- * @property {boolean} [jserrors.enabled] - Turn on/off the jserrors feature (on by default).
33
- * @property {boolean} [jserrors.autoStart] - If true, the agent will automatically start the jserrors feature. Otherwise, it will be in a deferred state until the `start` API method is called.
34
- * @property {Object} [logging]
35
- * @property {boolean} [logging.enabled] - Turn on/off the logging feature (on by default).
36
- * @property {boolean} [logging.autoStart] - If true, the agent will automatically start the logging feature. Otherwise, it will be in a deferred state until the `start` API method is called.
37
- * @property {Object} [metrics]
38
- * @property {boolean} [metrics.enabled] - Turn on/off the metrics feature (on by default).
39
- * @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.
40
- * @property {Array<Object>} [obfuscate] - Array of regexp and corresponding replacement patterns for obfuscating data.
41
- * @property {Object} [page_action]
42
- * @property {boolean} [page_action.enabled] - Must be true to allow PageAction events to be captured.
43
- * @property {Object} [page_view_event]
44
- * @property {boolean} [page_view_event.enabled] - This setting is ignored! PageViewEvent is always enabled by force.
45
- * @property {boolean} [page_view_event.autoStart] - If true, the agent will automatically send the RUM request. Otherwise, it will be in a deferred state until the `start` API method is called.
46
- * @property {Object} [page_view_timing]
47
- * @property {boolean} [page_view_timing.enabled] - Turn on/off the page view timing feature (on by default).
48
- * @property {boolean} [page_view_timing.autoStart] - If true, the agent will automatically start the page view timing feature. Otherwise, it will be in a deferred state until the `start` API method is called.
49
- * @property {Object} [performance]
50
- * @property {boolean} [performance.capture_marks] - If true, the agent will capture PerformanceMark events.
51
- * @property {boolean} [performance.capture_measures] - If true, the agent will capture PerformanceMeasure events.
52
- * @property {boolean} [performance.capture_detail] - If true, `BrowserPerformance` events from marks and measures will include, as attribute(s), the `detail` metadata provided to `markOptions` and `measureOptions`.
53
- * @property {Object} [performance.resources]
54
- * @property {boolean} [performance.resources.enabled] - If true, the agent will capture PerformanceResourceTiming entries.
55
- * @property {Array<string>} [performance.resources.asset_types] - Array of `initiatorType` strings to filter the desired ResourceTiming entries. By default, all resource types are captured.
56
- * @property {Array<string>} [performance.resources.first_party_domains] - Each resource URL will be checked against this list to determine if it should be labeled "first party" in the resulting `BrowserPerformance` event.
57
- * @property {boolean} [performance.resources.ignore_newrelic] - When true (default), resource entries associated with New Relic domains will be ignored.
58
- * @property {Object} [privacy]
59
- * @property {boolean} [privacy.cookies_enabled] - If true (default), session tracking of users across page loads is enabled in the agent. This is required for session trace, replay, and session-related features.
60
- * @property {Object} [proxy]
61
- * @property {string} [proxy.assets] - Set value will be used to overwrite the webpack asset path used to fetch agent assets.
62
- * @property {string} [proxy.beacon] - Set value will be used to overwrite the endpoint URL to which we send analytics.
63
- * @property {Object} [session]
64
- * @property {number} [session.expiresMs] - When session tracking is on, this determines how long a session will last before expiring. Modifying this value is not recommended.
65
- * @property {number} [session.inactiveMs] - When session tracking is on, this determines how long a session will last without user activity before expiring. Modifying this value is not recommended.
66
- * @property {Object} [session_replay]
67
- * @property {boolean} [session_replay.autoStart] - If true, the agent will automatically start the session replay feature. Otherwise, it will be in a deferred state until the `start` API method is called.
68
- * @property {boolean} [session_replay.enabled] - Turn on/off the session replay feature (off by default).
69
- * @property {boolean} [session_replay.preload] - If true, allow the agent to run rrweb recorder immediately instead of waiting until after the window.load event, for new sessions. Existing sessions ignore this setting.
70
- * @property {number} [session_replay.sampling_rate] - This setting is deprecated and ineffective. Sampling is controlled in New Relic by server-side configuration.
71
- * @property {number} [session_replay.error_sampling_rate] - This setting is deprecated and ineffective.
72
- * @property {boolean} [session_replay.collect_fonts] - When true, serialize fonts for collection without public asset url. This is currently broken -- https://github.com/rrweb-io/rrweb/issues/1304.
73
- * @property {boolean} [session_replay.inline_images] - When true, serialize images for collection without public asset url. Not recommended for use. This is currently for TESTING as it easily generates payloads too large to be harvested.
74
- * @property {boolean} [session_replay.fix_stylesheets] - When true, tries to fetch any missing stylesheets again to inline in replayer.
75
- * @property {boolean} [session_replay.mask_all_inputs] - If true, all input content will be masked with asterisks.
76
- * @property {string} [session_replay.mask_text_selector] - Set value should be in CSS selector syntax and is used to identify matching elements to mask.
77
- * @property {string} [session_replay.block_selector] - Set value should be in CSS selector syntax and is used to identify matching elements to block.
78
- * @property {Object} [session_replay.mask_input_options] - If mask_all_inputs is not true, this object will be used to select what input to mask. Passwords are forcibly always masked.
79
- * @property {Object} [session_trace]
80
- * @property {boolean} [session_trace.enabled] - Turn on/off the session trace feature (on by default).
81
- * @property {boolean} [session_trace.autoStart] - If true, the agent will automatically start the session trace feature. Otherwise, it will be in a deferred state until the `start` API method is called.
82
- * @property {Object} [soft_navigations]
83
- * @property {boolean} [soft_navigations.enabled] - Turn on/off the soft navigations feature (on by default).
84
- * @property {boolean} [soft_navigations.autoStart] - If true, the agent will automatically start the soft navigations feature. Otherwise, it will be in a deferred state until the `start` API method is called.
85
- * @property {Object} [spa]
86
- * @property {boolean} [spa.enabled] - Turn on/off the single page application feature (on by default). NOTE: the SPA feature is deprecated and under removal procedure.
87
- * @property {boolean} [spa.autoStart] - If true, the agent will automatically start the single page application feature. Otherwise, it will be in a deferred state until the `start` API method is called.
88
- * @property {boolean} [ssl] - If explicitly false, the agent will use HTTP instead of HTTPS. This setting should NOT be used.
89
- * @property {Object} [user_actions]
90
- * @property {boolean} [user_actions.enabled] - Must be true to allow UserAction events to be captured.
91
- * @property {Array<string>} [user_actions.elementAttributes] - List of HTML Element properties to be captured with UserAction events' target elements. This may help to identify the source element being interacted with in the UI.
13
+ * @typedef {import('./init-types').Init} Init
92
14
  */
93
15
 
94
16
  const nrMask = '[data-nr-mask]';
17
+
18
+ /**
19
+ * @returns {Init} the default configuration object
20
+ */
95
21
  const model = () => {
96
22
  const hiddenState = {
97
23
  feature_flags: [],
@@ -129,6 +55,10 @@ const model = () => {
129
55
  enabled: true,
130
56
  autoStart: true
131
57
  },
58
+ api: {
59
+ allow_registered_children: true,
60
+ duplicate_registered_data: false
61
+ },
132
62
  distributed_tracing: {
133
63
  enabled: undefined,
134
64
  exclude_newrelic_header: undefined,
@@ -19,21 +19,22 @@ const readonly = {
19
19
  originTime
20
20
  };
21
21
  const model = {
22
+ /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
23
+ appMetadata: {},
22
24
  customTransaction: undefined,
25
+ denyList: undefined,
23
26
  disabled: false,
27
+ entityManager: undefined,
28
+ harvester: undefined,
24
29
  isolatedBacklog: false,
25
30
  loaderType: undefined,
26
31
  maxBytes: 30000,
32
+ obfuscator: undefined,
27
33
  onerror: undefined,
28
34
  ptid: undefined,
29
35
  releaseIds: {},
30
- /** Agent-specific metadata found in the RUM call response. ex. entityGuid */
31
- appMetadata: {},
32
36
  session: undefined,
33
- denyList: undefined,
34
- timeKeeper: undefined,
35
- obfuscator: undefined,
36
- harvester: undefined
37
+ timeKeeper: undefined
37
38
  };
38
39
  const _cache = {};
39
40
  export function getRuntime(id) {
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.285.0";
14
+ export const VERSION = "1.287.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -11,7 +11,7 @@
11
11
  /**
12
12
  * Exposes the version of the agent
13
13
  */
14
- export const VERSION = "1.285.0";
14
+ export const VERSION = "1.287.0";
15
15
 
16
16
  /**
17
17
  * Exposes the build type of the agent
@@ -40,7 +40,7 @@ export const generateSelectorPath = (elem, targetFields = []) => {
40
40
  localName
41
41
  } = elem;
42
42
  targetFields.forEach(field => {
43
- nearestFields[nearestAttrName(field)] ||= elem[field];
43
+ nearestFields[nearestAttrName(field)] ||= elem[field]?.baseVal || elem[field];
44
44
  });
45
45
  const selector = [localName, id ? "#".concat(id) : '', pathSelector ? ">".concat(pathSelector) : ''].join('');
46
46
  pathSelector = selector;
@@ -145,7 +145,7 @@ function send(agentRef, {
145
145
  const protocol = agentRef.init.ssl === false ? 'http' : 'https';
146
146
  const perceivedBeacon = agentRef.init.proxy.beacon || agentRef.info.errorBeacon;
147
147
  const url = raw ? "".concat(protocol, "://").concat(perceivedBeacon, "/").concat(endpoint) : "".concat(protocol, "://").concat(perceivedBeacon).concat(endpoint !== RUM ? '/' + endpoint : '', "/1/").concat(targetApp.licenseKey);
148
- const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint, targetApp.appId) : '';
148
+ const baseParams = !raw ? baseQueryString(agentRef, qs, endpoint, targetApp.applicationID) : '';
149
149
  let payloadParams = obj(qs, agentRef.runtime.maxBytes);
150
150
  if (baseParams === '' && payloadParams.startsWith('&')) {
151
151
  payloadParams = payloadParams.substring(1);
@@ -185,8 +185,8 @@ function send(agentRef, {
185
185
  sent: this.status !== 0,
186
186
  status: this.status,
187
187
  retry: shouldRetry(this.status),
188
- xhr: this,
189
188
  fullUrl,
189
+ xhr: this,
190
190
  targetApp
191
191
  };
192
192
  if (localOpts.needResponse) cbResult.responseText = this.responseText;
@@ -210,7 +210,7 @@ function send(agentRef, {
210
210
  }
211
211
  dispatchGlobalEvent({
212
212
  agentIdentifier: agentRef.agentIdentifier,
213
- loaded: !!activatedFeatures?.[agentRef.agentIdentifier],
213
+ drained: !!activatedFeatures?.[agentRef.agentIdentifier],
214
214
  type: 'data',
215
215
  name: 'harvest',
216
216
  feature: featureName,
@@ -2,7 +2,6 @@
2
2
  * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
- import { ee } from '../event-emitter/contextual-ee';
6
5
  import { dispatchGlobalEvent } from '../dispatch/global-event';
7
6
  const sentIds = new Set();
8
7
 
@@ -16,19 +15,20 @@ export const activatedFeatures = {};
16
15
  * @param {string} agentIdentifier agent instance identifier
17
16
  * @returns {void}
18
17
  */
19
- export function activateFeatures(flags, agentIdentifier) {
20
- const sharedEE = ee.get(agentIdentifier);
18
+ export function activateFeatures(flags, agentRef) {
19
+ const agentIdentifier = agentRef.agentIdentifier;
21
20
  activatedFeatures[agentIdentifier] ??= {};
22
- if (!(flags && typeof flags === 'object')) return;
21
+ if (!flags || typeof flags !== 'object') return;
23
22
  if (sentIds.has(agentIdentifier)) return;
24
- sharedEE.emit('rumresp', [flags]);
23
+ agentRef.ee.emit('rumresp', [flags]);
25
24
  activatedFeatures[agentIdentifier] = flags;
26
25
  sentIds.add(agentIdentifier);
27
26
 
28
- // let any window level subscribers know that the agent is running
27
+ // let any window level subscribers know that the agent is running, per install docs
29
28
  dispatchGlobalEvent({
30
29
  agentIdentifier,
31
30
  loaded: true,
31
+ drained: true,
32
32
  type: 'lifecycle',
33
33
  name: 'load',
34
34
  feature: undefined,
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright 2020-2025 New Relic, Inc. All rights reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * @param {Object} [target] - the target to be validated
8
+ * @param {boolean} [allowUndefined=true]
9
+ * @returns {boolean}
10
+ */
11
+ export function isValidTarget(target) {
12
+ /** target can be undefined to support legacy/default behaviors - main agent does not supply a target */
13
+ if (!target) return true;
14
+ /** if not undefined, we require specific values */
15
+ return !!(target.licenseKey && target.applicationID);
16
+ }
17
+
18
+ /**
19
+ * Checks if the target matches the container agent target
20
+ * @param {*} target the target to be validated
21
+ * @param {*} agentRef the agent reference to be validated
22
+ * @returns {boolean}
23
+ */
24
+ export function isContainerAgentTarget(target, agentRef) {
25
+ if (!target) return true;
26
+ return target.licenseKey === agentRef.info.licenseKey && target.applicationID === agentRef.info.applicationID;
27
+ }
@@ -54,7 +54,7 @@ export class Aggregate extends AggregateBase {
54
54
 
55
55
  // Report ajax timeslice metric (to be harvested by jserrors feature, but only if it's running).
56
56
  if (jserrorsInUse && (shouldCollect || !shouldOmitAjaxMetrics)) {
57
- this.agentRef.sharedAggregator.add(['xhr', hash, params, metrics]);
57
+ this.agentRef.sharedAggregator?.add(['xhr', hash, params, metrics]);
58
58
  }
59
59
  if (!shouldCollect) {
60
60
  if (params.hostname === this.agentRef.info.errorBeacon || this.agentRef.init.proxy?.beacon && params.hostname === this.agentRef.init.proxy.beacon) {
@@ -107,6 +107,7 @@ export class Aggregate extends AggregateBase {
107
107
  }
108
108
  }
109
109
  serializer(eventBuffer) {
110
+ if (!eventBuffer.length) return;
110
111
  const addString = getAddStringContext(this.agentIdentifier);
111
112
  let payload = 'bel.7;';
112
113
  for (let i = 0; i < eventBuffer.length; i++) {
@@ -37,7 +37,8 @@ export class Aggregate extends AggregateBase {
37
37
  });
38
38
  }, this.featureName, this.ee);
39
39
  if (agentRef.init.page_action.enabled) {
40
- registerHandler('api-addPageAction', (timestamp, name, attributes) => {
40
+ registerHandler('api-addPageAction', (timestamp, name, attributes, targetEntityGuid) => {
41
+ if (!this.agentRef.runtime.entityManager.get(targetEntityGuid)) return warn(56, this.featureName);
41
42
  this.addEvent({
42
43
  ...attributes,
43
44
  eventType: 'PageAction',
@@ -49,7 +50,7 @@ export class Aggregate extends AggregateBase {
49
50
  browserWidth: window.document.documentElement?.clientWidth,
50
51
  browserHeight: window.document.documentElement?.clientHeight
51
52
  })
52
- });
53
+ }, targetEntityGuid);
53
54
  }, this.featureName, this.ee);
54
55
  }
55
56
  let addUserAction = () => {/** no-op */};
@@ -231,9 +232,10 @@ export class Aggregate extends AggregateBase {
231
232
  * * sessionTraceId: set by the `ptid=` query param
232
233
  * * userAgent*: set by the userAgent header
233
234
  * @param {object=} obj the event object for storing in the event buffer
235
+ * @param {string=} targetEntityGuid the target entity guid for the event to scope buffering and harvesting. Defaults to agent config if undefined
234
236
  * @returns void
235
237
  */
236
- addEvent(obj = {}) {
238
+ addEvent(obj = {}, targetEntityGuid) {
237
239
  if (!obj || !Object.keys(obj).length) return;
238
240
  if (!obj.eventType) {
239
241
  warn(44);
@@ -258,14 +260,16 @@ export class Aggregate extends AggregateBase {
258
260
  /** Event-specific attributes take precedence over agent-level custom attributes and fallbacks */
259
261
  ...obj
260
262
  };
261
- const addedEvent = this.events.add(eventAttributes);
262
- if (!addedEvent && !this.events.isEmpty()) {
263
+ const addedEvent = this.events.add(eventAttributes, targetEntityGuid);
264
+ if (!addedEvent && !this.events.isEmpty(undefined, targetEntityGuid)) {
263
265
  /** could not add the event because it pushed the buffer over the limit
264
266
  * so we harvest early, and try to add it again now that the buffer is cleared
265
267
  * if it fails again, we do nothing
266
268
  */
267
269
  this.ee.emit(SUPPORTABILITY_METRIC_CHANNEL, ['GenericEvents/Harvest/Max/Seen']);
268
- this.agentRef.runtime.harvester.triggerHarvestFor(this);
270
+ this.agentRef.runtime.harvester.triggerHarvestFor(this, {
271
+ targetEntityGuid
272
+ });
269
273
  this.events.add(eventAttributes);
270
274
  }
271
275
  }
@@ -17,6 +17,8 @@ import { AggregateBase } from '../../utils/aggregate-base';
17
17
  import { now } from '../../../common/timing/now';
18
18
  import { applyFnToProps } from '../../../common/util/traverse';
19
19
  import { evaluateInternalError } from './internal-errors';
20
+ import { isContainerAgentTarget } from '../../../common/util/target';
21
+ import { warn } from '../../../common/util/console';
20
22
 
21
23
  /**
22
24
  * @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
@@ -96,10 +98,13 @@ export class Aggregate extends AggregateBase {
96
98
  * @param {object=} customAttributes any custom attributes to be included in the error payload
97
99
  * @param {boolean=} hasReplay a flag indicating if the error occurred during a replay session
98
100
  * @param {string=} swallowReason a string indicating pre-defined reason if swallowing the error. Mainly used by the internal error SMs.
101
+ * @param {object=} target the target to buffer and harvest to, if undefined the default configuration target is used
99
102
  * @returns
100
103
  */
101
- storeError(err, time, internal, customAttributes, hasReplay, swallowReason) {
104
+ storeError(err, time, internal, customAttributes, hasReplay, swallowReason, targetEntityGuid) {
102
105
  if (!err) return;
106
+ const target = this.agentRef.runtime.entityManager.get(targetEntityGuid);
107
+ if (!target) return warn(56, this.featureName);
103
108
  // are we in an interaction
104
109
  time = time || now();
105
110
  let filterOutput;
@@ -131,7 +136,9 @@ export class Aggregate extends AggregateBase {
131
136
  // Notice if filterOutput isn't false|undefined OR our specified object, this func would've returned already (so it's unnecessary to req-check group).
132
137
  // Do not modify the name ('errorGroup') of params without DEM approval!
133
138
  if (filterOutput?.group) params.errorGroup = filterOutput.group;
134
- if (hasReplay) params.hasReplay = hasReplay;
139
+
140
+ // Should only decorate "hasReplay" for the container agent, so check if the target matches the config
141
+ if (hasReplay && isContainerAgentTarget(target, this.agentRef)) params.hasReplay = hasReplay;
135
142
  /**
136
143
  * The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
137
144
  * stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
@@ -166,33 +173,38 @@ export class Aggregate extends AggregateBase {
166
173
 
167
174
  // Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
168
175
  const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
169
- handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee);
176
+ if (this.shouldAllowMainAgentToCapture(targetEntityGuid)) handle('trace-jserror', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee);
170
177
  // still send EE events for other features such as above, but stop this one from aggregating internal data
171
178
  if (this.blocked) return;
172
179
  if (err?.__newrelic?.[this.agentIdentifier]) {
173
180
  params._interactionId = err.__newrelic[this.agentIdentifier].interactionId;
174
181
  params._interactionNodeId = err.__newrelic[this.agentIdentifier].interactionNodeId;
175
182
  }
176
- const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav]);
177
- // 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.
178
- // They each will also tack on their respective properties to the params object as part of the decision flow.
179
- if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee);else handle('spa-jserror', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee);
180
- if (params.browserInteractionId && !params._softNavFinished) {
181
- // hold onto the error until the in-progress interaction is done, eithered saved or discarded
182
- this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= [];
183
- this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent);
184
- } else if (params._interactionId != null) {
185
- // same as above, except tailored for the way old spa does it
186
- this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || [];
187
- this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent);
188
- } else {
189
- // Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
190
- // The old spa does not look up completed interactions at all, so there's no need to consider it.
191
- this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes);
183
+ if (this.shouldAllowMainAgentToCapture(targetEntityGuid)) {
184
+ const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav]);
185
+ // 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.
186
+ // They each will also tack on their respective properties to the params object as part of the decision flow.
187
+ if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee);else handle('spa-jserror', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee);
188
+ if (params.browserInteractionId && !params._softNavFinished) {
189
+ // hold onto the error until the in-progress interaction is done, eithered saved or discarded
190
+ this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= [];
191
+ this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent);
192
+ } else if (params._interactionId != null) {
193
+ // same as above, except tailored for the way old spa does it
194
+ this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || [];
195
+ this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent);
196
+ } else {
197
+ // Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
198
+ // The old spa does not look up completed interactions at all, so there's no need to consider it.
199
+ this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes);
200
+ }
192
201
  }
202
+
203
+ // always add directly if scoped to a sub-entity, the other pathways above will be deterministic if the main agent should procede
204
+ if (targetEntityGuid) this.#storeJserrorForHarvest([...jsErrorEvent, targetEntityGuid], false, params._softNavAttributes);
193
205
  }
194
206
  #storeJserrorForHarvest(errorInfoArr, softNavOccurredFinished, softNavCustomAttrs = {}) {
195
- let [type, bucketHash, params, newMetrics, localAttrs] = errorInfoArr;
207
+ let [type, bucketHash, params, newMetrics, localAttrs, targetEntityGuid] = errorInfoArr;
196
208
  const allCustomAttrs = {};
197
209
  if (softNavOccurredFinished) {
198
210
  Object.entries(softNavCustomAttrs).forEach(([k, v]) => setCustom(k, v)); // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
@@ -208,12 +220,22 @@ export class Aggregate extends AggregateBase {
208
220
 
209
221
  const jsAttributesHash = stringHashCode(stringify(allCustomAttrs));
210
222
  const aggregateHash = bucketHash + ':' + jsAttributesHash;
211
- this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs]);
223
+ this.events.add([type, aggregateHash, params, newMetrics, allCustomAttrs], targetEntityGuid);
212
224
  function setCustom(key, val) {
213
225
  allCustomAttrs[key] = val && typeof val === 'object' ? stringify(val) : val;
214
226
  }
215
227
  }
216
228
 
229
+ /**
230
+ * 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
231
+ * the main agent should not capture events unless it is configured to do so.
232
+ * @param {string} entityGuid - the context object for the event
233
+ * @returns {boolean} - whether the main agent should capture the event to its internal target
234
+ */
235
+ shouldAllowMainAgentToCapture(entityGuid) {
236
+ return !entityGuid || this.agentRef.init.api.duplicate_registered_data;
237
+ }
238
+
217
239
  // TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
218
240
  onInteractionDone(interaction, wasSaved) {
219
241
  if (!this.bufferedErrorsUnderSpa[interaction.id] || this.blocked) return;
@@ -232,7 +254,7 @@ export class Aggregate extends AggregateBase {
232
254
  var hash = wasSaved ? item[1] + interaction.root.attrs.id : item[1];
233
255
  var jsAttributesHash = stringHashCode(stringify(allCustomAttrs));
234
256
  var aggregateHash = hash + ':' + jsAttributesHash;
235
- this.events.add([item[0], aggregateHash, params, item[3], allCustomAttrs]);
257
+ this.events.add([item[0], aggregateHash, params, item[3], allCustomAttrs], item[5]);
236
258
  function setCustom([key, val]) {
237
259
  allCustomAttrs[key] = val && typeof val === 'object' ? stringify(val) : val;
238
260
  }