@newrelic/browser-agent 1.297.1-rc.4 → 1.297.1
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/vitals/largest-contentful-paint.js +1 -4
- package/dist/cjs/common/wrap/wrap-function.js +4 -9
- package/dist/cjs/features/ajax/aggregate/index.js +2 -10
- package/dist/cjs/features/ajax/instrument/index.js +0 -1
- package/dist/cjs/features/jserrors/aggregate/index.js +4 -9
- package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +3 -11
- package/dist/cjs/features/soft_navigations/aggregate/index.js +14 -38
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +20 -34
- package/dist/cjs/features/soft_navigations/constants.js +4 -8
- package/dist/cjs/features/soft_navigations/instrument/index.js +6 -9
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/vitals/largest-contentful-paint.js +1 -4
- package/dist/esm/common/wrap/wrap-function.js +4 -9
- package/dist/esm/features/ajax/aggregate/index.js +2 -10
- package/dist/esm/features/ajax/instrument/index.js +0 -1
- package/dist/esm/features/jserrors/aggregate/index.js +4 -9
- package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +3 -11
- package/dist/esm/features/soft_navigations/aggregate/index.js +15 -39
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +21 -35
- package/dist/esm/features/soft_navigations/constants.js +3 -7
- package/dist/esm/features/soft_navigations/instrument/index.js +7 -10
- package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +1 -2
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +3 -6
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/constants.d.ts +0 -4
- package/dist/types/features/soft_navigations/constants.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/vitals/largest-contentful-paint.js +1 -2
- package/src/common/wrap/wrap-function.js +4 -9
- package/src/features/ajax/aggregate/index.js +2 -10
- package/src/features/ajax/instrument/index.js +0 -1
- package/src/features/jserrors/aggregate/index.js +6 -10
- package/src/features/soft_navigations/aggregate/ajax-node.js +4 -8
- package/src/features/soft_navigations/aggregate/index.js +15 -39
- package/src/features/soft_navigations/aggregate/interaction.js +19 -33
- package/src/features/soft_navigations/constants.js +2 -5
- package/src/features/soft_navigations/instrument/index.js +8 -9
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.297.1
|
|
20
|
+
const VERSION = exports.VERSION = "1.297.1";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -17,7 +17,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the version of the agent
|
|
19
19
|
*/
|
|
20
|
-
const VERSION = exports.VERSION = "1.297.1
|
|
20
|
+
const VERSION = exports.VERSION = "1.297.1";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Exposes the build type of the agent
|
|
@@ -34,10 +34,7 @@ if (_runtime.isBrowserScope) {
|
|
|
34
34
|
resourceLoadDuration: attribution.resourceLoadDuration,
|
|
35
35
|
resourceLoadTime: attribution.resourceLoadDuration,
|
|
36
36
|
// kept for NR backwards compatibility, deprecated in v3->v4
|
|
37
|
-
elementRenderDelay: attribution.elementRenderDelay
|
|
38
|
-
...(attribution.navigationEntry && {
|
|
39
|
-
pageUrl: (0, _cleanUrl.cleanURL)(attribution.navigationEntry.name)
|
|
40
|
-
}) // used to ensure the LCP gets the correct URL at harvest time if a soft nav has occurred before page load
|
|
37
|
+
elementRenderDelay: attribution.elementRenderDelay
|
|
41
38
|
};
|
|
42
39
|
if (attribution.url) attrs.elUrl = (0, _cleanUrl.cleanURL)(attribution.url);
|
|
43
40
|
if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
|
|
@@ -18,7 +18,6 @@ var _bundleId = require("../ids/bundle-id");
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
const flag = exports.flag = "nr@original:".concat(_bundleId.bundleId);
|
|
21
|
-
const LONG_TASK_THRESHOLD = 50;
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
* A convenience alias of `hasOwnProperty`.
|
|
@@ -95,7 +94,7 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
95
94
|
// Warning: start events may mutate args!
|
|
96
95
|
safeEmit(prefix + 'start', [args, originalThis, methodName], ctx, bubble);
|
|
97
96
|
const fnStartTime = performance.now();
|
|
98
|
-
let fnEndTime;
|
|
97
|
+
let fnEndTime = fnStartTime;
|
|
99
98
|
try {
|
|
100
99
|
result = fn.apply(originalThis, args);
|
|
101
100
|
fnEndTime = performance.now();
|
|
@@ -109,20 +108,16 @@ function createWrapperWithEmitter(emitter, always) {
|
|
|
109
108
|
} finally {
|
|
110
109
|
const duration = fnEndTime - fnStartTime;
|
|
111
110
|
const task = {
|
|
112
|
-
start: fnStartTime,
|
|
113
|
-
end: fnEndTime,
|
|
114
111
|
duration,
|
|
115
|
-
isLongTask: duration >=
|
|
112
|
+
isLongTask: duration >= 50,
|
|
116
113
|
methodName,
|
|
117
114
|
thrownError
|
|
118
115
|
// could add more properties here later if needed by downstream features
|
|
119
116
|
};
|
|
120
117
|
// standalone long task message
|
|
121
|
-
if (task.isLongTask)
|
|
122
|
-
safeEmit('long-task', [task, originalThis], ctx, bubble);
|
|
123
|
-
}
|
|
118
|
+
if (task.isLongTask) safeEmit('long-task', [task], ctx, bubble);
|
|
124
119
|
// -end message also includes the task execution info
|
|
125
|
-
safeEmit(prefix + 'end', [args, originalThis, result], ctx, bubble);
|
|
120
|
+
safeEmit(prefix + 'end', [args, originalThis, result, task], ctx, bubble);
|
|
126
121
|
}
|
|
127
122
|
}
|
|
128
123
|
}
|
|
@@ -13,7 +13,6 @@ var _features = require("../../../loaders/features/features");
|
|
|
13
13
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
14
14
|
var _gql = require("./gql");
|
|
15
15
|
var _belSerializer = require("../../../common/serialize/bel-serializer");
|
|
16
|
-
var _nreum = require("../../../common/window/nreum");
|
|
17
16
|
/**
|
|
18
17
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
19
18
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -50,13 +49,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
50
49
|
// the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
|
|
51
50
|
classThis.storeXhr(...arguments, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
|
|
52
51
|
}, this.featureName, this.ee);
|
|
53
|
-
this.ee.on('long-task', (task, originator) => {
|
|
54
|
-
if (originator instanceof (0, _nreum.gosNREUMOriginals)().o.XHR) {
|
|
55
|
-
// any time a long task from XHR callback is observed, update the end time for soft nav use
|
|
56
|
-
const xhrMetadata = this.ee.context(originator);
|
|
57
|
-
xhrMetadata.latestLongtaskEnd = task.end;
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
52
|
this.waitForFlags([]).then(() => this.drain());
|
|
61
53
|
}
|
|
62
54
|
storeXhr(params, metrics, startTime, endTime, type, ctx) {
|
|
@@ -116,8 +108,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
116
108
|
if (event.gql) this.reportSupportabilityMetric('Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length);
|
|
117
109
|
const softNavInUse = Boolean(this.agentRef.features?.[_features.FEATURE_NAMES.softNav]);
|
|
118
110
|
if (softNavInUse) {
|
|
119
|
-
// For newer soft nav (when running), pass the event
|
|
120
|
-
(0, _handle.handle)('ajax', [event
|
|
111
|
+
// For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
|
|
112
|
+
(0, _handle.handle)('ajax', [event], undefined, _features.FEATURE_NAMES.softNav, this.ee);
|
|
121
113
|
} else if (ctx.spaNode) {
|
|
122
114
|
// For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
123
115
|
const interactionId = ctx.spaNode.interaction.id;
|
|
@@ -106,7 +106,6 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
|
|
|
106
106
|
ctx.loadCaptureCalled = false;
|
|
107
107
|
ctx.params = this.params || {};
|
|
108
108
|
ctx.metrics = this.metrics || {};
|
|
109
|
-
ctx.latestLongtaskEnd = 0;
|
|
110
109
|
xhr.addEventListener('load', function (event) {
|
|
111
110
|
captureXhrData(ctx, xhr);
|
|
112
111
|
}, (0, _eventListenerOpts.eventListenerOpts)(false));
|
|
@@ -44,7 +44,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
44
44
|
this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
|
|
45
45
|
(0, _registerHandler.registerHandler)('err', (...args) => this.storeError(...args), this.featureName, this.ee);
|
|
46
46
|
(0, _registerHandler.registerHandler)('ierr', (...args) => this.storeError(...args), this.featureName, this.ee);
|
|
47
|
-
(0, _registerHandler.registerHandler)('softNavFlush', (interactionId, wasFinished, softNavAttrs
|
|
47
|
+
(0, _registerHandler.registerHandler)('softNavFlush', (interactionId, wasFinished, softNavAttrs) => this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs), this.featureName, this.ee); // when an ixn is done or cancelled
|
|
48
48
|
|
|
49
49
|
this.harvestOpts.aggregatorTypes = ['err', 'ierr', 'xhr']; // the types in EventAggregator this feature cares about
|
|
50
50
|
|
|
@@ -271,15 +271,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
271
271
|
});
|
|
272
272
|
delete this.bufferedErrorsUnderSpa[interaction.id];
|
|
273
273
|
}
|
|
274
|
-
onSoftNavNotification(interactionId, wasFinished, softNavAttrs
|
|
274
|
+
onSoftNavNotification(interactionId, wasFinished, softNavAttrs) {
|
|
275
275
|
if (this.blocked) return;
|
|
276
|
-
this.bufferedErrorsUnderSpa[interactionId]?.forEach(jsErrorEvent =>
|
|
277
|
-
|
|
278
|
-
if (!wasFinished) return this.#storeJserrorForHarvest(jsErrorEvent, false, softNavAttrs);
|
|
279
|
-
const startTime = jsErrorEvent[3].time; // in storeError fn, the newMetrics obj contains the time passed to & used by SN to seek the ixn
|
|
280
|
-
if (startTime > interactionEndTime) return this.#storeJserrorForHarvest(jsErrorEvent, false, softNavAttrs); // disassociate any error that ultimately falls outside the final ixn span
|
|
281
|
-
return this.#storeJserrorForHarvest(jsErrorEvent, true, softNavAttrs);
|
|
282
|
-
});
|
|
276
|
+
this.bufferedErrorsUnderSpa[interactionId]?.forEach(jsErrorEvent => this.#storeJserrorForHarvest(jsErrorEvent, wasFinished, softNavAttrs) // this should not modify the re-used softNavAttrs contents
|
|
277
|
+
);
|
|
283
278
|
delete this.bufferedErrorsUnderSpa[interactionId]; // wipe the list of jserrors so they aren't duplicated by another call to the same id
|
|
284
279
|
}
|
|
285
280
|
}
|
|
@@ -13,7 +13,7 @@ var _belNode = require("./bel-node");
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
class AjaxNode extends _belNode.BelNode {
|
|
16
|
-
constructor(ajaxEvent
|
|
16
|
+
constructor(ajaxEvent) {
|
|
17
17
|
super();
|
|
18
18
|
this.belType = _constants.NODE_TYPE.AJAX;
|
|
19
19
|
this.method = ajaxEvent.method;
|
|
@@ -27,12 +27,8 @@ class AjaxNode extends _belNode.BelNode {
|
|
|
27
27
|
this.traceId = ajaxEvent.traceId;
|
|
28
28
|
this.spanTimestamp = ajaxEvent.spanTimestamp;
|
|
29
29
|
this.gql = ajaxEvent.gql;
|
|
30
|
-
this.start = ajaxEvent.startTime;
|
|
30
|
+
this.start = ajaxEvent.startTime; // 5000 --- 5500 --> 10500
|
|
31
31
|
this.end = ajaxEvent.endTime;
|
|
32
|
-
if (ajaxContext?.latestLongtaskEnd) {
|
|
33
|
-
this.callbackEnd = Math.max(ajaxContext.latestLongtaskEnd, this.end); // typically lt end if non-zero, but added clamping to end just in case
|
|
34
|
-
this.callbackDuration = this.callbackEnd - this.end; // callbackDuration is the time from ajax loaded to last long task observed from it
|
|
35
|
-
} else this.callbackEnd = this.end; // if no long task was observed, callbackEnd is the same as end
|
|
36
32
|
}
|
|
37
33
|
serialize(parentStartTimestamp, agentRef) {
|
|
38
34
|
const addString = (0, _belSerializer.getAddStringContext)(agentRef.runtime.obfuscator);
|
|
@@ -45,11 +41,7 @@ class AjaxNode extends _belNode.BelNode {
|
|
|
45
41
|
// start relative to parent start (if part of first node in payload) or first parent start
|
|
46
42
|
(0, _belSerializer.numeric)(this.end - this.start),
|
|
47
43
|
// end is relative to start
|
|
48
|
-
(0, _belSerializer.numeric)(this.callbackEnd
|
|
49
|
-
// callbackEnd is relative to end
|
|
50
|
-
(0, _belSerializer.numeric)(this.callbackDuration),
|
|
51
|
-
// not relative
|
|
52
|
-
addString(this.method), (0, _belSerializer.numeric)(this.status), addString(this.domain), addString(this.path), (0, _belSerializer.numeric)(this.txSize), (0, _belSerializer.numeric)(this.rxSize), this.requestedWith, addString(this.nodeId), (0, _belSerializer.nullable)(this.spanId, addString, true) + (0, _belSerializer.nullable)(this.traceId, addString, true) + (0, _belSerializer.nullable)(this.spanTimestamp, _belSerializer.numeric)];
|
|
44
|
+
(0, _belSerializer.numeric)(this.callbackEnd), (0, _belSerializer.numeric)(this.callbackDuration), addString(this.method), (0, _belSerializer.numeric)(this.status), addString(this.domain), addString(this.path), (0, _belSerializer.numeric)(this.txSize), (0, _belSerializer.numeric)(this.rxSize), this.requestedWith, addString(this.nodeId), (0, _belSerializer.nullable)(this.spanId, addString, true) + (0, _belSerializer.nullable)(this.traceId, addString, true) + (0, _belSerializer.nullable)(this.spanTimestamp, _belSerializer.numeric)];
|
|
53
45
|
let allAttachedNodes = [];
|
|
54
46
|
if (typeof this.gql === 'object') allAttachedNodes = (0, _belSerializer.addCustomAttributes)(this.gql, addString);
|
|
55
47
|
this.children.forEach(node => allAttachedNodes.push(node.serialize())); // no children is expected under ajax nodes at this time
|
|
@@ -47,7 +47,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
47
47
|
});
|
|
48
48
|
this.latestRouteSetByApi = null;
|
|
49
49
|
this.interactionInProgress = null; // aside from the "page load" interaction, there can only ever be 1 ongoing at a time
|
|
50
|
-
this.latestHistoryUrl =
|
|
50
|
+
this.latestHistoryUrl = null;
|
|
51
51
|
this.harvestOpts.beforeUnload = () => this.interactionInProgress?.done(); // return any withheld ajax or jserr events so they can be sent with EoL harvest
|
|
52
52
|
|
|
53
53
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
@@ -63,25 +63,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
63
63
|
// By default, a complete UI driven interaction requires event -> URL change -> DOM mod in that exact order.
|
|
64
64
|
(0, _registerHandler.registerHandler)('newUIEvent', event => this.startUIInteraction(event.type, Math.floor(event.timeStamp), event.target), this.featureName, this.ee);
|
|
65
65
|
(0, _registerHandler.registerHandler)('newURL', (timestamp, url) => {
|
|
66
|
-
//
|
|
67
|
-
// Because for 'popstate' triggered newUIEVent, by the time the event fires, the page URL has already changed so the previous URL is lost if not recorded.
|
|
66
|
+
// In the case of 'popstate' trigger, by the time the event fires, the URL has already changed, so we need to store what-will-be the *previous* URL for oldURL of next popstate ixn.
|
|
68
67
|
this.latestHistoryUrl = url;
|
|
69
68
|
this.interactionInProgress?.updateHistory(timestamp, url);
|
|
70
69
|
}, this.featureName, this.ee);
|
|
71
70
|
(0, _registerHandler.registerHandler)('newDom', timestamp => {
|
|
72
71
|
this.interactionInProgress?.updateDom(timestamp);
|
|
73
|
-
this.interactionInProgress?.
|
|
72
|
+
if (this.interactionInProgress?.seenHistoryAndDomChange()) this.interactionInProgress.done();
|
|
74
73
|
}, this.featureName, this.ee);
|
|
75
|
-
this.ee.on('long-task', task => {
|
|
76
|
-
if (!this.interactionInProgress?.watchLongtaskTimer) return; // no ixn in progress or it's not yet in a pending-finish state, as indicated by the lack of a watchLongtask timeout
|
|
77
|
-
clearTimeout(this.interactionInProgress.watchLongtaskTimer);
|
|
78
|
-
// Provided there isn't another long task, the ixn span will be extended to include this long task that would finish the interaction.
|
|
79
|
-
this.interactionInProgress.customEnd = task.end;
|
|
80
|
-
this.interactionInProgress.watchLongtaskTimer = setTimeout(() => this.interactionInProgress.done(), _constants.NO_LONG_TASK_WINDOW);
|
|
81
|
-
|
|
82
|
-
// Report metric on frequency of ixn extension due to long task
|
|
83
|
-
this.reportSupportabilityMetric('SoftNav/Interaction/Extended');
|
|
84
|
-
});
|
|
85
74
|
this.#registerApiHandlers();
|
|
86
75
|
(0, _registerHandler.registerHandler)('ajax', this.#handleAjaxEvent.bind(this), this.featureName, this.ee);
|
|
87
76
|
(0, _registerHandler.registerHandler)('jserror', this.#handleJserror.bind(this), this.featureName, this.ee);
|
|
@@ -100,11 +89,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
100
89
|
startUIInteraction(eventName, startedAt, sourceElem) {
|
|
101
90
|
// this is throttled by instrumentation so that it isn't excessively called
|
|
102
91
|
if (this.interactionInProgress?.createdByApi) return; // api-started interactions cannot be disrupted aka cancelled by UI events (and the vice versa applies as well)
|
|
103
|
-
|
|
104
|
-
if (eventName === _constants.POPSTATE_TRIGGER && this.interactionInProgress?.trigger !== _constants.POPSTATE_TRIGGER && startedAt - this.interactionInProgress?.start <= _constants.POPSTATE_MERGE_WINDOW) return;
|
|
105
|
-
if (this.interactionInProgress?.done() === false) return; // current in-progress is blocked from closing if true, e.g. by 'waitForEnd' api option; notice this cancels/finishes existing in-progress ixn
|
|
92
|
+
if (this.interactionInProgress?.done() === false) return; // current in-progress is blocked from closing, e.g. by 'waitForEnd' api option
|
|
106
93
|
|
|
107
|
-
const oldURL = eventName === _constants.
|
|
94
|
+
const oldURL = eventName === _constants.INTERACTION_TRIGGERS[3] ? this.latestHistoryUrl : undefined; // see related comment in 'newURL' handler above, 'popstate'
|
|
108
95
|
this.interactionInProgress = new _interaction.Interaction(eventName, startedAt, this.latestRouteSetByApi, oldURL);
|
|
109
96
|
if (eventName === _constants.INTERACTION_TRIGGERS[0]) {
|
|
110
97
|
// 'click'
|
|
@@ -167,30 +154,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
167
154
|
/**
|
|
168
155
|
* Handles or redirect ajax event based on the interaction, if any, that it's tied to.
|
|
169
156
|
* @param {Object} event see Ajax feature's storeXhr function for object definition
|
|
170
|
-
* @param {Object} metadata reference to the ajax context, used to pass long task info
|
|
171
157
|
*/
|
|
172
|
-
#handleAjaxEvent(event
|
|
158
|
+
#handleAjaxEvent(event) {
|
|
173
159
|
const associatedInteraction = this.getInteractionFor(event.startTime);
|
|
174
160
|
if (!associatedInteraction) {
|
|
175
161
|
// no interaction was happening when this ajax started, so give it back to Ajax feature for processing
|
|
176
162
|
(0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee);
|
|
177
163
|
} else {
|
|
178
|
-
if (associatedInteraction.status === _constants.INTERACTION_STATUS.FIN) processAjax
|
|
164
|
+
if (associatedInteraction.status === _constants.INTERACTION_STATUS.FIN) processAjax(event, associatedInteraction); // tack ajax onto the ixn object awaiting harvest
|
|
179
165
|
else {
|
|
180
166
|
// same thing as above, just at a later time -- if the interaction in progress is cancelled, just send the event back to ajax feat unmodified
|
|
181
|
-
associatedInteraction.on('finished', () => processAjax
|
|
167
|
+
associatedInteraction.on('finished', () => processAjax(event, associatedInteraction));
|
|
182
168
|
associatedInteraction.on('cancelled', () => (0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee));
|
|
183
169
|
}
|
|
184
170
|
}
|
|
185
|
-
function processAjax(event,
|
|
186
|
-
const
|
|
187
|
-
if (event.startTime > finalEnd) {
|
|
188
|
-
(0, _handle.handle)('returnAjax', [event], undefined, _features.FEATURE_NAMES.ajax, this.ee); // falling outside the final span, returned as standalone
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Metadata(ctx) should contain any long task end time associated with this XHR which should be up-to-date by the time the in-progress ixn & ajax children are being finalized for harvest.
|
|
193
|
-
const newNode = new _ajaxNode.AjaxNode(event, metadata);
|
|
171
|
+
function processAjax(event, parentInteraction) {
|
|
172
|
+
const newNode = new _ajaxNode.AjaxNode(event);
|
|
194
173
|
parentInteraction.addChild(newNode);
|
|
195
174
|
}
|
|
196
175
|
}
|
|
@@ -213,7 +192,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
213
192
|
} else {
|
|
214
193
|
// These callbacks may be added multiple times for an ixn, but just a single run will deal with all jserrors associated with the interaction.
|
|
215
194
|
// As such, be cautious not to use the params object since that's tied to one specific jserror and won't affect the rest of them.
|
|
216
|
-
associatedInteraction.on('finished', (0, _invoke.single)(() => (0, _handle.handle)('softNavFlush', [associatedInteraction.id, true, associatedInteraction.customAttributes
|
|
195
|
+
associatedInteraction.on('finished', (0, _invoke.single)(() => (0, _handle.handle)('softNavFlush', [associatedInteraction.id, true, associatedInteraction.customAttributes], undefined, _features.FEATURE_NAMES.jserrors, this.ee)));
|
|
217
196
|
associatedInteraction.on('cancelled', (0, _invoke.single)(() => (0, _handle.handle)('softNavFlush', [associatedInteraction.id, false, undefined], undefined, _features.FEATURE_NAMES.jserrors, this.ee))); // don't take custom attrs from cancelled ixns
|
|
218
197
|
}
|
|
219
198
|
}
|
|
@@ -228,7 +207,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
228
207
|
this.associatedInteraction = thisClass.getInteractionFor(time);
|
|
229
208
|
if (this.associatedInteraction?.trigger === _constants.IPL_TRIGGER_NAME) this.associatedInteraction = null; // the api get-interaction method cannot target IPL
|
|
230
209
|
if (!this.associatedInteraction) {
|
|
231
|
-
// This new api-driven interaction will be the target of any subsequent .interaction() call, until it is closed by EITHER .end() OR the regular
|
|
210
|
+
// This new api-driven interaction will be the target of any subsequent .interaction() call, until it is closed by EITHER .end() OR the regular seenHistoryAndDomChange process.
|
|
232
211
|
this.associatedInteraction = thisClass.interactionInProgress = new _interaction.Interaction(_constants.API_TRIGGER_NAME, time, thisClass.latestRouteSetByApi);
|
|
233
212
|
thisClass.domObserver.observe(document.body, {
|
|
234
213
|
attributes: true,
|
|
@@ -238,13 +217,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
238
217
|
}); // start observing for DOM changes like a regular UI-driven interaction
|
|
239
218
|
thisClass.setClosureHandlers();
|
|
240
219
|
}
|
|
241
|
-
if (waitForEnd === true)
|
|
242
|
-
this.associatedInteraction.keepOpenUntilEndApi = true;
|
|
243
|
-
clearTimeout(this.associatedInteraction.cancellationTimer); // get rid of the auto-cancel 30s timer for UI ixns when users specify waitForEnd manual override
|
|
244
|
-
}
|
|
220
|
+
if (waitForEnd === true) this.associatedInteraction.keepOpenUntilEndApi = true;
|
|
245
221
|
}, thisClass.featureName, thisClass.ee);
|
|
246
222
|
(0, _registerHandler.registerHandler)(INTERACTION_API + 'end', function (timeNow) {
|
|
247
|
-
this.associatedInteraction.done(timeNow
|
|
223
|
+
this.associatedInteraction.done(timeNow);
|
|
248
224
|
}, thisClass.featureName, thisClass.ee);
|
|
249
225
|
(0, _registerHandler.registerHandler)(INTERACTION_API + 'save', function () {
|
|
250
226
|
this.associatedInteraction.forceSave = true;
|
|
@@ -35,9 +35,7 @@ class Interaction extends _belNode.BelNode {
|
|
|
35
35
|
createdByApi = false;
|
|
36
36
|
keepOpenUntilEndApi = false;
|
|
37
37
|
onDone = [];
|
|
38
|
-
customEnd = 0;
|
|
39
38
|
cancellationTimer;
|
|
40
|
-
watchLongtaskTimer;
|
|
41
39
|
constructor(uiEvent, uiEventTimestamp, currentRouteKnown, currentUrl) {
|
|
42
40
|
super();
|
|
43
41
|
this.belType = _constants.NODE_TYPE.INTERACTION;
|
|
@@ -49,48 +47,36 @@ class Interaction extends _belNode.BelNode {
|
|
|
49
47
|
if (this.trigger === _constants.API_TRIGGER_NAME) this.createdByApi = true;
|
|
50
48
|
this.newURL = this.oldURL = currentUrl || _runtime.globalScope?.location.href;
|
|
51
49
|
}
|
|
52
|
-
updateHistory(timestamp, newUrl) {
|
|
53
|
-
if (this.domTimestamp > 0) return; // url is locked once ui>url>dom change sequence is seen
|
|
54
|
-
if (!newUrl || newUrl === this.oldURL) return; // url must be different for interaction heuristic to proceed
|
|
55
|
-
this.newURL = newUrl;
|
|
56
|
-
this.historyTimestamp = timestamp || (0, _now.now)();
|
|
57
|
-
}
|
|
58
50
|
updateDom(timestamp) {
|
|
59
|
-
if (!this.historyTimestamp || timestamp < this.historyTimestamp) return; // dom change must come after (any) url change, though this can be updated multiple times, taking the last dom timestamp
|
|
60
51
|
this.domTimestamp = timestamp || (0, _now.now)(); // default timestamp should be precise for accurate isActiveDuring calculations
|
|
61
52
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
clearTimeout(this.cancellationTimer); // "pending-finish" ixns cannot be auto cancelled anymore
|
|
69
|
-
this.watchLongtaskTimer ??= setTimeout(() => this.done(), _constants.NO_LONG_TASK_WINDOW);
|
|
70
|
-
// Notice that by not providing a specific end time to `.done()`, the ixn will use the dom timestamp in the event of no long task, which is what we want.
|
|
71
|
-
return true;
|
|
53
|
+
updateHistory(timestamp, newUrl) {
|
|
54
|
+
this.newURL = newUrl || '' + _runtime.globalScope?.location;
|
|
55
|
+
this.historyTimestamp = timestamp || (0, _now.now)();
|
|
56
|
+
}
|
|
57
|
+
seenHistoryAndDomChange() {
|
|
58
|
+
return this.historyTimestamp > 0 && this.domTimestamp > this.historyTimestamp; // URL must change before DOM does
|
|
72
59
|
}
|
|
73
60
|
on(event, cb) {
|
|
74
61
|
if (!this.eventSubscription.has(event)) throw new Error('Cannot subscribe to non pre-defined events.');
|
|
75
62
|
if (typeof cb !== 'function') throw new Error('Must supply function as callback.');
|
|
76
63
|
this.eventSubscription.get(event).push(cb);
|
|
77
64
|
}
|
|
78
|
-
done(customEndTime
|
|
79
|
-
// User could've mark this interaction--regardless UI or api started--as "don't close until .end() is called on it".
|
|
80
|
-
if (this.keepOpenUntilEndApi &&
|
|
65
|
+
done(customEndTime) {
|
|
66
|
+
// User could've mark this interaction--regardless UI or api started--as "don't close until .end() is called on it". Only .end provides a timestamp; the default flows do not.
|
|
67
|
+
if (this.keepOpenUntilEndApi && customEndTime === undefined) return false;
|
|
81
68
|
// If interaction is already closed, this is a no-op. However, returning true lets startUIInteraction know that it CAN start a new interaction, as this one is done.
|
|
82
|
-
if (this.status
|
|
83
|
-
clearTimeout(this.cancellationTimer); // clean up timers in case this is called by any flow that doesn't already do so
|
|
84
|
-
clearTimeout(this.watchLongtaskTimer);
|
|
69
|
+
if (this.status !== _constants.INTERACTION_STATUS.IP) return true;
|
|
85
70
|
this.onDone.forEach(apiProvidedCb => apiProvidedCb(this.customDataByApi)); // this interaction's .save or .ignore can still be set by these user provided callbacks for example
|
|
86
71
|
|
|
87
72
|
if (this.forceIgnore) this.#cancel(); // .ignore() always has precedence over save actions
|
|
88
|
-
else if (this.
|
|
73
|
+
else if (this.seenHistoryAndDomChange()) this.#finish(customEndTime); // then this should've already finished while it was the interactionInProgress, with a natural end time
|
|
89
74
|
else if (this.forceSave) this.#finish(customEndTime || performance.now()); // a manually saved ixn (did not fulfill conditions) must have a specified end time, if one wasn't provided
|
|
90
75
|
else this.#cancel();
|
|
91
76
|
return true;
|
|
92
77
|
}
|
|
93
|
-
#finish(customEndTime) {
|
|
78
|
+
#finish(customEndTime = 0) {
|
|
79
|
+
clearTimeout(this.cancellationTimer);
|
|
94
80
|
this.end = Math.max(this.domTimestamp, this.historyTimestamp, customEndTime);
|
|
95
81
|
this.status = _constants.INTERACTION_STATUS.FIN;
|
|
96
82
|
|
|
@@ -99,6 +85,7 @@ class Interaction extends _belNode.BelNode {
|
|
|
99
85
|
callbacks.forEach(fn => fn());
|
|
100
86
|
}
|
|
101
87
|
#cancel() {
|
|
88
|
+
clearTimeout(this.cancellationTimer);
|
|
102
89
|
this.status = _constants.INTERACTION_STATUS.CAN;
|
|
103
90
|
|
|
104
91
|
// Run all the callbacks listening to this interaction's potential cancellation.
|
|
@@ -109,13 +96,12 @@ class Interaction extends _belNode.BelNode {
|
|
|
109
96
|
/**
|
|
110
97
|
* Given a timestamp, determine if it falls within this interaction's span, i.e. if this was the active interaction during that time.
|
|
111
98
|
* For in-progress interactions, this only compares the time with the start of span. Cancelled interactions are not considered active at all.
|
|
112
|
-
* Pending-finish interactions are also considered still active wrt assigning ajax or jserrors to them during the wait period.
|
|
113
99
|
* @param {DOMHighResTimeStamp} timestamp
|
|
114
100
|
* @returns True or false boolean.
|
|
115
101
|
*/
|
|
116
102
|
isActiveDuring(timestamp) {
|
|
117
|
-
if (this.status === _constants.INTERACTION_STATUS.IP
|
|
118
|
-
return this.status === _constants.INTERACTION_STATUS.FIN && this.start <= timestamp &&
|
|
103
|
+
if (this.status === _constants.INTERACTION_STATUS.IP) return this.start <= timestamp;
|
|
104
|
+
return this.status === _constants.INTERACTION_STATUS.FIN && this.start <= timestamp && this.end > timestamp;
|
|
119
105
|
}
|
|
120
106
|
|
|
121
107
|
// Following are virtual properties overridden by a subclass:
|
|
@@ -143,10 +129,10 @@ class Interaction extends _belNode.BelNode {
|
|
|
143
129
|
// the very 1st ixn does not require offset so it should fallback to a 0 while rest is offset by the very 1st ixn's start
|
|
144
130
|
(0, _belSerializer.numeric)(this.end - this.start),
|
|
145
131
|
// end -- relative to start
|
|
146
|
-
(0, _belSerializer.numeric)(
|
|
147
|
-
//
|
|
148
|
-
(0, _belSerializer.numeric)(
|
|
149
|
-
// not relative
|
|
132
|
+
(0, _belSerializer.numeric)(this.callbackEnd),
|
|
133
|
+
// cbEnd -- relative to start; not used by BrowserInteraction events
|
|
134
|
+
(0, _belSerializer.numeric)(this.callbackDuration),
|
|
135
|
+
// not relative
|
|
150
136
|
addString(this.trigger), addString((0, _cleanUrl.cleanURL)(this.initialPageURL, true)), addString((0, _cleanUrl.cleanURL)(this.oldURL, true)), addString((0, _cleanUrl.cleanURL)(this.newURL, true)), addString(this.customName), ixnType, (0, _belSerializer.nullable)(this.queueTime, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.appTime, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.oldRoute, addString, true) + (0, _belSerializer.nullable)(this.newRoute, addString, true) + addString(this.id), addString(this.nodeId), (0, _belSerializer.nullable)(this.firstPaint, _belSerializer.numeric, true) + (0, _belSerializer.nullable)(this.firstContentfulPaint, _belSerializer.numeric)];
|
|
151
137
|
const customAttributes = {
|
|
152
138
|
...agentRef.info.jsAttributes,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.NODE_TYPE = exports.IPL_TRIGGER_NAME = exports.INTERACTION_TYPE = exports.INTERACTION_TRIGGERS = exports.INTERACTION_STATUS = exports.FEATURE_NAME = exports.API_TRIGGER_NAME = void 0;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
8
|
/**
|
|
9
9
|
* Copyright 2020-2025 New Relic, Inc. All rights reserved.
|
|
@@ -14,15 +14,13 @@ const INTERACTION_TRIGGERS = exports.INTERACTION_TRIGGERS = ['click',
|
|
|
14
14
|
// e.g. user clicks link or the page back/forward buttons
|
|
15
15
|
'keydown',
|
|
16
16
|
// e.g. user presses left and right arrow key to switch between displayed photo gallery
|
|
17
|
-
'submit'
|
|
17
|
+
'submit',
|
|
18
|
+
// e.g. user clicks submit butotn or presses enter while editing a form field
|
|
19
|
+
'popstate' // history api is used to navigate back and forward
|
|
18
20
|
];
|
|
19
|
-
const POPSTATE_TRIGGER = exports.POPSTATE_TRIGGER = 'popstate'; // e.g. user clicks browser back/forward button or history API is used programmatically
|
|
20
21
|
const API_TRIGGER_NAME = exports.API_TRIGGER_NAME = 'api';
|
|
21
22
|
const IPL_TRIGGER_NAME = exports.IPL_TRIGGER_NAME = 'initialPageLoad';
|
|
22
23
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.softNav;
|
|
23
|
-
const NO_LONG_TASK_WINDOW = exports.NO_LONG_TASK_WINDOW = 5000; // purpose is to wait 5 seconds wherein no long task is detected
|
|
24
|
-
const POPSTATE_MERGE_WINDOW = exports.POPSTATE_MERGE_WINDOW = 500; // "coalesce" (discard) a popstate that happen within this period following an INTERACTION_TRIGGER opening ixn, e.g. click->popstate
|
|
25
|
-
|
|
26
24
|
const INTERACTION_TYPE = exports.INTERACTION_TYPE = {
|
|
27
25
|
INITIAL_PAGE_LOAD: '',
|
|
28
26
|
ROUTE_CHANGE: 1,
|
|
@@ -36,8 +34,6 @@ const NODE_TYPE = exports.NODE_TYPE = {
|
|
|
36
34
|
};
|
|
37
35
|
const INTERACTION_STATUS = exports.INTERACTION_STATUS = {
|
|
38
36
|
IP: 'in progress',
|
|
39
|
-
PF: 'pending finish',
|
|
40
|
-
// interaction meets the hard criteria but is awaiting flexible conditions to fully finish
|
|
41
37
|
FIN: 'finished',
|
|
42
38
|
CAN: 'cancelled'
|
|
43
39
|
};
|
|
@@ -36,22 +36,19 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
36
36
|
if (!_runtime.isBrowserScope || !(0, _nreum.gosNREUMOriginals)().o.MO) return; // soft navigations is not supported outside web env or browsers without the mutation observer API
|
|
37
37
|
|
|
38
38
|
const historyEE = (0, _wrapHistory.wrapHistory)(this.ee);
|
|
39
|
-
try {
|
|
40
|
-
this.removeOnAbort = new AbortController();
|
|
41
|
-
} catch (e) {}
|
|
42
39
|
_constants.INTERACTION_TRIGGERS.forEach(trigger => {
|
|
43
40
|
(0, _eventListenerOpts.windowAddEventListener)(trigger, evt => {
|
|
44
41
|
processUserInteraction(evt);
|
|
45
|
-
}, true
|
|
42
|
+
}, true);
|
|
46
43
|
});
|
|
47
44
|
const trackURLChange = () => (0, _handle.handle)('newURL', [(0, _now.now)(), '' + window.location], undefined, this.featureName, this.ee);
|
|
48
45
|
historyEE.on('pushState-end', trackURLChange);
|
|
49
46
|
historyEE.on('replaceState-end', trackURLChange);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
try {
|
|
48
|
+
this.removeOnAbort = new AbortController();
|
|
49
|
+
} catch (e) {}
|
|
50
|
+
const trackURLChangeEvent = evt => (0, _handle.handle)('newURL', [evt.timeStamp, '' + window.location], undefined, this.featureName, this.ee);
|
|
51
|
+
(0, _eventListenerOpts.windowAddEventListener)('popstate', trackURLChangeEvent, true, this.removeOnAbort?.signal);
|
|
55
52
|
let oncePerFrame = false; // attempt to reduce dom noice since the observer runs very frequently with below options
|
|
56
53
|
const domObserver = new ((0, _nreum.gosNREUMOriginals)().o.MO)((domChanges, observer) => {
|
|
57
54
|
if (oncePerFrame) return;
|
|
@@ -27,10 +27,7 @@ if (isBrowserScope) {
|
|
|
27
27
|
resourceLoadDuration: attribution.resourceLoadDuration,
|
|
28
28
|
resourceLoadTime: attribution.resourceLoadDuration,
|
|
29
29
|
// kept for NR backwards compatibility, deprecated in v3->v4
|
|
30
|
-
elementRenderDelay: attribution.elementRenderDelay
|
|
31
|
-
...(attribution.navigationEntry && {
|
|
32
|
-
pageUrl: cleanURL(attribution.navigationEntry.name)
|
|
33
|
-
}) // used to ensure the LCP gets the correct URL at harvest time if a soft nav has occurred before page load
|
|
30
|
+
elementRenderDelay: attribution.elementRenderDelay
|
|
34
31
|
};
|
|
35
32
|
if (attribution.url) attrs.elUrl = cleanURL(attribution.url);
|
|
36
33
|
if (lcpEntry.element?.tagName) attrs.elTag = lcpEntry.element.tagName;
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import { ee } from '../event-emitter/contextual-ee';
|
|
11
11
|
import { bundleId } from '../ids/bundle-id';
|
|
12
12
|
export const flag = "nr@original:".concat(bundleId);
|
|
13
|
-
const LONG_TASK_THRESHOLD = 50;
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* A convenience alias of `hasOwnProperty`.
|
|
@@ -88,7 +87,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
88
87
|
// Warning: start events may mutate args!
|
|
89
88
|
safeEmit(prefix + 'start', [args, originalThis, methodName], ctx, bubble);
|
|
90
89
|
const fnStartTime = performance.now();
|
|
91
|
-
let fnEndTime;
|
|
90
|
+
let fnEndTime = fnStartTime;
|
|
92
91
|
try {
|
|
93
92
|
result = fn.apply(originalThis, args);
|
|
94
93
|
fnEndTime = performance.now();
|
|
@@ -102,20 +101,16 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
102
101
|
} finally {
|
|
103
102
|
const duration = fnEndTime - fnStartTime;
|
|
104
103
|
const task = {
|
|
105
|
-
start: fnStartTime,
|
|
106
|
-
end: fnEndTime,
|
|
107
104
|
duration,
|
|
108
|
-
isLongTask: duration >=
|
|
105
|
+
isLongTask: duration >= 50,
|
|
109
106
|
methodName,
|
|
110
107
|
thrownError
|
|
111
108
|
// could add more properties here later if needed by downstream features
|
|
112
109
|
};
|
|
113
110
|
// standalone long task message
|
|
114
|
-
if (task.isLongTask)
|
|
115
|
-
safeEmit('long-task', [task, originalThis], ctx, bubble);
|
|
116
|
-
}
|
|
111
|
+
if (task.isLongTask) safeEmit('long-task', [task], ctx, bubble);
|
|
117
112
|
// -end message also includes the task execution info
|
|
118
|
-
safeEmit(prefix + 'end', [args, originalThis, result], ctx, bubble);
|
|
113
|
+
safeEmit(prefix + 'end', [args, originalThis, result, task], ctx, bubble);
|
|
119
114
|
}
|
|
120
115
|
}
|
|
121
116
|
}
|
|
@@ -11,7 +11,6 @@ import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
|
11
11
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
12
12
|
import { parseGQL } from './gql';
|
|
13
13
|
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer';
|
|
14
|
-
import { gosNREUMOriginals } from '../../../common/window/nreum';
|
|
15
14
|
export class Aggregate extends AggregateBase {
|
|
16
15
|
static featureName = FEATURE_NAME;
|
|
17
16
|
constructor(agentRef) {
|
|
@@ -43,13 +42,6 @@ export class Aggregate extends AggregateBase {
|
|
|
43
42
|
// the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
|
|
44
43
|
classThis.storeXhr(...arguments, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
|
|
45
44
|
}, this.featureName, this.ee);
|
|
46
|
-
this.ee.on('long-task', (task, originator) => {
|
|
47
|
-
if (originator instanceof gosNREUMOriginals().o.XHR) {
|
|
48
|
-
// any time a long task from XHR callback is observed, update the end time for soft nav use
|
|
49
|
-
const xhrMetadata = this.ee.context(originator);
|
|
50
|
-
xhrMetadata.latestLongtaskEnd = task.end;
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
45
|
this.waitForFlags([]).then(() => this.drain());
|
|
54
46
|
}
|
|
55
47
|
storeXhr(params, metrics, startTime, endTime, type, ctx) {
|
|
@@ -109,8 +101,8 @@ export class Aggregate extends AggregateBase {
|
|
|
109
101
|
if (event.gql) this.reportSupportabilityMetric('Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length);
|
|
110
102
|
const softNavInUse = Boolean(this.agentRef.features?.[FEATURE_NAMES.softNav]);
|
|
111
103
|
if (softNavInUse) {
|
|
112
|
-
// For newer soft nav (when running), pass the event
|
|
113
|
-
handle('ajax', [event
|
|
104
|
+
// For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
|
|
105
|
+
handle('ajax', [event], undefined, FEATURE_NAMES.softNav, this.ee);
|
|
114
106
|
} else if (ctx.spaNode) {
|
|
115
107
|
// For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
116
108
|
const interactionId = ctx.spaNode.interaction.id;
|
|
@@ -98,7 +98,6 @@ function subscribeToEvents(agentRef, ee, handler, dt) {
|
|
|
98
98
|
ctx.loadCaptureCalled = false;
|
|
99
99
|
ctx.params = this.params || {};
|
|
100
100
|
ctx.metrics = this.metrics || {};
|
|
101
|
-
ctx.latestLongtaskEnd = 0;
|
|
102
101
|
xhr.addEventListener('load', function (event) {
|
|
103
102
|
captureXhrData(ctx, xhr);
|
|
104
103
|
}, eventListenerOpts(false));
|