@newrelic/browser-agent 1.254.1 → 1.256.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 +25 -0
- package/dist/cjs/common/config/state/runtime.js +2 -1
- package/dist/cjs/common/constants/env.cdn.js +2 -2
- package/dist/cjs/common/constants/env.npm.js +2 -2
- package/dist/cjs/common/constants/runtime.js +1 -1
- package/dist/cjs/common/context/shared-context.js +1 -1
- package/dist/cjs/common/harvest/harvest.js +2 -1
- package/dist/cjs/common/session/session-entity.js +2 -1
- package/dist/cjs/common/timer/interaction-timer.js +16 -2
- package/dist/cjs/common/timing/time-keeper.js +23 -19
- package/dist/cjs/common/vitals/cumulative-layout-shift.js +12 -5
- package/dist/cjs/common/vitals/first-contentful-paint.js +10 -6
- package/dist/cjs/common/vitals/first-input-delay.js +12 -10
- package/dist/cjs/common/vitals/first-paint.js +1 -2
- package/dist/cjs/common/vitals/interaction-to-next-paint.js +11 -7
- package/dist/cjs/common/vitals/largest-contentful-paint.js +19 -17
- package/dist/cjs/common/vitals/long-task.js +0 -1
- package/dist/cjs/common/vitals/time-to-first-byte.js +11 -6
- package/dist/cjs/common/vitals/vital-metric.js +1 -4
- package/dist/cjs/common/window/nreum.js +1 -1
- package/dist/cjs/features/ajax/aggregate/index.js +3 -2
- package/dist/cjs/features/ajax/instrument/index.js +1 -1
- package/dist/cjs/features/jserrors/aggregate/index.js +19 -8
- package/dist/cjs/features/jserrors/instrument/index.js +9 -4
- package/dist/cjs/features/page_action/aggregate/index.js +3 -2
- package/dist/cjs/features/page_view_event/aggregate/index.js +10 -5
- package/dist/cjs/features/page_view_timing/aggregate/index.js +21 -7
- package/dist/cjs/features/page_view_timing/instrument/index.js +1 -1
- package/dist/cjs/features/session_replay/aggregate/index.js +59 -35
- package/dist/cjs/features/session_replay/constants.js +2 -1
- package/dist/cjs/features/session_replay/instrument/index.js +9 -2
- package/dist/cjs/features/session_replay/shared/recorder.js +20 -4
- package/dist/cjs/features/session_replay/shared/utils.js +12 -0
- package/dist/cjs/features/session_trace/aggregate/index.js +20 -23
- package/dist/cjs/features/session_trace/instrument/index.js +1 -1
- package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -2
- package/dist/cjs/features/soft_navigations/instrument/index.js +1 -1
- package/dist/cjs/features/spa/aggregate/index.js +19 -10
- package/dist/cjs/features/spa/instrument/index.js +1 -1
- package/dist/cjs/features/utils/feature-base.js +0 -2
- package/dist/cjs/loaders/agent-base.js +0 -2
- package/dist/cjs/loaders/agent.js +1 -1
- package/dist/cjs/loaders/api/api.js +8 -2
- package/dist/cjs/loaders/configure/configure.js +1 -0
- package/dist/cjs/loaders/micro-agent.js +4 -7
- package/dist/esm/common/config/state/runtime.js +2 -1
- package/dist/esm/common/constants/env.cdn.js +2 -2
- package/dist/esm/common/constants/env.npm.js +2 -2
- package/dist/esm/common/constants/runtime.js +1 -1
- package/dist/esm/common/context/shared-context.js +1 -1
- package/dist/esm/common/harvest/harvest.js +2 -1
- package/dist/esm/common/session/session-entity.js +2 -1
- package/dist/esm/common/timer/interaction-timer.js +16 -2
- package/dist/esm/common/timing/time-keeper.js +23 -20
- package/dist/esm/common/vitals/cumulative-layout-shift.js +11 -4
- package/dist/esm/common/vitals/first-contentful-paint.js +9 -5
- package/dist/esm/common/vitals/first-input-delay.js +11 -9
- package/dist/esm/common/vitals/first-paint.js +1 -2
- package/dist/esm/common/vitals/interaction-to-next-paint.js +10 -6
- package/dist/esm/common/vitals/largest-contentful-paint.js +18 -16
- package/dist/esm/common/vitals/long-task.js +0 -1
- package/dist/esm/common/vitals/time-to-first-byte.js +10 -5
- package/dist/esm/common/vitals/vital-metric.js +1 -4
- package/dist/esm/common/window/nreum.js +1 -1
- package/dist/esm/features/ajax/aggregate/index.js +3 -2
- package/dist/esm/features/ajax/instrument/index.js +1 -1
- package/dist/esm/features/jserrors/aggregate/index.js +19 -8
- package/dist/esm/features/jserrors/instrument/index.js +9 -4
- package/dist/esm/features/page_action/aggregate/index.js +3 -2
- package/dist/esm/features/page_view_event/aggregate/index.js +10 -5
- package/dist/esm/features/page_view_timing/aggregate/index.js +21 -7
- package/dist/esm/features/page_view_timing/instrument/index.js +1 -1
- package/dist/esm/features/session_replay/aggregate/index.js +59 -35
- package/dist/esm/features/session_replay/constants.js +2 -1
- package/dist/esm/features/session_replay/instrument/index.js +9 -2
- package/dist/esm/features/session_replay/shared/recorder.js +21 -5
- package/dist/esm/features/session_replay/shared/utils.js +11 -0
- package/dist/esm/features/session_trace/aggregate/index.js +20 -23
- package/dist/esm/features/session_trace/instrument/index.js +1 -1
- package/dist/esm/features/soft_navigations/aggregate/index.js +2 -2
- package/dist/esm/features/soft_navigations/instrument/index.js +1 -1
- package/dist/esm/features/spa/aggregate/index.js +19 -10
- package/dist/esm/features/spa/instrument/index.js +1 -1
- package/dist/esm/features/utils/feature-base.js +0 -2
- package/dist/esm/loaders/agent-base.js +0 -2
- package/dist/esm/loaders/agent.js +1 -1
- package/dist/esm/loaders/api/api.js +8 -2
- package/dist/esm/loaders/configure/configure.js +1 -0
- package/dist/esm/loaders/micro-agent.js +4 -7
- package/dist/types/common/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/timer/interaction-timer.d.ts +2 -0
- package/dist/types/common/timer/interaction-timer.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts +4 -9
- package/dist/types/common/timing/time-keeper.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/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +2 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/page_action/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/page_view_timing/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +9 -2
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +1 -0
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts +1 -0
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +8 -0
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +2 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
- package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +0 -2
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/utils/feature-base.d.ts +0 -1
- package/dist/types/features/utils/feature-base.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +0 -2
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/common/config/state/runtime.js +2 -1
- package/src/common/constants/runtime.js +1 -1
- package/src/common/context/__mocks__/shared-context.js +3 -0
- package/src/common/context/shared-context.js +1 -1
- package/src/common/harvest/harvest.js +2 -2
- package/src/common/session/session-entity.js +2 -1
- package/src/common/timer/interaction-timer.js +17 -2
- package/src/common/timing/__mocks__/time-keeper.js +2 -0
- package/src/common/timing/time-keeper.js +25 -22
- package/src/common/vitals/cumulative-layout-shift.js +10 -4
- package/src/common/vitals/first-contentful-paint.js +9 -4
- package/src/common/vitals/first-input-delay.js +11 -6
- package/src/common/vitals/first-paint.js +1 -1
- package/src/common/vitals/interaction-to-next-paint.js +10 -3
- package/src/common/vitals/largest-contentful-paint.js +19 -15
- package/src/common/vitals/long-task.js +0 -1
- package/src/common/vitals/time-to-first-byte.js +5 -4
- package/src/common/vitals/vital-metric.js +2 -4
- package/src/common/window/nreum.js +1 -1
- package/src/features/ajax/aggregate/index.js +3 -2
- package/src/features/ajax/instrument/index.js +1 -1
- package/src/features/jserrors/aggregate/index.js +18 -8
- package/src/features/jserrors/instrument/index.js +10 -5
- package/src/features/page_action/aggregate/index.js +3 -2
- package/src/features/page_view_event/aggregate/index.js +11 -5
- package/src/features/page_view_timing/aggregate/index.js +16 -6
- package/src/features/page_view_timing/instrument/index.js +1 -1
- package/src/features/session_replay/aggregate/index.js +53 -31
- package/src/features/session_replay/constants.js +2 -1
- package/src/features/session_replay/instrument/index.js +7 -2
- package/src/features/session_replay/shared/recorder.js +23 -5
- package/src/features/session_replay/shared/utils.js +12 -0
- package/src/features/session_trace/aggregate/index.js +19 -17
- package/src/features/session_trace/instrument/index.js +1 -1
- package/src/features/soft_navigations/aggregate/index.js +2 -2
- package/src/features/soft_navigations/instrument/index.js +1 -1
- package/src/features/spa/aggregate/index.js +19 -8
- package/src/features/spa/instrument/index.js +1 -1
- package/src/features/utils/feature-base.js +0 -3
- package/src/loaders/agent-base.js +0 -2
- package/src/loaders/agent.js +1 -1
- package/src/loaders/api/api.js +11 -2
- package/src/loaders/configure/configure.js +1 -0
- package/src/loaders/micro-agent.js +4 -6
- package/src/common/vitals/__mocks__/web-vitals.js +0 -19
|
@@ -14,13 +14,13 @@ var _stringify = require("../../../common/util/stringify");
|
|
|
14
14
|
var _handle = require("../../../common/event-emitter/handle");
|
|
15
15
|
var _mapOwn = require("../../../common/util/map-own");
|
|
16
16
|
var _config = require("../../../common/config/config");
|
|
17
|
-
var _now = require("../../../common/timing/now");
|
|
18
17
|
var _runtime = require("../../../common/constants/runtime");
|
|
19
18
|
var _constants = require("../constants");
|
|
20
19
|
var _features = require("../../../loaders/features/features");
|
|
21
20
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
22
21
|
var _nreum = require("../../../common/window/nreum");
|
|
23
22
|
var _drain = require("../../../common/drain/drain");
|
|
23
|
+
var _now = require("../../../common/timing/now");
|
|
24
24
|
/*
|
|
25
25
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
26
26
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -42,9 +42,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
42
42
|
this.bufferedErrorsUnderSpa = {};
|
|
43
43
|
this.currentBody = undefined;
|
|
44
44
|
this.errorOnPage = false;
|
|
45
|
+
this.replayAborted = false;
|
|
45
46
|
|
|
46
47
|
// this will need to change to match whatever ee we use in the instrument
|
|
47
48
|
this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
|
|
49
|
+
this.ee.on('REPLAY_ABORTED', () => {
|
|
50
|
+
this.replayAborted = true;
|
|
51
|
+
});
|
|
48
52
|
(0, _registerHandler.registerHandler)('err', function () {
|
|
49
53
|
return _this.storeError(...arguments);
|
|
50
54
|
}, this.featureName, this.ee);
|
|
@@ -89,9 +93,16 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
89
93
|
if (releaseIds !== '{}') {
|
|
90
94
|
payload.qs.ri = releaseIds;
|
|
91
95
|
}
|
|
92
|
-
if (body && body.err && body.err.length
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
if (body && body.err && body.err.length) {
|
|
97
|
+
if (this.replayAborted) {
|
|
98
|
+
body.err.forEach(e => {
|
|
99
|
+
delete e.params?.hasReplay;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
if (!this.errorOnPage) {
|
|
103
|
+
payload.qs.pve = '1';
|
|
104
|
+
this.errorOnPage = true;
|
|
105
|
+
}
|
|
95
106
|
}
|
|
96
107
|
return payload;
|
|
97
108
|
}
|
|
@@ -136,7 +147,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
136
147
|
}
|
|
137
148
|
return canonicalStackString;
|
|
138
149
|
}
|
|
139
|
-
storeError(err, time, internal, customAttributes) {
|
|
150
|
+
storeError(err, time, internal, customAttributes, hasReplay) {
|
|
140
151
|
// are we in an interaction
|
|
141
152
|
time = time || (0, _now.now)();
|
|
142
153
|
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
@@ -172,7 +183,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
172
183
|
if (!this.stackReported[bucketHash]) {
|
|
173
184
|
this.stackReported[bucketHash] = true;
|
|
174
185
|
params.stack_trace = (0, _formatStackTrace.truncateSize)(stackInfo.stackString);
|
|
175
|
-
this.observedAt[bucketHash] = agentRuntime.
|
|
186
|
+
this.observedAt[bucketHash] = agentRuntime.timeKeeper.convertRelativeTimestamp(time);
|
|
176
187
|
} else {
|
|
177
188
|
params.browser_stack_hash = (0, _stringHashCode.stringHashCode)(stackInfo.stackString);
|
|
178
189
|
}
|
|
@@ -187,8 +198,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
187
198
|
params.pageview = 1;
|
|
188
199
|
this.pageviewReported[bucketHash] = true;
|
|
189
200
|
}
|
|
190
|
-
if (
|
|
201
|
+
if (hasReplay && !this.replayAborted) params.hasReplay = hasReplay;
|
|
191
202
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash];
|
|
203
|
+
params.timestamp = this.observedAt[bucketHash];
|
|
192
204
|
var type = internal ? 'ierr' : 'err';
|
|
193
205
|
var newMetrics = {
|
|
194
206
|
time
|
|
@@ -197,7 +209,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
197
209
|
// Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
|
|
198
210
|
const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
|
|
199
211
|
(0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionTrace, this.ee);
|
|
200
|
-
(0, _handle.handle)('errorAgg', jsErrorEvent, undefined, _features.FEATURE_NAMES.sessionReplay, this.ee);
|
|
201
212
|
// still send EE events for other features such as above, but stop this one from aggregating internal data
|
|
202
213
|
if (this.blocked) return;
|
|
203
214
|
const softNavInUse = Boolean((0, _nreum.getNREUMInitializedAgent)(this.agentIdentifier)?.features[_features.FEATURE_NAMES.softNav]);
|
|
@@ -5,7 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.Instrument = void 0;
|
|
7
7
|
var _handle = require("../../../common/event-emitter/handle");
|
|
8
|
-
var _now = require("../../../common/timing/now");
|
|
9
8
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
10
9
|
var _constants = require("../constants");
|
|
11
10
|
var _features = require("../../../loaders/features/features");
|
|
@@ -13,6 +12,8 @@ var _runtime = require("../../../common/constants/runtime");
|
|
|
13
12
|
var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
|
|
14
13
|
var _stringify = require("../../../common/util/stringify");
|
|
15
14
|
var _uncaughtError = require("./uncaught-error");
|
|
15
|
+
var _now = require("../../../common/timing/now");
|
|
16
|
+
var _constants2 = require("../../session_replay/constants");
|
|
16
17
|
/*
|
|
17
18
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
18
19
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -21,6 +22,7 @@ var _uncaughtError = require("./uncaught-error");
|
|
|
21
22
|
class Instrument extends _instrumentBase.InstrumentBase {
|
|
22
23
|
static featureName = _constants.FEATURE_NAME;
|
|
23
24
|
#seenErrors = new Set();
|
|
25
|
+
#replayRunning = false;
|
|
24
26
|
constructor(agentIdentifier, aggregator) {
|
|
25
27
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
26
28
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
|
|
@@ -37,13 +39,16 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
37
39
|
});
|
|
38
40
|
this.ee.on('internal-error', error => {
|
|
39
41
|
if (!this.abortHandler) return;
|
|
40
|
-
(0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
42
|
+
(0, _handle.handle)('ierr', [this.#castError(error), (0, _now.now)(), true, {}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
43
|
+
});
|
|
44
|
+
this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
45
|
+
this.#replayRunning = isRunning;
|
|
41
46
|
});
|
|
42
47
|
_runtime.globalScope.addEventListener('unhandledrejection', promiseRejectionEvent => {
|
|
43
48
|
if (!this.abortHandler) return;
|
|
44
49
|
(0, _handle.handle)('err', [this.#castPromiseRejectionEvent(promiseRejectionEvent), (0, _now.now)(), false, {
|
|
45
50
|
unhandledPromiseRejection: 1
|
|
46
|
-
}], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
51
|
+
}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
47
52
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
48
53
|
_runtime.globalScope.addEventListener('error', errorEvent => {
|
|
49
54
|
if (!this.abortHandler) return;
|
|
@@ -56,7 +61,7 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
56
61
|
this.#seenErrors.delete(errorEvent.error);
|
|
57
62
|
return;
|
|
58
63
|
}
|
|
59
|
-
(0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)()], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
64
|
+
(0, _handle.handle)('err', [this.#castErrorEvent(errorEvent), (0, _now.now)(), false, {}, this.#replayRunning], undefined, _features.FEATURE_NAMES.jserrors, this.ee);
|
|
60
65
|
}, (0, _eventListenerOpts.eventListenerOpts)(false, this.removeOnAbort?.signal));
|
|
61
66
|
this.abortHandler = this.#abort; // we also use this as a flag to denote that the feature is active or on and handling errors
|
|
62
67
|
this.importAggregator();
|
|
@@ -95,14 +95,15 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
95
95
|
width = window.document.documentElement.clientWidth;
|
|
96
96
|
height = window.document.documentElement.clientHeight;
|
|
97
97
|
}
|
|
98
|
+
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
98
99
|
var defaults = {
|
|
99
|
-
timestamp:
|
|
100
|
+
timestamp: agentRuntime.timeKeeper.convertRelativeTimestamp(t),
|
|
100
101
|
timeSinceLoad: t / 1000,
|
|
101
102
|
browserWidth: width,
|
|
102
103
|
browserHeight: height,
|
|
103
104
|
referrerUrl: this.referrerUrl,
|
|
104
105
|
currentUrl: (0, _cleanUrl.cleanURL)('' + location),
|
|
105
|
-
pageUrl: (0, _cleanUrl.cleanURL)(
|
|
106
|
+
pageUrl: (0, _cleanUrl.cleanURL)(agentRuntime.origin),
|
|
106
107
|
eventType: 'PageAction'
|
|
107
108
|
};
|
|
108
109
|
(0, _mapOwn.mapOwn)(defaults, set);
|
|
@@ -21,6 +21,8 @@ var _drain = require("../../../common/drain/drain");
|
|
|
21
21
|
var _features = require("../../../loaders/features/features");
|
|
22
22
|
var _handle = require("../../../common/event-emitter/handle");
|
|
23
23
|
var _constants2 = require("../../metrics/constants");
|
|
24
|
+
var _now = require("../../../common/timing/now");
|
|
25
|
+
var _timeKeeper = require("../../../common/timing/time-keeper");
|
|
24
26
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
25
27
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
26
28
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
@@ -35,9 +37,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
35
37
|
_timeToFirstByte.timeToFirstByte.subscribe(_ref => {
|
|
36
38
|
let {
|
|
37
39
|
value,
|
|
38
|
-
|
|
40
|
+
attrs
|
|
39
41
|
} = _ref;
|
|
40
|
-
const navEntry =
|
|
42
|
+
const navEntry = attrs.navigationEntry;
|
|
41
43
|
this.timeToFirstByte = Math.max(value, this.timeToFirstByte);
|
|
42
44
|
this.firstByteToWindowLoad = Math.max(Math.round(navEntry.loadEventEnd - this.timeToFirstByte), this.firstByteToWindowLoad); // our "frontend" duration
|
|
43
45
|
this.firstByteToDomContent = Math.max(Math.round(navEntry.domContentLoadedEventEnd - this.timeToFirstByte), this.firstByteToDomContent); // our "dom processing" duration
|
|
@@ -116,7 +118,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
116
118
|
}
|
|
117
119
|
queryParameters.fp = _firstPaint.firstPaint.current.value;
|
|
118
120
|
queryParameters.fcp = _firstContentfulPaint.firstContentfulPaint.current.value;
|
|
119
|
-
const rumStartTime =
|
|
121
|
+
const rumStartTime = (0, _now.now)();
|
|
120
122
|
harvester.send({
|
|
121
123
|
endpoint: 'rum',
|
|
122
124
|
payload: {
|
|
@@ -133,14 +135,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
133
135
|
responseText,
|
|
134
136
|
xhr
|
|
135
137
|
} = _ref3;
|
|
136
|
-
const rumEndTime =
|
|
138
|
+
const rumEndTime = (0, _now.now)();
|
|
137
139
|
if (status >= 400 || status === 0) {
|
|
138
140
|
// Adding retry logic for the rum call will be a separate change
|
|
139
141
|
this.ee.abort();
|
|
140
142
|
return;
|
|
141
143
|
}
|
|
142
144
|
try {
|
|
143
|
-
|
|
145
|
+
const timeKeeper = new _timeKeeper.TimeKeeper();
|
|
146
|
+
timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
|
|
147
|
+
if (!timeKeeper.ready) throw new Error('TimeKeeper not ready');
|
|
148
|
+
agentRuntime.timeKeeper = timeKeeper;
|
|
144
149
|
} catch (error) {
|
|
145
150
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['PVE/NRTime/Calculation/Failed'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
146
151
|
(0, _drain.drain)(this.agentIdentifier, _features.FEATURE_NAMES.metrics, true);
|
|
@@ -21,6 +21,8 @@ var _interactionToNextPaint = require("../../../common/vitals/interaction-to-nex
|
|
|
21
21
|
var _largestContentfulPaint = require("../../../common/vitals/largest-contentful-paint");
|
|
22
22
|
var _timeToFirstByte = require("../../../common/vitals/time-to-first-byte");
|
|
23
23
|
var _longTask = require("../../../common/vitals/long-task");
|
|
24
|
+
var _pageVisibility = require("../../../common/window/page-visibility");
|
|
25
|
+
var _constants2 = require("../../../common/vitals/constants");
|
|
24
26
|
/*
|
|
25
27
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
26
28
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -44,15 +46,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
44
46
|
this.timingsSent = [];
|
|
45
47
|
this.curSessEndRecorded = false;
|
|
46
48
|
if ((0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.long_task') === true) _longTask.longTask.subscribe(this.#handleVitalMetric);
|
|
47
|
-
|
|
48
|
-
/* It's important that CWV api, like "onLCP", is called before this scheduler is initialized. The reason is because they listen to the same
|
|
49
|
-
on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
|
|
50
|
-
|
|
51
49
|
(0, _registerHandler.registerHandler)('docHidden', msTimestamp => this.endCurrentSession(msTimestamp), this.featureName, this.ee);
|
|
52
50
|
(0, _registerHandler.registerHandler)('winPagehide', msTimestamp => this.recordPageUnload(msTimestamp), this.featureName, this.ee);
|
|
53
51
|
const initialHarvestSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.initialHarvestSeconds') || 10;
|
|
54
52
|
const harvestTimeSeconds = (0, _config.getConfigurationValue)(this.agentIdentifier, 'page_view_timing.harvestTimeSeconds') || 30;
|
|
55
53
|
this.waitForFlags([]).then(() => {
|
|
54
|
+
/* It's important that CWV api, like "onLCP", is called before the **scheduler** is initialized. The reason is because they listen to the same
|
|
55
|
+
on vis change or pagehide events, and we'd want ex. onLCP to record the timing (win the race) before we try to send "final harvest". */
|
|
56
56
|
_firstPaint.firstPaint.subscribe(this.#handleVitalMetric);
|
|
57
57
|
_firstContentfulPaint.firstContentfulPaint.subscribe(this.#handleVitalMetric);
|
|
58
58
|
_firstInputDelay.firstInputDelay.subscribe(this.#handleVitalMetric);
|
|
@@ -60,10 +60,23 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
60
60
|
_interactionToNextPaint.interactionToNextPaint.subscribe(this.#handleVitalMetric);
|
|
61
61
|
_timeToFirstByte.timeToFirstByte.subscribe(_ref2 => {
|
|
62
62
|
let {
|
|
63
|
-
|
|
63
|
+
attrs
|
|
64
64
|
} = _ref2;
|
|
65
|
-
this.addTiming('load', Math.round(
|
|
65
|
+
this.addTiming('load', Math.round(attrs.navigationEntry.loadEventEnd));
|
|
66
66
|
});
|
|
67
|
+
(0, _pageVisibility.subscribeToVisibilityChange)(() => {
|
|
68
|
+
/* Downstream, the event consumer interprets all timing node value as ms-unit and converts it to seconds via division by 1000. CLS is unitless so this normally is a problem.
|
|
69
|
+
bel.6 schema also doesn't support decimal values, of which cls within [0,1). However, the two nicely cancels out, and we can multiply cls by 1000 to both negate the division
|
|
70
|
+
and send an integer > 1. We effectively lose some precision down to 3 decimal places for this workaround. E.g. (real) 0.749132... -> 749.132...-> 749 -> 0.749 (final) */
|
|
71
|
+
const {
|
|
72
|
+
name,
|
|
73
|
+
value,
|
|
74
|
+
attrs
|
|
75
|
+
} = _cumulativeLayoutShift.cumulativeLayoutShift.current;
|
|
76
|
+
if (value === undefined) return;
|
|
77
|
+
this.addTiming(name, value * 1000, attrs);
|
|
78
|
+
}, true); // CLS node should only reports on vis change rather than on every change
|
|
79
|
+
|
|
67
80
|
const scheduler = new _harvestScheduler.HarvestScheduler('events', {
|
|
68
81
|
onFinished: function () {
|
|
69
82
|
return _this.onHarvestFinished(...arguments);
|
|
@@ -113,8 +126,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
113
126
|
Issue: Because NR 'pageHide' was only sent once with what is considered the "final" CLS value, in the case that 'pageHide' fires before 'load' happens, we incorrectly a final CLS of 0 for that page.
|
|
114
127
|
Mitigation: We've set initial CLS to null so that it's omitted from timings like 'pageHide' in that edge case. It should only be included if onCLS callback was executed at least once.
|
|
115
128
|
Future: onCLS value changes should be reported directly & CLS separated into its own timing node so it's not beholden to 'pageHide' firing. It'd also be possible to report the real final CLS.
|
|
129
|
+
*cli Mar'24 update: CLS now emitted as its own timing node in addition to as-property under other nodes. The 'cls' property is unnecessary for cls nodes.
|
|
116
130
|
*/
|
|
117
|
-
if (_cumulativeLayoutShift.cumulativeLayoutShift.current.value >= 0) {
|
|
131
|
+
if (name !== _constants2.VITAL_NAMES.CUMULATIVE_LAYOUT_SHIFT && _cumulativeLayoutShift.cumulativeLayoutShift.current.value >= 0) {
|
|
118
132
|
attrs.cls = _cumulativeLayoutShift.cumulativeLayoutShift.current.value;
|
|
119
133
|
}
|
|
120
134
|
this.timings.push({
|
|
@@ -7,10 +7,10 @@ exports.Instrument = void 0;
|
|
|
7
7
|
var _handle = require("../../../common/event-emitter/handle");
|
|
8
8
|
var _pageVisibility = require("../../../common/window/page-visibility");
|
|
9
9
|
var _eventListenerOpts = require("../../../common/event-listener/event-listener-opts");
|
|
10
|
-
var _now = require("../../../common/timing/now");
|
|
11
10
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
12
11
|
var _constants = require("../constants");
|
|
13
12
|
var _runtime = require("../../../common/constants/runtime");
|
|
13
|
+
var _now = require("../../../common/timing/now");
|
|
14
14
|
/*
|
|
15
15
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
16
16
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -17,11 +17,12 @@ var _constants2 = require("../../metrics/constants");
|
|
|
17
17
|
var _handle = require("../../../common/event-emitter/handle");
|
|
18
18
|
var _features = require("../../../loaders/features/features");
|
|
19
19
|
var _env = require("../../../common/constants/env.npm");
|
|
20
|
-
var _now = require("../../../common/timing/now");
|
|
21
20
|
var _constants3 = require("../../../common/session/constants");
|
|
22
21
|
var _stringify = require("../../../common/util/stringify");
|
|
23
22
|
var _stylesheetEvaluator = require("../shared/stylesheet-evaluator");
|
|
24
23
|
var _drain = require("../../../common/drain/drain");
|
|
24
|
+
var _now = require("../../../common/timing/now");
|
|
25
|
+
var _utils = require("../shared/utils");
|
|
25
26
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
26
27
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /*
|
|
27
28
|
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
@@ -35,6 +36,8 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
|
|
|
35
36
|
*/
|
|
36
37
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
37
38
|
static featureName = _constants.FEATURE_NAME;
|
|
39
|
+
mode = _constants3.MODE.OFF;
|
|
40
|
+
|
|
38
41
|
// pass the recorder into the aggregator
|
|
39
42
|
constructor(agentIdentifier, aggregator, args) {
|
|
40
43
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
|
|
@@ -48,21 +51,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
48
51
|
this.gzipper = undefined;
|
|
49
52
|
/** populated with the u8 string lib async */
|
|
50
53
|
this.u8 = undefined;
|
|
51
|
-
/** the mode to start in. Defaults to off */
|
|
52
|
-
const {
|
|
53
|
-
session
|
|
54
|
-
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
55
|
-
this.mode = session.state.sessionReplayMode || _constants3.MODE.OFF;
|
|
56
54
|
|
|
57
55
|
/** set by BCS response */
|
|
58
56
|
this.entitled = false;
|
|
57
|
+
/** set at BCS response, stored in runtime */
|
|
58
|
+
this.timeKeeper = undefined;
|
|
59
59
|
this.recorder = args?.recorder;
|
|
60
|
-
|
|
60
|
+
this.preloaded = !!this.recorder;
|
|
61
|
+
this.errorNoticed = args?.errorNoticed || false;
|
|
61
62
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
62
63
|
|
|
63
64
|
// The SessionEntity class can emit a message indicating the session was cleared and reset (expiry, inactivity). This feature must abort and never resume if that occurs.
|
|
64
65
|
this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
|
|
65
|
-
this.scheduler.runHarvest();
|
|
66
66
|
this.abort(_constants.ABORT_REASONS.RESET);
|
|
67
67
|
});
|
|
68
68
|
|
|
@@ -106,17 +106,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
106
106
|
(0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
|
|
107
107
|
this.forceStop(this.mode !== _constants3.MODE.ERROR);
|
|
108
108
|
}, this.featureName, this.ee);
|
|
109
|
-
|
|
110
|
-
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
111
|
-
// This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
|
|
112
|
-
(0, _registerHandler.registerHandler)('errorAgg', e => {
|
|
113
|
-
this.errorNoticed = true;
|
|
114
|
-
if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
|
|
115
|
-
// run once
|
|
116
|
-
if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
|
|
117
|
-
this.switchToFull();
|
|
118
|
-
}
|
|
119
|
-
}, this.featureName, this.ee);
|
|
120
109
|
const {
|
|
121
110
|
error_sampling_rate,
|
|
122
111
|
sampling_rate,
|
|
@@ -157,6 +146,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
157
146
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
158
147
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
159
148
|
}
|
|
149
|
+
handleError(e) {
|
|
150
|
+
if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
|
|
151
|
+
// run once
|
|
152
|
+
if (this.mode === _constants3.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
|
|
153
|
+
this.switchToFull();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
160
156
|
switchToFull() {
|
|
161
157
|
this.mode = _constants3.MODE.FULL;
|
|
162
158
|
// if the error was noticed AFTER the recorder was already imported....
|
|
@@ -189,8 +185,10 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
189
185
|
// session replay samples can only be decided on the first load of a session
|
|
190
186
|
// session replays can continue if already in progress
|
|
191
187
|
const {
|
|
192
|
-
session
|
|
188
|
+
session,
|
|
189
|
+
timeKeeper
|
|
193
190
|
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
191
|
+
this.timeKeeper = timeKeeper;
|
|
194
192
|
if (!session.isNew && !ignoreSession) {
|
|
195
193
|
// inherit the mode of the existing session
|
|
196
194
|
this.mode = session.state.sessionReplayMode;
|
|
@@ -219,12 +217,13 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
219
217
|
} catch (err) {
|
|
220
218
|
return this.abort(_constants.ABORT_REASONS.IMPORT);
|
|
221
219
|
}
|
|
220
|
+
} else {
|
|
221
|
+
this.recorder.parent = this;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
// If an error was noticed before the mode could be set (like in the early lifecycle of the page), immediately set to FULL mode
|
|
225
|
-
if (this.mode === _constants3.MODE.ERROR && this.errorNoticed)
|
|
226
|
-
|
|
227
|
-
}
|
|
225
|
+
if (this.mode === _constants3.MODE.ERROR && this.errorNoticed) this.mode = _constants3.MODE.FULL;
|
|
226
|
+
if (!this.preloaded) this.ee.on('err', e => this.handleError(e));
|
|
228
227
|
|
|
229
228
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
230
229
|
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
@@ -256,7 +255,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
256
255
|
let {
|
|
257
256
|
opts
|
|
258
257
|
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
259
|
-
if (!this.recorder) return;
|
|
258
|
+
if (!this.recorder || !this.timeKeeper?.ready) return;
|
|
260
259
|
const recorderEvents = this.recorder.getEvents();
|
|
261
260
|
// get the event type and use that to trigger another harvest if needed
|
|
262
261
|
if (!recorderEvents.events.length || this.mode !== _constants3.MODE.FULL || this.blocked) return;
|
|
@@ -265,18 +264,39 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
265
264
|
this.recorder.clearBuffer();
|
|
266
265
|
return;
|
|
267
266
|
}
|
|
267
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/Harvest/Attempts'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
268
268
|
let len = 0;
|
|
269
269
|
if (!!this.gzipper && !!this.u8) {
|
|
270
|
-
payload.body = this.gzipper(this.u8("[".concat(payload.body.map(
|
|
270
|
+
payload.body = this.gzipper(this.u8("[".concat(payload.body.map(_ref2 => {
|
|
271
|
+
let {
|
|
272
|
+
__serialized,
|
|
273
|
+
...e
|
|
274
|
+
} = _ref2;
|
|
275
|
+
if (e.__newrelic && __serialized) return __serialized;
|
|
276
|
+
const output = {
|
|
277
|
+
...e
|
|
278
|
+
};
|
|
279
|
+
if (!output.__newrelic) {
|
|
280
|
+
output.__newrelic = (0, _utils.buildNRMetaNode)(e.timestamp, this.timeKeeper);
|
|
281
|
+
output.timestamp = this.timeKeeper.correctAbsoluteTimestamp(e.timestamp);
|
|
282
|
+
}
|
|
283
|
+
return (0, _stringify.stringify)(output);
|
|
284
|
+
}).join(','), "]")));
|
|
271
285
|
len = payload.body.length;
|
|
272
286
|
this.scheduler.opts.gzip = true;
|
|
273
287
|
} else {
|
|
274
|
-
payload.body = payload.body.map(
|
|
288
|
+
payload.body = payload.body.map(_ref3 => {
|
|
275
289
|
let {
|
|
276
290
|
__serialized,
|
|
277
291
|
...node
|
|
278
|
-
} =
|
|
279
|
-
return node;
|
|
292
|
+
} = _ref3;
|
|
293
|
+
if (node.__newrelic) return node;
|
|
294
|
+
const output = {
|
|
295
|
+
...node
|
|
296
|
+
};
|
|
297
|
+
output.__newrelic = (0, _utils.buildNRMetaNode)(node.timestamp, this.timeKeeper);
|
|
298
|
+
output.timestamp = this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
|
|
299
|
+
return output;
|
|
280
300
|
});
|
|
281
301
|
len = (0, _stringify.stringify)(payload.body).length;
|
|
282
302
|
this.scheduler.opts.gzip = false;
|
|
@@ -296,6 +316,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
296
316
|
if (recorderEvents.type === 'preloaded') this.scheduler.runHarvest(opts);
|
|
297
317
|
return [payload];
|
|
298
318
|
}
|
|
319
|
+
getCorrectedTimestamp(node) {
|
|
320
|
+
if (!node.timestamp) return;
|
|
321
|
+
if (node.__newrelic) return node.timestamp;
|
|
322
|
+
return this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
|
|
323
|
+
}
|
|
299
324
|
getHarvestContents(recorderEvents) {
|
|
300
325
|
recorderEvents ??= this.recorder.getEvents();
|
|
301
326
|
let events = recorderEvents.events;
|
|
@@ -320,12 +345,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
320
345
|
events = events.slice(0, events.length - 1);
|
|
321
346
|
recorderEvents.hasMeta = !!events.find(x => x.type === _constants.RRWEB_EVENT_TYPES.Meta);
|
|
322
347
|
}
|
|
323
|
-
const agentOffset = (0, _config.getRuntime)(this.agentIdentifier).offset;
|
|
324
348
|
const relativeNow = (0, _now.now)();
|
|
325
|
-
const firstEventTimestamp = events[0]
|
|
326
|
-
const lastEventTimestamp = events[events.length - 1]
|
|
327
|
-
const firstTimestamp = firstEventTimestamp || recorderEvents.cycleTimestamp; // from rrweb node || from when the harvest cycle started
|
|
328
|
-
const lastTimestamp = lastEventTimestamp ||
|
|
349
|
+
const firstEventTimestamp = this.getCorrectedTimestamp(events[0]); // from rrweb node
|
|
350
|
+
const lastEventTimestamp = this.getCorrectedTimestamp(events[events.length - 1]); // from rrweb node
|
|
351
|
+
const firstTimestamp = firstEventTimestamp || this.timeKeeper.correctAbsoluteTimestamp(recorderEvents.cycleTimestamp); // from rrweb node || from when the harvest cycle started
|
|
352
|
+
const lastTimestamp = lastEventTimestamp || this.timeKeeper.convertRelativeTimestamp(relativeNow);
|
|
329
353
|
const agentMetadata = agentRuntime.appMetadata?.agents?.[0] || {};
|
|
330
354
|
return {
|
|
331
355
|
qs: {
|
|
@@ -333,6 +357,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
333
357
|
type: 'SessionReplay',
|
|
334
358
|
app_id: info.applicationID,
|
|
335
359
|
protocol_version: '0',
|
|
360
|
+
timestamp: firstTimestamp,
|
|
336
361
|
attributes: (0, _encode.obj)({
|
|
337
362
|
// this section of attributes must be controllable and stay below the query param padding limit -- see QUERY_PARAM_PADDING
|
|
338
363
|
// if not, data could be lost to truncation at time of sending, potentially breaking parsing / API behavior in NR1
|
|
@@ -343,9 +368,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
343
368
|
entityGuid: agentMetadata.entityGuid
|
|
344
369
|
}),
|
|
345
370
|
'replay.firstTimestamp': firstTimestamp,
|
|
346
|
-
'replay.firstTimestampOffset': firstTimestamp - agentOffset,
|
|
347
371
|
'replay.lastTimestamp': lastTimestamp,
|
|
348
|
-
'replay.durationMs': lastTimestamp - firstTimestamp,
|
|
349
372
|
'replay.nodes': events.length,
|
|
350
373
|
'session.durationMs': agentRuntime.session.getDuration(),
|
|
351
374
|
agentVersion: agentRuntime.version,
|
|
@@ -359,6 +382,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
359
382
|
invalidStylesheetsDetected: _stylesheetEvaluator.stylesheetEvaluator.invalidStylesheetsDetected,
|
|
360
383
|
inlinedAllStylesheets: recorderEvents.inlinedAllStylesheets,
|
|
361
384
|
'rrweb.version': _env.RRWEB_VERSION,
|
|
385
|
+
'payload.type': recorderEvents.type,
|
|
362
386
|
// customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs
|
|
363
387
|
...(endUserId && {
|
|
364
388
|
'enduser.id': endUserId
|
|
@@ -9,7 +9,8 @@ var _features = require("../../loaders/features/features");
|
|
|
9
9
|
const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionReplay;
|
|
10
10
|
const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
|
|
11
11
|
RECORD: 'recordReplay',
|
|
12
|
-
PAUSE: 'pauseReplay'
|
|
12
|
+
PAUSE: 'pauseReplay',
|
|
13
|
+
REPLAY_RUNNING: 'replayRunning'
|
|
13
14
|
};
|
|
14
15
|
const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
|
|
15
16
|
const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
|
|
@@ -29,6 +29,11 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
29
29
|
session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
|
|
30
30
|
} catch (err) {}
|
|
31
31
|
if (this.#canPreloadRecorder(session)) {
|
|
32
|
+
/** If this is preloaded, set up a buffer, if not, later when sampling we will set up a .on for live events */
|
|
33
|
+
this.ee.on('err', e => {
|
|
34
|
+
this.errorNoticed = true;
|
|
35
|
+
if (this.featAggregate) this.featAggregate.handleError();
|
|
36
|
+
});
|
|
32
37
|
this.#startRecording(session?.sessionReplayMode);
|
|
33
38
|
} else {
|
|
34
39
|
this.importAggregator();
|
|
@@ -55,12 +60,14 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
55
60
|
} = await Promise.resolve().then(() => _interopRequireWildcard(require( /* webpackChunkName: "recorder" */'../shared/recorder')));
|
|
56
61
|
this.recorder = new Recorder({
|
|
57
62
|
mode,
|
|
58
|
-
agentIdentifier: this.agentIdentifier
|
|
63
|
+
agentIdentifier: this.agentIdentifier,
|
|
64
|
+
ee: this.ee
|
|
59
65
|
});
|
|
60
66
|
this.recorder.startRecording();
|
|
61
67
|
this.abortHandler = this.recorder.stopRecording;
|
|
62
68
|
this.importAggregator({
|
|
63
|
-
recorder: this.recorder
|
|
69
|
+
recorder: this.recorder,
|
|
70
|
+
errorNoticed: this.errorNoticed
|
|
64
71
|
});
|
|
65
72
|
}
|
|
66
73
|
}
|
|
@@ -14,16 +14,20 @@ var _stylesheetEvaluator = require("./stylesheet-evaluator");
|
|
|
14
14
|
var _handle = require("../../../common/event-emitter/handle");
|
|
15
15
|
var _constants3 = require("../../metrics/constants");
|
|
16
16
|
var _features = require("../../../loaders/features/features");
|
|
17
|
+
var _utils = require("./utils");
|
|
17
18
|
class Recorder {
|
|
18
19
|
/** Each page mutation or event will be stored (raw) in this array. This array will be cleared on each harvest */
|
|
19
|
-
#events
|
|
20
|
+
#events;
|
|
20
21
|
/** Backlog used for a 2-part sliding window to guarantee a 15-30s buffer window */
|
|
21
|
-
#backloggedEvents
|
|
22
|
+
#backloggedEvents;
|
|
22
23
|
/** array of recorder events -- Will be filled only if forced harvest was triggered and harvester does not exist */
|
|
23
|
-
#preloaded
|
|
24
|
+
#preloaded;
|
|
24
25
|
/** flag that if true, blocks events from being "stored". Only set to true when a full snapshot has incomplete nodes (only stylesheets ATM) */
|
|
25
26
|
#fixing = false;
|
|
26
27
|
constructor(parent) {
|
|
28
|
+
this.#events = new _recorderEvents.RecorderEvents();
|
|
29
|
+
this.#backloggedEvents = new _recorderEvents.RecorderEvents();
|
|
30
|
+
this.#preloaded = [new _recorderEvents.RecorderEvents()];
|
|
27
31
|
/** True when actively recording, false when paused or stopped */
|
|
28
32
|
this.recording = false;
|
|
29
33
|
/** The pointer to the current bucket holding rrweb events */
|
|
@@ -77,6 +81,10 @@ class Recorder {
|
|
|
77
81
|
inline_images,
|
|
78
82
|
collect_fonts
|
|
79
83
|
} = (0, _config.getConfigurationValue)(this.parent.agentIdentifier, 'session_replay');
|
|
84
|
+
const customMasker = (text, element) => {
|
|
85
|
+
if (element?.type?.toLowerCase() !== 'password' && (element?.dataset.nrUnmask !== undefined || element?.classList.contains('nr-unmask'))) return text;
|
|
86
|
+
return '*'.repeat(text.length);
|
|
87
|
+
};
|
|
80
88
|
// set up rrweb configurations for maximum privacy --
|
|
81
89
|
// https://newrelic.atlassian.net/wiki/spaces/O11Y/pages/2792293280/2023+02+28+Browser+-+Session+Replay#Configuration-options
|
|
82
90
|
const stop = (0, _rrweb.record)({
|
|
@@ -87,14 +95,18 @@ class Recorder {
|
|
|
87
95
|
blockSelector: block_selector,
|
|
88
96
|
maskInputOptions: mask_input_options,
|
|
89
97
|
maskTextSelector: mask_text_selector,
|
|
98
|
+
maskTextFn: customMasker,
|
|
90
99
|
maskAllInputs: mask_all_inputs,
|
|
100
|
+
maskInputFn: customMasker,
|
|
91
101
|
inlineStylesheet: inline_stylesheet,
|
|
92
102
|
inlineImages: inline_images,
|
|
93
103
|
collectFonts: collect_fonts,
|
|
94
104
|
checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode]
|
|
95
105
|
});
|
|
106
|
+
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
96
107
|
this.stopRecording = () => {
|
|
97
108
|
this.recording = false;
|
|
109
|
+
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
|
|
98
110
|
stop();
|
|
99
111
|
};
|
|
100
112
|
}
|
|
@@ -136,9 +148,13 @@ class Recorder {
|
|
|
136
148
|
/** Store a payload in the buffer (this.#events). This should be the callback to the recording lib noticing a mutation */
|
|
137
149
|
store(event, isCheckout) {
|
|
138
150
|
if (!event) return;
|
|
139
|
-
event.__serialized = (0, _stringify.stringify)(event);
|
|
140
151
|
if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
141
152
|
if (this.parent.blocked) return;
|
|
153
|
+
if (this.parent.timeKeeper?.ready && !event.__newrelic) {
|
|
154
|
+
event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
|
|
155
|
+
event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
|
|
156
|
+
}
|
|
157
|
+
event.__serialized = (0, _stringify.stringify)(event);
|
|
142
158
|
const eventBytes = event.__serialized.length;
|
|
143
159
|
/** The estimated size of the payload after compression */
|
|
144
160
|
const payloadSize = this.getPayloadSize(eventBytes);
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.buildNRMetaNode = buildNRMetaNode;
|
|
6
7
|
exports.canImportReplayAgg = canImportReplayAgg;
|
|
7
8
|
exports.enableSessionTracking = void 0;
|
|
8
9
|
exports.isPreloadAllowed = isPreloadAllowed;
|
|
@@ -23,4 +24,15 @@ function isPreloadAllowed(agentId) {
|
|
|
23
24
|
function canImportReplayAgg(agentId, sessionMgr) {
|
|
24
25
|
if (!hasReplayPrerequisite(agentId)) return false;
|
|
25
26
|
return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
27
|
+
}
|
|
28
|
+
function buildNRMetaNode(timestamp, timeKeeper) {
|
|
29
|
+
const correctedTimestamp = timeKeeper.correctAbsoluteTimestamp(timestamp);
|
|
30
|
+
return {
|
|
31
|
+
originalTimestamp: timestamp,
|
|
32
|
+
correctedTimestamp,
|
|
33
|
+
timestampDiff: timestamp - correctedTimestamp,
|
|
34
|
+
timeKeeperOriginTime: timeKeeper.originTime,
|
|
35
|
+
timeKeeperCorrectedOriginTime: timeKeeper.correctedOriginTime,
|
|
36
|
+
timeKeeperDiff: Math.floor(timeKeeper.originTime - timeKeeper.correctedOriginTime)
|
|
37
|
+
};
|
|
26
38
|
}
|