@newrelic/browser-agent 1.256.1 → 1.258.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 +32 -0
- package/dist/cjs/common/config/state/configurable.js +8 -5
- package/dist/cjs/common/config/state/init.js +0 -2
- package/dist/cjs/common/config/state/runtime.js +10 -8
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/constants/runtime.js +8 -2
- package/dist/cjs/common/harvest/harvest.js +7 -5
- package/dist/cjs/common/session/constants.js +1 -0
- package/dist/cjs/common/session/session-entity.js +3 -0
- package/dist/cjs/common/timing/time-keeper.js +45 -9
- package/dist/cjs/common/vitals/time-to-first-byte.js +1 -1
- package/dist/cjs/common/vitals/vital-metric.js +1 -1
- package/dist/cjs/features/ajax/aggregate/chunk.js +50 -0
- package/dist/cjs/features/ajax/aggregate/index.js +131 -191
- package/dist/cjs/features/ajax/instrument/index.js +0 -3
- package/dist/cjs/features/jserrors/aggregate/index.js +26 -13
- package/dist/cjs/features/page_view_event/aggregate/index.js +3 -3
- package/dist/cjs/features/session_replay/aggregate/index.js +12 -5
- package/dist/cjs/features/session_replay/constants.js +2 -1
- package/dist/cjs/features/session_replay/instrument/index.js +15 -5
- package/dist/cjs/features/session_replay/shared/recorder.js +6 -3
- package/dist/cjs/features/session_replay/shared/utils.js +9 -8
- package/dist/cjs/features/session_trace/aggregate/index.js +3 -5
- package/dist/cjs/features/soft_navigations/aggregate/index.js +2 -2
- package/dist/cjs/features/spa/instrument/index.js +0 -2
- package/dist/cjs/features/utils/agent-session.js +1 -5
- package/dist/cjs/features/utils/instrument-base.js +11 -14
- package/dist/cjs/features/utils/nr1-debugger.js +27 -0
- package/dist/cjs/loaders/agent.js +4 -0
- package/dist/cjs/loaders/api/apiAsync.js +5 -4
- package/dist/esm/common/config/state/configurable.js +8 -5
- package/dist/esm/common/config/state/init.js +0 -2
- package/dist/esm/common/config/state/runtime.js +11 -9
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/constants/runtime.js +7 -1
- package/dist/esm/common/harvest/harvest.js +7 -5
- package/dist/esm/common/session/constants.js +1 -0
- package/dist/esm/common/session/session-entity.js +3 -0
- package/dist/esm/common/timing/time-keeper.js +46 -9
- package/dist/esm/common/vitals/time-to-first-byte.js +2 -2
- package/dist/esm/common/vitals/vital-metric.js +1 -1
- package/dist/esm/features/ajax/aggregate/chunk.js +43 -0
- package/dist/esm/features/ajax/aggregate/index.js +130 -191
- package/dist/esm/features/ajax/instrument/index.js +1 -4
- package/dist/esm/features/jserrors/aggregate/index.js +26 -13
- package/dist/esm/features/page_view_event/aggregate/index.js +4 -4
- package/dist/esm/features/session_replay/aggregate/index.js +12 -5
- package/dist/esm/features/session_replay/constants.js +2 -1
- package/dist/esm/features/session_replay/instrument/index.js +16 -6
- package/dist/esm/features/session_replay/shared/recorder.js +6 -3
- package/dist/esm/features/session_replay/shared/utils.js +9 -7
- package/dist/esm/features/session_trace/aggregate/index.js +3 -5
- package/dist/esm/features/soft_navigations/aggregate/index.js +2 -2
- package/dist/esm/features/spa/instrument/index.js +0 -2
- package/dist/esm/features/utils/agent-session.js +1 -5
- package/dist/esm/features/utils/instrument-base.js +11 -14
- package/dist/esm/features/utils/nr1-debugger.js +21 -0
- package/dist/esm/loaders/agent.js +4 -0
- package/dist/esm/loaders/api/apiAsync.js +5 -4
- 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/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/runtime.d.ts +6 -1
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/session/constants.d.ts +1 -0
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts +1 -1
- package/dist/types/common/timing/time-keeper.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/chunk.d.ts +8 -0
- package/dist/types/features/ajax/aggregate/chunk.d.ts.map +1 -0
- package/dist/types/features/ajax/aggregate/index.d.ts +8 -6
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/ajax/instrument/index.d.ts +2 -2
- package/dist/types/features/ajax/instrument/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +0 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +0 -1
- 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 +1 -0
- package/dist/types/features/session_replay/shared/recorder.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +5 -5
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +8 -8
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/agent-session.d.ts.map +1 -1
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/features/utils/nr1-debugger.d.ts +2 -0
- package/dist/types/features/utils/nr1-debugger.d.ts.map +1 -0
- package/dist/types/loaders/agent.d.ts +5 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/common/config/state/configurable.js +9 -8
- package/src/common/config/state/init.js +0 -1
- package/src/common/config/state/runtime.js +12 -9
- package/src/common/constants/__mocks__/runtime.js +2 -0
- package/src/common/constants/runtime.js +6 -1
- package/src/common/drain/__mocks__/drain.js +2 -0
- package/src/common/harvest/harvest.js +8 -6
- package/src/common/session/constants.js +1 -0
- package/src/common/session/session-entity.js +2 -0
- package/src/common/timing/time-keeper.js +44 -10
- package/src/common/vitals/time-to-first-byte.js +2 -2
- package/src/common/vitals/vital-metric.js +1 -1
- package/src/common/window/__mocks__/load.js +3 -0
- package/src/features/ajax/aggregate/chunk.js +51 -0
- package/src/features/ajax/aggregate/index.js +128 -200
- package/src/features/ajax/instrument/index.js +1 -4
- package/src/features/jserrors/aggregate/index.js +28 -11
- package/src/features/page_view_event/aggregate/index.js +4 -4
- package/src/features/session_replay/aggregate/index.js +19 -7
- package/src/features/session_replay/constants.js +2 -1
- package/src/features/session_replay/instrument/index.js +16 -6
- package/src/features/session_replay/shared/__mocks__/utils.js +2 -0
- package/src/features/session_replay/shared/recorder.js +7 -4
- package/src/features/session_replay/shared/utils.js +9 -7
- package/src/features/session_trace/aggregate/index.js +3 -6
- package/src/features/soft_navigations/aggregate/index.js +2 -2
- package/src/features/spa/instrument/index.js +0 -3
- package/src/features/utils/__mocks__/agent-session.js +1 -0
- package/src/features/utils/__mocks__/feature-base.js +11 -0
- package/src/features/utils/agent-session.js +1 -7
- package/src/features/utils/instrument-base.js +11 -14
- package/src/features/utils/nr1-debugger.js +22 -0
- package/src/loaders/agent.js +4 -0
- package/src/loaders/api/apiAsync.js +5 -4
- package/dist/cjs/common/storage/first-party-cookies.js +0 -36
- package/dist/esm/common/storage/first-party-cookies.js +0 -29
- package/dist/types/common/storage/first-party-cookies.d.ts +0 -8
- package/dist/types/common/storage/first-party-cookies.d.ts.map +0 -1
- package/src/common/storage/first-party-cookies.js +0 -32
|
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.Aggregate = void 0;
|
|
7
7
|
var _registerHandler = require("../../../common/event-emitter/register-handler");
|
|
8
8
|
var _stringify = require("../../../common/util/stringify");
|
|
9
|
-
var _belSerializer = require("../../../common/serialize/bel-serializer");
|
|
10
9
|
var _handle = require("../../../common/event-emitter/handle");
|
|
11
10
|
var _config = require("../../../common/config/config");
|
|
12
11
|
var _harvestScheduler = require("../../../common/harvest/harvest-scheduler");
|
|
@@ -17,6 +16,8 @@ var _constants2 = require("../../metrics/constants");
|
|
|
17
16
|
var _aggregateBase = require("../../utils/aggregate-base");
|
|
18
17
|
var _gql = require("./gql");
|
|
19
18
|
var _nreum = require("../../../common/window/nreum");
|
|
19
|
+
var _chunk = _interopRequireDefault(require("./chunk"));
|
|
20
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
20
21
|
/*
|
|
21
22
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
22
23
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -24,224 +25,163 @@ var _nreum = require("../../../common/window/nreum");
|
|
|
24
25
|
|
|
25
26
|
class Aggregate extends _aggregateBase.AggregateBase {
|
|
26
27
|
static featureName = _constants.FEATURE_NAME;
|
|
28
|
+
#agentInfo;
|
|
29
|
+
#agentRuntime;
|
|
30
|
+
#agentInit;
|
|
27
31
|
constructor(agentIdentifier, aggregator) {
|
|
28
32
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME);
|
|
29
|
-
|
|
30
|
-
(0,
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const agentRuntime = (0, _config.getRuntime)(agentIdentifier);
|
|
40
|
-
const denyList = agentRuntime.denyList;
|
|
41
|
-
(0, _denyList.setDenyList)(denyList);
|
|
42
|
-
let ajaxEvents = [];
|
|
43
|
-
let spaAjaxEvents = {};
|
|
44
|
-
let sentAjaxEvents = [];
|
|
45
|
-
const ee = this.ee;
|
|
46
|
-
const harvestTimeSeconds = agentInit.ajax.harvestTimeSeconds || 10;
|
|
47
|
-
const MAX_PAYLOAD_SIZE = agentInit.ajax.maxPayloadSize || 1000000;
|
|
48
|
-
|
|
49
|
-
// Exposes these methods to browser test files -- future TO DO: can be removed once these fns are extracted from the constructor into class func
|
|
50
|
-
this.storeXhr = storeXhr;
|
|
51
|
-
this.prepareHarvest = prepareHarvest;
|
|
52
|
-
this.getStoredEvents = function () {
|
|
53
|
-
return {
|
|
54
|
-
ajaxEvents,
|
|
55
|
-
spaAjaxEvents
|
|
56
|
-
};
|
|
57
|
-
};
|
|
33
|
+
this.#agentInfo = (0, _config.getInfo)(agentIdentifier);
|
|
34
|
+
this.#agentRuntime = (0, _config.getRuntime)(agentIdentifier);
|
|
35
|
+
this.#agentInit = (0, _config.getConfiguration)(agentIdentifier);
|
|
36
|
+
const harvestTimeSeconds = this.#agentInit.ajax.harvestTimeSeconds || 10;
|
|
37
|
+
this.MAX_PAYLOAD_SIZE = this.#agentInit.ajax.maxPayloadSize || 1000000;
|
|
38
|
+
(0, _denyList.setDenyList)(this.#agentRuntime.denyList);
|
|
39
|
+
this.ajaxEvents = [];
|
|
40
|
+
this.spaAjaxEvents = {};
|
|
41
|
+
this.sentAjaxEvents = [];
|
|
42
|
+
const classThis = this;
|
|
58
43
|
|
|
59
44
|
// --- v Used by old spa feature
|
|
60
|
-
ee.on('interactionDone', (interaction, wasSaved) => {
|
|
61
|
-
if (!spaAjaxEvents[interaction.id]) return;
|
|
45
|
+
this.ee.on('interactionDone', (interaction, wasSaved) => {
|
|
46
|
+
if (!this.spaAjaxEvents[interaction.id]) return;
|
|
62
47
|
if (!wasSaved) {
|
|
63
48
|
// if the ixn was saved, then its ajax reqs are part of the payload whereas if it was discarded, it should still be harvested in the ajax feature itself
|
|
64
|
-
spaAjaxEvents[interaction.id].forEach(
|
|
65
|
-
ajaxEvents.push(item);
|
|
66
|
-
});
|
|
49
|
+
this.spaAjaxEvents[interaction.id].forEach(item => this.ajaxEvents.push(item));
|
|
67
50
|
}
|
|
68
|
-
delete spaAjaxEvents[interaction.id];
|
|
51
|
+
delete this.spaAjaxEvents[interaction.id];
|
|
69
52
|
});
|
|
70
53
|
// --- ^
|
|
71
54
|
// --- v Used by new soft nav
|
|
72
|
-
(0, _registerHandler.registerHandler)('returnAjax', event => ajaxEvents.push(event), this.featureName, this.ee);
|
|
55
|
+
(0, _registerHandler.registerHandler)('returnAjax', event => this.ajaxEvents.push(event), this.featureName, this.ee);
|
|
73
56
|
// --- ^
|
|
57
|
+
(0, _registerHandler.registerHandler)('xhr', function () {
|
|
58
|
+
// the EE-drain system not only switches "this" but also passes a new EventContext with info. Should consider platform refactor to another system which passes a mutable context around separately and predictably to avoid problems like this.
|
|
59
|
+
classThis.storeXhr(...arguments, this); // this switches the context back to the class instance while passing the NR context as an argument -- see "ctx" in storeXhr
|
|
60
|
+
}, this.featureName, this.ee);
|
|
61
|
+
this.waitForFlags([]).then(() => {
|
|
62
|
+
const scheduler = new _harvestScheduler.HarvestScheduler('events', {
|
|
63
|
+
onFinished: this.onEventsHarvestFinished.bind(this),
|
|
64
|
+
getPayload: this.prepareHarvest.bind(this)
|
|
65
|
+
}, this);
|
|
66
|
+
scheduler.startTimer(harvestTimeSeconds);
|
|
67
|
+
this.drain();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
storeXhr(params, metrics, startTime, endTime, type, ctx) {
|
|
71
|
+
metrics.time = startTime;
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
// send to session traces
|
|
74
|
+
let hash;
|
|
75
|
+
if (params.cat) {
|
|
76
|
+
hash = (0, _stringify.stringify)([params.status, params.cat]);
|
|
77
|
+
} else {
|
|
78
|
+
hash = (0, _stringify.stringify)([params.status, params.host, params.pathname]);
|
|
79
|
+
}
|
|
80
|
+
const shouldCollect = (0, _denyList.shouldCollectEvent)(params);
|
|
81
|
+
const shouldOmitAjaxMetrics = this.#agentInit.feature_flags?.includes('ajax_metrics_deny_list');
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
// store for timeslice metric (harvested by jserrors feature)
|
|
84
|
+
if (shouldCollect || !shouldOmitAjaxMetrics) {
|
|
85
|
+
this.aggregator.store('xhr', hash, params, metrics);
|
|
86
|
+
}
|
|
87
|
+
if (!shouldCollect) {
|
|
88
|
+
if (params.hostname === this.#agentInfo.errorBeacon || this.#agentInit.proxy?.beacon && params.hostname === this.#agentInit.proxy.beacon) {
|
|
89
|
+
// This doesn't make a distinction if the same-domain request is going to a different port or path...
|
|
90
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
91
|
+
if (shouldOmitAjaxMetrics) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/Agent'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
84
92
|
} else {
|
|
85
|
-
|
|
93
|
+
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/App'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
94
|
+
if (shouldOmitAjaxMetrics) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/App'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
86
95
|
}
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
return; // do not send this ajax as an event
|
|
97
|
+
}
|
|
98
|
+
(0, _handle.handle)('bstXhrAgg', ['xhr', hash, params, metrics], undefined, _features.FEATURE_NAMES.sessionTrace, this.ee); // have trace feature harvest AjaxNode
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
(0, _handle.handle)('bstXhrAgg', ['xhr', hash, params, metrics], undefined, _features.FEATURE_NAMES.sessionTrace, ee);
|
|
110
|
-
var xhrContext = this;
|
|
111
|
-
var event = {
|
|
112
|
-
method: params.method,
|
|
113
|
-
status: params.status,
|
|
114
|
-
domain: params.host,
|
|
115
|
-
path: params.pathname,
|
|
116
|
-
requestSize: metrics.txSize,
|
|
117
|
-
responseSize: metrics.rxSize,
|
|
118
|
-
type,
|
|
119
|
-
startTime,
|
|
120
|
-
endTime,
|
|
121
|
-
callbackDuration: metrics.cbTime
|
|
122
|
-
};
|
|
123
|
-
if (xhrContext.dt) {
|
|
124
|
-
event.spanId = xhrContext.dt.spanId;
|
|
125
|
-
event.traceId = xhrContext.dt.traceId;
|
|
126
|
-
event.spanTimestamp = agentRuntime.timeKeeper.correctAbsoluteTimestamp(xhrContext.dt.timestamp);
|
|
127
|
-
}
|
|
100
|
+
const event = {
|
|
101
|
+
method: params.method,
|
|
102
|
+
status: params.status,
|
|
103
|
+
domain: params.host,
|
|
104
|
+
path: params.pathname,
|
|
105
|
+
requestSize: metrics.txSize,
|
|
106
|
+
responseSize: metrics.rxSize,
|
|
107
|
+
type,
|
|
108
|
+
startTime,
|
|
109
|
+
endTime,
|
|
110
|
+
callbackDuration: metrics.cbTime
|
|
111
|
+
};
|
|
112
|
+
if (ctx.dt) {
|
|
113
|
+
event.spanId = ctx.dt.spanId;
|
|
114
|
+
event.traceId = ctx.dt.traceId;
|
|
115
|
+
event.spanTimestamp = this.#agentRuntime.timeKeeper.correctAbsoluteTimestamp(ctx.dt.timestamp);
|
|
116
|
+
}
|
|
128
117
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
118
|
+
// parsed from the AJAX body, looking for operationName param & parsing query for operationType
|
|
119
|
+
event.gql = params.gql = (0, _gql.parseGQL)({
|
|
120
|
+
body: ctx.body,
|
|
121
|
+
query: ctx.parsedOrigin?.search
|
|
122
|
+
});
|
|
123
|
+
if (event.gql) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', (0, _stringify.stringify)(event.gql).length], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
124
|
+
const softNavInUse = Boolean((0, _nreum.getNREUMInitializedAgent)(this.agentIdentifier)?.features?.[_features.FEATURE_NAMES.softNav]);
|
|
125
|
+
if (softNavInUse) {
|
|
126
|
+
// For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
|
|
127
|
+
(0, _handle.handle)('ajax', [event], undefined, _features.FEATURE_NAMES.softNav, this.ee);
|
|
128
|
+
} else if (ctx.spaNode) {
|
|
129
|
+
// For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
130
|
+
const interactionId = ctx.spaNode.interaction.id;
|
|
131
|
+
this.spaAjaxEvents[interactionId] = this.spaAjaxEvents[interactionId] || [];
|
|
132
|
+
this.spaAjaxEvents[interactionId].push(event);
|
|
133
|
+
} else {
|
|
134
|
+
this.ajaxEvents.push(event);
|
|
147
135
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
body: {
|
|
158
|
-
e: payload[i]
|
|
159
|
-
}
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
if (options.retry) {
|
|
163
|
-
sentAjaxEvents = ajaxEvents.slice();
|
|
136
|
+
}
|
|
137
|
+
prepareHarvest(options) {
|
|
138
|
+
options = options || {};
|
|
139
|
+
if (this.ajaxEvents.length === 0) return null;
|
|
140
|
+
const payload = this.#getPayload(this.ajaxEvents);
|
|
141
|
+
const payloadObjs = [];
|
|
142
|
+
for (let i = 0; i < payload.length; i++) payloadObjs.push({
|
|
143
|
+
body: {
|
|
144
|
+
e: payload[i]
|
|
164
145
|
}
|
|
165
|
-
|
|
166
|
-
|
|
146
|
+
});
|
|
147
|
+
if (options.retry) this.sentAjaxEvents = this.ajaxEvents;
|
|
148
|
+
this.ajaxEvents = [];
|
|
149
|
+
return payloadObjs;
|
|
150
|
+
}
|
|
151
|
+
onEventsHarvestFinished(result) {
|
|
152
|
+
if (result.retry && this.sentAjaxEvents.length > 0) {
|
|
153
|
+
this.ajaxEvents.unshift(...this.sentAjaxEvents);
|
|
154
|
+
this.sentAjaxEvents = [];
|
|
167
155
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
it cant get any smaller and we dont want to recurse forever */
|
|
182
|
-
tooBig = true;
|
|
183
|
-
break;
|
|
184
|
-
}
|
|
185
|
-
} else {
|
|
186
|
-
payload.push(currentChunk.payload);
|
|
156
|
+
}
|
|
157
|
+
#getPayload(events, numberOfChunks) {
|
|
158
|
+
numberOfChunks = numberOfChunks || 1;
|
|
159
|
+
const payload = [];
|
|
160
|
+
const chunkSize = events.length / numberOfChunks;
|
|
161
|
+
const eventChunks = splitChunks.call(this, events, chunkSize);
|
|
162
|
+
let tooBig = false;
|
|
163
|
+
for (let i = 0; i < eventChunks.length; i++) {
|
|
164
|
+
const currentChunk = eventChunks[i];
|
|
165
|
+
if (currentChunk.tooBig) {
|
|
166
|
+
if (currentChunk.events.length > 1) {
|
|
167
|
+
tooBig = true;
|
|
168
|
+
break; // if the payload is too big BUT is made of more than 1 event, we can split it down again
|
|
187
169
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
function onEventsHarvestFinished(result) {
|
|
193
|
-
if (result.retry && sentAjaxEvents.length > 0) {
|
|
194
|
-
ajaxEvents.unshift(...sentAjaxEvents);
|
|
195
|
-
sentAjaxEvents = [];
|
|
170
|
+
// Otherwise, if it consists of one sole event, we do not send it (discarded) since we cannot break it apart any further.
|
|
171
|
+
} else {
|
|
172
|
+
payload.push(currentChunk.payload);
|
|
196
173
|
}
|
|
197
174
|
}
|
|
175
|
+
// Check if the current payload string is too big, if so then run getPayload again with more buckets.
|
|
176
|
+
return tooBig ? this.#getPayload(events, ++numberOfChunks) : payload;
|
|
198
177
|
function splitChunks(arr, chunkSize) {
|
|
199
178
|
chunkSize = chunkSize || arr.length;
|
|
200
|
-
|
|
201
|
-
for (
|
|
202
|
-
chunks.push(new
|
|
179
|
+
const chunks = [];
|
|
180
|
+
for (let i = 0, len = arr.length; i < len; i += chunkSize) {
|
|
181
|
+
chunks.push(new _chunk.default(arr.slice(i, i + chunkSize), this));
|
|
203
182
|
}
|
|
204
183
|
return chunks;
|
|
205
184
|
}
|
|
206
|
-
function Chunk(events) {
|
|
207
|
-
this.addString = (0, _belSerializer.getAddStringContext)(agentIdentifier); // pass agentIdentifier here
|
|
208
|
-
this.events = events;
|
|
209
|
-
this.payload = 'bel.7;';
|
|
210
|
-
for (var i = 0; i < events.length; i++) {
|
|
211
|
-
var event = events[i];
|
|
212
|
-
var fields = [(0, _belSerializer.numeric)(event.startTime), (0, _belSerializer.numeric)(event.endTime - event.startTime), (0, _belSerializer.numeric)(0),
|
|
213
|
-
// callbackEnd
|
|
214
|
-
(0, _belSerializer.numeric)(0),
|
|
215
|
-
// no callbackDuration for non-SPA events
|
|
216
|
-
this.addString(event.method), (0, _belSerializer.numeric)(event.status), this.addString(event.domain), this.addString(event.path), (0, _belSerializer.numeric)(event.requestSize), (0, _belSerializer.numeric)(event.responseSize), event.type === 'fetch' ? 1 : '', this.addString(0),
|
|
217
|
-
// nodeId
|
|
218
|
-
(0, _belSerializer.nullable)(event.spanId, this.addString, true) +
|
|
219
|
-
// guid
|
|
220
|
-
(0, _belSerializer.nullable)(event.traceId, this.addString, true) +
|
|
221
|
-
// traceId
|
|
222
|
-
(0, _belSerializer.nullable)(event.spanTimestamp, _belSerializer.numeric, false) // timestamp
|
|
223
|
-
];
|
|
224
|
-
var insert = '2,';
|
|
225
|
-
|
|
226
|
-
// add custom attributes
|
|
227
|
-
// gql decorators are added as custom attributes to alleviate need for new BEL schema
|
|
228
|
-
var attrParts = (0, _belSerializer.addCustomAttributes)({
|
|
229
|
-
...((0, _config.getInfo)(agentIdentifier).jsAttributes || {}),
|
|
230
|
-
...(event.gql || {})
|
|
231
|
-
}, this.addString);
|
|
232
|
-
fields.unshift((0, _belSerializer.numeric)(attrParts.length));
|
|
233
|
-
insert += fields.join(',');
|
|
234
|
-
if (attrParts && attrParts.length > 0) {
|
|
235
|
-
insert += ';' + attrParts.join(';');
|
|
236
|
-
}
|
|
237
|
-
if (i + 1 < events.length) insert += ';';
|
|
238
|
-
this.payload += insert;
|
|
239
|
-
}
|
|
240
|
-
this.tooBig = function (maxPayloadSize) {
|
|
241
|
-
maxPayloadSize = maxPayloadSize || MAX_PAYLOAD_SIZE;
|
|
242
|
-
return this.payload.length * 2 > maxPayloadSize;
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
185
|
}
|
|
246
186
|
}
|
|
247
187
|
exports.Aggregate = Aggregate;
|
|
@@ -33,9 +33,6 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
33
33
|
constructor(agentIdentifier, aggregator) {
|
|
34
34
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
35
35
|
super(agentIdentifier, aggregator, _constants.FEATURE_NAME, auto);
|
|
36
|
-
|
|
37
|
-
// Very unlikely, but in case the existing XMLHttpRequest.prototype object on the page couldn't be wrapped.
|
|
38
|
-
if (!(0, _config.getRuntime)(agentIdentifier).xhrWrappable) return;
|
|
39
36
|
this.dt = new _distributedTracing.DT(agentIdentifier);
|
|
40
37
|
this.handler = (type, args, ctx, group) => (0, _handle.handle)(type, args, ctx, group, this.ee);
|
|
41
38
|
|
|
@@ -42,13 +42,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
42
42
|
this.bufferedErrorsUnderSpa = {};
|
|
43
43
|
this.currentBody = undefined;
|
|
44
44
|
this.errorOnPage = false;
|
|
45
|
-
this.replayAborted = false;
|
|
46
45
|
|
|
47
46
|
// this will need to change to match whatever ee we use in the instrument
|
|
48
47
|
this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
|
|
49
|
-
this.ee.on('REPLAY_ABORTED', () => {
|
|
50
|
-
this.replayAborted = true;
|
|
51
|
-
});
|
|
52
48
|
(0, _registerHandler.registerHandler)('err', function () {
|
|
53
49
|
return _this.storeError(...arguments);
|
|
54
50
|
}, this.featureName, this.ee);
|
|
@@ -94,11 +90,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
94
90
|
payload.qs.ri = releaseIds;
|
|
95
91
|
}
|
|
96
92
|
if (body && body.err && body.err.length) {
|
|
97
|
-
|
|
98
|
-
body.err.forEach(e => {
|
|
99
|
-
delete e.params?.hasReplay;
|
|
100
|
-
});
|
|
101
|
-
}
|
|
93
|
+
this.#runCrossFeatureChecks(body.err);
|
|
102
94
|
if (!this.errorOnPage) {
|
|
103
95
|
payload.qs.pve = '1';
|
|
104
96
|
this.errorOnPage = true;
|
|
@@ -172,14 +164,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
172
164
|
// Notice if filterOutput isn't false|undefined OR our specified object, this func would've returned already (so it's unnecessary to req-check group).
|
|
173
165
|
// Do not modify the name ('errorGroup') of params without DEM approval!
|
|
174
166
|
if (filterOutput?.group) params.errorGroup = filterOutput.group;
|
|
175
|
-
|
|
167
|
+
if (hasReplay) params.hasReplay = hasReplay;
|
|
176
168
|
/**
|
|
177
169
|
* The bucketHash is different from the params.stackHash because the params.stackHash is based on the canonicalized
|
|
178
170
|
* stack trace and is used downstream in NR1 to attempt to group the same errors across different browsers. However,
|
|
179
171
|
* the canonical stack trace excludes items like the column number increasing the hit-rate of different errors potentially
|
|
180
172
|
* bucketing and ultimately resulting in the loss of data in NR1.
|
|
181
173
|
*/
|
|
182
|
-
var bucketHash = (0, _stringHashCode.stringHashCode)("".concat(stackInfo.name, "_").concat(stackInfo.message, "_").concat(stackInfo.stackString));
|
|
174
|
+
var bucketHash = (0, _stringHashCode.stringHashCode)("".concat(stackInfo.name, "_").concat(stackInfo.message, "_").concat(stackInfo.stackString, "_").concat(params.hasReplay ? 1 : 0));
|
|
183
175
|
if (!this.stackReported[bucketHash]) {
|
|
184
176
|
this.stackReported[bucketHash] = true;
|
|
185
177
|
params.stack_trace = (0, _formatStackTrace.truncateSize)(stackInfo.stackString);
|
|
@@ -198,9 +190,8 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
198
190
|
params.pageview = 1;
|
|
199
191
|
this.pageviewReported[bucketHash] = true;
|
|
200
192
|
}
|
|
201
|
-
if (hasReplay && !this.replayAborted) params.hasReplay = hasReplay;
|
|
202
193
|
params.firstOccurrenceTimestamp = this.observedAt[bucketHash];
|
|
203
|
-
params.timestamp =
|
|
194
|
+
params.timestamp = agentRuntime.timeKeeper.convertRelativeTimestamp(time);
|
|
204
195
|
var type = internal ? 'ierr' : 'err';
|
|
205
196
|
var newMetrics = {
|
|
206
197
|
time
|
|
@@ -293,5 +284,27 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
293
284
|
);
|
|
294
285
|
delete this.bufferedErrorsUnderSpa[interactionId]; // wipe the list of jserrors so they aren't duplicated by another call to the same id
|
|
295
286
|
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Dispatches a cross-feature communication event to allow other
|
|
290
|
+
* features to provide flags and data that can be used to mutation
|
|
291
|
+
* to the payload and to allow features to know about a feature
|
|
292
|
+
* harvest happening.
|
|
293
|
+
* @param {any[]} errors Array of errors from the payload body
|
|
294
|
+
*/
|
|
295
|
+
#runCrossFeatureChecks(errors) {
|
|
296
|
+
const errorHashes = errors.map(error => error.params.stackHash);
|
|
297
|
+
const crossFeatureData = {
|
|
298
|
+
errorHashes
|
|
299
|
+
};
|
|
300
|
+
this.ee.emit("cfc.".concat(this.featureName), [crossFeatureData]);
|
|
301
|
+
let hasReplayFlag = errors.find(err => err.params.hasReplay);
|
|
302
|
+
if (hasReplayFlag && !crossFeatureData.hasReplay) {
|
|
303
|
+
// Some errors have `hasReplay` and a replay is not being recorded
|
|
304
|
+
errors.forEach(error => {
|
|
305
|
+
delete error.params.hasReplay;
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
296
309
|
}
|
|
297
310
|
exports.Aggregate = Aggregate;
|
|
@@ -103,14 +103,14 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
103
103
|
// Navigation Timing level 2 API that replaced PerformanceTiming & PerformanceNavigation
|
|
104
104
|
const navTimingEntry = _runtime.globalScope?.performance?.getEntriesByType('navigation')?.[0];
|
|
105
105
|
const perf = {
|
|
106
|
-
timing: (0, _navTiming.addPT)(
|
|
106
|
+
timing: (0, _navTiming.addPT)(_runtime.originTime, navTimingEntry, {}),
|
|
107
107
|
navigation: (0, _navTiming.addPN)(navTimingEntry, {})
|
|
108
108
|
};
|
|
109
109
|
queryParameters.perf = (0, _stringify.stringify)(perf);
|
|
110
110
|
} else if (typeof PerformanceTiming !== 'undefined') {
|
|
111
111
|
// Safari pre-15 did not support level 2 timing
|
|
112
112
|
const perf = {
|
|
113
|
-
timing: (0, _navTiming.addPT)(
|
|
113
|
+
timing: (0, _navTiming.addPT)(_runtime.originTime, _runtime.globalScope.performance.timing, {}, true),
|
|
114
114
|
navigation: (0, _navTiming.addPN)(_runtime.globalScope.performance.navigation, {})
|
|
115
115
|
};
|
|
116
116
|
queryParameters.perf = (0, _stringify.stringify)(perf);
|
|
@@ -142,7 +142,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
142
142
|
return;
|
|
143
143
|
}
|
|
144
144
|
try {
|
|
145
|
-
const timeKeeper = new _timeKeeper.TimeKeeper();
|
|
145
|
+
const timeKeeper = new _timeKeeper.TimeKeeper(this.agentIdentifier);
|
|
146
146
|
timeKeeper.processRumRequest(xhr, rumStartTime, rumEndTime);
|
|
147
147
|
if (!timeKeeper.ready) throw new Error('TimeKeeper not ready');
|
|
148
148
|
agentRuntime.timeKeeper = timeKeeper;
|
|
@@ -57,9 +57,11 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
57
57
|
/** set at BCS response, stored in runtime */
|
|
58
58
|
this.timeKeeper = undefined;
|
|
59
59
|
this.recorder = args?.recorder;
|
|
60
|
-
this.preloaded = !!this.recorder;
|
|
61
60
|
this.errorNoticed = args?.errorNoticed || false;
|
|
62
61
|
(0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
62
|
+
this.ee.on("cfc.".concat(_features.FEATURE_NAMES.jserrors), crossFeatureData => {
|
|
63
|
+
crossFeatureData.hasReplay = !!(this.scheduler?.started && this.recorder && this.mode === _constants3.MODE.FULL && !this.blocked && this.entitled);
|
|
64
|
+
});
|
|
63
65
|
|
|
64
66
|
// 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.
|
|
65
67
|
this.ee.on(_constants3.SESSION_EVENTS.RESET, () => {
|
|
@@ -106,6 +108,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
106
108
|
(0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.PAUSE, () => {
|
|
107
109
|
this.forceStop(this.mode !== _constants3.MODE.ERROR);
|
|
108
110
|
}, this.featureName, this.ee);
|
|
111
|
+
(0, _registerHandler.registerHandler)(_constants.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, e => {
|
|
112
|
+
this.handleError(e);
|
|
113
|
+
}, this.featureName, this.ee);
|
|
109
114
|
const {
|
|
110
115
|
error_sampling_rate,
|
|
111
116
|
sampling_rate,
|
|
@@ -154,15 +159,17 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
154
159
|
}
|
|
155
160
|
}
|
|
156
161
|
switchToFull() {
|
|
162
|
+
if (!this.entitled || this.blocked) return;
|
|
157
163
|
this.mode = _constants3.MODE.FULL;
|
|
158
164
|
// if the error was noticed AFTER the recorder was already imported....
|
|
159
165
|
if (this.recorder && this.initialized) {
|
|
160
|
-
this.recorder.
|
|
161
|
-
this.recorder.startRecording();
|
|
166
|
+
if (!this.recorder.recording) this.recorder.startRecording();
|
|
162
167
|
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
163
168
|
this.syncWithSessionManager({
|
|
164
169
|
sessionReplayMode: this.mode
|
|
165
170
|
});
|
|
171
|
+
} else {
|
|
172
|
+
this.initializeRecording(false, true, true);
|
|
166
173
|
}
|
|
167
174
|
}
|
|
168
175
|
|
|
@@ -223,7 +230,6 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
223
230
|
|
|
224
231
|
// 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
232
|
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));
|
|
227
233
|
|
|
228
234
|
// FULL mode records AND reports from the beginning, while ERROR mode only records (but does not report).
|
|
229
235
|
// ERROR mode will do this until an error is thrown, and then switch into FULL mode.
|
|
@@ -317,7 +323,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
317
323
|
return [payload];
|
|
318
324
|
}
|
|
319
325
|
getCorrectedTimestamp(node) {
|
|
320
|
-
if (!node
|
|
326
|
+
if (!node?.timestamp) return;
|
|
321
327
|
if (node.__newrelic) return node.timestamp;
|
|
322
328
|
return this.timeKeeper.correctAbsoluteTimestamp(node.timestamp);
|
|
323
329
|
}
|
|
@@ -367,6 +373,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
367
373
|
...(agentMetadata.entityGuid && {
|
|
368
374
|
entityGuid: agentMetadata.entityGuid
|
|
369
375
|
}),
|
|
376
|
+
harvestId: [agentRuntime.session?.state.value, agentRuntime.ptid, agentRuntime.harvestCount].filter(x => x).join('_'),
|
|
370
377
|
'replay.firstTimestamp': firstTimestamp,
|
|
371
378
|
'replay.lastTimestamp': lastTimestamp,
|
|
372
379
|
'replay.nodes': events.length,
|
|
@@ -10,7 +10,8 @@ const FEATURE_NAME = exports.FEATURE_NAME = _features.FEATURE_NAMES.sessionRepla
|
|
|
10
10
|
const SR_EVENT_EMITTER_TYPES = exports.SR_EVENT_EMITTER_TYPES = {
|
|
11
11
|
RECORD: 'recordReplay',
|
|
12
12
|
PAUSE: 'pauseReplay',
|
|
13
|
-
REPLAY_RUNNING: 'replayRunning'
|
|
13
|
+
REPLAY_RUNNING: 'replayRunning',
|
|
14
|
+
ERROR_DURING_REPLAY: 'errorDuringReplay'
|
|
14
15
|
};
|
|
15
16
|
const AVG_COMPRESSION = exports.AVG_COMPRESSION = 0.12;
|
|
16
17
|
const RRWEB_EVENT_TYPES = exports.RRWEB_EVENT_TYPES = {
|
|
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.Instrument = void 0;
|
|
7
|
+
var _handle = require("../../../common/event-emitter/handle");
|
|
7
8
|
var _constants = require("../../../common/session/constants");
|
|
8
9
|
var _instrumentBase = require("../../utils/instrument-base");
|
|
9
10
|
var _constants2 = require("../constants");
|
|
@@ -25,19 +26,28 @@ class Instrument extends _instrumentBase.InstrumentBase {
|
|
|
25
26
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
26
27
|
super(agentIdentifier, aggregator, _constants2.FEATURE_NAME, auto);
|
|
27
28
|
let session;
|
|
29
|
+
this.replayRunning = false;
|
|
28
30
|
try {
|
|
29
31
|
session = JSON.parse(localStorage.getItem("".concat(_constants.PREFIX, "_").concat(_constants.DEFAULT_KEY)));
|
|
30
32
|
} catch (err) {}
|
|
31
33
|
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
|
-
});
|
|
37
34
|
this.#startRecording(session?.sessionReplayMode);
|
|
38
35
|
} else {
|
|
39
36
|
this.importAggregator();
|
|
40
37
|
}
|
|
38
|
+
|
|
39
|
+
/** If the recorder is running, we can pass error events on to the agg to help it switch to full mode later */
|
|
40
|
+
this.ee.on('err', e => {
|
|
41
|
+
if (this.replayRunning) {
|
|
42
|
+
this.errorNoticed = true;
|
|
43
|
+
(0, _handle.handle)(_constants2.SR_EVENT_EMITTER_TYPES.ERROR_DURING_REPLAY, [e], undefined, this.featureName, this.ee);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
/** Emitted by the recorder when it starts capturing data, used to determine if we should pass errors on to the agg */
|
|
48
|
+
this.ee.on(_constants2.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, isRunning => {
|
|
49
|
+
this.replayRunning = isRunning;
|
|
50
|
+
});
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
// At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
|
|
@@ -103,11 +103,10 @@ class Recorder {
|
|
|
103
103
|
collectFonts: collect_fonts,
|
|
104
104
|
checkoutEveryNms: _constants.CHECKOUT_MS[this.parent.mode]
|
|
105
105
|
});
|
|
106
|
-
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
107
106
|
this.stopRecording = () => {
|
|
108
107
|
this.recording = false;
|
|
109
108
|
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [false, this.parent.mode]);
|
|
110
|
-
stop();
|
|
109
|
+
stop?.();
|
|
111
110
|
};
|
|
112
111
|
}
|
|
113
112
|
|
|
@@ -150,6 +149,10 @@ class Recorder {
|
|
|
150
149
|
if (!event) return;
|
|
151
150
|
if (!this.parent.scheduler && this.#preloaded.length) this.currentBufferTarget = this.#preloaded[this.#preloaded.length - 1];else this.currentBufferTarget = this.#events;
|
|
152
151
|
if (this.parent.blocked) return;
|
|
152
|
+
if (!this.notified) {
|
|
153
|
+
this.parent.ee.emit(_constants.SR_EVENT_EMITTER_TYPES.REPLAY_RUNNING, [true, this.parent.mode]);
|
|
154
|
+
this.notified = true;
|
|
155
|
+
}
|
|
153
156
|
if (this.parent.timeKeeper?.ready && !event.__newrelic) {
|
|
154
157
|
event.__newrelic = (0, _utils.buildNRMetaNode)(event.timestamp, this.parent.timeKeeper);
|
|
155
158
|
event.timestamp = this.parent.timeKeeper.correctAbsoluteTimestamp(event.timestamp);
|
|
@@ -180,7 +183,7 @@ class Recorder {
|
|
|
180
183
|
|
|
181
184
|
// We are making an effort to try to keep payloads manageable for unloading. If they reach the unload limit before their interval,
|
|
182
185
|
// it will send immediately. This often happens on the first snapshot, which can be significantly larger than the other payloads.
|
|
183
|
-
if (payloadSize > _constants.IDEAL_PAYLOAD_SIZE && this.parent.mode
|
|
186
|
+
if ((event.type === _constants.RRWEB_EVENT_TYPES.FullSnapshot && this.currentBufferTarget.hasMeta || payloadSize > _constants.IDEAL_PAYLOAD_SIZE) && this.parent.mode === _constants2.MODE.FULL) {
|
|
184
187
|
// if we've made it to the ideal size of ~64kb before the interval timer, we should send early.
|
|
185
188
|
if (this.parent.scheduler) {
|
|
186
189
|
this.parent.scheduler.runHarvest();
|