@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@statsig/session-replay",
3
- "version": "3.16.2",
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.16.2"
13
+ "@statsig/client-core": "3.17.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@rrweb/types": "2.0.0-alpha.14"
@@ -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;
@@ -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.stop();
108
- client_core_1.StatsigMetadataProvider.add({ isRecordingSession: 'false' });
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
- var _a, _b;
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
- if (options === null || options === void 0 ? void 0 : options.autoStartRecording) {
112
- this._attemptToStartRecording((_a = this._options) === null || _a === void 0 ? void 0 : _a.forceRecording);
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
- else if (options === null || options === void 0 ? void 0 : options.keepRollingWindow) {
115
- this._attemptToStartRollingWindow();
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;