/** * First argument to `vt.sendChatMessage()` / {@link LazyLoadedChatInterface.sendMessage}. * * Plain strings stay plain text for backward compatibility. Use `{ markdown: '...' }`, * `{ html: '...' }`, or `options.format` for rich content. */ type SendChatMessageContent = string | { text: string; } | { markdown: string; } | { html: string; }; /** * Options for `vt.sendChatMessage()` / {@link LazyLoadedChatInterface.sendMessage}. */ interface SendChatMessageOptions { /** * Target conversation: `'new'` creates a channel, a UUID selects an existing one, * omit to use the active conversation (must already be in conversation view). */ channel?: "new" | string; /** * Open the widget panel before sending. Default: true. Pass `open: false` to send without opening. */ open?: boolean; /** * Applies when the first argument is a plain string. Default: `text`. * `markdown` and `html` use the same sanitized rendering as AI/agent messages. */ format?: "text" | "markdown" | "html"; /** * Marks the message as automatically injected by your site (form submit, CTA, * etc.) rather than typed by the visitor in the widget. Stored on the message * as `message_source` metadata so the AI can respond appropriately — e.g. * `source: "landing_form"`. Slug-like: letters, numbers, `_`, `-`, max 64 chars. */ source?: string; } /** * Pixel inset of the chat bubble from the viewport edges. * Use to clear host floating buttons, cookie banners, or mobile tab bars. */ interface BubbleOffset { /** Distance from the bottom edge (default: 20) */ bottom?: number; /** Distance from the right edge when `position` is `bottom-right` (default: 20) */ right?: number; /** Distance from the left edge when `position` is `bottom-left` (default: 20) */ left?: number; } /** * Chat bubble appearance and behavior configuration */ interface BubbleConfig { /** Allow user to drag the bubble to reposition (default: false) */ draggable?: boolean; /** Show the bubble on load (default: true). Set to false to control via vt.chat.show() */ visible?: boolean; /** * Viewport inset in pixels. Prefer this over custom CSS so positioning * survives widget UI updates. Pass `null` in `vt.updateConfig({ chat: { bubble: { offset: null } } })` to reset * to SDK defaults (20px). */ offset?: BubbleOffset | null; } /** * Person profiles configuration mode * - 'always': Always create person profiles (default) * - 'identified_only': Only create profiles when user is identified * - 'never': Never create person profiles (events only) */ type PersonProfilesMode = "always" | "identified_only" | "never"; /** * VTilt Types * * Type definitions for the VTilt tracking SDK. * Following PostHog's patterns where applicable. */ interface VTiltConfig { /** Project identifier (required) */ token: string; /** * API host for all SDK requests (events, config, recordings, chat). * Set this to your own domain when using a reverse proxy. * If not set, SDK uses relative URLs (same-origin). */ api_host?: string; /** UI host for dashboard links */ ui_host?: string | null; /** * CDN host for loading extension scripts (recorder.js, etc.) * If set, scripts load from {script_host}/{script}.js (no /dist/) * If not set, falls back to {api_host}/dist/{script}.js */ script_host?: string; /** Instance name (for multiple instances) */ name?: string; /** Domain to track (auto-detected if not provided) - used in event properties */ domain?: string; /** Storage method for session data */ storage?: PersistenceMethod; /** Persistence method for user data */ persistence?: PersistenceMethod; /** Persistence name prefix */ persistence_name?: string; /** * Enable cross-subdomain cookies. * When true, cookies are shared across subdomains (e.g., app.example.com and www.example.com). * Auto-detects false for platforms like herokuapp.com, vercel.app, netlify.app. * @default true (except for excluded platforms) */ cross_subdomain_cookie?: boolean; /** * Person profiles mode: * - 'always': Always create person profiles (default) * - 'identified_only': Only create when user is identified * - 'never': Never create person profiles */ person_profiles?: PersonProfilesMode; /** * Enable autocapture for automatic DOM event tracking. * Can be a boolean or an object with detailed configuration. * When true, captures clicks, form submissions, and input changes. */ autocapture?: boolean | AutocaptureOptions; /** * Enable web vitals tracking. * Can be a boolean or an object with detailed configuration. */ capture_performance?: boolean | CapturePerformanceConfig; /** Enable page view tracking */ capture_pageview?: boolean | "auto"; /** Enable page leave tracking */ capture_pageleave?: boolean | "if_capture_pageview"; /** Enable rage click detection (rapid clicks in same area) */ rageclick?: boolean; /** Disable compression */ disable_compression?: boolean; /** Whether to stringify payload before sending */ stringifyPayload?: boolean; /** Properties to exclude from events */ property_denylist?: string[]; /** Mask text in autocapture */ mask_all_text?: boolean; /** Mask all element attributes */ mask_all_element_attributes?: boolean; /** Respect Do Not Track browser setting */ respect_dnt?: boolean; /** * When false (default), events are not sent when the browser looks like a bot * (known crawler user agents, `navigator.webdriver`, or blocked CH brands). * Set to true to disable bot filtering (PostHog: `opt_out_useragent_filter`). */ opt_out_useragent_filter?: boolean; /** * Extra user-agent substrings to treat as bots (case-insensitive), merged with * the built-in list aligned with PostHog (PostHog: `custom_blocked_useragents`). */ custom_blocked_useragents?: string[]; /** Opt users out by default */ opt_out_capturing_by_default?: boolean; /** * When true, non-essential events are blocked until setConsent() is called. * Essential events ($identify, $alias, $set) are always allowed. * Use with vt.setConsent({ analytics: true, marketing: true, advertising: true }). */ require_consent?: boolean; /** Session recording configuration. Set `{ enabled: false }` to disable. */ session_recording?: SessionRecordingOptions; /** Chat widget configuration. Set `{ enabled: false }` to disable. */ chat?: ChatWidgetConfig; /** Outbound messaging (banners/posts). Set `{ enabled: true }` to opt in. */ outbound?: OutboundWidgetConfig; /** * Google Tag Gateway feature config (merged `ga4_gtag` + `google_ads_gtag` * rows). Property name is the stable SDK config key; populated from `/decide` * or set for SSR/tests. Enablement is driven by the presence of tag IDs from * the dashboard — disable it by turning the gtag destination off there. */ google_tag?: GoogleTagClientConfig; /** * Console log level for SDK output prefixed with `[vTilt]`. * * Levels (each includes the levels above it): * - 'none' — silence everything (escape hatch for shared kiosks etc.) * - 'error' — SDK errors only * - 'warn' — errors + warnings (default) * - 'info' — + lifecycle events (init, autocapture started, replay started) * - 'debug' — + per-event trace (every captured event, autocapture skips, requests) * * Default is 'warn' so SDK errors and warnings always surface without opt-in * (matches Amplitude/PostHog/Sentry). * * NOT to be confused with the per-event `$debug` flag — that is a separate * marker on the event payload itself, controlled by `debug: true` (below) * or the `?vtilt_debug=1` URL parameter, and consumed by the Debug View in * the dashboard. See the public docs for details. * * Setting `log_level` (or `debug: true`) at init time pins the level — it * cannot be overridden by remote config / Default Configuration. Leave it * unset to let project admins control verbosity from the dashboard. */ log_level?: "none" | "error" | "warn" | "info" | "debug"; /** * PostHog-style shorthand: when true, equivalent to `log_level: 'debug'`. * Ignored if `log_level` is also set. * * Setting this to true ALSO marks every captured event with `$debug: true` * so the dashboard's Debug View highlights it. The `?vtilt_debug=1` URL * parameter sets the event flag without raising the console log level. */ debug?: boolean; /** Global attributes added to all events */ globalAttributes?: Record; /** Bootstrap data for initialization (server-side rendering) */ bootstrap?: { distinctID?: string; isIdentifiedID?: boolean; featureFlags?: Record; /** Remote config from /decide endpoint - use this for SSR to avoid async fetch delay */ remoteConfig?: RemoteConfig; }; /** Before send hook for modifying events */ before_send?: (event: CaptureResult) => CaptureResult | null; /** Loaded callback */ loaded?: (vtilt: any) => void; /** @internal Set by RemoteConfigManager after a fresh /decide response (or fetch failure). */ __remote_config_loaded?: boolean; } interface EventPayload { [key: string]: any; } interface CaptureResult { uuid: string; event: string; properties: Properties; $set?: Properties; $set_once?: Properties; timestamp?: string; } interface CaptureOptions { /** Override timestamp */ timestamp?: Date; /** Properties to $set on person */ $set?: Properties; /** Properties to $set_once on person */ $set_once?: Properties; /** Send immediately (skip batching) */ send_instantly?: boolean; } interface TrackingEvent { timestamp: string; event: string; distinct_id: string; anonymous_id?: string; payload: EventPayload; } type Property = string | number | boolean | null | undefined | Date | any[] | Record; interface Properties { [key: string]: Property; } interface PropertyOperations { $set?: Properties; $set_once?: Properties; $unset?: string[]; } interface SessionData { value: string; expiry: number; } /** * Persistence method for user/session data * Following PostHog's approach: * - 'localStorage+cookie': Stores limited data in cookies, rest in localStorage (default) * - 'cookie': Stores all data in cookies * - 'localStorage': Stores all data in localStorage * - 'sessionStorage': Stores all data in sessionStorage * - 'memory': Stores all data in memory only (no persistence) */ type PersistenceMethod = "localStorage+cookie" | "cookie" | "localStorage" | "sessionStorage" | "memory"; /** User identity state */ interface UserIdentity { /** Current distinct ID (null if anonymous) */ distinct_id: string | null; /** Anonymous ID (always present) */ anonymous_id: string; /** Device ID (persists across sessions) */ device_id: string; /** User properties */ properties: Properties; /** Identity state */ user_state: "anonymous" | "identified"; } interface UserProperties { [key: string]: any; } /** Batched identity state change — applied atomically with a single save. */ interface IdentityUpdate { distinct_id?: string; user_state?: "anonymous" | "identified"; device_id?: string; properties_set?: Properties; properties_set_once?: Properties; } /** Supported Web Vitals metrics */ type SupportedWebVitalsMetric = "LCP" | "CLS" | "FCP" | "INP" | "TTFB"; /** All supported Web Vitals metrics */ declare const ALL_WEB_VITALS_METRICS: SupportedWebVitalsMetric[]; /** Default Web Vitals metrics (matches PostHog defaults) */ declare const DEFAULT_WEB_VITALS_METRICS: SupportedWebVitalsMetric[]; /** * Web Vitals capture configuration */ interface CapturePerformanceConfig { /** Enable or disable web vitals capture */ web_vitals?: boolean; /** Which metrics to capture (default: LCP, CLS, FCP, INP) */ web_vitals_allowed_metrics?: SupportedWebVitalsMetric[]; /** Delay before flushing metrics in ms (default: 5000) */ web_vitals_delayed_flush_ms?: number; /** * Maximum allowed metric value in ms (default: 900000 = 15 minutes) * Values above this are considered anomalies and ignored. * Set to 0 to disable this check. */ __web_vitals_max_value?: number; } interface WebVitalMetric { name: string; value: number; delta: number; rating: "good" | "needs-improvement" | "poor"; id: string; navigationType: string; /** Timestamp when the metric was captured (added internally) */ timestamp?: number; /** Attribution data from web-vitals library */ attribution?: Record; } interface GeolocationData { country?: string; locale?: string; } interface GroupsConfig { [groupType: string]: string; } interface FeatureFlagsConfig { [flagKey: string]: boolean | string; } type SessionIdChangedCallback = (newSessionId: string, previousSessionId: string | null, changeInfo: { reason: "timeout" | "new_session" | "reset"; }) => void; interface RequestOptions { method?: "POST" | "GET"; headers?: Record; timeout?: number; retry?: boolean; } /** * Autocapture configuration options * Controls automatic DOM event tracking (clicks, form submissions, input changes) */ interface AutocaptureOptions { /** * Enable autocapture (default: true when autocapture config is present) */ enabled?: boolean; /** * URL patterns to allow autocapture on (default: all URLs) * Supports strings (exact match) and RegExp patterns */ url_allowlist?: (string | RegExp)[]; /** * URL patterns to exclude from autocapture * Supports strings (exact match) and RegExp patterns */ url_ignorelist?: (string | RegExp)[]; /** * DOM events to capture (default: ['click', 'change', 'submit']) */ dom_event_allowlist?: ("click" | "change" | "submit")[]; /** * Element tags to capture (default: ['a', 'button', 'form', 'input', 'select', 'textarea', 'label']) */ element_allowlist?: string[]; /** * CSS selectors to allow for capture (elements matching these are always captured) * Example: ['[data-track]', '.track-click'] */ css_selector_allowlist?: string[]; /** * CSS selectors to exclude from autocapture. * If any element in the tree matches, the event is NOT captured. * * By default, common cookie consent banner patterns are excluded: * - Elements with 'cookie', 'consent', 'gdpr', 'privacy' in id/class * - Known consent management platforms (CookieBot, OneTrust, Iubenda, etc.) * * Set to empty array [] to disable default filtering. * * Example: ['#my-custom-banner', '[data-no-track]'] */ css_selector_ignorelist?: string[]; /** * Element attributes to exclude from capture * Example: ['data-secret', 'aria-label'] */ element_attribute_ignorelist?: string[]; /** * Capture text content from copy/cut events (default: false) */ capture_copied_text?: boolean; /** * Mask all text content in captured elements (use placeholder). * Overrides VTiltConfig.mask_all_text for autocapture specifically. */ mask_all_text?: boolean; /** * Mask all element attributes (only safe attributes or none). * Overrides VTiltConfig.mask_all_element_attributes for autocapture specifically. */ mask_all_element_attributes?: boolean; /** * Capture form field values on change events (default: false) * * When enabled, captures $el_value and $selected_text properties: * - $el_value: The programmatic value (input.value, option.value, checkbox checked state) * - $selected_text: Human-readable selection (option text, checkbox label) * * Protected by multiple privacy layers: * - Password and hidden inputs are never captured * - Fields with sensitive names (cc, pass, ssn, etc.) are skipped * - Credit card and SSN patterns in values are filtered out * - Elements with vt-sensitive or vt-no-capture classes are excluded * * Use case: Tracking search keywords, filter selections, etc. * * @default false */ capture_element_values?: boolean; /** * Scroll depth autocapture — two independently opt-in modes (both default off). */ scroll_depth?: { milestones?: boolean | { thresholds?: number[]; }; pageleave?: boolean; scroll_root_selector?: string | string[]; }; } /** Mask options for input elements in session recording */ interface SessionRecordingMaskInputOptions { color?: boolean; date?: boolean; "datetime-local"?: boolean; email?: boolean; month?: boolean; number?: boolean; range?: boolean; search?: boolean; tel?: boolean; text?: boolean; time?: boolean; url?: boolean; week?: boolean; textarea?: boolean; select?: boolean; password?: boolean; } /** Session recording configuration */ interface SessionRecordingOptions { /** Enable session recording */ enabled?: boolean; /** Sample rate (0-1, where 1 = 100%) */ sampleRate?: number; /** Minimum session duration in ms before sending */ minimumDurationMs?: number; /** Session idle threshold in ms (default: 5 minutes) */ sessionIdleThresholdMs?: number; /** Full snapshot interval in ms (default: 5 minutes) */ fullSnapshotIntervalMs?: number; /** Enable console log capture */ captureConsole?: boolean; /** Enable network request capture */ captureNetwork?: boolean; /** Canvas recording settings */ captureCanvas?: { recordCanvas?: boolean; canvasFps?: number; canvasQuality?: number; }; /** Block class for elements to hide (default: 'vt-no-capture') */ blockClass?: string; /** Block selector for elements to hide */ blockSelector?: string; /** * When `true`, skip built-in `blockSelector` entries for common invisible * template nodes (screen-reader-only, Webflow CMS placeholders, etc.). */ skipDefaultInvisibleBlocking?: boolean; /** Ignore class for input masking (default: 'vt-ignore-input') */ ignoreClass?: string; /** Mask text class (default: 'vt-mask') */ maskTextClass?: string; /** Mask text selector */ maskTextSelector?: string; /** Mask all inputs (default: true) */ maskAllInputs?: boolean; /** Mask input options */ maskInputOptions?: SessionRecordingMaskInputOptions; /** Masking configuration */ masking?: { maskAllInputs?: boolean; maskTextSelector?: string; blockSelector?: string; }; /** Record headers in network requests */ recordHeaders?: boolean; /** Record body in network requests */ recordBody?: boolean; /** Gzip-compress the request body before sending (default: true) */ compressEvents?: boolean; /** Internal: Mutation throttler refill rate */ __mutationThrottlerRefillRate?: number; /** Internal: Mutation throttler bucket size */ __mutationThrottlerBucketSize?: number; } /** Outbound messaging (banners/posts/carousels) widget configuration. */ interface OutboundWidgetConfig { /** Opt-in for v1 — the banner only renders when explicitly enabled. */ enabled?: boolean; } /** Chat widget configuration */ interface ChatWidgetConfig { /** Enable/disable chat widget (default: auto from dashboard) */ enabled?: boolean; /** Auto-fetch settings from dashboard (default: true) */ autoConfig?: boolean; /** Widget position (default: 'bottom-right') */ position?: "bottom-right" | "bottom-left"; /** Widget header/greeting message */ greeting?: string; /** Widget primary color */ color?: string; /** Start in AI mode (default: true) */ aiMode?: boolean; /** AI greeting message (first message from AI) */ aiGreeting?: string; /** Preload widget script on idle vs on-demand */ preload?: boolean; /** Offline message shown when business is unavailable */ offlineMessage?: string; /** Collect email when offline */ collectEmailOffline?: boolean; /** Bubble appearance and behavior */ bubble?: BubbleConfig; } type GoogleConsentValue = "follow_advertising" | "follow_analytics" | "granted" | "denied"; interface GoogleConsentOverride { ad_storage?: GoogleConsentValue; ad_user_data?: GoogleConsentValue; ad_personalization?: GoogleConsentValue; analytics_storage?: GoogleConsentValue; } interface GoogleAdsConversionMapping { event_name: string; send_to: string; value_param_path?: string; currency_param_path?: string; default_currency?: string; send_transaction_id?: boolean; transaction_id_param_path?: string; } /** * How gtag.js is loaded — mirrors the server-side `proxy_mode` destination * setting: * * - `proxied` (default) — route through `api_host/gt/*`. `api_host` is the * SDK-level proxy setting; self-hosted customers point it at their own * domain (which must implement the `/gt/*` spec). * - `direct` — no proxy; gtag.js loads straight from * `https://www.googletagmanager.com/gtag/js`. */ type GoogleTagProxyMode = "proxied" | "direct"; /** * Configuration for the Google Tag Gateway SDK feature. Ships under the * `googleTag` key of /decide when at least one `ga4_gtag` or `google_ads_gtag` destination is * enabled for the project. */ interface GoogleTagClientConfig { destinationId: string; /** * Proxy mode. When absent the SDK falls back to `proxied` so older * `/decide` responses and the zero-config default remain compatible. */ proxyMode?: GoogleTagProxyMode; tagIds: string[]; conversions: GoogleAdsConversionMapping[]; enhancedConversions: boolean; conversionLinker: boolean; linkerDomains: string[]; capturePageview: boolean; debugMode: boolean; consentOverride?: GoogleConsentOverride; /** * Master switch for forwarding every `vt.capture()` event to GA4 via * `gtag('event', name, params)`. Defaults to `true` when absent. When * `false`, only events that match an explicit row in `eventMappings` — or * an Ads `conversions` mapping — fire; everything else is left to the * Google tag's own behavior (page_view, enhanced measurement, etc.). */ autoForward?: boolean; /** * Whether to fire the direct Ads conversion beacon * (`gtag('event', 'conversion', {send_to: 'AW-.../...'})`) for rows in * `conversions`. Defaults to `true` when absent. Turn off when GA4 is * linked to Google Ads in the Ads account — in that mode the GA4 event * (marked as a conversion in GA4) propagates to Ads automatically and * the direct beacon would duplicate it. Has no effect when `conversions` * is empty. */ sendAdsConversions?: boolean; /** * Admin-configured event renames and param remappings. Mirrors the * generic `event_mappings` configured on the destination. Applied after * the filter check and before firing `gtag('event', ...)`. */ eventMappings?: GoogleTagEventMapping[]; /** * Admin-configured include/exclude lists. Mirrors the generic * `event_filter` on the destination. Evaluated before any mapping. */ eventFilter?: { include?: string[]; exclude?: string[]; }; } /** * Rename an event and optionally remap payload paths to gtag param paths. * Structurally identical to the server-side `EventMapping` type but lives * in the browser package to avoid cross-package imports. Source paths use * dotted notation into the captured payload (e.g. `order.total`); dest * paths use dotted notation into the outgoing gtag `params` object. */ interface GoogleTagEventMapping { source: string; destination: string; param_mappings?: GoogleTagParamMapping[]; } interface GoogleTagParamMapping { source: string; destination: string; } interface RemoteConfig { sessionRecording?: { enabled?: boolean; sampleRate?: number; minimumDurationMs?: number; /** Full DOM snapshot interval in ms (default 300000 = 5 min). Higher = less data. */ fullSnapshotIntervalMs?: number; maskAllInputs?: boolean; maskAllText?: boolean; captureConsole?: boolean; captureCanvas?: { recordCanvas?: boolean; canvasFps?: number; canvasQuality?: number; }; }; chat?: { enabled?: boolean; widgetPosition?: "bottom-right" | "bottom-left"; widgetColor?: string; bubbleDraggable?: boolean; bubbleVisible?: boolean; }; chatTracking?: { trackUserMessages?: boolean; trackAgentMessages?: boolean; }; analytics?: { capturePageview?: boolean; capturePageleave?: boolean; capturePerformance?: boolean; autocapture?: boolean; scrollDepthMilestones?: boolean; scrollDepthPageleave?: boolean; }; privacy?: { respectDnt?: boolean; requireConsent?: boolean; ipAnonymization?: boolean; }; /** * Diagnostics — runtime-tunable console verbosity. Applied by the SDK only * when the integrator did NOT pass `log_level` / `debug` to `vt.init()`. * Set from the dashboard's Default Configuration → Diagnostics tab. */ diagnostics?: { defaultLogLevel?: "none" | "error" | "warn" | "info" | "debug"; }; featureFlags?: FeatureFlagsConfig; /** Whether to send elements as chain string (PostHog compatibility) */ elementsChainAsString?: boolean; /** Server-side autocapture opt-out */ autocapture_opt_out?: boolean; /** * Google Tag Gateway config (absent when no client Google gtag destination is * enabled for the project). Drives the gtag.js proxy loader, Consent Mode * v2 bridge, and event → conversion mappings. */ googleTag?: GoogleTagClientConfig; /** * Outbound surfaces (banner). `enabled` is derived server-side from whether * the project has an active message, so operators publish from the dashboard * instead of editing `vt.init`. Off means there is nothing to show, so the * lazy `outbound.js` bundle is never fetched. */ outbound?: { enabled?: boolean; }; } /** * Session Manager - Handles session_id and window_id * * Uses shared StorageManager for consistent storage operations. * * Session ID: Unique per user session, expires after 30 minutes of inactivity * Window ID: Unique per browser tab, persists across page reloads */ declare class SessionManager { private storage; private _windowId; private _isNewSession; constructor(storageMethod?: PersistenceMethod, cross_subdomain?: boolean); /** * Hydrate window ID from sessionStorage. * Called by VTilt._boot() once the browser environment is ready. */ hydrateFromStorage(): void; /** * Get session ID (always returns a value, generates if needed) */ getSessionId(): string; /** * Set session ID in storage * Extends TTL if session_id exists, generates new one if not */ setSessionId(): string; /** * Reset session ID (generates new session on reset) */ resetSessionId(): void; /** * Returns true if the current session was just started (new session ID generated * because the session cookie expired or didn't exist). Resets after first call. * Matches GA4's _ss=1 semantics. */ consumeSessionStart(): boolean; /** * Get session ID from storage (raw, can return null) * Cookie Max-Age handles expiration automatically */ private _getSessionIdRaw; /** * Store session ID * Uses plain string format - cookie Max-Age handles expiration */ private _storeSessionId; /** * Clear session ID from storage */ private _clearSessionId; /** * When a tab stays open for longer than the session timeout, rotate the * session even if a system event (e.g. $pageleave on background) recently * refreshed the cookie TTL. */ private _rotateSessionIfExpired; private _bootstrapSessionActivityFromStorage; private _readSessionActivityMs; private _touchSessionActivity; private _clearSessionActivity; /** * Get window ID * Window ID is unique per browser tab/window and persists across page reloads * Always returns a window_id (generates one if not set) */ getWindowId(): string; /** * Set window ID * Stores in sessionStorage which is unique per tab */ private _setWindowId; /** * Initialize window ID * Detects tab duplication and handles window_id persistence */ private _initializeWindowId; /** * Listen to window unload to clear primary window flag * This helps distinguish between page reloads and tab duplication */ private _listenToUnload; /** * Update storage method at runtime */ updateStorageMethod(method: PersistenceMethod, cross_subdomain?: boolean): void; } /** * User Manager - Handles user identity and properties * * Uses shared StorageManager for consistent storage operations. * * Manages: * - anonymous_id: Generated ID for anonymous users * - distinct_id: User-provided ID after identification * - device_id: Persistent device identifier * - user_properties: Custom properties set via identify/setUserProperties * - user_state: "anonymous" or "identified" */ declare class UserManager { private storage; private userIdentity; private _isFirstVisit; constructor(storageMethod?: PersistenceMethod, cross_subdomain?: boolean); /** * Hydrate identity from persistent storage. * * Called by VTilt._boot() once the browser environment is guaranteed ready * (DOMContentLoaded). This is the only code path that reads from * localStorage / cookies. If storage is empty (first visit), the generated * IDs are persisted so they survive the next page load. */ hydrateFromStorage(): void; /** * Generate an ephemeral in-memory identity with no storage access. * Used by the constructor so the SDK is safe to instantiate in SSR. */ private generateEphemeralIdentity; /** * Get current user identity */ getUserIdentity(): UserIdentity; /** * Get current distinct ID (identified user ID) */ getDistinctId(): string | null; /** * Get current anonymous ID */ getAnonymousId(): string; /** * Get current user properties */ getUserProperties(): Record; /** * Get the effective ID for event tracking */ getEffectiveId(): string; /** * Get current device ID */ getDeviceId(): string; /** * Get current user state */ getUserState(): "anonymous" | "identified"; /** * Returns true if this is the user's first visit (no prior anonymous_id * in storage when the SDK booted). Resets after first call. * Matches GA4's _fv=1 semantics. */ consumeFirstVisit(): boolean; /** * Apply batched identity changes with a single save. * Replaces individual setters to avoid multiple storage writes per operation. */ applyUpdate(update: IdentityUpdate): void; /** * Reset identity to anonymous state. Generates new IDs and clears user data. */ reset(resetDeviceId?: boolean): void; /** * Set initial person info */ set_initial_person_info(maskPersonalDataProperties?: boolean, customPersonalDataProperties?: string[]): void; /** * Load user identity from storage. * * For traditional SSR websites where each page navigation reloads JavaScript, * identity MUST be persisted immediately when generated to ensure the same * anonymous_id is used across all page loads. * * Flow: * 1. Load from storage (reads cookies first for critical properties in SSR mode) * 2. Generate new IDs if not found * 3. Immediately persist to storage (saved to both localStorage and cookies) * * With `localStorage+cookie` persistence (default): * - Critical properties are stored in cookies for SSR compatibility * - Full data is stored in localStorage for fast SPA-style access * - Cookies ensure identity persists across full page reloads */ private loadUserIdentity; /** * Save user identity to storage */ private saveUserIdentity; /** * Get user properties from storage */ private getStoredUserProperties; /** * Set user properties in storage */ private setStoredUserProperties; /** * Register super properties once — a key is written only if it is not * already present. Mirrors PostHog's `persistence.register_once`: the value * is written to the **in-memory** identity (so it ships on the very next * event in this same page load, not only after a reload re-hydrates from * storage) and then persisted. */ private register_once; private generateAnonymousId; private generateDeviceId; /** * Update storage method at runtime. */ updateStorageMethod(method: PersistenceMethod, cross_subdomain?: boolean): void; } /** * Feature Interface & Base Classes * * Standard interfaces and abstract base classes for vTilt SDK features. * * Hierarchy: * Feature (interface) — eager features (Autocapture, HistoryAutocapture) * ToggleableFeature (interface)— features that actively stop when disabled * LazyFeature (abstract) — lazy-loaded features (WebVitals, Chat) * ToggleableLazyFeature — lazy-loaded + toggle (SessionRecording) * * @see docs/patterns/tracker-feature-lifecycle.md */ /** * Feature interface that all vTilt features should implement. * Provides a consistent lifecycle for feature initialization and management. */ interface Feature { readonly name: string; readonly isEnabled: boolean; readonly isStarted: boolean; startIfEnabled(): void; stop(): void; /** * Handle VTilt configuration updates. * Called when the main VTilt config is updated via updateConfig(). * Features should re-evaluate their enabled state and start/stop accordingly. */ onConfigUpdate?(config: VTiltConfig): void; } interface FeatureConfig { enabled?: boolean; } /** * Autocapture * * Automatic DOM event capture for clicks, form submissions, and input changes. * Privacy-first approach with element chain tracking and sensitive data filtering. * * Lifecycle (see docs/patterns/tracker-feature-lifecycle.md): * - Construction: created by FeatureManager when not hard-disabled. * - startIfEnabled: attaches DOM listeners when isEnabled becomes true. * - stop: detaches DOM listeners. Subsequent startIfEnabled re-attaches. * - onConfigUpdate: re-evaluates isEnabled on every config change and starts or * stops accordingly. This is what flips autocapture on when the * /decide endpoint returns `analytics.autocapture: true`. * * Enabled state precedence (highest first): * 1. _userOverride === false → off (set by vt.stopAutocapture()) * 2. _isDisabledServerSide === true → off (set by remote autocapture_opt_out) * 3. _userOverride === true → on (set by vt.startAutocapture()) * 4. config.autocapture truthy → on * 5. otherwise → off */ /** * Reasons isEnabled may evaluate to false. Surfaced by getDiagnostics() so that * integrators can pinpoint why no `$autocapture` events are flowing. */ type AutocaptureDisabledReason = "user_stop_called" | "server_opt_out" | "config_autocapture_false" | "config_autocapture_undefined"; interface AutocaptureDiagnostics { /** Whether the feature is enabled given current config and overrides. */ isEnabled: boolean; /** Whether DOM listeners are currently attached. */ isStarted: boolean; /** Why the feature is disabled (if any). */ disabledReason: AutocaptureDisabledReason | null; /** Snapshot of inputs that drove the decision. */ inputs: { configAutocapture: unknown; isDisabledServerSide: boolean; userOverride: boolean | null; elementsChainAsString: boolean; captureCopiedText: boolean; scrollDepthMilestones: boolean; scrollDepthPageleave: boolean; scrollDepthListenerAttached: boolean; }; } /** * Autocapture class for automatic DOM event tracking. * Implements the Feature interface for consistent lifecycle management. */ declare class Autocapture implements Feature { readonly name = "Autocapture"; private _instance; private _initialized; private _isDisabledServerSide; /** Explicit user override via vt.startAutocapture() / vt.stopAutocapture(). */ private _userOverride; private _elementSelectors; private _rageclicks; /** * Compact-payload mode for `$autocapture`. When true (the default), the SDK * only sends the `$elements_chain` string and omits the verbose `$elements` * array — the array is duplicate information for the ingestion side and is * what pushes payloads past the client size cap on apps with deep DOM * trees / Tailwind / Material class soup. Integrators that still need * `$elements` (legacy filters / external pipelines) can opt out with * `vt.init({ elementsChainAsString: false })` or via `/decide`. */ private _elementsChainAsString; private _cachedConfig; private _cachedConfigSource; /** * Bound DOM handlers. Stored so stop() can detach them — passing the same * function reference is required by removeEventListener(). */ private _domHandler; private _copyHandler; private _copyHandlerAttached; private _scrollDepthTracker; private _pageviewUnsubscribe; static extractConfig(config: VTiltConfig): { enabled: boolean; }; constructor(instance: VTilt, _config?: { enabled: boolean; }); private get _config(); get isEnabled(): boolean; get isStarted(): boolean; startIfEnabled(): void; stop(): void; /** * Max scroll depth % for the current page when pageleave mode is enabled. * Used to enrich `$pageleave` payloads. */ getMaxScrollDepthPctForPageleave(): number | null; onConfigUpdate(config: VTiltConfig): void; /** * Update autocapture configuration (for programmatic control). * Called from vt.startAutocapture() / vt.stopAutocapture(). */ updateConfig(config: Partial<{ enabled: boolean; }>): void; getDiagnostics(): AutocaptureDiagnostics; setElementSelectors(selectors: Set): void; getElementSelectors(element: Element | null): string[] | null; /** * Single-source-of-truth for whether autocapture should be active. * Returns the disabled reason so diagnostics and logging can be precise. */ private _evaluateEnabled; private _addDomEventHandlers; private _removeDomEventHandlers; /** * Scroll depth listener — opt-in via `autocapture.scroll_depth` milestones and/or pageleave. */ private _syncScrollDepthHandler; private _teardownScrollDepth; /** * The copy/cut listener is opt-in via `autocapture.capture_copied_text`. * Callable from both startIfEnabled and onConfigUpdate so toggling the flag * mid-session correctly attaches or detaches the listener. */ private _syncCopyHandler; private _captureEvent; private _isBrowserSupported; } /** * Consent Manager * * Manages user consent state for analytics, marketing, and advertising. * Stores consent in a first-party cookie (_vtilt_consent) and provides * consent properties to attach to every event. * * When `requireConsent` is true in config, the CaptureManager gates * non-essential events until setConsent() is called. */ interface ConsentState { analytics?: boolean; marketing?: boolean; advertising?: boolean; } interface ConsentHost { _emitter?: { emit(event: string, payload?: unknown): void; }; getConfig(): { require_consent?: boolean; }; } declare class ConsentManager { private _host; private _state; private _hasBeenSet; constructor(host: ConsentHost); /** * Set consent state. Merges with existing state. * Persists to cookie and emits consent:updated. */ setConsent(consent: ConsentState): void; /** * Get current consent state. * Returns undefined fields for categories not yet set. */ getConsent(): ConsentState; /** * Whether setConsent() has been called (or consent was loaded from cookie). * Used by CaptureManager to gate events when requireConsent is true. */ hasConsent(): boolean; /** * Apply default-all-granted state when no explicit consent exists. * Sets all categories to true in memory but does NOT persist to cookie * (implicit defaults don't need storage — only explicit user choices do). */ setDefaultGranted(): void; /** * Returns event properties to attach to every captured event. * Only includes fields that have been explicitly set. */ getConsentProperties(): Record; /** * Reset consent state (e.g. on user logout). */ reset(): void; private _readFromCookie; private _writeToCookie; private _removeCookie; } /** * Consent Mode v2 bridge. * * Maps vTilt's 3-boolean consent (analytics / marketing / advertising) to * Google's 4-key Consent Mode v2 payload (ad_storage, ad_user_data, * ad_personalization, analytics_storage) and pushes it through gtag. * * Mapping (default): * advertising -> ad_storage + ad_user_data + ad_personalization * analytics -> analytics_storage * marketing is not sent to Google by default (it covers email/SMS, not ads). * * Admin UI may override individual keys (`consentOverride`) to hard-wire a * specific value regardless of user consent — useful for jurisdictions or * projects with fixed policies. */ type GtagFn = (...args: unknown[]) => void; /** * `vt.gtag(...)` escape hatch. * * Power users may need to call `gtag` directly (e.g. to fire a custom * conversion from a third-party widget or to set a debug param). We expose * a flat passthrough that: * * - queues calls made before the SDK has booted (keeps them in FIFO order), * - queues calls made before `gtag.js` has loaded (forwarded via the * `dataLayer` shim, which naturally buffers until the real script runs), * - records every call so tests + the delivery log can see them. */ type GtagCall = unknown[]; /** * Enhanced Conversions helper. * * Hashes user-provided PII (email/phone/name/address) with SHA-256 before * it leaves the browser, matching Google Ads' Enhanced Conversions format: * https://support.google.com/google-ads/answer/13258081 * * Returns an object suitable for `gtag('set', 'user_data', { ... })`. */ interface RawUserData { email?: string | null; phone?: string | null; first_name?: string | null; last_name?: string | null; street?: string | null; city?: string | null; region?: string | null; postal_code?: string | null; country?: string | null; } /** * GoogleTagGateway feature. * * Orchestrates the client-side half of the Google Tag Gateway destination: * * 1. Maintains `window.dataLayer` / `window.gtag`. * 2. Pushes Consent Mode v2 defaults BEFORE gtag.js loads, and re-pushes * on `consent:updated` events. * 3. Injects `gtag/js?id=` from the vTilt gateway (`/gt`) so * every measurement request flows through the first-party origin. * 4. Subscribes to EVENT_CAPTURED and forwards mapped events to Ads * (`gtag('event', 'conversion', ...)`). * 5. Exposes a flat `vt.gtag(...)` passthrough for power users. * * Forwards only after `__remote_config_loaded` is true (same signal as * EventBuffer: fresh `/decide` applied, bootstrap, or fetch failure). * Captures before that are queued so we never drop events just because * `/decide` has not committed yet. After remote is ready, we install the * official `dataLayer` + `gtag` shim (`ensureDataLayer`) and forward * immediately — the shim queues until `gtag.js` loads; we do not maintain a * second SDK queue for that window. * * Feature lifecycle matches the rest of the SDK — registered with * FeatureManager via a descriptor that pulls the admin-configured shape * out of `/decide` under the `googleTag` key. */ interface GoogleTagGatewayFeatureConfig extends FeatureConfig { remote?: GoogleTagClientConfig; } interface DeliveryLogEntry { ts: number; tag_ids: string[]; event_name: string; send_to: string; status: "fired" | "dropped"; reason?: string; } declare class GoogleTagGateway implements Feature { readonly name = "GoogleTagGateway"; private _instance; private _config; private _isStarted; private _scriptInjected; private _scriptLoaded; private _consentDefaultsPushed; private _gtag; private _publicApi; private _loaderOptions; private _unsubscribeCaptured; private _unsubscribeConsent; private _deliveryLog; private readonly _maxDeliveryLog; /** * Captures observed before `__remote_config_loaded` only. Once remote * commits, `ensureDataLayer` + gtag forward — gtag's own queue handles * ordering until `gtag.js` is on the wire. */ private readonly _pendingCaptures; constructor(instance: VTilt, config?: GoogleTagGatewayFeatureConfig); static extractConfig(config: VTiltConfig): GoogleTagGatewayFeatureConfig; get isEnabled(): boolean; get isStarted(): boolean; /** Exposed for tests and the public `vt.gtag` binding. */ get gtag(): GtagFn; get deliveryLog(): DeliveryLogEntry[]; startIfEnabled(): void; stop(): void; onConfigUpdate(config: VTiltConfig): void; /** * Set the user identifiers that power Enhanced Conversions. Values are * normalized + SHA-256 hashed client-side before being pushed to gtag, * so raw PII never leaves the browser. */ setUserData(raw: RawUserData): Promise; getRecentPublicCalls(): GtagCall[]; private _start; private _drainPending; private _pushConsentDefaultsOnce; private _subscribeCaptured; /** * Handle one captured event. Only `__remote_config_loaded === false` uses * `_pendingCaptures`. After remote commits, we boot the gtag pipeline on * demand (`startIfEnabled`) and forward — the dataLayer shim buffers until * `gtag.js` loads. */ private _handleCaptured; private _subscribeConsent; private _record; } /** * VTD Overlay Feature * * Displays destination content in a fullscreen iframe overlay when vtd= URL parameter is present. * Used for tracking links that redirect to specific content (video, page, etc.). * * Example: https://example.com/?vt=person_id&vtd=https://youtube.com/watch?v=abc123 * * @see docs/architecture/proposals/vt-vtd-tracking-links.md */ interface VtdOverlayConfig { /** Whether the overlay feature is enabled */ enabled?: boolean; } declare class VtdOverlay implements Feature { readonly name = "VtdOverlay"; private _instance; private _config; private _isStarted; private _destinationUrl; private _originalUrl; private _isLoading; private _container; private _iframe; private _loadingEl; constructor(instance: VTilt, config?: VtdOverlayConfig); static extractConfig(config: VTiltConfig): VtdOverlayConfig; get isEnabled(): boolean; get isStarted(): boolean; startIfEnabled(): void; stop(): void; onConfigUpdate(config: VTiltConfig): void; /** * Set the destination URL and show the overlay. * Called by vtilt.ts when vtd= parameter is detected. * * @param url - The destination URL (can be URL-encoded) */ setDestinationUrl(url: string): void; /** * Get the current destination URL. */ getDestinationUrl(): string | null; /** * Close the overlay with animation. */ close(): void; private _isValidUrl; /** * Transform URLs to embeddable format for known platforms. * Many sites block iframe embedding but provide dedicated embed URLs. */ private _toEmbedUrl; private _getHostname; private _getFaviconUrl; private _showOverlay; private _destroyOverlay; private _attachEventListeners; private _getBackdropStyles; private _getModalStyles; private _getHeaderStyles; private _getHeaderHTML; private _getIframeWrapperStyles; private _getLoadingStyles; private _getLoadingHTML; private _getIframeStyles; private _getGlobalStyles; private _truncateUrl; private _escapeHtml; } /** * Request Queue - Event Batching (PostHog-style) * * Batches multiple events together and sends them at configurable intervals. * This reduces the number of HTTP requests significantly for active users. * * Features: * - Configurable flush interval (default 3 seconds) * - Batches events by URL/batchKey * - Uses sendBeacon on page unload for reliable delivery * - Converts absolute timestamps to relative offsets before sending */ interface QueuedRequest { url: string; event: TrackingEvent; batchKey?: string; transport?: "xhr" | "sendBeacon"; } /** * Rate Limiter - Token Bucket Algorithm (PostHog-style) * * Prevents runaway loops from flooding the server with events. * Uses a token bucket algorithm with configurable rate and burst limits. * * Features: * - Configurable events per second (default: 10) * - Configurable burst limit (default: 100) * - Token replenishment over time * - Warning event when rate limited */ interface RateLimitBucket { tokens: number; last: number; } interface RateLimiterConfig { eventsPerSecond?: number; eventsBurstLimit?: number; persistence?: { get: (key: string) => RateLimitBucket | null; set: (key: string, value: RateLimitBucket) => void; }; captureWarning?: (message: string) => void; } declare class RateLimiter { private eventsPerSecond; private eventsBurstLimit; private lastEventRateLimited; private persistence?; private captureWarning?; constructor(config?: RateLimiterConfig); /** * Check if the client should be rate limited * * @param checkOnly - If true, don't consume a token (just check) * @returns Object with isRateLimited flag and remaining tokens */ checkRateLimit(checkOnly?: boolean): { isRateLimited: boolean; remainingTokens: number; }; /** * Check if an event should be allowed (consumes a token if allowed) */ shouldAllowEvent(): boolean; /** * Get remaining tokens without consuming */ getRemainingTokens(): number; } /** * Simple Event Emitter * * Lightweight pub/sub system for internal feature communication. * Following PostHog's SimpleEventEmitter pattern. */ /** * Event listener function type */ type EventListener = (payload: T) => void; /** * Unsubscribe function returned by on() */ type Unsubscribe = () => void; /** * Simple event emitter for internal SDK communication. * Features can emit and listen to events without direct coupling. * * @example * ```typescript * const emitter = new SimpleEventEmitter(); * * // Subscribe to events * const unsubscribe = emitter.on('user:identified', (data) => { * console.log('User identified:', data); * }); * * // Emit events * emitter.emit('user:identified', { userId: '123' }); * * // Unsubscribe when done * unsubscribe(); * ``` */ declare class SimpleEventEmitter { private _events; private _onceEvents; /** * Subscribe to an event. * * @param event - Event name to subscribe to * @param listener - Callback function * @returns Unsubscribe function */ on(event: string, listener: EventListener): Unsubscribe; /** * Subscribe to an event once (auto-unsubscribes after first call). * * @param event - Event name to subscribe to * @param listener - Callback function * @returns Unsubscribe function */ once(event: string, listener: EventListener): Unsubscribe; /** * Emit an event to all listeners. * * @param event - Event name to emit * @param payload - Data to pass to listeners */ emit(event: string, payload?: T): void; /** * Remove all listeners for an event. * * @param event - Event name (or undefined to remove all) */ off(event?: string): void; /** * Get the number of listeners for an event. * * @param event - Event name * @returns Number of listeners */ listenerCount(event: string): number; /** * Check if there are any listeners for an event. * * @param event - Event name * @returns True if there are listeners */ hasListeners(event: string): boolean; } /** * Feature Manager * * Central registry for SDK features. Features self-describe via descriptors * that declare their config keys, remote config mappings, and class references. * * Responsibilities: * - Registration: features register descriptors before init * - Instance creation: createInstances() constructs instances during init() (pre-boot) * - Starting: initAll() starts created instances at boot via startIfEnabled * - Config propagation: notifyAll() calls onConfigUpdate on each instance * - Descriptor exposure: getDescriptors() lets RemoteConfigManager._apply iterate * * @see docs/patterns/tracker-feature-lifecycle.md */ /** * Self-describing metadata for a feature. * Declared once at registration time; used by FeatureManager and RemoteConfigManager. */ interface FeatureDescriptor { /** Unique name (used as map key and for get()) */ name: string; /** VTiltConfig key that holds this feature's config (e.g. "session_recording") */ configKey?: keyof VTiltConfig; /** Remote config mapping — tells _apply how to extract config from RemoteConfig */ remoteConfig?: { /** Key on the RemoteConfig object (e.g. "sessionRecording") */ key: string; /** Transform remote section into the shape written to VTiltConfig[configKey] */ map: (remote: Record) => Record; }; /** Feature class — must have a static extractConfig and a constructor */ FeatureClass: { new (instance: any, config?: any): Feature; extractConfig(config: VTiltConfig): any; }; } /** * Minimal host interface to avoid circular dependency with VTilt. */ interface FeatureHost { getConfig(): VTiltConfig; } declare class FeatureManager { private _host; private _descriptors; private _instances; constructor(host: FeatureHost); /** Register a feature descriptor. Call before initAll(). */ register(desc: FeatureDescriptor): void; /** * Create instances for all registered features without starting them. * Safe to call before DOM boot — constructors are lightweight. * Idempotent: skips features that already have an instance. * * Hard-disabled features are skipped (instance not created) when their * `configKey` section has `enabled: false` (e.g. `chat: { enabled: false }`). */ createInstances(): void; /** * Create and start all registered features. * Calls `createInstances()` first (idempotent), then `startIfEnabled()` on each. */ initAll(): void; /** Notify all initialized features of a config change. */ notifyAll(config: VTiltConfig): void; /** Get a feature instance by name. */ get(name: string): T | undefined; /** Register a late-created feature instance (e.g. from a public start*() call). */ set(name: string, instance: Feature): void; /** Expose descriptors so RemoteConfigManager._apply can iterate for remote config mapping. */ getDescriptors(): Map; /** * True when the integrator explicitly hard-disabled this feature in code * config — its `configKey` section has `enabled: false` * (e.g. `chat: { enabled: false }`). * * When `enabled` is omitted (undefined), the feature may still auto-configure * from dashboard settings, so we must construct the instance. */ private _isHardDisabled; } /** * VTilt SDK - Main Entry Point * * Privacy-first analytics SDK with modular architecture. * This file is a thin orchestrator that delegates to specialized managers: * * - ConfigManager: Configuration management * - SessionManager: Session and window ID management * - UserManager: User identity and properties * - CaptureManager: Event capture and payload enrichment * - IdentityManager: High-level identity operations * - RemoteConfigManager: Remote configuration from /decide * - FeatureManager: Feature lifecycle management (descriptors, initAll, notifyAll) * * @see docs/patterns/tracker-feature-lifecycle.md */ declare class VTilt { readonly version: string; __loaded: boolean; private configManager; sessionManager: SessionManager; userManager: UserManager; private _captureManager; private _identityManager; private _remoteConfigManager; consentManager: ConsentManager; _featureManager: FeatureManager; vtdOverlay?: VtdOverlay; private _eventBuffer; private requestQueue; private retryQueue; rateLimiter: RateLimiter; _emitter: SimpleEventEmitter; private _has_warned_about_config; private static readonly BOOT_GATED_METHODS; private _booted; private _pendingCalls; private _postBootInitDone; constructor(config?: Partial); /** * Register all features with their descriptors. * Descriptors tell FeatureManager and RemoteConfigManager how each feature works. */ private _registerFeatures; private _installBootGate; _boot(): void; init(token: string, config?: Partial, name?: string): VTilt; private _init; /** * Substantive init work that requires both init() config AND a booted * browser environment. Runs exactly once. * * Init order: hydrate storage -> initAll features -> load remote config * Features must exist before remote config loads so cached/fresh config * can trigger onConfigUpdate on already-initialized features. */ private _runPostBootInit; /** * Grant all consent categories by default when `require_consent` is not * enabled and the visitor has no explicit consent cookie. Deferred until * privacy is resolved so remote `require_consent: true` can still gate. */ private _applyDefaultConsentIfAllowed; startAutocapture(): void; stopAutocapture(): void; isAutocaptureActive(): boolean; /** * Returns a structured snapshot of the autocapture state — useful for * debugging "no `$autocapture` events" complaints. Returns `null` if the * Autocapture feature has not been registered (e.g. before init). */ getAutocaptureDiagnostics(): ReturnType | null; startSessionRecording(): void; stopSessionRecording(): void; isRecordingActive(): boolean; getSessionRecordingId(): string | null; /** * Keep recording aligned with analytics `$pageview`. On pathname change the * recorder takes a FullSnapshot (Meta+FS page boundary) in addition to * updating flush-time `$current_url`. */ notifyRecordingPageUrl(href: string): void; openChat(): void; closeChat(): void; toggleChat(): void; showChat(): void; hideChat(): void; /** * Send a chat message. By default uses the active conversation; pass options to * start a new conversation, target a channel, or open the widget panel. */ sendChatMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): Promise; /** * Raw `gtag(...)` passthrough. Queues calls made before the feature is * enabled or before `gtag.js` has loaded, flushed FIFO once ready. Safe * to call at any time, including before `vt.init()`. * * @example * vt.gtag('event', 'sign_up', { method: 'email' }) */ gtag(...args: unknown[]): void; /** * Set Enhanced Conversions user identifiers. Values are normalized and * SHA-256 hashed in the browser before being forwarded to gtag. */ setGoogleUserData(data: Parameters[0]): Promise; get featureManager(): FeatureManager; on(event: string, listener: EventListener): Unsubscribe; once(event: string, listener: EventListener): Unsubscribe; /** Removes all listeners for `event` (same semantics as internal emitter). */ off(event: string): void; /** * Whether this browser session looks like a bot/crawler (PostHog parity). * When true and bot filtering is enabled, `capture()` is a no-op. */ _is_bot(): boolean; capture(name: string, payload: EventPayload, options?: { skip_client_rate_limiting?: boolean; skip_engagement?: boolean; }): void; /** Emit $pageleave for the current page when enabled (SPA transitions, lifecycle hooks). */ tryCapturePageleave(reason: string): void; identify(newDistinctId?: string, userPropertiesToSet?: Record, userPropertiesToSetOnce?: Record): void; setUserProperties(userPropertiesToSet?: Record, userPropertiesToSetOnce?: Record): void; resetUser(reset_device_id?: boolean): void; alias(alias: string, original?: string): void; setConsent(consent: ConsentState): void; getConsent(): ConsentState; getUserIdentity(): Record; getDeviceId(): string; getUserState(): "anonymous" | "identified"; getConfig(): VTiltConfig; getRemoteConfig(): RemoteConfig | null; getSessionId(): string | null; getDistinctId(): string; getAnonymousId(): string; toString(): string; /** * Merge a partial config patch into the live SDK config and notify all features. * Use for any post-`init()` change (autocapture, `chat`, persistence, etc.). * Top-level keys shallow-merge; nested `chat` deep-merges. */ updateConfig(config: Partial): void; buildUrl(): string; buildEndpointUrl(path: string): string; sendRequest(url: string, event: TrackingEvent): void; /** * Route a captured event through the EventBuffer. * $snapshot and $snapshot_items bypass the buffer (they have identity from * UserManager and don't need config filtering). */ bufferEvent(name: string, url: string, event: TrackingEvent): void; private _is_configured; private _send_batched_request; private _send_http_request; private _send_beacon_request; _send_retriable_request(item: QueuedRequest): void; private _setup_unload_handler; private _start_queue_if_opted_in; private _read_vt_param_from_url; private _initVtdOverlay; _execute_array(array: any[]): void; _dom_loaded(): void; } /** * Method names registered on the inline `window.vt` stub before `array.js` loads. * * npm / ESM imports (`import { vt } from '@v-tilt/browser'`) do **not** use this * list — `vt` is the real SDK instance. Only the HTML snippet's loader IIFE * needs these names so calls queue in `window.vt._i` until the bundle loads. * * When adding a new public `vt.*` method that integrators may call before * `init()` completes, append it here and update public install docs. */ declare const VTILT_SNIPPET_STUB_METHOD_NAMES: readonly ["init", "capture", "identify", "setUserProperties", "resetUser", "getUserIdentity", "getDeviceId", "getUserState", "alias", "getConfig", "getSessionId", "updateConfig", "setConsent", "on", "once", "off", "startAutocapture", "stopAutocapture", "startSessionRecording", "stopSessionRecording", "openChat", "closeChat", "toggleChat", "showChat", "hideChat", "sendChatMessage", "gtag", "setGoogleUserData"]; /** Space-separated stub method string for the minified install snippet. */ declare const VTILT_SNIPPET_STUB_METHODS: string; type VtiltSnippetStubMethodName = (typeof VTILT_SNIPPET_STUB_METHOD_NAMES)[number]; declare const vt: VTilt; /** * ESM core bundle with chat pre-registered — bundler equivalent of `array.chat.js`. */ export { ALL_WEB_VITALS_METRICS, DEFAULT_WEB_VITALS_METRICS, VTILT_SNIPPET_STUB_METHODS, VTILT_SNIPPET_STUB_METHOD_NAMES, VTilt, vt as default, vt }; export type { AutocaptureOptions, CaptureOptions, CapturePerformanceConfig, CaptureResult, ChatWidgetConfig, EventPayload, FeatureFlagsConfig, GeolocationData, GoogleAdsConversionMapping, GoogleConsentOverride, GoogleConsentValue, GoogleTagClientConfig, GoogleTagEventMapping, GoogleTagParamMapping, GoogleTagProxyMode, GroupsConfig, IdentityUpdate, OutboundWidgetConfig, PersistenceMethod, Properties, Property, PropertyOperations, RemoteConfig, RequestOptions, SessionData, SessionIdChangedCallback, SessionRecordingMaskInputOptions, SessionRecordingOptions, SupportedWebVitalsMetric, TrackingEvent, UserIdentity, UserProperties, VTiltConfig, VtiltSnippetStubMethodName, WebVitalMetric };