@newrelic/browser-agent 1.319.0-rc.0 → 1.319.0-rc.2

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 (72) hide show
  1. package/dist/cjs/common/config/init.js +17 -2
  2. package/dist/cjs/common/config/runtime.js +2 -1
  3. package/dist/cjs/common/constants/env.cdn.js +1 -1
  4. package/dist/cjs/common/constants/env.npm.js +1 -1
  5. package/dist/cjs/common/constants/iframe-constants.js +16 -0
  6. package/dist/cjs/common/url/add-url.js +23 -0
  7. package/dist/cjs/common/util/console.js +40 -33
  8. package/dist/cjs/common/v2/mfe-vitals.js +6 -0
  9. package/dist/cjs/common/v2/script-tracker.js +33 -24
  10. package/dist/cjs/common/v2/timing-factory.js +18 -0
  11. package/dist/cjs/common/v2/utils.js +100 -6
  12. package/dist/cjs/features/ajax/instrument/index.js +4 -14
  13. package/dist/cjs/features/utils/instrument-base.js +13 -0
  14. package/dist/cjs/interfaces/registered-iframe-entity.js +527 -0
  15. package/dist/cjs/loaders/api/register.js +9 -5
  16. package/dist/cjs/loaders/configure/configure.js +14 -0
  17. package/dist/cjs/loaders/configure/iframe-message-handler.js +270 -0
  18. package/dist/esm/common/config/init.js +17 -2
  19. package/dist/esm/common/config/runtime.js +2 -1
  20. package/dist/esm/common/constants/env.cdn.js +1 -1
  21. package/dist/esm/common/constants/env.npm.js +1 -1
  22. package/dist/esm/common/constants/iframe-constants.js +10 -0
  23. package/dist/esm/common/url/add-url.js +16 -0
  24. package/dist/esm/common/util/console.js +40 -33
  25. package/dist/esm/common/v2/mfe-vitals.js +6 -0
  26. package/dist/esm/common/v2/script-tracker.js +33 -24
  27. package/dist/esm/common/v2/timing-factory.js +12 -0
  28. package/dist/esm/common/v2/utils.js +97 -6
  29. package/dist/esm/features/ajax/instrument/index.js +1 -11
  30. package/dist/esm/features/utils/instrument-base.js +13 -0
  31. package/dist/esm/interfaces/registered-iframe-entity.js +518 -0
  32. package/dist/esm/loaders/api/register.js +9 -5
  33. package/dist/esm/loaders/configure/configure.js +15 -1
  34. package/dist/esm/loaders/configure/iframe-message-handler.js +263 -0
  35. package/dist/tsconfig.tsbuildinfo +1 -1
  36. package/dist/types/common/config/init.d.ts.map +1 -1
  37. package/dist/types/common/config/runtime.d.ts.map +1 -1
  38. package/dist/types/common/constants/iframe-constants.d.ts +11 -0
  39. package/dist/types/common/constants/iframe-constants.d.ts.map +1 -0
  40. package/dist/types/common/url/add-url.d.ts +2 -0
  41. package/dist/types/common/url/add-url.d.ts.map +1 -0
  42. package/dist/types/common/util/console.d.ts +40 -33
  43. package/dist/types/common/util/console.d.ts.map +1 -1
  44. package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -1
  45. package/dist/types/common/v2/script-tracker.d.ts.map +1 -1
  46. package/dist/types/common/v2/timing-factory.d.ts +9 -0
  47. package/dist/types/common/v2/timing-factory.d.ts.map +1 -0
  48. package/dist/types/common/v2/utils.d.ts +37 -0
  49. package/dist/types/common/v2/utils.d.ts.map +1 -1
  50. package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
  51. package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
  52. package/dist/types/interfaces/registered-iframe-entity.d.ts +116 -0
  53. package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -0
  54. package/dist/types/loaders/configure/configure.d.ts.map +1 -1
  55. package/dist/types/loaders/configure/iframe-message-handler.d.ts +17 -0
  56. package/dist/types/loaders/configure/iframe-message-handler.d.ts.map +1 -0
  57. package/package.json +9 -1
  58. package/src/common/config/init.js +13 -2
  59. package/src/common/config/runtime.js +2 -1
  60. package/src/common/constants/iframe-constants.js +11 -0
  61. package/src/common/url/add-url.js +18 -0
  62. package/src/common/util/console.js +40 -33
  63. package/src/common/v2/mfe-vitals.js +4 -2
  64. package/src/common/v2/script-tracker.js +36 -21
  65. package/src/common/v2/timing-factory.js +10 -0
  66. package/src/common/v2/utils.js +102 -6
  67. package/src/features/ajax/instrument/index.js +1 -13
  68. package/src/features/utils/instrument-base.js +12 -0
  69. package/src/interfaces/registered-iframe-entity.js +482 -0
  70. package/src/loaders/api/register.js +8 -4
  71. package/src/loaders/configure/configure.js +15 -1
  72. package/src/loaders/configure/iframe-message-handler.js +244 -0
@@ -29,7 +29,9 @@ const InitModelFn = () => {
29
29
  feature_flags: [],
30
30
  experimental: {
31
31
  register: false,
32
- resources: false
32
+ resources: false,
33
+ iframe_bridge: false,
34
+ iframe_domains: []
33
35
  },
34
36
  mask_selector: '*',
35
37
  block_selector: '[data-nr-block]',
@@ -69,7 +71,20 @@ const InitModelFn = () => {
69
71
  set enabled(val) {
70
72
  hiddenState.experimental.register = val;
71
73
  },
72
- duplicate_data_to_container: false
74
+ duplicate_data_to_container: false,
75
+ // experimental iframe bridge feature
76
+ get allow_iframe_bridge() {
77
+ return hiddenState.feature_flags.includes(_constants.FEATURE_FLAGS.IFRAME_BRIDGE) || hiddenState.experimental.iframe_bridge;
78
+ },
79
+ set allow_iframe_bridge(val) {
80
+ hiddenState.experimental.iframe_bridge = val;
81
+ },
82
+ get iframe_domains() {
83
+ return hiddenState.experimental.iframe_domains;
84
+ },
85
+ set iframe_domains(val) {
86
+ if (Array.isArray(val)) hiddenState.experimental.iframe_domains = val;else (0, _console.warn)(1, val);
87
+ }
73
88
  }
74
89
  },
75
90
  browser_consent_mode: {
@@ -63,7 +63,8 @@ const RuntimeModel = {
63
63
  },
64
64
  get harvestCount() {
65
65
  return ++_harvestCount;
66
- }
66
+ },
67
+ listeningForIframeMessages: false
67
68
  };
68
69
  const mergeRuntime = runtime => {
69
70
  const modeledObject = (0, _configurable.getModeledObject)(runtime, RuntimeModel);
@@ -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.319.0-rc.0";
20
+ const VERSION = exports.VERSION = "1.319.0-rc.2";
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.319.0-rc.0";
20
+ const VERSION = exports.VERSION = "1.319.0-rc.2";
21
21
 
22
22
  /**
23
23
  * Exposes the build type of the agent
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.prefix = exports.IFRAME_VITALS_UPDATE = exports.IFRAME_TIMING_UPDATE = exports.IFRAME_API_RESPONSE = exports.IFRAME_API = exports.IFRAME_AJAX = void 0;
7
+ /**
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
+ * SPDX-License-Identifier: Apache-2.0
10
+ */
11
+ const prefix = exports.prefix = 'newrelic-iframe-';
12
+ const IFRAME_TIMING_UPDATE = exports.IFRAME_TIMING_UPDATE = prefix + 'timing-update';
13
+ const IFRAME_API = exports.IFRAME_API = prefix + 'api';
14
+ const IFRAME_API_RESPONSE = exports.IFRAME_API_RESPONSE = prefix + 'api-response';
15
+ const IFRAME_VITALS_UPDATE = exports.IFRAME_VITALS_UPDATE = prefix + 'vitals-update';
16
+ const IFRAME_AJAX = exports.IFRAME_AJAX = prefix + 'ajax';
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addUrl = addUrl;
7
+ var _parseUrl = require("./parse-url.js");
8
+ /**
9
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
10
+ * SPDX-License-Identifier: Apache-2.0
11
+ */
12
+
13
+ function addUrl(ctx, url) {
14
+ var parsed = (0, _parseUrl.parseUrl)(url);
15
+ var params = ctx.params || ctx;
16
+ params.hostname = parsed.hostname;
17
+ params.port = parsed.port;
18
+ params.protocol = parsed.protocol;
19
+ params.host = parsed.hostname + ':' + parsed.port;
20
+ params.pathname = parsed.pathname;
21
+ ctx.parsedOrigin = parsed;
22
+ ctx.sameOrigin = parsed.sameOrigin;
23
+ }
@@ -21,39 +21,39 @@ var _globalEvent = require("../dispatch/global-event");
21
21
  *
22
22
  * | Code | Message |
23
23
  * |------|---------|
24
- * | 1 | An error occurred while setting a property of a Configurable |
25
- * | 2 | An error occurred while setting a Configurable |
26
- * | 3 | Setting a Configurable requires an object as input |
27
- * | 4 | Setting a Configurable requires a model to set its initial properties |
24
+ * | 1 | An error occurred while setting a property of a Configurable. |
25
+ * | 2 | An error occurred while setting a Configurable. |
26
+ * | 3 | Setting a Configurable requires an object as input. |
27
+ * | 4 | Setting a Configurable requires a model to set its initial properties. |
28
28
  * | 5 | An invalid session_replay.mask_selector was provided. \* will be used. |
29
- * | 6 | An invalid session_replay.block_selector was provided and will not be used |
30
- * | 7 | An invalid session_replay.mask_input_option was provided and will not be used |
31
- * | 8 | Shared context requires an object as input |
32
- * | 9 | An error occurred while setting SharedContext |
33
- * | 10 | Failed to read from storage API |
34
- * | 11 | Failed to write to the storage API |
29
+ * | 6 | An invalid session_replay.block_selector was provided and will not be used. |
30
+ * | 7 | An invalid session_replay.mask_input_option was provided and will not be used. |
31
+ * | 8 | Shared context requires an object as input. |
32
+ * | 9 | An error occurred while setting SharedContext. |
33
+ * | 10 | Failed to read from storage API. |
34
+ * | 11 | Failed to write to the storage API. |
35
35
  * | 12 | An obfuscation replacement rule was detected missing a "regex" value. |
36
- * | 13 | An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp) |
37
- * | 14 | An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string) |
38
- * | 15 | An error occurred while intercepting XHR |
39
- * | 16 | Could not cast log message to string |
36
+ * | 13 | An obfuscation replacement rule contains a "regex" value with an invalid type (must be a string or RegExp). |
37
+ * | 14 | An obfuscation replacement rule contains a "replacement" value with an invalid type (must be a string). |
38
+ * | 15 | An error occurred while intercepting XHR. |
39
+ * | 16 | Could not cast log message to string. |
40
40
  * | 17 | Could not calculate New Relic server time. Agent shutting down. |
41
41
  * | 18 | RUM call failed. Agent shutting down. |
42
42
  * | 19 | SPA scheduler is not initialized. Saved interaction is not sent! |
43
43
  * | 20 | A problem occurred when starting up session manager. This page will not start or extend any session. |
44
44
  * | 21 | Failed to initialize the agent. Could not determine the runtime environment. |
45
45
  * | 22 | Failed to initialize all enabled instrument classes (agent aborted) - |
46
- * | 23 | An unexpected issue occurred |
46
+ * | 23 | An unexpected issue occurred. |
47
47
  * | 24 | Something prevented the agent from instrumenting. |
48
48
  * | 25 | Something prevented the agent from being downloaded. |
49
49
  * | 26 | Failed to initialize instrument classes. |
50
50
  * | 27 | Downloading runtime APIs failed... |
51
51
  * | 28 | The Browser Agent is attempting to send a very large payload. This is usually tied to large amounts of custom attributes. Please check your configurations. |
52
- * | 29 | Failed to wrap logger: invalid argument(s) |
53
- * | 30 | Invalid log level |
54
- * | 31 | Ignored log: Log is larger than maximum payload size |
55
- * | 32 | Ignored log: Invalid message |
56
- * | 33 | Session Replay Aborted |
52
+ * | 29 | Failed to wrap logger: invalid argument(s). |
53
+ * | 30 | Invalid log level. |
54
+ * | 31 | Ignored log: Log is larger than maximum payload size. |
55
+ * | 32 | Ignored log: Invalid message. |
56
+ * | 33 | Session Replay Aborted. |
57
57
  * | 34 | Downloading and initializing a feature failed... |
58
58
  * | 35 | Call to agent api failed. The API is not currently initialized. |
59
59
  * | 36 | A feature is enabled but one or more dependent features have not been initialized. This may cause unintended consequences or missing data... |
@@ -62,7 +62,7 @@ var _globalEvent = require("../dispatch/global-event");
62
62
  * | 39 | Failed to execute setCustomAttribute. Name must be a string type. |
63
63
  * | 40 | Failed to execute setCustomAttribute. Non-null value must be a string, number or boolean type. |
64
64
  * | 41 | Failed to execute setUserId. Non-null value must be a string type. |
65
- * | 42 | Failed to execute setApplicationVersion. Expected <String \| null> |
65
+ * | 42 | Failed to execute setApplicationVersion. Expected <String \| null>. |
66
66
  * | 43 | Agent not configured properly. |
67
67
  * | 44 | Invalid object passed to generic event aggregate. Missing "eventType". |
68
68
  * | 45 | An internal agent process failed to execute. |
@@ -71,27 +71,34 @@ var _globalEvent = require("../dispatch/global-event");
71
71
  * | 48 | Supplied an invalid API target. Must be an <Object> that contains valid (string) id and name properties. |
72
72
  * | 49 | Supplied API target is missing an entityGuid. Some APIs may not behave correctly without a valid entityGuid (ex. logs). |
73
73
  * | 50 | Failed to connect. Cannot allow registered API. |
74
- * | 51 | Container agent is not available to register with. Can not connect |
74
+ * | 51 | Container agent is not available to register with. Can not connect. |
75
75
  * | 52 | Unexpected problem encountered. There should be at least one app for harvest! |
76
- * | 53 | Did not receive a valid entityGuid from connection response |
77
- * | 54 | An experimental feature is being used. Support can not be offered for issues |
78
- * | 55 | Register API has been disabled on the container agent |
79
- * | 56 | Could not find a matching entity to store data |
76
+ * | 53 | Did not receive a valid entityGuid from connection response. |
77
+ * | 54 | An experimental feature is being used. Support can not be offered for issues. |
78
+ * | 55 | Register API has been disabled on the container agent. |
79
+ * | 56 | Could not find a matching entity to store data. |
80
80
  * | 57 | Failed to execute measure. Arguments must have valid types. |
81
81
  * | 58 | Failed to execute measure. Resulting duration must be non-negative. |
82
- * | 59 | Session replay harvested before a session trace payload could be sent. This could be problematic for replays that rely on a trace |
83
- * | 60 | Session trace aborted |
82
+ * | 59 | Session replay harvested before a session trace payload could be sent. This could be problematic for replays that rely on a trace. |
83
+ * | 60 | Session trace aborted. |
84
84
  * | 61 | Timestamps must be non-negative and end time cannot be before start time. |
85
- * | 62 | Timestamp must be a unix timestamp greater than the page origin time |
86
- * | 63 | A single event was larger than the maximum allowed payload size |
85
+ * | 62 | Timestamp must be a unix timestamp greater than the page origin time. |
86
+ * | 63 | A single event was larger than the maximum allowed payload size. |
87
87
  * | 64 | Required globals have been mutated before being accessed by the browser agent. This can cause issues and should be avoided. |
88
- * | 65 | Consent API argument must be boolean or undefined |
89
- * | 66 | A new agent session has started |
88
+ * | 65 | Consent API argument must be boolean or undefined. |
89
+ * | 66 | A new agent session has started. |
90
90
  * | 67 | The "spa" feature has been deprecated and disabled. Please use/import "soft_navigations" instead for tracking of BrowserInteraction data. |
91
91
  * | 68 | API has been deregistered and can no longer be used. Call "register" API again with credentials to start over. |
92
- * | 69 | More than one Browser agent is running on the page |
92
+ * | 69 | More than one Browser agent is running on the page. |
93
93
  * | 70 | A session replay payload failed to send and is being retried. Recording is paused during the retry period, and will resume when a successful harvest is made. Some replay activity may be missed during retry phases. |
94
94
  * | 71 | An invalid feature mode was detected and set to "off". |
95
+ * | 72 | RegisteredIframeEntity failed to transmit API data from an iframe to window context. |
96
+ * | 73 | RegisteredIframeEntity failed to register with window context. |
97
+ * | 74 | RegisteredIframeEntity rejected message from unauthorized origin. |
98
+ * | 75 | RegisteredIframeEntity rejected message with mismatched iframeInterfaceId. |
99
+ * | 76 | Agent rejected post message, could not match with existing entity. |
100
+ * | 77 | Agent rejected post message, could not validate origin. |
101
+ * | 78 | RegisteredIframeEntity could not determine parent origin and will not register, to avoid trusting messages from any origin. |
95
102
  *
96
103
  * @param {number} code The warning code to emit, which will be used to link to the warning code documentation
97
104
  * @param {*} [secondary] Secondary data to include, usually an extra message, error or object
@@ -139,11 +139,17 @@ function trackMFEVitals(target, timings) {
139
139
  fcp: {
140
140
  get value() {
141
141
  return getTimeRelativeToScriptStart(fcpObservedAt);
142
+ },
143
+ set value(v) {
144
+ fcpObservedAt = v;
142
145
  }
143
146
  },
144
147
  lcp: {
145
148
  get value() {
146
149
  return getTimeRelativeToScriptStart(lcpObservedAt);
150
+ },
151
+ set value(v) {
152
+ lcpObservedAt = v;
147
153
  }
148
154
  },
149
155
  cls: {
@@ -12,6 +12,7 @@ var _now = require("../timing/now");
12
12
  var _cleanUrl = require("../url/clean-url");
13
13
  var _browserStackMatchers = require("../util/browser-stack-matchers");
14
14
  var _scriptCorrelation = require("./script-correlation");
15
+ var _timingFactory = require("./timing-factory");
15
16
  /**
16
17
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
17
18
  * SPDX-License-Identifier: Apache-2.0
@@ -30,7 +31,7 @@ try {
30
31
  }
31
32
 
32
33
  /** @type {(entry: PerformanceEntry) => boolean} - A shared function to determine if a performance entry is a valid script or link resource for evaluation */
33
- const validEntryCriteria = entry => entry.initiatorType === 'script' || ['link', 'fetch'].includes(entry.initiatorType) && entry.name.endsWith('.js');
34
+ const validEntryCriteria = entry => entry.initiatorType === 'script' || ['link', 'fetch'].includes(entry.initiatorType) && (0, _cleanUrl.cleanURL)(entry.name).endsWith('.js');
34
35
 
35
36
  /** @type {Map<string, ScriptCorrelation>} - Central registry for script correlations containing both DOM and Performance data */
36
37
  const scriptCorrelations = exports.scriptCorrelations = new Map();
@@ -205,6 +206,25 @@ function applyPerformanceEntry(timings, entry) {
205
206
  timings.type = entry.initiatorType;
206
207
  }
207
208
 
209
+ /**
210
+ * Subscribes to late resource timing emissions for a script URL.
211
+ * @param {RegisterAPITimings} timings - The timings object to update
212
+ * @param {string} mfeScriptUrl - The script URL to match
213
+ */
214
+ function subscribeToLatePerformanceEntry(timings, mfeScriptUrl) {
215
+ if (!_runtime.globalScope.PerformanceObserver?.supportedEntryTypes?.includes('resource')) return;
216
+ poSubscribers.push({
217
+ addedAt: (0, _now.now)(),
218
+ test: entry => {
219
+ if (entryMatchesUrl(entry, mfeScriptUrl)) {
220
+ applyPerformanceEntry(timings, entry);
221
+ return true;
222
+ }
223
+ return false;
224
+ }
225
+ });
226
+ }
227
+
208
228
  /**
209
229
  * Uses the stack of the initiator function, returns script timing information if a script can be found with the resource timing API matching the URL found in the stack.
210
230
  * @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
@@ -239,38 +259,27 @@ function findScriptTimings() {
239
259
  // Get correlation data
240
260
  timings.correlation = findCorrelation(mfeScriptUrl);
241
261
 
242
- // Use correlation's performance entry if available, otherwise check live performance API
243
- const performanceEntry = timings.correlation?.performance.value || performance.getEntriesByType('resource').find(e => entryMatchesUrl(e, mfeScriptUrl));
262
+ // Use correlation's performance entry if available, otherwise check the live performance API before falling back to the buffered observer.
263
+ const performanceEntry = timings.correlation?.performance.value || _runtime.globalScope.performance?.getEntriesByType('resource')?.find(e => entryMatchesUrl(e, mfeScriptUrl));
244
264
  if (performanceEntry) {
245
265
  applyPerformanceEntry(timings, performanceEntry);
246
- } else if (wasPreloaded(mfeScriptUrl)) {
247
- // Handle preloaded scripts that may report late
248
- timings.asset = mfeScriptUrl;
249
- timings.type = 'preload';
266
+ } else {
267
+ const isPreloaded = wasPreloaded(mfeScriptUrl);
250
268
 
251
- // Subscribe to late performance observer callbacks
252
- poSubscribers.push({
253
- addedAt: (0, _now.now)(),
254
- test: entry => {
255
- if (entryMatchesUrl(entry, mfeScriptUrl)) {
256
- applyPerformanceEntry(timings, entry);
257
- return true;
258
- }
259
- return false;
260
- }
261
- });
269
+ // Handle preloaded scripts and any late resource emissions through the shared buffered observer.
270
+ if (isPreloaded) {
271
+ timings.asset = mfeScriptUrl;
272
+ timings.type = 'preload';
273
+ }
274
+ subscribeToLatePerformanceEntry(timings, mfeScriptUrl);
262
275
  }
263
276
 
264
277
  /*
265
278
  * Use getters here because the correlation data may arrive after this function returns the timing object, and we want to provide the most up-to-date timing information possible when the getters are accessed at harvest time.
266
279
  * The getters will fall back to fetchEnd if correlation data isn't available yet, which is our best approximation for script execution start when actual script timings can not be determined.
267
280
  */
268
- Object.defineProperty(timings, 'scriptStart', {
269
- get: () => timings.correlation?.script.start || timings.fetchEnd
270
- });
271
- Object.defineProperty(timings, 'scriptEnd', {
272
- get: () => timings.correlation?.script.end || timings.registeredAt
273
- });
281
+ Object.defineProperty(timings, 'scriptStart', (0, _timingFactory.timingFactory)(() => timings.correlation?.script.start || timings.fetchEnd));
282
+ Object.defineProperty(timings, 'scriptEnd', (0, _timingFactory.timingFactory)(() => timings.correlation?.script.end || timings.registeredAt));
274
283
  } catch (error) {
275
284
  // Don't let stack parsing errors break anything
276
285
  }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.timingFactory = timingFactory;
7
+ /**
8
+ * Copyright 2020-2026 New Relic, Inc. All rights reserved.
9
+ * SPDX-License-Identifier: Apache-2.0
10
+ */
11
+ function timingFactory(getLazyValue = () => {}, override) {
12
+ return {
13
+ get: () => override !== undefined ? override : getLazyValue(),
14
+ set: newValue => {
15
+ override = newValue;
16
+ }
17
+ };
18
+ }
@@ -4,7 +4,10 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.V2_TYPES = void 0;
7
+ exports.dedupeRegisteredEntitiesByAsset = dedupeRegisteredEntitiesByAsset;
8
+ exports.dedupeTargetsByInstance = dedupeTargetsByInstance;
7
9
  exports.findTargetsFromStackTrace = findTargetsFromStackTrace;
10
+ exports.getRegisteredEntityByIframeInterfaceId = getRegisteredEntityByIframeInterfaceId;
8
11
  exports.getRegisteredTargetsFromFilename = getRegisteredTargetsFromFilename;
9
12
  exports.getRegisteredTargetsFromId = getRegisteredTargetsFromId;
10
13
  exports.getVersion2Attributes = getVersion2Attributes;
@@ -27,6 +30,18 @@ const V2_TYPES = exports.V2_TYPES = {
27
30
  BA: 'BA'
28
31
  };
29
32
 
33
+ /**
34
+ * Returns a single registered entity associated with a given iframe interface ID. Returns undefined if no entity is found.
35
+ * @param {string} iframeInterfaceId
36
+ * @param {*} agentRef the agent reference
37
+ * @returns {import("../../loaders/api/register-api-types").RegisterAPI|undefined}
38
+ */
39
+ function getRegisteredEntityByIframeInterfaceId(iframeInterfaceId, agentRef) {
40
+ if (!isValid(iframeInterfaceId, agentRef)) return undefined;
41
+ const registeredEntities = agentRef.runtime.registeredEntities;
42
+ return registeredEntities?.find(entity => entity.metadata.target.iframeInterfaceId === iframeInterfaceId);
43
+ }
44
+
30
45
  /**
31
46
  * Returns the registered target associated with a given ID. Returns an empty array if no target is found.
32
47
  * @param {string|number} id
@@ -34,21 +49,69 @@ const V2_TYPES = exports.V2_TYPES = {
34
49
  * @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
35
50
  */
36
51
  function getRegisteredTargetsFromId(id, agentRef) {
37
- if (!id || !agentRef?.init.api.register.enabled) return [];
52
+ if (!isValid(id, agentRef)) return [];
38
53
  const registeredEntities = agentRef.runtime.registeredEntities;
39
54
  return registeredEntities?.filter(entity => String(entity.metadata.target.id) === String(id)).map(entity => entity.metadata.target) || [];
40
55
  }
41
56
 
42
57
  /**
43
58
  * Returns the registered target(s) associated with a given filename if found in the resource timing API during registration. Returns an empty array if no target is found.
59
+ * Multiple registrations that resolve to the same underlying script asset AND represent the same logical MFE (i.e.
60
+ * share the same customer-supplied `target.id`) are collapsed to a single target via
61
+ * {@link dedupeRegisteredEntitiesByAsset}, since auto-instrumented events (AJAX, JS errors, logs, WebSockets) should
62
+ * only be reported once per real occurrence when the same MFE was registered many times over -- not once per
63
+ * duplicate registration. Distinct MFEs (different `target.id`) that happen to share a script (e.g. two different
64
+ * MFEs both registered from the same inline `<script>` block) are intentionally NOT collapsed, since each is a
65
+ * genuinely different entity that should still receive its own copy of the matched event.
44
66
  * @param {string} filename
45
67
  * @param {*} agentRef
46
68
  * @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
47
69
  */
48
70
  function getRegisteredTargetsFromFilename(filename, agentRef) {
49
- if (!filename || !agentRef?.init.api.register.enabled) return [];
71
+ if (!isValid(filename, agentRef)) return [];
50
72
  const registeredEntities = agentRef.runtime.registeredEntities;
51
- return registeredEntities?.filter(entity => entity.metadata.timings?.asset?.endsWith(filename)).map(entity => entity.metadata.target) || [];
73
+ const matches = registeredEntities?.filter(entity => entity.metadata.timings?.asset?.endsWith(filename));
74
+ return dedupeRegisteredEntitiesByAsset(matches).map(entity => entity.metadata.target);
75
+ }
76
+
77
+ /**
78
+ * Collapses a list of registered entities down to one canonical entity per unique, defined `metadata.timings.asset` +
79
+ * `target.id` combination. Entities whose asset could not be resolved (undefined -- e.g. inline scripts or scripts
80
+ * not found in the resource timing buffer) are never deduped against each other or against resolved entities, since
81
+ * there is no signal that they represent the same underlying script. Entities that share an asset but have different
82
+ * `target.id`s are never deduped against each other either, since a differing id means the customer registered
83
+ * genuinely distinct MFEs (not duplicate registrations of the same one).
84
+ *
85
+ * Canonical selection per asset+id: prefer an entity whose target has not been deregistered (`target.blocked ===
86
+ * false`) over one that has; otherwise the first-encountered entity wins, for determinism.
87
+ * @param {Array} entities registered entities (each with `metadata.timings.asset` and `metadata.target`)
88
+ * @returns {Array} deduped list of entities, preserving relative order of first occurrence
89
+ */
90
+ function dedupeRegisteredEntitiesByAsset(entities) {
91
+ if (!entities?.length) return entities || [];
92
+ const byKey = new Map(); // `${asset}::${id}` -> canonical entity
93
+ const result = [];
94
+ for (const entity of entities) {
95
+ const asset = entity.metadata?.timings?.asset;
96
+ if (!asset) {
97
+ // can't safely dedupe unresolved-asset entities -- always keep as-is
98
+ result.push(entity);
99
+ continue;
100
+ }
101
+ const key = "".concat(asset, "::").concat(entity.metadata?.target?.id);
102
+ const existing = byKey.get(key);
103
+ if (!existing) {
104
+ byKey.set(key, entity);
105
+ result.push(entity);
106
+ } else if (existing.metadata.target?.blocked && !entity.metadata.target?.blocked) {
107
+ // swap in a non-deregistered target as the canonical one for this asset+id
108
+ const idx = result.indexOf(existing);
109
+ if (idx !== -1) result[idx] = entity;
110
+ byKey.set(key, entity);
111
+ }
112
+ // else: existing canonical entity wins (already non-blocked, or both blocked -- first wins); drop this duplicate
113
+ }
114
+ return result;
52
115
  }
53
116
 
54
117
  /**
@@ -107,7 +170,7 @@ function shouldDuplicate(target, aggregateInstance) {
107
170
  * @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array with undefined.
108
171
  */
109
172
  function findTargetsFromStackTrace(agentRef) {
110
- if (!agentRef?.init.api.register.enabled || !agentRef?.runtime?.registeredEntities?.length) return [undefined];
173
+ if (!isValid(true, agentRef) || !agentRef?.runtime?.registeredEntities?.length) return [undefined];
111
174
  const targets = [];
112
175
  try {
113
176
  var urls = (0, _scriptTracker.extractUrlsFromStack)((0, _scriptTracker.getDeepStackTrace)());
@@ -118,8 +181,29 @@ function findTargetsFromStackTrace(agentRef) {
118
181
  } catch (err) {
119
182
  // Silent catch to prevent errors from propagating
120
183
  }
121
- if (!targets.length) targets.push(undefined); // if we can't find any targets from the stack trace, return an array with undefined to signify the container agent is the target
122
- return targets;
184
+ const deduped = dedupeTargetsByInstance(targets);
185
+ if (!deduped.length) deduped.push(undefined); // if we can't find any targets from the stack trace, return an array with undefined to signify the container agent is the target
186
+ return deduped;
187
+ }
188
+
189
+ /**
190
+ * Removes duplicate targets from an array, keyed by `target.instance`. This guards against the same canonical
191
+ * target re-entering the array via multiple matched stack-frame URLs (e.g. a recursive call whose stack contains the
192
+ * same file at multiple depths). Entries with no `instance` (i.e. `undefined`, meaning "the container agent")
193
+ * naturally collapse to a single entry too, which is the desired behavior.
194
+ * @param {Array} targets
195
+ * @returns {Array} deduped list of targets, preserving relative order of first occurrence
196
+ */
197
+ function dedupeTargetsByInstance(targets) {
198
+ const seen = new Set();
199
+ const result = [];
200
+ for (const target of targets) {
201
+ const key = target?.instance;
202
+ if (seen.has(key)) continue;
203
+ seen.add(key);
204
+ result.push(target);
205
+ }
206
+ return result;
123
207
  }
124
208
 
125
209
  /**
@@ -131,4 +215,14 @@ function findTargetsFromStackTrace(agentRef) {
131
215
  */
132
216
  function supportsV2(aggregateInstance) {
133
217
  return aggregateInstance?.harvestEndpointVersion === 2;
218
+ }
219
+
220
+ /**
221
+ * Determines if the given identifier and agent reference are valid for use for entity lookups and other operations in the utils methods. This is a common check that is used across multiple methods in this module to ensure that the necessary data is present and that the register API is enabled before attempting to perform operations that depend on those things.
222
+ * @param {*} identifier The identifier to check.
223
+ * @param {*} agentRef The agent reference to check.
224
+ * @returns {boolean} Returns true if the identifier and agent reference are valid, false otherwise.
225
+ */
226
+ function isValid(identifier, agentRef) {
227
+ return !!identifier && !!agentRef?.init.api.register.enabled;
134
228
  }
@@ -22,6 +22,7 @@ var _constants2 = require("../../metrics/constants");
22
22
  var _now = require("../../../common/timing/now");
23
23
  var _denyList = require("../../../common/deny-list/deny-list");
24
24
  var _extractUrl = require("../../../common/url/extract-url");
25
+ var _addUrl = require("../../../common/url/add-url");
25
26
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /**
26
27
  * Copyright 2020-2026 New Relic, Inc. All rights reserved.
27
28
  * SPDX-License-Identifier: Apache-2.0
@@ -63,7 +64,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
63
64
  duration: Math.floor(resource.duration),
64
65
  cbTime: 0
65
66
  };
66
- addUrl(params, resource.name);
67
+ (0, _addUrl.addUrl)(params, resource.name);
67
68
  this.handler('xhr', [params, metrics, resource.startTime, resource.responseEnd, initiators[resource.initiatorType]], undefined, _features.FEATURE_NAMES.ajax);
68
69
  }
69
70
  });
@@ -129,7 +130,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
129
130
  this.params = {
130
131
  method: args[0]
131
132
  };
132
- addUrl(this, args[1]);
133
+ (0, _addUrl.addUrl)(this, args[1]);
133
134
  this.metrics = {};
134
135
  }
135
136
  function onOpenXhrEnd(args, xhr) {
@@ -305,7 +306,7 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
305
306
  this.startTime = (0, _now.now)();
306
307
  this.dt = dtPayload;
307
308
  let [target, opts = {}] = fetchArguments;
308
- addUrl(this, (0, _extractUrl.extractUrl)(target));
309
+ (0, _addUrl.addUrl)(this, (0, _extractUrl.extractUrl)(target));
309
310
  const method = ('' + (target && target instanceof origRequest && target.method || opts.method || 'GET')).toUpperCase();
310
311
  this.params.method = method;
311
312
  this.txSize = (0, _dataSize.dataSize)(opts.body || target?.body) || 0;
@@ -441,17 +442,6 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
441
442
  ctx.loadCaptureCalled = true;
442
443
  }
443
444
  }
444
- function addUrl(ctx, url) {
445
- var parsed = (0, _parseUrl.parseUrl)(url);
446
- var params = ctx.params || ctx;
447
- params.hostname = parsed.hostname;
448
- params.port = parsed.port;
449
- params.protocol = parsed.protocol;
450
- params.host = parsed.hostname + ':' + parsed.port;
451
- params.pathname = parsed.pathname;
452
- ctx.parsedOrigin = parsed;
453
- ctx.sameOrigin = parsed.sameOrigin;
454
- }
455
445
  function parseResponseHeaders(headerStr) {
456
446
  const headers = {};
457
447
  if (!headerStr) return headers;
@@ -116,6 +116,19 @@ class InstrumentBase extends _featureBase.FeatureBase {
116
116
  this.ee.emit('internal-error', [e]);
117
117
  (0, _handle.handle)(_agentConstants.SESSION_ERROR, [e], undefined, this.featureName, this.ee);
118
118
  }
119
+ if (agentRef.init.api.register.allow_iframe_bridge) {
120
+ try {
121
+ // This chunk doesn't exist in the lite build (see webpack IgnorePlugin config) since none
122
+ // of lite's features wire up agent.register -- guard against that rather than letting an
123
+ // unhandled rejection surface if this flag is ever set on a lite page.
124
+ const {
125
+ setupIframeMFEMessageListener
126
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require(/* webpackChunkName: "iframe-message-handler" */'../../loaders/configure/iframe-message-handler')));
127
+ setupIframeMFEMessageListener(agentRef);
128
+ } catch (e) {
129
+ (0, _console.warn)(23, e);
130
+ }
131
+ }
119
132
 
120
133
  /**
121
134
  * Note this try-catch differs from the one in Agent.run() in that it's placed later in a page's lifecycle and