@newrelic/browser-agent 1.252.1 → 1.253.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 +10 -0
- package/README.md +1 -1
- package/dist/cjs/cdn/experimental.js +6 -2
- package/dist/cjs/cdn/spa.js +5 -3
- package/dist/cjs/common/aggregate/aggregator.js +1 -8
- package/dist/cjs/common/config/state/init.js +7 -0
- package/dist/cjs/common/constants/env.cdn.js +1 -1
- package/dist/cjs/common/constants/env.npm.js +1 -1
- package/dist/cjs/common/context/observation-context-manager.js +56 -0
- package/dist/cjs/common/event-emitter/contextual-ee.js +12 -9
- package/dist/cjs/common/session/constants.js +2 -1
- package/dist/cjs/common/timing/nav-timing.js +8 -3
- package/dist/cjs/common/timing/now.js +1 -1
- package/dist/cjs/common/util/feature-flags.js +1 -1
- package/dist/cjs/common/wrap/index.js +0 -7
- package/dist/cjs/common/wrap/wrap-events.js +2 -2
- package/dist/cjs/common/wrap/wrap-fetch.js +2 -1
- package/dist/cjs/common/wrap/wrap-function.js +5 -7
- package/dist/cjs/common/wrap/wrap-promise.js +2 -1
- package/dist/cjs/features/ajax/aggregate/index.js +34 -16
- package/dist/cjs/features/jserrors/aggregate/index.js +77 -66
- package/dist/cjs/features/page_view_event/aggregate/index.js +1 -1
- package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +1 -0
- package/dist/cjs/features/session_replay/aggregate/index.js +96 -94
- package/dist/cjs/features/session_replay/constants.js +5 -1
- package/dist/cjs/features/session_replay/instrument/index.js +24 -8
- package/dist/cjs/features/session_replay/shared/utils.js +26 -0
- package/dist/cjs/features/soft_navigations/aggregate/ajax-node.js +50 -0
- package/dist/cjs/features/soft_navigations/aggregate/bel-node.js +29 -0
- package/dist/cjs/features/soft_navigations/aggregate/index.js +263 -0
- package/dist/cjs/features/soft_navigations/aggregate/initial-page-load-interaction.js +62 -0
- package/dist/cjs/features/soft_navigations/aggregate/interaction.js +146 -0
- package/dist/cjs/features/soft_navigations/constants.js +31 -0
- package/dist/cjs/features/soft_navigations/index.js +12 -0
- package/dist/cjs/features/soft_navigations/instrument/index.js +79 -0
- package/dist/cjs/features/spa/aggregate/index.js +4 -4
- package/dist/cjs/features/utils/agent-session.js +2 -1
- package/dist/cjs/features/utils/instrument-base.js +6 -9
- package/dist/cjs/features/utils/lazy-feature-loader.js +2 -0
- package/dist/cjs/loaders/agent-base.js +18 -3
- package/dist/cjs/loaders/agent.js +15 -18
- package/dist/cjs/loaders/api/api-methods.js +2 -1
- package/dist/cjs/loaders/api/api.js +14 -11
- package/dist/cjs/loaders/configure/configure.js +4 -1
- package/dist/cjs/loaders/features/enabled-features.js +1 -1
- package/dist/cjs/loaders/features/features.js +3 -1
- package/dist/esm/cdn/experimental.js +5 -2
- package/dist/esm/cdn/spa.js +3 -1
- package/dist/esm/common/aggregate/aggregator.js +1 -8
- package/dist/esm/common/config/state/init.js +7 -0
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/context/observation-context-manager.js +49 -0
- package/dist/esm/common/event-emitter/contextual-ee.js +12 -9
- package/dist/esm/common/session/constants.js +1 -0
- package/dist/esm/common/timing/nav-timing.js +8 -3
- package/dist/esm/common/timing/now.js +1 -1
- package/dist/esm/common/util/feature-flags.js +1 -1
- package/dist/esm/common/wrap/index.js +1 -2
- package/dist/esm/common/wrap/wrap-events.js +3 -3
- package/dist/esm/common/wrap/wrap-fetch.js +3 -2
- package/dist/esm/common/wrap/wrap-function.js +4 -5
- package/dist/esm/common/wrap/wrap-promise.js +3 -2
- package/dist/esm/features/ajax/aggregate/index.js +36 -18
- package/dist/esm/features/jserrors/aggregate/index.js +77 -66
- package/dist/esm/features/page_view_event/aggregate/index.js +1 -1
- package/dist/esm/features/page_view_event/aggregate/initialized-features.js +1 -0
- package/dist/esm/features/session_replay/aggregate/index.js +97 -95
- package/dist/esm/features/session_replay/constants.js +4 -0
- package/dist/esm/features/session_replay/instrument/index.js +25 -9
- package/dist/esm/features/session_replay/shared/utils.js +17 -0
- package/dist/esm/features/soft_navigations/aggregate/ajax-node.js +43 -0
- package/dist/esm/features/soft_navigations/aggregate/bel-node.js +22 -0
- package/dist/esm/features/soft_navigations/aggregate/index.js +256 -0
- package/dist/esm/features/soft_navigations/aggregate/initial-page-load-interaction.js +55 -0
- package/dist/esm/features/soft_navigations/aggregate/interaction.js +140 -0
- package/dist/esm/features/soft_navigations/constants.js +25 -0
- package/dist/esm/features/soft_navigations/index.js +1 -0
- package/dist/esm/features/soft_navigations/instrument/index.js +73 -0
- package/dist/esm/features/spa/aggregate/index.js +4 -4
- package/dist/esm/features/utils/agent-session.js +2 -1
- package/dist/esm/features/utils/instrument-base.js +7 -10
- package/dist/esm/features/utils/lazy-feature-loader.js +2 -0
- package/dist/esm/loaders/agent-base.js +18 -3
- package/dist/esm/loaders/agent.js +15 -18
- package/dist/esm/loaders/api/api-methods.js +2 -1
- package/dist/esm/loaders/api/api.js +14 -11
- package/dist/esm/loaders/configure/configure.js +4 -1
- package/dist/esm/loaders/features/enabled-features.js +1 -1
- package/dist/esm/loaders/features/features.js +3 -1
- package/dist/types/common/aggregate/aggregator.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/context/event-context.d.ts.map +1 -0
- package/dist/types/common/context/observation-context-manager.d.ts +28 -0
- package/dist/types/common/context/observation-context-manager.d.ts.map +1 -0
- package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -2
- package/dist/types/common/event-emitter/contextual-ee.d.ts.map +1 -1
- package/dist/types/common/session/constants.d.ts +1 -0
- package/dist/types/common/session/constants.d.ts.map +1 -1
- package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
- package/dist/types/common/wrap/index.d.ts +1 -2
- package/dist/types/common/wrap/index.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-function.d.ts +0 -1
- package/dist/types/common/wrap/wrap-function.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +4 -3
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts +1 -1
- package/dist/types/features/session_replay/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/constants.d.ts +4 -0
- package/dist/types/features/session_replay/constants.d.ts.map +1 -1
- package/dist/types/features/session_replay/instrument/index.d.ts.map +1 -1
- package/dist/types/features/session_replay/shared/utils.d.ts +4 -0
- package/dist/types/features/session_replay/shared/utils.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts +19 -0
- package/dist/types/features/soft_navigations/aggregate/ajax-node.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts +16 -0
- package/dist/types/features/soft_navigations/aggregate/bel-node.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/aggregate/index.d.ts +36 -0
- package/dist/types/features/soft_navigations/aggregate/index.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts +12 -0
- package/dist/types/features/soft_navigations/aggregate/initial-page-load-interaction.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts +50 -0
- package/dist/types/features/soft_navigations/aggregate/interaction.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/constants.d.ts +20 -0
- package/dist/types/features/soft_navigations/constants.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/index.d.ts +2 -0
- package/dist/types/features/soft_navigations/index.d.ts.map +1 -0
- package/dist/types/features/soft_navigations/instrument/index.d.ts +7 -0
- package/dist/types/features/soft_navigations/instrument/index.d.ts.map +1 -0
- package/dist/types/features/spa/aggregate/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 +1 -7
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/features/utils/lazy-feature-loader.d.ts.map +1 -1
- package/dist/types/loaders/agent-base.d.ts +5 -1
- package/dist/types/loaders/agent-base.d.ts.map +1 -1
- package/dist/types/loaders/agent.d.ts +2 -2
- package/dist/types/loaders/agent.d.ts.map +1 -1
- package/dist/types/loaders/api/api-methods.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts +3 -5
- 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/features/features.d.ts +1 -0
- package/dist/types/loaders/features/features.d.ts.map +1 -1
- package/dist/types/loaders/micro-agent.d.ts +0 -1
- package/dist/types/loaders/micro-agent.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cdn/experimental.js +4 -2
- package/src/cdn/spa.js +3 -1
- package/src/common/aggregate/aggregator.js +2 -11
- package/src/common/config/state/init.js +3 -1
- package/src/common/context/observation-context-manager.js +55 -0
- package/src/common/event-emitter/contextual-ee.js +20 -10
- package/src/common/session/constants.js +1 -0
- package/src/common/timing/nav-timing.js +7 -3
- package/src/common/timing/now.js +1 -1
- package/src/common/util/feature-flags.js +1 -1
- package/src/common/wrap/index.js +1 -2
- package/src/common/wrap/wrap-events.js +3 -3
- package/src/common/wrap/wrap-fetch.js +3 -2
- package/src/common/wrap/wrap-function.js +4 -6
- package/src/common/wrap/wrap-promise.js +3 -2
- package/src/features/ajax/aggregate/index.js +36 -18
- package/src/features/jserrors/aggregate/index.js +70 -73
- package/src/features/page_view_event/aggregate/index.js +1 -1
- package/src/features/page_view_event/aggregate/initialized-features.js +1 -0
- package/src/features/session_replay/aggregate/index.js +92 -95
- package/src/features/session_replay/constants.js +5 -0
- package/src/features/session_replay/instrument/index.js +24 -9
- package/src/features/session_replay/shared/utils.js +19 -0
- package/src/features/soft_navigations/aggregate/ajax-node.js +57 -0
- package/src/features/soft_navigations/aggregate/bel-node.js +26 -0
- package/src/features/soft_navigations/aggregate/index.js +254 -0
- package/src/features/soft_navigations/aggregate/initial-page-load-interaction.js +53 -0
- package/src/features/soft_navigations/aggregate/interaction.js +159 -0
- package/src/features/soft_navigations/constants.js +29 -0
- package/src/features/soft_navigations/index.js +1 -0
- package/src/features/soft_navigations/instrument/index.js +67 -0
- package/src/features/spa/aggregate/index.js +5 -4
- package/src/features/utils/agent-session.js +2 -1
- package/src/features/utils/instrument-base.js +7 -10
- package/src/features/utils/lazy-feature-loader.js +2 -0
- package/src/loaders/agent-base.js +18 -3
- package/src/loaders/agent.js +18 -17
- package/src/loaders/api/api-methods.js +4 -1
- package/src/loaders/api/api.js +14 -12
- package/src/loaders/configure/configure.js +4 -1
- package/src/loaders/features/enabled-features.js +1 -1
- package/src/loaders/features/features.js +3 -1
- package/dist/cjs/common/wrap/wrap-raf.js +0 -55
- package/dist/esm/common/wrap/wrap-raf.js +0 -48
- package/dist/types/common/event-emitter/event-context.d.ts.map +0 -1
- package/dist/types/common/wrap/wrap-raf.d.ts +0 -16
- package/dist/types/common/wrap/wrap-raf.d.ts.map +0 -1
- package/src/common/wrap/wrap-raf.js +0 -52
- /package/dist/cjs/common/{event-emitter → context}/event-context.js +0 -0
- /package/dist/esm/common/{event-emitter → context}/event-context.js +0 -0
- /package/dist/types/common/{event-emitter → context}/event-context.d.ts +0 -0
- /package/src/common/{event-emitter → context}/event-context.js +0 -0
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* @file Wraps `fetch` and related methods for instrumentation.
|
|
7
7
|
* This module is used by: ajax, spa.
|
|
8
8
|
*/
|
|
9
|
-
import { ee as baseEE
|
|
9
|
+
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
10
10
|
import { globalScope } from '../constants/runtime';
|
|
11
|
+
import { ObservationContextManager } from '../context/observation-context-manager';
|
|
11
12
|
var prefix = 'fetch-';
|
|
12
13
|
var bodyPrefix = prefix + 'body-';
|
|
13
14
|
var bodyMethods = ['arrayBuffer', 'blob', 'json', 'text', 'formData'];
|
|
@@ -70,7 +71,7 @@ export function wrapFetch(sharedEE) {
|
|
|
70
71
|
// we are wrapping args in an array so we can preserve the reference
|
|
71
72
|
ee.emit(prefix + 'before-start', [args], ctx);
|
|
72
73
|
var dtPayload;
|
|
73
|
-
if (ctx[contextId] && ctx[contextId].dt) dtPayload = ctx[contextId].dt;
|
|
74
|
+
if (ctx[ObservationContextManager.contextId] && ctx[ObservationContextManager.contextId].dt) dtPayload = ctx[ObservationContextManager.contextId].dt;
|
|
74
75
|
var origPromiseFromFetch = fn.apply(this, args);
|
|
75
76
|
ee.emit(prefix + 'start', [args, dtPayload], origPromiseFromFetch);
|
|
76
77
|
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { ee } from '../event-emitter/contextual-ee';
|
|
10
|
-
import {
|
|
11
|
-
export const flag = "nr@original:".concat(bundleId);
|
|
10
|
+
import { ObservationContextManager } from '../context/observation-context-manager';
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* A convenience alias of `hasOwnProperty`.
|
|
@@ -40,7 +39,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
40
39
|
* As a property on a wrapped function, contains the original function.
|
|
41
40
|
* @type {string}
|
|
42
41
|
*/
|
|
43
|
-
wrapFn.flag =
|
|
42
|
+
wrapFn.flag = ObservationContextManager.contextOriginalId;
|
|
44
43
|
return wrapFn;
|
|
45
44
|
|
|
46
45
|
/**
|
|
@@ -56,7 +55,7 @@ export function createWrapperWithEmitter(emitter, always) {
|
|
|
56
55
|
// Unless fn is both wrappable and unwrapped, return it unchanged.
|
|
57
56
|
if (notWrappable(fn)) return fn;
|
|
58
57
|
if (!prefix) prefix = '';
|
|
59
|
-
nrWrapper[
|
|
58
|
+
nrWrapper[ObservationContextManager.contextOriginalId] = fn;
|
|
60
59
|
copy(fn, nrWrapper, emitter);
|
|
61
60
|
return nrWrapper;
|
|
62
61
|
|
|
@@ -210,5 +209,5 @@ function copy(from, to, emitter) {
|
|
|
210
209
|
* @returns {boolean} Whether the passed function is ineligible to be wrapped.
|
|
211
210
|
*/
|
|
212
211
|
function notWrappable(fn) {
|
|
213
|
-
return !(fn && typeof fn === 'function' && fn.apply && !fn[
|
|
212
|
+
return !(fn && typeof fn === 'function' && fn.apply && !fn[ObservationContextManager.contextOriginalId]);
|
|
214
213
|
}
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
* This module is used by: spa.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { createWrapperWithEmitter as wrapFn
|
|
10
|
+
import { createWrapperWithEmitter as wrapFn } from './wrap-function';
|
|
11
11
|
import { ee as baseEE } from '../event-emitter/contextual-ee';
|
|
12
12
|
import { globalScope } from '../constants/runtime';
|
|
13
|
+
import { ObservationContextManager } from '../context/observation-context-manager';
|
|
13
14
|
const wrapped = {};
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -118,7 +119,7 @@ export function wrapPromise(sharedEE) {
|
|
|
118
119
|
promiseEE.emit('propagate', [originalThis, true], origFnCallWithThis, false, false);
|
|
119
120
|
return origFnCallWithThis;
|
|
120
121
|
};
|
|
121
|
-
prevPromiseObj.prototype.then[
|
|
122
|
+
prevPromiseObj.prototype.then[ObservationContextManager.contextOriginalId] = prevPromiseOrigThen;
|
|
122
123
|
promiseEE.on('executor-start', function (args) {
|
|
123
124
|
args[0] = promiseWrapper(args[0], 'resolve-', this, null, false);
|
|
124
125
|
args[1] = promiseWrapper(args[1], 'resolve-', this, null, false);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright 2020 New Relic Corporation. All rights reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
|
-
import { registerHandler
|
|
5
|
+
import { registerHandler } from '../../../common/event-emitter/register-handler';
|
|
6
6
|
import { stringify } from '../../../common/util/stringify';
|
|
7
7
|
import { nullable, numeric, getAddStringContext, addCustomAttributes } from '../../../common/serialize/bel-serializer';
|
|
8
8
|
import { handle } from '../../../common/event-emitter/handle';
|
|
@@ -14,13 +14,14 @@ import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
|
14
14
|
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../metrics/constants';
|
|
15
15
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
16
16
|
import { parseGQL } from './gql';
|
|
17
|
+
import { getNREUMInitializedAgent } from '../../../common/window/nreum';
|
|
17
18
|
export class Aggregate extends AggregateBase {
|
|
18
19
|
static featureName = FEATURE_NAME;
|
|
19
20
|
constructor(agentIdentifier, aggregator) {
|
|
20
21
|
super(agentIdentifier, aggregator, FEATURE_NAME);
|
|
21
22
|
const agentInit = getConfiguration(agentIdentifier);
|
|
22
23
|
const allAjaxIsEnabled = agentInit.ajax.enabled !== false;
|
|
23
|
-
|
|
24
|
+
registerHandler('xhr', storeXhr, this.featureName, this.ee);
|
|
24
25
|
if (!allAjaxIsEnabled) {
|
|
25
26
|
this.drain();
|
|
26
27
|
return; // feature will only collect timeslice metrics & ajax trace nodes if it's not fully enabled
|
|
@@ -43,19 +44,23 @@ export class Aggregate extends AggregateBase {
|
|
|
43
44
|
spaAjaxEvents
|
|
44
45
|
};
|
|
45
46
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
delete spaAjaxEvents[interaction.id];
|
|
50
|
-
});
|
|
51
|
-
ee.on('interactionDiscarded', interaction => {
|
|
47
|
+
|
|
48
|
+
// --- v Used by old spa feature
|
|
49
|
+
ee.on('interactionDone', (interaction, wasSaved) => {
|
|
52
50
|
if (!spaAjaxEvents[interaction.id]) return;
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
if (!wasSaved) {
|
|
52
|
+
// 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
|
|
53
|
+
spaAjaxEvents[interaction.id].forEach(function (item) {
|
|
54
|
+
ajaxEvents.push(item);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
57
|
delete spaAjaxEvents[interaction.id];
|
|
58
58
|
});
|
|
59
|
+
// --- ^
|
|
60
|
+
// --- v Used by new soft nav
|
|
61
|
+
registerHandler('returnAjax', event => ajaxEvents.push(event), this.featureName, this.ee);
|
|
62
|
+
// --- ^
|
|
63
|
+
|
|
59
64
|
const scheduler = new HarvestScheduler('events', {
|
|
60
65
|
onFinished: onEventsHarvestFinished,
|
|
61
66
|
getPayload: prepareHarvest
|
|
@@ -76,16 +81,26 @@ export class Aggregate extends AggregateBase {
|
|
|
76
81
|
} else {
|
|
77
82
|
hash = stringify([params.status, params.host, params.pathname]);
|
|
78
83
|
}
|
|
84
|
+
const shouldCollect = shouldCollectEvent(params);
|
|
85
|
+
const ajaxMetricDenyListEnabled = agentInit.feature_flags?.includes('ajax_metrics_deny_list');
|
|
79
86
|
|
|
80
87
|
// store as metric
|
|
81
|
-
|
|
88
|
+
if (shouldCollect || !ajaxMetricDenyListEnabled) {
|
|
89
|
+
aggregator.store('xhr', hash, params, metrics);
|
|
90
|
+
}
|
|
82
91
|
if (!allAjaxIsEnabled) return;
|
|
83
|
-
if (!
|
|
92
|
+
if (!shouldCollect) {
|
|
84
93
|
if (params.hostname === beacon || proxyBeacon && params.hostname === proxyBeacon) {
|
|
85
94
|
// This doesn't make a distinction if the same-domain request is going to a different port or path...
|
|
86
95
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee);
|
|
96
|
+
if (ajaxMetricDenyListEnabled) {
|
|
97
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/Agent'], undefined, FEATURE_NAMES.metrics, ee);
|
|
98
|
+
}
|
|
87
99
|
} else {
|
|
88
100
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee);
|
|
101
|
+
if (ajaxMetricDenyListEnabled) {
|
|
102
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Metrics/Excluded/App'], undefined, FEATURE_NAMES.metrics, ee);
|
|
103
|
+
}
|
|
89
104
|
}
|
|
90
105
|
return;
|
|
91
106
|
}
|
|
@@ -115,10 +130,13 @@ export class Aggregate extends AggregateBase {
|
|
|
115
130
|
query: this?.parsedOrigin?.search
|
|
116
131
|
});
|
|
117
132
|
if (event.gql) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Ajax/Events/GraphQL/Bytes-Added', stringify(event.gql).length], undefined, FEATURE_NAMES.metrics, ee);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
const softNavInUse = Boolean(getNREUMInitializedAgent(agentIdentifier)?.features?.[FEATURE_NAMES.softNav]);
|
|
134
|
+
if (softNavInUse) {
|
|
135
|
+
// For newer soft nav (when running), pass the event to it for evaluation -- either part of an interaction or is given back
|
|
136
|
+
handle('ajax', [event], undefined, FEATURE_NAMES.softNav, ee);
|
|
137
|
+
} else if (this.spaNode) {
|
|
138
|
+
// For old spa (when running), if the ajax happened inside an interaction, hold it until the interaction finishes
|
|
139
|
+
const interactionId = this.spaNode.interaction.id;
|
|
122
140
|
spaAjaxEvents[interactionId] = spaAjaxEvents[interactionId] || [];
|
|
123
141
|
spaAjaxEvents[interactionId].push(event);
|
|
124
142
|
} else {
|
|
@@ -18,6 +18,7 @@ import { globalScope } from '../../../common/constants/runtime';
|
|
|
18
18
|
import { FEATURE_NAME } from '../constants';
|
|
19
19
|
import { FEATURE_NAMES } from '../../../loaders/features/features';
|
|
20
20
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
21
|
+
import { getNREUMInitializedAgent } from '../../../common/window/nreum';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* @typedef {import('./compute-stack-trace.js').StackInfo} StackInfo
|
|
@@ -32,21 +33,20 @@ export class Aggregate extends AggregateBase {
|
|
|
32
33
|
this.stackReported = {};
|
|
33
34
|
this.observedAt = {};
|
|
34
35
|
this.pageviewReported = {};
|
|
35
|
-
this.
|
|
36
|
+
this.bufferedErrorsUnderSpa = {};
|
|
36
37
|
this.currentBody = undefined;
|
|
37
38
|
this.errorOnPage = false;
|
|
38
39
|
|
|
39
40
|
// this will need to change to match whatever ee we use in the instrument
|
|
40
|
-
this.ee.on('
|
|
41
|
-
|
|
42
|
-
// this will need to change to match whatever ee we use in the instrument
|
|
43
|
-
this.ee.on('interactionDiscarded', interaction => this.onInteractionDiscarded(interaction));
|
|
41
|
+
this.ee.on('interactionDone', (interaction, wasSaved) => this.onInteractionDone(interaction, wasSaved));
|
|
44
42
|
register('err', function () {
|
|
45
43
|
return _this.storeError(...arguments);
|
|
46
44
|
}, this.featureName, this.ee);
|
|
47
45
|
register('ierr', function () {
|
|
48
46
|
return _this.storeError(...arguments);
|
|
49
47
|
}, this.featureName, this.ee);
|
|
48
|
+
register('softNavFlush', (interactionId, wasFinished, softNavAttrs) => this.onSoftNavNotification(interactionId, wasFinished, softNavAttrs), this.featureName, this.ee); // when an ixn is done or cancelled
|
|
49
|
+
|
|
50
50
|
const harvestTimeSeconds = getConfigurationValue(this.agentIdentifier, 'jserrors.harvestTimeSeconds') || 10;
|
|
51
51
|
const scheduler = new HarvestScheduler('jserrors', {
|
|
52
52
|
onFinished: function () {
|
|
@@ -188,81 +188,92 @@ export class Aggregate extends AggregateBase {
|
|
|
188
188
|
time
|
|
189
189
|
};
|
|
190
190
|
|
|
191
|
-
//
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
handle('errorAgg',
|
|
195
|
-
handle('errorAgg', msg, undefined, FEATURE_NAMES.spa, this.ee);
|
|
196
|
-
handle('errorAgg', msg, undefined, FEATURE_NAMES.sessionReplay, this.ee);
|
|
197
|
-
|
|
191
|
+
// Trace sends the error in its payload, and both trace & replay simply listens for any error to occur.
|
|
192
|
+
const jsErrorEvent = [type, bucketHash, params, newMetrics, customAttributes];
|
|
193
|
+
handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionTrace, this.ee);
|
|
194
|
+
handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.sessionReplay, this.ee);
|
|
198
195
|
// still send EE events for other features such as above, but stop this one from aggregating internal data
|
|
199
196
|
if (this.blocked) return;
|
|
200
|
-
|
|
201
|
-
if
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
197
|
+
const softNavInUse = Boolean(getNREUMInitializedAgent(this.agentIdentifier)?.features[FEATURE_NAMES.softNav]);
|
|
198
|
+
// Note: the following are subject to potential race cond wherein if the other feature aren't fully initialized, it'll be treated as there being no associated interaction.
|
|
199
|
+
// They each will also tack on their respective properties to the params object as part of the decision flow.
|
|
200
|
+
if (softNavInUse) handle('jserror', [params, time], undefined, FEATURE_NAMES.softNav, this.ee);else handle('errorAgg', jsErrorEvent, undefined, FEATURE_NAMES.spa, this.ee);
|
|
201
|
+
if (params.browserInteractionId && !params._softNavFinished) {
|
|
202
|
+
// hold onto the error until the in-progress interaction is done, eithered saved or discarded
|
|
203
|
+
this.bufferedErrorsUnderSpa[params.browserInteractionId] ??= [];
|
|
204
|
+
this.bufferedErrorsUnderSpa[params.browserInteractionId].push(jsErrorEvent);
|
|
205
|
+
} else if (params._interactionId != null) {
|
|
206
|
+
// same as above, except tailored for the way old spa does it
|
|
207
|
+
this.bufferedErrorsUnderSpa[params._interactionId] = this.bufferedErrorsUnderSpa[params._interactionId] || [];
|
|
208
|
+
this.bufferedErrorsUnderSpa[params._interactionId].push(jsErrorEvent);
|
|
205
209
|
} else {
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if (customAttributes) {
|
|
210
|
-
mapOwn(customAttributes, setCustom);
|
|
211
|
-
}
|
|
212
|
-
var jsAttributesHash = stringHashCode(stringify(customParams));
|
|
213
|
-
var aggregateHash = bucketHash + ':' + jsAttributesHash;
|
|
214
|
-
this.aggregator.store(type, aggregateHash, params, newMetrics, customParams);
|
|
210
|
+
// Either there is no interaction (then all these params properties will be undefined) OR there's a related soft navigation that's already completed.
|
|
211
|
+
// The old spa does not look up completed interactions at all, so there's no need to consider it.
|
|
212
|
+
this.#storeJserrorForHarvest(jsErrorEvent, params.browserInteractionId !== undefined, params._softNavAttributes);
|
|
215
213
|
}
|
|
214
|
+
}
|
|
215
|
+
#storeJserrorForHarvest(errorInfoArr, softNavOccurredFinished) {
|
|
216
|
+
let softNavCustomAttrs = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
217
|
+
let [type, bucketHash, params, newMetrics, localAttrs] = errorInfoArr;
|
|
218
|
+
const allCustomAttrs = {};
|
|
219
|
+
if (softNavOccurredFinished) {
|
|
220
|
+
Object.entries(softNavCustomAttrs).forEach(_ref => {
|
|
221
|
+
let [k, v] = _ref;
|
|
222
|
+
return setCustom(k, v);
|
|
223
|
+
}); // when an ixn finishes, it'll include stuff in jsAttributes + attrs specific to the ixn
|
|
224
|
+
bucketHash += params.browserInteractionId;
|
|
225
|
+
delete params._softNavAttributes; // cleanup temp properties from synchronous evaluation; this is harmless when async from soft nav (properties DNE)
|
|
226
|
+
delete params._softNavFinished;
|
|
227
|
+
} else {
|
|
228
|
+
// interaction was cancelled -> error should not be associated OR there was no interaction
|
|
229
|
+
Object.entries(getInfo(this.agentIdentifier).jsAttributes).forEach(_ref2 => {
|
|
230
|
+
let [k, v] = _ref2;
|
|
231
|
+
return setCustom(k, v);
|
|
232
|
+
});
|
|
233
|
+
delete params.browserInteractionId;
|
|
234
|
+
}
|
|
235
|
+
if (localAttrs) Object.entries(localAttrs).forEach(_ref3 => {
|
|
236
|
+
let [k, v] = _ref3;
|
|
237
|
+
return setCustom(k, v);
|
|
238
|
+
}); // local custom attrs are applied in either case with the highest precedence
|
|
239
|
+
|
|
240
|
+
const jsAttributesHash = stringHashCode(stringify(allCustomAttrs));
|
|
241
|
+
const aggregateHash = bucketHash + ':' + jsAttributesHash;
|
|
242
|
+
this.aggregator.store(type, aggregateHash, params, newMetrics, allCustomAttrs);
|
|
216
243
|
function setCustom(key, val) {
|
|
217
|
-
|
|
244
|
+
allCustomAttrs[key] = val && typeof val === 'object' ? stringify(val) : val;
|
|
218
245
|
}
|
|
219
246
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
var
|
|
226
|
-
|
|
227
|
-
mapOwn(interaction.root.attrs.custom, setCustom);
|
|
228
|
-
mapOwn(
|
|
247
|
+
|
|
248
|
+
// TO-DO: Remove this function when old spa is taken out. #storeJserrorForHarvest handles the work with the softnav feature.
|
|
249
|
+
onInteractionDone(interaction, wasSaved) {
|
|
250
|
+
if (!this.bufferedErrorsUnderSpa[interaction.id] || this.blocked) return;
|
|
251
|
+
this.bufferedErrorsUnderSpa[interaction.id].forEach(item => {
|
|
252
|
+
var allCustomAttrs = {};
|
|
253
|
+
const localCustomAttrs = item[4];
|
|
254
|
+
mapOwn(interaction.root.attrs.custom, setCustom); // tack on custom attrs from the interaction
|
|
255
|
+
mapOwn(localCustomAttrs, setCustom);
|
|
229
256
|
var params = item[2];
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
params.parentNodeId = params._interactionNodeId.toString();
|
|
234
|
-
delete params._interactionNodeId;
|
|
235
|
-
}
|
|
236
|
-
var hash = item[1] + interaction.root.attrs.id;
|
|
237
|
-
var jsAttributesHash = stringHashCode(stringify(customParams));
|
|
238
|
-
var aggregateHash = hash + ':' + jsAttributesHash;
|
|
239
|
-
this.aggregator.store(item[0], aggregateHash, params, item[3], customParams);
|
|
240
|
-
function setCustom(key, val) {
|
|
241
|
-
customParams[key] = val && typeof val === 'object' ? stringify(val) : val;
|
|
257
|
+
if (wasSaved) {
|
|
258
|
+
params.browserInteractionId = interaction.root.attrs.id;
|
|
259
|
+
if (params._interactionNodeId) params.parentNodeId = params._interactionNodeId.toString();
|
|
242
260
|
}
|
|
243
|
-
});
|
|
244
|
-
delete this.errorCache[interaction.id];
|
|
245
|
-
}
|
|
246
|
-
onInteractionDiscarded(interaction) {
|
|
247
|
-
if (!this.errorCache || !this.errorCache[interaction.id] || this.blocked) return;
|
|
248
|
-
this.errorCache[interaction.id].forEach(item => {
|
|
249
|
-
var customParams = {};
|
|
250
|
-
var globalCustomParams = item[4];
|
|
251
|
-
var localCustomParams = item[5];
|
|
252
|
-
mapOwn(globalCustomParams, setCustom);
|
|
253
|
-
mapOwn(interaction.root.attrs.custom, setCustom);
|
|
254
|
-
mapOwn(localCustomParams, setCustom);
|
|
255
|
-
var params = item[2];
|
|
256
261
|
delete params._interactionId;
|
|
257
262
|
delete params._interactionNodeId;
|
|
258
|
-
var hash = item[1];
|
|
259
|
-
var jsAttributesHash = stringHashCode(stringify(
|
|
263
|
+
var hash = wasSaved ? item[1] + interaction.root.attrs.id : item[1];
|
|
264
|
+
var jsAttributesHash = stringHashCode(stringify(allCustomAttrs));
|
|
260
265
|
var aggregateHash = hash + ':' + jsAttributesHash;
|
|
261
|
-
this.aggregator.store(item[0], aggregateHash,
|
|
266
|
+
this.aggregator.store(item[0], aggregateHash, params, item[3], allCustomAttrs);
|
|
262
267
|
function setCustom(key, val) {
|
|
263
|
-
|
|
268
|
+
allCustomAttrs[key] = val && typeof val === 'object' ? stringify(val) : val;
|
|
264
269
|
}
|
|
265
270
|
});
|
|
266
|
-
delete this.
|
|
271
|
+
delete this.bufferedErrorsUnderSpa[interaction.id];
|
|
272
|
+
}
|
|
273
|
+
onSoftNavNotification(interactionId, wasFinished, softNavAttrs) {
|
|
274
|
+
if (this.blocked) return;
|
|
275
|
+
this.bufferedErrorsUnderSpa[interactionId]?.forEach(jsErrorEvent => this.#storeJserrorForHarvest(jsErrorEvent, wasFinished, softNavAttrs) // this should not modify the re-used softNavAttrs contents
|
|
276
|
+
);
|
|
277
|
+
delete this.bufferedErrorsUnderSpa[interactionId]; // wipe the list of jserrors so they aren't duplicated by another call to the same id
|
|
267
278
|
}
|
|
268
279
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { registerHandler } from '../../../common/event-emitter/register-handler';
|
|
14
14
|
import { HarvestScheduler } from '../../../common/harvest/harvest-scheduler';
|
|
15
|
-
import { ABORT_REASONS, FEATURE_NAME, MAX_PAYLOAD_SIZE, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES } from '../constants';
|
|
15
|
+
import { ABORT_REASONS, FEATURE_NAME, MAX_PAYLOAD_SIZE, QUERY_PARAM_PADDING, RRWEB_EVENT_TYPES, SR_EVENT_EMITTER_TYPES } from '../constants';
|
|
16
16
|
import { getConfigurationValue, getInfo, getRuntime } from '../../../common/config/config';
|
|
17
17
|
import { AggregateBase } from '../../utils/aggregate-base';
|
|
18
18
|
import { sharedChannel } from '../../../common/constants/shared-channel';
|
|
@@ -53,103 +53,100 @@ export class Aggregate extends AggregateBase {
|
|
|
53
53
|
this.recorder = args?.recorder;
|
|
54
54
|
if (this.recorder) this.recorder.parent = this;
|
|
55
55
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/Enabled'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
56
|
-
const shouldSetup = getConfigurationValue(agentIdentifier, 'privacy.cookies_enabled') === true && getConfigurationValue(agentIdentifier, 'session_trace.enabled') === true;
|
|
57
|
-
if (shouldSetup) {
|
|
58
|
-
// 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.
|
|
59
|
-
this.ee.on(SESSION_EVENTS.RESET, () => {
|
|
60
|
-
this.scheduler.runHarvest();
|
|
61
|
-
this.abort(ABORT_REASONS.RESET);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
|
|
65
|
-
this.ee.on(SESSION_EVENTS.PAUSE, () => {
|
|
66
|
-
this.recorder?.stopRecording();
|
|
67
|
-
});
|
|
68
|
-
// The SessionEntity class can emit a message indicating the session was resumed (visibility change). This feature must start running again (if already running) if that occurs.
|
|
69
|
-
this.ee.on(SESSION_EVENTS.RESUME, () => {
|
|
70
|
-
if (!this.recorder) return;
|
|
71
|
-
// if the mode changed on a different tab, it needs to update this instance to match
|
|
72
|
-
const {
|
|
73
|
-
session
|
|
74
|
-
} = getRuntime(this.agentIdentifier);
|
|
75
|
-
this.mode = session.state.sessionReplayMode;
|
|
76
|
-
if (!this.initialized || this.mode === MODE.OFF) return;
|
|
77
|
-
this.recorder?.startRecording();
|
|
78
|
-
});
|
|
79
|
-
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
80
|
-
if (!this.recorder || !this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return;
|
|
81
|
-
if (this.mode !== MODE.OFF && data.sessionReplayMode === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
82
|
-
this.mode = data.sessionReplay;
|
|
83
|
-
});
|
|
84
56
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
raw: true
|
|
91
|
-
}, this);
|
|
92
|
-
if (this.recorder?.getEvents().type === 'preloaded') {
|
|
93
|
-
this.prepUtils().then(() => {
|
|
94
|
-
this.scheduler.runHarvest();
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
registerHandler('recordReplay', () => {
|
|
98
|
-
// if it has aborted or BCS returned bad entitlements, do not allow
|
|
99
|
-
if (this.blocked || !this.entitled) return;
|
|
100
|
-
// if it isnt already (fully) initialized... initialize it
|
|
101
|
-
if (!this.recorder) this.initializeRecording(false, true, true);
|
|
102
|
-
// its been initialized and imported the recorder but its not recording (mode === off || error)
|
|
103
|
-
else if (this.mode !== MODE.FULL) this.switchToFull();
|
|
104
|
-
// if it gets all the way to here, that means a full session is already recording... do nothing
|
|
105
|
-
}, this.featureName, this.ee);
|
|
106
|
-
registerHandler('pauseReplay', () => {
|
|
107
|
-
this.forceStop(this.mode !== MODE.ERROR);
|
|
108
|
-
}, this.featureName, this.ee);
|
|
57
|
+
// 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.
|
|
58
|
+
this.ee.on(SESSION_EVENTS.RESET, () => {
|
|
59
|
+
this.scheduler.runHarvest();
|
|
60
|
+
this.abort(ABORT_REASONS.RESET);
|
|
61
|
+
});
|
|
109
62
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
}, this.featureName, this.ee);
|
|
63
|
+
// The SessionEntity class can emit a message indicating the session was paused (visibility change). This feature must stop recording if that occurs.
|
|
64
|
+
this.ee.on(SESSION_EVENTS.PAUSE, () => {
|
|
65
|
+
this.recorder?.stopRecording();
|
|
66
|
+
});
|
|
67
|
+
// The SessionEntity class can emit a message indicating the session was resumed (visibility change). This feature must start running again (if already running) if that occurs.
|
|
68
|
+
this.ee.on(SESSION_EVENTS.RESUME, () => {
|
|
69
|
+
if (!this.recorder) return;
|
|
70
|
+
// if the mode changed on a different tab, it needs to update this instance to match
|
|
120
71
|
const {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
let [flagOn] = _ref;
|
|
133
|
-
this.entitled = flagOn;
|
|
134
|
-
if (!this.entitled && this.recorder?.recording) {
|
|
135
|
-
this.recorder.abort(ABORT_REASONS.ENTITLEMENTS);
|
|
136
|
-
handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
137
|
-
}
|
|
138
|
-
this.initializeRecording(Math.random() * 100 < error_sampling_rate, Math.random() * 100 < sampling_rate);
|
|
139
|
-
}).then(() => sharedChannel.onReplayReady(this.mode)); // notify watchers that replay started with the mode
|
|
72
|
+
session
|
|
73
|
+
} = getRuntime(this.agentIdentifier);
|
|
74
|
+
this.mode = session.state.sessionReplayMode;
|
|
75
|
+
if (!this.initialized || this.mode === MODE.OFF) return;
|
|
76
|
+
this.recorder?.startRecording();
|
|
77
|
+
});
|
|
78
|
+
this.ee.on(SESSION_EVENTS.UPDATE, (type, data) => {
|
|
79
|
+
if (!this.recorder || !this.initialized || this.blocked || type !== SESSION_EVENT_TYPES.CROSS_TAB) return;
|
|
80
|
+
if (this.mode !== MODE.OFF && data.sessionReplayMode === MODE.OFF) this.abort(ABORT_REASONS.CROSS_TAB);
|
|
81
|
+
this.mode = data.sessionReplay;
|
|
82
|
+
});
|
|
140
83
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
84
|
+
// Bespoke logic for blobs endpoint.
|
|
85
|
+
this.scheduler = new HarvestScheduler('browser/blobs', {
|
|
86
|
+
onFinished: this.onHarvestFinished.bind(this),
|
|
87
|
+
retryDelay: this.harvestTimeSeconds,
|
|
88
|
+
getPayload: this.prepareHarvest.bind(this),
|
|
89
|
+
raw: true
|
|
90
|
+
}, this);
|
|
91
|
+
registerHandler(SR_EVENT_EMITTER_TYPES.RECORD, () => {
|
|
92
|
+
// if it has aborted or BCS returned bad entitlements, do not allow
|
|
93
|
+
if (this.blocked || !this.entitled) return;
|
|
94
|
+
// if it isnt already (fully) initialized... initialize it
|
|
95
|
+
if (!this.recorder) this.initializeRecording(false, true, true);
|
|
96
|
+
// its been initialized and imported the recorder but its not recording (mode === off || error)
|
|
97
|
+
else if (this.mode !== MODE.FULL) this.switchToFull();
|
|
98
|
+
// if it gets all the way to here, that means a full session is already recording... do nothing
|
|
99
|
+
}, this.featureName, this.ee);
|
|
100
|
+
registerHandler(SR_EVENT_EMITTER_TYPES.PAUSE, () => {
|
|
101
|
+
this.forceStop(this.mode !== MODE.ERROR);
|
|
102
|
+
}, this.featureName, this.ee);
|
|
103
|
+
|
|
104
|
+
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
105
|
+
// This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
|
|
106
|
+
registerHandler('errorAgg', e => {
|
|
107
|
+
this.errorNoticed = true;
|
|
108
|
+
if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
|
|
109
|
+
// run once
|
|
110
|
+
if (this.mode === MODE.ERROR && globalScope?.document.visibilityState === 'visible') {
|
|
111
|
+
this.switchToFull();
|
|
112
|
+
}
|
|
113
|
+
}, this.featureName, this.ee);
|
|
114
|
+
const {
|
|
115
|
+
error_sampling_rate,
|
|
116
|
+
sampling_rate,
|
|
117
|
+
autoStart,
|
|
118
|
+
block_selector,
|
|
119
|
+
mask_text_selector,
|
|
120
|
+
mask_all_inputs,
|
|
121
|
+
inline_stylesheet,
|
|
122
|
+
inline_images,
|
|
123
|
+
collect_fonts
|
|
124
|
+
} = getConfigurationValue(this.agentIdentifier, 'session_replay');
|
|
125
|
+
this.waitForFlags(['sr']).then(_ref => {
|
|
126
|
+
let [flagOn] = _ref;
|
|
127
|
+
this.entitled = flagOn;
|
|
128
|
+
if (!this.entitled && this.recorder?.recording) {
|
|
129
|
+
this.abort(ABORT_REASONS.ENTITLEMENTS);
|
|
130
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.initializeRecording(Math.random() * 100 < error_sampling_rate, Math.random() * 100 < sampling_rate);
|
|
134
|
+
}).then(() => {
|
|
135
|
+
if (this.mode === MODE.OFF) args?.recorder?.stopRecording(); // stop any conservative preload recording launched by instrument
|
|
136
|
+
sharedChannel.onReplayReady(this.mode); // notify watchers that replay started with the mode
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
/** Detect if the default configs have been altered and report a SM. This is useful to evaluate what the reasonable defaults are across a customer base over time */
|
|
140
|
+
if (!autoStart) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
141
|
+
if (collect_fonts === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
142
|
+
if (inline_stylesheet !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
143
|
+
if (inline_images === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
144
|
+
if (mask_all_inputs !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
145
|
+
if (block_selector !== '[data-nr-block]') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
146
|
+
if (mask_text_selector !== '*') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
147
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
148
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
149
|
+
this.drain();
|
|
153
150
|
}
|
|
154
151
|
switchToFull() {
|
|
155
152
|
this.mode = MODE.FULL;
|
|
@@ -197,6 +194,11 @@ export class Aggregate extends AggregateBase {
|
|
|
197
194
|
return;
|
|
198
195
|
}
|
|
199
196
|
}
|
|
197
|
+
if (this.recorder?.getEvents().type === 'preloaded') {
|
|
198
|
+
this.prepUtils().then(() => {
|
|
199
|
+
this.scheduler.runHarvest();
|
|
200
|
+
});
|
|
201
|
+
}
|
|
200
202
|
if (!this.recorder) {
|
|
201
203
|
try {
|
|
202
204
|
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
@@ -389,7 +391,7 @@ export class Aggregate extends AggregateBase {
|
|
|
389
391
|
});
|
|
390
392
|
this.recorder?.clearTimestamps?.();
|
|
391
393
|
this.ee.emit('REPLAY_ABORTED');
|
|
392
|
-
this.recorder?.clearBuffer?.();
|
|
394
|
+
while (this.recorder?.getEvents().events.length) this.recorder?.clearBuffer?.();
|
|
393
395
|
}
|
|
394
396
|
syncWithSessionManager() {
|
|
395
397
|
let state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { MODE } from '../../common/session/constants';
|
|
2
2
|
import { FEATURE_NAMES } from '../../loaders/features/features';
|
|
3
3
|
export const FEATURE_NAME = FEATURE_NAMES.sessionReplay;
|
|
4
|
+
export const SR_EVENT_EMITTER_TYPES = {
|
|
5
|
+
RECORD: 'recordReplay',
|
|
6
|
+
PAUSE: 'pauseReplay'
|
|
7
|
+
};
|
|
4
8
|
export const AVG_COMPRESSION = 0.12;
|
|
5
9
|
export const RRWEB_EVENT_TYPES = {
|
|
6
10
|
DomContentLoaded: 0,
|