@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,211 @@
1
+ /**
2
+ * Session Recording Types
3
+ *
4
+ * Type definitions for rrweb session recording.
5
+ * Based on PostHog's implementation.
6
+ */
7
+ import type { blockClass, eventWithTime, hooksParam, KeepIframeSrcFn, maskTextClass, PackFn, RecordPlugin, SamplingStrategy } from "@rrweb/types";
8
+ /** Mask options for input elements */
9
+ export type MaskInputOptions = Partial<{
10
+ color: boolean;
11
+ date: boolean;
12
+ "datetime-local": boolean;
13
+ email: boolean;
14
+ month: boolean;
15
+ number: boolean;
16
+ range: boolean;
17
+ search: boolean;
18
+ tel: boolean;
19
+ text: boolean;
20
+ time: boolean;
21
+ url: boolean;
22
+ week: boolean;
23
+ textarea: boolean;
24
+ select: boolean;
25
+ password: boolean;
26
+ }>;
27
+ /** Function to mask input values */
28
+ export type MaskInputFn = (text: string, element: HTMLElement) => string;
29
+ /** Function to mask text content */
30
+ export type MaskTextFn = (text: string, element: HTMLElement | null) => string;
31
+ /** Options for slim DOM mode */
32
+ export type SlimDOMOptions = Partial<{
33
+ script: boolean;
34
+ comment: boolean;
35
+ headFavicon: boolean;
36
+ headWhitespace: boolean;
37
+ headMetaDescKeywords: boolean;
38
+ headMetaSocial: boolean;
39
+ headMetaRobots: boolean;
40
+ headMetaHttpEquiv: boolean;
41
+ headMetaAuthorship: boolean;
42
+ headMetaVerification: boolean;
43
+ headTitleMutations: boolean;
44
+ }>;
45
+ /** Options for data URL generation */
46
+ export type DataURLOptions = Partial<{
47
+ type: string;
48
+ quality: number;
49
+ }>;
50
+ /** Error handler type */
51
+ export type ErrorHandler = (error: unknown) => void | boolean;
52
+ /** rrweb record options */
53
+ export interface RecordOptions {
54
+ emit?: (e: eventWithTime, isCheckout?: boolean) => void;
55
+ checkoutEveryNth?: number;
56
+ checkoutEveryNms?: number;
57
+ blockClass?: blockClass;
58
+ blockSelector?: string;
59
+ ignoreClass?: string;
60
+ ignoreSelector?: string;
61
+ maskTextClass?: maskTextClass;
62
+ maskTextSelector?: string;
63
+ maskAllInputs?: boolean;
64
+ maskInputOptions?: MaskInputOptions;
65
+ maskInputFn?: MaskInputFn;
66
+ maskTextFn?: MaskTextFn;
67
+ slimDOMOptions?: SlimDOMOptions | "all" | true;
68
+ ignoreCSSAttributes?: Set<string>;
69
+ inlineStylesheet?: boolean;
70
+ hooks?: hooksParam;
71
+ packFn?: PackFn;
72
+ sampling?: SamplingStrategy;
73
+ dataURLOptions?: DataURLOptions;
74
+ recordDOM?: boolean;
75
+ recordCanvas?: boolean;
76
+ recordCrossOriginIframes?: boolean;
77
+ recordAfter?: "DOMContentLoaded" | "load";
78
+ userTriggeredOnInput?: boolean;
79
+ collectFonts?: boolean;
80
+ inlineImages?: boolean;
81
+ plugins?: RecordPlugin[];
82
+ mousemoveWait?: number;
83
+ keepIframeSrcFn?: KeepIframeSrcFn;
84
+ errorHandler?: ErrorHandler;
85
+ }
86
+ /** rrweb record function type */
87
+ export interface RRWebRecord {
88
+ (options: RecordOptions): () => void;
89
+ addCustomEvent: (tag: string, payload: unknown) => void;
90
+ takeFullSnapshot: () => void;
91
+ mirror: {
92
+ getId(n: Node | undefined | null): number;
93
+ getNode(id: number): Node | null;
94
+ };
95
+ }
96
+ /** Canvas recording configuration */
97
+ export interface CanvasRecordingConfig {
98
+ enabled: boolean;
99
+ fps: number;
100
+ quality: number;
101
+ }
102
+ /** Network payload capture configuration */
103
+ export interface NetworkPayloadCaptureConfig {
104
+ recordHeaders?: boolean;
105
+ recordBody?: boolean;
106
+ recordPerformance?: boolean;
107
+ }
108
+ /** Masking configuration */
109
+ export interface MaskingConfig {
110
+ maskAllInputs?: boolean;
111
+ maskTextSelector?: string;
112
+ blockSelector?: string;
113
+ }
114
+ /** Session recording configuration */
115
+ export interface SessionRecordingConfig {
116
+ /** Enable session recording */
117
+ enabled?: boolean;
118
+ /** Sample rate (0-1, where 1 = 100%) */
119
+ sampleRate?: number;
120
+ /** Minimum session duration in ms before sending */
121
+ minimumDurationMs?: number;
122
+ /** Session idle threshold in ms */
123
+ sessionIdleThresholdMs?: number;
124
+ /** Full snapshot interval in ms */
125
+ fullSnapshotIntervalMs?: number;
126
+ /** Enable console log capture */
127
+ captureConsole?: boolean;
128
+ /** Enable network request capture */
129
+ captureNetwork?: boolean;
130
+ /** Canvas recording settings */
131
+ captureCanvas?: {
132
+ recordCanvas?: boolean;
133
+ canvasFps?: number;
134
+ canvasQuality?: number;
135
+ };
136
+ /** Masking settings */
137
+ masking?: MaskingConfig;
138
+ /** Block class for elements to hide */
139
+ blockClass?: string;
140
+ /** Block selector for elements to hide */
141
+ blockSelector?: string;
142
+ /** Ignore class for input masking */
143
+ ignoreClass?: string;
144
+ /** Mask text class */
145
+ maskTextClass?: string;
146
+ /** Mask text selector */
147
+ maskTextSelector?: string;
148
+ /** Mask all inputs */
149
+ maskAllInputs?: boolean;
150
+ /** Mask input options */
151
+ maskInputOptions?: MaskInputOptions;
152
+ /** Record headers in network requests */
153
+ recordHeaders?: boolean;
154
+ /** Record body in network requests */
155
+ recordBody?: boolean;
156
+ /** Compress events before sending */
157
+ compressEvents?: boolean;
158
+ /** Internal: Mutation throttler refill rate */
159
+ __mutationThrottlerRefillRate?: number;
160
+ /** Internal: Mutation throttler bucket size */
161
+ __mutationThrottlerBucketSize?: number;
162
+ }
163
+ /** Recording status values */
164
+ export type SessionRecordingStatus = "disabled" | "buffering" | "active" | "paused" | "sampled" | "trigger_pending";
165
+ /** Recording start reason */
166
+ export type SessionStartReason = "recording_initialized" | "session_id_changed" | "linked_flag_matched" | "linked_flag_overridden" | "sampling_overridden" | "url_trigger_matched" | "event_trigger_matched" | "sampled";
167
+ /** Trigger type for starting recording */
168
+ export type TriggerType = "url" | "event";
169
+ /** Buffer for snapshot events */
170
+ export interface SnapshotBuffer {
171
+ size: number;
172
+ data: eventWithTime[];
173
+ sessionId: string;
174
+ windowId: string;
175
+ }
176
+ /** Remote configuration for session recording */
177
+ export interface SessionRecordingRemoteConfig {
178
+ enabled?: boolean;
179
+ endpoint?: string;
180
+ sampleRate?: string;
181
+ minimumDurationMilliseconds?: number;
182
+ consoleLogRecordingEnabled?: boolean;
183
+ networkPayloadCapture?: {
184
+ recordHeaders?: boolean;
185
+ recordBody?: boolean;
186
+ capturePerformance?: boolean;
187
+ };
188
+ masking?: MaskingConfig;
189
+ recordCanvas?: boolean;
190
+ canvasFps?: number;
191
+ canvasQuality?: number;
192
+ scriptConfig?: {
193
+ script?: string;
194
+ };
195
+ triggerMatchType?: "any" | "all";
196
+ urlTriggers?: Array<{
197
+ url: string;
198
+ matching: "regex" | "exact" | "contains";
199
+ }>;
200
+ urlBlocklist?: Array<{
201
+ url: string;
202
+ matching: "regex" | "exact" | "contains";
203
+ }>;
204
+ eventTriggers?: string[];
205
+ }
206
+ /** Queued rrweb method call */
207
+ export interface QueuedRRWebEvent {
208
+ rrwebMethod: () => void;
209
+ attempt: number;
210
+ enqueuedAt: number;
211
+ }
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Session Recording Types
4
+ *
5
+ * Type definitions for rrweb session recording.
6
+ * Based on PostHog's implementation.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/types.d.ts CHANGED
@@ -12,6 +12,13 @@ export interface VTiltConfig {
12
12
  api_host?: string;
13
13
  /** UI host for dashboard links */
14
14
  ui_host?: string | null;
15
+ /**
16
+ * Host for loading extension scripts (recorder.js, etc.)
17
+ * Defaults to unpkg CDN: https://unpkg.com/@v-tilt/browser@{version}/dist
18
+ * Can also use jsdelivr: https://cdn.jsdelivr.net/npm/@v-tilt/browser@{version}/dist
19
+ * Or self-hosted: https://your-domain.com/static
20
+ */
21
+ script_host?: string;
15
22
  /** Proxy domain for tracking requests */
16
23
  proxy?: string;
17
24
  /** Full proxy URL for tracking requests */
@@ -44,8 +51,11 @@ export interface VTiltConfig {
44
51
  person_profiles?: PersonProfilesMode;
45
52
  /** Enable autocapture */
46
53
  autocapture?: boolean;
47
- /** Enable web vitals tracking */
48
- capture_performance?: boolean;
54
+ /**
55
+ * Enable web vitals tracking.
56
+ * Can be a boolean or an object with detailed configuration.
57
+ */
58
+ capture_performance?: boolean | CapturePerformanceConfig;
49
59
  /** Enable page view tracking */
50
60
  capture_pageview?: boolean | "auto";
51
61
  /** Enable page leave tracking */
@@ -64,6 +74,10 @@ export interface VTiltConfig {
64
74
  respect_dnt?: boolean;
65
75
  /** Opt users out by default */
66
76
  opt_out_capturing_by_default?: boolean;
77
+ /** Session recording configuration */
78
+ session_recording?: SessionRecordingOptions;
79
+ /** Disable session recording (convenience flag) */
80
+ disable_session_recording?: boolean;
67
81
  /** Global attributes added to all events */
68
82
  globalAttributes?: Record<string, string>;
69
83
  /** Bootstrap data for initialization */
@@ -149,6 +163,29 @@ export interface AliasEvent {
149
163
  distinct_id: string;
150
164
  original: string;
151
165
  }
166
+ /** Supported Web Vitals metrics */
167
+ export type SupportedWebVitalsMetric = "LCP" | "CLS" | "FCP" | "INP" | "TTFB";
168
+ /** All supported Web Vitals metrics */
169
+ export declare const ALL_WEB_VITALS_METRICS: SupportedWebVitalsMetric[];
170
+ /** Default Web Vitals metrics (matches PostHog defaults) */
171
+ export declare const DEFAULT_WEB_VITALS_METRICS: SupportedWebVitalsMetric[];
172
+ /**
173
+ * Web Vitals capture configuration
174
+ */
175
+ export interface CapturePerformanceConfig {
176
+ /** Enable or disable web vitals capture */
177
+ web_vitals?: boolean;
178
+ /** Which metrics to capture (default: LCP, CLS, FCP, INP) */
179
+ web_vitals_allowed_metrics?: SupportedWebVitalsMetric[];
180
+ /** Delay before flushing metrics in ms (default: 5000) */
181
+ web_vitals_delayed_flush_ms?: number;
182
+ /**
183
+ * Maximum allowed metric value in ms (default: 900000 = 15 minutes)
184
+ * Values above this are considered anomalies and ignored.
185
+ * Set to 0 to disable this check.
186
+ */
187
+ __web_vitals_max_value?: number;
188
+ }
152
189
  export interface WebVitalMetric {
153
190
  name: string;
154
191
  value: number;
@@ -156,6 +193,10 @@ export interface WebVitalMetric {
156
193
  rating: "good" | "needs-improvement" | "poor";
157
194
  id: string;
158
195
  navigationType: string;
196
+ /** Timestamp when the metric was captured (added internally) */
197
+ timestamp?: number;
198
+ /** Attribution data from web-vitals library */
199
+ attribution?: Record<string, unknown>;
159
200
  }
160
201
  export interface GeolocationData {
161
202
  country?: string;
@@ -176,14 +217,83 @@ export interface RequestOptions {
176
217
  timeout?: number;
177
218
  retry?: boolean;
178
219
  }
220
+ /** Mask options for input elements in session recording */
221
+ export interface SessionRecordingMaskInputOptions {
222
+ color?: boolean;
223
+ date?: boolean;
224
+ "datetime-local"?: boolean;
225
+ email?: boolean;
226
+ month?: boolean;
227
+ number?: boolean;
228
+ range?: boolean;
229
+ search?: boolean;
230
+ tel?: boolean;
231
+ text?: boolean;
232
+ time?: boolean;
233
+ url?: boolean;
234
+ week?: boolean;
235
+ textarea?: boolean;
236
+ select?: boolean;
237
+ password?: boolean;
238
+ }
239
+ /** Session recording configuration */
240
+ export interface SessionRecordingOptions {
241
+ /** Enable session recording */
242
+ enabled?: boolean;
243
+ /** Sample rate (0-1, where 1 = 100%) */
244
+ sampleRate?: number;
245
+ /** Minimum session duration in ms before sending */
246
+ minimumDurationMs?: number;
247
+ /** Session idle threshold in ms (default: 5 minutes) */
248
+ sessionIdleThresholdMs?: number;
249
+ /** Full snapshot interval in ms (default: 5 minutes) */
250
+ fullSnapshotIntervalMs?: number;
251
+ /** Enable console log capture */
252
+ captureConsole?: boolean;
253
+ /** Enable network request capture */
254
+ captureNetwork?: boolean;
255
+ /** Canvas recording settings */
256
+ captureCanvas?: {
257
+ recordCanvas?: boolean;
258
+ canvasFps?: number;
259
+ canvasQuality?: number;
260
+ };
261
+ /** Block class for elements to hide (default: 'vt-no-capture') */
262
+ blockClass?: string;
263
+ /** Block selector for elements to hide */
264
+ blockSelector?: string;
265
+ /** Ignore class for input masking (default: 'vt-ignore-input') */
266
+ ignoreClass?: string;
267
+ /** Mask text class (default: 'vt-mask') */
268
+ maskTextClass?: string;
269
+ /** Mask text selector */
270
+ maskTextSelector?: string;
271
+ /** Mask all inputs (default: true) */
272
+ maskAllInputs?: boolean;
273
+ /** Mask input options */
274
+ maskInputOptions?: SessionRecordingMaskInputOptions;
275
+ /** Masking configuration */
276
+ masking?: {
277
+ maskAllInputs?: boolean;
278
+ maskTextSelector?: string;
279
+ blockSelector?: string;
280
+ };
281
+ /** Record headers in network requests */
282
+ recordHeaders?: boolean;
283
+ /** Record body in network requests */
284
+ recordBody?: boolean;
285
+ /** Compress events before sending (default: true) */
286
+ compressEvents?: boolean;
287
+ /** Internal: Mutation throttler refill rate */
288
+ __mutationThrottlerRefillRate?: number;
289
+ /** Internal: Mutation throttler bucket size */
290
+ __mutationThrottlerBucketSize?: number;
291
+ }
179
292
  export interface RemoteConfig {
180
293
  /** Default to identified_only mode */
181
294
  defaultIdentifiedOnly?: boolean;
182
295
  /** Feature flags */
183
296
  featureFlags?: FeatureFlagsConfig;
184
297
  /** Session recording config */
185
- sessionRecording?: {
186
- enabled?: boolean;
187
- sampleRate?: number;
188
- };
298
+ sessionRecording?: SessionRecordingOptions;
189
299
  }
package/lib/types.js CHANGED
@@ -6,3 +6,19 @@
6
6
  * Following PostHog's patterns where applicable.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.DEFAULT_WEB_VITALS_METRICS = exports.ALL_WEB_VITALS_METRICS = void 0;
10
+ /** All supported Web Vitals metrics */
11
+ exports.ALL_WEB_VITALS_METRICS = [
12
+ "LCP",
13
+ "CLS",
14
+ "FCP",
15
+ "INP",
16
+ "TTFB",
17
+ ];
18
+ /** Default Web Vitals metrics (matches PostHog defaults) */
19
+ exports.DEFAULT_WEB_VITALS_METRICS = [
20
+ "LCP",
21
+ "CLS",
22
+ "FCP",
23
+ "INP",
24
+ ];
@@ -1,6 +1,75 @@
1
1
  declare const win: (Window & typeof globalThis) | undefined;
2
+ /**
3
+ * Extension kinds that can be lazy loaded
4
+ */
5
+ export type VTiltExtensionKind = "recorder" | "web-vitals";
6
+ /**
7
+ * Interface for lazy-loaded session recording (set by recorder.ts)
8
+ * Matches LazyLoadedSessionRecordingInterface in session-recording-wrapper.ts
9
+ */
10
+ export interface LazyLoadedSessionRecordingInterface {
11
+ start: (startReason?: string) => void;
12
+ stop: () => void;
13
+ sessionId: string;
14
+ status: string;
15
+ isStarted: boolean;
16
+ onRemoteConfig?: (response: any) => void;
17
+ log: (message: string, level: "log" | "warn" | "error") => void;
18
+ updateConfig: (config: any) => void;
19
+ }
20
+ /**
21
+ * Web Vitals metric callback type
22
+ */
23
+ export type WebVitalsMetricCallback = (metric: any) => void;
24
+ /**
25
+ * Options for web vitals callbacks
26
+ */
27
+ export interface WebVitalsCallbackOptions {
28
+ /** Report all changes (useful for CLS which updates over time) */
29
+ reportAllChanges?: boolean;
30
+ }
31
+ /**
32
+ * Web Vitals callback function type (with optional options)
33
+ */
34
+ export type WebVitalsCallbackFn = (callback: WebVitalsMetricCallback, options?: WebVitalsCallbackOptions) => void;
35
+ /**
36
+ * Web Vitals callbacks interface (set by web-vitals.ts entrypoint)
37
+ */
38
+ export interface WebVitalsCallbacks {
39
+ onLCP: WebVitalsCallbackFn;
40
+ onCLS: WebVitalsCallbackFn;
41
+ onFCP: WebVitalsCallbackFn;
42
+ onINP: WebVitalsCallbackFn;
43
+ onTTFB?: WebVitalsCallbackFn;
44
+ }
45
+ /**
46
+ * VTilt Extensions interface for dynamically loaded modules
47
+ * This is the contract between lazily loaded extensions and the SDK
48
+ */
49
+ export interface VTiltExtensions {
50
+ /** Load an external dependency script */
51
+ loadExternalDependency?: (instance: any, // VTilt instance - using any to avoid circular imports
52
+ kind: VTiltExtensionKind, callback: (error?: string | Event, event?: Event) => void) => void;
53
+ /** rrweb record function (set by recorder.ts) */
54
+ rrweb?: {
55
+ record: any;
56
+ version?: string;
57
+ };
58
+ /** rrweb plugins (set by recorder.ts) */
59
+ rrwebPlugins?: {
60
+ getRecordConsolePlugin?: () => any;
61
+ getRecordNetworkPlugin?: (options: any) => any;
62
+ };
63
+ /** Factory to create LazyLoadedSessionRecording (set by recorder.ts) */
64
+ initSessionRecording?: (instance: any, config?: any) => LazyLoadedSessionRecordingInterface;
65
+ /** Web Vitals callbacks (set by web-vitals.ts entrypoint) */
66
+ webVitalsCallbacks?: WebVitalsCallbacks;
67
+ }
2
68
  export type AssignableWindow = Window & typeof globalThis & {
69
+ /** Main VTilt instance */
3
70
  vt: any;
71
+ /** VTilt Extensions for dynamically loaded modules */
72
+ __VTiltExtensions__?: VTiltExtensions;
4
73
  };
5
74
  export declare const ArrayProto: any[];
6
75
  export declare const nativeForEach: (callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any) => void;
@@ -10,6 +10,8 @@
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.window = exports.assignableWindow = exports.userAgent = exports.AbortController = exports.XMLHttpRequest = exports.fetch = exports.location = exports.document = exports.navigator = exports.nativeIndexOf = exports.nativeForEach = exports.ArrayProto = void 0;
13
+ // Note: We use 'any' for VTilt to avoid circular imports
14
+ // The actual type checking happens in the external-scripts-loader.ts
13
15
  const win = typeof window !== "undefined" ? window : undefined;
14
16
  exports.window = win;
15
17
  const global = typeof globalThis !== "undefined" ? globalThis : win;
package/lib/vtilt.d.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { VTiltConfig, EventPayload } from "./types";
2
2
  import { HistoryAutocapture } from "./extensions/history-autocapture";
3
+ import { SessionRecordingWrapper } from "./extensions/replay";
3
4
  import { type QueuedRequest } from "./request-queue";
4
5
  export declare class VTilt {
6
+ readonly version = "1.1.5";
5
7
  private configManager;
6
8
  private sessionManager;
7
9
  private userManager;
@@ -10,6 +12,7 @@ export declare class VTilt {
10
12
  private retryQueue;
11
13
  private rateLimiter;
12
14
  historyAutocapture?: HistoryAutocapture;
15
+ sessionRecording?: SessionRecordingWrapper;
13
16
  __loaded: boolean;
14
17
  private _initial_pageview_captured;
15
18
  private _visibility_state_listener;
@@ -236,10 +239,43 @@ export declare class VTilt {
236
239
  * Get current session ID
237
240
  */
238
241
  getSessionId(): string | null;
242
+ /**
243
+ * Get current distinct ID
244
+ */
245
+ getDistinctId(): string;
246
+ /**
247
+ * Get anonymous ID
248
+ */
249
+ getAnonymousId(): string;
239
250
  /**
240
251
  * Update configuration
241
252
  */
242
253
  updateConfig(config: Partial<VTiltConfig>): void;
254
+ /**
255
+ * Initialize session recording
256
+ */
257
+ private _initSessionRecording;
258
+ /**
259
+ * Build session recording config from VTiltConfig
260
+ */
261
+ private _buildSessionRecordingConfig;
262
+ /**
263
+ * Start session recording
264
+ * Call this to manually start recording if it wasn't enabled initially
265
+ */
266
+ startSessionRecording(): void;
267
+ /**
268
+ * Stop session recording
269
+ */
270
+ stopSessionRecording(): void;
271
+ /**
272
+ * Check if session recording is active
273
+ */
274
+ isSessionRecordingActive(): boolean;
275
+ /**
276
+ * Get session recording ID
277
+ */
278
+ getSessionRecordingId(): string | null;
243
279
  /**
244
280
  * _execute_array() deals with processing any vTilt function
245
281
  * calls that were called before the vTilt library was loaded
package/lib/vtilt.js CHANGED
@@ -8,6 +8,7 @@ const session_1 = require("./session");
8
8
  const user_manager_1 = require("./user-manager");
9
9
  const web_vitals_1 = require("./web-vitals");
10
10
  const history_autocapture_1 = require("./extensions/history-autocapture");
11
+ const replay_1 = require("./extensions/replay");
11
12
  const request_1 = require("./request");
12
13
  const request_queue_1 = require("./request-queue");
13
14
  const retry_queue_1 = require("./retry-queue");
@@ -31,6 +32,8 @@ Use TypeScript accessibility modifiers (private/protected) for non-public member
31
32
  const isArray = Array.isArray;
32
33
  class VTilt {
33
34
  constructor(config = {}) {
35
+ // SDK version - injected at build time or defaults to package version
36
+ this.version = "1.1.5";
34
37
  this.__loaded = false; // Matches snippet's window.vt.__loaded check
35
38
  this._initial_pageview_captured = false;
36
39
  this._visibility_state_listener = null;
@@ -132,6 +135,8 @@ class VTilt {
132
135
  // Initialize history autocapture
133
136
  this.historyAutocapture = new history_autocapture_1.HistoryAutocapture(this);
134
137
  this.historyAutocapture.startIfEnabled();
138
+ // Initialize session recording if enabled
139
+ this._initSessionRecording();
135
140
  // Set up page unload handler to flush queued events
136
141
  this._setup_unload_handler();
137
142
  // Enable the request queue for batched sending (PostHog pattern)
@@ -719,6 +724,18 @@ class VTilt {
719
724
  getSessionId() {
720
725
  return this.sessionManager.getSessionId();
721
726
  }
727
+ /**
728
+ * Get current distinct ID
729
+ */
730
+ getDistinctId() {
731
+ return (this.userManager.getDistinctId() || this.userManager.getAnonymousId());
732
+ }
733
+ /**
734
+ * Get anonymous ID
735
+ */
736
+ getAnonymousId() {
737
+ return this.userManager.getAnonymousId();
738
+ }
722
739
  /**
723
740
  * Update configuration
724
741
  */
@@ -729,6 +746,95 @@ class VTilt {
729
746
  this.sessionManager = new session_1.SessionManager(fullConfig.storage || "cookie", fullConfig.cross_subdomain_cookie);
730
747
  this.userManager = new user_manager_1.UserManager(fullConfig.persistence || "localStorage", fullConfig.cross_subdomain_cookie);
731
748
  this.webVitalsManager = new web_vitals_1.WebVitalsManager(fullConfig, this);
749
+ // Update session recording config if it exists
750
+ if (this.sessionRecording && fullConfig.session_recording) {
751
+ this.sessionRecording.updateConfig(this._buildSessionRecordingConfig(fullConfig));
752
+ }
753
+ }
754
+ // ============================================================================
755
+ // Session Recording
756
+ // ============================================================================
757
+ /**
758
+ * Initialize session recording
759
+ */
760
+ _initSessionRecording() {
761
+ const config = this.configManager.getConfig();
762
+ // Don't initialize if explicitly disabled
763
+ if (config.disable_session_recording) {
764
+ return;
765
+ }
766
+ // Build session recording config
767
+ const sessionRecordingConfig = this._buildSessionRecordingConfig(config);
768
+ // Create session recording wrapper (lightweight - actual recorder is lazy-loaded)
769
+ this.sessionRecording = new replay_1.SessionRecordingWrapper(this, sessionRecordingConfig);
770
+ // Start recording if enabled (this will lazy-load the recorder script)
771
+ if (sessionRecordingConfig.enabled) {
772
+ this.sessionRecording.startIfEnabledOrStop("recording_initialized");
773
+ }
774
+ }
775
+ /**
776
+ * Build session recording config from VTiltConfig
777
+ */
778
+ _buildSessionRecordingConfig(config) {
779
+ var _a, _b, _c;
780
+ const sessionRecording = config.session_recording || {};
781
+ return {
782
+ enabled: (_a = sessionRecording.enabled) !== null && _a !== void 0 ? _a : false,
783
+ sampleRate: sessionRecording.sampleRate,
784
+ minimumDurationMs: sessionRecording.minimumDurationMs,
785
+ sessionIdleThresholdMs: sessionRecording.sessionIdleThresholdMs,
786
+ fullSnapshotIntervalMs: sessionRecording.fullSnapshotIntervalMs,
787
+ captureConsole: (_b = sessionRecording.captureConsole) !== null && _b !== void 0 ? _b : false,
788
+ captureNetwork: (_c = sessionRecording.captureNetwork) !== null && _c !== void 0 ? _c : false,
789
+ captureCanvas: sessionRecording.captureCanvas,
790
+ blockClass: sessionRecording.blockClass,
791
+ blockSelector: sessionRecording.blockSelector,
792
+ ignoreClass: sessionRecording.ignoreClass,
793
+ maskTextClass: sessionRecording.maskTextClass,
794
+ maskTextSelector: sessionRecording.maskTextSelector,
795
+ maskAllInputs: sessionRecording.maskAllInputs,
796
+ maskInputOptions: sessionRecording.maskInputOptions,
797
+ masking: sessionRecording.masking,
798
+ recordHeaders: sessionRecording.recordHeaders,
799
+ recordBody: sessionRecording.recordBody,
800
+ compressEvents: sessionRecording.compressEvents,
801
+ __mutationThrottlerRefillRate: sessionRecording.__mutationThrottlerRefillRate,
802
+ __mutationThrottlerBucketSize: sessionRecording.__mutationThrottlerBucketSize,
803
+ };
804
+ }
805
+ /**
806
+ * Start session recording
807
+ * Call this to manually start recording if it wasn't enabled initially
808
+ */
809
+ startSessionRecording() {
810
+ if (!this.sessionRecording) {
811
+ const config = this.configManager.getConfig();
812
+ const sessionRecordingConfig = this._buildSessionRecordingConfig(config);
813
+ sessionRecordingConfig.enabled = true;
814
+ this.sessionRecording = new replay_1.SessionRecordingWrapper(this, sessionRecordingConfig);
815
+ }
816
+ this.sessionRecording.startIfEnabledOrStop("recording_initialized");
817
+ }
818
+ /**
819
+ * Stop session recording
820
+ */
821
+ stopSessionRecording() {
822
+ var _a;
823
+ (_a = this.sessionRecording) === null || _a === void 0 ? void 0 : _a.stopRecording();
824
+ }
825
+ /**
826
+ * Check if session recording is active
827
+ */
828
+ isSessionRecordingActive() {
829
+ var _a;
830
+ return ((_a = this.sessionRecording) === null || _a === void 0 ? void 0 : _a.status) === "active";
831
+ }
832
+ /**
833
+ * Get session recording ID
834
+ */
835
+ getSessionRecordingId() {
836
+ var _a;
837
+ return ((_a = this.sessionRecording) === null || _a === void 0 ? void 0 : _a.sessionId) || null;
732
838
  }
733
839
  /**
734
840
  * _execute_array() deals with processing any vTilt function