@statsig/session-replay 3.16.1 → 3.16.2

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.1",
3
+ "version": "3.16.2",
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.1"
13
+ "@statsig/client-core": "3.16.2"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@rrweb/types": "2.0.0-alpha.14"
@@ -39,6 +39,10 @@ class SessionReplay extends SessionReplayBase_1.SessionReplayBase {
39
39
  this._shutdown();
40
40
  return;
41
41
  }
42
+ if ((values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting) === false) {
43
+ this._shutdown();
44
+ return;
45
+ }
42
46
  if (this._replayer.isRecording()) {
43
47
  return;
44
48
  }
@@ -28,6 +28,7 @@ export declare abstract class SessionReplayBase {
28
28
  protected _getSessionIdFromClient(): string;
29
29
  protected abstract _shutdown(endReason?: EndReason): void;
30
30
  protected _shutdownImpl(endReason?: EndReason): void;
31
+ private _makeEmptySessionData;
31
32
  protected _onRecordingEvent(event: ReplayEvent, data: ReplaySessionData): void;
32
33
  }
33
34
  export {};
@@ -8,7 +8,7 @@ const SizeOf_1 = require("./SizeOf");
8
8
  class SessionReplayBase {
9
9
  constructor(client, options) {
10
10
  this._sessionData = {
11
- startTime: 0,
11
+ startTime: -1,
12
12
  endTime: 0,
13
13
  clickCount: 0,
14
14
  };
@@ -106,13 +106,24 @@ class SessionReplayBase {
106
106
  _shutdownImpl(endReason) {
107
107
  this._replayer.stop();
108
108
  client_core_1.StatsigMetadataProvider.add({ isRecordingSession: 'false' });
109
- this._currentEventIndex = 0;
110
109
  if (this._events.length === 0) {
110
+ // only reset if session expired otherwise we might start recording again
111
+ if (endReason === 'session_expired') {
112
+ this._currentEventIndex = 0;
113
+ this._sessionData = this._makeEmptySessionData();
114
+ }
111
115
  return;
112
116
  }
113
117
  this._logRecording(endReason);
114
- this._sessionData = {
115
- startTime: 0,
118
+ // only reset if session expired otherwise we might start recording again
119
+ if (endReason === 'session_expired') {
120
+ this._currentEventIndex = 0;
121
+ this._sessionData = this._makeEmptySessionData();
122
+ }
123
+ }
124
+ _makeEmptySessionData() {
125
+ return {
126
+ startTime: -1,
116
127
  endTime: 0,
117
128
  clickCount: 0,
118
129
  };
@@ -126,7 +137,10 @@ class SessionReplayBase {
126
137
  event.eventIndex = this._currentEventIndex++;
127
138
  // Update the session data
128
139
  this._sessionData.clickCount += data.clickCount;
129
- this._sessionData.startTime = Math.min(this._sessionData.startTime, data.startTime);
140
+ this._sessionData.startTime =
141
+ this._sessionData.startTime === -1
142
+ ? data.startTime
143
+ : Math.min(this._sessionData.startTime, data.startTime);
130
144
  this._sessionData.endTime = Math.max(this._sessionData.endTime, data.endTime);
131
145
  const eventApproxSize = (0, SizeOf_1._fastApproxSizeOf)(event, SessionReplayUtils_1.MAX_INDIVIDUAL_EVENT_BYTES);
132
146
  if (eventApproxSize > SessionReplayUtils_1.MAX_INDIVIDUAL_EVENT_BYTES) {
@@ -15,7 +15,6 @@ export declare class SessionReplayClient {
15
15
  private _stopCallback?;
16
16
  private _startTimestamp;
17
17
  private _endTimestamp;
18
- private _eventCounter;
19
18
  record(callback: (latest: ReplayEvent, data: ReplaySessionData, isCheckout?: boolean) => void, config: RRWebConfig, stopCallback?: () => void, keepRollingWindow?: boolean): void;
20
19
  stop(): void;
21
20
  isRecording(): boolean;
@@ -4,12 +4,11 @@ exports.SessionReplayClient = void 0;
4
4
  const rrweb = require("rrweb");
5
5
  const client_core_1 = require("@statsig/client-core");
6
6
  const TIMEOUT_MS = 1000 * 60 * 60 * 4; // 4 hours
7
- const CHECKOUT_WINDOW_MS = 1000 * 60; // 1 minute
7
+ const CHECKOUT_WINDOW_MS = 1000 * 30; // 30 seconds
8
8
  class SessionReplayClient {
9
9
  constructor() {
10
10
  this._startTimestamp = null;
11
11
  this._endTimestamp = null;
12
- this._eventCounter = 0;
13
12
  }
14
13
  record(callback, config, stopCallback, keepRollingWindow = false) {
15
14
  if ((0, client_core_1._getDocumentSafe)() == null) {
@@ -18,7 +17,6 @@ class SessionReplayClient {
18
17
  // Always reset session id and tracking fields for a new recording
19
18
  this._startTimestamp = null;
20
19
  this._endTimestamp = null;
21
- this._eventCounter = 0;
22
20
  this._stopCallback = stopCallback;
23
21
  if (this._stopFn) {
24
22
  return;
@@ -17,6 +17,7 @@ export declare class StatsigTriggeredSessionReplayPlugin implements StatsigPlugi
17
17
  }
18
18
  export declare function runStatsigSessionReplay(client: PrecomputedEvaluationsInterface, options?: SessionReplayOptions): void;
19
19
  export declare function startRecording(sdkKey: string): void;
20
+ export declare function forceStartRecording(sdkKey: string): void;
20
21
  export declare function stopRecording(sdkKey: string): void;
21
22
  export declare class TriggeredSessionReplay extends SessionReplayBase {
22
23
  private _runningEventData;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TriggeredSessionReplay = exports.stopRecording = exports.startRecording = exports.runStatsigSessionReplay = exports.StatsigTriggeredSessionReplayPlugin = void 0;
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
6
  class StatsigTriggeredSessionReplayPlugin {
@@ -25,6 +25,14 @@ function startRecording(sdkKey) {
25
25
  }
26
26
  }
27
27
  exports.startRecording = startRecording;
28
+ function forceStartRecording(sdkKey) {
29
+ var _a, _b;
30
+ 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];
31
+ if (inst instanceof TriggeredSessionReplay) {
32
+ inst.forceStartRecording();
33
+ }
34
+ }
35
+ exports.forceStartRecording = forceStartRecording;
28
36
  function stopRecording(sdkKey) {
29
37
  var _a, _b;
30
38
  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];
@@ -50,6 +58,56 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
50
58
  }
51
59
  }
52
60
  });
61
+ this._client.$on('log_event_called', (event) => {
62
+ var _a;
63
+ if (this._wasStopped) {
64
+ return;
65
+ }
66
+ const values = this._client.getContext().values;
67
+ const passedTargeting = values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting;
68
+ if (passedTargeting === false ||
69
+ (values === null || values === void 0 ? void 0 : values.session_recording_event_triggers) == null) {
70
+ return;
71
+ }
72
+ const trigger = values.session_recording_event_triggers[event.event.eventName];
73
+ if (trigger == null) {
74
+ return;
75
+ }
76
+ const targetValues = trigger.values;
77
+ if (targetValues == null) {
78
+ this._attemptToStartRecording(true);
79
+ return;
80
+ }
81
+ if (targetValues.includes(String((_a = event.event.value) !== null && _a !== void 0 ? _a : ''))) {
82
+ this._attemptToStartRecording(true);
83
+ return;
84
+ }
85
+ });
86
+ 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
+ });
53
111
  if (options === null || options === void 0 ? void 0 : options.autoStartRecording) {
54
112
  this._attemptToStartRecording((_a = this._options) === null || _a === void 0 ? void 0 : _a.forceRecording);
55
113
  }
@@ -79,7 +137,10 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
79
137
  for (let i = 0; i < currentEvents.length; i++) {
80
138
  currentEvents[i].event.eventIndex = i;
81
139
  this._sessionData.clickCount += currentEvents[i].data.clickCount;
82
- this._sessionData.startTime = Math.min(this._sessionData.startTime, currentEvents[i].data.startTime);
140
+ this._sessionData.startTime =
141
+ this._sessionData.startTime === -1
142
+ ? currentEvents[i].data.startTime
143
+ : Math.min(this._sessionData.startTime, currentEvents[i].data.startTime);
83
144
  this._sessionData.endTime = Math.max(this._sessionData.endTime, currentEvents[i].data.endTime);
84
145
  }
85
146
  this._events = currentEvents.map((e) => e.event);
@@ -90,6 +151,8 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
90
151
  else {
91
152
  this._logRecording();
92
153
  }
154
+ // stop recording and since it will be started again
155
+ this._replayer.stop();
93
156
  }
94
157
  _shutdown(endReason) {
95
158
  this._isActiveRecording = false;
@@ -124,7 +187,7 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
124
187
  _attemptToStartRollingWindow() {
125
188
  var _a, _b;
126
189
  const values = this._client.getContext().values;
127
- if ((values === null || values === void 0 ? void 0 : values.can_record_session) !== true) {
190
+ if ((values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting) === false) {
128
191
  this._shutdown();
129
192
  return;
130
193
  }
@@ -142,6 +205,10 @@ class TriggeredSessionReplay extends SessionReplayBase_1.SessionReplayBase {
142
205
  this._shutdown();
143
206
  return;
144
207
  }
208
+ if ((values === null || values === void 0 ? void 0 : values.passes_session_recording_targeting) === false) {
209
+ this._shutdown();
210
+ return;
211
+ }
145
212
  this._handleStartActiveRecording();
146
213
  this._wasStopped = false;
147
214
  client_core_1.StatsigMetadataProvider.add({ isRecordingSession: 'true' });
package/src/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { SessionReplay, StatsigSessionReplayPlugin, runStatsigSessionReplay } from './SessionReplay';
2
2
  import { SessionReplayClient } from './SessionReplayClient';
3
- import { StatsigTriggeredSessionReplayPlugin, startRecording, stopRecording } from './TriggeredSessionReplay';
3
+ import { StatsigTriggeredSessionReplayPlugin, forceStartRecording, startRecording, stopRecording } from './TriggeredSessionReplay';
4
4
  export type { ReplaySessionData as ReplayData, ReplayEvent, } from './SessionReplayClient';
5
- export { SessionReplayClient, SessionReplay, runStatsigSessionReplay, StatsigSessionReplayPlugin, StatsigTriggeredSessionReplayPlugin, startRecording, stopRecording, };
5
+ export { SessionReplayClient, SessionReplay, runStatsigSessionReplay, StatsigSessionReplayPlugin, StatsigTriggeredSessionReplayPlugin, startRecording, stopRecording, forceStartRecording, };
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.stopRecording = exports.startRecording = exports.StatsigTriggeredSessionReplayPlugin = exports.StatsigSessionReplayPlugin = exports.runStatsigSessionReplay = exports.SessionReplay = exports.SessionReplayClient = void 0;
3
+ exports.forceStartRecording = exports.stopRecording = exports.startRecording = exports.StatsigTriggeredSessionReplayPlugin = exports.StatsigSessionReplayPlugin = exports.runStatsigSessionReplay = exports.SessionReplay = exports.SessionReplayClient = void 0;
4
4
  const client_core_1 = require("@statsig/client-core");
5
5
  const SessionReplay_1 = require("./SessionReplay");
6
6
  Object.defineProperty(exports, "SessionReplay", { enumerable: true, get: function () { return SessionReplay_1.SessionReplay; } });
@@ -10,6 +10,7 @@ const SessionReplayClient_1 = require("./SessionReplayClient");
10
10
  Object.defineProperty(exports, "SessionReplayClient", { enumerable: true, get: function () { return SessionReplayClient_1.SessionReplayClient; } });
11
11
  const TriggeredSessionReplay_1 = require("./TriggeredSessionReplay");
12
12
  Object.defineProperty(exports, "StatsigTriggeredSessionReplayPlugin", { enumerable: true, get: function () { return TriggeredSessionReplay_1.StatsigTriggeredSessionReplayPlugin; } });
13
+ Object.defineProperty(exports, "forceStartRecording", { enumerable: true, get: function () { return TriggeredSessionReplay_1.forceStartRecording; } });
13
14
  Object.defineProperty(exports, "startRecording", { enumerable: true, get: function () { return TriggeredSessionReplay_1.startRecording; } });
14
15
  Object.defineProperty(exports, "stopRecording", { enumerable: true, get: function () { return TriggeredSessionReplay_1.stopRecording; } });
15
16
  Object.assign((0, client_core_1._getStatsigGlobal)(), {