@v-tilt/browser 1.1.4 → 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.
Files changed (62) hide show
  1. package/dist/array.js +1 -1
  2. package/dist/array.js.map +1 -1
  3. package/dist/array.no-external.js +1 -1
  4. package/dist/array.no-external.js.map +1 -1
  5. package/dist/entrypoints/array.d.ts +1 -0
  6. package/dist/entrypoints/external-scripts-loader.d.ts +24 -0
  7. package/dist/entrypoints/module.es.d.ts +1 -0
  8. package/dist/entrypoints/recorder.d.ts +23 -0
  9. package/dist/extensions/replay/index.d.ts +13 -0
  10. package/dist/extensions/replay/session-recording-utils.d.ts +92 -0
  11. package/dist/extensions/replay/session-recording-wrapper.d.ts +61 -0
  12. package/dist/extensions/replay/session-recording.d.ts +95 -0
  13. package/dist/extensions/replay/types.d.ts +211 -0
  14. package/dist/external-scripts-loader.js +2 -0
  15. package/dist/external-scripts-loader.js.map +1 -0
  16. package/dist/main.js +1 -1
  17. package/dist/main.js.map +1 -1
  18. package/dist/module.d.ts +271 -8
  19. package/dist/module.js +1 -1
  20. package/dist/module.js.map +1 -1
  21. package/dist/module.no-external.d.ts +271 -8
  22. package/dist/module.no-external.js +1 -1
  23. package/dist/module.no-external.js.map +1 -1
  24. package/dist/recorder.js +2 -0
  25. package/dist/recorder.js.map +1 -0
  26. package/dist/session.d.ts +4 -2
  27. package/dist/storage.d.ts +8 -3
  28. package/dist/types.d.ts +91 -7
  29. package/dist/user-manager.d.ts +2 -2
  30. package/dist/utils/globals.d.ts +42 -0
  31. package/dist/vtilt.d.ts +36 -0
  32. package/lib/config.js +2 -0
  33. package/lib/entrypoints/array.d.ts +1 -0
  34. package/lib/entrypoints/array.js +1 -0
  35. package/lib/entrypoints/external-scripts-loader.d.ts +24 -0
  36. package/lib/entrypoints/external-scripts-loader.js +107 -0
  37. package/lib/entrypoints/module.es.d.ts +1 -0
  38. package/lib/entrypoints/module.es.js +1 -0
  39. package/lib/entrypoints/recorder.d.ts +23 -0
  40. package/lib/entrypoints/recorder.js +42 -0
  41. package/lib/extensions/replay/index.d.ts +13 -0
  42. package/lib/extensions/replay/index.js +31 -0
  43. package/lib/extensions/replay/session-recording-utils.d.ts +92 -0
  44. package/lib/extensions/replay/session-recording-utils.js +212 -0
  45. package/lib/extensions/replay/session-recording-wrapper.d.ts +61 -0
  46. package/lib/extensions/replay/session-recording-wrapper.js +149 -0
  47. package/lib/extensions/replay/session-recording.d.ts +95 -0
  48. package/lib/extensions/replay/session-recording.js +700 -0
  49. package/lib/extensions/replay/types.d.ts +211 -0
  50. package/lib/extensions/replay/types.js +8 -0
  51. package/lib/session.d.ts +4 -2
  52. package/lib/session.js +7 -41
  53. package/lib/storage.d.ts +8 -3
  54. package/lib/storage.js +62 -9
  55. package/lib/types.d.ts +91 -7
  56. package/lib/user-manager.d.ts +2 -2
  57. package/lib/user-manager.js +4 -4
  58. package/lib/utils/globals.d.ts +42 -0
  59. package/lib/utils/globals.js +2 -0
  60. package/lib/vtilt.d.ts +36 -0
  61. package/lib/vtilt.js +110 -14
  62. package/package.json +4 -1
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Session Recording Utilities
3
+ *
4
+ * Utility functions for session recording.
5
+ * Based on PostHog's sessionrecording-utils.ts
6
+ */
7
+ import { EventType, IncrementalSource, eventWithTime } from "@rrweb/types";
8
+ import type { SnapshotBuffer } from "./types";
9
+ export declare const INCREMENTAL_SNAPSHOT_EVENT_TYPE = 3;
10
+ export declare const ONE_KB = 1024;
11
+ export declare const ONE_MB: number;
12
+ export declare const SEVEN_MB: number;
13
+ export declare const PARTIAL_COMPRESSION_THRESHOLD = 1024;
14
+ export declare const RECORDING_MAX_EVENT_SIZE: number;
15
+ export declare const RECORDING_BUFFER_TIMEOUT = 2000;
16
+ export declare const RECORDING_IDLE_THRESHOLD_MS: number;
17
+ /** Active interaction sources */
18
+ export declare const ACTIVE_SOURCES: IncrementalSource[];
19
+ /**
20
+ * Gzip compress data to a base64 string
21
+ */
22
+ export declare function gzipToString(data: unknown): string;
23
+ /**
24
+ * Estimate the size of an object in bytes
25
+ */
26
+ export declare function estimateSize(obj: unknown): number;
27
+ export interface CompressedFullSnapshotEvent {
28
+ type: typeof EventType.FullSnapshot;
29
+ data: string;
30
+ }
31
+ export interface CompressedIncrementalSnapshotEvent {
32
+ type: typeof EventType.IncrementalSnapshot;
33
+ data: {
34
+ source: IncrementalSource;
35
+ texts: string;
36
+ attributes: string;
37
+ removes: string;
38
+ adds: string;
39
+ };
40
+ }
41
+ export interface CompressedIncrementalStyleSnapshotEvent {
42
+ type: typeof EventType.IncrementalSnapshot;
43
+ data: {
44
+ source: typeof IncrementalSource.StyleSheetRule;
45
+ id?: number;
46
+ styleId?: number;
47
+ replace?: string;
48
+ replaceSync?: string;
49
+ adds?: string;
50
+ removes?: string;
51
+ };
52
+ }
53
+ export type CompressedEvent = CompressedFullSnapshotEvent | CompressedIncrementalSnapshotEvent | CompressedIncrementalStyleSnapshotEvent;
54
+ export type CompressedEventWithTime = CompressedEvent & {
55
+ timestamp: number;
56
+ delay?: number;
57
+ cv: "2024-10";
58
+ };
59
+ /**
60
+ * Compress an rrweb event for transmission
61
+ * Only compresses full snapshots and mutation events above threshold
62
+ */
63
+ export declare function compressEvent(event: eventWithTime): eventWithTime | CompressedEventWithTime;
64
+ /**
65
+ * Truncate large console log payloads to prevent excessive data
66
+ */
67
+ export declare function truncateLargeConsoleLogs(event: eventWithTime): eventWithTime;
68
+ /**
69
+ * Split a large buffer into smaller chunks for transmission
70
+ */
71
+ export declare function splitBuffer(buffer: SnapshotBuffer, sizeLimit?: number): SnapshotBuffer[];
72
+ /**
73
+ * Check if an event is a session idle custom event
74
+ */
75
+ export declare function isSessionIdleEvent(event: eventWithTime): event is eventWithTime & {
76
+ data: {
77
+ tag: string;
78
+ payload: unknown;
79
+ };
80
+ };
81
+ /**
82
+ * Check if an event is a recording paused custom event
83
+ */
84
+ export declare function isRecordingPausedEvent(event: eventWithTime): boolean;
85
+ /**
86
+ * Check if an event represents user interaction
87
+ */
88
+ export declare function isInteractiveEvent(event: eventWithTime): boolean;
89
+ /**
90
+ * Clamp a value to a range with logging
91
+ */
92
+ export declare function clampToRange(value: number, min: number, max: number, label: string, defaultValue: number): number;
@@ -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
+ }