@statsig/session-replay 3.16.2 → 3.17.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/package.json +2 -2
- package/src/SessionReplay.js +8 -0
- package/src/SessionReplayBase.d.ts +1 -0
- package/src/SessionReplayBase.js +13 -2
- package/src/SessionReplayUtils.d.ts +1 -0
- package/src/SessionReplayUtils.js +2 -1
- package/src/TriggeredSessionReplay.d.ts +6 -0
- package/src/TriggeredSessionReplay.js +63 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statsig/session-replay",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.17.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"homepage": "https://github.com/statsig-io/js-client-monorepo",
|
|
6
6
|
"repository": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"rrweb": "2.0.0-alpha.14",
|
|
13
|
-
"@statsig/client-core": "3.
|
|
13
|
+
"@statsig/client-core": "3.17.0"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@rrweb/types": "2.0.0-alpha.14"
|
package/src/SessionReplay.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SessionReplay = exports.runStatsigSessionReplay = exports.StatsigSessionReplayPlugin = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
5
|
const SessionReplayBase_1 = require("./SessionReplayBase");
|
|
6
|
+
const SessionReplayUtils_1 = require("./SessionReplayUtils");
|
|
6
7
|
class StatsigSessionReplayPlugin {
|
|
7
8
|
constructor(options) {
|
|
8
9
|
this.options = options;
|
|
@@ -34,7 +35,14 @@ class SessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
34
35
|
}
|
|
35
36
|
_attemptToStartRecording(force = false) {
|
|
36
37
|
var _a, _b;
|
|
38
|
+
if (this._totalLogs >= SessionReplayUtils_1.MAX_LOGS) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
37
41
|
const values = this._client.getContext().values;
|
|
42
|
+
if ((values === null || values === void 0 ? void 0 : values.recording_blocked) === true) {
|
|
43
|
+
this._shutdown();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
38
46
|
if (!force && (values === null || values === void 0 ? void 0 : values.can_record_session) !== true) {
|
|
39
47
|
this._shutdown();
|
|
40
48
|
return;
|
|
@@ -15,6 +15,7 @@ export declare abstract class SessionReplayBase {
|
|
|
15
15
|
protected _replayer: SessionReplayClient;
|
|
16
16
|
protected _wasStopped: boolean;
|
|
17
17
|
protected _currentEventIndex: number;
|
|
18
|
+
protected _totalLogs: number;
|
|
18
19
|
constructor(client: PrecomputedEvaluationsInterface, options?: SessionReplayOptions);
|
|
19
20
|
forceStartRecording(): void;
|
|
20
21
|
stopRecording(): void;
|
package/src/SessionReplayBase.js
CHANGED
|
@@ -15,6 +15,7 @@ class SessionReplayBase {
|
|
|
15
15
|
this._events = [];
|
|
16
16
|
this._wasStopped = false;
|
|
17
17
|
this._currentEventIndex = 0;
|
|
18
|
+
this._totalLogs = 0;
|
|
18
19
|
this._client = client;
|
|
19
20
|
this._options = options;
|
|
20
21
|
const { sdkKey, errorBoundary } = this._client.getContext();
|
|
@@ -87,6 +88,7 @@ class SessionReplayBase {
|
|
|
87
88
|
if (endReason) {
|
|
88
89
|
event.metadata[endReason] = 'true';
|
|
89
90
|
}
|
|
91
|
+
this._totalLogs++;
|
|
90
92
|
this._client.logEvent(event);
|
|
91
93
|
if (slicedID != null) {
|
|
92
94
|
this._client.flush().catch((e) => {
|
|
@@ -95,6 +97,9 @@ class SessionReplayBase {
|
|
|
95
97
|
}
|
|
96
98
|
}
|
|
97
99
|
this._events = [];
|
|
100
|
+
if (this._totalLogs > SessionReplayUtils_1.MAX_LOGS) {
|
|
101
|
+
this._shutdown();
|
|
102
|
+
}
|
|
98
103
|
}
|
|
99
104
|
_bumpSessionIdleTimerAndLogRecording() {
|
|
100
105
|
this._getSessionIdFromClient();
|
|
@@ -104,8 +109,10 @@ class SessionReplayBase {
|
|
|
104
109
|
return this._client.getContext().session.data.sessionID;
|
|
105
110
|
}
|
|
106
111
|
_shutdownImpl(endReason) {
|
|
107
|
-
this._replayer.
|
|
108
|
-
|
|
112
|
+
if (this._replayer.isRecording()) {
|
|
113
|
+
this._replayer.stop();
|
|
114
|
+
client_core_1.StatsigMetadataProvider.add({ isRecordingSession: 'false' });
|
|
115
|
+
}
|
|
109
116
|
if (this._events.length === 0) {
|
|
110
117
|
// only reset if session expired otherwise we might start recording again
|
|
111
118
|
if (endReason === 'session_expired') {
|
|
@@ -134,6 +141,10 @@ class SessionReplayBase {
|
|
|
134
141
|
this._shutdown('session_expired');
|
|
135
142
|
return;
|
|
136
143
|
}
|
|
144
|
+
if (this._totalLogs >= SessionReplayUtils_1.MAX_LOGS) {
|
|
145
|
+
this._shutdown();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
137
148
|
event.eventIndex = this._currentEventIndex++;
|
|
138
149
|
// Update the session data
|
|
139
150
|
this._sessionData.clickCount += data.clickCount;
|
|
@@ -16,6 +16,7 @@ export type RRWebPayload = {
|
|
|
16
16
|
};
|
|
17
17
|
export declare const REPLAY_ENQUEUE_TRIGGER_BYTES: number;
|
|
18
18
|
export declare const MAX_INDIVIDUAL_EVENT_BYTES: number;
|
|
19
|
+
export declare const MAX_LOGS = 10000;
|
|
19
20
|
export declare function _makeLoggableRrwebEvent(slice: string, payload: string, sessionID: string, data: ReplaySessionData): StatsigEvent & {
|
|
20
21
|
metadata: RRWebPayload;
|
|
21
22
|
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports._appendSlicedMetadata = exports._slicePayload = exports._makeLoggableRrwebEvent = exports.MAX_INDIVIDUAL_EVENT_BYTES = exports.REPLAY_ENQUEUE_TRIGGER_BYTES = void 0;
|
|
3
|
+
exports._appendSlicedMetadata = exports._slicePayload = exports._makeLoggableRrwebEvent = exports.MAX_LOGS = exports.MAX_INDIVIDUAL_EVENT_BYTES = exports.REPLAY_ENQUEUE_TRIGGER_BYTES = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
5
|
const REPLAY_SLICE_BYTES = 1024 * 1024; // 1 MB
|
|
6
6
|
exports.REPLAY_ENQUEUE_TRIGGER_BYTES = 1024 * 10; // 10 KB
|
|
7
7
|
exports.MAX_INDIVIDUAL_EVENT_BYTES = 1024 * 1024 * 10; // 10 MB
|
|
8
|
+
exports.MAX_LOGS = 10000; // 10K logs
|
|
8
9
|
function _makeLoggableRrwebEvent(slice, payload, sessionID, data) {
|
|
9
10
|
const metadata = {
|
|
10
11
|
session_start_ts: String(data.startTime),
|
|
@@ -23,6 +23,12 @@ export declare class TriggeredSessionReplay extends SessionReplayBase {
|
|
|
23
23
|
private _runningEventData;
|
|
24
24
|
private _isActiveRecording;
|
|
25
25
|
constructor(client: PrecomputedEvaluationsInterface, options?: TriggeredSessionReplayOptions);
|
|
26
|
+
private _subscribeToClientEvents;
|
|
27
|
+
private _subscribeToValuesUpdated;
|
|
28
|
+
private _subscribeToLogEventCalled;
|
|
29
|
+
private _subscribeToGateEvaluation;
|
|
30
|
+
private _subscribeToExperimentEvaluation;
|
|
31
|
+
private _tryStartExposureRecording;
|
|
26
32
|
startRecording(): void;
|
|
27
33
|
forceStartRecording(): void;
|
|
28
34
|
stopRecording(): void;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.TriggeredSessionReplay = exports.stopRecording = exports.forceStartRecording = exports.startRecording = exports.runStatsigSessionReplay = exports.StatsigTriggeredSessionReplayPlugin = void 0;
|
|
4
4
|
const client_core_1 = require("@statsig/client-core");
|
|
5
5
|
const SessionReplayBase_1 = require("./SessionReplayBase");
|
|
6
|
+
const SessionReplayUtils_1 = require("./SessionReplayUtils");
|
|
6
7
|
class StatsigTriggeredSessionReplayPlugin {
|
|
7
8
|
constructor(options) {
|
|
8
9
|
this.options = options;
|
|
@@ -47,6 +48,21 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
47
48
|
super(client, options);
|
|
48
49
|
this._runningEventData = [];
|
|
49
50
|
this._isActiveRecording = false;
|
|
51
|
+
this._subscribeToClientEvents(options);
|
|
52
|
+
if (options === null || options === void 0 ? void 0 : options.autoStartRecording) {
|
|
53
|
+
this._attemptToStartRecording((_a = this._options) === null || _a === void 0 ? void 0 : _a.forceRecording);
|
|
54
|
+
}
|
|
55
|
+
else if (options === null || options === void 0 ? void 0 : options.keepRollingWindow) {
|
|
56
|
+
this._attemptToStartRollingWindow();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
_subscribeToClientEvents(options) {
|
|
60
|
+
this._subscribeToValuesUpdated(options);
|
|
61
|
+
this._subscribeToLogEventCalled();
|
|
62
|
+
this._subscribeToGateEvaluation();
|
|
63
|
+
this._subscribeToExperimentEvaluation();
|
|
64
|
+
}
|
|
65
|
+
_subscribeToValuesUpdated(options) {
|
|
50
66
|
this._client.$on('values_updated', () => {
|
|
51
67
|
var _a;
|
|
52
68
|
if (!this._wasStopped) {
|
|
@@ -58,6 +74,8 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
58
74
|
}
|
|
59
75
|
}
|
|
60
76
|
});
|
|
77
|
+
}
|
|
78
|
+
_subscribeToLogEventCalled() {
|
|
61
79
|
this._client.$on('log_event_called', (event) => {
|
|
62
80
|
var _a;
|
|
63
81
|
if (this._wasStopped) {
|
|
@@ -73,6 +91,9 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
73
91
|
if (trigger == null) {
|
|
74
92
|
return;
|
|
75
93
|
}
|
|
94
|
+
if (trigger.passes_sampling === false) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
76
97
|
const targetValues = trigger.values;
|
|
77
98
|
if (targetValues == null) {
|
|
78
99
|
this._attemptToStartRecording(true);
|
|
@@ -83,36 +104,44 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
83
104
|
return;
|
|
84
105
|
}
|
|
85
106
|
});
|
|
107
|
+
}
|
|
108
|
+
_subscribeToGateEvaluation() {
|
|
86
109
|
this._client.$on('gate_evaluation', (event) => {
|
|
87
|
-
|
|
88
|
-
if (this._wasStopped) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
const values = this._client.getContext().values;
|
|
92
|
-
const passedTargeting = values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting;
|
|
93
|
-
if (passedTargeting === false ||
|
|
94
|
-
(values === null || values === void 0 ? void 0 : values.session_recording_exposure_triggers) == null) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const trigger = (_a = values.session_recording_exposure_triggers[event.gate.name]) !== null && _a !== void 0 ? _a : values.session_recording_exposure_triggers[(0, client_core_1._DJB2)(event.gate.name)];
|
|
98
|
-
if (trigger == null) {
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
const targetValues = trigger.values;
|
|
102
|
-
if (targetValues == null) {
|
|
103
|
-
this._attemptToStartRecording(true);
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (targetValues.includes(String((_b = event.gate.value) !== null && _b !== void 0 ? _b : false))) {
|
|
107
|
-
this._attemptToStartRecording(true);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
+
this._tryStartExposureRecording(event.gate.name, String(event.gate.value));
|
|
110
111
|
});
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
}
|
|
113
|
+
_subscribeToExperimentEvaluation() {
|
|
114
|
+
this._client.$on('experiment_evaluation', (event) => {
|
|
115
|
+
var _a;
|
|
116
|
+
this._tryStartExposureRecording(event.experiment.name, (_a = event.experiment.groupName) !== null && _a !== void 0 ? _a : '');
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
_tryStartExposureRecording(name, value) {
|
|
120
|
+
var _a;
|
|
121
|
+
if (this._wasStopped) {
|
|
122
|
+
return;
|
|
113
123
|
}
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
const values = this._client.getContext().values;
|
|
125
|
+
const passedTargeting = values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting;
|
|
126
|
+
if (passedTargeting === false ||
|
|
127
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_exposure_triggers) == null) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const trigger = (_a = values.session_recording_exposure_triggers[name]) !== null && _a !== void 0 ? _a : values.session_recording_exposure_triggers[(0, client_core_1._DJB2)(name)];
|
|
131
|
+
if (trigger == null) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (trigger.passes_sampling === false) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const targetValues = trigger.values;
|
|
138
|
+
if (targetValues == null) {
|
|
139
|
+
this._attemptToStartRecording(true);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (targetValues.includes(value)) {
|
|
143
|
+
this._attemptToStartRecording(true);
|
|
144
|
+
return;
|
|
116
145
|
}
|
|
117
146
|
}
|
|
118
147
|
startRecording() {
|
|
@@ -200,7 +229,14 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
200
229
|
}
|
|
201
230
|
_attemptToStartRecording(force = false) {
|
|
202
231
|
var _a, _b;
|
|
232
|
+
if (this._totalLogs >= SessionReplayUtils_1.MAX_LOGS) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
203
235
|
const values = this._client.getContext().values;
|
|
236
|
+
if ((values === null || values === void 0 ? void 0 : values.recording_blocked) === true) {
|
|
237
|
+
this._shutdown();
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
204
240
|
if (!force && (values === null || values === void 0 ? void 0 : values.can_record_session) !== true) {
|
|
205
241
|
this._shutdown();
|
|
206
242
|
return;
|