@newrelic/browser-agent 1.246.1 → 1.248.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 +24 -0
- package/README.md +10 -2
- package/dist/cjs/common/config/state/info.js +2 -1
- package/dist/cjs/common/config/state/init.js +2 -1
- package/dist/cjs/common/config/state/loader-config.js +2 -1
- package/dist/cjs/common/config/state/runtime.js +2 -1
- 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 +3 -1
- package/dist/cjs/common/dispatch/global-event.js +19 -0
- package/dist/cjs/common/session/session-entity.js +14 -9
- package/dist/cjs/common/util/feature-flags.js +17 -12
- package/dist/cjs/common/window/load.js +1 -0
- package/dist/cjs/common/window/nreum.js +18 -17
- package/dist/cjs/features/metrics/aggregate/index.js +4 -2
- package/dist/cjs/features/session_replay/aggregate/index.js +70 -20
- package/dist/cjs/features/session_trace/aggregate/index.js +33 -18
- package/dist/cjs/features/spa/aggregate/index.js +7 -1
- package/dist/cjs/features/utils/aggregate-base.js +3 -1
- package/dist/cjs/loaders/agent-base.js +19 -0
- package/dist/cjs/loaders/agent.js +6 -4
- package/dist/cjs/loaders/api/api.js +10 -1
- package/dist/cjs/loaders/configure/configure.js +14 -13
- package/dist/cjs/loaders/configure/nonce.js +13 -0
- package/dist/cjs/loaders/configure/nonce.npm.js +2 -0
- package/dist/cjs/loaders/micro-agent.js +5 -4
- package/dist/esm/common/config/state/info.js +3 -2
- package/dist/esm/common/config/state/init.js +3 -2
- package/dist/esm/common/config/state/loader-config.js +3 -2
- package/dist/esm/common/config/state/runtime.js +3 -2
- 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 +1 -0
- package/dist/esm/common/dispatch/global-event.js +13 -0
- package/dist/esm/common/session/session-entity.js +14 -9
- package/dist/esm/common/util/feature-flags.js +17 -12
- package/dist/esm/common/window/load.js +1 -1
- package/dist/esm/common/window/nreum.js +16 -16
- package/dist/esm/features/metrics/aggregate/index.js +4 -2
- package/dist/esm/features/session_replay/aggregate/index.js +69 -19
- package/dist/esm/features/session_trace/aggregate/index.js +33 -18
- package/dist/esm/features/spa/aggregate/index.js +7 -1
- package/dist/esm/features/utils/aggregate-base.js +3 -1
- package/dist/esm/loaders/agent-base.js +19 -0
- package/dist/esm/loaders/agent.js +7 -5
- package/dist/esm/loaders/api/api.js +10 -1
- package/dist/esm/loaders/configure/configure.js +15 -14
- package/dist/esm/loaders/configure/nonce.js +11 -0
- package/dist/esm/loaders/configure/nonce.npm.js +1 -0
- package/dist/esm/loaders/micro-agent.js +6 -5
- package/dist/types/common/config/state/info.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/config/state/loader-config.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 +1 -0
- package/dist/types/common/constants/runtime.d.ts.map +1 -1
- package/dist/types/common/dispatch/global-event.d.ts +2 -0
- package/dist/types/common/dispatch/global-event.d.ts.map +1 -0
- package/dist/types/common/session/session-entity.d.ts +1 -0
- package/dist/types/common/session/session-entity.d.ts.map +1 -1
- package/dist/types/common/util/feature-flags.d.ts.map +1 -1
- package/dist/types/common/window/load.d.ts +1 -0
- package/dist/types/common/window/load.d.ts.map +1 -1
- package/dist/types/common/window/nreum.d.ts +7 -1
- package/dist/types/common/window/nreum.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +11 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +13 -0
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +2 -0
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts +4 -11
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/dist/types/loaders/configure/nonce.d.ts +1 -0
- package/dist/types/loaders/configure/nonce.d.ts.map +1 -0
- package/dist/types/loaders/configure/nonce.npm.d.ts +1 -0
- package/dist/types/loaders/configure/nonce.npm.d.ts.map +1 -0
- package/dist/types/loaders/micro-agent.d.ts +2 -2
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/common/config/state/info.js +3 -2
- package/src/common/config/state/init.js +3 -2
- package/src/common/config/state/loader-config.js +3 -2
- package/src/common/config/state/runtime.js +3 -2
- package/src/common/constants/runtime.js +2 -0
- package/src/common/dispatch/global-event.js +12 -0
- package/src/common/session/session-entity.js +13 -9
- package/src/common/util/feature-flags.js +15 -12
- package/src/common/window/__mocks__/nreum.js +2 -1
- package/src/common/window/load.js +1 -1
- package/src/common/window/nreum.js +15 -18
- package/src/features/metrics/aggregate/index.js +5 -1
- package/src/features/session_replay/aggregate/index.js +74 -23
- package/src/features/session_trace/aggregate/index.js +33 -14
- package/src/features/spa/aggregate/index.js +10 -1
- package/src/features/utils/aggregate-base.js +1 -1
- package/src/loaders/agent-base.js +19 -0
- package/src/loaders/agent.js +5 -5
- package/src/loaders/api/api.js +12 -1
- package/src/loaders/configure/configure.js +17 -15
- package/src/loaders/configure/nonce.js +12 -0
- package/src/loaders/configure/nonce.npm.js +1 -0
- package/src/loaders/micro-agent.js +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.248.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.247.0...v1.248.0) (2023-11-16)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Report enduser.id with Session Replay ([#815](https://github.com/newrelic/newrelic-browser-agent/issues/815)) ([8f5446d](https://github.com/newrelic/newrelic-browser-agent/commit/8f5446d1f7679f6a5ea0ba90eb082d1d4deb0d93))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Fix issue with errors forcefully triggering session traces ([#819](https://github.com/newrelic/newrelic-browser-agent/issues/819)) ([3872c35](https://github.com/newrelic/newrelic-browser-agent/commit/3872c35a173f76644b663df5ca0474971451b7cf))
|
|
17
|
+
|
|
18
|
+
## [1.247.0](https://github.com/newrelic/newrelic-browser-agent/compare/v1.246.1...v1.247.0) (2023-11-14)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* Add basic support for deferring the browser agent loader script ([#800](https://github.com/newrelic/newrelic-browser-agent/issues/800)) ([92d864c](https://github.com/newrelic/newrelic-browser-agent/commit/92d864cb12a3076fd9b623fcd411d2dc9190110c))
|
|
24
|
+
* Add relative timestamps to Session Replay payloads ([#810](https://github.com/newrelic/newrelic-browser-agent/issues/810)) ([e4d1c70](https://github.com/newrelic/newrelic-browser-agent/commit/e4d1c701228e011d7c6f9d84cdc107044c69ce79))
|
|
25
|
+
* Add session durationMs to Session Replay payloads ([#792](https://github.com/newrelic/newrelic-browser-agent/issues/792)) ([3dfc4d4](https://github.com/newrelic/newrelic-browser-agent/commit/3dfc4d43fa978eeec47ebf432f8741562d0dd864))
|
|
26
|
+
* Enable SRI and nonce attributes for async chunks ([#805](https://github.com/newrelic/newrelic-browser-agent/issues/805)) ([fd9c3f3](https://github.com/newrelic/newrelic-browser-agent/commit/fd9c3f388f17353796ac2ebf18814353ca819dcf))
|
|
27
|
+
* Expose library versions used to capture session replay data ([#809](https://github.com/newrelic/newrelic-browser-agent/issues/809)) ([bc275ee](https://github.com/newrelic/newrelic-browser-agent/commit/bc275ee20242a5208358a0a77ac75e2b7cbd11c4))
|
|
28
|
+
* Session Replay API ([#803](https://github.com/newrelic/newrelic-browser-agent/issues/803)) ([12eb453](https://github.com/newrelic/newrelic-browser-agent/commit/12eb4530cfb5eb1e0a94d858485be0df40582c21))
|
|
29
|
+
|
|
6
30
|
## [1.246.1](https://github.com/newrelic/newrelic-browser-agent/compare/v1.246.0...v1.246.1) (2023-10-31)
|
|
7
31
|
|
|
8
32
|
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Before instrumenting your app using the NPM package, a Browser App should be con
|
|
|
39
39
|
|
|
40
40
|
## Instantiating the agent
|
|
41
41
|
|
|
42
|
-
For best results, import and instantiate the `BrowserAgent` class as close to the top of the `head` element of your app's HTML output as possible. The specific location and method will vary based on your application's architecture or framework.
|
|
42
|
+
For best results, import and instantiate the `BrowserAgent` class as close to the top of the `head` element of your app's HTML output as possible. The specific location and method will vary based on your application's architecture or framework. See [Library Support](#library-support) for more information.
|
|
43
43
|
|
|
44
44
|
Populate the `options` parameter using configuration values found in the the *Copy/Paste JavaScript* box in your browser app's *Application settings* page in New Relic One.
|
|
45
45
|
|
|
@@ -229,6 +229,14 @@ import { Agent } from '@newrelic/browser-agent/loaders/agent'
|
|
|
229
229
|
import { Metrics } from '@newrelic/browser-agent/src/features/metrics'
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
+
## Library Support
|
|
233
|
+
|
|
234
|
+
The browser agent is written to be agnostic to any JavaScript library or framework. The agent exposes a number of [API methods](https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/using-browser-apis/) that can be incorporated into libraries and frameworks. For example, export or make available the initialized agent and create a new error boundary in your react application that calls `browserAgent.noticeError()`.
|
|
235
|
+
|
|
236
|
+
### Server-Side Rendering
|
|
237
|
+
|
|
238
|
+
A lot of new frameworks support the concept of server-side rendering the pages of an application to improve SEO and the performance of the user experience. The browser agent must be imported and instantiated within a browser context and will not work if executed on a server or in the server context of a server-side rendered application. These frameworks typically provide support for defining code that should only be run on the client. Check your frameworks documentation for more information.
|
|
239
|
+
|
|
232
240
|
## Disclaimers
|
|
233
241
|
|
|
234
242
|
The session replay library shipping with this version of the browser agent is in *limited preview* and is not turned on by default. To use the feature, users will need to be part of the limited preview customer group and configure their browser application settings in the UI.
|
|
@@ -259,6 +267,6 @@ To all contributors, we thank you! Without your contribution, this project would
|
|
|
259
267
|
|
|
260
268
|
## License
|
|
261
269
|
|
|
262
|
-
The Browser agent is licensed under the [Apache 2.0](
|
|
270
|
+
The Browser agent is licensed under the [Apache 2.0](https://apache.org/licenses/LICENSE-2.0.txt) License.
|
|
263
271
|
|
|
264
272
|
The Browser agent also uses source code from third-party libraries. Full details on which libraries are used and the terms under which they are licensed can be found in the [third-party notices document](THIRD_PARTY_NOTICES.md).
|
|
@@ -46,5 +46,6 @@ function getInfo(id) {
|
|
|
46
46
|
function setInfo(id, obj) {
|
|
47
47
|
if (!id) throw new Error('All info objects require an agent identifier!');
|
|
48
48
|
_cache[id] = (0, _configurable.getModeledObject)(obj, model);
|
|
49
|
-
(0, _nreum.
|
|
49
|
+
const agentInst = (0, _nreum.getNREUMInitializedAgent)(id);
|
|
50
|
+
if (agentInst) agentInst.info = _cache[id];
|
|
50
51
|
}
|
|
@@ -173,7 +173,8 @@ function getConfiguration(id) {
|
|
|
173
173
|
function setConfiguration(id, obj) {
|
|
174
174
|
if (!id) throw new Error(missingAgentIdError);
|
|
175
175
|
_cache[id] = (0, _configurable.getModeledObject)(obj, model());
|
|
176
|
-
(0, _nreum.
|
|
176
|
+
const agentInst = (0, _nreum.getNREUMInitializedAgent)(id);
|
|
177
|
+
if (agentInst) agentInst.init = _cache[id];
|
|
177
178
|
}
|
|
178
179
|
function getConfigurationValue(id, path) {
|
|
179
180
|
if (!id) throw new Error(missingAgentIdError);
|
|
@@ -24,5 +24,6 @@ function getLoaderConfig(id) {
|
|
|
24
24
|
function setLoaderConfig(id, obj) {
|
|
25
25
|
if (!id) throw new Error('All loader-config objects require an agent identifier!');
|
|
26
26
|
_cache[id] = (0, _configurable.getModeledObject)(obj, model);
|
|
27
|
-
(0, _nreum.
|
|
27
|
+
const agentInst = (0, _nreum.getNREUMInitializedAgent)(id);
|
|
28
|
+
if (agentInst) agentInst.loader_config = _cache[id];
|
|
28
29
|
}
|
|
@@ -38,5 +38,6 @@ function getRuntime(id) {
|
|
|
38
38
|
function setRuntime(id, obj) {
|
|
39
39
|
if (!id) throw new Error('All runtime objects require an agent identifier!');
|
|
40
40
|
_cache[id] = (0, _configurable.getModeledObject)(obj, model);
|
|
41
|
-
(0, _nreum.
|
|
41
|
+
const agentInst = (0, _nreum.getNREUMInitializedAgent)(id);
|
|
42
|
+
if (agentInst) agentInst.runtime = _cache[id];
|
|
42
43
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.supportsSendBeacon = exports.offset = exports.isiOS = exports.isWorkerScope = exports.isIE = exports.isBrowserScope = exports.initiallyHidden = exports.initialLocation = exports.iOSBelow16 = exports.globalScope = exports.ffVersion = void 0;
|
|
6
|
+
exports.supportsSendBeacon = exports.offset = exports.loadedAsDeferredBrowserScript = exports.isiOS = exports.isWorkerScope = exports.isIE = exports.isBrowserScope = exports.initiallyHidden = exports.initialLocation = exports.iOSBelow16 = exports.globalScope = exports.ffVersion = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* @file Contains constants about the environment the agent is running
|
|
9
9
|
* within. These values are derived at the time the agent is first loaded.
|
|
@@ -24,6 +24,8 @@ const isWorkerScope = typeof WorkerGlobalScope !== 'undefined' && (typeof self !
|
|
|
24
24
|
exports.isWorkerScope = isWorkerScope;
|
|
25
25
|
const globalScope = isBrowserScope ? window : typeof WorkerGlobalScope !== 'undefined' && (typeof self !== 'undefined' && self instanceof WorkerGlobalScope && self || typeof globalThis !== 'undefined' && globalThis instanceof WorkerGlobalScope && globalThis);
|
|
26
26
|
exports.globalScope = globalScope;
|
|
27
|
+
const loadedAsDeferredBrowserScript = globalScope?.document?.readyState === 'complete';
|
|
28
|
+
exports.loadedAsDeferredBrowserScript = loadedAsDeferredBrowserScript;
|
|
27
29
|
const initiallyHidden = Boolean(globalScope?.document?.visibilityState === 'hidden');
|
|
28
30
|
exports.initiallyHidden = initiallyHidden;
|
|
29
31
|
const initialLocation = '' + globalScope?.location;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.dispatchGlobalEvent = dispatchGlobalEvent;
|
|
7
|
+
var _runtime = require("../constants/runtime");
|
|
8
|
+
const GLOBAL_EVENT_NAMESPACE = 'newrelic';
|
|
9
|
+
function dispatchGlobalEvent() {
|
|
10
|
+
let detail = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
11
|
+
try {
|
|
12
|
+
_runtime.globalScope.dispatchEvent(new CustomEvent(GLOBAL_EVENT_NAMESPACE, {
|
|
13
|
+
detail
|
|
14
|
+
}));
|
|
15
|
+
} catch (err) {
|
|
16
|
+
// something happened... dispatchEvent or CustomEvent might not be supported
|
|
17
|
+
// decide what to do about it here
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -113,8 +113,8 @@ class SessionEntity {
|
|
|
113
113
|
this.expiresTimer = new _timer.Timer({
|
|
114
114
|
// When the inactive timer ends, collect a SM and reset the session
|
|
115
115
|
onEnd: () => {
|
|
116
|
-
this.collectSM('expired'
|
|
117
|
-
this.collectSM('duration'
|
|
116
|
+
this.collectSM('expired');
|
|
117
|
+
this.collectSM('duration');
|
|
118
118
|
this.reset();
|
|
119
119
|
}
|
|
120
120
|
}, this.state.expiresAt - Date.now());
|
|
@@ -130,8 +130,8 @@ class SessionEntity {
|
|
|
130
130
|
this.inactiveTimer = new _interactionTimer.InteractionTimer({
|
|
131
131
|
// When the inactive timer ends, collect a SM and reset the session
|
|
132
132
|
onEnd: () => {
|
|
133
|
-
this.collectSM('inactive'
|
|
134
|
-
this.collectSM('duration'
|
|
133
|
+
this.collectSM('inactive');
|
|
134
|
+
this.collectSM('duration');
|
|
135
135
|
this.reset();
|
|
136
136
|
},
|
|
137
137
|
// When the inactive timer refreshes, it will update the storage values with an update timestamp
|
|
@@ -181,14 +181,14 @@ class SessionEntity {
|
|
|
181
181
|
if (this.isInvalid(obj)) return {};
|
|
182
182
|
// if the session expires, collect a SM count before resetting
|
|
183
183
|
if (this.isExpired(obj.expiresAt)) {
|
|
184
|
-
this.collectSM('expired'
|
|
184
|
+
this.collectSM('expired');
|
|
185
185
|
this.collectSM('duration', obj, true);
|
|
186
186
|
return this.reset();
|
|
187
187
|
}
|
|
188
188
|
// if "inactive" timer is expired at "read" time -- esp. initial read -- reset
|
|
189
189
|
// collect a SM count before resetting
|
|
190
190
|
if (this.isExpired(obj.inactiveAt)) {
|
|
191
|
-
this.collectSM('inactive'
|
|
191
|
+
this.collectSM('inactive');
|
|
192
192
|
this.collectSM('duration', obj, true);
|
|
193
193
|
return this.reset();
|
|
194
194
|
}
|
|
@@ -279,15 +279,20 @@ class SessionEntity {
|
|
|
279
279
|
collectSM(type, data, useUpdatedAt) {
|
|
280
280
|
let value, tag;
|
|
281
281
|
if (type === 'duration') {
|
|
282
|
-
|
|
283
|
-
const endingTimestamp = useUpdatedAt ? data.updatedAt : Date.now();
|
|
284
|
-
value = endingTimestamp - startingTimestamp;
|
|
282
|
+
value = this.getDuration(data, useUpdatedAt);
|
|
285
283
|
tag = 'Session/Duration/Ms';
|
|
286
284
|
}
|
|
287
285
|
if (type === 'expired') tag = 'Session/Expired/Seen';
|
|
288
286
|
if (type === 'inactive') tag = 'Session/Inactive/Seen';
|
|
289
287
|
if (tag) (0, _handle.handle)(_constants2.SUPPORTABILITY_METRIC_CHANNEL, [tag, value], undefined, _features.FEATURE_NAMES.metrics, this.ee);
|
|
290
288
|
}
|
|
289
|
+
getDuration() {
|
|
290
|
+
let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.state;
|
|
291
|
+
let useUpdatedAt = arguments.length > 1 ? arguments[1] : undefined;
|
|
292
|
+
const startingTimestamp = data.expiresAt - this.expiresMs;
|
|
293
|
+
const endingTimestamp = !useUpdatedAt ? data.updatedAt : Date.now();
|
|
294
|
+
return endingTimestamp - startingTimestamp;
|
|
295
|
+
}
|
|
291
296
|
|
|
292
297
|
/**
|
|
293
298
|
* @param {number} futureMs - The number of ms to use to generate a future timestamp
|
|
@@ -8,6 +8,7 @@ exports.activatedFeatures = void 0;
|
|
|
8
8
|
var _contextualEe = require("../event-emitter/contextual-ee");
|
|
9
9
|
var _handle = require("../event-emitter/handle");
|
|
10
10
|
var _features = require("../../loaders/features/features");
|
|
11
|
+
var _globalEvent = require("../dispatch/global-event");
|
|
11
12
|
/*
|
|
12
13
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
13
14
|
* SPDX-License-Identifier: Apache-2.0
|
|
@@ -26,18 +27,17 @@ const sentIds = new Set();
|
|
|
26
27
|
function activateFeatures(flags, agentIdentifier) {
|
|
27
28
|
const sharedEE = _contextualEe.ee.get(agentIdentifier);
|
|
28
29
|
if (!(flags && typeof flags === 'object')) return;
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
30
|
+
if (sentIds.has(agentIdentifier)) return;
|
|
31
|
+
Object.entries(flags).forEach(_ref => {
|
|
32
|
+
let [flag, num] = _ref;
|
|
33
|
+
if (bucketMap[flag]) {
|
|
34
|
+
bucketMap[flag].forEach(feat => {
|
|
35
|
+
if (!num) (0, _handle.handle)('block-' + flag, [], undefined, feat, sharedEE);else (0, _handle.handle)('feat-' + flag, [], undefined, feat, sharedEE);
|
|
36
|
+
(0, _handle.handle)('rumresp-' + flag, [Boolean(num)], undefined, feat, sharedEE); // this is a duplicate of feat-/block- but makes awaiting for 1 event easier than 2
|
|
37
|
+
});
|
|
38
|
+
} else if (num) (0, _handle.handle)('feat-' + flag, [], undefined, undefined, sharedEE); // not sure what other flags are overlooked, but there's a test for ones not in the map --
|
|
39
|
+
activatedFeatures[flag] = Boolean(num);
|
|
40
|
+
});
|
|
41
41
|
|
|
42
42
|
// Let the features waiting on their respective flags know that RUM response was received and that any missing flags are interpreted as bad entitlement / "off".
|
|
43
43
|
// Hence, those features will not be hanging forever if their flags aren't included in the response.
|
|
@@ -48,6 +48,11 @@ function activateFeatures(flags, agentIdentifier) {
|
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
sentIds.add(agentIdentifier);
|
|
51
|
+
|
|
52
|
+
// let any window level subscribers know that the agent is running
|
|
53
|
+
(0, _globalEvent.dispatchGlobalEvent)({
|
|
54
|
+
loaded: true
|
|
55
|
+
});
|
|
51
56
|
}
|
|
52
57
|
const activatedFeatures = {};
|
|
53
58
|
exports.activatedFeatures = activatedFeatures;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.checkState = checkState;
|
|
6
7
|
exports.onDOMContentLoaded = onDOMContentLoaded;
|
|
7
8
|
exports.onWindowLoad = onWindowLoad;
|
|
8
9
|
var _eventListenerOpts = require("../event-listener/event-listener-opts");
|
|
@@ -6,13 +6,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.NREUMinitialized = NREUMinitialized;
|
|
7
7
|
exports.addToNREUM = addToNREUM;
|
|
8
8
|
exports.defaults = void 0;
|
|
9
|
+
exports.getNREUMInitializedAgent = getNREUMInitializedAgent;
|
|
9
10
|
exports.gosCDN = gosCDN;
|
|
10
11
|
exports.gosNREUM = gosNREUM;
|
|
11
12
|
exports.gosNREUMInfo = gosNREUMInfo;
|
|
12
13
|
exports.gosNREUMInit = gosNREUMInit;
|
|
13
|
-
exports.gosNREUMInitializedAgents = gosNREUMInitializedAgents;
|
|
14
14
|
exports.gosNREUMLoaderConfig = gosNREUMLoaderConfig;
|
|
15
15
|
exports.gosNREUMOriginals = gosNREUMOriginals;
|
|
16
|
+
exports.setNREUMInitializedAgent = setNREUMInitializedAgent;
|
|
16
17
|
var _now = require("../timing/now");
|
|
17
18
|
var _runtime = require("../constants/runtime");
|
|
18
19
|
const defaults = {
|
|
@@ -71,24 +72,24 @@ function gosNREUMOriginals() {
|
|
|
71
72
|
}
|
|
72
73
|
return nr;
|
|
73
74
|
}
|
|
74
|
-
function
|
|
75
|
+
function setNREUMInitializedAgent(id, newAgentInstance) {
|
|
75
76
|
let nr = gosNREUM();
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
ms: (0, _now.now)(),
|
|
81
|
-
date: new Date()
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
nr.initializedAgents = {
|
|
85
|
-
...externallySupplied,
|
|
86
|
-
[id]: {
|
|
87
|
-
...curr,
|
|
88
|
-
[target]: obj
|
|
89
|
-
}
|
|
77
|
+
nr.initializedAgents ??= {};
|
|
78
|
+
newAgentInstance.initializedAt = {
|
|
79
|
+
ms: (0, _now.now)(),
|
|
80
|
+
date: new Date()
|
|
90
81
|
};
|
|
91
|
-
|
|
82
|
+
nr.initializedAgents[id] = newAgentInstance;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the agent instance under the associated identifier on the global newrelic object.
|
|
87
|
+
* @see setNREUMInitializedAgents
|
|
88
|
+
* @returns Existing agent instance under newrelic.initializedAgent[id], or undefined if it does not exist.
|
|
89
|
+
*/
|
|
90
|
+
function getNREUMInitializedAgent(id) {
|
|
91
|
+
let nr = gosNREUM();
|
|
92
|
+
return nr.initializedAgents?.[id];
|
|
92
93
|
}
|
|
93
94
|
function addToNREUM(fnName, fn) {
|
|
94
95
|
let nr = gosNREUM();
|
|
@@ -69,10 +69,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
69
69
|
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
70
70
|
if (loaderType) this.storeSupportabilityMetrics("Generic/LoaderType/".concat(loaderType, "/Detected"));
|
|
71
71
|
if (distMethod) this.storeSupportabilityMetrics("Generic/DistMethod/".concat(distMethod, "/Detected"));
|
|
72
|
-
|
|
73
|
-
// frameworks on page
|
|
74
72
|
if (_runtime.isBrowserScope) {
|
|
75
73
|
this.storeSupportabilityMetrics('Generic/Runtime/Browser/Detected');
|
|
74
|
+
const nonce = document?.currentScript?.nonce;
|
|
75
|
+
if (nonce && nonce !== '') {
|
|
76
|
+
this.storeSupportabilityMetrics('Generic/Runtime/Nonce/Detected');
|
|
77
|
+
}
|
|
76
78
|
|
|
77
79
|
// These SMs are used by the AppExp team
|
|
78
80
|
(0, _load.onDOMContentLoaded)(() => {
|
|
@@ -19,6 +19,7 @@ var _constants2 = require("../../metrics/constants");
|
|
|
19
19
|
var _handle = require("../../../common/event-emitter/handle");
|
|
20
20
|
var _features = require("../../../loaders/features/features");
|
|
21
21
|
var _env = require("../../../common/constants/env.npm");
|
|
22
|
+
var _now = require("../../../common/timing/now");
|
|
22
23
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
23
24
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } /*
|
|
24
25
|
* Copyright 2023 New Relic Corporation. All rights reserved.
|
|
@@ -70,8 +71,10 @@ const MAX_PAYLOAD_SIZE = 1000000;
|
|
|
70
71
|
/** Unloading caps around 64kb */
|
|
71
72
|
exports.MAX_PAYLOAD_SIZE = MAX_PAYLOAD_SIZE;
|
|
72
73
|
const IDEAL_PAYLOAD_SIZE = 64000;
|
|
73
|
-
/**
|
|
74
|
+
/** Reserved room for query param attrs */
|
|
74
75
|
exports.IDEAL_PAYLOAD_SIZE = IDEAL_PAYLOAD_SIZE;
|
|
76
|
+
const QUERY_PARAM_PADDING = 5000;
|
|
77
|
+
/** Interval between forcing new full snapshots -- 15 seconds in error mode (x2), 5 minutes in full mode */
|
|
75
78
|
const CHECKOUT_MS = {
|
|
76
79
|
[_sessionEntity.MODE.ERROR]: 15000,
|
|
77
80
|
[_sessionEntity.MODE.FULL]: 300000,
|
|
@@ -120,6 +123,9 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
120
123
|
|
|
121
124
|
/** Hold on to the last meta node, so that it can be re-inserted if the meta and snapshot nodes are broken up due to harvesting */
|
|
122
125
|
this.lastMeta = undefined;
|
|
126
|
+
|
|
127
|
+
/** set by BCS response */
|
|
128
|
+
this.entitled = false;
|
|
123
129
|
const shouldSetup = (0, _config.getConfigurationValue)(agentIdentifier, 'privacy.cookies_enabled') === true && (0, _config.getConfigurationValue)(agentIdentifier, 'session_trace.enabled') === true;
|
|
124
130
|
|
|
125
131
|
/** The method to stop recording. This defaults to a noop, but is overwritten once the recording library is imported and initialized */
|
|
@@ -157,6 +163,18 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
157
163
|
getPayload: this.prepareHarvest.bind(this),
|
|
158
164
|
raw: true
|
|
159
165
|
}, this);
|
|
166
|
+
(0, _registerHandler.registerHandler)('recordReplay', () => {
|
|
167
|
+
// if it has aborted or BCS returned bad entitlements, do not allow
|
|
168
|
+
if (this.blocked || !this.entitled) return;
|
|
169
|
+
// if it isnt already (fully) initialized... initialize it
|
|
170
|
+
if (!recorder) this.initializeRecording(false, true, true);
|
|
171
|
+
// its been initialized and imported the recorder but its not recording (mode === off || error)
|
|
172
|
+
else if (this.mode !== _sessionEntity.MODE.FULL) this.switchToFull();
|
|
173
|
+
// if it gets all the way to here, that means a full session is already recording... do nothing
|
|
174
|
+
}, this.featureName, this.ee);
|
|
175
|
+
(0, _registerHandler.registerHandler)('pauseReplay', () => {
|
|
176
|
+
this.forceStop(this.mode !== _sessionEntity.MODE.ERROR);
|
|
177
|
+
}, this.featureName, this.ee);
|
|
160
178
|
|
|
161
179
|
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
162
180
|
// This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
|
|
@@ -165,37 +183,42 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
165
183
|
this.errorNoticed = true;
|
|
166
184
|
// run once
|
|
167
185
|
if (this.mode === _sessionEntity.MODE.ERROR && _runtime.globalScope?.document.visibilityState === 'visible') {
|
|
168
|
-
this.
|
|
169
|
-
// if the error was noticed AFTER the recorder was already imported....
|
|
170
|
-
if (recorder && this.initialized) {
|
|
171
|
-
this.stopRecording();
|
|
172
|
-
this.startRecording();
|
|
173
|
-
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
174
|
-
this.syncWithSessionManager({
|
|
175
|
-
sessionReplayMode: this.mode
|
|
176
|
-
});
|
|
177
|
-
}
|
|
186
|
+
this.switchToFull();
|
|
178
187
|
}
|
|
179
188
|
}, this.featureName, this.ee);
|
|
180
189
|
this.waitForFlags(['sr']).then(_ref => {
|
|
181
190
|
let [flagOn] = _ref;
|
|
182
|
-
|
|
191
|
+
this.entitled = flagOn;
|
|
192
|
+
this.initializeRecording(Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.error_sampling_rate'), Math.random() * 100 < (0, _config.getConfigurationValue)(this.agentIdentifier, 'session_replay.sampling_rate'));
|
|
183
193
|
}).then(() => _sharedChannel.sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
|
|
184
194
|
|
|
185
195
|
this.drain();
|
|
186
196
|
}
|
|
187
197
|
}
|
|
198
|
+
switchToFull() {
|
|
199
|
+
this.mode = _sessionEntity.MODE.FULL;
|
|
200
|
+
// if the error was noticed AFTER the recorder was already imported....
|
|
201
|
+
if (recorder && this.initialized) {
|
|
202
|
+
this.stopRecording();
|
|
203
|
+
this.startRecording();
|
|
204
|
+
this.scheduler.startTimer(this.harvestTimeSeconds);
|
|
205
|
+
this.syncWithSessionManager({
|
|
206
|
+
sessionReplayMode: this.mode
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
188
210
|
|
|
189
211
|
/**
|
|
190
212
|
* Evaluate entitlements and sampling before starting feature mechanics, importing and configuring recording library, and setting storage state
|
|
191
213
|
* @param {boolean} entitlements - the true/false state of the "sr" flag from RUM response
|
|
192
214
|
* @param {boolean} errorSample - the true/false state of the error sampling decision
|
|
193
215
|
* @param {boolean} fullSample - the true/false state of the full sampling decision
|
|
216
|
+
* @param {boolean} ignoreSession - whether to force the method to ignore the session state and use just the sample flags
|
|
194
217
|
* @returns {void}
|
|
195
218
|
*/
|
|
196
|
-
async initializeRecording(
|
|
219
|
+
async initializeRecording(errorSample, fullSample, ignoreSession) {
|
|
197
220
|
this.initialized = true;
|
|
198
|
-
if (!
|
|
221
|
+
if (!this.entitled || this.recording) return;
|
|
199
222
|
const {
|
|
200
223
|
session
|
|
201
224
|
} = (0, _config.getRuntime)(this.agentIdentifier);
|
|
@@ -205,7 +228,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
205
228
|
// we are not actively recording SR... DO NOT import or run the recording library
|
|
206
229
|
// session replay samples can only be decided on the first load of a session
|
|
207
230
|
// session replays can continue if already in progress
|
|
208
|
-
if (!session.isNew) {
|
|
231
|
+
if (!session.isNew && !ignoreSession) {
|
|
209
232
|
// inherit the mode of the existing session
|
|
210
233
|
this.mode = session.state.sessionReplayMode;
|
|
211
234
|
} else {
|
|
@@ -277,6 +300,7 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
277
300
|
getHarvestContents() {
|
|
278
301
|
const agentRuntime = (0, _config.getRuntime)(this.agentIdentifier);
|
|
279
302
|
const info = (0, _config.getInfo)(this.agentIdentifier);
|
|
303
|
+
const endUserId = info.jsAttributes?.['enduser.id'];
|
|
280
304
|
if (this.backloggedEvents.length) this.events = [...this.backloggedEvents, ...this.events];
|
|
281
305
|
|
|
282
306
|
// do not let the first node be a full snapshot node, since this NEEDS to be preceded by a meta node
|
|
@@ -296,10 +320,12 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
296
320
|
this.events = this.events.slice(0, this.events.length - 1);
|
|
297
321
|
this.hasMeta = !!this.events.find(x => x.type === RRWEB_EVENT_TYPES.Meta);
|
|
298
322
|
}
|
|
323
|
+
const agentOffset = (0, _config.getRuntime)(this.agentIdentifier).offset;
|
|
324
|
+
const relativeNow = (0, _now.now)();
|
|
299
325
|
const firstEventTimestamp = this.events[0]?.timestamp; // from rrweb node
|
|
300
326
|
const lastEventTimestamp = this.events[this.events.length - 1]?.timestamp; // from rrweb node
|
|
301
327
|
const firstTimestamp = firstEventTimestamp || this.cycleTimestamp;
|
|
302
|
-
const lastTimestamp = lastEventTimestamp ||
|
|
328
|
+
const lastTimestamp = lastEventTimestamp || agentOffset + relativeNow;
|
|
303
329
|
return {
|
|
304
330
|
qs: {
|
|
305
331
|
browser_monitoring_key: info.licenseKey,
|
|
@@ -307,22 +333,32 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
307
333
|
app_id: info.applicationID,
|
|
308
334
|
protocol_version: '0',
|
|
309
335
|
attributes: (0, _encode.obj)({
|
|
336
|
+
// this section of attributes must be controllable and stay below the query param padding limit -- see QUERY_PARAM_PADDING
|
|
337
|
+
// if not, data could be lost to truncation at time of sending, potentially breaking parsing / API behavior in NR1
|
|
310
338
|
...(this.shouldCompress && {
|
|
311
339
|
content_encoding: 'gzip'
|
|
312
340
|
}),
|
|
313
341
|
'replay.firstTimestamp': firstTimestamp,
|
|
342
|
+
'replay.firstTimestampOffset': firstTimestamp - agentOffset,
|
|
314
343
|
'replay.lastTimestamp': lastTimestamp,
|
|
315
344
|
'replay.durationMs': lastTimestamp - firstTimestamp,
|
|
316
345
|
'replay.nodes': this.events.length,
|
|
346
|
+
'session.durationMs': agentRuntime.session.getDuration(),
|
|
317
347
|
agentVersion: agentRuntime.version,
|
|
318
348
|
session: agentRuntime.session.state.value,
|
|
349
|
+
rst: relativeNow,
|
|
319
350
|
hasMeta: this.hasMeta,
|
|
320
351
|
hasSnapshot: this.hasSnapshot,
|
|
321
352
|
hasError: this.hasError,
|
|
322
353
|
isFirstChunk: agentRuntime.session.state.sessionReplaySentFirstChunk === false,
|
|
323
354
|
decompressedBytes: this.payloadBytesEstimation,
|
|
324
|
-
'
|
|
325
|
-
|
|
355
|
+
'rrweb.version': _env.RRWEB_VERSION,
|
|
356
|
+
// customer-defined data should go last so that if it exceeds the query param padding limit it will be truncated instead of important attrs
|
|
357
|
+
...(endUserId && {
|
|
358
|
+
'enduser.id': endUserId
|
|
359
|
+
})
|
|
360
|
+
// The Query Param is being arbitrarily limited in length here. It is also applied when estimating the size of the payload in getPayloadSize()
|
|
361
|
+
}, QUERY_PARAM_PADDING).substring(1) // remove the leading '&'
|
|
326
362
|
},
|
|
327
363
|
|
|
328
364
|
body: this.events
|
|
@@ -444,8 +480,22 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
444
480
|
/** Estimate the payload size */
|
|
445
481
|
getPayloadSize() {
|
|
446
482
|
let newBytes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
|
|
447
|
-
// the
|
|
448
|
-
return this.estimateCompression(this.payloadBytesEstimation + newBytes) +
|
|
483
|
+
// the query param padding constant gives us some padding for the other metadata to be safely injected
|
|
484
|
+
return this.estimateCompression(this.payloadBytesEstimation + newBytes) + QUERY_PARAM_PADDING;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Forces the agent into OFF mode so that changing tabs or navigating
|
|
489
|
+
* does not restart the recording. This is used when the customer calls
|
|
490
|
+
* the stopRecording API.
|
|
491
|
+
*/
|
|
492
|
+
forceStop(forceHarvest) {
|
|
493
|
+
if (forceHarvest) this.scheduler.runHarvest();
|
|
494
|
+
this.mode = _sessionEntity.MODE.OFF;
|
|
495
|
+
this.stopRecording();
|
|
496
|
+
this.syncWithSessionManager({
|
|
497
|
+
sessionReplayMode: this.mode
|
|
498
|
+
});
|
|
449
499
|
}
|
|
450
500
|
|
|
451
501
|
/** Abort the feature, once aborted it will not resume */
|
|
@@ -89,30 +89,45 @@ class Aggregate extends _aggregateBase.AggregateBase {
|
|
|
89
89
|
break;
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
|
+
let seenAnError = false;
|
|
93
|
+
let mostRecentModeKnown;
|
|
94
|
+
this.ee.on(_sessionEntity.SESSION_EVENTS.UPDATE, (eventType, sessionState) => {
|
|
95
|
+
// this will only have an effect if ST is NOT already in full mode
|
|
96
|
+
if (sessionState.sessionReplayMode === _sessionEntity.MODE.FULL) switchToFull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* The goal of switchToFull is to take external input to trigger a change from off or error to full.
|
|
101
|
+
* It will have no effect if already running in full mode.
|
|
102
|
+
* "external" input in this case means errors thrown on the page or session replay itself being triggered to run in full mode by the API, which updates the session entity.
|
|
103
|
+
*/
|
|
104
|
+
const switchToFull = () => {
|
|
105
|
+
if (this.agentRuntime?.session?.state?.sessionReplayMode !== _sessionEntity.MODE.FULL) return;
|
|
106
|
+
if (mostRecentModeKnown !== _sessionEntity.MODE.FULL) {
|
|
107
|
+
const prevMode = mostRecentModeKnown;
|
|
108
|
+
mostRecentModeKnown = _sessionEntity.MODE.FULL;
|
|
109
|
+
sessionEntity.write({
|
|
110
|
+
sessionTraceMode: mostRecentModeKnown
|
|
111
|
+
});
|
|
112
|
+
this.isStandalone = false;
|
|
113
|
+
if (prevMode === _sessionEntity.MODE.ERROR && this.#scheduler) {
|
|
114
|
+
this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // up until now, Trace would've been just buffering nodes up to max, which needs to be trimmed to last X seconds
|
|
115
|
+
this.#scheduler.runHarvest({
|
|
116
|
+
needResponse: true
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
controlTraceOp(_sessionEntity.MODE.FULL);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
92
123
|
if (!sessionEntity) {
|
|
93
124
|
// Since session manager isn't around, do the old Trace behavior of waiting for RUM response to decide feature activation.
|
|
94
125
|
this.isStandalone = true;
|
|
95
126
|
(0, _registerHandler.registerHandler)('rumresp-stn', on => controlTraceOp(on), this.featureName, this.ee);
|
|
96
127
|
} else {
|
|
97
|
-
let seenAnError = false;
|
|
98
|
-
let mostRecentModeKnown;
|
|
99
128
|
(0, _registerHandler.registerHandler)('errorAgg', () => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
seenAnError = true;
|
|
103
|
-
/* If this cb executes before Trace has started, then no further action needed. But if...
|
|
104
|
-
- startTracing already ran under ERROR mode, then it will NOT have kicked off the harvest-scheduler so that needs to be done & switch mode.
|
|
105
|
-
- startTracing never ran because mode is OFF or Replay aborted or Traced turned off elsewhere OR trace already in FULL, then this should do nothing. */
|
|
106
|
-
if (sessionEntity.state.sessionTraceMode === _sessionEntity.MODE.ERROR && this.#scheduler) {
|
|
107
|
-
sessionEntity.write({
|
|
108
|
-
sessionTraceMode: mostRecentModeKnown = _sessionEntity.MODE.FULL
|
|
109
|
-
});
|
|
110
|
-
this.trimSTNs(ERROR_MODE_SECONDS_WINDOW); // up until now, Trace would've been just buffering nodes up to max, which needs to be trimmed to last X seconds
|
|
111
|
-
this.#scheduler.runHarvest({
|
|
112
|
-
needResponse: true
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
}
|
|
129
|
+
seenAnError = true;
|
|
130
|
+
switchToFull();
|
|
116
131
|
}, this.featureName, this.ee);
|
|
117
132
|
const stopTracePerm = () => {
|
|
118
133
|
if (sessionEntity.state.sessionTraceMode !== _sessionEntity.MODE.OFF) sessionEntity.write({
|