@newrelic/browser-agent 1.276.0 → 1.277.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 +12 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/features/generic_events/aggregate/index.js +14 -3
- package/dist/cjs/features/generic_events/constants.js +2 -1
- package/dist/cjs/features/soft_navigations/aggregate/index.js +28 -11
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +12 -12
- package/dist/cjs/features/soft_navigations/constants.js +5 -2
- package/dist/cjs/loaders/api/api-methods.js +1 -1
- package/dist/cjs/loaders/api/api.js +2 -1
- package/dist/cjs/loaders/micro-agent-base.js +10 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/features/generic_events/aggregate/index.js +15 -4
- package/dist/esm/features/generic_events/constants.js +1 -0
- package/dist/esm/features/soft_navigations/aggregate/index.js +29 -12
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +13 -13
- package/dist/esm/features/soft_navigations/constants.js +4 -1
- package/dist/esm/loaders/api/api-methods.js +1 -1
- package/dist/esm/loaders/api/api.js +2 -1
- package/dist/esm/loaders/micro-agent-base.js +10 -0
- package/dist/types/features/generic_events/aggregate/index.d.ts +1 -0
- package/dist/types/features/generic_events/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/generic_events/constants.d.ts +1 -0
- package/dist/types/features/generic_events/constants.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +1 -0
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +3 -3
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/constants.d.ts +1 -0
- package/dist/types/features/soft_navigations/constants.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +1 -0
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent-base.d.ts +7 -0
- package/dist/types/loaders/micro-agent-base.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/features/generic_events/aggregate/index.js +17 -4
- package/src/features/generic_events/constants.js +2 -0
- package/src/features/soft_navigations/aggregate/index.js +22 -11
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +2 -1
- package/src/features/soft_navigations/aggregate/interaction.js +13 -12
- package/src/features/soft_navigations/constants.js +3 -1
- package/src/loaders/api/api-methods.js +1 -1
- package/src/loaders/api/api.js +3 -1
- package/src/loaders/micro-agent-base.js +10 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.277.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.276.0...v1.277.0) (2024-12-18)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Add custom events API ([#1263](https://github.com/newrelic/newrelic-browser-agent/issues/1263)) ([9395415](https://github.com/newrelic/newrelic-browser-agent/commit/9395415d942b55e88e89438aa203c6a1642d9e6b))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Soft navigation bug fixes and new soft navigation tests ([#1268](https://github.com/newrelic/newrelic-browser-agent/issues/1268)) ([7624928](https://github.com/newrelic/newrelic-browser-agent/commit/762492896a7b96564269aab1aadeb6e44a4da242))
|
|
17
|
+
|
|
6
18
|
## [1.276.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.275.0...v1.276.0) (2024-12-16)
|
|
7
19
|
|
|
8
20
|
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = exports.VERSION = "1.
|
|
15
|
+
const VERSION = exports.VERSION = "1.277.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -12,7 +12,7 @@ exports.VERSION = exports.RRWEB_VERSION = exports.DIST_METHOD = exports.BUILD_EN
|
|
|
12
12
|
/**
|
|
13
13
|
* Exposes the version of the agent
|
|
14
14
|
*/
|
|
15
|
-
const VERSION = exports.VERSION = "1.
|
|
15
|
+
const VERSION = exports.VERSION = "1.277.0";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Exposes the build type of the agent
|
|
@@ -38,12 +38,20 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
this.trackSupportabilityMetrics();
|
|
41
|
+
(0, _registerHandler.registerHandler)('api-recordCustomEvent', (timestamp, eventType, attributes) => {
|
|
42
|
+
if (_constants.RESERVED_EVENT_TYPES.includes(eventType)) return (0, _console.warn)(46);
|
|
43
|
+
this.addEvent({
|
|
44
|
+
eventType,
|
|
45
|
+
timestamp: this.toEpoch(timestamp),
|
|
46
|
+
...attributes
|
|
47
|
+
});
|
|
48
|
+
}, this.featureName, this.ee);
|
|
41
49
|
if (agentRef.init.page_action.enabled) {
|
|
42
50
|
(0, _registerHandler.registerHandler)('api-addPageAction', (timestamp, name, attributes) => {
|
|
43
51
|
this.addEvent({
|
|
44
52
|
...attributes,
|
|
45
53
|
eventType: 'PageAction',
|
|
46
|
-
timestamp:
|
|
54
|
+
timestamp: this.toEpoch(timestamp),
|
|
47
55
|
timeSinceLoad: timestamp / 1000,
|
|
48
56
|
actionName: name,
|
|
49
57
|
referrerUrl: this.referrerUrl,
|
|
@@ -69,7 +77,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
69
77
|
} = aggregatedUserAction.event;
|
|
70
78
|
this.addEvent({
|
|
71
79
|
eventType: 'UserAction',
|
|
72
|
-
timestamp:
|
|
80
|
+
timestamp: this.toEpoch(timeStamp),
|
|
73
81
|
action: type,
|
|
74
82
|
actionCount: aggregatedUserAction.count,
|
|
75
83
|
actionDuration: aggregatedUserAction.relativeMs[aggregatedUserAction.relativeMs.length - 1],
|
|
@@ -130,7 +138,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
130
138
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
|
|
131
139
|
this.addEvent({
|
|
132
140
|
eventType: 'BrowserPerformance',
|
|
133
|
-
timestamp:
|
|
141
|
+
timestamp: this.toEpoch(entry.startTime),
|
|
134
142
|
entryName: (0, _cleanUrl.cleanURL)(entry.name),
|
|
135
143
|
entryDuration: entry.duration,
|
|
136
144
|
entryType: type,
|
|
@@ -262,6 +270,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
262
270
|
at: this.agentRef.info.atts
|
|
263
271
|
};
|
|
264
272
|
}
|
|
273
|
+
toEpoch(timestamp) {
|
|
274
|
+
return Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp));
|
|
275
|
+
}
|
|
265
276
|
trackSupportabilityMetrics() {
|
|
266
277
|
/** track usage SMs to improve these experimental features */
|
|
267
278
|
const configPerfTag = 'Config/Performance/';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = exports.FEATURE_FLAGS = void 0;
|
|
6
|
+
exports.RESERVED_EVENT_TYPES = exports.RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_EVENTS = exports.OBSERVED_WINDOW_EVENTS = exports.OBSERVED_EVENTS = exports.MAX_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = exports.FEATURE_NAME = exports.FEATURE_FLAGS = void 0;
|
|
7
7
|
var _features = require("../../loaders/features/features");
|
|
8
8
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.genericEvents;
|
|
9
9
|
const IDEAL_PAYLOAD_SIZE = exports.IDEAL_PAYLOAD_SIZE = 64000;
|
|
@@ -12,6 +12,7 @@ const OBSERVED_EVENTS = exports.OBSERVED_EVENTS = ['auxclick', 'click', 'copy',
|
|
|
12
12
|
const OBSERVED_WINDOW_EVENTS = exports.OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
|
|
13
13
|
const RAGE_CLICK_THRESHOLD_EVENTS = exports.RAGE_CLICK_THRESHOLD_EVENTS = 4;
|
|
14
14
|
const RAGE_CLICK_THRESHOLD_MS = exports.RAGE_CLICK_THRESHOLD_MS = 1000;
|
|
15
|
+
const RESERVED_EVENT_TYPES = exports.RESERVED_EVENT_TYPES = ['PageAction', 'UserAction', 'BrowserPerformance'];
|
|
15
16
|
const FEATURE_FLAGS = exports.FEATURE_FLAGS = {
|
|
16
17
|
MARKS: 'experimental.marks',
|
|
17
18
|
MEASURES: 'experimental.measures',
|
|
@@ -26,20 +26,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
26
26
|
this.interactionsToHarvest = this.events;
|
|
27
27
|
this.domObserver = domObserver;
|
|
28
28
|
this.initialPageLoadInteraction = new _initialPageLoadInteraction.InitialPageLoadInteraction(agentRef.agentIdentifier);
|
|
29
|
+
this.initialPageLoadInteraction.onDone.push(() => {
|
|
30
|
+
// this ensures the .end() method also works with iPL
|
|
31
|
+
this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
|
|
32
|
+
this.interactionsToHarvest.add(this.initialPageLoadInteraction);
|
|
33
|
+
this.initialPageLoadInteraction = null;
|
|
34
|
+
});
|
|
29
35
|
_timeToFirstByte.timeToFirstByte.subscribe(({
|
|
30
36
|
attrs
|
|
31
37
|
}) => {
|
|
32
38
|
const loadEventTime = attrs.navigationEntry.loadEventEnd;
|
|
33
|
-
this.initialPageLoadInteraction.forceSave = true;
|
|
34
39
|
this.initialPageLoadInteraction.done(loadEventTime);
|
|
35
|
-
this.interactionsToHarvest.add(this.initialPageLoadInteraction);
|
|
36
|
-
this.initialPageLoadInteraction = null;
|
|
37
40
|
// Report metric on the initial page load time
|
|
38
41
|
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['SoftNav/Interaction/InitialPageLoad/Duration/Ms', Math.round(loadEventTime)], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
39
42
|
});
|
|
40
43
|
this.latestRouteSetByApi = null;
|
|
41
44
|
this.interactionInProgress = null; // aside from the "page load" interaction, there can only ever be 1 ongoing at a time
|
|
42
|
-
|
|
45
|
+
this.latestHistoryUrl = null;
|
|
43
46
|
this.blocked = false;
|
|
44
47
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
45
48
|
if (spaOn) {
|
|
@@ -59,7 +62,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
59
62
|
|
|
60
63
|
// By default, a complete UI driven interaction requires event -> URL change -> DOM mod in that exact order.
|
|
61
64
|
(0, _registerHandler.registerHandler)('newUIEvent', event => this.startUIInteraction(event.type, Math.floor(event.timeStamp), event.target), this.featureName, this.ee);
|
|
62
|
-
(0, _registerHandler.registerHandler)('newURL', (timestamp, url) =>
|
|
65
|
+
(0, _registerHandler.registerHandler)('newURL', (timestamp, url) => {
|
|
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.
|
|
67
|
+
this.latestHistoryUrl = url;
|
|
68
|
+
this.interactionInProgress?.updateHistory(timestamp, url);
|
|
69
|
+
}, this.featureName, this.ee);
|
|
63
70
|
(0, _registerHandler.registerHandler)('newDom', timestamp => {
|
|
64
71
|
this.interactionInProgress?.updateDom(timestamp);
|
|
65
72
|
if (this.interactionInProgress?.seenHistoryAndDomChange()) this.interactionInProgress.done();
|
|
@@ -71,20 +78,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
71
78
|
serializer(eventBuffer) {
|
|
72
79
|
// The payload depacker takes the first ixn of a payload (if there are multiple ixns) and positively offset the subsequent ixns timestamps by that amount.
|
|
73
80
|
// In order to accurately portray the real start & end times of the 2nd & onward ixns, we hence need to negatively offset their start timestamps with that of the 1st ixn.
|
|
74
|
-
let firstIxnStartTime
|
|
81
|
+
let firstIxnStartTime;
|
|
75
82
|
const serializedIxnList = [];
|
|
76
83
|
for (const interaction of eventBuffer) {
|
|
77
84
|
serializedIxnList.push(interaction.serialize(firstIxnStartTime));
|
|
78
|
-
if (
|
|
85
|
+
if (firstIxnStartTime === undefined) firstIxnStartTime = Math.floor(interaction.start); // careful not to match or overwrite on 0 value!
|
|
79
86
|
}
|
|
80
87
|
return "bel.7;".concat(serializedIxnList.join(';'));
|
|
81
88
|
}
|
|
82
89
|
startUIInteraction(eventName, startedAt, sourceElem) {
|
|
83
90
|
// this is throttled by instrumentation so that it isn't excessively called
|
|
84
91
|
if (this.interactionInProgress?.createdByApi) return; // api-started interactions cannot be disrupted aka cancelled by UI events (and the vice versa applies as well)
|
|
85
|
-
if (this.interactionInProgress?.done() === false) return;
|
|
86
|
-
|
|
87
|
-
|
|
92
|
+
if (this.interactionInProgress?.done() === false) return; // current in-progress is blocked from closing, e.g. by 'waitForEnd' api option
|
|
93
|
+
|
|
94
|
+
const oldURL = eventName === _constants2.INTERACTION_TRIGGERS[3] ? this.latestHistoryUrl : undefined; // see related comment in 'newURL' handler above, 'popstate'
|
|
95
|
+
this.interactionInProgress = new _interaction.Interaction(this.agentIdentifier, eventName, startedAt, this.latestRouteSetByApi, oldURL);
|
|
96
|
+
if (eventName === _constants2.INTERACTION_TRIGGERS[0]) {
|
|
97
|
+
// 'click'
|
|
88
98
|
const sourceElemText = getActionText(sourceElem);
|
|
89
99
|
if (sourceElemText) this.interactionInProgress.customAttributes.actionText = sourceElemText;
|
|
90
100
|
}
|
|
@@ -130,7 +140,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
130
140
|
// reverse search for the latest completed interaction for efficiency
|
|
131
141
|
const finishedInteraction = interactionsBuffer[idx];
|
|
132
142
|
if (finishedInteraction.isActiveDuring(timestamp)) {
|
|
133
|
-
if (finishedInteraction.trigger !==
|
|
143
|
+
if (finishedInteraction.trigger !== _constants2.IPL_TRIGGER_NAME) return finishedInteraction;
|
|
134
144
|
// It's possible that a complete interaction occurs before page is fully loaded, so we need to consider if a route-change ixn may have overlapped this iPL
|
|
135
145
|
else saveIxn = finishedInteraction;
|
|
136
146
|
}
|
|
@@ -194,9 +204,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
194
204
|
// In here, 'this' refers to the EventContext specific to per InteractionHandle instance spawned by each .interaction() api call.
|
|
195
205
|
// Each api call aka IH instance would therefore retain a reference to either the in-progress interaction *at the time of the call* OR a new api-started interaction.
|
|
196
206
|
this.associatedInteraction = thisClass.getInteractionFor(time);
|
|
207
|
+
if (this.associatedInteraction?.trigger === _constants2.IPL_TRIGGER_NAME) this.associatedInteraction = null; // the api get-interaction method cannot target IPL
|
|
197
208
|
if (!this.associatedInteraction) {
|
|
198
209
|
// 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.
|
|
199
210
|
this.associatedInteraction = thisClass.interactionInProgress = new _interaction.Interaction(thisClass.agentIdentifier, _constants2.API_TRIGGER_NAME, time, thisClass.latestRouteSetByApi);
|
|
211
|
+
thisClass.domObserver.observe(document.body, {
|
|
212
|
+
attributes: true,
|
|
213
|
+
childList: true,
|
|
214
|
+
subtree: true,
|
|
215
|
+
characterData: true
|
|
216
|
+
}); // start observing for DOM changes like a regular UI-driven interaction
|
|
200
217
|
thisClass.setClosureHandlers();
|
|
201
218
|
}
|
|
202
219
|
if (waitForEnd === true) this.associatedInteraction.keepOpenUntilEndApi = true;
|
|
@@ -10,9 +10,10 @@ var _belSerializer = require("../../../common/serialize/bel-serializer");
|
|
|
10
10
|
var _firstPaint = require("../../../common/vitals/first-paint");
|
|
11
11
|
var _firstContentfulPaint = require("../../../common/vitals/first-contentful-paint");
|
|
12
12
|
var _info = require("../../../common/config/info");
|
|
13
|
+
var _constants = require("../constants");
|
|
13
14
|
class InitialPageLoadInteraction extends _interaction.Interaction {
|
|
14
15
|
constructor(agentIdentifier) {
|
|
15
|
-
super(agentIdentifier,
|
|
16
|
+
super(agentIdentifier, _constants.IPL_TRIGGER_NAME, 0, null);
|
|
16
17
|
const agentInfo = (0, _info.getInfo)(agentIdentifier);
|
|
17
18
|
this.queueTime = agentInfo.queueTime;
|
|
18
19
|
this.appTime = agentInfo.applicationTime;
|
|
@@ -18,8 +18,6 @@ var _belNode = require("./bel-node");
|
|
|
18
18
|
class Interaction extends _belNode.BelNode {
|
|
19
19
|
id = (0, _uniqueId.generateUuid)(); // unique id that is serialized and used to link interactions with errors
|
|
20
20
|
initialPageURL = _runtime.initialLocation;
|
|
21
|
-
oldURL = '' + _runtime.globalScope?.location;
|
|
22
|
-
newURL = '' + _runtime.globalScope?.location;
|
|
23
21
|
customName;
|
|
24
22
|
customAttributes = {};
|
|
25
23
|
customDataByApi = {};
|
|
@@ -34,7 +32,7 @@ class Interaction extends _belNode.BelNode {
|
|
|
34
32
|
keepOpenUntilEndApi = false;
|
|
35
33
|
onDone = [];
|
|
36
34
|
cancellationTimer;
|
|
37
|
-
constructor(agentIdentifier, uiEvent, uiEventTimestamp, currentRouteKnown) {
|
|
35
|
+
constructor(agentIdentifier, uiEvent, uiEventTimestamp, currentRouteKnown, currentUrl) {
|
|
38
36
|
super(agentIdentifier);
|
|
39
37
|
this.belType = _constants.NODE_TYPE.INTERACTION;
|
|
40
38
|
this.trigger = uiEvent;
|
|
@@ -43,6 +41,7 @@ class Interaction extends _belNode.BelNode {
|
|
|
43
41
|
this.eventSubscription = new Map([['finished', []], ['cancelled', []]]);
|
|
44
42
|
this.forceSave = this.forceIgnore = false;
|
|
45
43
|
if (this.trigger === _constants.API_TRIGGER_NAME) this.createdByApi = true;
|
|
44
|
+
this.newURL = this.oldURL = currentUrl || _runtime.globalScope?.location.href;
|
|
46
45
|
}
|
|
47
46
|
updateDom(timestamp) {
|
|
48
47
|
this.domTimestamp = timestamp || (0, _now.now)(); // default timestamp should be precise for accurate isActiveDuring calculations
|
|
@@ -62,6 +61,8 @@ class Interaction extends _belNode.BelNode {
|
|
|
62
61
|
done(customEndTime) {
|
|
63
62
|
// 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.
|
|
64
63
|
if (this.keepOpenUntilEndApi && customEndTime === undefined) return false;
|
|
64
|
+
// 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.
|
|
65
|
+
if (this.status !== _constants.INTERACTION_STATUS.IP) return true;
|
|
65
66
|
this.onDone.forEach(apiProvidedCb => apiProvidedCb(this.customDataByApi)); // this interaction's .save or .ignore can still be set by these user provided callbacks for example
|
|
66
67
|
|
|
67
68
|
if (this.forceIgnore) this.#cancel(); // .ignore() always has precedence over save actions
|
|
@@ -71,7 +72,6 @@ class Interaction extends _belNode.BelNode {
|
|
|
71
72
|
return true;
|
|
72
73
|
}
|
|
73
74
|
#finish(customEndTime = 0) {
|
|
74
|
-
if (this.status !== _constants.INTERACTION_STATUS.IP) return; // disallow this call if the ixn is already done aka not in-progress
|
|
75
75
|
clearTimeout(this.cancellationTimer);
|
|
76
76
|
this.end = Math.max(this.domTimestamp, this.historyTimestamp, customEndTime);
|
|
77
77
|
this.customAttributes = {
|
|
@@ -85,7 +85,6 @@ class Interaction extends _belNode.BelNode {
|
|
|
85
85
|
callbacks.forEach(fn => fn());
|
|
86
86
|
}
|
|
87
87
|
#cancel() {
|
|
88
|
-
if (this.status !== _constants.INTERACTION_STATUS.IP) return; // disallow this call if the ixn is already done aka not in-progress
|
|
89
88
|
clearTimeout(this.cancellationTimer);
|
|
90
89
|
this.status = _constants.INTERACTION_STATUS.CAN;
|
|
91
90
|
|
|
@@ -102,7 +101,7 @@ class Interaction extends _belNode.BelNode {
|
|
|
102
101
|
*/
|
|
103
102
|
isActiveDuring(timestamp) {
|
|
104
103
|
if (this.status === _constants.INTERACTION_STATUS.IP) return this.start <= timestamp;
|
|
105
|
-
return this.status === _constants.INTERACTION_STATUS.FIN && this.start <= timestamp && this.end
|
|
104
|
+
return this.status === _constants.INTERACTION_STATUS.FIN && this.start <= timestamp && this.end > timestamp;
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
// Following are virtual properties overridden by a subclass:
|
|
@@ -110,16 +109,17 @@ class Interaction extends _belNode.BelNode {
|
|
|
110
109
|
get firstContentfulPaint() {}
|
|
111
110
|
get navTiming() {}
|
|
112
111
|
serialize(firstStartTimeOfPayload) {
|
|
112
|
+
const isFirstIxnOfPayload = firstStartTimeOfPayload === undefined;
|
|
113
113
|
const addString = (0, _belSerializer.getAddStringContext)(this.agentIdentifier);
|
|
114
114
|
const nodeList = [];
|
|
115
115
|
let ixnType;
|
|
116
|
-
if (this.trigger ===
|
|
116
|
+
if (this.trigger === _constants.IPL_TRIGGER_NAME) ixnType = _constants.INTERACTION_TYPE.INITIAL_PAGE_LOAD;else if (this.newURL !== this.oldURL) ixnType = _constants.INTERACTION_TYPE.ROUTE_CHANGE;else ixnType = _constants.INTERACTION_TYPE.UNSPECIFIED;
|
|
117
117
|
|
|
118
118
|
// IMPORTANT: The order in which addString is called matters and correlates to the order in which string shows up in the harvest payload. Do not re-order the following code.
|
|
119
119
|
const fields = [(0, _belSerializer.numeric)(this.belType), 0,
|
|
120
120
|
// this will be overwritten below with number of attached nodes
|
|
121
|
-
(0, _belSerializer.numeric)(this.start - firstStartTimeOfPayload),
|
|
122
|
-
//
|
|
121
|
+
(0, _belSerializer.numeric)(this.start - (isFirstIxnOfPayload ? 0 : firstStartTimeOfPayload)),
|
|
122
|
+
// 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
|
|
123
123
|
(0, _belSerializer.numeric)(this.end - this.start),
|
|
124
124
|
// end -- relative to start
|
|
125
125
|
(0, _belSerializer.numeric)(this.callbackEnd),
|
|
@@ -130,9 +130,9 @@ class Interaction extends _belNode.BelNode {
|
|
|
130
130
|
const allAttachedNodes = (0, _belSerializer.addCustomAttributes)(this.customAttributes || {}, addString); // start with all custom attributes
|
|
131
131
|
if ((0, _info.getInfo)(this.agentIdentifier).atts) allAttachedNodes.push('a,' + addString((0, _info.getInfo)(this.agentIdentifier).atts)); // add apm provided attributes
|
|
132
132
|
/* Querypack encoder+decoder quirkiness:
|
|
133
|
-
- If first ixn node of payload is being processed,
|
|
134
|
-
- Else for subsequent
|
|
135
|
-
this.children.forEach(node => allAttachedNodes.push(node.serialize(
|
|
133
|
+
- If first ixn node of payload is being processed, its children's start time must be offset by this node's start. (firstStartTime should be undefined.)
|
|
134
|
+
- Else for subsequent ixns in the same payload, we go back to using that first ixn node's start to offset their children's start. */
|
|
135
|
+
this.children.forEach(node => allAttachedNodes.push(node.serialize(isFirstIxnOfPayload ? this.start : firstStartTimeOfPayload))); // recursively add the serialized string of every child of this (ixn) bel node
|
|
136
136
|
|
|
137
137
|
fields[1] = (0, _belSerializer.numeric)(allAttachedNodes.length);
|
|
138
138
|
nodeList.push(fields);
|
|
@@ -3,15 +3,18 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.NODE_TYPE = exports.INTERACTION_TYPE = exports.INTERACTION_TRIGGERS = exports.INTERACTION_STATUS = exports.FEATURE_NAME = exports.API_TRIGGER_NAME = void 0;
|
|
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
|
const INTERACTION_TRIGGERS = exports.INTERACTION_TRIGGERS = ['click',
|
|
9
9
|
// e.g. user clicks link or the page back/forward buttons
|
|
10
10
|
'keydown',
|
|
11
11
|
// e.g. user presses left and right arrow key to switch between displayed photo gallery
|
|
12
|
-
'submit'
|
|
12
|
+
'submit',
|
|
13
|
+
// e.g. user clicks submit butotn or presses enter while editing a form field
|
|
14
|
+
'popstate' // history api is used to navigate back and forward
|
|
13
15
|
];
|
|
14
16
|
const API_TRIGGER_NAME = exports.API_TRIGGER_NAME = 'api';
|
|
17
|
+
const IPL_TRIGGER_NAME = exports.IPL_TRIGGER_NAME = 'initialPageLoad';
|
|
15
18
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.softNav;
|
|
16
19
|
const INTERACTION_TYPE = exports.INTERACTION_TYPE = {
|
|
17
20
|
INITIAL_PAGE_LOAD: '',
|
|
@@ -5,5 +5,5 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.asyncApiMethods = exports.apiMethods = void 0;
|
|
7
7
|
var _constants = require("../../features/session_replay/constants");
|
|
8
|
-
const apiMethods = exports.apiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', _constants.SR_EVENT_EMITTER_TYPES.RECORD, _constants.SR_EVENT_EMITTER_TYPES.PAUSE, 'log', 'wrapLogger'];
|
|
8
|
+
const apiMethods = exports.apiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease', 'recordCustomEvent', 'addPageAction', 'setCurrentRouteName', 'setPageViewName', 'setCustomAttribute', 'interaction', 'noticeError', 'setUserId', 'setApplicationVersion', 'start', _constants.SR_EVENT_EMITTER_TYPES.RECORD, _constants.SR_EVENT_EMITTER_TYPES.PAUSE, 'log', 'wrapLogger'];
|
|
9
9
|
const asyncApiMethods = exports.asyncApiMethods = ['setErrorHandler', 'finished', 'addToTrace', 'addRelease'];
|
|
@@ -80,6 +80,7 @@ function setAPI(agentIdentifier, forceDrain, runSoftNavOverSpa = false) {
|
|
|
80
80
|
apiInterface[fnName] = apiCall(prefix, fnName, true, 'api');
|
|
81
81
|
});
|
|
82
82
|
apiInterface.addPageAction = apiCall(prefix, 'addPageAction', true, _features.FEATURE_NAMES.genericEvents);
|
|
83
|
+
apiInterface.recordCustomEvent = apiCall(prefix, 'recordCustomEvent', true, _features.FEATURE_NAMES.genericEvents);
|
|
83
84
|
apiInterface.setPageViewName = function (name, host) {
|
|
84
85
|
if (typeof name !== 'string') return;
|
|
85
86
|
if (name.charAt(0) !== '/') name = '/' + name;
|
|
@@ -198,7 +199,7 @@ function setAPI(agentIdentifier, forceDrain, runSoftNavOverSpa = false) {
|
|
|
198
199
|
function apiCall(prefix, name, notSpa, bufferGroup) {
|
|
199
200
|
return function () {
|
|
200
201
|
(0, _handle.handle)(_constants.SUPPORTABILITY_METRIC_CHANNEL, ['API/' + name + '/called'], undefined, _features.FEATURE_NAMES.metrics, instanceEE);
|
|
201
|
-
if (bufferGroup) (0, _handle.handle)(prefix + name, [(0, _now.now)(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE); // no bufferGroup means only the SM is emitted
|
|
202
|
+
if (bufferGroup) (0, _handle.handle)(prefix + name, [notSpa ? (0, _now.now)() : performance.now(), ...arguments], notSpa ? null : this, bufferGroup, instanceEE); // no bufferGroup means only the SM is emitted
|
|
202
203
|
return notSpa ? undefined : this; // returns the InteractionHandle which allows these methods to be chained
|
|
203
204
|
};
|
|
204
205
|
}
|
|
@@ -33,6 +33,16 @@ class MicroAgentBase {
|
|
|
33
33
|
return this.#callMethod('addPageAction', name, attributes);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Records a custom event with a specified eventType and attributes.
|
|
38
|
+
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/recordCustomEvent/}
|
|
39
|
+
* @param {string} eventType The eventType to store the event as.
|
|
40
|
+
* @param {object} [attributes] JSON object with one or more key/value pairs. For example: {key:"value"}.
|
|
41
|
+
*/
|
|
42
|
+
recordCustomEvent(eventType, attributes) {
|
|
43
|
+
return this.#callMethod('recordCustomEvent', eventType, attributes);
|
|
44
|
+
}
|
|
45
|
+
|
|
36
46
|
/**
|
|
37
47
|
* Groups page views to help URL structure or to capture the URL's routing information.
|
|
38
48
|
* {@link https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setpageviewname/}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { stringify } from '../../../common/util/stringify';
|
|
6
6
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
7
7
|
import { cleanURL } from '../../../common/url/clean-url';
|
|
8
|
-
import { FEATURE_NAME } from '../constants';
|
|
8
|
+
import { FEATURE_NAME, RESERVED_EVENT_TYPES } from '../constants';
|
|
9
9
|
import { globalScope, initialLocation, isBrowserScope } from '../../../common/constants/runtime';
|
|
10
10
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
11
11
|
import { warn } from '../../../common/util/console';
|
|
@@ -31,12 +31,20 @@ export class Aggregate extends AggregateBase {
|
|
|
31
31
|
return;
|
|
32
32
|
}
|
|
33
33
|
this.trackSupportabilityMetrics();
|
|
34
|
+
registerHandler('api-recordCustomEvent', (timestamp, eventType, attributes) => {
|
|
35
|
+
if (RESERVED_EVENT_TYPES.includes(eventType)) return warn(46);
|
|
36
|
+
this.addEvent({
|
|
37
|
+
eventType,
|
|
38
|
+
timestamp: this.toEpoch(timestamp),
|
|
39
|
+
...attributes
|
|
40
|
+
});
|
|
41
|
+
}, this.featureName, this.ee);
|
|
34
42
|
if (agentRef.init.page_action.enabled) {
|
|
35
43
|
registerHandler('api-addPageAction', (timestamp, name, attributes) => {
|
|
36
44
|
this.addEvent({
|
|
37
45
|
...attributes,
|
|
38
46
|
eventType: 'PageAction',
|
|
39
|
-
timestamp:
|
|
47
|
+
timestamp: this.toEpoch(timestamp),
|
|
40
48
|
timeSinceLoad: timestamp / 1000,
|
|
41
49
|
actionName: name,
|
|
42
50
|
referrerUrl: this.referrerUrl,
|
|
@@ -62,7 +70,7 @@ export class Aggregate extends AggregateBase {
|
|
|
62
70
|
} = aggregatedUserAction.event;
|
|
63
71
|
this.addEvent({
|
|
64
72
|
eventType: 'UserAction',
|
|
65
|
-
timestamp:
|
|
73
|
+
timestamp: this.toEpoch(timeStamp),
|
|
66
74
|
action: type,
|
|
67
75
|
actionCount: aggregatedUserAction.count,
|
|
68
76
|
actionDuration: aggregatedUserAction.relativeMs[aggregatedUserAction.relativeMs.length - 1],
|
|
@@ -123,7 +131,7 @@ export class Aggregate extends AggregateBase {
|
|
|
123
131
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Generic/Performance/' + type + '/Seen']);
|
|
124
132
|
this.addEvent({
|
|
125
133
|
eventType: 'BrowserPerformance',
|
|
126
|
-
timestamp:
|
|
134
|
+
timestamp: this.toEpoch(entry.startTime),
|
|
127
135
|
entryName: cleanURL(entry.name),
|
|
128
136
|
entryDuration: entry.duration,
|
|
129
137
|
entryType: type,
|
|
@@ -255,6 +263,9 @@ export class Aggregate extends AggregateBase {
|
|
|
255
263
|
at: this.agentRef.info.atts
|
|
256
264
|
};
|
|
257
265
|
}
|
|
266
|
+
toEpoch(timestamp) {
|
|
267
|
+
return Math.floor(this.agentRef.runtime.timeKeeper.correctRelativeTimestamp(timestamp));
|
|
268
|
+
}
|
|
258
269
|
trackSupportabilityMetrics() {
|
|
259
270
|
/** track usage SMs to improve these experimental features */
|
|
260
271
|
const configPerfTag = 'Config/Performance/';
|
|
@@ -6,6 +6,7 @@ export const OBSERVED_EVENTS = ['auxclick', 'click', 'copy', 'keydown', 'paste',
|
|
|
6
6
|
export const OBSERVED_WINDOW_EVENTS = ['focus', 'blur'];
|
|
7
7
|
export const RAGE_CLICK_THRESHOLD_EVENTS = 4;
|
|
8
8
|
export const RAGE_CLICK_THRESHOLD_MS = 1000;
|
|
9
|
+
export const RESERVED_EVENT_TYPES = ['PageAction', 'UserAction', 'BrowserPerformance'];
|
|
9
10
|
export const FEATURE_FLAGS = {
|
|
10
11
|
MARKS: 'experimental.marks',
|
|
11
12
|
MEASURES: 'experimental.measures',
|
|
@@ -6,7 +6,7 @@ import { timeToFirstByte } from '../../../common/vitals/time-to-first-byte';
|
|
|
6
6
|
import { FEATURE_NAMES, FEATURE_TO_ENDPOINT } from '../../../loaders/features/features';
|
|
7
7
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
|
|
8
8
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
9
|
-
import { API_TRIGGER_NAME, FEATURE_NAME, INTERACTION_STATUS } from '../constants';
|
|
9
|
+
import { API_TRIGGER_NAME, FEATURE_NAME, INTERACTION_STATUS, INTERACTION_TRIGGERS, IPL_TRIGGER_NAME } from '../constants';
|
|
10
10
|
import { AjaxNode } from './ajax-node';
|
|
11
11
|
import { InitialPageLoadInteraction } from './initial-page-load-interaction';
|
|
12
12
|
import { Interaction } from './interaction';
|
|
@@ -20,20 +20,23 @@ export class Aggregate extends AggregateBase {
|
|
|
20
20
|
this.interactionsToHarvest = this.events;
|
|
21
21
|
this.domObserver = domObserver;
|
|
22
22
|
this.initialPageLoadInteraction = new InitialPageLoadInteraction(agentRef.agentIdentifier);
|
|
23
|
+
this.initialPageLoadInteraction.onDone.push(() => {
|
|
24
|
+
// this ensures the .end() method also works with iPL
|
|
25
|
+
this.initialPageLoadInteraction.forceSave = true; // unless forcibly ignored, iPL always finish by default
|
|
26
|
+
this.interactionsToHarvest.add(this.initialPageLoadInteraction);
|
|
27
|
+
this.initialPageLoadInteraction = null;
|
|
28
|
+
});
|
|
23
29
|
timeToFirstByte.subscribe(({
|
|
24
30
|
attrs
|
|
25
31
|
}) => {
|
|
26
32
|
const loadEventTime = attrs.navigationEntry.loadEventEnd;
|
|
27
|
-
this.initialPageLoadInteraction.forceSave = true;
|
|
28
33
|
this.initialPageLoadInteraction.done(loadEventTime);
|
|
29
|
-
this.interactionsToHarvest.add(this.initialPageLoadInteraction);
|
|
30
|
-
this.initialPageLoadInteraction = null;
|
|
31
34
|
// Report metric on the initial page load time
|
|
32
35
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['SoftNav/Interaction/InitialPageLoad/Duration/Ms', Math.round(loadEventTime)], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
33
36
|
});
|
|
34
37
|
this.latestRouteSetByApi = null;
|
|
35
38
|
this.interactionInProgress = null; // aside from the "page load" interaction, there can only ever be 1 ongoing at a time
|
|
36
|
-
|
|
39
|
+
this.latestHistoryUrl = null;
|
|
37
40
|
this.blocked = false;
|
|
38
41
|
this.waitForFlags(['spa']).then(([spaOn]) => {
|
|
39
42
|
if (spaOn) {
|
|
@@ -53,7 +56,11 @@ export class Aggregate extends AggregateBase {
|
|
|
53
56
|
|
|
54
57
|
// By default, a complete UI driven interaction requires event -> URL change -> DOM mod in that exact order.
|
|
55
58
|
registerHandler('newUIEvent', event => this.startUIInteraction(event.type, Math.floor(event.timeStamp), event.target), this.featureName, this.ee);
|
|
56
|
-
registerHandler('newURL', (timestamp, url) =>
|
|
59
|
+
registerHandler('newURL', (timestamp, url) => {
|
|
60
|
+
// 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.
|
|
61
|
+
this.latestHistoryUrl = url;
|
|
62
|
+
this.interactionInProgress?.updateHistory(timestamp, url);
|
|
63
|
+
}, this.featureName, this.ee);
|
|
57
64
|
registerHandler('newDom', timestamp => {
|
|
58
65
|
this.interactionInProgress?.updateDom(timestamp);
|
|
59
66
|
if (this.interactionInProgress?.seenHistoryAndDomChange()) this.interactionInProgress.done();
|
|
@@ -65,20 +72,23 @@ export class Aggregate extends AggregateBase {
|
|
|
65
72
|
serializer(eventBuffer) {
|
|
66
73
|
// The payload depacker takes the first ixn of a payload (if there are multiple ixns) and positively offset the subsequent ixns timestamps by that amount.
|
|
67
74
|
// In order to accurately portray the real start & end times of the 2nd & onward ixns, we hence need to negatively offset their start timestamps with that of the 1st ixn.
|
|
68
|
-
let firstIxnStartTime
|
|
75
|
+
let firstIxnStartTime;
|
|
69
76
|
const serializedIxnList = [];
|
|
70
77
|
for (const interaction of eventBuffer) {
|
|
71
78
|
serializedIxnList.push(interaction.serialize(firstIxnStartTime));
|
|
72
|
-
if (
|
|
79
|
+
if (firstIxnStartTime === undefined) firstIxnStartTime = Math.floor(interaction.start); // careful not to match or overwrite on 0 value!
|
|
73
80
|
}
|
|
74
81
|
return "bel.7;".concat(serializedIxnList.join(';'));
|
|
75
82
|
}
|
|
76
83
|
startUIInteraction(eventName, startedAt, sourceElem) {
|
|
77
84
|
// this is throttled by instrumentation so that it isn't excessively called
|
|
78
85
|
if (this.interactionInProgress?.createdByApi) return; // api-started interactions cannot be disrupted aka cancelled by UI events (and the vice versa applies as well)
|
|
79
|
-
if (this.interactionInProgress?.done() === false) return;
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
if (this.interactionInProgress?.done() === false) return; // current in-progress is blocked from closing, e.g. by 'waitForEnd' api option
|
|
87
|
+
|
|
88
|
+
const oldURL = eventName === INTERACTION_TRIGGERS[3] ? this.latestHistoryUrl : undefined; // see related comment in 'newURL' handler above, 'popstate'
|
|
89
|
+
this.interactionInProgress = new Interaction(this.agentIdentifier, eventName, startedAt, this.latestRouteSetByApi, oldURL);
|
|
90
|
+
if (eventName === INTERACTION_TRIGGERS[0]) {
|
|
91
|
+
// 'click'
|
|
82
92
|
const sourceElemText = getActionText(sourceElem);
|
|
83
93
|
if (sourceElemText) this.interactionInProgress.customAttributes.actionText = sourceElemText;
|
|
84
94
|
}
|
|
@@ -124,7 +134,7 @@ export class Aggregate extends AggregateBase {
|
|
|
124
134
|
// reverse search for the latest completed interaction for efficiency
|
|
125
135
|
const finishedInteraction = interactionsBuffer[idx];
|
|
126
136
|
if (finishedInteraction.isActiveDuring(timestamp)) {
|
|
127
|
-
if (finishedInteraction.trigger !==
|
|
137
|
+
if (finishedInteraction.trigger !== IPL_TRIGGER_NAME) return finishedInteraction;
|
|
128
138
|
// It's possible that a complete interaction occurs before page is fully loaded, so we need to consider if a route-change ixn may have overlapped this iPL
|
|
129
139
|
else saveIxn = finishedInteraction;
|
|
130
140
|
}
|
|
@@ -188,9 +198,16 @@ export class Aggregate extends AggregateBase {
|
|
|
188
198
|
// In here, 'this' refers to the EventContext specific to per InteractionHandle instance spawned by each .interaction() api call.
|
|
189
199
|
// Each api call aka IH instance would therefore retain a reference to either the in-progress interaction *at the time of the call* OR a new api-started interaction.
|
|
190
200
|
this.associatedInteraction = thisClass.getInteractionFor(time);
|
|
201
|
+
if (this.associatedInteraction?.trigger === IPL_TRIGGER_NAME) this.associatedInteraction = null; // the api get-interaction method cannot target IPL
|
|
191
202
|
if (!this.associatedInteraction) {
|
|
192
203
|
// 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.
|
|
193
204
|
this.associatedInteraction = thisClass.interactionInProgress = new Interaction(thisClass.agentIdentifier, API_TRIGGER_NAME, time, thisClass.latestRouteSetByApi);
|
|
205
|
+
thisClass.domObserver.observe(document.body, {
|
|
206
|
+
attributes: true,
|
|
207
|
+
childList: true,
|
|
208
|
+
subtree: true,
|
|
209
|
+
characterData: true
|
|
210
|
+
}); // start observing for DOM changes like a regular UI-driven interaction
|
|
194
211
|
thisClass.setClosureHandlers();
|
|
195
212
|
}
|
|
196
213
|
if (waitForEnd === true) this.associatedInteraction.keepOpenUntilEndApi = true;
|
|
@@ -4,9 +4,10 @@ import { numeric } from '../../../common/serialize/bel-serializer';
|
|
|
4
4
|
import { firstPaint } from '../../../common/vitals/first-paint';
|
|
5
5
|
import { firstContentfulPaint } from '../../../common/vitals/first-contentful-paint';
|
|
6
6
|
import { getInfo } from '../../../common/config/info';
|
|
7
|
+
import { IPL_TRIGGER_NAME } from '../constants';
|
|
7
8
|
export class InitialPageLoadInteraction extends Interaction {
|
|
8
9
|
constructor(agentIdentifier) {
|
|
9
|
-
super(agentIdentifier,
|
|
10
|
+
super(agentIdentifier, IPL_TRIGGER_NAME, 0, null);
|
|
10
11
|
const agentInfo = getInfo(agentIdentifier);
|
|
11
12
|
this.queueTime = agentInfo.queueTime;
|
|
12
13
|
this.appTime = agentInfo.applicationTime;
|