/** * 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 */ import type { VTiltConfig } from "./types"; /** * Feature interface that all vTilt features should implement. * Provides a consistent lifecycle for feature initialization and management. */ export 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; } /** * Feature with the startIfEnabledOrStop pattern. * Used for features that should actively stop when disabled mid-session * (e.g. session recording stops if server disables it). */ export interface ToggleableFeature extends Feature { startIfEnabledOrStop(trigger?: string): void; } export interface FeatureConfig { enabled?: boolean; } /** * Static interface for feature classes. * Every feature class must provide extractConfig for self-contained config extraction. */ export interface FeatureStatic { extractConfig(config: VTiltConfig): TConfig; } export interface VTiltInstance { getConfig(): VTiltConfig; capture(event: string, properties?: Record): void; } /** * Abstract base class for features that require lazy-loaded external scripts. * * Handles the common pattern of: * 1. Checking if extension is already on window.__VTiltExtensions__ * 2. Loading the script via loadExternalDependency if not * 3. Creating the heavy instance via _createLoaded() * 4. Calling _onLoaded() for feature-specific start logic * 5. Guarding against duplicate loads * * Subclasses must implement: name, scriptName, _createLoaded() * Subclasses may override: _onLoaded(), isEnabled, startIfEnabled(), stop() * * Deferral: Subclasses with public API methods (e.g. Chat) use _deferCall() * to queue calls until async initialization completes, then _markApiReady() * to flush the queue. This is the standard pattern for features whose * public API must wait for an async start step. */ export declare abstract class LazyFeature implements Feature { protected _instance: VTiltInstance; protected _config: TConfig; protected _loaded: TLoaded | undefined; protected _isStarted: boolean; protected _isLoading: boolean; private _loadCallbacks; private _apiReady; private _deferredCalls; abstract readonly name: string; /** Script name passed to loadExternalDependency (e.g. "recorder", "chat", "web-vitals") */ abstract readonly scriptName: string; constructor(instance: VTiltInstance, config: TConfig); get isEnabled(): boolean; get isStarted(): boolean; get isLoaded(): boolean; /** Create the heavy instance after the external script has loaded. */ protected abstract _createLoaded(): TLoaded; /** * Load external script (if needed) then invoke callback with the loaded instance. * Safe to call multiple times — guards against duplicate loads. */ protected _ensureLoaded(callback: (loaded: TLoaded) => void): void; startIfEnabled(): void; /** Called after the script loads and the loaded instance is created. Override for custom start logic. */ protected _onLoaded(_loaded: TLoaded): void; stop(): void; onConfigUpdate?(config: VTiltConfig): void; updateConfig(config: Partial): void; /** Queue fn until _markApiReady(). If already ready, execute immediately. */ protected _deferCall(fn: () => void): void; /** Flush all deferred calls. Idempotent — subsequent calls are no-ops. */ protected _markApiReady(): void; /** Reset deferral state (for destroy/cleanup). */ protected _resetDeferral(): void; } /** * Lazy-loaded feature that can be toggled on/off at runtime. * Adds startIfEnabledOrStop() which actively stops the feature when disabled. */ export declare abstract class ToggleableLazyFeature extends LazyFeature implements ToggleableFeature { startIfEnabledOrStop(trigger?: string): void; }