@statsig/session-replay 3.31.0 → 3.31.1
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statsig/session-replay",
|
|
3
|
-
"version": "3.31.
|
|
3
|
+
"version": "3.31.1",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"homepage": "https://github.com/statsig-io/js-client-monorepo",
|
|
6
6
|
"repository": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"rrweb": "2.0.0-alpha.17",
|
|
13
13
|
"@rrweb/record": "2.0.0-alpha.17",
|
|
14
|
-
"@statsig/client-core": "3.31.
|
|
14
|
+
"@statsig/client-core": "3.31.1"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@rrweb/types": "2.0.0-alpha.16"
|
package/src/SessionReplay.js
CHANGED
|
@@ -24,30 +24,25 @@ function runStatsigSessionReplay(client, options) {
|
|
|
24
24
|
exports.runStatsigSessionReplay = runStatsigSessionReplay;
|
|
25
25
|
class SessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
26
26
|
constructor(client, options) {
|
|
27
|
-
var _a
|
|
28
|
-
|
|
29
|
-
const privacySettings = (_a = client.getContext().values) === null || _a === void 0 ? void 0 : _a.session_recording_privacy_settings;
|
|
30
|
-
if (privacySettings) {
|
|
31
|
-
newOptions = (0, SessionReplayUtils_1.getNewOptionsWithPrivacySettings)(newOptions !== null && newOptions !== void 0 ? newOptions : {}, privacySettings);
|
|
32
|
-
}
|
|
33
|
-
super(client, newOptions);
|
|
27
|
+
var _a;
|
|
28
|
+
super(client, options);
|
|
34
29
|
this._client.$on('values_updated', () => {
|
|
35
30
|
var _a;
|
|
36
31
|
if (!this._wasStopped) {
|
|
37
32
|
this._attemptToStartRecording((_a = this._options) === null || _a === void 0 ? void 0 : _a.forceRecording);
|
|
38
33
|
}
|
|
39
34
|
});
|
|
40
|
-
this._attemptToStartRecording((
|
|
35
|
+
this._attemptToStartRecording((_a = this._options) === null || _a === void 0 ? void 0 : _a.forceRecording);
|
|
41
36
|
}
|
|
42
37
|
_shutdown(endReason) {
|
|
43
38
|
super._shutdownImpl(endReason);
|
|
44
39
|
}
|
|
45
40
|
_attemptToStartRecording(force = false) {
|
|
46
|
-
var _a, _b;
|
|
41
|
+
var _a, _b, _c, _d, _e;
|
|
47
42
|
if (this._totalLogs >= SessionReplayUtils_1.MAX_LOGS) {
|
|
48
43
|
return;
|
|
49
44
|
}
|
|
50
|
-
const values = this._client.
|
|
45
|
+
const values = this._client.getContextHandle().values;
|
|
51
46
|
if ((values === null || values === void 0 ? void 0 : values.recording_blocked) === true) {
|
|
52
47
|
this._shutdown();
|
|
53
48
|
return;
|
|
@@ -63,9 +58,17 @@ class SessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
63
58
|
if (this._replayer.isRecording()) {
|
|
64
59
|
return;
|
|
65
60
|
}
|
|
61
|
+
const usePrivacySettings = (values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings) &&
|
|
62
|
+
((values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.privacy_mode) !== 'min' ||
|
|
63
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.blocked_elements) ||
|
|
64
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.masked_elements) ||
|
|
65
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.unmasked_elements));
|
|
66
|
+
const newRRWebConfig = usePrivacySettings
|
|
67
|
+
? (0, SessionReplayUtils_1.getNewRRWebConfigWithPrivacySettings)((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.rrwebConfig) !== null && _b !== void 0 ? _b : {}, (_c = values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings) !== null && _c !== void 0 ? _c : {})
|
|
68
|
+
: (_e = (_d = this._options) === null || _d === void 0 ? void 0 : _d.rrwebConfig) !== null && _e !== void 0 ? _e : {};
|
|
66
69
|
this._wasStopped = false;
|
|
67
70
|
client_core_1.StatsigMetadataProvider.add({ isRecordingSession: 'true' });
|
|
68
|
-
this._replayer.record((e, d) => this._onRecordingEvent(e, d),
|
|
71
|
+
this._replayer.record((e, d) => this._onRecordingEvent(e, d), newRRWebConfig, () => {
|
|
69
72
|
this._shutdown();
|
|
70
73
|
});
|
|
71
74
|
}
|
|
@@ -30,7 +30,7 @@ export declare abstract class SessionReplayBase {
|
|
|
30
30
|
protected _subscribeToVisibilityChanged(): void;
|
|
31
31
|
protected _logRecordingWithSessionID(sessionID: string, endReason?: EndReason): void;
|
|
32
32
|
protected _bumpSessionIdleTimerAndLogRecording(): void;
|
|
33
|
-
protected _getSessionIdFromClient(): string;
|
|
33
|
+
protected _getSessionIdFromClient(bumpSession?: boolean): string;
|
|
34
34
|
protected abstract _shutdown(endReason?: EndReason): void;
|
|
35
35
|
protected _shutdownImpl(endReason?: EndReason): void;
|
|
36
36
|
private _makeEmptySessionData;
|
package/src/SessionReplayBase.js
CHANGED
|
@@ -39,7 +39,7 @@ class SessionReplayBase {
|
|
|
39
39
|
this._totalLogs = 0;
|
|
40
40
|
this._client = client;
|
|
41
41
|
this._options = options;
|
|
42
|
-
const { sdkKey, errorBoundary } = this._client.
|
|
42
|
+
const { sdkKey, errorBoundary } = this._client.getContextHandle();
|
|
43
43
|
this._errorBoundary = errorBoundary;
|
|
44
44
|
this._errorBoundary.wrap(this);
|
|
45
45
|
this._replayer = new SessionReplayClient_1.SessionReplayClient();
|
|
@@ -91,7 +91,7 @@ class SessionReplayBase {
|
|
|
91
91
|
}
|
|
92
92
|
_subscribeToVisibilityChanged() {
|
|
93
93
|
// Note: this exists as a separate function to ensure closure scope only contains `sdkKey`
|
|
94
|
-
const { sdkKey } = this._client.
|
|
94
|
+
const { sdkKey } = this._client.getContextHandle();
|
|
95
95
|
(0, client_core_1._subscribeToVisiblityChanged)((vis) => {
|
|
96
96
|
var _a, _b;
|
|
97
97
|
const inst = (_b = (_a = (0, client_core_1._getStatsigGlobal)()) === null || _a === void 0 ? void 0 : _a.srInstances) === null || _b === void 0 ? void 0 : _b[sdkKey];
|
|
@@ -110,7 +110,7 @@ class SessionReplayBase {
|
|
|
110
110
|
const slicedID = parts.length > 1 ? (0, client_core_1.getUUID)() : null;
|
|
111
111
|
for (let i = 0; i < parts.length; i++) {
|
|
112
112
|
const slice = parts[i];
|
|
113
|
-
const event = (0, SessionReplayUtils_1._makeLoggableRrwebEvent)(slice, payload, sessionID, data, this._client.
|
|
113
|
+
const event = (0, SessionReplayUtils_1._makeLoggableRrwebEvent)(slice, payload, sessionID, data, this._client.getContextHandle().sdkInstanceID);
|
|
114
114
|
if (slicedID != null) {
|
|
115
115
|
(0, SessionReplayUtils_1._appendSlicedMetadata)(event.metadata, slicedID, i, parts.length, slice.length);
|
|
116
116
|
}
|
|
@@ -134,8 +134,9 @@ class SessionReplayBase {
|
|
|
134
134
|
this._getSessionIdFromClient();
|
|
135
135
|
this._logRecording();
|
|
136
136
|
}
|
|
137
|
-
_getSessionIdFromClient() {
|
|
138
|
-
return this._client.
|
|
137
|
+
_getSessionIdFromClient(bumpSession) {
|
|
138
|
+
return this._client.getContextHandle().getSession(bumpSession).data
|
|
139
|
+
.sessionID;
|
|
139
140
|
}
|
|
140
141
|
_shutdownImpl(endReason) {
|
|
141
142
|
if (this._replayer.isRecording()) {
|
|
@@ -170,7 +171,7 @@ class SessionReplayBase {
|
|
|
170
171
|
}
|
|
171
172
|
_onRecordingEvent(event, data) {
|
|
172
173
|
// The session has expired so we should stop recording
|
|
173
|
-
if (this._currentSessionID !== this._getSessionIdFromClient()) {
|
|
174
|
+
if (this._currentSessionID !== this._getSessionIdFromClient(false)) {
|
|
174
175
|
this._shutdown('session_expired');
|
|
175
176
|
return;
|
|
176
177
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { SessionReplayPrivacySettings, StatsigEvent } from '@statsig/client-core';
|
|
2
|
-
import { ReplaySessionData } from './SessionReplayClient';
|
|
3
|
-
import { SessionReplayOptions } from './TriggeredSessionReplay';
|
|
2
|
+
import { RRWebConfig, ReplaySessionData } from './SessionReplayClient';
|
|
4
3
|
export type RRWebPayload = {
|
|
5
4
|
session_start_ts: string;
|
|
6
5
|
session_end_ts: string;
|
|
@@ -24,4 +23,4 @@ export declare function _makeLoggableRrwebEvent(slice: string, payload: string,
|
|
|
24
23
|
};
|
|
25
24
|
export declare function _slicePayload(payload: string): string[];
|
|
26
25
|
export declare function _appendSlicedMetadata(metadata: RRWebPayload, slicedID: string, sliceIndex: number, sliceCount: number, sliceByteSize: number): void;
|
|
27
|
-
export declare function
|
|
26
|
+
export declare function getNewRRWebConfigWithPrivacySettings(originalOptions: RRWebConfig, privacySettings: SessionReplayPrivacySettings): RRWebConfig;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getNewRRWebConfigWithPrivacySettings = 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
|
|
@@ -38,16 +38,39 @@ function _appendSlicedMetadata(metadata, slicedID, sliceIndex, sliceCount, slice
|
|
|
38
38
|
metadata.slice_byte_size = String(sliceByteSize);
|
|
39
39
|
}
|
|
40
40
|
exports._appendSlicedMetadata = _appendSlicedMetadata;
|
|
41
|
-
function
|
|
41
|
+
function getNewRRWebConfigWithPrivacySettings(originalOptions, privacySettings) {
|
|
42
42
|
const maskValue = (value) => {
|
|
43
43
|
return value.replace(/./g, '*');
|
|
44
44
|
};
|
|
45
|
+
function getNearestPrivacyMatch(element, maskedSelectors = [], unmaskedSelectors = []) {
|
|
46
|
+
let current = element;
|
|
47
|
+
while (current !== null) {
|
|
48
|
+
if (maskedSelectors.some((sel) => current === null || current === void 0 ? void 0 : current.matches(sel))) {
|
|
49
|
+
return 'masked';
|
|
50
|
+
}
|
|
51
|
+
if (unmaskedSelectors.some((sel) => current === null || current === void 0 ? void 0 : current.matches(sel))) {
|
|
52
|
+
return 'unmasked';
|
|
53
|
+
}
|
|
54
|
+
current = current.parentElement;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
45
58
|
const blockSelector = privacySettings.blocked_elements
|
|
46
59
|
? privacySettings.blocked_elements.join(', ')
|
|
47
60
|
: undefined;
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
61
|
+
const maskTextFn = (value, element) => {
|
|
62
|
+
if (!element) {
|
|
63
|
+
return privacySettings.privacy_mode === 'max' ? maskValue(value) : value;
|
|
64
|
+
}
|
|
65
|
+
const nearest = getNearestPrivacyMatch(element, privacySettings.masked_elements, privacySettings.unmasked_elements);
|
|
66
|
+
if (nearest === 'masked') {
|
|
67
|
+
return maskValue(value);
|
|
68
|
+
}
|
|
69
|
+
if (nearest === 'unmasked') {
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
return privacySettings.privacy_mode === 'max' ? maskValue(value) : value;
|
|
73
|
+
};
|
|
51
74
|
const maskInputFn = (value, element) => {
|
|
52
75
|
var _a, _b;
|
|
53
76
|
if ((_a = privacySettings.masked_elements) === null || _a === void 0 ? void 0 : _a.some((sel) => element.closest(sel))) {
|
|
@@ -61,7 +84,6 @@ function getNewOptionsWithPrivacySettings(originalOptions, privacySettings) {
|
|
|
61
84
|
? maskValue(value)
|
|
62
85
|
: value;
|
|
63
86
|
};
|
|
64
|
-
return Object.assign(Object.assign({}, originalOptions), {
|
|
65
|
-
maskTextSelector, maskAllInputs: true, maskInputOptions: undefined, blockSelector }) });
|
|
87
|
+
return Object.assign(Object.assign({}, originalOptions), { maskTextFn: maskTextFn, maskInputFn, maskTextSelector: '*', maskAllInputs: true, maskInputOptions: undefined, blockSelector });
|
|
66
88
|
}
|
|
67
|
-
exports.
|
|
89
|
+
exports.getNewRRWebConfigWithPrivacySettings = getNewRRWebConfigWithPrivacySettings;
|
|
@@ -22,20 +22,15 @@ function runStatsigTriggeredSessionReplay(client, options) {
|
|
|
22
22
|
exports.runStatsigTriggeredSessionReplay = runStatsigTriggeredSessionReplay;
|
|
23
23
|
class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
24
24
|
constructor(client, options) {
|
|
25
|
-
var _a
|
|
26
|
-
|
|
27
|
-
const privacySettings = (_a = client.getContext().values) === null || _a === void 0 ? void 0 : _a.session_recording_privacy_settings;
|
|
28
|
-
if (privacySettings) {
|
|
29
|
-
newOptions = (0, SessionReplayUtils_1.getNewOptionsWithPrivacySettings)(newOptions !== null && newOptions !== void 0 ? newOptions : {}, privacySettings);
|
|
30
|
-
}
|
|
31
|
-
super(client, newOptions);
|
|
25
|
+
var _a;
|
|
26
|
+
super(client, options);
|
|
32
27
|
this._runningEventData = [];
|
|
33
28
|
this._isActiveRecording = false;
|
|
34
|
-
this._subscribeToClientEvents(
|
|
35
|
-
if (
|
|
36
|
-
this._attemptToStartRecording((
|
|
29
|
+
this._subscribeToClientEvents(options);
|
|
30
|
+
if (options === null || options === void 0 ? void 0 : options.autoStartRecording) {
|
|
31
|
+
this._attemptToStartRecording((_a = this._options) === null || _a === void 0 ? void 0 : _a.forceRecording);
|
|
37
32
|
}
|
|
38
|
-
else if (
|
|
33
|
+
else if (options === null || options === void 0 ? void 0 : options.keepRollingWindow) {
|
|
39
34
|
this._attemptToStartRollingWindow();
|
|
40
35
|
}
|
|
41
36
|
}
|
|
@@ -64,7 +59,7 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
64
59
|
if (this._wasStopped) {
|
|
65
60
|
return;
|
|
66
61
|
}
|
|
67
|
-
const values = this._client.
|
|
62
|
+
const values = this._client.getContextHandle().values;
|
|
68
63
|
const passedTargeting = values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting;
|
|
69
64
|
if (passedTargeting === false ||
|
|
70
65
|
(values === null || values === void 0 ? void 0 : values.session_recording_event_triggers) == null) {
|
|
@@ -104,7 +99,7 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
104
99
|
if (this._wasStopped) {
|
|
105
100
|
return;
|
|
106
101
|
}
|
|
107
|
-
const values = this._client.
|
|
102
|
+
const values = this._client.getContextHandle().values;
|
|
108
103
|
const passedTargeting = values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting;
|
|
109
104
|
if (passedTargeting === false ||
|
|
110
105
|
(values === null || values === void 0 ? void 0 : values.session_recording_exposure_triggers) == null) {
|
|
@@ -201,7 +196,7 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
201
196
|
}
|
|
202
197
|
_attemptToStartRollingWindow() {
|
|
203
198
|
var _a, _b;
|
|
204
|
-
const values = this._client.
|
|
199
|
+
const values = this._client.getContextHandle().values;
|
|
205
200
|
if ((values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting) === false) {
|
|
206
201
|
this._shutdown();
|
|
207
202
|
return;
|
|
@@ -214,11 +209,11 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
214
209
|
}, true);
|
|
215
210
|
}
|
|
216
211
|
_attemptToStartRecording(force = false) {
|
|
217
|
-
var _a, _b;
|
|
212
|
+
var _a, _b, _c, _d, _e;
|
|
218
213
|
if (this._totalLogs >= SessionReplayUtils_1.MAX_LOGS) {
|
|
219
214
|
return;
|
|
220
215
|
}
|
|
221
|
-
const values = this._client.
|
|
216
|
+
const values = this._client.getContextHandle().values;
|
|
222
217
|
if ((values === null || values === void 0 ? void 0 : values.recording_blocked) === true) {
|
|
223
218
|
this._shutdown();
|
|
224
219
|
return;
|
|
@@ -237,7 +232,15 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
|
|
|
237
232
|
if (this._replayer.isRecording()) {
|
|
238
233
|
return;
|
|
239
234
|
}
|
|
240
|
-
|
|
235
|
+
const usePrivacySettings = (values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings) &&
|
|
236
|
+
((values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.privacy_mode) !== 'min' ||
|
|
237
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.blocked_elements) ||
|
|
238
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.masked_elements) ||
|
|
239
|
+
(values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings.unmasked_elements));
|
|
240
|
+
const newRRWebConfig = usePrivacySettings
|
|
241
|
+
? (0, SessionReplayUtils_1.getNewRRWebConfigWithPrivacySettings)((_b = (_a = this._options) === null || _a === void 0 ? void 0 : _a.rrwebConfig) !== null && _b !== void 0 ? _b : {}, (_c = values === null || values === void 0 ? void 0 : values.session_recording_privacy_settings) !== null && _c !== void 0 ? _c : {})
|
|
242
|
+
: (_e = (_d = this._options) === null || _d === void 0 ? void 0 : _d.rrwebConfig) !== null && _e !== void 0 ? _e : {};
|
|
243
|
+
this._replayer.record((e, d, isCheckOut) => this._onRecordingEvent(e, d, isCheckOut), newRRWebConfig, () => {
|
|
241
244
|
this._shutdown();
|
|
242
245
|
});
|
|
243
246
|
}
|