@statsig/session-replay 3.3.0-beta.3 → 3.4.0-beta.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,9 +1,9 @@
1
1
  {
2
2
  "name": "@statsig/session-replay",
3
- "version": "3.3.0-beta.3",
3
+ "version": "3.4.0-beta.1",
4
4
  "dependencies": {
5
5
  "rrweb": "2.0.0-alpha.12",
6
- "@statsig/client-core": "3.3.0-beta.3"
6
+ "@statsig/client-core": "3.4.0-beta.1"
7
7
  },
8
8
  "devDependencies": {
9
9
  "@rrweb/types": "2.0.0-alpha.12"
@@ -12,7 +12,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.SessionReplay = exports.runStatsigSessionReplay = exports.StatsigSessionReplayPlugin = void 0;
13
13
  const client_core_1 = require("@statsig/client-core");
14
14
  const SessionReplayClient_1 = require("./SessionReplayClient");
15
- const MAX_REPLAY_PAYLOAD_BYTES = 2048;
15
+ const SizeOf_1 = require("./SizeOf");
16
+ const REPLAY_ENQUEUE_TRIGGER_BYTES = 1024 * 10; // 10 KB
17
+ const REPLAY_SLICE_BYTES = 1024 * 1024; // 1 MB
18
+ const MAX_INDIVIDUAL_EVENT_BYTES = 1024 * 1024 * 10; // 10 MB
16
19
  class StatsigSessionReplayPlugin {
17
20
  constructor(options) {
18
21
  this.options = options;
@@ -79,15 +82,22 @@ class SessionReplay {
79
82
  }
80
83
  _onRecordingEvent(event, data) {
81
84
  this._sessionData = data;
85
+ const eventApproxSize = (0, SizeOf_1._fastApproxSizeOf)(event, MAX_INDIVIDUAL_EVENT_BYTES);
86
+ if (eventApproxSize > MAX_INDIVIDUAL_EVENT_BYTES) {
87
+ client_core_1.Log.warn(`SessionReplay event is too large (~${eventApproxSize} bytes) and will not be logged`, event);
88
+ return;
89
+ }
90
+ const approxArraySizeBefore = (0, SizeOf_1._fastApproxSizeOf)(this._events, REPLAY_ENQUEUE_TRIGGER_BYTES);
82
91
  this._events.push(event);
83
- const payload = JSON.stringify(this._events);
84
- if (payload.length > MAX_REPLAY_PAYLOAD_BYTES) {
85
- if ((0, client_core_1._isCurrentlyVisible)()) {
86
- this._bumpSessionIdleTimerAndLogRecording();
87
- }
88
- else {
89
- this._logRecording();
90
- }
92
+ if (approxArraySizeBefore + eventApproxSize <
93
+ REPLAY_ENQUEUE_TRIGGER_BYTES) {
94
+ return;
95
+ }
96
+ if ((0, client_core_1._isCurrentlyVisible)()) {
97
+ this._bumpSessionIdleTimerAndLogRecording();
98
+ }
99
+ else {
100
+ this._logRecording();
91
101
  }
92
102
  }
93
103
  _attemptToStartRecording(force = false) {
@@ -128,22 +138,24 @@ class SessionReplay {
128
138
  return;
129
139
  }
130
140
  const payload = JSON.stringify(this._events);
131
- const event = {
132
- eventName: 'statsig::session_recording',
133
- value: sessionID,
134
- metadata: {
135
- session_start_ts: String(data.startTime),
136
- session_end_ts: String(data.endTime),
137
- clicks_captured_cumulative: String(data.clickCount),
138
- rrweb_events: payload,
139
- rrweb_payload_size: String(payload.length),
140
- session_replay_sdk_version: client_core_1.SDK_VERSION,
141
- },
142
- };
143
- if (endReason) {
144
- event.metadata[endReason] = 'true';
141
+ const parts = _slicePayload(payload);
142
+ const slicedID = parts.length > 1 ? (0, client_core_1.getUUID)() : null;
143
+ for (let i = 0; i < parts.length; i++) {
144
+ const slice = parts[i];
145
+ const event = _makeLoggableRrwebEvent(slice, payload, sessionID, data);
146
+ if (slicedID != null) {
147
+ _appendSlicedMetadata(event.metadata, slicedID, i, parts.length, slice.length);
148
+ }
149
+ if (endReason) {
150
+ event.metadata[endReason] = 'true';
151
+ }
152
+ this._client.logEvent(event);
153
+ if (slicedID != null) {
154
+ this._client.flush().catch((e) => {
155
+ client_core_1.Log.error(e);
156
+ });
157
+ }
145
158
  }
146
- this._client.logEvent(event);
147
159
  this._events = [];
148
160
  }
149
161
  _bumpSessionIdleTimerAndLogRecording() {
@@ -157,3 +169,31 @@ class SessionReplay {
157
169
  }
158
170
  }
159
171
  exports.SessionReplay = SessionReplay;
172
+ function _slicePayload(payload) {
173
+ const parts = [];
174
+ for (let i = 0; i < payload.length; i += REPLAY_SLICE_BYTES) {
175
+ parts.push(payload.slice(i, i + REPLAY_SLICE_BYTES));
176
+ }
177
+ return parts;
178
+ }
179
+ function _makeLoggableRrwebEvent(slice, payload, sessionID, data) {
180
+ const metadata = {
181
+ session_start_ts: String(data.startTime),
182
+ session_end_ts: String(data.endTime),
183
+ clicks_captured_cumulative: String(data.clickCount),
184
+ rrweb_events: slice,
185
+ rrweb_payload_size: String(payload.length),
186
+ session_replay_sdk_version: client_core_1.SDK_VERSION,
187
+ };
188
+ return {
189
+ eventName: 'statsig::session_recording',
190
+ value: sessionID,
191
+ metadata,
192
+ };
193
+ }
194
+ function _appendSlicedMetadata(metadata, slicedID, sliceIndex, sliceCount, sliceByteSize) {
195
+ metadata.sliced_id = slicedID;
196
+ metadata.slice_index = String(sliceIndex);
197
+ metadata.slice_count = String(sliceCount);
198
+ metadata.slice_byte_size = String(sliceByteSize);
199
+ }
@@ -0,0 +1 @@
1
+ export declare const _fastApproxSizeOf: (obj: Record<string, unknown> | Array<unknown>, max: number) => number;
package/src/SizeOf.js ADDED
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._fastApproxSizeOf = void 0;
4
+ const CURLY_AND_SQUARE_BRACKET_SIZE = 2; // [] for array, {} for object
5
+ const APPROX_ADDITIONAL_SIZE = 1; // additional size for comma and stuff
6
+ const _fastApproxSizeOf = (obj, max) => {
7
+ let size = 0;
8
+ const keys = Object.keys(obj);
9
+ for (let i = 0; i < keys.length; i++) {
10
+ const key = keys[i];
11
+ const value = obj[key];
12
+ size += key.length;
13
+ if (typeof value === 'object' && value !== null) {
14
+ size += (0, exports._fastApproxSizeOf)(value, max) + CURLY_AND_SQUARE_BRACKET_SIZE;
15
+ }
16
+ else {
17
+ size += String(value).length + APPROX_ADDITIONAL_SIZE;
18
+ }
19
+ if (size >= max) {
20
+ // exit early if we've exceeded the max
21
+ return size;
22
+ }
23
+ }
24
+ return size;
25
+ };
26
+ exports._fastApproxSizeOf = _fastApproxSizeOf;