@newrelic/browser-agent 1.245.0 → 1.246.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 +14 -0
- package/dist/cjs/cdn/polyfills.js +2 -1
- package/dist/cjs/common/config/state/configurable.js +1 -1
- package/dist/cjs/common/config/state/init.js +1 -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/session/session-entity.js +3 -2
- package/dist/cjs/common/url/parse-url.js +20 -44
- package/dist/cjs/common/vitals/first-input-delay.js +1 -2
- package/dist/cjs/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/cjs/common/vitals/vital-metric.js +2 -12
- package/dist/cjs/features/ajax/aggregate/index.js +1 -0
- package/dist/cjs/features/jserrors/aggregate/index.js +1 -1
- package/dist/cjs/features/page_view_event/aggregate/index.js +2 -0
- package/dist/cjs/features/page_view_timing/aggregate/index.js +10 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +6 -6
- package/dist/cjs/features/session_trace/aggregate/index.js +14 -3
- package/dist/cjs/features/spa/aggregate/index.js +5 -3
- package/dist/cjs/features/utils/instrument-base.js +1 -1
- package/dist/esm/cdn/polyfills.js +2 -1
- package/dist/esm/common/config/state/configurable.js +1 -1
- package/dist/esm/common/config/state/init.js +1 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/session/session-entity.js +3 -2
- package/dist/esm/common/url/parse-url.js +21 -45
- package/dist/esm/common/vitals/first-input-delay.js +1 -2
- package/dist/esm/common/vitals/largest-contentful-paint.js +1 -2
- package/dist/esm/common/vitals/vital-metric.js +1 -11
- package/dist/esm/features/ajax/aggregate/index.js +1 -0
- package/dist/esm/features/jserrors/aggregate/index.js +1 -1
- package/dist/esm/features/page_view_event/aggregate/index.js +2 -0
- package/dist/esm/features/page_view_timing/aggregate/index.js +9 -0
- package/dist/esm/features/session_replay/aggregate/index.js +6 -6
- package/dist/esm/features/session_trace/aggregate/index.js +14 -3
- package/dist/esm/features/spa/aggregate/index.js +5 -3
- package/dist/esm/features/utils/instrument-base.js +1 -1
- package/dist/types/common/config/state/configurable.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/url/parse-url.d.ts +12 -1
- package/dist/types/common/url/parse-url.d.ts.map +1 -1
- package/dist/types/common/vitals/vital-metric.d.ts +1 -2
- package/dist/types/common/vitals/vital-metric.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +1 -0
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +1 -0
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/configure/public-path.npm.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cdn/polyfills.js +1 -0
- package/src/common/config/state/configurable.js +2 -1
- package/src/common/config/state/init.js +1 -0
- package/src/common/session/session-entity.js +3 -2
- package/src/common/url/parse-url.js +21 -51
- package/src/common/vitals/first-input-delay.js +1 -2
- package/src/common/vitals/largest-contentful-paint.js +1 -2
- package/src/common/vitals/vital-metric.js +2 -12
- package/src/features/ajax/aggregate/index.js +2 -0
- package/src/features/jserrors/aggregate/index.js +1 -1
- package/src/features/page_view_event/aggregate/index.js +2 -0
- package/src/features/page_view_timing/aggregate/index.js +11 -0
- package/src/features/session_replay/aggregate/index.js +6 -6
- package/src/features/session_trace/aggregate/index.js +10 -2
- package/src/features/spa/aggregate/index.js +5 -3
- package/src/features/utils/instrument-base.js +1 -1
- package/src/loaders/configure/public-path.npm.js +0 -1
|
@@ -100,6 +100,7 @@ export class Aggregate extends AggregateBase {
|
|
|
100
100
|
}
|
|
101
101
|
addTiming(name, value, attrs) {
|
|
102
102
|
attrs = attrs || {};
|
|
103
|
+
addConnectionAttributes(attrs); // network conditions may differ from the actual for VitalMetrics when they were captured
|
|
103
104
|
|
|
104
105
|
// If cls was set to another value by `onCLS`, then it's supported and is attached onto any timing but is omitted until such time.
|
|
105
106
|
/*
|
|
@@ -170,4 +171,12 @@ export class Aggregate extends AggregateBase {
|
|
|
170
171
|
}
|
|
171
172
|
return payload;
|
|
172
173
|
}
|
|
174
|
+
}
|
|
175
|
+
function addConnectionAttributes(obj) {
|
|
176
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection; // to date, both window & worker shares the same support for connection
|
|
177
|
+
if (!connection) return;
|
|
178
|
+
if (connection.type) obj['net-type'] = connection.type;
|
|
179
|
+
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType;
|
|
180
|
+
if (connection.rtt) obj['net-rtt'] = connection.rtt;
|
|
181
|
+
if (connection.downlink) obj['net-dlink'] = connection.downlink;
|
|
173
182
|
}
|
|
@@ -131,13 +131,13 @@ export class Aggregate extends AggregateBase {
|
|
|
131
131
|
const {
|
|
132
132
|
session
|
|
133
133
|
} = getRuntime(this.agentIdentifier);
|
|
134
|
-
this.mode = session.state.
|
|
134
|
+
this.mode = session.state.sessionReplayMode;
|
|
135
135
|
if (!this.initialized || this.mode === MODE.OFF) return;
|
|
136
136
|
this.startRecording();
|
|
137
137
|
});
|
|
138
138
|
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
139
139
|
if (!this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return;
|
|
140
|
-
if (this.mode !== MODE.OFF && data.
|
|
140
|
+
if (this.mode !== MODE.OFF && data.sessionReplayMode === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
141
141
|
this.mode = data.sessionReplay;
|
|
142
142
|
});
|
|
143
143
|
|
|
@@ -163,7 +163,7 @@ export class Aggregate extends AggregateBase {
|
|
|
163
163
|
this.startRecording();
|
|
164
164
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
165
165
|
this.syncWithSessionManager({
|
|
166
|
-
|
|
166
|
+
sessionReplayMode: this.mode
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -198,7 +198,7 @@ export class Aggregate extends AggregateBase {
|
|
|
198
198
|
// session replays can continue if already in progress
|
|
199
199
|
if (!session.isNew) {
|
|
200
200
|
// inherit the mode of the existing session
|
|
201
|
-
this.mode = session.state.
|
|
201
|
+
this.mode = session.state.sessionReplayMode;
|
|
202
202
|
} else {
|
|
203
203
|
// The session is new... determine the mode the new session should start in
|
|
204
204
|
if (fullSample) this.mode = MODE.FULL; // full mode has precedence over error mode
|
|
@@ -239,7 +239,7 @@ export class Aggregate extends AggregateBase {
|
|
|
239
239
|
}
|
|
240
240
|
this.startRecording();
|
|
241
241
|
this.syncWithSessionManager({
|
|
242
|
-
|
|
242
|
+
sessionReplayMode: this.mode
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
prepareHarvest() {
|
|
@@ -448,7 +448,7 @@ export class Aggregate extends AggregateBase {
|
|
|
448
448
|
this.mode = MODE.OFF;
|
|
449
449
|
this.stopRecording();
|
|
450
450
|
this.syncWithSessionManager({
|
|
451
|
-
|
|
451
|
+
sessionReplayMode: this.mode
|
|
452
452
|
});
|
|
453
453
|
this.clearTimestamps();
|
|
454
454
|
this.ee.emit('REPLAY_ABORTED');
|
|
@@ -141,7 +141,7 @@ export class Aggregate extends AggregateBase {
|
|
|
141
141
|
});
|
|
142
142
|
if (!sessionEntity.isNew) {
|
|
143
143
|
// inherit the same mode as existing session's Trace
|
|
144
|
-
if (sessionEntity.state.
|
|
144
|
+
if (sessionEntity.state.sessionReplayMode === MODE.OFF) this.isStandalone = true;
|
|
145
145
|
controlTraceOp(mostRecentModeKnown = sessionEntity.state.sessionTraceMode);
|
|
146
146
|
} else {
|
|
147
147
|
// for new sessions, see the truth table associated with NEWRELIC-8662 wrt the new Trace behavior under session management
|
|
@@ -503,6 +503,16 @@ export class Aggregate extends AggregateBase {
|
|
|
503
503
|
}
|
|
504
504
|
this.trace = {};
|
|
505
505
|
this.nodeCount = 0;
|
|
506
|
+
let firstHarvestOfSession;
|
|
507
|
+
if (this.agentRuntime.session) {
|
|
508
|
+
const isFirstPayload = !this.agentRuntime.session.state.traceHarvestStarted;
|
|
509
|
+
firstHarvestOfSession = {
|
|
510
|
+
fsh: Number(isFirstPayload)
|
|
511
|
+
}; // converted to '0' | '1'
|
|
512
|
+
if (isFirstPayload) this.agentRuntime.session.write({
|
|
513
|
+
traceHarvestStarted: true
|
|
514
|
+
});
|
|
515
|
+
}
|
|
506
516
|
return {
|
|
507
517
|
qs: {
|
|
508
518
|
st: this.agentRuntime.offset,
|
|
@@ -513,9 +523,10 @@ export class Aggregate extends AggregateBase {
|
|
|
513
523
|
* so that blob parsing doesn't need to happen to support UI/API functions */
|
|
514
524
|
fts: this.agentRuntime.offset + earliestTimeStamp,
|
|
515
525
|
/** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
|
|
516
|
-
n: stns.length
|
|
526
|
+
n: stns.length,
|
|
527
|
+
// node count
|
|
528
|
+
...firstHarvestOfSession
|
|
517
529
|
},
|
|
518
|
-
|
|
519
530
|
body: {
|
|
520
531
|
res: stns
|
|
521
532
|
}
|
|
@@ -58,7 +58,9 @@ export class Aggregate extends AggregateBase {
|
|
|
58
58
|
depth: 0,
|
|
59
59
|
harvestTimeSeconds: getConfigurationValue(agentIdentifier, 'spa.harvestTimeSeconds') || 10,
|
|
60
60
|
interactionsToHarvest: [],
|
|
61
|
-
interactionsSent: []
|
|
61
|
+
interactionsSent: [],
|
|
62
|
+
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
63
|
+
disableSpaFix: (getConfigurationValue(agentIdentifier, 'feature_flags') || []).indexOf('disable-spa-fix') > -1
|
|
62
64
|
};
|
|
63
65
|
this.serializer = new Serializer(this);
|
|
64
66
|
const {
|
|
@@ -278,7 +280,7 @@ export class Aggregate extends AggregateBase {
|
|
|
278
280
|
// context is stored on the xhr and is shared with all callbacks associated
|
|
279
281
|
// with the new xhr
|
|
280
282
|
register('new-xhr', function () {
|
|
281
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
283
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
282
284
|
/*
|
|
283
285
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
284
286
|
* in case this XHR is associated with a route change.
|
|
@@ -369,7 +371,7 @@ export class Aggregate extends AggregateBase {
|
|
|
369
371
|
}, this.featureName, jsonpEE);
|
|
370
372
|
register(FETCH_START, function (fetchArguments, dtPayload) {
|
|
371
373
|
if (fetchArguments) {
|
|
372
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
374
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
373
375
|
/*
|
|
374
376
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
375
377
|
* in case this XHR is associated with a route change.
|
|
@@ -134,7 +134,7 @@ export class InstrumentBase extends FeatureBase {
|
|
|
134
134
|
if (featureName === FEATURE_NAMES.sessionReplay) {
|
|
135
135
|
if (!originals.MO) return false; // Session Replay cannot work without Mutation Observer
|
|
136
136
|
if (getConfigurationValue(this.agentIdentifier, 'session_trace.enabled') === false) return false; // Session Replay as of now is tightly coupled with Session Trace in the UI
|
|
137
|
-
return !!session?.isNew || !!session?.state.
|
|
137
|
+
return !!session?.isNew || !!session?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"configurable.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/configurable.js"],"names":[],"mappings":"AAEA,
|
|
1
|
+
{"version":3,"file":"configurable.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/configurable.js"],"names":[],"mappings":"AAEA,4DAyBC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../src/common/config/state/init.js"],"names":[],"mappings":"AA2GA,+CAIC;AAED,0DAIC;AAED,+DAYC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"session-entity.d.ts","sourceRoot":"","sources":["../../../../src/common/session/session-entity.js"],"names":[],"mappings":";;;;;;;;;;;;;;;AA6CA;IACE;;;;;OAKG;IACH,uBA0BC;IApBC,qBAAsC;IACtC,aAAsB;IACtB,UAAe;IAGf,SAAc;IAEd,QAAiC;IAenC;;;;aAsEC;IA/DC,8BAA0B;IAC1B,+BAA4B;IAc1B,gCAOqC;IAUrC,4CAiBsC;IAOxC,2BAA6C;IAM7C,iCAAuB;IAIzB,wBAEC;IAED,sBAEC;IAED;;;OAGG;IACH,QAFa,MAAM,CA6BlB;IAED;;;;;;OAMG;IACH,YAHW,MAAM,GACJ,MAAM,CAkBlB;IAED,gBAuBC;IAED;;OAEG;IACH,gBAIC;IAED;;;OAGG;IACH,qBAHW,MAAM,GACJ,OAAO,CAInB;IAED;;;OAGG;IACH,gBAHW,MAAM,GACJ,OAAO,CAKnB;IAED,yDAYC;IAED;;;OAGG;IACH,6BAHW,MAAM,GACJ,MAAM,CAIlB;IAED,gDAaC;IAHG,YAAuD;CAI5D;sBA9SqB,gBAAgB;iCAGL,4BAA4B"}
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
-
export function parseUrl(url: any):
|
|
1
|
+
export function parseUrl(url: any): {
|
|
2
|
+
port: string;
|
|
3
|
+
hostname: string;
|
|
4
|
+
pathname: string;
|
|
5
|
+
search: string;
|
|
6
|
+
protocol: string;
|
|
7
|
+
sameOrigin: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
protocol: string;
|
|
10
|
+
} | {
|
|
11
|
+
protocol?: undefined;
|
|
12
|
+
};
|
|
2
13
|
//# sourceMappingURL=parse-url.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/parse-url.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parse-url.d.ts","sourceRoot":"","sources":["../../../../src/common/url/parse-url.js"],"names":[],"mappings":"AAOA;;;;;;;;;;;EAkCC"}
|
|
@@ -4,11 +4,10 @@ export class VitalMetric {
|
|
|
4
4
|
name: any;
|
|
5
5
|
attrs: {};
|
|
6
6
|
roundingMethod: any;
|
|
7
|
-
update({ value, entries, attrs
|
|
7
|
+
update({ value, entries, attrs }: {
|
|
8
8
|
value: any;
|
|
9
9
|
entries?: any[] | undefined;
|
|
10
10
|
attrs?: {} | undefined;
|
|
11
|
-
shouldAddConnectionAttributes?: boolean | undefined;
|
|
12
11
|
}): void;
|
|
13
12
|
get current(): any;
|
|
14
13
|
get isValid(): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vital-metric.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/vital-metric.js"],"names":[],"mappings":"AAAA;IAIE,4CAIC;IAND,eAAY;IAGV,UAAgB;IAChB,UAAe;IACf,oBAAwF;IAG1F
|
|
1
|
+
{"version":3,"file":"vital-metric.d.ts","sourceRoot":"","sources":["../../../../src/common/vitals/vital-metric.js"],"names":[],"mappings":"AAAA;IAIE,4CAIC;IAND,eAAY;IAGV,UAAgB;IAChB,UAAe;IACf,oBAAwF;IAG1F;;;;aAiBC;IAED,mBAOC;IAED,uBAEC;IAED,uEAMC;;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/ajax/aggregate/index.js"],"names":[],"mappings":"AAiBA;IACE,2BAAiC;IACjC,mDAyOC;IAlNC,qGAAwB;IACxB;;;;4BAAoC;IACpC;;;mBAA2E;CAiN9E;8BA/O6B,4BAA4B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_event/aggregate/index.js"],"names":[],"mappings":"AAcA;IACE,2BAA2C;IAC3C,mDAoBC;IAjBC,wBAAwB;IACxB,8BAA8B;IAC9B,8BAA8B;IAiBhC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_event/aggregate/index.js"],"names":[],"mappings":"AAcA;IACE,2BAA2C;IAC3C,mDAoBC;IAjBC,wBAAwB;IACxB,8BAA8B;IAC9B,8BAA8B;IAiBhC,gBAgFC;CACF;8BA9G6B,4BAA4B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AAuBA;IACE,2BAAiC;IAMjC,mDAoCC;IAjCC,eAAiB;IACjB,mBAAqB;IACrB,4BAA+B;IAuB7B,4BAGQ;IAOZ;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED;;OAEG;IACH,uCAUC;IAED,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/page_view_timing/aggregate/index.js"],"names":[],"mappings":"AAuBA;IACE,2BAAiC;IAMjC,mDAoCC;IAjCC,eAAiB;IACjB,mBAAqB;IACrB,4BAA+B;IAuB7B,4BAGQ;IAOZ;;;OAGG;IACH,6BAFW,MAAM,QAOhB;IAED;;OAEG;IACH,uCAUC;IAED,mDAsBC;IAED,qCAKC;IAED,gDAWC;IAGD;;;;kBAWC;IAGD,8BAuBC;;CACF;8BApK6B,4BAA4B;iCANzB,2CAA2C"}
|
|
@@ -37,6 +37,7 @@ export class Aggregate extends AggregateBase {
|
|
|
37
37
|
body?: undefined;
|
|
38
38
|
} | {
|
|
39
39
|
qs: {
|
|
40
|
+
fsh?: number | undefined;
|
|
40
41
|
st: any;
|
|
41
42
|
/** hr === "hasReplay" in NR1, standalone is always checked and processed before harvesting
|
|
42
43
|
* so a race condition between ST and SR states should not be a concern if implemented here */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAiCA;IACE,2BAAiC;IAGjC,iEAyHC;IAvHC,kBAA+C;IAK/C,sBAAiD;IACjD,yBAAc;IACd,sBAAe;IACf,8BAAkB;IAClB,iCAAqB;IACrB,wBAA0G;IAC1G,wBAA4G;IAC5G;;4EAEwE;IACxE,kCAAyB;IAGzB,0CAAsC;IAuGxC,sEAcC;IA6CD,oDAOC;IAGD,oCAwBC;IAGD,uEAkBC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDASC;IAID,qCAkBC;IAGD,qEAUC;IAGD,mEAUC;IAGD,yBAeC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAGD
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/session_trace/aggregate/index.js"],"names":[],"mappings":"AAiCA;IACE,2BAAiC;IAGjC,iEAyHC;IAvHC,kBAA+C;IAK/C,sBAAiD;IACjD,yBAAc;IACd,sBAAe;IACf,8BAAkB;IAClB,iCAAqB;IACrB,wBAA0G;IAC1G,wBAA4G;IAC5G;;4EAEwE;IACxE,kCAAyB;IAGzB,0CAAsC;IAuGxC,sEAcC;IA6CD,oDAOC;IAGD,oCAwBC;IAGD,uEAkBC;IAED,oDAKC;IAED,wBAwBC;IAED,uCAuBC;IAGD,gDASC;IAID,qCAkBC;IAGD,qEAUC;IAGD,mEAUC;IAGD,yBAeC;IAED;;;;OAIG;IACH,2BAHW,MAAM,GACJ,MAAM,CAsBlB;IAGD;;;;;;;YAkCM;0GAC8F;;YAE9F;0FAC8E;;YAE9E,4IAA4I;;;;;;MAMjJ;IAED,mEA6BC;;CACF;8BAzf6B,4BAA4B;6BAH7B,2BAA2B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA4BA;IACE,2BAAiC;IACjC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/features/spa/aggregate/index.js"],"names":[],"mappings":"AA4BA;IACE,2BAAiC;IACjC,mDAgsBC;IA7rBC;;;;;;;;;;;;;;;;;MAkBC;IAED,uBAAsC;CA0qBzC;8BA5sB6B,4BAA4B;2BAJ/B,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-path.npm.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/public-path.npm.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"public-path.npm.d.ts","sourceRoot":"","sources":["../../../../src/loaders/configure/public-path.npm.js"],"names":[],"mappings":"AAAO,2CAEN"}
|
package/package.json
CHANGED
package/src/cdn/polyfills.js
CHANGED
|
@@ -13,7 +13,8 @@ export function getModeledObject (obj, model) {
|
|
|
13
13
|
for (let key in target) {
|
|
14
14
|
if (obj[key] !== undefined) {
|
|
15
15
|
try {
|
|
16
|
-
if (
|
|
16
|
+
if (Array.isArray(obj[key]) && Array.isArray(model[key])) output[key] = Array.from(new Set([...obj[key], ...model[key]]))
|
|
17
|
+
else if (typeof obj[key] === 'object' && typeof model[key] === 'object') output[key] = getModeledObject(obj[key], model[key])
|
|
17
18
|
else output[key] = obj[key]
|
|
18
19
|
} catch (e) {
|
|
19
20
|
warn('An error occurred while setting a property of a Configurable', e)
|
|
@@ -29,6 +29,7 @@ const model = () => {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
return {
|
|
32
|
+
feature_flags: [],
|
|
32
33
|
proxy: {
|
|
33
34
|
assets: undefined, // if this value is set, it will be used to overwrite the webpack asset path used to fetch assets
|
|
34
35
|
beacon: undefined // likewise for the url to which we send analytics
|
|
@@ -25,9 +25,10 @@ const model = {
|
|
|
25
25
|
inactiveAt: 0,
|
|
26
26
|
expiresAt: 0,
|
|
27
27
|
updatedAt: Date.now(),
|
|
28
|
-
|
|
28
|
+
sessionReplayMode: MODE.OFF,
|
|
29
29
|
sessionReplaySentFirstChunk: false,
|
|
30
30
|
sessionTraceMode: MODE.OFF,
|
|
31
|
+
traceHarvestStarted: false,
|
|
31
32
|
custom: {}
|
|
32
33
|
}
|
|
33
34
|
export const SESSION_EVENTS = {
|
|
@@ -140,7 +141,7 @@ export class SessionEntity {
|
|
|
140
141
|
|
|
141
142
|
// The fact that the session is "new" or pre-existing is used in some places in the agent. Session Replay and Trace
|
|
142
143
|
// can use this info to inform whether to trust a new sampling decision vs continue a previous tracking effort.
|
|
143
|
-
|
|
144
|
+
this.isNew = !Object.keys(initialRead).length
|
|
144
145
|
// if its a "new" session, we write to storage API with the default values. These values may change over the lifespan of the agent run.
|
|
145
146
|
// we can use a modeled object here to help us know and manage what values are being used. -- see "model" above
|
|
146
147
|
if (this.isNew) this.write(getModeledObject(this.state, model), true)
|
|
@@ -3,15 +3,9 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { globalScope
|
|
7
|
-
|
|
8
|
-
var stringsToParsedUrls = {}
|
|
6
|
+
import { globalScope } from '../constants/runtime'
|
|
9
7
|
|
|
10
8
|
export function parseUrl (url) {
|
|
11
|
-
if (url in stringsToParsedUrls) {
|
|
12
|
-
return stringsToParsedUrls[url]
|
|
13
|
-
}
|
|
14
|
-
|
|
15
9
|
// Return if URL is a data URL, parseUrl assumes urls are http/https
|
|
16
10
|
if ((url || '').indexOf('data:') === 0) {
|
|
17
11
|
return {
|
|
@@ -19,54 +13,30 @@ export function parseUrl (url) {
|
|
|
19
13
|
}
|
|
20
14
|
}
|
|
21
15
|
|
|
22
|
-
let urlEl
|
|
23
|
-
var location = globalScope?.location
|
|
24
|
-
var ret = {}
|
|
25
|
-
|
|
26
16
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
17
|
+
const parsedUrl = new URL(url, location.href)
|
|
18
|
+
const returnVal = {
|
|
19
|
+
port: parsedUrl.port,
|
|
20
|
+
hostname: parsedUrl.hostname,
|
|
21
|
+
pathname: parsedUrl.pathname,
|
|
22
|
+
search: parsedUrl.search,
|
|
23
|
+
protocol: parsedUrl.protocol.slice(0, parsedUrl.protocol.indexOf(':')),
|
|
24
|
+
sameOrigin: parsedUrl.protocol === globalScope?.location?.protocol && parsedUrl.host === globalScope?.location?.host
|
|
35
25
|
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
ret.port = urlEl.port
|
|
39
|
-
|
|
40
|
-
ret.search = urlEl.search
|
|
41
26
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
if (!ret.port || ret.port === '0') ret.port = (firstSplit[0] === 'https' ? '443' : '80')
|
|
48
|
-
|
|
49
|
-
// Host not provided in IE for relative urls
|
|
50
|
-
ret.hostname = (urlEl.hostname || location.hostname)
|
|
51
|
-
|
|
52
|
-
ret.pathname = urlEl.pathname
|
|
53
|
-
|
|
54
|
-
ret.protocol = firstSplit[0]
|
|
55
|
-
|
|
56
|
-
// Pathname sometimes doesn't have leading slash (IE 8 and 9)
|
|
57
|
-
if (ret.pathname.charAt(0) !== '/') ret.pathname = '/' + ret.pathname
|
|
58
|
-
|
|
59
|
-
// urlEl.protocol is ':' in old ie when protocol is not specified
|
|
60
|
-
var sameProtocol = !urlEl.protocol || urlEl.protocol === ':' || urlEl.protocol === location.protocol
|
|
61
|
-
var sameDomain = urlEl.hostname === location.hostname && urlEl.port === location.port
|
|
27
|
+
if (!returnVal.port || returnVal.port === '') {
|
|
28
|
+
if (parsedUrl.protocol === 'http:') returnVal.port = '80'
|
|
29
|
+
if (parsedUrl.protocol === 'https:') returnVal.port = '443'
|
|
30
|
+
}
|
|
62
31
|
|
|
63
|
-
|
|
64
|
-
|
|
32
|
+
if (!returnVal.pathname || returnVal.pathname === '') {
|
|
33
|
+
returnVal.pathname = '/'
|
|
34
|
+
} else if (!returnVal.pathname.startsWith('/')) {
|
|
35
|
+
returnVal.pathname = `/${returnVal.pathname}`
|
|
36
|
+
}
|
|
65
37
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
38
|
+
return returnVal
|
|
39
|
+
} catch (err) {
|
|
40
|
+
return {}
|
|
69
41
|
}
|
|
70
|
-
|
|
71
|
-
return ret
|
|
72
42
|
}
|
|
@@ -13,8 +13,7 @@ if (isBrowserScope) {
|
|
|
13
13
|
firstInputDelay.update({
|
|
14
14
|
value: entries[0].startTime,
|
|
15
15
|
entries,
|
|
16
|
-
attrs: { type: entries[0].name, fid: Math.round(value) }
|
|
17
|
-
shouldAddConnectionAttributes: true
|
|
16
|
+
attrs: { type: entries[0].name, fid: Math.round(value) }
|
|
18
17
|
})
|
|
19
18
|
})
|
|
20
19
|
}
|
|
@@ -8,7 +8,7 @@ export class VitalMetric {
|
|
|
8
8
|
this.roundingMethod = typeof roundingMethod === 'function' ? roundingMethod : Math.floor
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
update ({ value, entries = [], attrs = {}
|
|
11
|
+
update ({ value, entries = [], attrs = {} }) {
|
|
12
12
|
if (value < 0) return
|
|
13
13
|
const state = {
|
|
14
14
|
value: this.roundingMethod(value),
|
|
@@ -16,7 +16,7 @@ export class VitalMetric {
|
|
|
16
16
|
entries,
|
|
17
17
|
attrs
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
this.history.push(state)
|
|
21
21
|
this.#subscribers.forEach(cb => {
|
|
22
22
|
try {
|
|
@@ -48,13 +48,3 @@ export class VitalMetric {
|
|
|
48
48
|
return () => { this.#subscribers.delete(callback) }
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
function addConnectionAttributes (obj) {
|
|
53
|
-
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection // to date, both window & worker shares the same support for connection
|
|
54
|
-
if (!connection) return
|
|
55
|
-
|
|
56
|
-
if (connection.type) obj['net-type'] = connection.type
|
|
57
|
-
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType
|
|
58
|
-
if (connection.rtt) obj['net-rtt'] = connection.rtt
|
|
59
|
-
if (connection.downlink) obj['net-dlink'] = connection.downlink
|
|
60
|
-
}
|
|
@@ -126,6 +126,8 @@ export class Aggregate extends AggregateBase {
|
|
|
126
126
|
query: this?.parsedOrigin?.search
|
|
127
127
|
})
|
|
128
128
|
|
|
129
|
+
if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, ee)
|
|
130
|
+
|
|
129
131
|
// if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
130
132
|
if (this.spaNode) {
|
|
131
133
|
var interactionId = this.spaNode.interaction.id
|
|
@@ -191,7 +191,7 @@ export class Aggregate extends AggregateBase {
|
|
|
191
191
|
this.pageviewReported[bucketHash] = true
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
if (agentRuntime?.session?.state?.
|
|
194
|
+
if (agentRuntime?.session?.state?.sessionReplayMode) params.hasReplay = true
|
|
195
195
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash]
|
|
196
196
|
|
|
197
197
|
var type = internal ? 'ierr' : 'err'
|
|
@@ -69,6 +69,8 @@ export class Aggregate extends AggregateBase {
|
|
|
69
69
|
at: info.atts
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
if (agentRuntime.session) queryParameters.fsh = Number(agentRuntime.session.isNew) // "first session harvest" aka RUM request or PageView event of a session
|
|
73
|
+
|
|
72
74
|
let body
|
|
73
75
|
if (typeof info.jsAttributes === 'object' && Object.keys(info.jsAttributes).length > 0) {
|
|
74
76
|
body = { ja: info.jsAttributes }
|
|
@@ -94,6 +94,7 @@ export class Aggregate extends AggregateBase {
|
|
|
94
94
|
|
|
95
95
|
addTiming (name, value, attrs) {
|
|
96
96
|
attrs = attrs || {}
|
|
97
|
+
addConnectionAttributes(attrs) // network conditions may differ from the actual for VitalMetrics when they were captured
|
|
97
98
|
|
|
98
99
|
// If cls was set to another value by `onCLS`, then it's supported and is attached onto any timing but is omitted until such time.
|
|
99
100
|
/*
|
|
@@ -175,3 +176,13 @@ export class Aggregate extends AggregateBase {
|
|
|
175
176
|
return payload
|
|
176
177
|
}
|
|
177
178
|
}
|
|
179
|
+
|
|
180
|
+
function addConnectionAttributes (obj) {
|
|
181
|
+
var connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection // to date, both window & worker shares the same support for connection
|
|
182
|
+
if (!connection) return
|
|
183
|
+
|
|
184
|
+
if (connection.type) obj['net-type'] = connection.type
|
|
185
|
+
if (connection.effectiveType) obj['net-etype'] = connection.effectiveType
|
|
186
|
+
if (connection.rtt) obj['net-rtt'] = connection.rtt
|
|
187
|
+
if (connection.downlink) obj['net-dlink'] = connection.downlink
|
|
188
|
+
}
|
|
@@ -133,14 +133,14 @@ export class Aggregate extends AggregateBase {
|
|
|
133
133
|
this.ee.on(SESSION_EVENTS.RESUME, () => {
|
|
134
134
|
// if the mode changed on a different tab, it needs to update this instance to match
|
|
135
135
|
const { session } = getRuntime(this.agentIdentifier)
|
|
136
|
-
this.mode = session.state.
|
|
136
|
+
this.mode = session.state.sessionReplayMode
|
|
137
137
|
if (!this.initialized || this.mode === MODE.OFF) return
|
|
138
138
|
this.startRecording()
|
|
139
139
|
})
|
|
140
140
|
|
|
141
141
|
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
142
142
|
if (!this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return
|
|
143
|
-
if (this.mode !== MODE.OFF && data.
|
|
143
|
+
if (this.mode !== MODE.OFF && data.sessionReplayMode === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB)
|
|
144
144
|
this.mode = data.sessionReplay
|
|
145
145
|
})
|
|
146
146
|
|
|
@@ -167,7 +167,7 @@ export class Aggregate extends AggregateBase {
|
|
|
167
167
|
|
|
168
168
|
this.scheduler.startTimer(this.harvestTimeSeconds)
|
|
169
169
|
|
|
170
|
-
this.syncWithSessionManager({
|
|
170
|
+
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
}, this.featureName, this.ee)
|
|
@@ -201,7 +201,7 @@ export class Aggregate extends AggregateBase {
|
|
|
201
201
|
// session replay samples can only be decided on the first load of a session
|
|
202
202
|
// session replays can continue if already in progress
|
|
203
203
|
if (!session.isNew) { // inherit the mode of the existing session
|
|
204
|
-
this.mode = session.state.
|
|
204
|
+
this.mode = session.state.sessionReplayMode
|
|
205
205
|
} else {
|
|
206
206
|
// The session is new... determine the mode the new session should start in
|
|
207
207
|
if (fullSample) this.mode = MODE.FULL // full mode has precedence over error mode
|
|
@@ -241,7 +241,7 @@ export class Aggregate extends AggregateBase {
|
|
|
241
241
|
}
|
|
242
242
|
this.startRecording()
|
|
243
243
|
|
|
244
|
-
this.syncWithSessionManager({
|
|
244
|
+
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
245
245
|
}
|
|
246
246
|
|
|
247
247
|
prepareHarvest () {
|
|
@@ -439,7 +439,7 @@ export class Aggregate extends AggregateBase {
|
|
|
439
439
|
this.blocked = true
|
|
440
440
|
this.mode = MODE.OFF
|
|
441
441
|
this.stopRecording()
|
|
442
|
-
this.syncWithSessionManager({
|
|
442
|
+
this.syncWithSessionManager({ sessionReplayMode: this.mode })
|
|
443
443
|
this.clearTimestamps()
|
|
444
444
|
this.ee.emit('REPLAY_ABORTED')
|
|
445
445
|
}
|
|
@@ -125,7 +125,7 @@ export class Aggregate extends AggregateBase {
|
|
|
125
125
|
this.ee.on(SESSION_EVENTS.PAUSE, () => { mostRecentModeKnown = sessionEntity.state.sessionTraceMode })
|
|
126
126
|
|
|
127
127
|
if (!sessionEntity.isNew) { // inherit the same mode as existing session's Trace
|
|
128
|
-
if (sessionEntity.state.
|
|
128
|
+
if (sessionEntity.state.sessionReplayMode === MODE.OFF) this.isStandalone = true
|
|
129
129
|
controlTraceOp(mostRecentModeKnown = sessionEntity.state.sessionTraceMode)
|
|
130
130
|
} else { // for new sessions, see the truth table associated with NEWRELIC-8662 wrt the new Trace behavior under session management
|
|
131
131
|
const replayMode = await getSessionReplayMode(agentIdentifier)
|
|
@@ -462,6 +462,13 @@ export class Aggregate extends AggregateBase {
|
|
|
462
462
|
this.trace = {}
|
|
463
463
|
this.nodeCount = 0
|
|
464
464
|
|
|
465
|
+
let firstHarvestOfSession
|
|
466
|
+
if (this.agentRuntime.session) {
|
|
467
|
+
const isFirstPayload = !this.agentRuntime.session.state.traceHarvestStarted
|
|
468
|
+
firstHarvestOfSession = { fsh: Number(isFirstPayload) } // converted to '0' | '1'
|
|
469
|
+
if (isFirstPayload) this.agentRuntime.session.write({ traceHarvestStarted: true })
|
|
470
|
+
}
|
|
471
|
+
|
|
465
472
|
return {
|
|
466
473
|
qs: {
|
|
467
474
|
st: this.agentRuntime.offset,
|
|
@@ -472,7 +479,8 @@ export class Aggregate extends AggregateBase {
|
|
|
472
479
|
* so that blob parsing doesn't need to happen to support UI/API functions */
|
|
473
480
|
fts: this.agentRuntime.offset + earliestTimeStamp,
|
|
474
481
|
/** n === "nodeCount" in NR1, a count of nodes in the ST payload, so that blob parsing doesn't need to happen to support UI/API functions */
|
|
475
|
-
n: stns.length // node count
|
|
482
|
+
n: stns.length, // node count
|
|
483
|
+
...firstHarvestOfSession
|
|
476
484
|
},
|
|
477
485
|
body: { res: stns }
|
|
478
486
|
}
|
|
@@ -46,7 +46,9 @@ export class Aggregate extends AggregateBase {
|
|
|
46
46
|
depth: 0,
|
|
47
47
|
harvestTimeSeconds: getConfigurationValue(agentIdentifier, 'spa.harvestTimeSeconds') || 10,
|
|
48
48
|
interactionsToHarvest: [],
|
|
49
|
-
interactionsSent: []
|
|
49
|
+
interactionsSent: [],
|
|
50
|
+
// The below feature flag is used to disable the SPA ajax fix for specific customers, see https://new-relic.atlassian.net/browse/NR-172169
|
|
51
|
+
disableSpaFix: (getConfigurationValue(agentIdentifier, 'feature_flags') || []).indexOf('disable-spa-fix') > -1
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
this.serializer = new Serializer(this)
|
|
@@ -279,7 +281,7 @@ export class Aggregate extends AggregateBase {
|
|
|
279
281
|
// context is stored on the xhr and is shared with all callbacks associated
|
|
280
282
|
// with the new xhr
|
|
281
283
|
register('new-xhr', function () {
|
|
282
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
284
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
283
285
|
/*
|
|
284
286
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
285
287
|
* in case this XHR is associated with a route change.
|
|
@@ -382,7 +384,7 @@ export class Aggregate extends AggregateBase {
|
|
|
382
384
|
|
|
383
385
|
register(FETCH_START, function (fetchArguments, dtPayload) {
|
|
384
386
|
if (fetchArguments) {
|
|
385
|
-
if (!state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
387
|
+
if (!state.disableSpaFix && !state.currentNode && state.prevInteraction && !state.prevInteraction.ignored) {
|
|
386
388
|
/*
|
|
387
389
|
* The previous interaction was discarded before a route change. Restore the interaction
|
|
388
390
|
* in case this XHR is associated with a route change.
|