@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.
- package/dist/cjs/common/config/init.js +17 -2
- package/dist/cjs/common/config/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/constants/iframe-constants.js +16 -0
- package/dist/cjs/common/url/add-url.js +23 -0
- package/dist/cjs/common/util/console.js +40 -33
- package/dist/cjs/common/v2/mfe-vitals.js +6 -0
- package/dist/cjs/common/v2/script-tracker.js +33 -24
- package/dist/cjs/common/v2/timing-factory.js +18 -0
- package/dist/cjs/common/v2/utils.js +100 -6
- package/dist/cjs/features/ajax/instrument/index.js +4 -14
- package/dist/cjs/features/utils/instrument-base.js +13 -0
- package/dist/cjs/interfaces/registered-iframe-entity.js +527 -0
- package/dist/cjs/loaders/api/register.js +9 -5
- package/dist/cjs/loaders/configure/configure.js +14 -0
- package/dist/cjs/loaders/configure/iframe-message-handler.js +270 -0
- package/dist/esm/common/config/init.js +17 -2
- package/dist/esm/common/config/runtime.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/constants/iframe-constants.js +10 -0
- package/dist/esm/common/url/add-url.js +16 -0
- package/dist/esm/common/util/console.js +40 -33
- package/dist/esm/common/v2/mfe-vitals.js +6 -0
- package/dist/esm/common/v2/script-tracker.js +33 -24
- package/dist/esm/common/v2/timing-factory.js +12 -0
- package/dist/esm/common/v2/utils.js +97 -6
- package/dist/esm/features/ajax/instrument/index.js +1 -11
- package/dist/esm/features/utils/instrument-base.js +13 -0
- package/dist/esm/interfaces/registered-iframe-entity.js +518 -0
- package/dist/esm/loaders/api/register.js +9 -5
- package/dist/esm/loaders/configure/configure.js +15 -1
- package/dist/esm/loaders/configure/iframe-message-handler.js +263 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/common/config/init.d.ts.map +1 -1
- package/dist/types/common/config/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/iframe-constants.d.ts +11 -0
- package/dist/types/common/constants/iframe-constants.d.ts.map +1 -0
- package/dist/types/common/url/add-url.d.ts +2 -0
- package/dist/types/common/url/add-url.d.ts.map +1 -0
- package/dist/types/common/util/console.d.ts +40 -33
- package/dist/types/common/util/console.d.ts.map +1 -1
- package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -1
- package/dist/types/common/v2/script-tracker.d.ts.map +1 -1
- package/dist/types/common/v2/timing-factory.d.ts +9 -0
- package/dist/types/common/v2/timing-factory.d.ts.map +1 -0
- package/dist/types/common/v2/utils.d.ts +37 -0
- package/dist/types/common/v2/utils.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/interfaces/registered-iframe-entity.d.ts +116 -0
- package/dist/types/interfaces/registered-iframe-entity.d.ts.map +1 -0
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/configure/iframe-message-handler.d.ts +17 -0
- package/dist/types/loaders/configure/iframe-message-handler.d.ts.map +1 -0
- package/package.json +9 -1
- package/src/common/config/init.js +13 -2
- package/src/common/config/runtime.js +2 -1
- package/src/common/constants/iframe-constants.js +11 -0
- package/src/common/url/add-url.js +18 -0
- package/src/common/util/console.js +40 -33
- package/src/common/v2/mfe-vitals.js +4 -2
- package/src/common/v2/script-tracker.js +36 -21
- package/src/common/v2/timing-factory.js +10 -0
- package/src/common/v2/utils.js +102 -6
- package/src/features/ajax/instrument/index.js +1 -13
- package/src/features/utils/instrument-base.js +12 -0
- package/src/interfaces/registered-iframe-entity.js +482 -0
- package/src/loaders/api/register.js +8 -4
- package/src/loaders/configure/configure.js +15 -1
- 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: {
|
|
@@ -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.
|
|
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.
|
|
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
|
|
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
|
|
247
|
-
|
|
248
|
-
timings.asset = mfeScriptUrl;
|
|
249
|
-
timings.type = 'preload';
|
|
266
|
+
} else {
|
|
267
|
+
const isPreloaded = wasPreloaded(mfeScriptUrl);
|
|
250
268
|
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
71
|
+
if (!isValid(filename, agentRef)) return [];
|
|
50
72
|
const registeredEntities = agentRef.runtime.registeredEntities;
|
|
51
|
-
|
|
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
|
|
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
|
-
|
|
122
|
-
return
|
|
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
|