@v-tilt/browser 1.1.5 → 1.3.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 (72) hide show
  1. package/dist/all-external-dependencies.js +2 -0
  2. package/dist/all-external-dependencies.js.map +1 -0
  3. package/dist/array.full.js +2 -0
  4. package/dist/array.full.js.map +1 -0
  5. package/dist/array.js +1 -1
  6. package/dist/array.js.map +1 -1
  7. package/dist/array.no-external.js +1 -1
  8. package/dist/array.no-external.js.map +1 -1
  9. package/dist/entrypoints/all-external-dependencies.d.ts +8 -0
  10. package/dist/entrypoints/array.d.ts +1 -0
  11. package/dist/entrypoints/array.full.d.ts +17 -0
  12. package/dist/entrypoints/external-scripts-loader.d.ts +24 -0
  13. package/dist/entrypoints/module.es.d.ts +1 -0
  14. package/dist/entrypoints/recorder.d.ts +23 -0
  15. package/dist/entrypoints/web-vitals.d.ts +14 -0
  16. package/dist/extensions/replay/index.d.ts +13 -0
  17. package/dist/extensions/replay/session-recording-utils.d.ts +92 -0
  18. package/dist/extensions/replay/session-recording-wrapper.d.ts +61 -0
  19. package/dist/extensions/replay/session-recording.d.ts +95 -0
  20. package/dist/extensions/replay/types.d.ts +211 -0
  21. package/dist/external-scripts-loader.js +2 -0
  22. package/dist/external-scripts-loader.js.map +1 -0
  23. package/dist/main.js +1 -1
  24. package/dist/main.js.map +1 -1
  25. package/dist/module.d.ts +297 -8
  26. package/dist/module.js +1 -1
  27. package/dist/module.js.map +1 -1
  28. package/dist/module.no-external.d.ts +297 -8
  29. package/dist/module.no-external.js +1 -1
  30. package/dist/module.no-external.js.map +1 -1
  31. package/dist/recorder.js +2 -0
  32. package/dist/recorder.js.map +1 -0
  33. package/dist/types.d.ts +116 -6
  34. package/dist/utils/globals.d.ts +69 -0
  35. package/dist/vtilt.d.ts +36 -0
  36. package/dist/web-vitals.d.ts +89 -5
  37. package/dist/web-vitals.js +2 -0
  38. package/dist/web-vitals.js.map +1 -0
  39. package/lib/config.js +7 -3
  40. package/lib/entrypoints/all-external-dependencies.d.ts +8 -0
  41. package/lib/entrypoints/all-external-dependencies.js +10 -0
  42. package/lib/entrypoints/array.d.ts +1 -0
  43. package/lib/entrypoints/array.full.d.ts +17 -0
  44. package/lib/entrypoints/array.full.js +19 -0
  45. package/lib/entrypoints/array.js +1 -0
  46. package/lib/entrypoints/external-scripts-loader.d.ts +24 -0
  47. package/lib/entrypoints/external-scripts-loader.js +107 -0
  48. package/lib/entrypoints/module.es.d.ts +1 -0
  49. package/lib/entrypoints/module.es.js +1 -0
  50. package/lib/entrypoints/recorder.d.ts +23 -0
  51. package/lib/entrypoints/recorder.js +42 -0
  52. package/lib/entrypoints/web-vitals.d.ts +14 -0
  53. package/lib/entrypoints/web-vitals.js +29 -0
  54. package/lib/extensions/replay/index.d.ts +13 -0
  55. package/lib/extensions/replay/index.js +31 -0
  56. package/lib/extensions/replay/session-recording-utils.d.ts +92 -0
  57. package/lib/extensions/replay/session-recording-utils.js +212 -0
  58. package/lib/extensions/replay/session-recording-wrapper.d.ts +61 -0
  59. package/lib/extensions/replay/session-recording-wrapper.js +149 -0
  60. package/lib/extensions/replay/session-recording.d.ts +95 -0
  61. package/lib/extensions/replay/session-recording.js +700 -0
  62. package/lib/extensions/replay/types.d.ts +211 -0
  63. package/lib/extensions/replay/types.js +8 -0
  64. package/lib/types.d.ts +116 -6
  65. package/lib/types.js +16 -0
  66. package/lib/utils/globals.d.ts +69 -0
  67. package/lib/utils/globals.js +2 -0
  68. package/lib/vtilt.d.ts +36 -0
  69. package/lib/vtilt.js +106 -0
  70. package/lib/web-vitals.d.ts +89 -5
  71. package/lib/web-vitals.js +354 -46
  72. package/package.json +4 -1
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ /**
3
+ * External Scripts Loader
4
+ *
5
+ * Dynamically loads external scripts (like rrweb) from CDN.
6
+ * Based on PostHog's implementation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getExtensionUrl = exports.loadScript = void 0;
10
+ const globals_1 = require("../utils/globals");
11
+ const LOGGER_PREFIX = "[ExternalScriptsLoader]";
12
+ /**
13
+ * Load a script dynamically
14
+ */
15
+ const loadScript = (vtilt, url, callback) => {
16
+ const config = vtilt.getConfig();
17
+ // Check if external script loading is disabled
18
+ if (config.disable_external_dependency_loading) {
19
+ console.warn(`${LOGGER_PREFIX} ${url} was requested but loading of external scripts is disabled.`);
20
+ return callback("Loading of external scripts is disabled");
21
+ }
22
+ // Check if script is already loaded
23
+ const existingScripts = globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.querySelectorAll("script");
24
+ if (existingScripts) {
25
+ for (let i = 0; i < existingScripts.length; i++) {
26
+ if (existingScripts[i].src === url) {
27
+ const alreadyExistingScriptTag = existingScripts[i];
28
+ if (alreadyExistingScriptTag.__vtilt_loading_callback_fired) {
29
+ // Script already exists and fired its load event
30
+ return callback();
31
+ }
32
+ // Script exists but hasn't loaded yet - attach callback
33
+ alreadyExistingScriptTag.addEventListener("load", (event) => {
34
+ alreadyExistingScriptTag.__vtilt_loading_callback_fired = true;
35
+ callback(undefined, event);
36
+ });
37
+ alreadyExistingScriptTag.onerror = (error) => callback(error);
38
+ return;
39
+ }
40
+ }
41
+ }
42
+ const addScript = () => {
43
+ var _a;
44
+ if (!globals_1.document) {
45
+ return callback("document not found");
46
+ }
47
+ const scriptTag = globals_1.document.createElement("script");
48
+ scriptTag.type = "text/javascript";
49
+ scriptTag.crossOrigin = "anonymous";
50
+ scriptTag.src = url;
51
+ scriptTag.onload = (event) => {
52
+ scriptTag.__vtilt_loading_callback_fired = true;
53
+ callback(undefined, event);
54
+ };
55
+ scriptTag.onerror = (error) => callback(error);
56
+ const scripts = globals_1.document.querySelectorAll("body > script");
57
+ if (scripts.length > 0) {
58
+ (_a = scripts[0].parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(scriptTag, scripts[0]);
59
+ }
60
+ else {
61
+ globals_1.document.body.appendChild(scriptTag);
62
+ }
63
+ };
64
+ if (globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.body) {
65
+ addScript();
66
+ }
67
+ else {
68
+ globals_1.document === null || globals_1.document === void 0 ? void 0 : globals_1.document.addEventListener("DOMContentLoaded", addScript);
69
+ }
70
+ };
71
+ exports.loadScript = loadScript;
72
+ /**
73
+ * SDK version - used for loading correct script version from CDN
74
+ */
75
+ const SDK_VERSION = "1.3.0";
76
+ /**
77
+ * Get the URL for an extension script
78
+ *
79
+ * Priority:
80
+ * 1. script_host config (if set) - loads from {script_host}/dist/{kind}.js
81
+ * 2. Default to unpkg CDN for npm package
82
+ *
83
+ * Note: script_host should be the base URL (e.g., "https://cdn.example.com")
84
+ * The /dist/ path is automatically appended to match the npm package structure
85
+ */
86
+ const getExtensionUrl = (vtilt, kind) => {
87
+ const config = vtilt.getConfig();
88
+ // Use script_host if configured (matches snippet behavior: script_host+"/dist/array.js")
89
+ if (config.script_host) {
90
+ const cleanHost = config.script_host.replace(/\/+$/gm, "");
91
+ return `${cleanHost}/dist/${kind}.js`;
92
+ }
93
+ // Default: load from unpkg (npm CDN)
94
+ // This serves the file directly from the published @v-tilt/browser package
95
+ return `https://unpkg.com/@v-tilt/browser@${SDK_VERSION}/dist/${kind}.js`;
96
+ };
97
+ exports.getExtensionUrl = getExtensionUrl;
98
+ // Initialize extensions object
99
+ globals_1.assignableWindow.__VTiltExtensions__ =
100
+ globals_1.assignableWindow.__VTiltExtensions__ || {};
101
+ /**
102
+ * Load an external dependency
103
+ */
104
+ globals_1.assignableWindow.__VTiltExtensions__.loadExternalDependency = (vtilt, kind, callback) => {
105
+ const url = getExtensionUrl(vtilt, kind);
106
+ loadScript(vtilt, url, callback);
107
+ };
@@ -1,3 +1,4 @@
1
+ import "./external-scripts-loader";
1
2
  import vt from "./module.no-external.es";
2
3
  export * from "./module.no-external.es";
3
4
  export default vt;
@@ -17,6 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
17
17
  return (mod && mod.__esModule) ? mod : { "default": mod };
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
+ require("./external-scripts-loader");
20
21
  const module_no_external_es_1 = __importDefault(require("./module.no-external.es"));
21
22
  __exportStar(require("./module.no-external.es"), exports);
22
23
  exports.default = module_no_external_es_1.default;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Recorder Entrypoint
3
+ *
4
+ * This file is built as a separate bundle (recorder.js) that can be
5
+ * lazy-loaded when session recording is enabled.
6
+ *
7
+ * It exports:
8
+ * - rrweb.record: The rrweb record function
9
+ * - rrwebPlugins: Plugins for console recording
10
+ * - initSessionRecording: Factory function to create LazyLoadedSessionRecording
11
+ */
12
+ import { record as rrwebRecord } from "@rrweb/record";
13
+ import { LazyLoadedSessionRecordingInterface } from "../utils/globals";
14
+ import type { SessionRecordingConfig } from "../extensions/replay/types";
15
+ import type { VTilt } from "../vtilt";
16
+ /**
17
+ * Factory function to create a LazyLoadedSessionRecording instance
18
+ *
19
+ * Called by SessionRecordingWrapper after the recorder script is loaded
20
+ */
21
+ declare function initSessionRecording(instance: VTilt, config?: SessionRecordingConfig): LazyLoadedSessionRecordingInterface;
22
+ export { initSessionRecording };
23
+ export default rrwebRecord;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ /**
3
+ * Recorder Entrypoint
4
+ *
5
+ * This file is built as a separate bundle (recorder.js) that can be
6
+ * lazy-loaded when session recording is enabled.
7
+ *
8
+ * It exports:
9
+ * - rrweb.record: The rrweb record function
10
+ * - rrwebPlugins: Plugins for console recording
11
+ * - initSessionRecording: Factory function to create LazyLoadedSessionRecording
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.initSessionRecording = initSessionRecording;
15
+ const record_1 = require("@rrweb/record");
16
+ const rrweb_plugin_console_record_1 = require("@rrweb/rrweb-plugin-console-record");
17
+ const globals_1 = require("../utils/globals");
18
+ const session_recording_1 = require("../extensions/replay/session-recording");
19
+ // Initialize extensions object
20
+ globals_1.assignableWindow.__VTiltExtensions__ =
21
+ globals_1.assignableWindow.__VTiltExtensions__ || {};
22
+ // Register rrweb record function
23
+ globals_1.assignableWindow.__VTiltExtensions__.rrweb = {
24
+ record: record_1.record,
25
+ version: "v2",
26
+ };
27
+ // Register rrweb plugins
28
+ globals_1.assignableWindow.__VTiltExtensions__.rrwebPlugins = {
29
+ getRecordConsolePlugin: rrweb_plugin_console_record_1.getRecordConsolePlugin,
30
+ };
31
+ /**
32
+ * Factory function to create a LazyLoadedSessionRecording instance
33
+ *
34
+ * Called by SessionRecordingWrapper after the recorder script is loaded
35
+ */
36
+ function initSessionRecording(instance, config) {
37
+ return new session_recording_1.LazyLoadedSessionRecording(instance, config);
38
+ }
39
+ // Register the factory function
40
+ globals_1.assignableWindow.__VTiltExtensions__.initSessionRecording =
41
+ initSessionRecording;
42
+ exports.default = record_1.record;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Web Vitals Extension Entrypoint
3
+ *
4
+ * This file imports the web-vitals library and registers its callbacks
5
+ * on the global __VTiltExtensions__ object. The main SDK checks for these
6
+ * callbacks and uses them if available.
7
+ *
8
+ * Usage:
9
+ * - Include this entrypoint in the full bundle (array.full.ts)
10
+ * - Or load it dynamically via loadExternalDependency('web-vitals', callback)
11
+ */
12
+ import { WebVitalsCallbacks } from "../utils/globals";
13
+ declare const vtiltWebVitalsCallbacks: WebVitalsCallbacks;
14
+ export default vtiltWebVitalsCallbacks;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ /**
3
+ * Web Vitals Extension Entrypoint
4
+ *
5
+ * This file imports the web-vitals library and registers its callbacks
6
+ * on the global __VTiltExtensions__ object. The main SDK checks for these
7
+ * callbacks and uses them if available.
8
+ *
9
+ * Usage:
10
+ * - Include this entrypoint in the full bundle (array.full.ts)
11
+ * - Or load it dynamically via loadExternalDependency('web-vitals', callback)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ const web_vitals_1 = require("web-vitals");
15
+ const globals_1 = require("../utils/globals");
16
+ const vtiltWebVitalsCallbacks = {
17
+ onLCP: web_vitals_1.onLCP,
18
+ onCLS: web_vitals_1.onCLS,
19
+ onFCP: web_vitals_1.onFCP,
20
+ onINP: web_vitals_1.onINP,
21
+ onTTFB: web_vitals_1.onTTFB,
22
+ };
23
+ // Initialize extensions object if not exists
24
+ globals_1.assignableWindow.__VTiltExtensions__ =
25
+ globals_1.assignableWindow.__VTiltExtensions__ || {};
26
+ // Register web vitals callbacks
27
+ globals_1.assignableWindow.__VTiltExtensions__.webVitalsCallbacks =
28
+ vtiltWebVitalsCallbacks;
29
+ exports.default = vtiltWebVitalsCallbacks;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Session Recording Extension
3
+ *
4
+ * Exports for rrweb session recording functionality.
5
+ *
6
+ * Architecture:
7
+ * - SessionRecordingWrapper: Lightweight wrapper in main bundle, handles lazy loading
8
+ * - LazyLoadedSessionRecording: Actual recording logic, loaded on demand via recorder.js
9
+ */
10
+ export { SessionRecordingWrapper, LAZY_LOADING, type SessionRecordingStatus, type LazyLoadedSessionRecordingInterface, } from "./session-recording-wrapper";
11
+ export { LazyLoadedSessionRecording } from "./session-recording";
12
+ export type { SessionRecordingConfig, SessionStartReason, RecordOptions, RRWebRecord, SnapshotBuffer, CanvasRecordingConfig, NetworkPayloadCaptureConfig, MaskingConfig, TriggerType, } from "./types";
13
+ export { compressEvent, estimateSize, truncateLargeConsoleLogs, splitBuffer, isSessionIdleEvent, isRecordingPausedEvent, isInteractiveEvent, RECORDING_MAX_EVENT_SIZE, RECORDING_BUFFER_TIMEOUT, RECORDING_IDLE_THRESHOLD_MS, } from "./session-recording-utils";
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Session Recording Extension
4
+ *
5
+ * Exports for rrweb session recording functionality.
6
+ *
7
+ * Architecture:
8
+ * - SessionRecordingWrapper: Lightweight wrapper in main bundle, handles lazy loading
9
+ * - LazyLoadedSessionRecording: Actual recording logic, loaded on demand via recorder.js
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RECORDING_IDLE_THRESHOLD_MS = exports.RECORDING_BUFFER_TIMEOUT = exports.RECORDING_MAX_EVENT_SIZE = exports.isInteractiveEvent = exports.isRecordingPausedEvent = exports.isSessionIdleEvent = exports.splitBuffer = exports.truncateLargeConsoleLogs = exports.estimateSize = exports.compressEvent = exports.LazyLoadedSessionRecording = exports.LAZY_LOADING = exports.SessionRecordingWrapper = void 0;
13
+ // Main export - the wrapper that lives in the main bundle
14
+ var session_recording_wrapper_1 = require("./session-recording-wrapper");
15
+ Object.defineProperty(exports, "SessionRecordingWrapper", { enumerable: true, get: function () { return session_recording_wrapper_1.SessionRecordingWrapper; } });
16
+ Object.defineProperty(exports, "LAZY_LOADING", { enumerable: true, get: function () { return session_recording_wrapper_1.LAZY_LOADING; } });
17
+ // Lazy-loaded implementation (only used in recorder.js bundle)
18
+ var session_recording_1 = require("./session-recording");
19
+ Object.defineProperty(exports, "LazyLoadedSessionRecording", { enumerable: true, get: function () { return session_recording_1.LazyLoadedSessionRecording; } });
20
+ // Utilities
21
+ var session_recording_utils_1 = require("./session-recording-utils");
22
+ Object.defineProperty(exports, "compressEvent", { enumerable: true, get: function () { return session_recording_utils_1.compressEvent; } });
23
+ Object.defineProperty(exports, "estimateSize", { enumerable: true, get: function () { return session_recording_utils_1.estimateSize; } });
24
+ Object.defineProperty(exports, "truncateLargeConsoleLogs", { enumerable: true, get: function () { return session_recording_utils_1.truncateLargeConsoleLogs; } });
25
+ Object.defineProperty(exports, "splitBuffer", { enumerable: true, get: function () { return session_recording_utils_1.splitBuffer; } });
26
+ Object.defineProperty(exports, "isSessionIdleEvent", { enumerable: true, get: function () { return session_recording_utils_1.isSessionIdleEvent; } });
27
+ Object.defineProperty(exports, "isRecordingPausedEvent", { enumerable: true, get: function () { return session_recording_utils_1.isRecordingPausedEvent; } });
28
+ Object.defineProperty(exports, "isInteractiveEvent", { enumerable: true, get: function () { return session_recording_utils_1.isInteractiveEvent; } });
29
+ Object.defineProperty(exports, "RECORDING_MAX_EVENT_SIZE", { enumerable: true, get: function () { return session_recording_utils_1.RECORDING_MAX_EVENT_SIZE; } });
30
+ Object.defineProperty(exports, "RECORDING_BUFFER_TIMEOUT", { enumerable: true, get: function () { return session_recording_utils_1.RECORDING_BUFFER_TIMEOUT; } });
31
+ Object.defineProperty(exports, "RECORDING_IDLE_THRESHOLD_MS", { enumerable: true, get: function () { return session_recording_utils_1.RECORDING_IDLE_THRESHOLD_MS; } });
@@ -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
+ }