@newrelic/browser-agent 1.319.0-rc.2 → 1.319.0-rc.3
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/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/v2/mfe-vitals.js +3 -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 +23 -7
- package/dist/cjs/loaders/api/register.js +1 -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/v2/mfe-vitals.js +3 -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 +23 -7
- package/dist/esm/loaders/api/register.js +1 -1
- package/dist/tsconfig.tsbuildinfo +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/package.json +1 -1
- package/src/common/v2/mfe-vitals.js +3 -2
- 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 +31 -7
- package/src/loaders/api/register.js +1 -1
|
@@ -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.3";
|
|
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.3";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.trackMFEVitals = trackMFEVitals;
|
|
7
7
|
var _runtime = require("../constants/runtime");
|
|
8
8
|
var _now = require("../timing/now");
|
|
9
|
+
var _scriptTrackerConstants = require("./script-tracker-constants");
|
|
9
10
|
/**
|
|
10
11
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
11
12
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -163,11 +164,11 @@ function trackMFEVitals(target, timings) {
|
|
|
163
164
|
if (!target || !_runtime.isBrowserScope || !_runtime.globalScope.MutationObserver || !_runtime.globalScope.PerformanceObserver) return vitals;
|
|
164
165
|
const observers = [];
|
|
165
166
|
|
|
166
|
-
// If FCP hasn't been observed within
|
|
167
|
+
// If FCP hasn't been observed within this window, give up and shut down all observers.
|
|
167
168
|
// Once FCP is observed, the other vitals are left to record until their natural lifespan ends.
|
|
168
169
|
setTimeout(() => {
|
|
169
170
|
if (!fcpObservedAt) vitals.disconnect();
|
|
170
|
-
},
|
|
171
|
+
}, _scriptTrackerConstants.CORRELATION_STALE_THRESHOLD_MS);
|
|
171
172
|
const populateVitalMinimums = () => {
|
|
172
173
|
fcpObservedAt ??= (0, _now.now)();
|
|
173
174
|
lcpObservedAt ??= (0, _now.now)();
|
|
@@ -24,6 +24,8 @@ class ScriptCorrelation {
|
|
|
24
24
|
constructor(url) {
|
|
25
25
|
/** @type {string} The cleaned URL of the script */
|
|
26
26
|
this.url = url;
|
|
27
|
+
/** @type {Set<string>} MFE target ids that have already claimed this correlation via a completed `findScriptTimings()` call. Keyed per target rather than a single flag so one script that registers multiple distinct MFEs (each with its own id) doesn't have the second MFE's registration mistaken for a stale reuse of the first's. */
|
|
28
|
+
this.claimedBy = new Set();
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
/**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.CORRELATION_STALE_THRESHOLD_MS = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
9
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {number} - Correlations are keyed only by script URL and never cleared per-registration, so a script registered more than once in a session (SPA remount, shared bundle, etc.) can reuse a correlation from a much earlier load. If the correlation's recorded start predates `registeredAt` by more than this, treat it as stale and fall back to `registeredAt`. */
|
|
13
|
+
const CORRELATION_STALE_THRESHOLD_MS = exports.CORRELATION_STALE_THRESHOLD_MS = 10000;
|
|
@@ -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 _scriptTrackerConstants = require("./script-tracker-constants");
|
|
15
16
|
var _timingFactory = require("./timing-factory");
|
|
16
17
|
/**
|
|
17
18
|
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
@@ -20,6 +21,7 @@ var _timingFactory = require("./timing-factory");
|
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
24
|
+
* @typedef {import('../../loaders/api/register-api-types').RegisterAPITarget} RegisterAPITarget
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
27
|
/** export for testing purposes */
|
|
@@ -227,9 +229,11 @@ function subscribeToLatePerformanceEntry(timings, mfeScriptUrl) {
|
|
|
227
229
|
|
|
228
230
|
/**
|
|
229
231
|
* 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.
|
|
232
|
+
* @param {RegisterAPITarget} [target] - The MFE target being registered. Its id is used to scope stale-correlation detection per-MFE rather than per-script-URL, so one script registering multiple distinct MFEs doesn't misclassify a later MFE's registration as a stale reuse of an earlier one's.
|
|
230
233
|
* @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
|
|
231
234
|
*/
|
|
232
|
-
function findScriptTimings() {
|
|
235
|
+
function findScriptTimings(target) {
|
|
236
|
+
const mfeId = target?.id;
|
|
233
237
|
const timings = {
|
|
234
238
|
registeredAt: (0, _now.now)(),
|
|
235
239
|
reportedAt: undefined,
|
|
@@ -274,12 +278,24 @@ function findScriptTimings() {
|
|
|
274
278
|
subscribeToLatePerformanceEntry(timings, mfeScriptUrl);
|
|
275
279
|
}
|
|
276
280
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
// A correlation can be reused across multiple `register()` calls for the same script URL (e.g. an SPA
|
|
282
|
+
// remounting the same MFE without the script actually reloading). When that happens, its dom/performance
|
|
283
|
+
// timings still describe the *original* load, not this one. Detect that case so scriptStart/scriptEnd
|
|
284
|
+
// below can ignore the stale data instead of reporting it as if it were fresh.
|
|
285
|
+
const correlation = timings.correlation;
|
|
286
|
+
const alreadyClaimedByThisMFE = !!mfeId && !!correlation?.claimedBy.has(mfeId);
|
|
287
|
+
if (correlation && mfeId) correlation.claimedBy.add(mfeId);
|
|
288
|
+
const isCorrelationStale = () => {
|
|
289
|
+
const correlationStart = correlation?.script.start;
|
|
290
|
+
if (!alreadyClaimedByThisMFE || !correlationStart) return false;
|
|
291
|
+
const staleness = timings.registeredAt - correlationStart;
|
|
292
|
+
return staleness > _scriptTrackerConstants.CORRELATION_STALE_THRESHOLD_MS;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
// 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.
|
|
296
|
+
// Non-stale: fall back to fetchEnd if correlation data isn't available yet (our best approximation for script execution start). Stale: fall back straight to registeredAt — fetchEnd would be derived from the same stale correlation, so it can't be trusted either.
|
|
297
|
+
Object.defineProperty(timings, 'scriptStart', (0, _timingFactory.timingFactory)(() => isCorrelationStale() ? timings.registeredAt : correlation?.script.start ?? timings.fetchEnd));
|
|
298
|
+
Object.defineProperty(timings, 'scriptEnd', (0, _timingFactory.timingFactory)(() => isCorrelationStale() ? timings.registeredAt : correlation?.script.end ?? timings.registeredAt));
|
|
283
299
|
} catch (error) {
|
|
284
300
|
// Don't let stack parsing errors break anything
|
|
285
301
|
}
|
|
@@ -84,7 +84,7 @@ function register(agentRef, target) {
|
|
|
84
84
|
// getter because this is asynchronously set
|
|
85
85
|
type: _utils.V2_TYPES.BA
|
|
86
86
|
};
|
|
87
|
-
const timings = (0, _scriptTracker.findScriptTimings)();
|
|
87
|
+
const timings = (0, _scriptTracker.findScriptTimings)(target);
|
|
88
88
|
|
|
89
89
|
// Track MFE vitals for this entity
|
|
90
90
|
const vitals = (0, _mfeVitals.trackMFEVitals)(target, timings);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { globalScope, isBrowserScope } from '../constants/runtime';
|
|
7
7
|
import { now } from '../timing/now';
|
|
8
|
+
import { CORRELATION_STALE_THRESHOLD_MS } from './script-tracker-constants';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @typedef {import('../../loaders/api/register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
@@ -158,11 +159,11 @@ export function trackMFEVitals(target, timings) {
|
|
|
158
159
|
if (!target || !isBrowserScope || !globalScope.MutationObserver || !globalScope.PerformanceObserver) return vitals;
|
|
159
160
|
const observers = [];
|
|
160
161
|
|
|
161
|
-
// If FCP hasn't been observed within
|
|
162
|
+
// If FCP hasn't been observed within this window, give up and shut down all observers.
|
|
162
163
|
// Once FCP is observed, the other vitals are left to record until their natural lifespan ends.
|
|
163
164
|
setTimeout(() => {
|
|
164
165
|
if (!fcpObservedAt) vitals.disconnect();
|
|
165
|
-
},
|
|
166
|
+
}, CORRELATION_STALE_THRESHOLD_MS);
|
|
166
167
|
const populateVitalMinimums = () => {
|
|
167
168
|
fcpObservedAt ??= now();
|
|
168
169
|
lcpObservedAt ??= now();
|
|
@@ -18,6 +18,8 @@ export class ScriptCorrelation {
|
|
|
18
18
|
constructor(url) {
|
|
19
19
|
/** @type {string} The cleaned URL of the script */
|
|
20
20
|
this.url = url;
|
|
21
|
+
/** @type {Set<string>} MFE target ids that have already claimed this correlation via a completed `findScriptTimings()` call. Keyed per target rather than a single flag so one script that registers multiple distinct MFEs (each with its own id) doesn't have the second MFE's registration mistaken for a stale reuse of the first's. */
|
|
22
|
+
this.claimedBy = new Set();
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** @type {number} - Correlations are keyed only by script URL and never cleared per-registration, so a script registered more than once in a session (SPA remount, shared bundle, etc.) can reuse a correlation from a much earlier load. If the correlation's recorded start predates `registeredAt` by more than this, treat it as stale and fall back to `registeredAt`. */
|
|
7
|
+
export const CORRELATION_STALE_THRESHOLD_MS = 10000;
|
|
@@ -8,10 +8,12 @@ import { now } from '../timing/now';
|
|
|
8
8
|
import { cleanURL } from '../url/clean-url';
|
|
9
9
|
import { chrome, chromeEval, gecko } from '../util/browser-stack-matchers';
|
|
10
10
|
import { ScriptCorrelation } from './script-correlation';
|
|
11
|
+
import { CORRELATION_STALE_THRESHOLD_MS } from './script-tracker-constants';
|
|
11
12
|
import { timingFactory } from './timing-factory';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
16
|
+
* @typedef {import('../../loaders/api/register-api-types').RegisterAPITarget} RegisterAPITarget
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
/** export for testing purposes */
|
|
@@ -219,9 +221,11 @@ function subscribeToLatePerformanceEntry(timings, mfeScriptUrl) {
|
|
|
219
221
|
|
|
220
222
|
/**
|
|
221
223
|
* 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.
|
|
224
|
+
* @param {RegisterAPITarget} [target] - The MFE target being registered. Its id is used to scope stale-correlation detection per-MFE rather than per-script-URL, so one script registering multiple distinct MFEs doesn't misclassify a later MFE's registration as a stale reuse of an earlier one's.
|
|
222
225
|
* @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
|
|
223
226
|
*/
|
|
224
|
-
export function findScriptTimings() {
|
|
227
|
+
export function findScriptTimings(target) {
|
|
228
|
+
const mfeId = target?.id;
|
|
225
229
|
const timings = {
|
|
226
230
|
registeredAt: now(),
|
|
227
231
|
reportedAt: undefined,
|
|
@@ -266,12 +270,24 @@ export function findScriptTimings() {
|
|
|
266
270
|
subscribeToLatePerformanceEntry(timings, mfeScriptUrl);
|
|
267
271
|
}
|
|
268
272
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
273
|
+
// A correlation can be reused across multiple `register()` calls for the same script URL (e.g. an SPA
|
|
274
|
+
// remounting the same MFE without the script actually reloading). When that happens, its dom/performance
|
|
275
|
+
// timings still describe the *original* load, not this one. Detect that case so scriptStart/scriptEnd
|
|
276
|
+
// below can ignore the stale data instead of reporting it as if it were fresh.
|
|
277
|
+
const correlation = timings.correlation;
|
|
278
|
+
const alreadyClaimedByThisMFE = !!mfeId && !!correlation?.claimedBy.has(mfeId);
|
|
279
|
+
if (correlation && mfeId) correlation.claimedBy.add(mfeId);
|
|
280
|
+
const isCorrelationStale = () => {
|
|
281
|
+
const correlationStart = correlation?.script.start;
|
|
282
|
+
if (!alreadyClaimedByThisMFE || !correlationStart) return false;
|
|
283
|
+
const staleness = timings.registeredAt - correlationStart;
|
|
284
|
+
return staleness > CORRELATION_STALE_THRESHOLD_MS;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// 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.
|
|
288
|
+
// Non-stale: fall back to fetchEnd if correlation data isn't available yet (our best approximation for script execution start). Stale: fall back straight to registeredAt — fetchEnd would be derived from the same stale correlation, so it can't be trusted either.
|
|
289
|
+
Object.defineProperty(timings, 'scriptStart', timingFactory(() => isCorrelationStale() ? timings.registeredAt : correlation?.script.start ?? timings.fetchEnd));
|
|
290
|
+
Object.defineProperty(timings, 'scriptEnd', timingFactory(() => isCorrelationStale() ? timings.registeredAt : correlation?.script.end ?? timings.registeredAt));
|
|
275
291
|
} catch (error) {
|
|
276
292
|
// Don't let stack parsing errors break anything
|
|
277
293
|
}
|
|
@@ -77,7 +77,7 @@ function register(agentRef, target) {
|
|
|
77
77
|
// getter because this is asynchronously set
|
|
78
78
|
type: V2_TYPES.BA
|
|
79
79
|
};
|
|
80
|
-
const timings = findScriptTimings();
|
|
80
|
+
const timings = findScriptTimings(target);
|
|
81
81
|
|
|
82
82
|
// Track MFE vitals for this entity
|
|
83
83
|
const vitals = trackMFEVitals(target, timings);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/events.js","../src/common/constants/iframe-constants.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/harvester.js","../src/common/harvest/send.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/payloads/payloads.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/session/session-key.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/add-url.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/browser-stack-matchers.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/short-circuit.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/webdriver-detection.js","../src/common/v2/mfe-vitals.js","../src/common/v2/script-correlation.js","../src/common/v2/script-tracker.js","../src/common/v2/timing-factory.js","../src/common/v2/utils.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/interfaces/registered-entity.js","../src/interfaces/registered-iframe-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/iframe-message-handler.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/index.js","../src/cdn/experimental.js","../src/cdn/lite.js","../src/cdn/pro.js","../src/cdn/spa.js","../src/common/aggregate/aggregator.js","../src/common/aggregate/event-aggregator.js","../src/common/config/configurable.js","../src/common/config/info.js","../src/common/config/init-types.js","../src/common/config/init.js","../src/common/config/loader-config.js","../src/common/config/runtime.js","../src/common/constants/agent-constants.js","../src/common/constants/env.cdn.js","../src/common/constants/env.js","../src/common/constants/env.npm.js","../src/common/constants/events.js","../src/common/constants/iframe-constants.js","../src/common/constants/runtime.js","../src/common/constants/shared-channel.js","../src/common/deny-list/deny-list.js","../src/common/dispatch/global-event.js","../src/common/dom/iframe.js","../src/common/dom/query-selector.js","../src/common/dom/selector-path.js","../src/common/drain/drain.js","../src/common/event-emitter/contextual-ee.js","../src/common/event-emitter/event-context.js","../src/common/event-emitter/handle.js","../src/common/event-emitter/register-handler.js","../src/common/event-listener/event-listener-opts.js","../src/common/harvest/harvester.js","../src/common/harvest/send.js","../src/common/harvest/types.js","../src/common/ids/bundle-id.js","../src/common/ids/id.js","../src/common/ids/unique-id.js","../src/common/payloads/payloads.js","../src/common/serialize/bel-serializer.js","../src/common/session/constants.js","../src/common/session/session-entity.js","../src/common/session/session-key.js","../src/common/storage/local-storage.js","../src/common/timer/interaction-timer.js","../src/common/timer/timer.js","../src/common/timing/nav-timing.js","../src/common/timing/now.js","../src/common/timing/time-keeper.js","../src/common/unload/eol.js","../src/common/url/add-url.js","../src/common/url/canonicalize-url.js","../src/common/url/clean-url.js","../src/common/url/encode.js","../src/common/url/extract-url.js","../src/common/url/location.js","../src/common/url/parse-url.js","../src/common/url/protocol.js","../src/common/util/attribute-size.js","../src/common/util/browser-stack-matchers.js","../src/common/util/console.js","../src/common/util/data-size.js","../src/common/util/event-origin.js","../src/common/util/feature-flags.js","../src/common/util/get-or-set.js","../src/common/util/invoke.js","../src/common/util/monkey-patched.js","../src/common/util/obfuscate.js","../src/common/util/short-circuit.js","../src/common/util/stringify.js","../src/common/util/submit-data.js","../src/common/util/text.js","../src/common/util/traverse.js","../src/common/util/type-check.js","../src/common/util/webdriver-detection.js","../src/common/v2/mfe-vitals.js","../src/common/v2/script-correlation.js","../src/common/v2/script-tracker-constants.js","../src/common/v2/script-tracker.js","../src/common/v2/timing-factory.js","../src/common/v2/utils.js","../src/common/vitals/constants.js","../src/common/vitals/cumulative-layout-shift.js","../src/common/vitals/first-contentful-paint.js","../src/common/vitals/first-paint.js","../src/common/vitals/interaction-to-next-paint.js","../src/common/vitals/largest-contentful-paint.js","../src/common/vitals/load-time.js","../src/common/vitals/time-to-first-byte.js","../src/common/vitals/vital-metric.js","../src/common/window/load.js","../src/common/window/nreum.js","../src/common/window/page-visibility.js","../src/common/wrap/wrap-events.js","../src/common/wrap/wrap-fetch.js","../src/common/wrap/wrap-function.js","../src/common/wrap/wrap-history.js","../src/common/wrap/wrap-logger.js","../src/common/wrap/wrap-websocket.js","../src/common/wrap/wrap-xhr.js","../src/features/ajax/constants.js","../src/features/ajax/index.js","../src/features/ajax/aggregate/gql.js","../src/features/ajax/aggregate/index.js","../src/features/ajax/instrument/distributed-tracing.js","../src/features/ajax/instrument/index.js","../src/features/ajax/instrument/response-size.js","../src/features/generic_events/constants.js","../src/features/generic_events/index.js","../src/features/generic_events/aggregate/index.js","../src/features/generic_events/aggregate/user-actions/aggregated-user-action.js","../src/features/generic_events/aggregate/user-actions/user-actions-aggregator.js","../src/features/generic_events/instrument/index.js","../src/features/jserrors/constants.js","../src/features/jserrors/index.js","../src/features/jserrors/aggregate/canonical-function-name.js","../src/features/jserrors/aggregate/cause-string.js","../src/features/jserrors/aggregate/compute-stack-trace.js","../src/features/jserrors/aggregate/format-stack-trace.js","../src/features/jserrors/aggregate/index.js","../src/features/jserrors/aggregate/internal-errors.js","../src/features/jserrors/aggregate/string-hash-code.js","../src/features/jserrors/instrument/index.js","../src/features/jserrors/shared/cast-error.js","../src/features/jserrors/shared/uncaught-error.js","../src/features/logging/constants.js","../src/features/logging/index.js","../src/features/logging/aggregate/index.js","../src/features/logging/instrument/index.js","../src/features/logging/shared/log.js","../src/features/logging/shared/utils.js","../src/features/metrics/constants.js","../src/features/metrics/index.js","../src/features/metrics/aggregate/framework-detection.js","../src/features/metrics/aggregate/harvest-metadata.js","../src/features/metrics/aggregate/index.js","../src/features/metrics/instrument/index.js","../src/features/page_action/constants.js","../src/features/page_action/index.js","../src/features/page_action/instrument/index.js","../src/features/page_view_event/constants.js","../src/features/page_view_event/index.js","../src/features/page_view_event/aggregate/index.js","../src/features/page_view_event/aggregate/initialized-features.js","../src/features/page_view_event/instrument/index.js","../src/features/page_view_timing/constants.js","../src/features/page_view_timing/index.js","../src/features/page_view_timing/aggregate/index.js","../src/features/page_view_timing/instrument/index.js","../src/features/session_replay/constants.js","../src/features/session_replay/index.js","../src/features/session_replay/aggregate/index.js","../src/features/session_replay/instrument/index.js","../src/features/session_replay/shared/recorder-events.js","../src/features/session_replay/shared/recorder.js","../src/features/session_replay/shared/stylesheet-evaluator.js","../src/features/session_replay/shared/utils.js","../src/features/session_trace/constants.js","../src/features/session_trace/index.js","../src/features/session_trace/aggregate/index.js","../src/features/session_trace/aggregate/trace/node.js","../src/features/session_trace/aggregate/trace/storage.js","../src/features/session_trace/aggregate/trace/utils.js","../src/features/session_trace/instrument/index.js","../src/features/soft_navigations/constants.js","../src/features/soft_navigations/index.js","../src/features/soft_navigations/aggregate/ajax-node.js","../src/features/soft_navigations/aggregate/bel-node.js","../src/features/soft_navigations/aggregate/index.js","../src/features/soft_navigations/aggregate/initial-page-load-interaction.js","../src/features/soft_navigations/aggregate/interaction.js","../src/features/soft_navigations/instrument/index.js","../src/features/utils/agent-session.js","../src/features/utils/aggregate-base.js","../src/features/utils/event-buffer.js","../src/features/utils/feature-base.js","../src/features/utils/feature-gates.js","../src/features/utils/instrument-base.js","../src/interfaces/registered-entity.js","../src/interfaces/registered-iframe-entity.js","../src/loaders/agent-base.js","../src/loaders/agent.js","../src/loaders/api-base.js","../src/loaders/browser-agent.js","../src/loaders/micro-agent-base.js","../src/loaders/micro-agent.js","../src/loaders/api/addPageAction.js","../src/loaders/api/addRelease.js","../src/loaders/api/addToTrace.js","../src/loaders/api/consent.js","../src/loaders/api/constants.js","../src/loaders/api/finished.js","../src/loaders/api/interaction-types.js","../src/loaders/api/interaction.js","../src/loaders/api/log.js","../src/loaders/api/measure.js","../src/loaders/api/noticeError.js","../src/loaders/api/pauseReplay.js","../src/loaders/api/recordCustomEvent.js","../src/loaders/api/recordReplay.js","../src/loaders/api/register-api-types.js","../src/loaders/api/register.js","../src/loaders/api/setApplicationVersion.js","../src/loaders/api/setCustomAttribute.js","../src/loaders/api/setErrorHandler.js","../src/loaders/api/setPageViewName.js","../src/loaders/api/setUserId.js","../src/loaders/api/sharedHandlers.js","../src/loaders/api/start.js","../src/loaders/api/topLevelCallers.js","../src/loaders/api/wrapLogger.js","../src/loaders/configure/configure.js","../src/loaders/configure/iframe-message-handler.js","../src/loaders/configure/nonce.js","../src/loaders/configure/public-path.js","../src/loaders/features/enabled-features.js","../src/loaders/features/featureDependencies.js","../src/loaders/features/features.js"],"version":"5.9.3"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mfe-vitals.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/mfe-vitals.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mfe-vitals.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/mfe-vitals.js"],"names":[],"mappings":"AAqHA;;;;;GAKG;AACH,uCAJW,iBAAiB,WACjB,kBAAkB,GAChB,iBAAiB,CAuM7B;iCAtTY,OAAO,sCAAsC,EAAE,kBAAkB;gCACjE,OAAO,sCAAsC,EAAE,iBAAiB;gCAChE,OAAO,sCAAsC,EAAE,iBAAiB"}
|
|
@@ -17,6 +17,8 @@ export class ScriptCorrelation {
|
|
|
17
17
|
performance: CorrelationTiming;
|
|
18
18
|
/** @type {string} The cleaned URL of the script */
|
|
19
19
|
url: string;
|
|
20
|
+
/** @type {Set<string>} MFE target ids that have already claimed this correlation via a completed `findScriptTimings()` call. Keyed per target rather than a single flag so one script that registers multiple distinct MFEs (each with its own id) doesn't have the second MFE's registration mistaken for a stale reuse of the first's. */
|
|
21
|
+
claimedBy: Set<string>;
|
|
20
22
|
/**
|
|
21
23
|
* Gets the script timing, using DOM timings if available, otherwise falling back to performance timings or registeredAt as appropriate. This is used to provide the most accurate script timing possible for registered entities.
|
|
22
24
|
* @returns {{start: number, end: number}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-correlation.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/script-correlation.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;;GAEG;AACH;IAME;;;OAGG;IACH,iBAFW,MAAM,
|
|
1
|
+
{"version":3,"file":"script-correlation.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/script-correlation.js"],"names":[],"mappings":"AAAA;;;GAGG;AACH;;GAEG;AACH;IAME;;;OAGG;IACH,iBAFW,MAAM,EAOhB;IAdD,gEAAgE;IAChE,KADW,iBAAiB,CACC;IAC7B,gFAAgF;IAChF,aADW,iBAAiB,CACS;IAOnC,mDAAmD;IACnD,KADW,MAAM,CACH;IACd,4UAA4U;IAC5U,WADW,GAAG,CAAC,MAAM,CAAC,CACI;IAG5B;;;OAGG;IACH,cAFa;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,CASxC;CACF;AAED;IACE,wEAAwE;IACxE,OADW,MAAM,CACR;IACT,wEAAwE;IACxE,KADW,MAAM,CACV;IACP,0CAA0C;IAC1C,OADW,GAAC,CACK;CAClB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/** @type {number} - Correlations are keyed only by script URL and never cleared per-registration, so a script registered more than once in a session (SPA remount, shared bundle, etc.) can reuse a correlation from a much earlier load. If the correlation's recorded start predates `registeredAt` by more than this, treat it as stale and fall back to `registeredAt`. */
|
|
6
|
+
export const CORRELATION_STALE_THRESHOLD_MS: number;
|
|
7
|
+
//# sourceMappingURL=script-tracker-constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script-tracker-constants.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/script-tracker-constants.js"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+WAA+W;AAC/W,6CADW,MAAM,CACkC"}
|
|
@@ -11,16 +11,19 @@ export function extractUrlsFromStack(stack: string): string[];
|
|
|
11
11
|
export function getDeepStackTrace(): Error.stack | undefined;
|
|
12
12
|
/**
|
|
13
13
|
* 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.
|
|
14
|
+
* @param {RegisterAPITarget} [target] - The MFE target being registered. Its id is used to scope stale-correlation detection per-MFE rather than per-script-URL, so one script registering multiple distinct MFEs doesn't misclassify a later MFE's registration as a stale reuse of an earlier one's.
|
|
14
15
|
* @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
|
|
15
16
|
*/
|
|
16
|
-
export function findScriptTimings(): RegisterAPITimings;
|
|
17
|
+
export function findScriptTimings(target?: RegisterAPITarget): RegisterAPITimings;
|
|
17
18
|
/**
|
|
18
19
|
* @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
20
|
+
* @typedef {import('../../loaders/api/register-api-types').RegisterAPITarget} RegisterAPITarget
|
|
19
21
|
*/
|
|
20
22
|
/** export for testing purposes */
|
|
21
23
|
export let thisFile: any;
|
|
22
24
|
/** @type {Map<string, ScriptCorrelation>} - Central registry for script correlations containing both DOM and Performance data */
|
|
23
25
|
export const scriptCorrelations: Map<string, ScriptCorrelation>;
|
|
24
26
|
export type RegisterAPITimings = any;
|
|
27
|
+
export type RegisterAPITarget = import("../../loaders/api/register-api-types").RegisterAPITarget;
|
|
25
28
|
import { ScriptCorrelation } from './script-correlation';
|
|
26
29
|
//# sourceMappingURL=script-tracker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-tracker.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/script-tracker.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"script-tracker.d.ts","sourceRoot":"","sources":["../../../../src/common/v2/script-tracker.js"],"names":[],"mappings":"AAkHA;;;;GAIG;AACH,4CAHW,MAAM,GACJ,MAAM,EAAE,CAsBpB;AAED;;;GAGG;AACH,qCAFa,KAAK,CAAC,KAAK,GAAG,SAAS,CAanC;AAiED;;;;GAIG;AACH,2CAHW,iBAAiB,GACf,kBAAkB,CA0E9B;AA7RD;;;GAGG;AAEH,kCAAkC;AAClC,yBAAmB;AAUnB,iIAAiI;AACjI,iCADW,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CACE;;gCAf9B,OAAO,sCAAsC,EAAE,iBAAiB;kCAN3C,sBAAsB"}
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { globalScope, isBrowserScope } from '../constants/runtime'
|
|
7
7
|
import { now } from '../timing/now'
|
|
8
|
+
import { CORRELATION_STALE_THRESHOLD_MS } from './script-tracker-constants'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @typedef {import('../../loaders/api/register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
@@ -151,11 +152,11 @@ export function trackMFEVitals (target, timings) {
|
|
|
151
152
|
|
|
152
153
|
const observers = []
|
|
153
154
|
|
|
154
|
-
// If FCP hasn't been observed within
|
|
155
|
+
// If FCP hasn't been observed within this window, give up and shut down all observers.
|
|
155
156
|
// Once FCP is observed, the other vitals are left to record until their natural lifespan ends.
|
|
156
157
|
setTimeout(() => {
|
|
157
158
|
if (!fcpObservedAt) vitals.disconnect()
|
|
158
|
-
},
|
|
159
|
+
}, CORRELATION_STALE_THRESHOLD_MS)
|
|
159
160
|
|
|
160
161
|
const populateVitalMinimums = () => {
|
|
161
162
|
fcpObservedAt ??= now()
|
|
@@ -18,6 +18,8 @@ export class ScriptCorrelation {
|
|
|
18
18
|
constructor (url) {
|
|
19
19
|
/** @type {string} The cleaned URL of the script */
|
|
20
20
|
this.url = url
|
|
21
|
+
/** @type {Set<string>} MFE target ids that have already claimed this correlation via a completed `findScriptTimings()` call. Keyed per target rather than a single flag so one script that registers multiple distinct MFEs (each with its own id) doesn't have the second MFE's registration mistaken for a stale reuse of the first's. */
|
|
22
|
+
this.claimedBy = new Set()
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/**
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2020-2026 New Relic, Inc. All rights reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** @type {number} - Correlations are keyed only by script URL and never cleared per-registration, so a script registered more than once in a session (SPA remount, shared bundle, etc.) can reuse a correlation from a much earlier load. If the correlation's recorded start predates `registeredAt` by more than this, treat it as stale and fall back to `registeredAt`. */
|
|
7
|
+
export const CORRELATION_STALE_THRESHOLD_MS = 10000
|
|
@@ -8,10 +8,12 @@ import { now } from '../timing/now'
|
|
|
8
8
|
import { cleanURL } from '../url/clean-url'
|
|
9
9
|
import { chrome, chromeEval, gecko } from '../util/browser-stack-matchers'
|
|
10
10
|
import { ScriptCorrelation } from './script-correlation'
|
|
11
|
+
import { CORRELATION_STALE_THRESHOLD_MS } from './script-tracker-constants'
|
|
11
12
|
import { timingFactory } from './timing-factory'
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* @typedef {import('./register-api-types').RegisterAPITimings} RegisterAPITimings
|
|
16
|
+
* @typedef {import('../../loaders/api/register-api-types').RegisterAPITarget} RegisterAPITarget
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
/** export for testing purposes */
|
|
@@ -219,9 +221,11 @@ function subscribeToLatePerformanceEntry (timings, mfeScriptUrl) {
|
|
|
219
221
|
|
|
220
222
|
/**
|
|
221
223
|
* 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.
|
|
224
|
+
* @param {RegisterAPITarget} [target] - The MFE target being registered. Its id is used to scope stale-correlation detection per-MFE rather than per-script-URL, so one script registering multiple distinct MFEs doesn't misclassify a later MFE's registration as a stale reuse of an earlier one's.
|
|
222
225
|
* @returns {RegisterAPITimings} Object containing script fetch start and end times, and the asset URL if found
|
|
223
226
|
*/
|
|
224
|
-
export function findScriptTimings () {
|
|
227
|
+
export function findScriptTimings (target) {
|
|
228
|
+
const mfeId = target?.id
|
|
225
229
|
const timings = { registeredAt: now(), reportedAt: undefined, fetchStart: 0, fetchEnd: 0, scriptStart: 0, scriptEnd: 0, asset: undefined, type: 'unknown' }
|
|
226
230
|
const stack = getDeepStackTrace()
|
|
227
231
|
if (!stack) return timings
|
|
@@ -261,12 +265,32 @@ export function findScriptTimings () {
|
|
|
261
265
|
subscribeToLatePerformanceEntry(timings, mfeScriptUrl)
|
|
262
266
|
}
|
|
263
267
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
// A correlation can be reused across multiple `register()` calls for the same script URL (e.g. an SPA
|
|
269
|
+
// remounting the same MFE without the script actually reloading). When that happens, its dom/performance
|
|
270
|
+
// timings still describe the *original* load, not this one. Detect that case so scriptStart/scriptEnd
|
|
271
|
+
// below can ignore the stale data instead of reporting it as if it were fresh.
|
|
272
|
+
const correlation = timings.correlation
|
|
273
|
+
const alreadyClaimedByThisMFE = !!mfeId && !!correlation?.claimedBy.has(mfeId)
|
|
274
|
+
if (correlation && mfeId) correlation.claimedBy.add(mfeId)
|
|
275
|
+
const isCorrelationStale = () => {
|
|
276
|
+
const correlationStart = correlation?.script.start
|
|
277
|
+
if (!alreadyClaimedByThisMFE || !correlationStart) return false
|
|
278
|
+
const staleness = timings.registeredAt - correlationStart
|
|
279
|
+
return staleness > CORRELATION_STALE_THRESHOLD_MS
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// 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.
|
|
283
|
+
// Non-stale: fall back to fetchEnd if correlation data isn't available yet (our best approximation for script execution start). Stale: fall back straight to registeredAt — fetchEnd would be derived from the same stale correlation, so it can't be trusted either.
|
|
284
|
+
Object.defineProperty(
|
|
285
|
+
timings,
|
|
286
|
+
'scriptStart',
|
|
287
|
+
timingFactory(() => isCorrelationStale() ? timings.registeredAt : (correlation?.script.start ?? timings.fetchEnd))
|
|
288
|
+
)
|
|
289
|
+
Object.defineProperty(
|
|
290
|
+
timings,
|
|
291
|
+
'scriptEnd',
|
|
292
|
+
timingFactory(() => isCorrelationStale() ? timings.registeredAt : (correlation?.script.end ?? timings.registeredAt))
|
|
293
|
+
)
|
|
270
294
|
} catch (error) {
|
|
271
295
|
// Don't let stack parsing errors break anything
|
|
272
296
|
}
|