@newrelic/browser-agent 1.252.1 → 1.254.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +2 -2
- package/dist/cjs/cdn/experimental.js +6 -2
- package/dist/cjs/cdn/polyfills.js +2 -1
- 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/config/state/runtime.js +4 -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/drain/drain.js +41 -27
- package/dist/cjs/common/event-emitter/contextual-ee.js +17 -12
- package/dist/cjs/common/harvest/harvest.js +5 -1
- 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/timing/time-keeper.js +94 -0
- package/dist/cjs/common/util/feature-flags.js +14 -31
- package/dist/cjs/common/wrap/index.js +0 -7
- package/dist/cjs/features/ajax/aggregate/index.js +41 -29
- package/dist/cjs/features/jserrors/aggregate/index.js +96 -84
- package/dist/cjs/features/metrics/aggregate/index.js +25 -24
- package/dist/cjs/features/page_action/aggregate/index.js +6 -4
- package/dist/cjs/features/page_view_event/aggregate/index.js +23 -3
- package/dist/cjs/features/page_view_event/aggregate/initialized-features.js +1 -0
- package/dist/cjs/features/page_view_timing/aggregate/index.js +15 -16
- package/dist/cjs/features/session_replay/aggregate/index.js +102 -92
- 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/session_trace/aggregate/index.js +11 -8
- 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 +268 -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 +23 -18
- package/dist/cjs/features/utils/agent-session.js +2 -1
- package/dist/cjs/features/utils/aggregate-base.js +18 -5
- package/dist/cjs/features/utils/feature-base.js +2 -0
- package/dist/cjs/features/utils/instrument-base.js +7 -9
- package/dist/cjs/features/utils/lazy-feature-loader.js +2 -0
- package/dist/cjs/loaders/agent-base.js +13 -3
- package/dist/cjs/loaders/agent.js +19 -22
- package/dist/cjs/loaders/api/api-methods.js +2 -1
- package/dist/cjs/loaders/api/api.js +15 -12
- package/dist/cjs/loaders/configure/configure.js +5 -2
- package/dist/cjs/loaders/configure/nonce.cdn.js +13 -0
- package/dist/cjs/loaders/configure/nonce.js +2 -13
- package/dist/cjs/loaders/configure/public-path.cdn.js +16 -0
- package/dist/cjs/loaders/configure/public-path.js +2 -8
- 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/polyfills.js +2 -1
- 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/config/state/runtime.js +4 -1
- package/dist/esm/common/constants/env.cdn.js +1 -1
- package/dist/esm/common/constants/env.npm.js +1 -1
- package/dist/esm/common/drain/drain.js +40 -27
- package/dist/esm/common/event-emitter/contextual-ee.js +17 -12
- package/dist/esm/common/harvest/harvest.js +5 -1
- 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/timing/time-keeper.js +88 -0
- package/dist/esm/common/util/feature-flags.js +14 -31
- package/dist/esm/common/wrap/index.js +1 -2
- package/dist/esm/features/ajax/aggregate/index.js +43 -31
- package/dist/esm/features/jserrors/aggregate/index.js +96 -84
- package/dist/esm/features/metrics/aggregate/index.js +25 -24
- package/dist/esm/features/page_action/aggregate/index.js +6 -4
- package/dist/esm/features/page_view_event/aggregate/index.js +23 -3
- package/dist/esm/features/page_view_event/aggregate/initialized-features.js +1 -0
- package/dist/esm/features/page_view_timing/aggregate/index.js +15 -16
- package/dist/esm/features/session_replay/aggregate/index.js +103 -93
- 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/session_trace/aggregate/index.js +11 -8
- 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 +261 -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 +23 -18
- package/dist/esm/features/utils/agent-session.js +2 -1
- package/dist/esm/features/utils/aggregate-base.js +18 -5
- package/dist/esm/features/utils/feature-base.js +2 -0
- package/dist/esm/features/utils/instrument-base.js +8 -10
- package/dist/esm/features/utils/lazy-feature-loader.js +2 -0
- package/dist/esm/loaders/agent-base.js +13 -3
- package/dist/esm/loaders/agent.js +19 -22
- package/dist/esm/loaders/api/api-methods.js +2 -1
- package/dist/esm/loaders/api/api.js +15 -12
- package/dist/esm/loaders/configure/configure.js +5 -2
- package/dist/esm/loaders/configure/nonce.cdn.js +11 -0
- package/dist/esm/loaders/configure/nonce.js +1 -11
- package/dist/esm/loaders/configure/public-path.cdn.js +9 -0
- package/dist/esm/loaders/configure/public-path.js +2 -8
- 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/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/drain/drain.d.ts +6 -0
- package/dist/types/common/drain/drain.d.ts.map +1 -1
- package/dist/types/common/event-emitter/contextual-ee.d.ts +2 -1
- package/dist/types/common/event-emitter/contextual-ee.d.ts.map +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/constants.d.ts.map +1 -1
- package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
- package/dist/types/common/timing/time-keeper.d.ts +31 -0
- package/dist/types/common/timing/time-keeper.d.ts.map +1 -0
- package/dist/types/common/util/feature-flags.d.ts +11 -2
- package/dist/types/common/util/feature-flags.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/features/ajax/aggregate/index.d.ts +5 -5
- 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/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/initialized-features.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +0 -2
- package/dist/types/features/page_view_timing/aggregate/index.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 +34 -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 +2 -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/aggregate-base.d.ts +2 -2
- package/dist/types/features/utils/aggregate-base.d.ts.map +1 -1
- package/dist/types/features/utils/feature-base.d.ts +1 -0
- package/dist/types/features/utils/feature-base.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/configure/public-path.d.ts +1 -1
- package/dist/types/loaders/configure/public-path.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/polyfills.js +1 -0
- 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/config/state/runtime.js +4 -1
- package/src/common/drain/drain.js +41 -28
- package/src/common/event-emitter/contextual-ee.js +21 -13
- package/src/common/harvest/harvest.js +4 -1
- 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/timing/time-keeper.js +96 -0
- package/src/common/util/feature-flags.js +13 -31
- package/src/common/wrap/index.js +1 -2
- package/src/features/ajax/aggregate/index.js +43 -33
- package/src/features/jserrors/aggregate/index.js +82 -87
- package/src/features/metrics/aggregate/index.js +18 -17
- package/src/features/page_action/aggregate/index.js +6 -5
- package/src/features/page_view_event/aggregate/index.js +19 -3
- package/src/features/page_view_event/aggregate/initialized-features.js +1 -0
- package/src/features/page_view_timing/aggregate/index.js +15 -15
- package/src/features/session_replay/aggregate/index.js +95 -92
- 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/session_trace/aggregate/index.js +2 -2
- 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 +256 -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 +20 -17
- package/src/features/utils/agent-session.js +2 -1
- package/src/features/utils/aggregate-base.js +16 -8
- package/src/features/utils/feature-base.js +3 -0
- package/src/features/utils/instrument-base.js +8 -10
- package/src/features/utils/lazy-feature-loader.js +2 -0
- package/src/loaders/agent-base.js +13 -3
- package/src/loaders/agent.js +20 -19
- package/src/loaders/api/api-methods.js +4 -1
- package/src/loaders/api/api.js +15 -13
- package/src/loaders/configure/configure.js +4 -1
- package/src/loaders/configure/nonce.cdn.js +12 -0
- package/src/loaders/configure/nonce.js +1 -12
- package/src/loaders/configure/public-path.cdn.js +9 -0
- package/src/loaders/configure/public-path.js +2 -8
- 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/cjs/loaders/configure/nonce.npm.js +0 -2
- package/dist/cjs/loaders/configure/public-path.npm.js +0 -10
- package/dist/esm/common/wrap/wrap-raf.js +0 -48
- package/dist/esm/loaders/configure/nonce.npm.js +0 -1
- package/dist/esm/loaders/configure/public-path.npm.js +0 -3
- package/dist/types/common/wrap/wrap-raf.d.ts +0 -16
- package/dist/types/common/wrap/wrap-raf.d.ts.map +0 -1
- package/dist/types/loaders/configure/nonce.npm.d.ts +0 -1
- package/dist/types/loaders/configure/nonce.npm.d.ts.map +0 -1
- package/dist/types/loaders/configure/public-path.npm.d.ts +0 -2
- package/dist/types/loaders/configure/public-path.npm.d.ts.map +0 -1
- package/src/common/wrap/wrap-raf.js +0 -52
- package/src/loaders/configure/nonce.npm.js +0 -1
- package/src/loaders/configure/public-path.npm.js +0 -3
|
@@ -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';
|
|
@@ -27,6 +27,7 @@ import { now } from '../../../common/timing/now';
|
|
|
27
27
|
import { MODE, SESSION_EVENTS, SESSION_EVENT_TYPES } from '../../../common/session/constants';
|
|
28
28
|
import { stringify } from '../../../common/util/stringify';
|
|
29
29
|
import { stylesheetEvaluator } from '../shared/stylesheet-evaluator';
|
|
30
|
+
import { deregisterDrain } from '../../../common/drain/drain';
|
|
30
31
|
export class Aggregate extends AggregateBase {
|
|
31
32
|
static featureName = FEATURE_NAME;
|
|
32
33
|
// pass the recorder into the aggregator
|
|
@@ -53,103 +54,103 @@ export class Aggregate extends AggregateBase {
|
|
|
53
54
|
this.recorder = args?.recorder;
|
|
54
55
|
if (this.recorder) this.recorder.parent = this;
|
|
55
56
|
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
57
|
|
|
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);
|
|
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
|
+
});
|
|
109
63
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
}, this.featureName, this.ee);
|
|
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
|
|
120
72
|
const {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
+
|
|
85
|
+
// Bespoke logic for blobs endpoint.
|
|
86
|
+
this.scheduler = new HarvestScheduler('browser/blobs', {
|
|
87
|
+
onFinished: this.onHarvestFinished.bind(this),
|
|
88
|
+
retryDelay: this.harvestTimeSeconds,
|
|
89
|
+
getPayload: this.prepareHarvest.bind(this),
|
|
90
|
+
raw: true
|
|
91
|
+
}, this);
|
|
92
|
+
registerHandler(SR_EVENT_EMITTER_TYPES.RECORD, () => {
|
|
93
|
+
// if it has aborted or BCS returned bad entitlements, do not allow
|
|
94
|
+
if (this.blocked || !this.entitled) return;
|
|
95
|
+
// if it isnt already (fully) initialized... initialize it
|
|
96
|
+
if (!this.recorder) this.initializeRecording(false, true, true);
|
|
97
|
+
// its been initialized and imported the recorder but its not recording (mode === off || error)
|
|
98
|
+
else if (this.mode !== MODE.FULL) this.switchToFull();
|
|
99
|
+
// if it gets all the way to here, that means a full session is already recording... do nothing
|
|
100
|
+
}, this.featureName, this.ee);
|
|
101
|
+
registerHandler(SR_EVENT_EMITTER_TYPES.PAUSE, () => {
|
|
102
|
+
this.forceStop(this.mode !== MODE.ERROR);
|
|
103
|
+
}, this.featureName, this.ee);
|
|
104
|
+
|
|
105
|
+
// Wait for an error to be reported. This currently is wrapped around the "Error" feature. This is a feature-feature dependency.
|
|
106
|
+
// This was to ensure that all errors, including those on the page before load and those handled with "noticeError" are accounted for. Needs evalulation
|
|
107
|
+
registerHandler('errorAgg', e => {
|
|
108
|
+
this.errorNoticed = true;
|
|
109
|
+
if (this.recorder) this.recorder.currentBufferTarget.hasError = true;
|
|
110
|
+
// run once
|
|
111
|
+
if (this.mode === MODE.ERROR && globalScope?.document.visibilityState === 'visible') {
|
|
112
|
+
this.switchToFull();
|
|
113
|
+
}
|
|
114
|
+
}, this.featureName, this.ee);
|
|
115
|
+
const {
|
|
116
|
+
error_sampling_rate,
|
|
117
|
+
sampling_rate,
|
|
118
|
+
autoStart,
|
|
119
|
+
block_selector,
|
|
120
|
+
mask_text_selector,
|
|
121
|
+
mask_all_inputs,
|
|
122
|
+
inline_stylesheet,
|
|
123
|
+
inline_images,
|
|
124
|
+
collect_fonts
|
|
125
|
+
} = getConfigurationValue(this.agentIdentifier, 'session_replay');
|
|
126
|
+
this.waitForFlags(['sr']).then(_ref => {
|
|
127
|
+
let [flagOn] = _ref;
|
|
128
|
+
this.entitled = flagOn;
|
|
129
|
+
if (!this.entitled) {
|
|
130
|
+
deregisterDrain(this.agentIdentifier, this.featureName);
|
|
131
|
+
if (this.recorder?.recording) {
|
|
132
|
+
this.abort(ABORT_REASONS.ENTITLEMENTS);
|
|
136
133
|
handle(SUPPORTABILITY_METRIC_CHANNEL, ['SessionReplay/EnabledNotEntitled/Detected'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
137
134
|
}
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** 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 */
|
|
142
|
-
if (!autoStart) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
143
|
-
if (collect_fonts === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
144
|
-
if (inline_stylesheet !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
145
|
-
if (inline_images === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
146
|
-
if (mask_all_inputs !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
147
|
-
if (block_selector !== '[data-nr-block]') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
148
|
-
if (mask_text_selector !== '*') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
149
|
-
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
150
|
-
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
151
137
|
this.drain();
|
|
152
|
-
|
|
138
|
+
this.initializeRecording(Math.random() * 100 < error_sampling_rate, Math.random() * 100 < sampling_rate);
|
|
139
|
+
}).then(() => {
|
|
140
|
+
if (this.mode === MODE.OFF) args?.recorder?.stopRecording(); // stop any conservative preload recording launched by instrument
|
|
141
|
+
sharedChannel.onReplayReady(this.mode); // notify watchers that replay started with the mode
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/** 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 */
|
|
145
|
+
if (!autoStart) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/AutoStart/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
146
|
+
if (collect_fonts === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/CollectFonts/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
147
|
+
if (inline_stylesheet !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineStylesheet/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
148
|
+
if (inline_images === true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/InlineImages/Modifed'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
149
|
+
if (mask_all_inputs !== true) handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskAllInputs/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
150
|
+
if (block_selector !== '[data-nr-block]') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/BlockSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
151
|
+
if (mask_text_selector !== '*') handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/MaskTextSelector/Modified'], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
152
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/SamplingRate/Value', sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
153
|
+
handle(SUPPORTABILITY_METRIC_CHANNEL, ['Config/SessionReplay/ErrorSamplingRate/Value', error_sampling_rate], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
153
154
|
}
|
|
154
155
|
switchToFull() {
|
|
155
156
|
this.mode = MODE.FULL;
|
|
@@ -197,6 +198,11 @@ export class Aggregate extends AggregateBase {
|
|
|
197
198
|
return;
|
|
198
199
|
}
|
|
199
200
|
}
|
|
201
|
+
if (this.recorder?.getEvents().type === 'preloaded') {
|
|
202
|
+
this.prepUtils().then(() => {
|
|
203
|
+
this.scheduler.runHarvest();
|
|
204
|
+
});
|
|
205
|
+
}
|
|
200
206
|
if (!this.recorder) {
|
|
201
207
|
try {
|
|
202
208
|
// Do not change the webpackChunkName or it will break the webpack nrba-chunking plugin
|
|
@@ -315,6 +321,7 @@ export class Aggregate extends AggregateBase {
|
|
|
315
321
|
const lastEventTimestamp = events[events.length - 1]?.timestamp; // from rrweb node
|
|
316
322
|
const firstTimestamp = firstEventTimestamp || recorderEvents.cycleTimestamp; // from rrweb node || from when the harvest cycle started
|
|
317
323
|
const lastTimestamp = lastEventTimestamp || agentOffset + relativeNow;
|
|
324
|
+
const agentMetadata = agentRuntime.appMetadata?.agents?.[0] || {};
|
|
318
325
|
return {
|
|
319
326
|
qs: {
|
|
320
327
|
browser_monitoring_key: info.licenseKey,
|
|
@@ -327,6 +334,9 @@ export class Aggregate extends AggregateBase {
|
|
|
327
334
|
...(!!this.gzipper && !!this.u8 && {
|
|
328
335
|
content_encoding: 'gzip'
|
|
329
336
|
}),
|
|
337
|
+
...(agentMetadata.entityGuid && {
|
|
338
|
+
entityGuid: agentMetadata.entityGuid
|
|
339
|
+
}),
|
|
330
340
|
'replay.firstTimestamp': firstTimestamp,
|
|
331
341
|
'replay.firstTimestampOffset': firstTimestamp - agentOffset,
|
|
332
342
|
'replay.lastTimestamp': lastTimestamp,
|
|
@@ -389,7 +399,7 @@ export class Aggregate extends AggregateBase {
|
|
|
389
399
|
});
|
|
390
400
|
this.recorder?.clearTimestamps?.();
|
|
391
401
|
this.ee.emit('REPLAY_ABORTED');
|
|
392
|
-
this.recorder?.clearBuffer?.();
|
|
402
|
+
while (this.recorder?.getEvents().events.length) this.recorder?.clearBuffer?.();
|
|
393
403
|
}
|
|
394
404
|
syncWithSessionManager() {
|
|
395
405
|
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,
|
|
@@ -9,23 +9,38 @@
|
|
|
9
9
|
* It is not production ready, and is not intended to be imported or implemented in any build of the browser agent until
|
|
10
10
|
* functionality is validated and a full user experience is curated.
|
|
11
11
|
*/
|
|
12
|
-
import { MODE } from '../../../common/session/constants';
|
|
12
|
+
import { DEFAULT_KEY, MODE, PREFIX } from '../../../common/session/constants';
|
|
13
13
|
import { InstrumentBase } from '../../utils/instrument-base';
|
|
14
14
|
import { FEATURE_NAME } from '../constants';
|
|
15
|
+
import { isPreloadAllowed } from '../shared/utils';
|
|
15
16
|
export class Instrument extends InstrumentBase {
|
|
16
17
|
static featureName = FEATURE_NAME;
|
|
17
18
|
constructor(agentIdentifier, aggregator) {
|
|
18
19
|
let auto = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
|
|
19
20
|
super(agentIdentifier, aggregator, FEATURE_NAME, auto);
|
|
21
|
+
let session;
|
|
20
22
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
23
|
+
session = JSON.parse(localStorage.getItem("".concat(PREFIX, "_").concat(DEFAULT_KEY)));
|
|
24
|
+
} catch (err) {}
|
|
25
|
+
if (this.#canPreloadRecorder(session)) {
|
|
26
|
+
this.#startRecording(session?.sessionReplayMode);
|
|
27
|
+
} else {
|
|
28
|
+
this.importAggregator();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// At this point wherein session state exists already but we haven't init SessionEntity aka verify timers.
|
|
33
|
+
#canPreloadRecorder(session) {
|
|
34
|
+
if (!session) {
|
|
35
|
+
// this might be a new session if entity initializes: conservatively start recording if first-time config allows
|
|
36
|
+
// Note: users with SR enabled, as well as these other configs enabled by-default, will be penalized by the recorder overhead EVEN IF they don't actually have or get
|
|
37
|
+
// entitlement or sampling decision, or otherwise intentionally opted-in for the feature.
|
|
38
|
+
return isPreloadAllowed(this.agentIdentifier);
|
|
39
|
+
} else if (session.sessionReplayMode === MODE.FULL || session.sessionReplayMode === MODE.ERROR) {
|
|
40
|
+
return true; // existing sessions get to continue recording, regardless of this page's configs or if it has expired (conservatively)
|
|
41
|
+
} else {
|
|
42
|
+
// SR mode was OFF but may potentially be turned on if session resets and configs allows the new session to have replay...
|
|
43
|
+
return isPreloadAllowed(this.agentIdentifier);
|
|
29
44
|
}
|
|
30
45
|
}
|
|
31
46
|
async #startRecording(mode) {
|
|
@@ -37,6 +52,7 @@ export class Instrument extends InstrumentBase {
|
|
|
37
52
|
agentIdentifier: this.agentIdentifier
|
|
38
53
|
});
|
|
39
54
|
this.recorder.startRecording();
|
|
55
|
+
this.abortHandler = this.recorder.stopRecording;
|
|
40
56
|
this.importAggregator({
|
|
41
57
|
recorder: this.recorder
|
|
42
58
|
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getConfigurationValue, originals } from '../../../common/config/config';
|
|
2
|
+
import { isBrowserScope } from '../../../common/constants/runtime';
|
|
3
|
+
export const enableSessionTracking = agentId => isBrowserScope && getConfigurationValue(agentId, 'privacy.cookies_enabled') === true;
|
|
4
|
+
function hasReplayPrerequisite(agentId) {
|
|
5
|
+
return originals.MO &&
|
|
6
|
+
// Session Replay cannot work without Mutation Observer
|
|
7
|
+
enableSessionTracking &&
|
|
8
|
+
// requires session tracking to be running (hence "session" replay...)
|
|
9
|
+
getConfigurationValue(agentId, 'session_trace.enabled') === true; // Session Replay as of now is tightly coupled with Session Trace in the UI
|
|
10
|
+
}
|
|
11
|
+
export function isPreloadAllowed(agentId) {
|
|
12
|
+
return getConfigurationValue(agentId, 'session_replay.preload') === true && hasReplayPrerequisite(agentId);
|
|
13
|
+
}
|
|
14
|
+
export function canImportReplayAgg(agentId, sessionMgr) {
|
|
15
|
+
if (!hasReplayPrerequisite(agentId)) return false;
|
|
16
|
+
return !!sessionMgr?.isNew || !!sessionMgr?.state.sessionReplayMode; // Session Replay should only try to run if already running from a previous page, or at the beginning of a session
|
|
17
|
+
}
|
|
@@ -116,7 +116,10 @@ export class Aggregate extends AggregateBase {
|
|
|
116
116
|
if (!sessionEntity) {
|
|
117
117
|
// Since session manager isn't around, do the old Trace behavior of waiting for RUM response to decide feature activation.
|
|
118
118
|
this.isStandalone = true;
|
|
119
|
-
|
|
119
|
+
this.waitForFlags(['stn']).then(_ref => {
|
|
120
|
+
let [on] = _ref;
|
|
121
|
+
return controlTraceOp(on);
|
|
122
|
+
}, this.featureName, this.ee);
|
|
120
123
|
} else {
|
|
121
124
|
registerHandler('errorAgg', () => {
|
|
122
125
|
seenAnError = true;
|
|
@@ -133,8 +136,8 @@ export class Aggregate extends AggregateBase {
|
|
|
133
136
|
};
|
|
134
137
|
|
|
135
138
|
// CAUTION: everything inside this promise runs post-load; event subscribers must be pre-load aka synchronous with constructor
|
|
136
|
-
this.waitForFlags(['stn', 'sr']).then(async
|
|
137
|
-
let [traceOn, replayOn] =
|
|
139
|
+
this.waitForFlags(['stn', 'sr']).then(async _ref2 => {
|
|
140
|
+
let [traceOn, replayOn] = _ref2;
|
|
138
141
|
if (!replayOn) {
|
|
139
142
|
// When sr = 0 from BCS, also do the old Trace behavior:
|
|
140
143
|
this.isStandalone = true;
|
|
@@ -164,7 +167,7 @@ export class Aggregate extends AggregateBase {
|
|
|
164
167
|
if (replayMode === MODE.OFF) this.isStandalone = true; // without SR, Traces are still subject to old harvest limits
|
|
165
168
|
|
|
166
169
|
let startingMode;
|
|
167
|
-
if (traceOn
|
|
170
|
+
if (traceOn) {
|
|
168
171
|
// CASE: both trace (entitlement+sampling) & replay (entitlement) flags are true from RUM
|
|
169
172
|
startingMode = MODE.FULL; // always full capture regardless of replay sampling decisions
|
|
170
173
|
} else {
|
|
@@ -252,8 +255,8 @@ export class Aggregate extends AggregateBase {
|
|
|
252
255
|
}
|
|
253
256
|
if (result.sent && result.retry && this.sentTrace) {
|
|
254
257
|
// merge previous trace back into buffer to retry for next harvest
|
|
255
|
-
Object.entries(this.sentTrace).forEach(
|
|
256
|
-
let [name, listOfSTNodes] =
|
|
258
|
+
Object.entries(this.sentTrace).forEach(_ref3 => {
|
|
259
|
+
let [name, listOfSTNodes] = _ref3;
|
|
257
260
|
if (this.nodeCount >= this.maxNodesPerHarvest) return;
|
|
258
261
|
this.nodeCount += listOfSTNodes.length;
|
|
259
262
|
this.trace[name] = this.trace[name] ? listOfSTNodes.concat(this.trace[name]) : listOfSTNodes;
|
|
@@ -499,8 +502,8 @@ export class Aggregate extends AggregateBase {
|
|
|
499
502
|
this.storeResources(window.performance.getEntriesByType('resource'));
|
|
500
503
|
}
|
|
501
504
|
let earliestTimeStamp = Infinity;
|
|
502
|
-
const stns = Object.entries(this.trace).flatMap(
|
|
503
|
-
let [name, listOfSTNodes] =
|
|
505
|
+
const stns = Object.entries(this.trace).flatMap(_ref4 => {
|
|
506
|
+
let [name, listOfSTNodes] = _ref4;
|
|
504
507
|
// basically take the "this.trace" map-obj and concat all the list-type values
|
|
505
508
|
const oldestNodeTS = listOfSTNodes.reduce((acc, next) => !acc || next.s < acc ? next.s : acc, undefined);
|
|
506
509
|
if (oldestNodeTS < earliestTimeStamp) earliestTimeStamp = oldestNodeTS;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { addCustomAttributes, getAddStringContext, nullable, numeric } from '../../../common/serialize/bel-serializer';
|
|
2
|
+
import { NODE_TYPE } from '../constants';
|
|
3
|
+
import { BelNode } from './bel-node';
|
|
4
|
+
export class AjaxNode extends BelNode {
|
|
5
|
+
constructor(agentIdentifier, ajaxEvent) {
|
|
6
|
+
super(agentIdentifier);
|
|
7
|
+
this.belType = NODE_TYPE.AJAX;
|
|
8
|
+
this.method = ajaxEvent.method;
|
|
9
|
+
this.status = ajaxEvent.status;
|
|
10
|
+
this.domain = ajaxEvent.domain;
|
|
11
|
+
this.path = ajaxEvent.path;
|
|
12
|
+
this.txSize = ajaxEvent.requestSize;
|
|
13
|
+
this.rxSize = ajaxEvent.responseSize;
|
|
14
|
+
this.requestedWith = ajaxEvent.type === 'fetch' ? 1 : ''; // 'xhr' and 'beacon' types get the empty string
|
|
15
|
+
this.spanId = ajaxEvent.spanId;
|
|
16
|
+
this.traceId = ajaxEvent.traceId;
|
|
17
|
+
this.spanTimestamp = ajaxEvent.spanTimestamp;
|
|
18
|
+
this.gql = ajaxEvent.gql;
|
|
19
|
+
this.start = ajaxEvent.startTime;
|
|
20
|
+
this.end = ajaxEvent.endTime;
|
|
21
|
+
}
|
|
22
|
+
serialize(parentStartTimestamp) {
|
|
23
|
+
const addString = getAddStringContext(this.agentIdentifier);
|
|
24
|
+
const nodeList = [];
|
|
25
|
+
|
|
26
|
+
// IMPORTANT: The order in which addString is called matters and correlates to the order in which string shows up in the harvest payload. Do not re-order the following code.
|
|
27
|
+
const fields = [numeric(this.belType), 0,
|
|
28
|
+
// this will be overwritten below with number of attached nodes
|
|
29
|
+
numeric(this.start - parentStartTimestamp),
|
|
30
|
+
// start relative to parent start (if part of first node in payload) or first parent start
|
|
31
|
+
numeric(this.end - this.start),
|
|
32
|
+
// end is relative to start
|
|
33
|
+
numeric(this.callbackEnd), numeric(this.callbackDuration), addString(this.method), numeric(this.status), addString(this.domain), addString(this.path), numeric(this.txSize), numeric(this.rxSize), this.requestedWith, addString(this.nodeId), nullable(this.spanId, addString, true) + nullable(this.traceId, addString, true) + nullable(this.spanTimestamp, numeric)];
|
|
34
|
+
let allAttachedNodes = [];
|
|
35
|
+
if (typeof this.gql === 'object') allAttachedNodes = addCustomAttributes(this.gql, addString);
|
|
36
|
+
this.children.forEach(node => allAttachedNodes.push(node.serialize())); // no children is expected under ajax nodes at this time
|
|
37
|
+
|
|
38
|
+
fields[1] = numeric(allAttachedNodes.length);
|
|
39
|
+
nodeList.push(fields);
|
|
40
|
+
if (allAttachedNodes.length) nodeList.push(allAttachedNodes.join(';'));
|
|
41
|
+
return nodeList.join(';');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { now } from '../../../common/timing/now';
|
|
2
|
+
let nodesSeen = 0;
|
|
3
|
+
export class BelNode {
|
|
4
|
+
belType;
|
|
5
|
+
/** List of other BelNode derivatives. Each children should be of a subclass that implements its own 'serialize' function. */
|
|
6
|
+
children = [];
|
|
7
|
+
start = now();
|
|
8
|
+
end;
|
|
9
|
+
callbackEnd = 0;
|
|
10
|
+
callbackDuration = 0;
|
|
11
|
+
nodeId = ++nodesSeen;
|
|
12
|
+
constructor(agentIdentifier) {
|
|
13
|
+
if (!agentIdentifier) throw new Error('Interaction is missing core attributes');
|
|
14
|
+
this.agentIdentifier = agentIdentifier;
|
|
15
|
+
}
|
|
16
|
+
addChild(child) {
|
|
17
|
+
this.children.push(child);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Virtual fn for stringifying an instance. */
|
|
21
|
+
serialize() {}
|
|
22
|
+
}
|