@statsig/session-replay 3.3.0-beta.3 → 3.4.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 +64 -24
- package/src/SizeOf.d.ts +1 -0
- package/src/SizeOf.js +26 -0
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statsig/session-replay",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"rrweb": "2.0.0-alpha.12",
|
|
6
|
-
"@statsig/client-core": "3.
|
|
6
|
+
"@statsig/client-core": "3.4.0"
|
|
7
7
|
},
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@rrweb/types": "2.0.0-alpha.12"
|
package/src/SessionReplay.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
+
}
|
package/src/SizeOf.d.ts
ADDED
|
@@ -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;
|