@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.
- package/dist/all-external-dependencies.js +2 -0
- package/dist/all-external-dependencies.js.map +1 -0
- package/dist/array.full.js +2 -0
- package/dist/array.full.js.map +1 -0
- 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/all-external-dependencies.d.ts +8 -0
- package/dist/entrypoints/array.d.ts +1 -0
- package/dist/entrypoints/array.full.d.ts +17 -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/entrypoints/web-vitals.d.ts +14 -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 +297 -8
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +297 -8
- 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 +116 -6
- package/dist/utils/globals.d.ts +69 -0
- package/dist/vtilt.d.ts +36 -0
- package/dist/web-vitals.d.ts +89 -5
- package/dist/web-vitals.js +2 -0
- package/dist/web-vitals.js.map +1 -0
- package/lib/config.js +7 -3
- package/lib/entrypoints/all-external-dependencies.d.ts +8 -0
- package/lib/entrypoints/all-external-dependencies.js +10 -0
- package/lib/entrypoints/array.d.ts +1 -0
- package/lib/entrypoints/array.full.d.ts +17 -0
- package/lib/entrypoints/array.full.js +19 -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/entrypoints/web-vitals.d.ts +14 -0
- package/lib/entrypoints/web-vitals.js +29 -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 +116 -6
- package/lib/types.js +16 -0
- package/lib/utils/globals.d.ts +69 -0
- package/lib/utils/globals.js +2 -0
- package/lib/vtilt.d.ts +36 -0
- package/lib/vtilt.js +106 -0
- package/lib/web-vitals.d.ts +89 -5
- package/lib/web-vitals.js +354 -46
- 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
|
+
};
|
|
@@ -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
|
+
}
|