@newrelic/browser-agent 1.319.0 → 1.320.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.
- package/CHANGELOG.md +13 -0
- 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 +9 -2
- package/dist/cjs/common/v2/script-correlation.js +2 -0
- package/dist/cjs/common/v2/script-tracker-constants.js +13 -0
- package/dist/cjs/common/v2/script-tracker.js +54 -29
- 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 +10 -6
- 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 +9 -2
- package/dist/esm/common/v2/script-correlation.js +2 -0
- package/dist/esm/common/v2/script-tracker-constants.js +7 -0
- package/dist/esm/common/v2/script-tracker.js +54 -29
- 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 +10 -6
- 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-correlation.d.ts +2 -0
- package/dist/types/common/v2/script-correlation.d.ts.map +1 -1
- package/dist/types/common/v2/script-tracker-constants.d.ts +7 -0
- package/dist/types/common/v2/script-tracker-constants.d.ts.map +1 -0
- package/dist/types/common/v2/script-tracker.d.ts +4 -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 +7 -4
- package/src/common/v2/script-correlation.js +2 -0
- package/src/common/v2/script-tracker-constants.js +7 -0
- package/src/common/v2/script-tracker.js +65 -26
- 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 +9 -5
- package/src/loaders/configure/configure.js +15 -1
- package/src/loaders/configure/iframe-message-handler.js +244 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
export function timingFactory(getLazyValue = () => {}, override) {
|
|
6
|
+
return {
|
|
7
|
+
get: () => override !== undefined ? override : getLazyValue(),
|
|
8
|
+
set: newValue => {
|
|
9
|
+
override = newValue;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -16,6 +16,18 @@ export const V2_TYPES = {
|
|
|
16
16
|
BA: 'BA'
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Returns a single registered entity associated with a given iframe interface ID. Returns undefined if no entity is found.
|
|
21
|
+
* @param {string} iframeInterfaceId
|
|
22
|
+
* @param {*} agentRef the agent reference
|
|
23
|
+
* @returns {import("../../loaders/api/register-api-types").RegisterAPI|undefined}
|
|
24
|
+
*/
|
|
25
|
+
export function getRegisteredEntityByIframeInterfaceId(iframeInterfaceId, agentRef) {
|
|
26
|
+
if (!isValid(iframeInterfaceId, agentRef)) return undefined;
|
|
27
|
+
const registeredEntities = agentRef.runtime.registeredEntities;
|
|
28
|
+
return registeredEntities?.find(entity => entity.metadata.target.iframeInterfaceId === iframeInterfaceId);
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
/**
|
|
20
32
|
* Returns the registered target associated with a given ID. Returns an empty array if no target is found.
|
|
21
33
|
* @param {string|number} id
|
|
@@ -23,21 +35,69 @@ export const V2_TYPES = {
|
|
|
23
35
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
24
36
|
*/
|
|
25
37
|
export function getRegisteredTargetsFromId(id, agentRef) {
|
|
26
|
-
if (!id
|
|
38
|
+
if (!isValid(id, agentRef)) return [];
|
|
27
39
|
const registeredEntities = agentRef.runtime.registeredEntities;
|
|
28
40
|
return registeredEntities?.filter(entity => String(entity.metadata.target.id) === String(id)).map(entity => entity.metadata.target) || [];
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
/**
|
|
32
44
|
* 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.
|
|
45
|
+
* Multiple registrations that resolve to the same underlying script asset AND represent the same logical MFE (i.e.
|
|
46
|
+
* share the same customer-supplied `target.id`) are collapsed to a single target via
|
|
47
|
+
* {@link dedupeRegisteredEntitiesByAsset}, since auto-instrumented events (AJAX, JS errors, logs, WebSockets) should
|
|
48
|
+
* only be reported once per real occurrence when the same MFE was registered many times over -- not once per
|
|
49
|
+
* duplicate registration. Distinct MFEs (different `target.id`) that happen to share a script (e.g. two different
|
|
50
|
+
* MFEs both registered from the same inline `<script>` block) are intentionally NOT collapsed, since each is a
|
|
51
|
+
* genuinely different entity that should still receive its own copy of the matched event.
|
|
33
52
|
* @param {string} filename
|
|
34
53
|
* @param {*} agentRef
|
|
35
54
|
* @returns {import("../../interfaces/registered-entity").RegisterAPIMetadataTarget[]}
|
|
36
55
|
*/
|
|
37
56
|
export function getRegisteredTargetsFromFilename(filename, agentRef) {
|
|
38
|
-
if (!filename
|
|
57
|
+
if (!isValid(filename, agentRef)) return [];
|
|
39
58
|
const registeredEntities = agentRef.runtime.registeredEntities;
|
|
40
|
-
|
|
59
|
+
const matches = registeredEntities?.filter(entity => entity.metadata.timings?.asset?.endsWith(filename));
|
|
60
|
+
return dedupeRegisteredEntitiesByAsset(matches).map(entity => entity.metadata.target);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Collapses a list of registered entities down to one canonical entity per unique, defined `metadata.timings.asset` +
|
|
65
|
+
* `target.id` combination. Entities whose asset could not be resolved (undefined -- e.g. inline scripts or scripts
|
|
66
|
+
* not found in the resource timing buffer) are never deduped against each other or against resolved entities, since
|
|
67
|
+
* there is no signal that they represent the same underlying script. Entities that share an asset but have different
|
|
68
|
+
* `target.id`s are never deduped against each other either, since a differing id means the customer registered
|
|
69
|
+
* genuinely distinct MFEs (not duplicate registrations of the same one).
|
|
70
|
+
*
|
|
71
|
+
* Canonical selection per asset+id: prefer an entity whose target has not been deregistered (`target.blocked ===
|
|
72
|
+
* false`) over one that has; otherwise the first-encountered entity wins, for determinism.
|
|
73
|
+
* @param {Array} entities registered entities (each with `metadata.timings.asset` and `metadata.target`)
|
|
74
|
+
* @returns {Array} deduped list of entities, preserving relative order of first occurrence
|
|
75
|
+
*/
|
|
76
|
+
export function dedupeRegisteredEntitiesByAsset(entities) {
|
|
77
|
+
if (!entities?.length) return entities || [];
|
|
78
|
+
const byKey = new Map(); // `${asset}::${id}` -> canonical entity
|
|
79
|
+
const result = [];
|
|
80
|
+
for (const entity of entities) {
|
|
81
|
+
const asset = entity.metadata?.timings?.asset;
|
|
82
|
+
if (!asset) {
|
|
83
|
+
// can't safely dedupe unresolved-asset entities -- always keep as-is
|
|
84
|
+
result.push(entity);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const key = "".concat(asset, "::").concat(entity.metadata?.target?.id);
|
|
88
|
+
const existing = byKey.get(key);
|
|
89
|
+
if (!existing) {
|
|
90
|
+
byKey.set(key, entity);
|
|
91
|
+
result.push(entity);
|
|
92
|
+
} else if (existing.metadata.target?.blocked && !entity.metadata.target?.blocked) {
|
|
93
|
+
// swap in a non-deregistered target as the canonical one for this asset+id
|
|
94
|
+
const idx = result.indexOf(existing);
|
|
95
|
+
if (idx !== -1) result[idx] = entity;
|
|
96
|
+
byKey.set(key, entity);
|
|
97
|
+
}
|
|
98
|
+
// else: existing canonical entity wins (already non-blocked, or both blocked -- first wins); drop this duplicate
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
41
101
|
}
|
|
42
102
|
|
|
43
103
|
/**
|
|
@@ -96,7 +156,7 @@ export function shouldDuplicate(target, aggregateInstance) {
|
|
|
96
156
|
* @returns {Array} An array of targets found from the stack trace. If no targets are found or allowed, returns an array with undefined.
|
|
97
157
|
*/
|
|
98
158
|
export function findTargetsFromStackTrace(agentRef) {
|
|
99
|
-
if (!agentRef
|
|
159
|
+
if (!isValid(true, agentRef) || !agentRef?.runtime?.registeredEntities?.length) return [undefined];
|
|
100
160
|
const targets = [];
|
|
101
161
|
try {
|
|
102
162
|
var urls = extractUrlsFromStack(getDeepStackTrace());
|
|
@@ -107,8 +167,29 @@ export function findTargetsFromStackTrace(agentRef) {
|
|
|
107
167
|
} catch (err) {
|
|
108
168
|
// Silent catch to prevent errors from propagating
|
|
109
169
|
}
|
|
110
|
-
|
|
111
|
-
return
|
|
170
|
+
const deduped = dedupeTargetsByInstance(targets);
|
|
171
|
+
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
|
|
172
|
+
return deduped;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Removes duplicate targets from an array, keyed by `target.instance`. This guards against the same canonical
|
|
177
|
+
* target re-entering the array via multiple matched stack-frame URLs (e.g. a recursive call whose stack contains the
|
|
178
|
+
* same file at multiple depths). Entries with no `instance` (i.e. `undefined`, meaning "the container agent")
|
|
179
|
+
* naturally collapse to a single entry too, which is the desired behavior.
|
|
180
|
+
* @param {Array} targets
|
|
181
|
+
* @returns {Array} deduped list of targets, preserving relative order of first occurrence
|
|
182
|
+
*/
|
|
183
|
+
export function dedupeTargetsByInstance(targets) {
|
|
184
|
+
const seen = new Set();
|
|
185
|
+
const result = [];
|
|
186
|
+
for (const target of targets) {
|
|
187
|
+
const key = target?.instance;
|
|
188
|
+
if (seen.has(key)) continue;
|
|
189
|
+
seen.add(key);
|
|
190
|
+
result.push(target);
|
|
191
|
+
}
|
|
192
|
+
return result;
|
|
112
193
|
}
|
|
113
194
|
|
|
114
195
|
/**
|
|
@@ -120,4 +201,14 @@ export function findTargetsFromStackTrace(agentRef) {
|
|
|
120
201
|
*/
|
|
121
202
|
function supportsV2(aggregateInstance) {
|
|
122
203
|
return aggregateInstance?.harvestEndpointVersion === 2;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 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.
|
|
208
|
+
* @param {*} identifier The identifier to check.
|
|
209
|
+
* @param {*} agentRef The agent reference to check.
|
|
210
|
+
* @returns {boolean} Returns true if the identifier and agent reference are valid, false otherwise.
|
|
211
|
+
*/
|
|
212
|
+
function isValid(identifier, agentRef) {
|
|
213
|
+
return !!identifier && !!agentRef?.init.api.register.enabled;
|
|
123
214
|
}
|
|
@@ -20,6 +20,7 @@ import { SUPPORTABILITY_METRIC } from '../../metrics/constants';
|
|
|
20
20
|
import { now } from '../../../common/timing/now';
|
|
21
21
|
import { hasUndefinedHostname } from '../../../common/deny-list/deny-list';
|
|
22
22
|
import { extractUrl } from '../../../common/url/extract-url';
|
|
23
|
+
import { addUrl } from '../../../common/url/add-url';
|
|
23
24
|
var handlers = ['load', 'error', 'abort', 'timeout'];
|
|
24
25
|
var handlersLen = handlers.length;
|
|
25
26
|
var origRequest = gosNREUMOriginals().o.REQ;
|
|
@@ -434,17 +435,6 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
|
|
|
434
435
|
ctx.loadCaptureCalled = true;
|
|
435
436
|
}
|
|
436
437
|
}
|
|
437
|
-
function addUrl(ctx, url) {
|
|
438
|
-
var parsed = parseUrl(url);
|
|
439
|
-
var params = ctx.params || ctx;
|
|
440
|
-
params.hostname = parsed.hostname;
|
|
441
|
-
params.port = parsed.port;
|
|
442
|
-
params.protocol = parsed.protocol;
|
|
443
|
-
params.host = parsed.hostname + ':' + parsed.port;
|
|
444
|
-
params.pathname = parsed.pathname;
|
|
445
|
-
ctx.parsedOrigin = parsed;
|
|
446
|
-
ctx.sameOrigin = parsed.sameOrigin;
|
|
447
|
-
}
|
|
448
438
|
function parseResponseHeaders(headerStr) {
|
|
449
439
|
const headers = {};
|
|
450
440
|
if (!headerStr) return headers;
|
|
@@ -113,6 +113,19 @@ export class InstrumentBase extends FeatureBase {
|
|
|
113
113
|
this.ee.emit('internal-error', [e]);
|
|
114
114
|
handle(SESSION_ERROR, [e], undefined, this.featureName, this.ee);
|
|
115
115
|
}
|
|
116
|
+
if (agentRef.init.api.register.allow_iframe_bridge) {
|
|
117
|
+
try {
|
|
118
|
+
// This chunk doesn't exist in the lite build (see webpack IgnorePlugin config) since none
|
|
119
|
+
// of lite's features wire up agent.register -- guard against that rather than letting an
|
|
120
|
+
// unhandled rejection surface if this flag is ever set on a lite page.
|
|
121
|
+
const {
|
|
122
|
+
setupIframeMFEMessageListener
|
|
123
|
+
} = await import(/* webpackChunkName: "iframe-message-handler" */'../../loaders/configure/iframe-message-handler');
|
|
124
|
+
setupIframeMFEMessageListener(agentRef);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
warn(23, e);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
116
129
|
|
|
117
130
|
/**
|
|
118
131
|
* Note this try-catch differs from the one in Agent.run() in that it's placed later in a page's lifecycle and
|