/** * Log levels for SDK debugging output * - 'none': No logging (production default) * - 'error': Only critical errors * - 'warn': Errors and warnings * - 'info': General information (initialization, session events) * - 'verbose': Detailed debugging information */ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'verbose'; /** * Error information passed to onError callback */ export interface SDKError { /** Error code for programmatic handling */ code: SDKErrorCode; /** Human-readable error message */ message: string; /** Original error if available */ originalError?: Error; /** Additional context */ context?: Record; } /** * Error codes for SDK errors */ export type SDKErrorCode = 'ALREADY_INITIALIZED' | 'NOT_INITIALIZED' | 'MISSING_PROJECT_KEY' | 'INVALID_CONFIG' | 'NETWORK_ERROR' | 'STORAGE_ERROR' | 'SESSION_NOT_SAMPLED' | 'ALREADY_RECORDING'; /** * SDK status information returned by getStatus() */ export interface SDKStatus { /** Whether the SDK has been initialized */ initialized: boolean; /** Whether recording is currently active */ recording: boolean; /** Whether recording is paused */ paused: boolean; /** Current session ID (null if not initialized or not sampled) */ sessionId: string | null; /** Current visitor ID */ visitorId: string | null; /** Number of events in the buffer waiting to be sent */ queueSize: number; /** Total events captured in this session */ eventCount: number; /** Whether the session was sampled out */ sampledOut: boolean; } /** * Configuration options for the SupaStory SDK */ export interface SDKConfig { /** * Your project key from the SupaStory dashboard (required) * @example 'pk_live_abc123' */ projectKey: string; /** * API endpoint for sending captured events * @default 'https://api.supastory.com/ingest' */ endpoint?: string; /** * Mask password and sensitive input fields * @default true */ maskInputs?: boolean; /** * Mask all input fields regardless of type * @default false */ maskAllInputs?: boolean; /** * CSS selectors for elements to exclude from capture * @example ['.sensitive-data', '[data-private]'] */ excludeSelectors?: string[]; /** * Session sampling rate (0.0 to 1.0) * - 1.0 = capture all sessions (100%) * - 0.5 = capture half of sessions (50%) * - 0.1 = capture 10% of sessions * @default 1.0 */ sampleRate?: number; /** * Maximum number of events to batch before sending * Higher values reduce network requests but increase memory usage * @default 50 */ batchSize?: number; /** * Interval in milliseconds between batch sends * @default 5000 */ batchIntervalMs?: number; /** * Maximum session duration in milliseconds * Recording automatically stops after this duration * @default 1800000 (30 minutes) */ maxSessionDurationMs?: number; /** * Capture network requests (XHR and Fetch) * @default true */ captureNetworkRequests?: boolean; /** * Include request/response payloads in network capture * Warning: May capture sensitive data * @default true */ captureNetworkPayloads?: boolean; /** * Delay in milliseconds before starting capture * Useful for SPAs that need time to render initial content * @default 5000 */ startDelayMs?: number; /** * Whether to automatically start recording after init() * Set to false to manually start with start() for consent flows * @default true */ autoStart?: boolean; /** * Enable debug logging (deprecated, use logLevel instead) * @default false * @deprecated Use logLevel instead */ debug?: boolean; /** * Log level for SDK output * @default 'none' */ logLevel?: LogLevel; /** * Callback fired when SDK encounters an error * Useful for monitoring and error tracking integration * @example * onError: (error) => { * myErrorTracker.capture(error); * } */ onError?: (error: SDKError) => void; /** * Callback fired when SDK is ready and recording has started * @example * onReady: (sessionId) => { * console.log('Recording session:', sessionId); * } */ onReady?: (sessionId: string) => void; /** * Callback fired when a session is sampled out * Useful for analytics on sample rate effectiveness */ onSampledOut?: () => void; } export interface SessionMeta { sessionId: string; visitorId: string; projectKey: string; userAgent: string; url: string; startedAt: number; } export interface SerializedNode { id: string; type: number; tagName?: string; attributes?: Record; textContent?: string; childNodes?: SerializedNode[]; } export interface Viewport { width: number; height: number; scrollX: number; scrollY: number; } export interface CapturedStyle { selector: string; css: string; } export interface SnapshotData { html: string; doctype?: string; baseUrl: string; viewport: Viewport; styles?: CapturedStyle[]; } export interface BaseEvent { t: number; id?: string; } export interface SnapshotEvent extends BaseEvent { type: 'snapshot'; data: SnapshotData; } export interface MutationData { adds?: Array<{ parentId: string; nextSiblingId: string | null; node: SerializedNode; }>; removes?: Array<{ parentId: string; nodeId: string; }>; attributes?: Array<{ targetId: string; name: string; value: string | null; }>; texts?: Array<{ targetId: string; value: string; }>; } export interface MutationEvent extends BaseEvent { type: 'mutation'; data: MutationData; } export interface ClickEventData { x: number; y: number; targetId: string; targetSelector?: string; targetTagName?: string; targetText?: string; button?: number; hasOnClick?: boolean; hasCursorPointer?: boolean; role?: string; classList?: string[]; tabIndex?: number; } export interface ClickEvent extends BaseEvent { type: 'click'; data: ClickEventData; } export interface ScrollEventData { x: number; y: number; targetId?: string; maxScrollY?: number; } export interface ScrollEvent extends BaseEvent { type: 'scroll'; data: ScrollEventData; } export interface InputEventData { targetId: string; targetSelector?: string; value: string; masked?: boolean; inputType?: string; } export interface InputEvent extends BaseEvent { type: 'input'; data: InputEventData; } export interface FocusEvent extends BaseEvent { type: 'focus'; data: { targetId: string; targetSelector?: string; }; } export interface BlurEvent extends BaseEvent { type: 'blur'; data: { targetId: string; targetSelector?: string; }; } export interface MouseMoveEvent extends BaseEvent { type: 'mousemove'; data: { x: number; y: number; targetId?: string; }; } export interface PageViewEvent extends BaseEvent { type: 'pageview'; data: { url: string; title?: string; referrer?: string; }; } export interface ErrorEventData { message: string; stack?: string; filename?: string; lineno?: number; colno?: number; type?: 'js' | 'network' | 'unhandledrejection'; } export interface ErrorEvent extends BaseEvent { type: 'error'; data: ErrorEventData; } export interface NetworkEventData { method: string; url: string; status?: number; duration?: number; requestHeaders?: Record; responseHeaders?: Record; requestBody?: string; responseBody?: string; connection?: { type?: string; downlink?: number; rtt?: number; saveData?: boolean; }; error?: string; } export interface NetworkEvent extends BaseEvent { type: 'network'; data: NetworkEventData; } export interface VisibilityEvent extends BaseEvent { type: 'visibility'; data: { hidden: boolean; }; } export interface ResizeEvent extends BaseEvent { type: 'resize'; data: Viewport; } export interface ConsoleEventData { level: 'log' | 'warn' | 'error' | 'info' | 'debug'; message: string; args?: string[]; } export interface ConsoleEvent extends BaseEvent { type: 'console'; data: ConsoleEventData; } export type CapturedEvent = SnapshotEvent | MutationEvent | ClickEvent | ScrollEvent | InputEvent | FocusEvent | BlurEvent | MouseMoveEvent | PageViewEvent | ErrorEvent | NetworkEvent | VisibilityEvent | ResizeEvent | ConsoleEvent; export interface IngestPayload { projectKey: string; sessionId: string; visitorId?: string; userId?: string; userTraits?: Record; chunk?: number; isFinal?: boolean; events: CapturedEvent[]; meta?: { userAgent?: string; url: string; timestamp: number; }; }