@v-tilt/browser 1.1.5 → 1.2.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/dist/array.js +1 -1
- package/dist/array.js.map +1 -1
- package/dist/array.no-external.js +1 -1
- package/dist/array.no-external.js.map +1 -1
- package/dist/entrypoints/array.d.ts +1 -0
- package/dist/entrypoints/external-scripts-loader.d.ts +24 -0
- package/dist/entrypoints/module.es.d.ts +1 -0
- package/dist/entrypoints/recorder.d.ts +23 -0
- package/dist/extensions/replay/index.d.ts +13 -0
- package/dist/extensions/replay/session-recording-utils.d.ts +92 -0
- package/dist/extensions/replay/session-recording-wrapper.d.ts +61 -0
- package/dist/extensions/replay/session-recording.d.ts +95 -0
- package/dist/extensions/replay/types.d.ts +211 -0
- package/dist/external-scripts-loader.js +2 -0
- package/dist/external-scripts-loader.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.d.ts +264 -5
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +264 -5
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/recorder.js +2 -0
- package/dist/recorder.js.map +1 -0
- package/dist/types.d.ts +84 -4
- package/dist/utils/globals.d.ts +42 -0
- package/dist/vtilt.d.ts +36 -0
- package/lib/config.js +2 -0
- package/lib/entrypoints/array.d.ts +1 -0
- package/lib/entrypoints/array.js +1 -0
- package/lib/entrypoints/external-scripts-loader.d.ts +24 -0
- package/lib/entrypoints/external-scripts-loader.js +107 -0
- package/lib/entrypoints/module.es.d.ts +1 -0
- package/lib/entrypoints/module.es.js +1 -0
- package/lib/entrypoints/recorder.d.ts +23 -0
- package/lib/entrypoints/recorder.js +42 -0
- package/lib/extensions/replay/index.d.ts +13 -0
- package/lib/extensions/replay/index.js +31 -0
- package/lib/extensions/replay/session-recording-utils.d.ts +92 -0
- package/lib/extensions/replay/session-recording-utils.js +212 -0
- package/lib/extensions/replay/session-recording-wrapper.d.ts +61 -0
- package/lib/extensions/replay/session-recording-wrapper.js +149 -0
- package/lib/extensions/replay/session-recording.d.ts +95 -0
- package/lib/extensions/replay/session-recording.js +700 -0
- package/lib/extensions/replay/types.d.ts +211 -0
- package/lib/extensions/replay/types.js +8 -0
- package/lib/types.d.ts +84 -4
- package/lib/utils/globals.d.ts +42 -0
- package/lib/utils/globals.js +2 -0
- package/lib/vtilt.d.ts +36 -0
- package/lib/vtilt.js +106 -0
- package/package.json +4 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Recording Utilities
|
|
4
|
+
*
|
|
5
|
+
* Utility functions for session recording.
|
|
6
|
+
* Based on PostHog's sessionrecording-utils.ts
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ACTIVE_SOURCES = exports.RECORDING_IDLE_THRESHOLD_MS = exports.RECORDING_BUFFER_TIMEOUT = exports.RECORDING_MAX_EVENT_SIZE = exports.PARTIAL_COMPRESSION_THRESHOLD = exports.SEVEN_MB = exports.ONE_MB = exports.ONE_KB = exports.INCREMENTAL_SNAPSHOT_EVENT_TYPE = void 0;
|
|
10
|
+
exports.gzipToString = gzipToString;
|
|
11
|
+
exports.estimateSize = estimateSize;
|
|
12
|
+
exports.compressEvent = compressEvent;
|
|
13
|
+
exports.truncateLargeConsoleLogs = truncateLargeConsoleLogs;
|
|
14
|
+
exports.splitBuffer = splitBuffer;
|
|
15
|
+
exports.isSessionIdleEvent = isSessionIdleEvent;
|
|
16
|
+
exports.isRecordingPausedEvent = isRecordingPausedEvent;
|
|
17
|
+
exports.isInteractiveEvent = isInteractiveEvent;
|
|
18
|
+
exports.clampToRange = clampToRange;
|
|
19
|
+
const types_1 = require("@rrweb/types");
|
|
20
|
+
const fflate_1 = require("fflate");
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Constants
|
|
23
|
+
// ============================================================================
|
|
24
|
+
exports.INCREMENTAL_SNAPSHOT_EVENT_TYPE = 3;
|
|
25
|
+
exports.ONE_KB = 1024;
|
|
26
|
+
exports.ONE_MB = exports.ONE_KB * exports.ONE_KB;
|
|
27
|
+
exports.SEVEN_MB = exports.ONE_MB * 7 * 0.9; // ~7mb with wiggle room
|
|
28
|
+
exports.PARTIAL_COMPRESSION_THRESHOLD = exports.ONE_KB;
|
|
29
|
+
exports.RECORDING_MAX_EVENT_SIZE = exports.ONE_MB * 0.9; // ~1mb with wiggle room
|
|
30
|
+
exports.RECORDING_BUFFER_TIMEOUT = 2000; // 2 seconds
|
|
31
|
+
exports.RECORDING_IDLE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
|
|
32
|
+
/** Active interaction sources */
|
|
33
|
+
exports.ACTIVE_SOURCES = [
|
|
34
|
+
types_1.IncrementalSource.MouseMove,
|
|
35
|
+
types_1.IncrementalSource.MouseInteraction,
|
|
36
|
+
types_1.IncrementalSource.Scroll,
|
|
37
|
+
types_1.IncrementalSource.ViewportResize,
|
|
38
|
+
types_1.IncrementalSource.Input,
|
|
39
|
+
types_1.IncrementalSource.TouchMove,
|
|
40
|
+
types_1.IncrementalSource.MediaInteraction,
|
|
41
|
+
types_1.IncrementalSource.Drag,
|
|
42
|
+
];
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Compression
|
|
45
|
+
// ============================================================================
|
|
46
|
+
/**
|
|
47
|
+
* Gzip compress data to a base64 string
|
|
48
|
+
*/
|
|
49
|
+
function gzipToString(data) {
|
|
50
|
+
return (0, fflate_1.strFromU8)((0, fflate_1.gzipSync)((0, fflate_1.strToU8)(JSON.stringify(data))), true);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Estimate the size of an object in bytes
|
|
54
|
+
*/
|
|
55
|
+
function estimateSize(obj) {
|
|
56
|
+
try {
|
|
57
|
+
return new Blob([JSON.stringify(obj)]).size;
|
|
58
|
+
}
|
|
59
|
+
catch (_a) {
|
|
60
|
+
// Fallback for environments without Blob
|
|
61
|
+
return JSON.stringify(obj).length * 2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Compress an rrweb event for transmission
|
|
66
|
+
* Only compresses full snapshots and mutation events above threshold
|
|
67
|
+
*/
|
|
68
|
+
function compressEvent(event) {
|
|
69
|
+
const originalSize = estimateSize(event);
|
|
70
|
+
if (originalSize < exports.PARTIAL_COMPRESSION_THRESHOLD) {
|
|
71
|
+
return event;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
if (event.type === types_1.EventType.FullSnapshot) {
|
|
75
|
+
return {
|
|
76
|
+
...event,
|
|
77
|
+
data: gzipToString(event.data),
|
|
78
|
+
cv: "2024-10",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (event.type === types_1.EventType.IncrementalSnapshot &&
|
|
82
|
+
event.data.source === types_1.IncrementalSource.Mutation) {
|
|
83
|
+
const mutationData = event.data;
|
|
84
|
+
return {
|
|
85
|
+
...event,
|
|
86
|
+
cv: "2024-10",
|
|
87
|
+
data: {
|
|
88
|
+
...mutationData,
|
|
89
|
+
texts: gzipToString(mutationData.texts),
|
|
90
|
+
attributes: gzipToString(mutationData.attributes),
|
|
91
|
+
removes: gzipToString(mutationData.removes),
|
|
92
|
+
adds: gzipToString(mutationData.adds),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (event.type === types_1.EventType.IncrementalSnapshot &&
|
|
97
|
+
event.data.source === types_1.IncrementalSource.StyleSheetRule) {
|
|
98
|
+
const styleData = event.data;
|
|
99
|
+
return {
|
|
100
|
+
...event,
|
|
101
|
+
cv: "2024-10",
|
|
102
|
+
data: {
|
|
103
|
+
...styleData,
|
|
104
|
+
adds: styleData.adds ? gzipToString(styleData.adds) : undefined,
|
|
105
|
+
removes: styleData.removes
|
|
106
|
+
? gzipToString(styleData.removes)
|
|
107
|
+
: undefined,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error("[SessionRecording] Could not compress event - will use uncompressed event", e);
|
|
114
|
+
}
|
|
115
|
+
return event;
|
|
116
|
+
}
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// Console Log Handling
|
|
119
|
+
// ============================================================================
|
|
120
|
+
const MAX_CONSOLE_LOG_SIZE = 1000;
|
|
121
|
+
/**
|
|
122
|
+
* Truncate large console log payloads to prevent excessive data
|
|
123
|
+
*/
|
|
124
|
+
function truncateLargeConsoleLogs(event) {
|
|
125
|
+
if (event.type !== types_1.EventType.Plugin ||
|
|
126
|
+
event.data.plugin !== "rrweb/console@1") {
|
|
127
|
+
return event;
|
|
128
|
+
}
|
|
129
|
+
const payload = event.data.payload;
|
|
130
|
+
if (!(payload === null || payload === void 0 ? void 0 : payload.payload)) {
|
|
131
|
+
return event;
|
|
132
|
+
}
|
|
133
|
+
const truncatedPayload = payload.payload.map((item) => {
|
|
134
|
+
if (typeof item === "string" && item.length > MAX_CONSOLE_LOG_SIZE) {
|
|
135
|
+
return item.substring(0, MAX_CONSOLE_LOG_SIZE) + "... [truncated]";
|
|
136
|
+
}
|
|
137
|
+
return item;
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
...event,
|
|
141
|
+
data: {
|
|
142
|
+
...event.data,
|
|
143
|
+
payload: {
|
|
144
|
+
...payload,
|
|
145
|
+
payload: truncatedPayload,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// ============================================================================
|
|
151
|
+
// Buffer Management
|
|
152
|
+
// ============================================================================
|
|
153
|
+
/**
|
|
154
|
+
* Split a large buffer into smaller chunks for transmission
|
|
155
|
+
*/
|
|
156
|
+
function splitBuffer(buffer, sizeLimit = exports.SEVEN_MB) {
|
|
157
|
+
if (buffer.size >= sizeLimit && buffer.data.length > 1) {
|
|
158
|
+
const half = Math.floor(buffer.data.length / 2);
|
|
159
|
+
const firstHalf = buffer.data.slice(0, half);
|
|
160
|
+
const secondHalf = buffer.data.slice(half);
|
|
161
|
+
return [
|
|
162
|
+
...splitBuffer({
|
|
163
|
+
size: estimateSize(firstHalf),
|
|
164
|
+
data: firstHalf,
|
|
165
|
+
sessionId: buffer.sessionId,
|
|
166
|
+
windowId: buffer.windowId,
|
|
167
|
+
}),
|
|
168
|
+
...splitBuffer({
|
|
169
|
+
size: estimateSize(secondHalf),
|
|
170
|
+
data: secondHalf,
|
|
171
|
+
sessionId: buffer.sessionId,
|
|
172
|
+
windowId: buffer.windowId,
|
|
173
|
+
}),
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
return [buffer];
|
|
177
|
+
}
|
|
178
|
+
// ============================================================================
|
|
179
|
+
// Event Helpers
|
|
180
|
+
// ============================================================================
|
|
181
|
+
/**
|
|
182
|
+
* Check if an event is a session idle custom event
|
|
183
|
+
*/
|
|
184
|
+
function isSessionIdleEvent(event) {
|
|
185
|
+
return (event.type === types_1.EventType.Custom &&
|
|
186
|
+
event.data.tag === "sessionIdle");
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check if an event is a recording paused custom event
|
|
190
|
+
*/
|
|
191
|
+
function isRecordingPausedEvent(event) {
|
|
192
|
+
return (event.type === types_1.EventType.Custom &&
|
|
193
|
+
event.data.tag === "recording paused");
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Check if an event represents user interaction
|
|
197
|
+
*/
|
|
198
|
+
function isInteractiveEvent(event) {
|
|
199
|
+
return (event.type === exports.INCREMENTAL_SNAPSHOT_EVENT_TYPE &&
|
|
200
|
+
exports.ACTIVE_SOURCES.includes(event.data
|
|
201
|
+
.source));
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Clamp a value to a range with logging
|
|
205
|
+
*/
|
|
206
|
+
function clampToRange(value, min, max, label, defaultValue) {
|
|
207
|
+
if (value < min || value > max) {
|
|
208
|
+
console.warn(`[SessionRecording] ${label} must be between ${min} and ${max}. Using default: ${defaultValue}`);
|
|
209
|
+
return defaultValue;
|
|
210
|
+
}
|
|
211
|
+
return value;
|
|
212
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Recording Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Lightweight wrapper that handles the decision of WHEN to load recording.
|
|
5
|
+
* The actual recording logic is in LazyLoadedSessionRecording which is
|
|
6
|
+
* loaded on demand.
|
|
7
|
+
*
|
|
8
|
+
* Based on PostHog's sessionrecording-wrapper.ts
|
|
9
|
+
*/
|
|
10
|
+
import type { VTilt } from "../../vtilt";
|
|
11
|
+
import type { SessionRecordingConfig, SessionStartReason } from "./types";
|
|
12
|
+
import { LazyLoadedSessionRecordingInterface } from "../../utils/globals";
|
|
13
|
+
/** Status when lazy loading is in progress */
|
|
14
|
+
export declare const LAZY_LOADING: "lazy_loading";
|
|
15
|
+
/** Session recording status */
|
|
16
|
+
export type SessionRecordingStatus = "disabled" | "buffering" | "active" | "paused" | "sampled" | "trigger_pending" | typeof LAZY_LOADING;
|
|
17
|
+
export type { LazyLoadedSessionRecordingInterface };
|
|
18
|
+
/**
|
|
19
|
+
* Session Recording Wrapper
|
|
20
|
+
*
|
|
21
|
+
* This is the lightweight class that lives in the main bundle.
|
|
22
|
+
* It handles:
|
|
23
|
+
* - Deciding if recording should be enabled
|
|
24
|
+
* - Lazy loading the actual recording code
|
|
25
|
+
* - Delegating to LazyLoadedSessionRecording
|
|
26
|
+
*/
|
|
27
|
+
export declare class SessionRecordingWrapper {
|
|
28
|
+
private readonly _instance;
|
|
29
|
+
private _lazyLoadedRecording;
|
|
30
|
+
private _config;
|
|
31
|
+
constructor(_instance: VTilt, config?: SessionRecordingConfig);
|
|
32
|
+
get started(): boolean;
|
|
33
|
+
get status(): SessionRecordingStatus;
|
|
34
|
+
get sessionId(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Start recording if enabled, otherwise stop
|
|
37
|
+
*/
|
|
38
|
+
startIfEnabledOrStop(startReason?: SessionStartReason): void;
|
|
39
|
+
/**
|
|
40
|
+
* Stop recording
|
|
41
|
+
*/
|
|
42
|
+
stopRecording(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Log a message to the recording
|
|
45
|
+
*/
|
|
46
|
+
log(message: string, level?: "log" | "warn" | "error"): void;
|
|
47
|
+
/**
|
|
48
|
+
* Update configuration
|
|
49
|
+
*/
|
|
50
|
+
updateConfig(config: Partial<SessionRecordingConfig>): void;
|
|
51
|
+
private get _isRecordingEnabled();
|
|
52
|
+
private get _scriptName();
|
|
53
|
+
/**
|
|
54
|
+
* Lazy load the recording script and start
|
|
55
|
+
*/
|
|
56
|
+
private _lazyLoadAndStart;
|
|
57
|
+
/**
|
|
58
|
+
* Called after the recording script is loaded
|
|
59
|
+
*/
|
|
60
|
+
private _onScriptLoaded;
|
|
61
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Session Recording Wrapper
|
|
4
|
+
*
|
|
5
|
+
* Lightweight wrapper that handles the decision of WHEN to load recording.
|
|
6
|
+
* The actual recording logic is in LazyLoadedSessionRecording which is
|
|
7
|
+
* loaded on demand.
|
|
8
|
+
*
|
|
9
|
+
* Based on PostHog's sessionrecording-wrapper.ts
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SessionRecordingWrapper = exports.LAZY_LOADING = void 0;
|
|
13
|
+
const globals_1 = require("../../utils/globals");
|
|
14
|
+
const LOGGER_PREFIX = "[SessionRecording]";
|
|
15
|
+
/** Status when lazy loading is in progress */
|
|
16
|
+
exports.LAZY_LOADING = "lazy_loading";
|
|
17
|
+
/**
|
|
18
|
+
* Session Recording Wrapper
|
|
19
|
+
*
|
|
20
|
+
* This is the lightweight class that lives in the main bundle.
|
|
21
|
+
* It handles:
|
|
22
|
+
* - Deciding if recording should be enabled
|
|
23
|
+
* - Lazy loading the actual recording code
|
|
24
|
+
* - Delegating to LazyLoadedSessionRecording
|
|
25
|
+
*/
|
|
26
|
+
class SessionRecordingWrapper {
|
|
27
|
+
constructor(_instance, config = {}) {
|
|
28
|
+
this._instance = _instance;
|
|
29
|
+
this._config = config;
|
|
30
|
+
}
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Public API
|
|
33
|
+
// ============================================================================
|
|
34
|
+
get started() {
|
|
35
|
+
var _a;
|
|
36
|
+
return !!((_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.isStarted);
|
|
37
|
+
}
|
|
38
|
+
get status() {
|
|
39
|
+
var _a;
|
|
40
|
+
return (((_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.status) ||
|
|
41
|
+
exports.LAZY_LOADING);
|
|
42
|
+
}
|
|
43
|
+
get sessionId() {
|
|
44
|
+
var _a;
|
|
45
|
+
return ((_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.sessionId) || "";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Start recording if enabled, otherwise stop
|
|
49
|
+
*/
|
|
50
|
+
startIfEnabledOrStop(startReason) {
|
|
51
|
+
var _a;
|
|
52
|
+
if (this._isRecordingEnabled && ((_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.isStarted)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Check browser compatibility
|
|
56
|
+
const canRunReplay = typeof Object.assign !== "undefined" && typeof Array.from !== "undefined";
|
|
57
|
+
if (this._isRecordingEnabled && canRunReplay) {
|
|
58
|
+
this._lazyLoadAndStart(startReason);
|
|
59
|
+
console.info(`${LOGGER_PREFIX} starting`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.stopRecording();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Stop recording
|
|
67
|
+
*/
|
|
68
|
+
stopRecording() {
|
|
69
|
+
var _a;
|
|
70
|
+
(_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.stop();
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Log a message to the recording
|
|
74
|
+
*/
|
|
75
|
+
log(message, level = "log") {
|
|
76
|
+
var _a;
|
|
77
|
+
if ((_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.log) {
|
|
78
|
+
this._lazyLoadedRecording.log(message, level);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.warn(`${LOGGER_PREFIX} log called before recorder was ready`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Update configuration
|
|
86
|
+
*/
|
|
87
|
+
updateConfig(config) {
|
|
88
|
+
var _a;
|
|
89
|
+
this._config = { ...this._config, ...config };
|
|
90
|
+
(_a = this._lazyLoadedRecording) === null || _a === void 0 ? void 0 : _a.updateConfig(this._config);
|
|
91
|
+
}
|
|
92
|
+
// ============================================================================
|
|
93
|
+
// Private Methods
|
|
94
|
+
// ============================================================================
|
|
95
|
+
get _isRecordingEnabled() {
|
|
96
|
+
var _a;
|
|
97
|
+
const config = this._instance.getConfig();
|
|
98
|
+
const enabled = (_a = this._config.enabled) !== null && _a !== void 0 ? _a : false;
|
|
99
|
+
const notDisabled = !config.disable_session_recording;
|
|
100
|
+
return !!globals_1.window && enabled && notDisabled;
|
|
101
|
+
}
|
|
102
|
+
get _scriptName() {
|
|
103
|
+
return "recorder";
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Lazy load the recording script and start
|
|
107
|
+
*/
|
|
108
|
+
_lazyLoadAndStart(startReason) {
|
|
109
|
+
var _a, _b, _c, _d;
|
|
110
|
+
if (!this._isRecordingEnabled) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Check if already loaded
|
|
114
|
+
if (((_b = (_a = globals_1.assignableWindow === null || globals_1.assignableWindow === void 0 ? void 0 : globals_1.assignableWindow.__VTiltExtensions__) === null || _a === void 0 ? void 0 : _a.rrweb) === null || _b === void 0 ? void 0 : _b.record) &&
|
|
115
|
+
((_c = globals_1.assignableWindow.__VTiltExtensions__) === null || _c === void 0 ? void 0 : _c.initSessionRecording)) {
|
|
116
|
+
this._onScriptLoaded(startReason);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// Load the recorder script
|
|
120
|
+
const loadExternalDependency = (_d = globals_1.assignableWindow.__VTiltExtensions__) === null || _d === void 0 ? void 0 : _d.loadExternalDependency;
|
|
121
|
+
if (!loadExternalDependency) {
|
|
122
|
+
console.error(`${LOGGER_PREFIX} loadExternalDependency not available. Session recording cannot start.`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
loadExternalDependency(this._instance, this._scriptName, (err) => {
|
|
126
|
+
if (err) {
|
|
127
|
+
console.error(`${LOGGER_PREFIX} could not load recorder:`, err);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
this._onScriptLoaded(startReason);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Called after the recording script is loaded
|
|
135
|
+
*/
|
|
136
|
+
_onScriptLoaded(startReason) {
|
|
137
|
+
var _a;
|
|
138
|
+
const initSessionRecording = (_a = globals_1.assignableWindow.__VTiltExtensions__) === null || _a === void 0 ? void 0 : _a.initSessionRecording;
|
|
139
|
+
if (!initSessionRecording) {
|
|
140
|
+
console.error(`${LOGGER_PREFIX} initSessionRecording not available after script load`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!this._lazyLoadedRecording) {
|
|
144
|
+
this._lazyLoadedRecording = initSessionRecording(this._instance, this._config);
|
|
145
|
+
}
|
|
146
|
+
this._lazyLoadedRecording.start(startReason);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.SessionRecordingWrapper = SessionRecordingWrapper;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy Loaded Session Recording
|
|
3
|
+
*
|
|
4
|
+
* The actual rrweb session recording implementation.
|
|
5
|
+
* This is loaded on demand when recording is enabled.
|
|
6
|
+
*
|
|
7
|
+
* Based on PostHog's lazy-loaded-session-recorder.ts
|
|
8
|
+
*/
|
|
9
|
+
import { type eventWithTime } from "@rrweb/types";
|
|
10
|
+
import type { VTilt } from "../../vtilt";
|
|
11
|
+
import { LazyLoadedSessionRecordingInterface } from "../../utils/globals";
|
|
12
|
+
import type { SessionRecordingConfig, SessionRecordingStatus } from "./types";
|
|
13
|
+
export declare const SESSION_RECORDING_BATCH_KEY = "recordings";
|
|
14
|
+
export declare class LazyLoadedSessionRecording implements LazyLoadedSessionRecordingInterface {
|
|
15
|
+
private _instance;
|
|
16
|
+
private _endpoint;
|
|
17
|
+
private _flushBufferTimer?;
|
|
18
|
+
private _fullSnapshotTimer?;
|
|
19
|
+
private _captureStarted;
|
|
20
|
+
private _stopRrweb?;
|
|
21
|
+
private _isIdle;
|
|
22
|
+
private _lastActivityTimestamp;
|
|
23
|
+
private _lastHref?;
|
|
24
|
+
private _sessionId;
|
|
25
|
+
private _windowId;
|
|
26
|
+
private _buffer;
|
|
27
|
+
private _queuedRRWebEvents;
|
|
28
|
+
private _config;
|
|
29
|
+
constructor(instance: VTilt, config?: SessionRecordingConfig);
|
|
30
|
+
get isStarted(): boolean;
|
|
31
|
+
/** @deprecated Use isStarted instead */
|
|
32
|
+
get started(): boolean;
|
|
33
|
+
get sessionId(): string;
|
|
34
|
+
get status(): SessionRecordingStatus;
|
|
35
|
+
/**
|
|
36
|
+
* Start session recording (interface method)
|
|
37
|
+
*/
|
|
38
|
+
start(startReason?: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Stop session recording (interface method)
|
|
41
|
+
*/
|
|
42
|
+
stop(): void;
|
|
43
|
+
/** @deprecated Use stop() instead */
|
|
44
|
+
stopRecording(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Add a custom event to the recording
|
|
47
|
+
*/
|
|
48
|
+
addCustomEvent(tag: string, payload: unknown): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Take a full snapshot
|
|
51
|
+
*/
|
|
52
|
+
takeFullSnapshot(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Log a message to the recording
|
|
55
|
+
*/
|
|
56
|
+
log(message: string, level?: "log" | "warn" | "error"): void;
|
|
57
|
+
/**
|
|
58
|
+
* Update configuration
|
|
59
|
+
*/
|
|
60
|
+
updateConfig(config: Partial<SessionRecordingConfig>): void;
|
|
61
|
+
private _onBeforeUnload;
|
|
62
|
+
private _onOffline;
|
|
63
|
+
private _onOnline;
|
|
64
|
+
private _onVisibilityChange;
|
|
65
|
+
private _isRecordingEnabled;
|
|
66
|
+
private _startCapture;
|
|
67
|
+
private _loadRecorder;
|
|
68
|
+
private _onScriptLoaded;
|
|
69
|
+
private _gatherRRWebPlugins;
|
|
70
|
+
onRRwebEmit(rawEvent: eventWithTime): void;
|
|
71
|
+
private _processQueuedEvents;
|
|
72
|
+
private _updateWindowAndSessionIds;
|
|
73
|
+
private _clearBuffer;
|
|
74
|
+
private _flushBuffer;
|
|
75
|
+
private _captureSnapshotBuffered;
|
|
76
|
+
private _captureSnapshot;
|
|
77
|
+
private _sendSnapshot;
|
|
78
|
+
/**
|
|
79
|
+
* Fetch with exponential backoff retry (PostHog-style)
|
|
80
|
+
* Retries on network errors and 5xx server errors
|
|
81
|
+
*/
|
|
82
|
+
private _fetchWithRetry;
|
|
83
|
+
/**
|
|
84
|
+
* Schedule a retry with exponential backoff and jitter
|
|
85
|
+
*/
|
|
86
|
+
private _scheduleRetry;
|
|
87
|
+
private _scheduleFullSnapshot;
|
|
88
|
+
private _tryRRWebMethod;
|
|
89
|
+
private _tryAddCustomEvent;
|
|
90
|
+
private _tryTakeFullSnapshot;
|
|
91
|
+
private _getCanvasConfig;
|
|
92
|
+
private _getMaskingConfig;
|
|
93
|
+
private _generateId;
|
|
94
|
+
private _reportStarted;
|
|
95
|
+
}
|