@newrelic/browser-agent 1.320.0 → 1.320.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/CHANGELOG.md +8 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/payloads/payloads.js +3 -2
- package/dist/cjs/common/serialize/bel-serializer.js +17 -4
- package/dist/cjs/common/v2/mfe-vitals.js +34 -11
- package/dist/cjs/features/ajax/aggregate/index.js +19 -8
- package/dist/cjs/features/page_view_timing/aggregate/index.js +28 -13
- package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +16 -4
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +10 -3
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/payloads/payloads.js +3 -2
- package/dist/esm/common/serialize/bel-serializer.js +17 -4
- package/dist/esm/common/v2/mfe-vitals.js +34 -11
- package/dist/esm/features/ajax/aggregate/index.js +19 -8
- package/dist/esm/features/page_view_timing/aggregate/index.js +28 -13
- package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +16 -4
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +10 -3
- package/dist/types/common/payloads/payloads.d.ts +3 -2
- package/dist/types/common/payloads/payloads.d.ts.map +1 -1
- package/dist/types/common/serialize/bel-serializer.d.ts +9 -1
- package/dist/types/common/serialize/bel-serializer.d.ts.map +1 -1
- package/dist/types/common/v2/mfe-vitals.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +0 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/payloads/payloads.js +3 -2
- package/src/common/serialize/bel-serializer.js +13 -4
- package/src/common/v2/mfe-vitals.js +39 -12
- package/src/features/ajax/aggregate/index.js +12 -9
- package/src/features/page_view_timing/aggregate/index.js +19 -14
- package/src/features/soft_navigations/aggregate/ajax-node.js +10 -7
- package/src/features/soft_navigations/aggregate/interaction.js +6 -5
|
@@ -145,6 +145,7 @@ export class Aggregate extends AggregateBase {
|
|
|
145
145
|
if (!eventBuffer.length) return;
|
|
146
146
|
const {
|
|
147
147
|
addString,
|
|
148
|
+
addStringRaw,
|
|
148
149
|
addStringWithTruncation
|
|
149
150
|
} = createStringAdders(getAddStringContext, this.obfuscator);
|
|
150
151
|
let payload = 'bel.7;';
|
|
@@ -158,11 +159,11 @@ export class Aggregate extends AggregateBase {
|
|
|
158
159
|
// callbackEnd
|
|
159
160
|
numeric(0),
|
|
160
161
|
// no callbackDuration for non-SPA events
|
|
161
|
-
|
|
162
|
+
addStringRaw(event.method), numeric(event.status), addString(event.domain), addString(event.path), numeric(event.requestSize), numeric(event.responseSize), event.type === 'fetch' ? 1 : '', addStringRaw(0),
|
|
162
163
|
// nodeId
|
|
163
|
-
nullable(event.spanId,
|
|
164
|
+
nullable(event.spanId, addStringRaw, true) +
|
|
164
165
|
// guid
|
|
165
|
-
nullable(event.traceId,
|
|
166
|
+
nullable(event.traceId, addStringRaw, true) +
|
|
166
167
|
// traceId
|
|
167
168
|
nullable(event.spanTimestamp, numeric, false) // timestamp
|
|
168
169
|
];
|
|
@@ -172,14 +173,21 @@ export class Aggregate extends AggregateBase {
|
|
|
172
173
|
const jsAttributes = this.agentRef.info.jsAttributes;
|
|
173
174
|
|
|
174
175
|
// Regular attributes: obfuscate only
|
|
175
|
-
|
|
176
|
+
let regularAttrs = addCustomAttributes({
|
|
176
177
|
...(jsAttributes || {}),
|
|
177
|
-
[AJAX_ID]: event[AJAX_ID],
|
|
178
|
-
// all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
179
178
|
...(event.targetAttributes || {}),
|
|
180
179
|
// used to supply the version 2 attributes, either MFE target or duplication attributes for the main agent app
|
|
181
180
|
...(event.gql || {})
|
|
182
|
-
},
|
|
181
|
+
}, {
|
|
182
|
+
addKey: addStringRaw,
|
|
183
|
+
addVal: addString
|
|
184
|
+
});
|
|
185
|
+
regularAttrs.push(...addCustomAttributes({
|
|
186
|
+
[AJAX_ID]: event[AJAX_ID] // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
187
|
+
}, {
|
|
188
|
+
addKey: addStringRaw,
|
|
189
|
+
addVal: addStringRaw
|
|
190
|
+
}));
|
|
183
191
|
|
|
184
192
|
// Payload attributes: obfuscate then truncate
|
|
185
193
|
const payloadAttrs = addCustomAttributes({
|
|
@@ -198,7 +206,10 @@ export class Aggregate extends AggregateBase {
|
|
|
198
206
|
...(event.responseHeaders ? {
|
|
199
207
|
responseHeaders: event.responseHeaders
|
|
200
208
|
} : {})
|
|
201
|
-
},
|
|
209
|
+
}, {
|
|
210
|
+
addKey: addStringRaw,
|
|
211
|
+
addVal: addStringWithTruncation
|
|
212
|
+
});
|
|
202
213
|
const attrParts = [...regularAttrs, ...payloadAttrs];
|
|
203
214
|
fields.unshift(numeric(attrParts.length));
|
|
204
215
|
insert += fields.join(',');
|
|
@@ -23,6 +23,7 @@ import { loadTime } from '../../../common/vitals/load-time';
|
|
|
23
23
|
import { webdriverDetected } from '../../../common/util/webdriver-detection';
|
|
24
24
|
import { cleanURL } from '../../../common/url/clean-url';
|
|
25
25
|
import { EVENT_TYPES } from '../../../common/constants/events';
|
|
26
|
+
import { createStringAdders } from '../../../common/payloads/payloads';
|
|
26
27
|
export class Aggregate extends AggregateBase {
|
|
27
28
|
static featureName = FEATURE_NAME;
|
|
28
29
|
#handleVitalMetric = ({
|
|
@@ -98,6 +99,7 @@ export class Aggregate extends AggregateBase {
|
|
|
98
99
|
if (name !== VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT && cumulativeLayoutShift.current.value >= 0) {
|
|
99
100
|
attrs.cls = cumulativeLayoutShift.current.value;
|
|
100
101
|
}
|
|
102
|
+
attrs.webdriverDetected = webdriverDetected;
|
|
101
103
|
const timing = {
|
|
102
104
|
name,
|
|
103
105
|
value,
|
|
@@ -127,15 +129,9 @@ export class Aggregate extends AggregateBase {
|
|
|
127
129
|
loadState: document.readyState
|
|
128
130
|
});
|
|
129
131
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Object.entries(this.agentRef.info.jsAttributes || {}).forEach(([key, val]) => {
|
|
134
|
-
if (reservedAttributes.indexOf(key) < 0) {
|
|
135
|
-
timingAttributes[key] = val;
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
timingAttributes.webdriverDetected = webdriverDetected;
|
|
132
|
+
#getGlobalCustomAttributes() {
|
|
133
|
+
const reservedAttributes = ['size', 'eid', 'cls', 'type', 'fid', 'elTag', 'elUrl', 'net-type', 'net-etype', 'net-rtt', 'net-dlink', 'webdriverDetected'];
|
|
134
|
+
return Object.fromEntries(Object.entries(this.agentRef.info.jsAttributes || {}).filter(([key]) => !reservedAttributes.includes(key)));
|
|
139
135
|
}
|
|
140
136
|
preHarvestChecks() {
|
|
141
137
|
this.checkForFirstInteraction();
|
|
@@ -145,15 +141,34 @@ export class Aggregate extends AggregateBase {
|
|
|
145
141
|
// serialize array of timing data
|
|
146
142
|
serializer(eventBuffer) {
|
|
147
143
|
if (!eventBuffer?.length) return '';
|
|
148
|
-
|
|
144
|
+
const {
|
|
145
|
+
addString,
|
|
146
|
+
addStringRaw
|
|
147
|
+
} = createStringAdders(getAddStringContext, this.obfuscator);
|
|
148
|
+
const keepOrigValue = {
|
|
149
|
+
addKey: addStringRaw,
|
|
150
|
+
addVal: addStringRaw
|
|
151
|
+
};
|
|
152
|
+
const obfuscateValue = {
|
|
153
|
+
addKey: addStringRaw,
|
|
154
|
+
addVal: addString
|
|
155
|
+
};
|
|
149
156
|
var payload = 'bel.6;';
|
|
150
157
|
for (var i = 0; i < eventBuffer.length; i++) {
|
|
151
158
|
var timing = eventBuffer[i];
|
|
152
159
|
payload += 'e,';
|
|
153
|
-
payload +=
|
|
160
|
+
payload += addStringRaw(timing.name) + ',';
|
|
154
161
|
payload += nullable(timing.value, numeric, false) + ',';
|
|
155
|
-
this
|
|
156
|
-
|
|
162
|
+
const userAttrs = this.#getGlobalCustomAttributes();
|
|
163
|
+
const {
|
|
164
|
+
pageUrl,
|
|
165
|
+
...systemReservedAttrs
|
|
166
|
+
} = timing.attrs || {};
|
|
167
|
+
var attrParts = addCustomAttributes(systemReservedAttrs, keepOrigValue);
|
|
168
|
+
attrParts.push(...addCustomAttributes(userAttrs, obfuscateValue));
|
|
169
|
+
if (pageUrl) attrParts.push(...addCustomAttributes({
|
|
170
|
+
pageUrl
|
|
171
|
+
}, obfuscateValue));
|
|
157
172
|
if (attrParts && attrParts.length > 0) {
|
|
158
173
|
payload += numeric(attrParts.length) + ';' + attrParts.join(';');
|
|
159
174
|
}
|
|
@@ -41,6 +41,7 @@ export class AjaxNode extends BelNode {
|
|
|
41
41
|
serialize(parentStartTimestamp, agentRef, ajaxObfuscator) {
|
|
42
42
|
const {
|
|
43
43
|
addString,
|
|
44
|
+
addStringRaw,
|
|
44
45
|
addStringWithTruncation
|
|
45
46
|
} = createStringAdders(getAddStringContext, ajaxObfuscator);
|
|
46
47
|
const nodeList = [];
|
|
@@ -56,13 +57,21 @@ export class AjaxNode extends BelNode {
|
|
|
56
57
|
// callbackEnd is relative to end
|
|
57
58
|
numeric(this.callbackDuration),
|
|
58
59
|
// not relative
|
|
59
|
-
|
|
60
|
+
addStringRaw(this.method), numeric(this.status), addString(this.domain), addString(this.path), numeric(this.txSize), numeric(this.rxSize), this.requestedWith, addStringRaw(this.nodeId), nullable(this.spanId, addStringRaw, true) + nullable(this.traceId, addStringRaw, true) + nullable(this.spanTimestamp, numeric)];
|
|
60
61
|
// Regular attributes: obfuscate only
|
|
61
62
|
const regularAttrs = addCustomAttributes({
|
|
62
|
-
[AJAX_ID]: this[AJAX_ID],
|
|
63
63
|
...(this.targetAttributes || {}),
|
|
64
64
|
...(this.gql || {})
|
|
65
|
-
},
|
|
65
|
+
}, {
|
|
66
|
+
addKey: addStringRaw,
|
|
67
|
+
addVal: addString
|
|
68
|
+
});
|
|
69
|
+
regularAttrs.push(...addCustomAttributes({
|
|
70
|
+
[AJAX_ID]: this[AJAX_ID]
|
|
71
|
+
}, {
|
|
72
|
+
addKey: addStringRaw,
|
|
73
|
+
addVal: addStringRaw
|
|
74
|
+
}));
|
|
66
75
|
|
|
67
76
|
// Payload attributes: obfuscate then truncate
|
|
68
77
|
const payloadAttrs = addCustomAttributes({
|
|
@@ -81,7 +90,10 @@ export class AjaxNode extends BelNode {
|
|
|
81
90
|
...(this.responseHeaders ? {
|
|
82
91
|
responseHeaders: this.responseHeaders
|
|
83
92
|
} : {})
|
|
84
|
-
},
|
|
93
|
+
}, {
|
|
94
|
+
addKey: addStringRaw,
|
|
95
|
+
addVal: addStringWithTruncation
|
|
96
|
+
});
|
|
85
97
|
let allAttachedNodes = [...regularAttrs, ...payloadAttrs];
|
|
86
98
|
this.children.forEach(node => allAttachedNodes.push(node.serialize())); // no children is expected under ajax nodes at this time
|
|
87
99
|
|
|
@@ -9,6 +9,7 @@ import { now } from '../../../common/timing/now';
|
|
|
9
9
|
import { cleanURL } from '../../../common/url/clean-url';
|
|
10
10
|
import { NODE_TYPE, INTERACTION_STATUS, INTERACTION_TYPE, API_TRIGGER_NAME, IPL_TRIGGER_NAME, NO_LONG_TASK_WINDOW } from '../constants';
|
|
11
11
|
import { BelNode } from './bel-node';
|
|
12
|
+
import { createStringAdders } from '../../../common/payloads/payloads';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* link https://github.com/newrelic/nr-querypack/blob/main/schemas/bel/7.qpschema
|
|
@@ -127,7 +128,10 @@ export class Interaction extends BelNode {
|
|
|
127
128
|
*/
|
|
128
129
|
serialize(firstStartTimeOfPayload, agentRef, interactionObfuscator, ajaxObfuscator) {
|
|
129
130
|
const isFirstIxnOfPayload = firstStartTimeOfPayload === undefined;
|
|
130
|
-
const
|
|
131
|
+
const {
|
|
132
|
+
addString,
|
|
133
|
+
addStringRaw
|
|
134
|
+
} = createStringAdders(getAddStringContext, interactionObfuscator);
|
|
131
135
|
const nodeList = [];
|
|
132
136
|
let ixnType;
|
|
133
137
|
if (this.trigger === IPL_TRIGGER_NAME) ixnType = INTERACTION_TYPE.INITIAL_PAGE_LOAD;else if (this.newURL !== this.oldURL) ixnType = INTERACTION_TYPE.ROUTE_CHANGE;else ixnType = INTERACTION_TYPE.UNSPECIFIED;
|
|
@@ -143,12 +147,15 @@ export class Interaction extends BelNode {
|
|
|
143
147
|
// callbackEnd -- relative to start; not used by BrowserInteraction events so these are always 0
|
|
144
148
|
numeric(0),
|
|
145
149
|
// not relative; always 0 for BrowserInteraction
|
|
146
|
-
|
|
150
|
+
addStringRaw(this.trigger), addString(cleanURL(this.initialPageURL, true)), addString(cleanURL(this.oldURL, true)), addString(cleanURL(this.newURL, true)), addString(this.customName), ixnType, nullable(this.queueTime, numeric, true) + nullable(this.appTime, numeric, true) + nullable(this.oldRoute, addString, true) + nullable(this.newRoute, addString, true) + addStringRaw(this.id), addStringRaw(this.nodeId), nullable(this.firstPaint, numeric, true) + nullable(this.firstContentfulPaint, numeric)];
|
|
147
151
|
const customAttributes = {
|
|
148
152
|
...agentRef.info.jsAttributes,
|
|
149
153
|
...this.customAttributes
|
|
150
154
|
}; // attrs specific to this interaction should have precedence over the general custom attrs
|
|
151
|
-
const allAttachedNodes = addCustomAttributes(customAttributes || {},
|
|
155
|
+
const allAttachedNodes = addCustomAttributes(customAttributes || {}, {
|
|
156
|
+
addKey: addStringRaw,
|
|
157
|
+
addVal: addString
|
|
158
|
+
}); // start with all custom attributes
|
|
152
159
|
if (agentRef.info.atts) allAttachedNodes.push('a,' + addString(agentRef.info.atts)); // add apm provided attributes
|
|
153
160
|
/* Querypack encoder+decoder quirkiness:
|
|
154
161
|
- 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.)
|
|
@@ -40,14 +40,15 @@ export function isLikelyHumanReadable(headers: Object, data: any): boolean;
|
|
|
40
40
|
*/
|
|
41
41
|
export function truncateAsString(data: any): string;
|
|
42
42
|
/**
|
|
43
|
-
* Creates string adder functions for BEL serialization with obfuscation and optional truncation.
|
|
43
|
+
* Creates string adder functions for BEL serialization with obfuscation and optional truncation, as well as raw string.
|
|
44
44
|
* This ensures a single string table is used while providing separate handling for regular vs payload attributes.
|
|
45
45
|
* @param {Function} getAddStringContext - Function that creates a new string table context
|
|
46
46
|
* @param {Object} obfuscator - Optional obfuscator instance for string obfuscation
|
|
47
|
-
* @returns {{addString: Function, addStringWithTruncation: Function}} Object containing
|
|
47
|
+
* @returns {{addString: Function, addStringRaw: Function, addStringWithTruncation: Function}} Object containing various string adder functions
|
|
48
48
|
*/
|
|
49
49
|
export function createStringAdders(getAddStringContext: Function, obfuscator: Object): {
|
|
50
50
|
addString: Function;
|
|
51
|
+
addStringRaw: Function;
|
|
51
52
|
addStringWithTruncation: Function;
|
|
52
53
|
};
|
|
53
54
|
//# sourceMappingURL=payloads.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payloads.d.ts","sourceRoot":"","sources":["../../../../src/common/payloads/payloads.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;GAYG;AACH,+CAVW,MAAM,+EAEd;IAAsB,UAAU,EAAxB,MAAM;IACS,YAAY,EAA3B,OAAO;IACO,WAAW,EAAzB,MAAM;IACQ,eAAe,EAA7B,MAAM;IACS,eAAe;CACtC,YAAQ,MAAM,EAAE,GACN,OAAO,CAqBnB;AAED;;;;GAIG;AACH,yCAHW,MAAM,GACJ,MAAM,GAAC,SAAS,CAgB5B;AAED;;;;;GAKG;AACH,+CAJW,MAAM,QACN,GAAC,GACC,OAAO,CA2BnB;AAED;;;;;;GAMG;AACH,uCAHW,GAAC,GACC,MAAM,CA2BlB;AAED;;;;;;GAMG;AACH,8EAHW,MAAM,GACJ;IAAC,SAAS,WAAW;IAAC,uBAAuB,WAAU;CAAC,
|
|
1
|
+
{"version":3,"file":"payloads.d.ts","sourceRoot":"","sources":["../../../../src/common/payloads/payloads.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;GAYG;AACH,+CAVW,MAAM,+EAEd;IAAsB,UAAU,EAAxB,MAAM;IACS,YAAY,EAA3B,OAAO;IACO,WAAW,EAAzB,MAAM;IACQ,eAAe,EAA7B,MAAM;IACS,eAAe;CACtC,YAAQ,MAAM,EAAE,GACN,OAAO,CAqBnB;AAED;;;;GAIG;AACH,yCAHW,MAAM,GACJ,MAAM,GAAC,SAAS,CAgB5B;AAED;;;;;GAKG;AACH,+CAJW,MAAM,QACN,GAAC,GACC,OAAO,CA2BnB;AAED;;;;;;GAMG;AACH,uCAHW,GAAC,GACC,MAAM,CA2BlB;AAED;;;;;;GAMG;AACH,8EAHW,MAAM,GACJ;IAAC,SAAS,WAAW;IAAC,YAAY,WAAW;IAAC,uBAAuB,WAAU;CAAC,CAkB5F"}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
export function nullable(val: any, fn: any, comma: any): string;
|
|
2
2
|
export function numeric(n: any, noDefault: any): string;
|
|
3
3
|
export function getAddStringContext(obfuscator: any, truncator: any): (str: any, obfuscate?: boolean) => string;
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Attributes are serialized according to their types, using the provided serializers. Only the first 64 attributes will be added to the payload.
|
|
6
|
+
* @param attrs
|
|
7
|
+
* @param serializers
|
|
8
|
+
* @param {function} serializers.addKey -function for serializing attribute keys
|
|
9
|
+
* @param {function} serializers.addVal - function for serializing attribute values
|
|
10
|
+
* @returns {*[]}
|
|
11
|
+
*/
|
|
12
|
+
export function addCustomAttributes(attrs: any, serializers: any): any[];
|
|
5
13
|
//# sourceMappingURL=bel-serializer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bel-serializer.d.ts","sourceRoot":"","sources":["../../../../src/common/serialize/bel-serializer.js"],"names":[],"mappings":"AAUA,gEAIC;AAED,wDAKC;AAED,gHAiBC;AAED,
|
|
1
|
+
{"version":3,"file":"bel-serializer.d.ts","sourceRoot":"","sources":["../../../../src/common/serialize/bel-serializer.js"],"names":[],"mappings":"AAUA,gEAIC;AAED,wDAKC;AAED,gHAiBC;AAED;;;;;;;GAOG;AACH,mEAFa,GAAC,EAAE,CA2Cf"}
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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,CAkO7B;iCAjVY,OAAO,sCAAsC,EAAE,kBAAkB;gCACjE,OAAO,sCAAsC,EAAE,iBAAiB;gCAChE,OAAO,sCAAsC,EAAE,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IAEjC,2BA6BC;IAvBC,uBAA4D;IAyB9D,0GA2FC;IAED,+CAOC;IAED,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAoBA;IACE,2BAAiC;IAEjC,2BA6BC;IAvBC,uBAA4D;IAyB9D,0GA2FC;IAED,+CAOC;IAED,iDAuEC;CACF;8BA1N6B,4BAA4B;2BAK/B,gCAAgC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AA2BA;IACE,2BAAiC;IAMjC,2BA+BC;IA7BC,4BAA+B;IAC/B,0BAA6B;IAK7B,uBAA2D;IAyB7D;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED;;;;MAiCC;IAED;;;OAGG;IACH,4BAFa,IAAI,CAahB;IAWD,4BAGC;IAGD,qCA8BC;;CACF;8BAtK6B,4BAA4B;2BAC/B,gCAAgC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ajax-node.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/ajax-node.js"],"names":[],"mappings":"AAUA;IACE,8CA8BC;IA5BC,gBAA6B;IAC7B,YAA8B;IAC9B,YAA8B;IAC9B,YAA8B;IAC9B,UAA0B;IAC1B,YAAmC;IACnC,YAAoC;IACpC,+BAAwD;IACxD,YAA8B;IAC9B,aAAgC;IAChC,mBAA4C;IAC5C,SAAwB;IACxB,sBAAkD;IAIlD,iBAAwC;IACxC,oBAA8C;IAC9C,kBAA0C;IAC1C,kBAA0C;IAC1C,qBAAgD;IAK9C,iBAAoE;IAKxE,
|
|
1
|
+
{"version":3,"file":"ajax-node.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/ajax-node.js"],"names":[],"mappings":"AAUA;IACE,8CA8BC;IA5BC,gBAA6B;IAC7B,YAA8B;IAC9B,YAA8B;IAC9B,YAA8B;IAC9B,UAA0B;IAC1B,YAAmC;IACnC,YAAoC;IACpC,+BAAwD;IACxD,YAA8B;IAC9B,aAAgC;IAChC,mBAA4C;IAC5C,SAAwB;IACxB,sBAAkD;IAIlD,iBAAwC;IACxC,oBAA8C;IAC9C,kBAA0C;IAC1C,kBAA0C;IAC1C,qBAAgD;IAK9C,iBAAoE;IAKxE,iFAkDC;IAnEC,sBAAkC;CAoErC;wBAtFuB,YAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/interaction.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"interaction.d.ts","sourceRoot":"","sources":["../../../../../src/features/soft_navigations/aggregate/interaction.js"],"names":[],"mappings":"AAaA;;IAEI;AACJ;IAoBE,0FAaC;IAhCD,WAAmB;IACnB,uBAAgC;IAChC,gBAAU;IACV,qBAAqB;IACrB,oBAAoB;IACpB,eAAS;IACT,aAAO;IACP,cAAQ;IACR,+EAA+E;IAC/E,eAA8B;IAC9B,qBAAgB;IAChB,yBAAoB;IACpB,sBAAoB;IACpB,6BAA2B;IAC3B,cAAW;IACX,kBAAa;IACb,uBAAiB;IACjB,wBAAkB;IAIhB,gBAAoC;IACpC,aAAsB;IAEtB,cAAiC;IACjC,wCAGE;IACF,mBAAyC;IAAxB,qBAAwB;IAEzC,YAAsE;IAAxD,YAAwD;IAGxE,iDAKC;IAED,gCAGC;IAED,oCAUC;IAED,8BAIC;IAED,6DAeC;IAmBD;;;;;;OAMG;IACH,0BAHW,mBAAmB,WAM7B;IAGD,uBAAoB;IACpB,iCAA8B;IAC9B,sBAAmB;IAEnB;;;;;;;OAOG;IACH,qDALW,KAAK,yBACL,UAAU,kBACV,UAAU,UAgDpB;;CACF;wBAhLuB,YAAY"}
|
package/package.json
CHANGED
|
@@ -129,11 +129,11 @@ export function truncateAsString (data) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
|
-
* Creates string adder functions for BEL serialization with obfuscation and optional truncation.
|
|
132
|
+
* Creates string adder functions for BEL serialization with obfuscation and optional truncation, as well as raw string.
|
|
133
133
|
* This ensures a single string table is used while providing separate handling for regular vs payload attributes.
|
|
134
134
|
* @param {Function} getAddStringContext - Function that creates a new string table context
|
|
135
135
|
* @param {Object} obfuscator - Optional obfuscator instance for string obfuscation
|
|
136
|
-
* @returns {{addString: Function, addStringWithTruncation: Function}} Object containing
|
|
136
|
+
* @returns {{addString: Function, addStringRaw: Function, addStringWithTruncation: Function}} Object containing various string adder functions
|
|
137
137
|
*/
|
|
138
138
|
export function createStringAdders (getAddStringContext, obfuscator) {
|
|
139
139
|
const addStringRaw = getAddStringContext()
|
|
@@ -148,6 +148,7 @@ export function createStringAdders (getAddStringContext, obfuscator) {
|
|
|
148
148
|
|
|
149
149
|
return {
|
|
150
150
|
addString: (str) => processString(str, false),
|
|
151
|
+
addStringRaw: (str) => addStringRaw(str),
|
|
151
152
|
addStringWithTruncation: (str) => processString(str, true)
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -40,7 +40,16 @@ export function getAddStringContext (obfuscator, truncator) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Attributes are serialized according to their types, using the provided serializers. Only the first 64 attributes will be added to the payload.
|
|
45
|
+
* @param attrs
|
|
46
|
+
* @param serializers
|
|
47
|
+
* @param {function} serializers.addKey -function for serializing attribute keys
|
|
48
|
+
* @param {function} serializers.addVal - function for serializing attribute values
|
|
49
|
+
* @returns {*[]}
|
|
50
|
+
*/
|
|
51
|
+
export function addCustomAttributes (attrs, serializers) {
|
|
52
|
+
const { addKey, addVal } = serializers
|
|
44
53
|
var attrParts = []
|
|
45
54
|
|
|
46
55
|
Object.entries(attrs || {}).forEach(([key, val]) => {
|
|
@@ -48,13 +57,13 @@ export function addCustomAttributes (attrs, addString) {
|
|
|
48
57
|
var type = 5
|
|
49
58
|
var serializedValue
|
|
50
59
|
// add key to string table first
|
|
51
|
-
key =
|
|
60
|
+
key = addKey(key)
|
|
52
61
|
|
|
53
62
|
switch (typeof val) {
|
|
54
63
|
case 'object':
|
|
55
64
|
if (val) {
|
|
56
65
|
// serialize objects to strings
|
|
57
|
-
serializedValue =
|
|
66
|
+
serializedValue = addVal(stringify(val))
|
|
58
67
|
} else {
|
|
59
68
|
// null attribute type
|
|
60
69
|
type = 9
|
|
@@ -73,7 +82,7 @@ export function addCustomAttributes (attrs, addString) {
|
|
|
73
82
|
type = 9
|
|
74
83
|
break
|
|
75
84
|
default:
|
|
76
|
-
serializedValue =
|
|
85
|
+
serializedValue = addVal(val)
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
attrParts.push([type, key + (serializedValue ? ',' + serializedValue : '')])
|
|
@@ -124,6 +124,9 @@ const observePerformance = (observers, config, onEntry) => {
|
|
|
124
124
|
export function trackMFEVitals (target, timings) {
|
|
125
125
|
let fcpObservedAt
|
|
126
126
|
let lcpObservedAt
|
|
127
|
+
// Once disconnect() runs, no further vitals data should be written - even from an observer callback that was
|
|
128
|
+
// already in flight when disconnect() was called.
|
|
129
|
+
let disconnected = false
|
|
127
130
|
|
|
128
131
|
const getTimeRelativeToScriptStart = (capturedAt) => {
|
|
129
132
|
if (capturedAt == null) return capturedAt
|
|
@@ -132,12 +135,21 @@ export function trackMFEVitals (target, timings) {
|
|
|
132
135
|
|
|
133
136
|
const vitals = {
|
|
134
137
|
fcp: {
|
|
135
|
-
|
|
136
|
-
|
|
138
|
+
// A clock freeze/tab suspend between script registration and first paint can make this value huge once the
|
|
139
|
+
// tab resumes. Rather than report a multi-hour "FCP", drop it so bad data doesn't reach the backend.
|
|
140
|
+
get value () {
|
|
141
|
+
const relative = getTimeRelativeToScriptStart(fcpObservedAt)
|
|
142
|
+
return relative > CORRELATION_STALE_THRESHOLD_MS ? undefined : relative
|
|
143
|
+
},
|
|
144
|
+
set value (v) { if (!disconnected) fcpObservedAt = v }
|
|
137
145
|
},
|
|
138
146
|
lcp: {
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
// LCP can never occur before FCP, so if FCP was dropped as invalid, LCP is derived from the same untrustworthy clock and must be dropped too.
|
|
148
|
+
get value () {
|
|
149
|
+
if (vitals.fcp.value === undefined) return undefined
|
|
150
|
+
return getTimeRelativeToScriptStart(lcpObservedAt)
|
|
151
|
+
},
|
|
152
|
+
set value (v) { if (!disconnected) lcpObservedAt = v }
|
|
141
153
|
},
|
|
142
154
|
cls: {
|
|
143
155
|
value: undefined
|
|
@@ -159,15 +171,22 @@ export function trackMFEVitals (target, timings) {
|
|
|
159
171
|
}, CORRELATION_STALE_THRESHOLD_MS)
|
|
160
172
|
|
|
161
173
|
const populateVitalMinimums = () => {
|
|
162
|
-
|
|
174
|
+
if (disconnected) return
|
|
175
|
+
if (fcpObservedAt == null) {
|
|
176
|
+
const capturedAt = now()
|
|
177
|
+
// If the very first observed render already exceeds the window, the clock is untrustworthy for this
|
|
178
|
+
// MFE (e.g. tab was frozen/suspended between registration and paint) - shut everything down instead
|
|
179
|
+
// of seeding vitals with data derived from it.
|
|
180
|
+
if (getTimeRelativeToScriptStart(capturedAt) > CORRELATION_STALE_THRESHOLD_MS) {
|
|
181
|
+
vitals.disconnect()
|
|
182
|
+
return
|
|
183
|
+
}
|
|
184
|
+
fcpObservedAt = capturedAt
|
|
185
|
+
}
|
|
163
186
|
lcpObservedAt ??= now()
|
|
164
187
|
vitals.cls.value ??= 0
|
|
165
188
|
}
|
|
166
189
|
|
|
167
|
-
// if the MFE has already rendered something on the page before we could set up listeners, just populate vital minimums immediately
|
|
168
|
-
const existingRoots = globalScope.document?.querySelectorAll(`[data-nr-mfe-id="${escapeSelectorValue(target.id)}"]`)
|
|
169
|
-
if (Array.from(existingRoots || []).some(x => isMatch(x.dataset, target))) populateVitalMinimums()
|
|
170
|
-
|
|
171
190
|
// Track FCP - first contentful paint
|
|
172
191
|
const fcpObs = observeMutations(target, (_, obs) => {
|
|
173
192
|
// An observed "FCP" means _something_ rendered, so at minimum we can populate all the baseline values for the vitals
|
|
@@ -183,12 +202,13 @@ export function trackMFEVitals (target, timings) {
|
|
|
183
202
|
|
|
184
203
|
try {
|
|
185
204
|
resizeObs = new globalScope.ResizeObserver((entries) => {
|
|
205
|
+
if (disconnected) return
|
|
186
206
|
entries.forEach((entry) => {
|
|
187
207
|
try {
|
|
188
208
|
const size = entry.contentRect.width * entry.contentRect.height
|
|
189
209
|
if (size > largestSize) {
|
|
190
210
|
largestSize = size
|
|
191
|
-
|
|
211
|
+
vitals.lcp.value = now()
|
|
192
212
|
}
|
|
193
213
|
resizeObs.unobserve(entry.target)
|
|
194
214
|
} catch (e) {
|
|
@@ -245,7 +265,7 @@ export function trackMFEVitals (target, timings) {
|
|
|
245
265
|
// Track CLS - cumulative layout shift
|
|
246
266
|
// Initialize CLS to 0 if browser supports it
|
|
247
267
|
observePerformance(observers, { type: 'layout-shift', buffered: true }, (entry) => {
|
|
248
|
-
if (entry.hadRecentInput) return
|
|
268
|
+
if (disconnected || entry.hadRecentInput) return
|
|
249
269
|
;(entry.sources || []).some(source => {
|
|
250
270
|
if (isInMFE(source.node, target)) {
|
|
251
271
|
// an observed "CLS" means _something_ rendered for the MFE, so at minimum we can make sure all the baseline values are populated for the vitals
|
|
@@ -259,7 +279,7 @@ export function trackMFEVitals (target, timings) {
|
|
|
259
279
|
|
|
260
280
|
// Track INP - interaction to next paint
|
|
261
281
|
observePerformance(observers, { type: 'event', buffered: true, durationThreshold: 40 }, (entry) => {
|
|
262
|
-
if (!entry.interactionId || !isInMFE(entry.target, target)) return
|
|
282
|
+
if (disconnected || !entry.interactionId || !isInMFE(entry.target, target)) return
|
|
263
283
|
if (vitals.inp.value === undefined || entry.duration > vitals.inp.value) {
|
|
264
284
|
// an observed "INP" means _something_ rendered for the MFE, so at minimum we can make sure all the baseline values are populated for the vitals
|
|
265
285
|
populateVitalMinimums()
|
|
@@ -276,6 +296,7 @@ export function trackMFEVitals (target, timings) {
|
|
|
276
296
|
|
|
277
297
|
// Disconnect all observers
|
|
278
298
|
vitals.disconnect = () => {
|
|
299
|
+
disconnected = true
|
|
279
300
|
// Disconnect all observers
|
|
280
301
|
observers.forEach(obs => {
|
|
281
302
|
try {
|
|
@@ -317,5 +338,11 @@ export function trackMFEVitals (target, timings) {
|
|
|
317
338
|
globalScope.addEventListener(type, vitals.disconnect, { once: true, passive: true })
|
|
318
339
|
})
|
|
319
340
|
|
|
341
|
+
// If the MFE has already rendered something on the page before we could set up listeners, just populate vital
|
|
342
|
+
// minimums immediately. Done last, once `observers` is fully populated and `vitals.disconnect` has its real
|
|
343
|
+
// implementation, so a stale-clock detection here can actually tear everything down instead of no-op'ing.
|
|
344
|
+
const existingRoots = globalScope.document?.querySelectorAll(`[data-nr-mfe-id="${escapeSelectorValue(target.id)}"]`)
|
|
345
|
+
if (Array.from(existingRoots || []).some(x => isMatch(x.dataset, target))) populateVitalMinimums()
|
|
346
|
+
|
|
320
347
|
return vitals
|
|
321
348
|
}
|
|
@@ -157,7 +157,7 @@ export class Aggregate extends AggregateBase {
|
|
|
157
157
|
serializer (eventBuffer) {
|
|
158
158
|
if (!eventBuffer.length) return
|
|
159
159
|
|
|
160
|
-
const { addString, addStringWithTruncation } = createStringAdders(getAddStringContext, this.obfuscator)
|
|
160
|
+
const { addString, addStringRaw, addStringWithTruncation } = createStringAdders(getAddStringContext, this.obfuscator)
|
|
161
161
|
|
|
162
162
|
let payload = 'bel.7;'
|
|
163
163
|
|
|
@@ -174,16 +174,16 @@ export class Aggregate extends AggregateBase {
|
|
|
174
174
|
numeric(event.endTime - event.startTime),
|
|
175
175
|
numeric(0), // callbackEnd
|
|
176
176
|
numeric(0), // no callbackDuration for non-SPA events
|
|
177
|
-
|
|
177
|
+
addStringRaw(event.method),
|
|
178
178
|
numeric(event.status),
|
|
179
179
|
addString(event.domain),
|
|
180
180
|
addString(event.path),
|
|
181
181
|
numeric(event.requestSize),
|
|
182
182
|
numeric(event.responseSize),
|
|
183
183
|
event.type === 'fetch' ? 1 : '',
|
|
184
|
-
|
|
185
|
-
nullable(event.spanId,
|
|
186
|
-
nullable(event.traceId,
|
|
184
|
+
addStringRaw(0), // nodeId
|
|
185
|
+
nullable(event.spanId, addStringRaw, true) + // guid
|
|
186
|
+
nullable(event.traceId, addStringRaw, true) + // traceId
|
|
187
187
|
nullable(event.spanTimestamp, numeric, false) // timestamp
|
|
188
188
|
]
|
|
189
189
|
|
|
@@ -193,12 +193,15 @@ export class Aggregate extends AggregateBase {
|
|
|
193
193
|
const jsAttributes = this.agentRef.info.jsAttributes
|
|
194
194
|
|
|
195
195
|
// Regular attributes: obfuscate only
|
|
196
|
-
|
|
196
|
+
let regularAttrs = addCustomAttributes({
|
|
197
197
|
...(jsAttributes || {}),
|
|
198
|
-
[AJAX_ID]: event[AJAX_ID], // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
199
198
|
...(event.targetAttributes || {}), // used to supply the version 2 attributes, either MFE target or duplication attributes for the main agent app
|
|
200
199
|
...(event.gql || {})
|
|
201
|
-
}, addString)
|
|
200
|
+
}, { addKey: addStringRaw, addVal: addString })
|
|
201
|
+
|
|
202
|
+
regularAttrs.push(...addCustomAttributes({
|
|
203
|
+
[AJAX_ID]: event[AJAX_ID] // all AjaxRequest events should have a unique identifier to allow for easier grouping and analysis in the UI
|
|
204
|
+
}, { addKey: addStringRaw, addVal: addStringRaw }))
|
|
202
205
|
|
|
203
206
|
// Payload attributes: obfuscate then truncate
|
|
204
207
|
const payloadAttrs = addCustomAttributes({
|
|
@@ -207,7 +210,7 @@ export class Aggregate extends AggregateBase {
|
|
|
207
210
|
...(event.requestQuery ? { requestQuery: event.requestQuery } : {}),
|
|
208
211
|
...(event.responseBody ? { responseBody: event.responseBody } : {}),
|
|
209
212
|
...(event.responseHeaders ? { responseHeaders: event.responseHeaders } : {})
|
|
210
|
-
}, addStringWithTruncation)
|
|
213
|
+
}, { addKey: addStringRaw, addVal: addStringWithTruncation })
|
|
211
214
|
|
|
212
215
|
const attrParts = [...regularAttrs, ...payloadAttrs]
|
|
213
216
|
fields.unshift(numeric(attrParts.length))
|
|
@@ -23,6 +23,7 @@ import { loadTime } from '../../../common/vitals/load-time'
|
|
|
23
23
|
import { webdriverDetected } from '../../../common/util/webdriver-detection'
|
|
24
24
|
import { cleanURL } from '../../../common/url/clean-url'
|
|
25
25
|
import { EVENT_TYPES } from '../../../common/constants/events'
|
|
26
|
+
import { createStringAdders } from '../../../common/payloads/payloads'
|
|
26
27
|
|
|
27
28
|
export class Aggregate extends AggregateBase {
|
|
28
29
|
static featureName = FEATURE_NAME
|
|
@@ -93,6 +94,8 @@ export class Aggregate extends AggregateBase {
|
|
|
93
94
|
attrs.cls = cumulativeLayoutShift.current.value
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
attrs.webdriverDetected = webdriverDetected
|
|
98
|
+
|
|
96
99
|
const timing = {
|
|
97
100
|
name,
|
|
98
101
|
value,
|
|
@@ -125,17 +128,13 @@ export class Aggregate extends AggregateBase {
|
|
|
125
128
|
})
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
#getGlobalCustomAttributes () {
|
|
132
|
+
const reservedAttributes = ['size', 'eid', 'cls', 'type', 'fid', 'elTag', 'elUrl', 'net-type',
|
|
133
|
+
'net-etype', 'net-rtt', 'net-dlink', 'webdriverDetected']
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (reservedAttributes.indexOf(key) < 0) {
|
|
135
|
-
timingAttributes[key] = val
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
timingAttributes.webdriverDetected = webdriverDetected
|
|
135
|
+
return Object.fromEntries(
|
|
136
|
+
Object.entries(this.agentRef.info.jsAttributes || {}).filter(([key]) => !reservedAttributes.includes(key))
|
|
137
|
+
)
|
|
139
138
|
}
|
|
140
139
|
|
|
141
140
|
preHarvestChecks () {
|
|
@@ -146,7 +145,9 @@ export class Aggregate extends AggregateBase {
|
|
|
146
145
|
// serialize array of timing data
|
|
147
146
|
serializer (eventBuffer) {
|
|
148
147
|
if (!eventBuffer?.length) return ''
|
|
149
|
-
|
|
148
|
+
const { addString, addStringRaw } = createStringAdders(getAddStringContext, this.obfuscator)
|
|
149
|
+
const keepOrigValue = { addKey: addStringRaw, addVal: addStringRaw }
|
|
150
|
+
const obfuscateValue = { addKey: addStringRaw, addVal: addString }
|
|
150
151
|
|
|
151
152
|
var payload = 'bel.6;'
|
|
152
153
|
|
|
@@ -154,12 +155,16 @@ export class Aggregate extends AggregateBase {
|
|
|
154
155
|
var timing = eventBuffer[i]
|
|
155
156
|
|
|
156
157
|
payload += 'e,'
|
|
157
|
-
payload +=
|
|
158
|
+
payload += addStringRaw(timing.name) + ','
|
|
158
159
|
payload += nullable(timing.value, numeric, false) + ','
|
|
159
160
|
|
|
160
|
-
this
|
|
161
|
+
const userAttrs = this.#getGlobalCustomAttributes()
|
|
162
|
+
|
|
163
|
+
const { pageUrl, ...systemReservedAttrs } = timing.attrs || {}
|
|
164
|
+
var attrParts = addCustomAttributes(systemReservedAttrs, keepOrigValue)
|
|
165
|
+
attrParts.push(...addCustomAttributes(userAttrs, obfuscateValue))
|
|
166
|
+
if (pageUrl) attrParts.push(...addCustomAttributes({ pageUrl }, obfuscateValue))
|
|
161
167
|
|
|
162
|
-
var attrParts = addCustomAttributes(timing.attrs, addString)
|
|
163
168
|
if (attrParts && attrParts.length > 0) {
|
|
164
169
|
payload += numeric(attrParts.length) + ';' + attrParts.join(';')
|
|
165
170
|
}
|