@tracelog/lib 3.0.0 → 3.1.0-rc.116.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/tracelog.esm.js +560 -484
- package/dist/browser/tracelog.esm.js.map +1 -1
- package/dist/browser/tracelog.js +2 -2
- package/dist/browser/tracelog.js.map +1 -1
- package/dist/public-api.cjs +2 -2
- package/dist/public-api.cjs.map +1 -1
- package/dist/public-api.d.mts +21 -1
- package/dist/public-api.d.ts +21 -1
- package/dist/public-api.js +2 -2
- package/dist/public-api.js.map +1 -1
- package/package.json +1 -1
package/dist/public-api.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants/config.constants.ts","../src/constants/storage.constants.ts","../src/types/config.types.ts","../src/types/device.types.ts","../src/types/emitter.types.ts","../src/types/error.types.ts","../src/types/event.types.ts","../src/types/mode.types.ts","../src/types/validation-error.types.ts","../src/constants/app.constants.ts","../src/utils/logging.utils.ts","../src/utils/browser/device-detector.utils.ts","../src/constants/performance.constants.ts","../package.json","../src/constants/version.constants.ts","../src/utils/browser/mode.utils.ts","../src/utils/browser/referrer.utils.ts","../src/utils/browser/utm-params.utils.ts","../src/utils/data/uuid.utils.ts","../src/utils/network/url.utils.ts","../src/utils/security/sanitize.utils.ts","../src/utils/security/pii.utils.ts","../src/utils/validations/config-validations.utils.ts","../src/utils/validations/type-guards.utils.ts","../src/utils/validations/metadata-validations.utils.ts","../src/utils/validations/event-validations.utils.ts","../src/utils/emitter.utils.ts","../src/utils/error-signature.utils.ts","../src/managers/state.manager.ts","../src/managers/sender.manager.ts","../src/managers/time.manager.ts","../src/managers/event.manager.ts","../src/managers/user.manager.ts","../src/managers/session.manager.ts","../src/handlers/session.handler.ts","../src/handlers/page-view.handler.ts","../src/handlers/click.handler.ts","../src/handlers/scroll.handler.ts","../src/ecommerce/shopify-cart-linker.ts","../src/managers/storage.manager.ts","../src/handlers/performance.handler.ts","../src/handlers/error.handler.ts","../src/app.ts","../src/api.ts","../src/public-api.ts"],"names":["DEFAULT_SESSION_TIMEOUT","MAX_CUSTOM_EVENT_NAME_LENGTH","MAX_CUSTOM_EVENT_STRING_SIZE","MAX_CUSTOM_EVENT_KEYS","MAX_CUSTOM_EVENT_ARRAY_SIZE","MAX_NESTED_OBJECT_KEYS","MAX_STRING_LENGTH","MAX_STRING_LENGTH_IN_ARRAY","MAX_ARRAY_LENGTH","HTML_DATA_ATTR_PREFIX","INTERACTIVE_SELECTORS","UTM_PARAMS","DEFAULT_SENSITIVE_QUERY_PARAMS","VALIDATION_MESSAGES","XSS_PATTERNS","STORAGE_BASE_KEY","QA_MODE_KEY","USER_ID_KEY","QA_MODE_URL_PARAM","QA_MODE_ENABLE_VALUE","QA_MODE_DISABLE_VALUE","QUEUE_KEY","id","RATE_LIMIT_KEY","SESSION_STORAGE_KEY","BROADCAST_CHANNEL_NAME","SESSION_COUNTS_KEY","userId","sessionId","SESSION_COUNTS_EXPIRY_MS","SESSION_COUNTS_LAST_CLEANUP_KEY","SESSION_COUNTS_CLEANUP_THROTTLE_MS","IDENTITY_KEY","projectId","PENDING_IDENTITY_KEY","SpecialApiUrl","DeviceType","EmitterEvent","PermanentError","_PermanentError","message","statusCode","responseCode","RateLimitError","_RateLimitError","TimeoutError","_TimeoutError","EventType","ScrollDirection","ErrorType","Mode","TraceLogValidationError","errorCode","layer","AppConfigValidationError","SessionTimeoutValidationError","SamplingRateValidationError","IntegrationValidationError","InitializationTimeoutError","timeoutMs","LOG_STYLE_ACTIVE","LOG_STYLE_DISABLED","LOG_STYLE_CRITICAL","formatLogMsg","msg","error","sanitizedMessage","isQaModeActive","log","type","extra","data","showToClient","style","visibility","formattedMsg","method","shouldShowLog","effectiveStyle","getEffectiveStyle","sanitizedData","sanitizeLogData","outputLog","providedStyle","hasStyle","styledMsg","sanitized","sensitiveKeys","key","value","lowerKey","sensitiveKey","item","coarsePointerQuery","noHoverQuery","initMediaQueries","UNKNOWN","detectOS","nav","platform","ua","detectBrowser","brands","firstBrand","b","brand","getDeviceType","uaPlatform","width","hasCoarsePointer","hasNoHover","hasTouchSupport","isMobileUA","isTabletUA","getDeviceInfo","WEB_VITALS_GOOD_THRESHOLDS","WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS","WEB_VITALS_POOR_THRESHOLDS","DEFAULT_WEB_VITALS_MODE","getWebVitalsThresholds","mode","MAX_NAVIGATION_HISTORY","version","LIB_VERSION","isBrowserEnvironment","cleanUrlParameter","params","search","url","detectQaMode","urlParam","storedState","newState","COMPOUND_TLDS","getRootDomain","hostname","parts","lastTwo","isSameDomain","hostname1","hostname2","getExternalReferrer","referrer","referrerHostname","currentHostname","getUTMParameters","urlParams","utmParams","param","generateUUID","c","r","eventSequence","lastEventTimestamp","generateEventId","timestamp","sequence","random","bytes","isValidUrl","generateSaasApiUrl","host","cleanDomain","collectApiUrl","getCollectApiUrls","config","urls","normalizeUrl","sensitiveQueryParams","urlObject","searchParams","allSensitiveParams","hasChanged","removedParams","sanitizeString","xssPatternMatches","pattern","beforeReplace","sanitizeValue","depth","sanitizedObject","limitedEntries","value_","sanitizedKey","sanitizedValue","sanitizeMetadata","metadata","errorMessage","PII_PATTERNS","sanitizePii","text","validateAppConfig","validateIntegrations","validModes","validKeys","integrations","validateAndNormalizeConfig","isSerializable","seen","v","isOnlyPrimitiveFields","object","sanitizeTraits","traits","filtered","isValidEventName","eventName","validateSingleMetadata","sanitizedMetadata","intro","jsonString","isValidMetadata","sanitizedArray","i","itemValidation","isEventValid","nameValidation","metadataValidation","Emitter","event","callback","callbacks","index","URL_PATTERN","UUID_PATTERN","HEX_ADDR_PATTERN","LONG_NUMBER_PATTERN","LONG_QUOTED_PATTERN","normalizeErrorMessage","buildErrorSignatureKey","input","rawFilename","cut","filename","line","globalState","StateManager","SenderManager","storeManager","apiUrl","legacySaasQueueKey","legacyCustomQueueKey","legacySaasRateLimitKey","legacyCustomRateLimitKey","legacyRaw","targetKey","currentRaw","legacy","current","e","mergedEvents","merged","until","raw","existing","stored","body","stableBody","existingFailures","success","recoveryBody","recoveryFailures","persistedData","rawFailures","attempt","exponentialDelay","jitter","resolve","requestBody","elapsed","payload","allTimeouts","hadHttpResponse","isLastAttempt","controller","didTimeout","timeoutId","response","blob","accepted","enrichedBody","preferredToken","idempotencyToken","ids","hash","storageKey","persistedDataString","queue","originalEvents","cutoff","filteredEvents","eventTimestamp","skipThrottle","timeSinceExisting","context","now","TimeManager","offset","VALID_EVENT_TYPES","EventManager","emitter","collectApiUrls","recoveryPromises","sender","_eventCount","recoveredEvents","eventIds","page_url","from_page_url","scroll_data","click_data","custom_event","web_vitals","error_data","page_view","currentSessionId","isCriticalEvent","eventType","typeLimit","currentCount","maxSameEventPerMinute","isSessionStart","currentPageUrl","_session_id","rest","bufferedEvents","result","groups","corruptedIds","group","groupEvents","isSync","planned","batch","totalEvents","acc","p","results","anySucceeded","sendPromises","failedCount","eventMap","order","signature","events","a","globalMetadata","identity","rawPageUrl","validation","sessionReferrer","sessionUtm","fingerprint","lastSeen","x","y","_","sorted","nonCriticalIndex","removedEvent","delay","baseInterval","backoff","samplingRate","validTimestamps","ts","eventIdSet","eventData","publicEvent","fn","args","parsed","lastCleanup","timeSinceLastCleanup","prefix","keysToRemove","dataToStore","UserManager","storageManager","storedUserId","newUserId","SESSION_ID_PATTERN","SessionManager","eventManager","action","messageProjectId","storedSession","sessionTimeout","lastActivity","utm","localData","sessionData","session","recoveredSessionId","newSessionId","SessionHandler","PageViewHandler","onTrack","original","rawUrl","normalizedUrl","throttleMs","fromUrl","pageViewData","title","ClickHandler","mouseEvent","target","clickedElement","clickThrottleMs","trackingElement","relevantClickElement","coordinates","trackingData","attributeData","clickData","element","lastClickTime","entries","excessCount","toDelete","testId","tlogName","path","selector","firstClass","parent","name","relevantElement","href","clickedText","relevantText","finalText","ScrollHandler","container","elements","walker","node","htmlElement","initialScrollTop","initialDepth","handleScroll","scrollData","newDepth","previous","scrollTop","scrollHeight","viewportHeight","maxScrollTop","lastScrollPos","direction","hasVerticalScrollableOverflow","hasVerticalOverflowContent","SHOPIFY_SESSION_ATTR","SHOPIFY_USER_ATTR","ShopifyCartLinker","rawUserId","dedupKey","attributes","StorageManager","retryError","criticalPrefixes","persistedKeys","nonCriticalKeys","storage","testKey","PerformanceHandler","obs","list","last","clsValue","currentNavId","navId","entry","worst","dur","onLCP","onCLS","onFCP","onTTFB","onINP","report","metric","ttfb","sample","reportedForNav","oldestNav","counter","baseId","supported","cb","options","once","observer","callbackError","threshold","ErrorHandler","_ErrorHandler","nextCount","stack","errorName","reason","truncated","lastSeenAt","limit","excess","App","normalizedMetadata","valid","force","handler","device","pageUrl","trimmedUserId","validTraits","projectKey","pendingRaw","pending","normalizedPending","normalizedIdentity","onPageView","linker","pendingListeners","app","isInitializing","isDestroying","initPromise","init","validatedConfig","instance","appInitPromise","timeoutPromise","reject","cleanupError","on","off","l","isInitialized","getSessionId","getUserId","destroy","identify","resetIdentity","tracelog"],"mappings":"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","file":"public-api.cjs","sourcesContent":["/**\n * Consolidated configuration constants for TraceLog\n * This file centralizes all timing, limits, browser, and initialization constants\n */\n\n// ============================================================================\n// SESSION & TIMING\n// ============================================================================\n\nexport const DEFAULT_SESSION_TIMEOUT = 15 * 60 * 1000; // 15 minutes\nexport const DUPLICATE_EVENT_THRESHOLD_MS = 1000; // 1 second (increased from 500ms to reduce duplicate events)\nexport const EVENT_SENT_INTERVAL_MS = 10000; // 10 seconds\nexport const MIN_SEND_INTERVAL_MS = 1000; // 1 second\nexport const MAX_SEND_INTERVAL_MS_CONFIG = 60000; // 60 seconds\n\n// Throttling and debouncing\nexport const SCROLL_DEBOUNCE_TIME_MS = 250;\nexport const DEFAULT_PAGE_VIEW_THROTTLE_MS = 1000; // 1 second throttle for page views\nexport const DEFAULT_CLICK_THROTTLE_MS = 300; // 300ms throttle for clicks per element\n\n// Click throttle cache limits\nexport const MAX_THROTTLE_CACHE_ENTRIES = 1000; // Maximum element signatures to track\nexport const THROTTLE_ENTRY_TTL_MS = 300000; // 5 minutes TTL for throttle entries\nexport const THROTTLE_PRUNE_INTERVAL_MS = 30000; // 30 seconds interval for cache pruning\n\n// Event expiry\nexport const EVENT_EXPIRY_HOURS = 2;\nexport const EVENT_PERSISTENCE_MAX_AGE_MS = 2 * 60 * 60 * 1000; // 2 hours\nexport const PERSISTENCE_THROTTLE_MS = 1000; // 1 second throttle for cross-tab persistence coordination\n\n/**\n * Maximum age (in ms) of individual events recovered from localStorage before\n * they are dropped during replay. Distinct from `EVENT_EXPIRY_HOURS` which\n * gates the whole persisted envelope — this gates each `events[].timestamp`.\n *\n * Why this matters: a batch can survive across multiple persist/recover/fail\n * cycles (the envelope timestamp is reset on every re-persist, but inner\n * event timestamps stay frozen at their original capture time). A device with\n * a stale clock or a long offline session can accumulate events older than the\n * server's `MAX_PAST_OFFSET_DAYS` window, which the API would clamp or reject.\n * Dropping them here avoids shipping data the server cannot trust.\n *\n * Kept one day under the server's 7-day past-window so borderline cases never\n * leave the client.\n */\nexport const MAX_EVENT_AGE_MS_ON_RECOVERY = 6 * 24 * 60 * 60 * 1000; // 6 days\n\n// ============================================================================\n// LIMITS & REQUESTS\n// ============================================================================\n\nexport const MAX_EVENTS_QUEUE_LENGTH = 100;\nexport const REQUEST_TIMEOUT_MS = 15000; // 15 seconds (to ensure requests complete before tab close/navigation)\nexport const MAX_METADATA_SIZE = 5000;\n\n// Motion and interaction thresholds\nexport const DEFAULT_MOTION_THRESHOLD = 2;\nexport const SIGNIFICANT_SCROLL_DELTA = 10;\nexport const MIN_SCROLL_DEPTH_CHANGE = 5;\nexport const SCROLL_MIN_EVENT_INTERVAL_MS = 500;\nexport const MAX_SCROLL_EVENTS_PER_SESSION = 120;\n\n// Sampling and rate limits\nexport const DEFAULT_SAMPLING_RATE = 1;\nexport const MIN_SAMPLING_RATE = 0;\nexport const MAX_SAMPLING_RATE = 1;\nexport const RATE_LIMIT_WINDOW_MS = 1000; // 1 second window\nexport const MAX_EVENTS_PER_SECOND = 50; // Maximum 50 events per second (Phase 3: reduced from 200)\nexport const MAX_SAME_EVENT_PER_MINUTE = 60; // Maximum same custom event name per minute (prevents infinite loops)\nexport const PER_EVENT_RATE_LIMIT_WINDOW_MS = 60000; // 60 second window for per-event-name rate limiting\n\n// Per-session event caps (Phase 3)\nexport const MAX_EVENTS_PER_SESSION = 1000;\nexport const MAX_CLICKS_PER_SESSION = 500;\nexport const MAX_PAGE_VIEWS_PER_SESSION = 100;\nexport const MAX_CUSTOM_EVENTS_PER_SESSION = 500;\n\n// Queue and batch limits\nexport const BATCH_SIZE_THRESHOLD = 50;\nexport const MAX_PENDING_EVENTS_BUFFER = 100; // Maximum events to buffer before session init\n\n// Session timeout validation limits\nexport const MIN_SESSION_TIMEOUT_MS = 30000; // 30 seconds minimum\nexport const MAX_SESSION_TIMEOUT_MS = 86400000; // 24 hours maximum\n\n// Custom event validation limits\nexport const MAX_CUSTOM_EVENT_NAME_LENGTH = 120;\nexport const MAX_CUSTOM_EVENT_STRING_SIZE = 48 * 1024; // 48KB (leaves headroom below 64KB sendBeacon limit)\nexport const MAX_CUSTOM_EVENT_KEYS = 100;\nexport const MAX_CUSTOM_EVENT_ARRAY_SIZE = 500;\nexport const MAX_NESTED_OBJECT_KEYS = 200; // Safety bound for sanitizer (must be > MAX_CUSTOM_EVENT_KEYS)\n\n// Text content limits\nexport const MAX_TEXT_LENGTH = 255; // For click tracking text content\n\n// Data sanitization limits\nexport const MAX_STRING_LENGTH = 1000;\nexport const MAX_STRING_LENGTH_IN_ARRAY = 500; // Strings within arrays are more limited\nexport const MAX_ARRAY_LENGTH = 1000; // Safety bound for sanitizer (must be > MAX_CUSTOM_EVENT_ARRAY_SIZE)\nexport const MAX_OBJECT_DEPTH = 10; // Sufficient for any real use case, protects against circular/malicious structures\n\n// Precision for numeric metrics\nexport const PRECISION_TWO_DECIMALS = 2 as const;\n\n// Sync XHR timeout\nexport const SYNC_XHR_TIMEOUT_MS = 2000; // 2 seconds\n\n// sendBeacon payload size limit (Phase 3)\nexport const MAX_BEACON_PAYLOAD_SIZE = 64 * 1024; // 64KB browser limit\n\n// Event fingerprint management (moderately increased for high-frequency sessions)\nexport const MAX_FINGERPRINTS = 1500; // Maximum fingerprints stored before cleanup (increased from 1000)\nexport const FINGERPRINT_CLEANUP_MULTIPLIER = 10; // Cleanup fingerprints older than 10x threshold (10 seconds)\nexport const MAX_FINGERPRINTS_HARD_LIMIT = 3000; // Hard limit for aggressive cleanup (increased from 2000)\n\n// ============================================================================\n// BROWSER & HTML\n// ============================================================================\n\n/**\n * Prefix for all HTML data attributes used by TraceLog\n * Used for features like data-tlog-ignore, data-tlog-event, etc.\n */\nexport const HTML_DATA_ATTR_PREFIX = 'data-tlog';\n\n/**\n * CSS selectors for interactive elements to track in click events\n *\n * Covers:\n * - Standard HTML interactive elements (button, a, input, select, textarea)\n * - ARIA roles (button, link, tab, menuitem, option, checkbox, radio, switch)\n * - Framework-specific attributes (routerLink, ng-click)\n * - Data attributes for actions (data-action, data-click, data-navigate, data-toggle)\n * - Common CSS classes (.btn, .button, .clickable, .nav-link, .menu-item)\n * - Testing attributes (data-testid)\n * - Accessibility (tabindex=\"0\")\n */\nexport const INTERACTIVE_SELECTORS = [\n 'button',\n 'a',\n 'input[type=\"button\"]',\n 'input[type=\"submit\"]',\n 'input[type=\"reset\"]',\n 'input[type=\"checkbox\"]',\n 'input[type=\"radio\"]',\n 'select',\n 'textarea',\n '[role=\"button\"]',\n '[role=\"link\"]',\n '[role=\"tab\"]',\n '[role=\"menuitem\"]',\n '[role=\"option\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"switch\"]',\n '[routerLink]',\n '[ng-click]',\n '[data-action]',\n '[data-click]',\n '[data-navigate]',\n '[data-toggle]',\n '[onclick]',\n '.btn',\n '.button',\n '.clickable',\n '.nav-link',\n '.menu-item',\n '[data-testid]',\n '[tabindex=\"0\"]',\n] as const;\n\n/**\n * Standard UTM (Urchin Tracking Module) parameters for marketing attribution\n * These parameters are preserved in URLs for campaign tracking\n */\nexport const UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n\n/**\n * Default list of sensitive URL query parameters to filter out for privacy protection\n *\n * Includes:\n * - Authentication tokens (token, auth, key, session, access_token, refresh_token)\n * - Password reset links (reset, password, verification, code, otp)\n * - API keys (api_key, apikey, secret)\n *\n * These parameters are removed from tracked URLs to prevent PII leakage\n */\nexport const DEFAULT_SENSITIVE_QUERY_PARAMS = [\n 'token',\n 'auth',\n 'key',\n 'session',\n 'reset',\n 'password',\n 'api_key',\n 'apikey',\n 'secret',\n 'access_token',\n 'refresh_token',\n 'verification',\n 'code',\n 'otp',\n] as const;\n\n// ============================================================================\n// ============================================================================\n// INITIALIZATION\n// ============================================================================\n\nexport const INITIALIZATION_MAX_CONCURRENT_RETRIES = 20;\nexport const INITIALIZATION_CONCURRENT_RETRY_DELAY_MS = 50;\nexport const INITIALIZATION_TIMEOUT_MS = 10000;\n\n// ============================================================================\n// SESSION MANAGEMENT\n// ============================================================================\n\nexport const SESSION_SYNC_TIMEOUT_MS = 2000;\nexport const SESSION_MAX_RETRY_ATTEMPTS = 3;\nexport const SESSION_CLEANUP_DELAY_MS = 100;\n\n// Cross-tab coordination\nexport const CROSS_TAB_INITIALIZATION_LOCK_TIMEOUT_MS = 5000;\nexport const TAB_HEARTBEAT_INTERVAL_MS = 5000; // 5 seconds\nexport const TAB_ELECTION_TIMEOUT_MS = 2000; // 2 seconds\nexport const TAB_CLEANUP_DELAY_MS = 1000; // 1 second\n\n// Session recovery\nexport const SESSION_RECOVERY_WINDOW_MULTIPLIER = 2; // 2x session timeout\nexport const MAX_SESSION_RECOVERY_ATTEMPTS = 3;\nexport const MAX_SESSION_RECOVERY_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours max\nexport const MIN_SESSION_RECOVERY_WINDOW_MS = 2 * 60 * 1000; // 2 minutes minimum\n\n// ============================================================================\n// SCROLL SUPPRESSION\n// ============================================================================\n\nexport const SCROLL_SUPPRESS_MULTIPLIER = 2;\n\n// ============================================================================\n// NETWORK TIMING\n// ============================================================================\n\nexport const RATE_LIMIT_INTERVAL = 1000; // 1 second\n\n// ============================================================================\n// RETRY CONFIGURATION\n// ============================================================================\n\n/**\n * Maximum number of retry attempts for failed event transmissions\n * Applied to 5xx errors and network timeouts (transient failures)\n * 4xx errors (permanent) are not retried\n */\nexport const MAX_SEND_RETRIES = 2;\n\n/**\n * Base delay for exponential backoff retry strategy (in milliseconds)\n * Formula: RETRY_BACKOFF_BASE_MS * (2 ^ attempt) + jitter\n * Example: attempt 1 = 100ms + jitter, attempt 2 = 200ms + jitter\n */\nexport const RETRY_BACKOFF_BASE_MS = 100;\n\n/**\n * Maximum random jitter added to retry backoff delay (in milliseconds)\n * Prevents thundering herd problem when multiple clients retry simultaneously\n * Jitter range: 0 to RETRY_BACKOFF_JITTER_MS\n */\nexport const RETRY_BACKOFF_JITTER_MS = 100;\n\n/**\n * Maximum delay between send attempts when using exponential backoff (in milliseconds)\n * Caps the backoff at 2 minutes to prevent excessively long delays\n * Formula: min(EVENT_SENT_INTERVAL_MS * 2^failures, MAX_SEND_INTERVAL_MS)\n */\nexport const MAX_SEND_INTERVAL_MS = 120000;\n\n/**\n * Maximum consecutive network-level failures (DNS, connection refused) before the\n * circuit opens and further send attempts are skipped for the current session.\n *\n * Network failures are fetch() rejections where no HTTP response was received —\n * distinct from timeout errors and HTTP status errors. Three consecutive failures\n * strongly indicate a permanently misconfigured URL rather than a transient outage.\n *\n * After CIRCUIT_BREAKER_COOLDOWN_MS elapses, the circuit transitions to half-open\n * and allows a single probe batch through. A successful probe closes the circuit;\n * a failed probe re-opens it for another cooldown period.\n */\nexport const MAX_CONSECUTIVE_NETWORK_FAILURES = 3;\n\n/**\n * Cooldown period before the network circuit breaker transitions to half-open\n * and allows a single probe request through. Aligned with MAX_SEND_INTERVAL_MS\n * so the EventManager's backoff scheduler naturally triggers the probe.\n */\nexport const CIRCUIT_BREAKER_COOLDOWN_MS = 120_000; // 2 minutes\n\n/**\n * Cooldown period applied after receiving a 429 response from the ingestion API.\n *\n * During this window the sender skips fetch() calls (events are persisted to\n * localStorage instead). Aligned with the server's session rate-limit window\n * (1 minute) so the next attempt arrives after the server's counter resets.\n *\n * **Why persisted**: Traditional server-rendered sites fire a full page load\n * on every navigation, and users frequently open multiple tabs on the same\n * origin. The cooldown lives in `localStorage` so it survives navigations and\n * is discoverable by any tab on the same origin — otherwise each fresh\n * SenderManager starts with `consecutiveSendFailures = 0` and hammers the API\n * through the 429 with no shared backoff memory.\n */\nexport const RATE_LIMIT_COOLDOWN_MS = 60_000; // 1 minute, matches server RATE_LIMIT_WINDOW_MS\n\n/**\n * Maximum number of cross-session recovery attempts for a persisted event batch.\n *\n * Each page load that attempts to recover a persisted batch and fails increments\n * this counter inside the stored data. When the counter reaches this limit the\n * batch is discarded instead of being kept for another attempt, breaking the\n * infinite persistence loop caused by permanently unreachable backend URLs.\n */\nexport const MAX_RECOVERY_FAILURES = 3;\n\n/**\n * Maximum consecutive send failures before entering cooldown mode.\n *\n * After this many failures:\n * - **Batch-threshold sends** (50+ events) are suppressed to prevent rapid retries\n * - **Periodic scheduler** continues at MAX_SEND_INTERVAL_MS (2 min) for auto-recovery\n * - Counter resets on successful send, stop(), or page reload\n *\n * This implements a circuit breaker pattern:\n * - Closed (0 failures): normal 10s send interval\n * - Half-open (1-4 failures): exponential backoff (20s, 40s, 80s, 120s)\n * - Open (5 failures): cooldown retries every 2 min, batch threshold blocked\n *\n * Each failure includes up to 2 per-request retries in SenderManager.\n */\nexport const MAX_CONSECUTIVE_SEND_FAILURES = 5;\n\n// ============================================================================\n// VALIDATION\n// ============================================================================\n\n/**\n * Standardized validation error messages for TraceLog configuration\n *\n * Centralizes all validation messages to ensure consistency across:\n * - API layer validation\n * - Configuration validation\n * - Runtime validation\n *\n * Messages include contextual information (e.g., min/max values) to help developers\n * quickly identify and fix configuration issues.\n */\nexport const VALIDATION_MESSAGES = {\n MISSING_PROJECT_ID: 'Project ID is required',\n PROJECT_ID_EMPTY_AFTER_TRIM: 'Project ID is required',\n INVALID_SESSION_TIMEOUT: `Session timeout must be between ${MIN_SESSION_TIMEOUT_MS}ms (30 seconds) and ${MAX_SESSION_TIMEOUT_MS}ms (24 hours)`,\n INVALID_SAMPLING_RATE: 'Sampling rate must be between 0 and 1',\n INVALID_ERROR_SAMPLING_RATE: 'Error sampling must be between 0 and 1',\n INVALID_TRACELOG_PROJECT_ID: 'TraceLog project ID is required when integration is enabled',\n INVALID_SCROLL_CONTAINER_SELECTORS: 'Scroll container selectors must be valid CSS selectors',\n INVALID_GLOBAL_METADATA: 'Global metadata must be an object',\n INVALID_SENSITIVE_QUERY_PARAMS: 'Sensitive query params must be an array of strings',\n INVALID_PAGE_VIEW_THROTTLE: 'Page view throttle must be a non-negative number',\n INVALID_CLICK_THROTTLE: 'Click throttle must be a non-negative number',\n INVALID_MAX_SAME_EVENT_PER_MINUTE: 'Max same event per minute must be a positive number',\n INVALID_SEND_INTERVAL: `Send interval must be between ${MIN_SEND_INTERVAL_MS}ms (1 second) and ${MAX_SEND_INTERVAL_MS_CONFIG}ms (60 seconds)`,\n} as const;\n\n// ============================================================================\n// SECURITY\n// ============================================================================\n\n/**\n * Regular expressions for detecting and sanitizing XSS (Cross-Site Scripting) attacks\n *\n * Patterns detect:\n * - Script tags (<script>)\n * - JavaScript protocol URLs (javascript:)\n * - Inline event handlers (onclick=, onload=, etc.)\n * - Embedded content (<iframe>, <embed>, <object>)\n *\n * Used to sanitize user-provided data before storage or transmission\n */\nexport const XSS_PATTERNS = [\n /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n /javascript:/gi,\n /on\\w+\\s*=/gi,\n /<iframe\\b[^<]*(?:(?!<\\/iframe>)<[^<]*)*<\\/iframe>/gi,\n /<embed\\b[^>]*>/gi,\n /<object\\b[^<]*(?:(?!<\\/object>)<[^<]*)*<\\/object>/gi,\n] as const;\n","/**\n * Storage key management constants for TraceLog\n * All keys are namespaced with 'tlog' prefix to avoid conflicts\n */\n\n/**\n * Base key prefix for all TraceLog localStorage items\n * Used as namespace to prevent conflicts with other libraries\n */\nexport const STORAGE_BASE_KEY = 'tlog';\n\n/**\n * Storage key for QA mode flag in sessionStorage\n * Format: 'tlog:qa_mode'\n */\nexport const QA_MODE_KEY = `${STORAGE_BASE_KEY}:qa_mode`;\n\n/**\n * Storage key for user ID in localStorage\n * Format: 'tlog:uid'\n */\nexport const USER_ID_KEY = `${STORAGE_BASE_KEY}:uid`;\n\n/**\n * URL parameter name for activating/deactivating QA mode\n * Example: ?tlog_mode=qa or ?tlog_mode=qa_off\n */\nexport const QA_MODE_URL_PARAM = 'tlog_mode';\n\n/**\n * URL parameter value to enable QA mode\n */\nexport const QA_MODE_ENABLE_VALUE = 'qa';\n\n/**\n * URL parameter value to disable QA mode\n */\nexport const QA_MODE_DISABLE_VALUE = 'qa_off';\n\n/**\n * Generates storage key for event queue\n *\n * @param id - User ID or project identifier\n * @returns localStorage key for event queue (e.g., 'tlog:user123:queue')\n */\nexport const QUEUE_KEY = (id: string): string => (id ? `${STORAGE_BASE_KEY}:${id}:queue` : `${STORAGE_BASE_KEY}:queue`);\n\n/**\n * Generates storage key for per-sender rate-limit cooldown timestamp.\n *\n * Persisted in `localStorage` so the cooldown survives both page navigations\n * and is visible to other tabs/windows on the same origin. Without this, each\n * fresh `SenderManager` starts with a zeroed backoff and would continue\n * hammering the API through the server's 429 window.\n *\n * @param id - User ID\n * @returns localStorage key (e.g., 'tlog:user123:rate_limit')\n */\nexport const RATE_LIMIT_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:rate_limit` : `${STORAGE_BASE_KEY}:rate_limit`;\n\n/**\n * Generates storage key for session data\n *\n * @param id - Project identifier\n * @returns localStorage key for session (e.g., 'tlog:project123:session')\n */\nexport const SESSION_STORAGE_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:session` : `${STORAGE_BASE_KEY}:session`;\n\n/**\n * Generates storage key for cross-tab session synchronization\n *\n * @param id - Project identifier\n * @returns localStorage key for cross-tab session (e.g., 'tlog:project123:cross_tab_session')\n */\nexport const CROSS_TAB_SESSION_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:cross_tab_session` : `${STORAGE_BASE_KEY}:cross_tab_session`;\n\n/**\n * Generates storage key for tab information registry\n *\n * @param id - Project identifier\n * @returns localStorage key for tab info (e.g., 'tlog:project123:tab_info')\n */\nexport const TAB_INFO_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:tab_info` : `${STORAGE_BASE_KEY}:tab_info`;\n\n/**\n * Generates storage key for specific tab information\n *\n * @param projectId - Project identifier\n * @param tabId - Unique tab identifier\n * @returns localStorage key for tab-specific info (e.g., 'tlog:project123:tab:abc456:info')\n */\nexport const TAB_SPECIFIC_INFO_KEY = (projectId: string, tabId: string): string =>\n `${STORAGE_BASE_KEY}:${projectId}:tab:${tabId}:info`;\n\n/**\n * Generates storage key for session recovery data\n *\n * @param id - Project identifier\n * @returns localStorage key for session recovery (e.g., 'tlog:project123:recovery')\n */\nexport const SESSION_RECOVERY_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:recovery` : `${STORAGE_BASE_KEY}:recovery`;\n\n/**\n * Generates BroadcastChannel name for cross-tab communication\n *\n * @param id - Project identifier\n * @returns BroadcastChannel name (e.g., 'tlog:project123:broadcast')\n */\nexport const BROADCAST_CHANNEL_NAME = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:broadcast` : `${STORAGE_BASE_KEY}:broadcast`;\n\n/**\n * Generates storage key for per-session event counts\n *\n * Used to persist rate limiting counters across page reloads within the same session.\n * This prevents users from bypassing per-session event limits by refreshing the page.\n *\n * @param userId - User identifier\n * @param sessionId - Session identifier\n * @returns localStorage key for session counts (e.g., 'tlog:user123:session_counts:session456')\n */\nexport const SESSION_COUNTS_KEY = (userId: string, sessionId: string): string =>\n `${STORAGE_BASE_KEY}:${userId}:session_counts:${sessionId}`;\n\n/**\n * Session counts expiry duration (7 days in milliseconds).\n *\n * Session counts are automatically cleaned up after this duration to prevent\n * localStorage pollution. Counts older than 7 days are considered stale and\n * are removed on next page load.\n *\n * **Rationale**: 7 days provides sufficient buffer for:\n * - Long-running sessions (rare but possible)\n * - Users returning after extended inactivity\n * - While preventing indefinite accumulation (~100 bytes per session)\n */\nexport const SESSION_COUNTS_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\n/**\n * Storage key for tracking last session counts cleanup timestamp.\n *\n * Used to throttle cleanup operations and prevent performance impact\n * from scanning localStorage on every EventManager initialization.\n *\n * Format: 'tlog:session_counts_last_cleanup'\n */\nexport const SESSION_COUNTS_LAST_CLEANUP_KEY = `${STORAGE_BASE_KEY}:session_counts_last_cleanup`;\n\n/**\n * Minimum interval between session counts cleanup runs (1 hour in milliseconds).\n *\n * Cleanup will only run if at least this much time has elapsed since the\n * last cleanup. This prevents performance degradation from frequent localStorage\n * scans while still ensuring regular cleanup of stale data.\n *\n * **Rationale**: 1 hour provides a good balance between:\n * - Preventing frequent scans on rapid page reloads\n * - Ensuring cleanup runs at least once per typical browsing session\n * - Minimal localStorage overhead (~100 entries typical, <1ms scan time)\n */\nexport const SESSION_COUNTS_CLEANUP_THROTTLE_MS = 60 * 60 * 1000; // 1 hour\n\n// ============================================================\n// Identity Storage Keys\n// ============================================================\n\n/**\n * Generates storage key for visitor identity data (project-scoped).\n *\n * Identity is scoped per project because the same user may have different\n * external IDs across different projects from the same site owner.\n *\n * @param projectId - Project identifier\n * @returns localStorage key for identity (e.g., 'tlog:project123:identity')\n */\nexport const IDENTITY_KEY = (projectId: string): string =>\n projectId ? `${STORAGE_BASE_KEY}:${projectId}:identity` : `${STORAGE_BASE_KEY}:identity`;\n\n/**\n * Temporary storage key for identity set before init().\n *\n * When identify() is called before init(), the projectId is unknown.\n * Identity is stored under this key and moved to the project-scoped key\n * once init() resolves the projectId.\n */\nexport const PENDING_IDENTITY_KEY = `${STORAGE_BASE_KEY}:pending_identity`;\n","import { MetadataType } from './common.types';\nimport { WebVitalType } from './event.types';\n\n/**\n * Web Vitals filtering mode\n * - 'all': Track all Web Vitals metrics (full analytics)\n * - 'needs-improvement': Track metrics that need improvement or are poor (default, balanced)\n * - 'poor': Track only poor metrics (minimal data)\n */\nexport type WebVitalsMode = 'all' | 'needs-improvement' | 'poor';\n\nexport interface Config {\n /** Session inactivity timeout in milliseconds. @default 900000 */\n sessionTimeout?: number;\n /** Metadata appended to every tracked event. */\n globalMetadata?: Record<string, MetadataType>;\n /** Query parameters to remove before tracking URLs. */\n sensitiveQueryParams?: string[];\n /** Error event sampling rate between 0 and 1. @default 1 */\n errorSampling?: number;\n /** Event sampling rate between 0 and 1. @default 1 */\n samplingRate?: number;\n /** Page view throttle duration in milliseconds to prevent rapid navigation spam. @default 1000 */\n pageViewThrottleMs?: number;\n /** Click throttle duration in milliseconds to prevent double-clicks and rapid spam. @default 300 */\n clickThrottleMs?: number;\n /** Maximum number of same custom event name allowed per minute to prevent infinite loops. @default 60 */\n maxSameEventPerMinute?: number;\n /**\n * Web Vitals filtering mode. @default 'needs-improvement'\n * - 'all': Track all metrics (good, needs-improvement, poor) - full trend analysis\n * - 'needs-improvement': Track metrics that need improvement or are poor - balanced approach\n * - 'poor': Track only poor metrics - minimal data, focus on problems\n */\n webVitalsMode?: WebVitalsMode;\n /**\n * Custom Web Vitals thresholds in milliseconds (except CLS which is unitless).\n * Only applies when webVitalsMode is set. Overrides default thresholds for the selected mode.\n */\n webVitalsThresholds?: Partial<Record<WebVitalType, number>>;\n /** Interval in milliseconds between event batch sends. @default 10000 (10 seconds) */\n sendIntervalMs?: number;\n /**\n * Opt-in: when `true`, the event queue is flushed after every SPA navigation\n * (`pushState`, `replaceState`, `popstate`, `hashchange`). Defaults to `false`\n * because per-route flushing can multiply request volume on SPA-heavy apps.\n * @default false\n */\n flushOnSpaNavigation?: boolean;\n /**\n * If true, the event queue is flushed when `document.hidden` becomes `true`\n * (tab switch, lock screen, app backgrounding). Especially relevant on mobile Safari\n * where `pagehide`/`beforeunload` may not fire reliably.\n * @default true\n */\n flushOnPageHidden?: boolean;\n /** TraceLog SaaS integration. */\n integrations?: {\n tracelog?: {\n /** Required project ID for TraceLog SaaS integration. */\n projectId: string;\n /** Enable Shopify cart attribute linking for webhook revenue attribution. */\n shopify?: boolean;\n };\n };\n}\n\nexport enum SpecialApiUrl {\n Localhost = 'localhost:8080',\n Fail = 'localhost:9999',\n}\n","/**\n * Device type classification for analytics segmentation\n *\n * **Detection Logic**:\n * - Mobile: User agent contains mobile keywords (iPhone, Android phone, etc.)\n * - Tablet: User agent contains tablet keywords (iPad, Android tablet, etc.)\n * - Desktop: Default fallback for non-mobile/tablet devices\n * - Unknown: User agent not detectable or SSR environment\n *\n * **Use Cases**:\n * - Device-specific analytics and dashboards\n * - User experience optimization by device type\n * - Performance monitoring segmented by device\n *\n * @see src/utils/browser/device-detector.utils.ts for detection implementation\n */\nexport enum DeviceType {\n /** Mobile phones (iPhone, Android phones) */\n Mobile = 'mobile',\n /** Tablet devices (iPad, Android tablets) */\n Tablet = 'tablet',\n /** Desktop computers and laptops */\n Desktop = 'desktop',\n /** Unable to determine device type */\n Unknown = 'unknown',\n}\n\n/**\n * Comprehensive device and environment information\n *\n * **Purpose**: Provides rich device context for analytics segmentation,\n * debugging, and user experience optimization.\n *\n * **Detection**: Uses navigator.userAgentData (modern) with UA string fallback.\n *\n * @see src/utils/browser/device-detector.utils.ts for detection implementation\n */\nexport interface DeviceInfo {\n /** Device form factor classification */\n type: DeviceType;\n /** OS name: \"Windows\", \"macOS\", \"iOS\", \"Android\", \"Linux\", \"ChromeOS\", \"Unknown\" */\n os: string;\n /** Browser name: \"Chrome\", \"Firefox\", \"Safari\", \"Edge\", \"Opera\", \"Unknown\" */\n browser: string;\n}\n","import { EventData } from './event.types';\nimport { EventsQueue } from './queue.types';\n\n/**\n * Generic callback function for event emitter subscriptions\n *\n * @template T - Type of data passed to the callback\n * @param data - Event data passed to the callback\n */\nexport type EmitterCallback<T = any> = (data: T) => void;\n\n/**\n * Available event emitter channels for TraceLog\n *\n * **Purpose**: Type-safe event subscription system for external integrations\n *\n * **Event Channels**:\n * - `event`: Individual events as they are tracked (real-time)\n * - `queue`: Complete event batches before network transmission (every 10s or 50 events)\n *\n * **Use Cases**:\n * - Real-time event processing\n * - Custom analytics integrations\n * - Debugging and monitoring\n *\n * @example\n * ```typescript\n * // Subscribe to individual events\n * tracelog.on('event', (event) => {\n * console.log('Event tracked:', event.type, event);\n * });\n *\n * // Subscribe to event batches\n * tracelog.on('queue', (batch) => {\n * console.log('Sending batch:', batch.events.length, 'events');\n * });\n * ```\n */\nexport enum EmitterEvent {\n /** Individual events as they are tracked */\n EVENT = 'event',\n /** Complete event batches before transmission */\n QUEUE = 'queue',\n}\n\n/**\n * Type mapping for event emitter channels\n *\n * **Purpose**: Ensures type safety when subscribing to events\n *\n * Maps each EmitterEvent to its corresponding payload type:\n * - `event` → `EventData`: Single event data\n * - `queue` → `EventsQueue`: Batch of events with metadata\n */\nexport interface EmitterMap {\n [EmitterEvent.EVENT]: EventData;\n [EmitterEvent.QUEUE]: EventsQueue;\n}\n","/**\n * Custom error types for TraceLog\n */\n\n/**\n * Represents a permanent HTTP error (4xx) that should not be retried\n * Examples: 400 Bad Request, 403 Forbidden, 404 Not Found\n */\nexport class PermanentError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly responseCode?: string,\n ) {\n super(message);\n this.name = 'PermanentError';\n\n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PermanentError);\n }\n }\n}\n\n/**\n * Represents a rate limit error (429) that should not be retried in the\n * inner send loop. Events are persisted for periodic retry via EventManager\n * backoff. Deduplication of retried events is integration-specific (e.g.\n * TraceLog SaaS handles it server-side; custom backends should implement\n * their own idempotency).\n */\nexport class RateLimitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RateLimitError';\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, RateLimitError);\n }\n }\n}\n\n/**\n * Represents a timeout error where the server likely received the request\n * but the response took too long. Events should NOT be persisted for retry\n * since the server most likely already processed them.\n */\nexport class TimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimeoutError';\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TimeoutError);\n }\n }\n}\n","import { MetadataType } from './common.types';\n\n/**\n * Coordinate information from a click event\n */\nexport type ClickCoordinates = Pick<ClickData, 'x' | 'y'>;\n\n/**\n * Web performance metric types tracked by the library\n * - LCP: Largest Contentful Paint\n * - CLS: Cumulative Layout Shift\n * - INP: Interaction to Next Paint\n * - FCP: First Contentful Paint\n * - TTFB: Time to First Byte\n */\nexport type WebVitalType = 'LCP' | 'CLS' | 'INP' | 'FCP' | 'TTFB';\n\n/**\n * Event type name\n */\nexport type EventTypeName = (typeof EventType)[keyof typeof EventType];\n\n/**\n * Event type enum\n */\nexport enum EventType {\n /** Page navigation and view tracking */\n PAGE_VIEW = 'page_view',\n /** User click interactions */\n CLICK = 'click',\n /** Scroll depth and behavior */\n SCROLL = 'scroll',\n /** Session initialization */\n SESSION_START = 'session_start',\n /** Custom business events */\n CUSTOM = 'custom',\n /** Performance metrics */\n WEB_VITALS = 'web_vitals',\n /** JavaScript errors and rejections */\n ERROR = 'error',\n}\n\n/**\n * Per-session event counts structure for rate limiting.\n *\n * Persisted to localStorage: `tlog:{userId}:session_counts:{sessionId}`\n * Restored on page reload to maintain limits across navigations.\n */\nexport interface SessionEventCounts {\n /** Total events across all types */\n total: number;\n /** Click events count */\n [EventType.CLICK]: number;\n /** Page view events count */\n [EventType.PAGE_VIEW]: number;\n /** Custom events count */\n [EventType.CUSTOM]: number;\n /** Scroll events count */\n [EventType.SCROLL]: number;\n /** Index signature for dynamic event type access */\n [key: string]: number;\n}\n\n/**\n * Scroll direction indicators\n */\nexport enum ScrollDirection {\n /** Scrolling upward */\n UP = 'up',\n /** Scrolling downward */\n DOWN = 'down',\n}\n\n/**\n * JavaScript error classification\n */\nexport enum ErrorType {\n /** Runtime JavaScript errors */\n JS_ERROR = 'js_error',\n /** Unhandled promise rejections */\n PROMISE_REJECTION = 'promise_rejection',\n}\n\n/**\n * Scroll event data captured during user scrolling\n */\nexport interface ScrollData {\n /** Current scroll depth as percentage (0-100) */\n depth: number;\n /** Direction of scroll movement */\n direction: ScrollDirection;\n /** CSS selector of the scrolled container */\n container_selector: string;\n}\n\n/**\n * Click event data capturing user interaction details\n */\nexport interface ClickData {\n /** Absolute X coordinate in viewport (pixels) */\n x: number;\n /** Absolute Y coordinate in viewport (pixels) */\n y: number;\n /** Element ID attribute */\n id?: string;\n /** Element class attribute */\n class?: string;\n /** HTML tag name */\n tag?: string;\n /** Element text content (truncated) */\n text?: string;\n /** Link href for anchor elements */\n href?: string;\n}\n\n/**\n * Element data for specialized click tracking\n * Used for form inputs and interactive elements\n */\nexport interface ClickTrackingElementData {\n /** DOM element being tracked */\n element: HTMLElement;\n /** Descriptive name for the element */\n name: string;\n /** Element value (for inputs) */\n value?: string;\n}\n\n/**\n * Custom event data for business-specific tracking\n */\nexport interface CustomEventData {\n /** Event name identifier */\n name: string;\n /** Additional event metadata */\n metadata?: Record<string, MetadataType> | Record<string, MetadataType>[];\n}\n\n/**\n * Optional flags for `tracelog.event()`.\n */\nexport interface EventOptions {\n /**\n * If `true`, the event queue is flushed via `navigator.sendBeacon()`\n * immediately after this event is tracked. The browser guarantees the\n * request is queued for delivery even if the page is about to unload.\n *\n * Use for high-value events where loss is unacceptable (Purchase, Signup,\n * AddPaymentInfo).\n *\n * @default false\n */\n critical?: boolean;\n}\n\n/**\n * Web performance metrics data\n */\nexport interface WebVitalsData {\n /** Type of performance metric */\n type: WebVitalType;\n /** Metric value (varies by type) */\n value: number;\n}\n\n/**\n * JavaScript error details\n */\nexport interface ErrorData {\n /** Error classification */\n type: ErrorType;\n /** Error message text */\n message: string;\n /** Error constructor name (TypeError, ReferenceError, etc.) when available */\n name?: string;\n /** Source file where error occurred */\n filename?: string;\n /** Line number in source file */\n line?: number;\n /** Column number in source file */\n column?: number;\n /** Error stack trace (truncated to 2000 chars) */\n stack?: string;\n}\n\n/**\n * UTM campaign tracking parameters\n */\nexport interface UTM {\n /** Campaign source (e.g., google, newsletter) */\n source?: string;\n /** Campaign medium (e.g., cpc, email) */\n medium?: string;\n /** Campaign name identifier */\n campaign?: string;\n /** Campaign search term */\n term?: string;\n /** Campaign content variation */\n content?: string;\n}\n\n/**\n * Page view navigation data\n */\nexport interface PageViewData {\n /** Previous page URL */\n referrer?: string;\n /** Page title from document */\n title?: string;\n}\n\n/**\n * Complete event data structure\n * All events share base properties with type-specific data\n */\nexport interface EventData {\n /** Unique event identifier */\n id: string;\n /** Event type classification */\n type: EventType;\n /** Current page URL where event occurred */\n page_url: string;\n /** Unix timestamp (milliseconds) */\n timestamp: number;\n /** HTTP referrer header */\n referrer?: string;\n /** Previous page URL for navigation events */\n from_page_url?: string;\n /** Scroll event details (when type is SCROLL) */\n scroll_data?: ScrollData;\n /** Click event details (when type is CLICK) */\n click_data?: ClickData;\n /** Custom event details (when type is CUSTOM) */\n custom_event?: CustomEventData;\n /** Performance metrics (when type is WEB_VITALS) */\n web_vitals?: WebVitalsData;\n /** Page view details (when type is PAGE_VIEW) */\n page_view?: PageViewData;\n /** Error details (when type is ERROR) */\n error_data?: ErrorData;\n /** Campaign tracking parameters */\n utm?: UTM;\n}\n\n/**\n * Internal queue entry: an `EventData` enriched with the session ID frozen at\n * `track()` time. Survives session renewal — when the user is idle past the\n * timeout, `state.sessionId` is nulled but events already in the queue keep\n * their original `_session_id`, so `EventManager.buildBatchesWithIds()` can\n * still attribute them correctly instead of emitting `session_id: null` to the\n * wire.\n */\nexport interface QueuedEvent extends EventData {\n _session_id: string;\n}\n","/**\n * App modes for the TraceLog Library\n *\n * - QA: Quality Assurance mode - logs custom events to console for verification\n */\nexport enum Mode {\n QA = 'qa',\n}\n","/**\n * Custom error classes for TraceLog validation errors\n * Provides better error handling and consistency across validation layers\n */\n\n/**\n * Base class for all TraceLog validation errors\n */\nexport abstract class TraceLogValidationError extends Error {\n constructor(\n message: string,\n public readonly errorCode: string,\n public readonly layer: 'config' | 'app' | 'runtime',\n ) {\n super(message);\n this.name = this.constructor.name;\n\n // Maintains proper stack trace for where error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when app configuration validation fails\n */\nexport class AppConfigValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'APP_CONFIG_INVALID', layer);\n }\n}\n\n/**\n * Thrown when session timeout validation fails\n */\nexport class SessionTimeoutValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'SESSION_TIMEOUT_INVALID', layer);\n }\n}\n\n/**\n * Thrown when sampling rate validation fails\n */\nexport class SamplingRateValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'SAMPLING_RATE_INVALID', layer);\n }\n}\n\n/**\n * Thrown when integrations validation fails\n */\nexport class IntegrationValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'INTEGRATION_INVALID', layer);\n }\n}\n\n/**\n * Thrown when initialization exceeds the maximum allowed timeout\n */\nexport class InitializationTimeoutError extends TraceLogValidationError {\n constructor(\n message: string,\n public readonly timeoutMs: number,\n layer: 'config' | 'app' | 'runtime' = 'runtime',\n ) {\n super(message, 'INITIALIZATION_TIMEOUT', layer);\n }\n}\n","/**\n * Console log style for active TraceLog operations\n * Used for visual highlighting in browser console during QA mode\n */\nexport const LOG_STYLE_ACTIVE =\n 'background: #ff9800; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;';\n\n/**\n * Console log style for disabled TraceLog operations\n * Used for visual indication when features are disabled\n */\nexport const LOG_STYLE_DISABLED =\n 'background: #9e9e9e; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;';\n\n/**\n * Console log style for critical errors (always visible)\n * Used for errors that must reach monitoring platforms like Sentry\n */\nexport const LOG_STYLE_CRITICAL =\n 'background: #d32f2f; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;';\n","import { QA_MODE_KEY } from '../constants/storage.constants';\nimport { LOG_STYLE_CRITICAL } from '../constants/app.constants';\n\n/**\n * Log visibility level determining when logs are shown\n *\n * - 'critical': Always visible (production included) - for Sentry/monitoring\n * - 'qa': Only visible when QA mode is active (equivalent to showToClient: true)\n * - undefined: Only visible in NODE_ENV=development\n */\nexport type LogVisibility = 'critical' | 'qa';\n\n/**\n * Formats log messages with optional error information and environment-specific sanitization\n *\n * **Purpose**: Creates consistent log message format across the library with automatic\n * error handling and production-safe output.\n *\n * **Behavior**:\n * - **Production**: Sanitizes stack traces and file paths from error messages\n * - **Development**: Preserves full error messages for debugging\n * - **Fallback**: Handles non-Error objects gracefully\n *\n * **Format**: `[TraceLog] {message}: {error}` or `[TraceLog] {message}` if no error\n *\n * **Stack Trace Sanitization** (production only):\n * - Removes \"at function (...)\" stack lines\n * - Removes file paths with line/column numbers\n * - Prevents leaking internal file structure\n *\n * @param msg - Base log message\n * @param error - Optional error object (Error, string, object, or unknown type)\n * @returns Formatted log message string\n *\n * @example\n * ```typescript\n * // Basic message\n * formatLogMsg('Session started');\n * // → \"[TraceLog] Session started\"\n *\n * // With Error object (development)\n * formatLogMsg('Failed to init', new Error('Network timeout'));\n * // → \"[TraceLog] Failed to init: Network timeout\"\n *\n * // With Error object (production - sanitized)\n * formatLogMsg('Failed to init', new Error('Network timeout at handleRequest (app.ts:42:10)'));\n * // → \"[TraceLog] Failed to init: Network timeout\"\n *\n * // With string error\n * formatLogMsg('Config invalid', 'Missing API key');\n * // → \"[TraceLog] Config invalid: Missing API key\"\n * ```\n */\nexport const formatLogMsg = (msg: string, error?: unknown): string => {\n if (error) {\n if (process.env.NODE_ENV !== 'development' && error instanceof Error) {\n const sanitizedMessage = error.message.replace(/\\s+at\\s+.*$/gm, '').replace(/\\s*\\([^()]+:\\d+:\\d+\\)/g, '');\n return `[TraceLog] ${msg}: ${sanitizedMessage}`;\n }\n\n if (error instanceof Error) {\n return `[TraceLog] ${msg}: ${error.message}`;\n }\n\n if (typeof error === 'string') {\n return `[TraceLog] ${msg}: ${error}`;\n }\n\n if (typeof error === 'object') {\n try {\n return `[TraceLog] ${msg}: ${JSON.stringify(error)}`;\n } catch {\n return `[TraceLog] ${msg}: [Unable to serialize error]`;\n }\n }\n\n return `[TraceLog] ${msg}: ${String(error)}`;\n }\n\n return `[TraceLog] ${msg}`;\n};\n\n/**\n * Check if QA mode is active by reading sessionStorage directly\n *\n * NOTE: Intentional duplication of mode.utils.ts:isQaModeActive() to avoid\n * circular dependency (mode.utils.ts imports log() from this file).\n * Changes to QA mode detection logic must be synchronized in both files.\n *\n * @see src/utils/browser/mode.utils.ts - Canonical public implementation\n */\nconst isQaModeActive = (): boolean => {\n if (typeof window === 'undefined' || typeof sessionStorage === 'undefined') {\n return false;\n }\n try {\n return sessionStorage.getItem(QA_MODE_KEY) === 'true';\n } catch {\n return false;\n }\n};\n\n/**\n * Safe logging utility that enforces zero logs in production\n *\n * @param type - Log level (info, warn, error, debug)\n * @param msg - Message to log\n * @param extra - Optional extra data\n * @param extra.error - Error object to include in the log message\n * @param extra.data - Additional data object to log (will be sanitized in production)\n * @param extra.showToClient - If true, log will be shown in production (QA mode only) - DEPRECATED: use visibility: 'qa'\n * @param extra.style - CSS styles to apply to the console message (browser only, uses %c formatting)\n * @param extra.visibility - Controls when log is visible:\n * - 'critical': Always visible (production included) - for monitoring/Sentry\n * - 'qa': Only visible when QA mode is active\n * - undefined: Only visible in NODE_ENV=development\n *\n * Visibility hierarchy (production):\n * - CRITICAL: Always shown - errors that must reach monitoring platforms\n * - QA: Shown with ?tlog_mode=qa - custom event verification\n * - Default: Never shown in production\n *\n * Development behavior (NODE_ENV=development):\n * - All logs visible regardless of visibility level\n * - Full error messages and stack traces preserved\n */\nexport const log = (\n type: 'info' | 'warn' | 'error' | 'debug',\n msg: string,\n extra?: {\n error?: unknown;\n data?: Record<string, unknown>;\n showToClient?: boolean;\n style?: string;\n visibility?: LogVisibility;\n },\n): void => {\n const { error, data, showToClient = false, style, visibility } = extra ?? {};\n const formattedMsg = error ? formatLogMsg(msg, error) : `[TraceLog] ${msg}`;\n const method = type === 'error' ? 'error' : type === 'warn' ? 'warn' : 'log';\n const isProduction = process.env.NODE_ENV !== 'development';\n\n // Development: All logs visible\n if (!isProduction) {\n outputLog(method, formattedMsg, style, data);\n return;\n }\n\n // Production: Check visibility level\n const shouldShow = shouldShowLog(visibility, showToClient);\n\n if (!shouldShow) {\n return;\n }\n\n // Apply appropriate style for visibility level\n const effectiveStyle = getEffectiveStyle(visibility, style);\n const sanitizedData = data !== undefined ? sanitizeLogData(data) : undefined;\n\n outputLog(method, formattedMsg, effectiveStyle, sanitizedData);\n};\n\n/**\n * Determines if a log should be shown based on visibility level\n */\nconst shouldShowLog = (visibility: LogVisibility | undefined, showToClient: boolean): boolean => {\n // Critical logs are always shown\n if (visibility === 'critical') {\n return true;\n }\n\n // QA mode logs (including legacy showToClient)\n if (visibility === 'qa' || showToClient) {\n return isQaModeActive();\n }\n\n // Default: not shown in production\n return false;\n};\n\n/**\n * Gets the appropriate style for the visibility level\n */\nconst getEffectiveStyle = (visibility: LogVisibility | undefined, providedStyle: string | undefined): string => {\n if (providedStyle !== undefined && providedStyle !== '') {\n return providedStyle;\n }\n\n if (visibility === 'critical') {\n return LOG_STYLE_CRITICAL;\n }\n\n return '';\n};\n\n/**\n * Outputs the log message to console\n */\nconst outputLog = (\n method: 'log' | 'warn' | 'error',\n formattedMsg: string,\n style: string | undefined,\n data: Record<string, unknown> | undefined,\n): void => {\n const hasStyle = style !== undefined && style !== '';\n const styledMsg = hasStyle ? `%c${formattedMsg}` : formattedMsg;\n\n if (data !== undefined) {\n if (hasStyle) {\n console[method](styledMsg, style, data);\n } else {\n console[method](styledMsg, data);\n }\n } else {\n if (hasStyle) {\n console[method](styledMsg, style);\n } else {\n console[method](styledMsg);\n }\n }\n};\n\n/**\n * Sanitizes log data in production to prevent sensitive information leakage\n *\n * **Purpose**: Recursively redacts sensitive keys while preserving object expandability\n * in browser console for debugging.\n *\n * **Sensitive Keys Detected** (case-insensitive substring matching):\n * - `token`, `password`, `secret`, `key`, `apikey`, `api_key`, `sessionid`, `session_id`\n *\n * **Behavior**:\n * - **Redaction**: Replaces values with `'[REDACTED]'` string\n * - **Deep Cloning**: Creates new objects to avoid mutating originals\n * - **Recursive**: Handles nested objects and arrays\n * - **Preserves Structure**: Maintains object hierarchy for console inspection\n *\n * **Use Cases**:\n * - Production logging where sensitive data might be present\n * - Debugging with potentially sensitive configuration objects\n * - Error logging with user or session data\n *\n * @param data - Object to sanitize\n * @returns New object with sensitive keys redacted\n *\n * @example\n * ```typescript\n * const data = {\n * userId: '123',\n * apiKey: 'secret-key-123',\n * config: {\n * endpoint: 'https://api.com',\n * token: 'bearer-xyz'\n * }\n * };\n *\n * const sanitized = sanitizeLogData(data);\n * // {\n * // userId: '123',\n * // apiKey: '[REDACTED]',\n * // config: {\n * // endpoint: 'https://api.com',\n * // token: '[REDACTED]'\n * // }\n * // }\n * ```\n */\nconst sanitizeLogData = (data: Record<string, unknown>): Record<string, unknown> => {\n const sanitized: Record<string, unknown> = {};\n const sensitiveKeys = ['token', 'password', 'secret', 'key', 'apikey', 'api_key', 'sessionid', 'session_id'];\n\n for (const [key, value] of Object.entries(data)) {\n const lowerKey = key.toLowerCase();\n\n if (sensitiveKeys.some((sensitiveKey) => lowerKey.includes(sensitiveKey))) {\n sanitized[key] = '[REDACTED]';\n continue;\n }\n\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n sanitized[key] = sanitizeLogData(value as Record<string, unknown>);\n } else if (Array.isArray(value)) {\n sanitized[key] = value.map((item) =>\n item !== null && typeof item === 'object' && !Array.isArray(item)\n ? sanitizeLogData(item as Record<string, unknown>)\n : item,\n );\n } else {\n sanitized[key] = value;\n }\n }\n\n return sanitized;\n};\n","import { DeviceInfo, DeviceType } from '../../types/device.types';\nimport { log } from '../logging.utils';\n\nlet coarsePointerQuery: MediaQueryList | undefined;\nlet noHoverQuery: MediaQueryList | undefined;\n\nconst initMediaQueries = (): void => {\n if (typeof window !== 'undefined' && !coarsePointerQuery) {\n coarsePointerQuery = window.matchMedia('(pointer: coarse)');\n noHoverQuery = window.matchMedia('(hover: none)');\n }\n};\n\ninterface UserAgentBrand {\n brand: string;\n version: string;\n}\n\ninterface NavigatorWithUserAgentData extends Navigator {\n userAgentData?: {\n mobile: boolean;\n platform?: string;\n brands?: UserAgentBrand[];\n };\n}\n\nconst UNKNOWN = 'Unknown';\n\n/**\n * Detects OS name from navigator.userAgentData (modern) or userAgent string (fallback)\n */\nconst detectOS = (nav: NavigatorWithUserAgentData): string => {\n // Modern API (Chromium 90+)\n const platform = nav.userAgentData?.platform;\n if (platform != null && platform !== '') {\n if (/windows/i.test(platform)) return 'Windows';\n if (/macos/i.test(platform)) return 'macOS';\n if (/android/i.test(platform)) return 'Android';\n if (/linux/i.test(platform)) return 'Linux';\n if (/chromeos/i.test(platform)) return 'ChromeOS';\n if (/ios/i.test(platform)) return 'iOS';\n }\n\n // Fallback: Parse userAgent string\n const ua = navigator.userAgent;\n if (/Windows/i.test(ua)) return 'Windows';\n if (/iPhone|iPad|iPod/i.test(ua)) return 'iOS';\n if (/Mac OS X|Macintosh/i.test(ua)) return 'macOS';\n if (/Android/i.test(ua)) return 'Android';\n if (/CrOS/i.test(ua)) return 'ChromeOS';\n if (/Linux/i.test(ua)) return 'Linux';\n\n return UNKNOWN;\n};\n\n/**\n * Detects browser name from navigator.userAgentData.brands (modern) or userAgent string (fallback)\n */\nconst detectBrowser = (nav: NavigatorWithUserAgentData): string => {\n // Modern API (Chromium 90+)\n const brands = nav.userAgentData?.brands;\n if (brands != null && brands.length > 0) {\n // Filter out generic brands and find the actual browser\n const validBrands = brands.filter((b) => !/not.?a.?brand|chromium/i.test(b.brand));\n const firstBrand = validBrands[0];\n if (firstBrand != null) {\n const brand = firstBrand.brand;\n // Normalize brand names\n if (/google chrome/i.test(brand)) return 'Chrome';\n if (/microsoft edge/i.test(brand)) return 'Edge';\n if (/opera/i.test(brand)) return 'Opera';\n return brand;\n }\n }\n\n // Fallback: Parse userAgent string (order matters!)\n const ua = navigator.userAgent;\n if (/Edg\\//i.test(ua)) return 'Edge';\n if (/OPR\\//i.test(ua)) return 'Opera';\n if (/Chrome/i.test(ua)) return 'Chrome';\n if (/Firefox/i.test(ua)) return 'Firefox';\n if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) return 'Safari';\n\n return UNKNOWN;\n};\n\n/**\n * Detects the device type based on screen size, user agent, and browser capabilities\n * @returns The detected device type\n */\nexport const getDeviceType = (): DeviceType => {\n try {\n const nav = navigator as NavigatorWithUserAgentData;\n\n if (nav.userAgentData != null && typeof nav.userAgentData.mobile === 'boolean') {\n const uaPlatform = nav.userAgentData.platform;\n if (uaPlatform != null && uaPlatform !== '' && /ipad|tablet/i.test(uaPlatform)) {\n return DeviceType.Tablet;\n }\n\n const result = nav.userAgentData.mobile ? DeviceType.Mobile : DeviceType.Desktop;\n return result;\n }\n\n initMediaQueries();\n\n const width = window.innerWidth;\n const hasCoarsePointer = coarsePointerQuery?.matches ?? false;\n const hasNoHover = noHoverQuery?.matches ?? false;\n const hasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n const ua = navigator.userAgent.toLowerCase();\n const isMobileUA = /mobile|android|iphone|ipod|blackberry|iemobile|opera mini/.test(ua);\n const isTabletUA = /tablet|ipad|android(?!.*mobile)/.test(ua);\n\n if (width <= 767 || (isMobileUA && hasTouchSupport)) {\n return DeviceType.Mobile;\n }\n\n if ((width >= 768 && width <= 1024) || isTabletUA || (hasCoarsePointer && hasNoHover && hasTouchSupport)) {\n return DeviceType.Tablet;\n }\n\n return DeviceType.Desktop;\n } catch (error) {\n log('debug', 'Device detection failed, defaulting to desktop', { error });\n\n return DeviceType.Desktop;\n }\n};\n\n/**\n * Detects comprehensive device information including type, OS, and browser.\n *\n * Uses navigator.userAgentData (modern Chromium 90+) with userAgent string fallback.\n *\n * @returns DeviceInfo object with type, os, and browser fields\n */\nexport const getDeviceInfo = (): DeviceInfo => {\n try {\n const nav = navigator as NavigatorWithUserAgentData;\n\n return {\n type: getDeviceType(),\n os: detectOS(nav),\n browser: detectBrowser(nav),\n };\n } catch (error) {\n log('debug', 'Device info detection failed, using defaults', { error });\n\n return {\n type: DeviceType.Desktop,\n os: UNKNOWN,\n browser: UNKNOWN,\n };\n }\n};\n","/**\n * Performance monitoring and web vitals constants for TraceLog\n * Centralizes thresholds and configuration for performance tracking\n */\n\nimport { WebVitalType } from '../types';\nimport type { WebVitalsMode } from '../types/config.types';\n\n// ============================================================================\n// WEB VITALS THRESHOLDS\n// ============================================================================\n\n/**\n * Web Vitals \"good\" thresholds (75th percentile boundaries)\n * Reference: https://web.dev/articles/vitals\n */\nexport const WEB_VITALS_GOOD_THRESHOLDS: Record<WebVitalType, number> = {\n LCP: 2500,\n FCP: 1800,\n CLS: 0.1,\n INP: 200,\n TTFB: 800,\n} as const;\n\n/**\n * Web Vitals \"needs improvement\" thresholds\n */\nexport const WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS: Record<WebVitalType, number> = {\n LCP: 2500,\n FCP: 1800,\n CLS: 0.1,\n INP: 200,\n TTFB: 800,\n} as const;\n\n/**\n * Web Vitals \"poor\" thresholds\n */\nexport const WEB_VITALS_POOR_THRESHOLDS: Record<WebVitalType, number> = {\n LCP: 4000,\n FCP: 3000,\n CLS: 0.25,\n INP: 500,\n TTFB: 1800,\n} as const;\n\n/**\n * Default Web Vitals mode\n * 'needs-improvement' provides balanced approach - captures metrics that need attention\n * while filtering out good performance (reduces noise and costs)\n */\nexport const DEFAULT_WEB_VITALS_MODE: WebVitalsMode = 'needs-improvement';\n\n/**\n * Get Web Vitals thresholds for the specified mode\n */\nexport const getWebVitalsThresholds = (mode: WebVitalsMode = DEFAULT_WEB_VITALS_MODE): Record<WebVitalType, number> => {\n switch (mode) {\n case 'all':\n return { LCP: 0, FCP: 0, CLS: 0, INP: 0, TTFB: 0 };\n case 'needs-improvement':\n return WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS;\n case 'poor':\n return WEB_VITALS_POOR_THRESHOLDS;\n default:\n return WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS;\n }\n};\n\n// ============================================================================\n// PERFORMANCE MONITORING LIMITS\n// ============================================================================\n\n/**\n * Maximum number of navigation history entries to keep in memory\n * Prevents unbounded growth of reportedByNav Map in long-running SPAs\n * Uses FIFO eviction when limit is exceeded\n */\nexport const MAX_NAVIGATION_HISTORY = 50;\n\n/**\n * Precision for performance metric values\n * All performance metrics are rounded to 2 decimal places\n */\nexport const PERFORMANCE_PRECISION_DECIMALS = 2 as const;\n","{\n \"name\": \"@tracelog/lib\",\n \"description\": \"JavaScript library for web analytics and real-time event tracking\",\n \"license\": \"MIT\",\n \"version\": \"2.10.0\",\n \"main\": \"./dist/public-api.cjs\",\n \"module\": \"./dist/public-api.js\",\n \"types\": \"./dist/public-api.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"exports\": {\n \".\": {\n \"types\": \"./dist/public-api.d.ts\",\n \"import\": \"./dist/public-api.js\",\n \"require\": \"./dist/public-api.cjs\"\n },\n \"./pixel\": {\n \"types\": \"./dist/pixel/index.d.ts\",\n \"import\": \"./dist/pixel/index.js\",\n \"require\": \"./dist/pixel/index.cjs\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/nacorga/tracelog-lib.git\"\n },\n \"homepage\": \"https://github.com/nacorga/tracelog-lib#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/nacorga/tracelog-lib/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:browser\": \"NODE_ENV=production vite build\",\n \"build:browser:dev\": \"NODE_ENV=development vite build\",\n \"build:pixel\": \"NODE_ENV=production vite build -c vite.pixel.config.mjs\",\n \"build:all\": \"npm run build && npm run build:browser && npm run build:pixel\",\n \"type-check\": \"npx tsc --noEmit\",\n \"type-check:watch\": \"npx tsc --noEmit --watch\",\n \"lint\": \"npx eslint \\\"src/**/*.ts\\\" \\\"tests/**/*.ts\\\"\",\n \"lint:fix\": \"npx eslint \\\"src/**/*.ts\\\" \\\"tests/**/*.ts\\\" --fix\",\n \"format\": \"prettier --write \\\"src/**/*.ts\\\" \\\"tests/**/*{.ts,.js,.html,.css,.json,.md}\\\" \\\"scripts/**/*.js\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.ts\\\" \\\"tests/**/*{.ts,.js,.html,.css,.json,.md}\\\" \\\"scripts/**/*.js\\\"\",\n \"check\": \"npm run lint && npm run format:check\",\n \"fix\": \"npm run lint:fix && npm run format\",\n \"test\": \"npm run test:unit && npm run test:integration\",\n \"test:unit\": \"vitest run\",\n \"test:unit:ci\": \"NODE_OPTIONS=\\\"--max-old-space-size=4096 --no-experimental-fetch\\\" vitest run --pool=threads\",\n \"test:unit:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.mjs\",\n \"serve\": \"http-server docs -p 3000 --cors\",\n \"docs:setup\": \"npm run build:browser:dev && cp dist/browser/tracelog.esm.js docs/tracelog.js\",\n \"docs:dev\": \"npm run docs:setup && npm run serve\",\n \"docs:gh-pages\": \"npm run build:browser && cp dist/browser/tracelog.esm.js docs/tracelog.js\",\n \"test:e2e\": \"npm run docs:setup && NODE_ENV=development playwright test\",\n \"test:e2e:ci\": \"npm run docs:setup && NODE_ENV=development playwright test\",\n \"test:critical:ingestion\": \"npm run test:unit -- sender-manager.test.ts session-manager.test.ts\",\n \"ci:build\": \"npm run build:all\",\n \"prepare\": \"husky\",\n \"release\": \"node scripts/release.js\",\n \"release:patch\": \"node scripts/release.js --force-version $(node -p \\\"const v=require('./package.json').version.split('.').map(Number); v[2]++; v.join('.')\\\")\",\n \"release:minor\": \"node scripts/release.js --force-version $(node -p \\\"const v=require('./package.json').version.split('.').map(Number); v[1]++; v[2]=0; v.join('.')\\\")\",\n \"release:major\": \"node scripts/release.js --force-version $(node -p \\\"const v=require('./package.json').version.split('.').map(Number); v[0]++; v[1]=0; v[2]=0; v.join('.')\\\")\",\n \"release:dry-run\": \"node scripts/release.js --dry-run\",\n \"changelog:generate\": \"node scripts/generate-changelog.js --full\",\n \"changelog:preview\": \"node scripts/generate-changelog.js --dry-run\"\n },\n \"dependencies\": {\n \"web-vitals\": \"4.2.4\"\n },\n \"devDependencies\": {\n \"@commitlint/config-conventional\": \"^19.8.1\",\n \"@eslint/js\": \"^9.30.1\",\n \"@playwright/test\": \"^1.54.0\",\n \"@types/jest\": \"^30.0.0\",\n \"@types/node\": \"^24.5.2\",\n \"@typescript-eslint/eslint-plugin\": \"^8.36.0\",\n \"@typescript-eslint/parser\": \"^8.36.0\",\n \"@vitest/coverage-v8\": \"^3.2.4\",\n \"commitlint\": \"^19.8.1\",\n \"cross-env\": \"^10.0.0\",\n \"eslint\": \"^8.57.1\",\n \"eslint-config-prettier\": \"^10.1.5\",\n \"eslint-plugin-prettier\": \"^5.5.1\",\n \"globals\": \"^16.3.0\",\n \"http-server\": \"^14.1.1\",\n \"husky\": \"^9.1.6\",\n \"jsdom\": \"^27.0.0\",\n \"lint-staged\": \"^15.2.10\",\n \"prettier\": \"^3.4.2\",\n \"terser\": \"^5.44.0\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.7.3\",\n \"typescript-eslint\": \"^8.36.0\",\n \"uglify-js\": \"^3.19.3\",\n \"vite\": \"^7.0.4\",\n \"vitest\": \"^3.2.4\"\n }\n}\n","import { version } from '../../package.json';\n\nexport const LIB_VERSION = version;\n","import {\n QA_MODE_KEY,\n QA_MODE_URL_PARAM,\n QA_MODE_ENABLE_VALUE,\n QA_MODE_DISABLE_VALUE,\n LOG_STYLE_ACTIVE,\n LOG_STYLE_DISABLED,\n} from '../../constants';\nimport { log } from '../logging.utils';\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\n/**\n * Check if browser environment is available\n */\nconst isBrowserEnvironment = (): boolean => {\n return typeof window !== 'undefined' && typeof sessionStorage !== 'undefined';\n};\n\n/**\n * Clean URL parameter from the current URL\n */\nconst cleanUrlParameter = (): void => {\n try {\n const params = new URLSearchParams(window.location.search);\n params.delete(QA_MODE_URL_PARAM);\n\n const search = params.toString();\n const url = window.location.pathname + (search ? '?' + search : '') + window.location.hash;\n\n window.history.replaceState({}, '', url);\n } catch {\n // Continue without cleaning URL\n }\n};\n\n// ============================================================================\n// QA Mode Public API\n// ============================================================================\n\n/**\n * Detects QA mode from URL parameter or sessionStorage.\n *\n * QA mode shows custom event logs to help verify tracking implementation.\n *\n * Activation:\n * - URL: `?tlog_mode=qa` to enable, `?tlog_mode=qa_off` to disable.\n *\n * @returns True if QA mode is active, false otherwise\n */\nexport const detectQaMode = (): boolean => {\n if (!isBrowserEnvironment()) {\n return false;\n }\n\n try {\n const params = new URLSearchParams(window.location.search);\n const urlParam = params.get(QA_MODE_URL_PARAM);\n const storedState = sessionStorage.getItem(QA_MODE_KEY);\n\n let newState: boolean | null = null;\n\n if (urlParam === QA_MODE_ENABLE_VALUE) {\n newState = true;\n sessionStorage.setItem(QA_MODE_KEY, 'true');\n\n log('info', 'QA Mode ACTIVE', {\n visibility: 'qa',\n style: LOG_STYLE_ACTIVE,\n });\n } else if (urlParam === QA_MODE_DISABLE_VALUE) {\n newState = false;\n sessionStorage.setItem(QA_MODE_KEY, 'false');\n\n log('info', 'QA Mode DISABLED', {\n visibility: 'qa',\n style: LOG_STYLE_DISABLED,\n });\n }\n\n if (urlParam === QA_MODE_ENABLE_VALUE || urlParam === QA_MODE_DISABLE_VALUE) {\n cleanUrlParameter();\n }\n\n return newState ?? storedState === 'true';\n } catch {\n return false;\n }\n};\n","import { log } from '../logging.utils';\n\n/**\n * List of compound TLDs that require special handling for root domain extraction.\n * Keep in sync with tracelog-api/src/utils/common/utils.ts\n */\nconst COMPOUND_TLDS = [\n 'co.uk',\n 'org.uk',\n 'com.au',\n 'net.au',\n 'com.br',\n 'co.nz',\n 'co.jp',\n 'com.mx',\n 'co.in',\n 'com.cn',\n 'co.za',\n];\n\n/**\n * Extracts the root (registrable) domain from a hostname.\n * Handles both standard TLDs (.com, .org) and compound TLDs (.co.uk, .com.br).\n *\n * @example\n * getRootDomain('www.example.com') // 'example.com'\n * getRootDomain('app.blog.example.com') // 'example.com'\n * getRootDomain('shop.example.co.uk') // 'example.co.uk'\n */\nconst getRootDomain = (hostname: string): string => {\n const parts = hostname.toLowerCase().split('.');\n if (parts.length <= 2) {\n return hostname.toLowerCase();\n }\n const lastTwo = parts.slice(-2).join('.');\n if (COMPOUND_TLDS.includes(lastTwo)) {\n return parts.slice(-3).join('.');\n }\n return parts.slice(-2).join('.');\n};\n\n/**\n * Checks if two hostnames belong to the same domain (including subdomains).\n * Extracts root domain and compares to handle cross-subdomain navigation.\n *\n * @example\n * isSameDomain('www.example.com', 'example.com') // true\n * isSameDomain('app.example.com', 'www.example.com') // true\n * isSameDomain('example.co.uk', 'app.example.co.uk') // true\n *\n * @param hostname1 - First hostname (e.g., 'www.example.com')\n * @param hostname2 - Second hostname (e.g., 'app.example.com')\n * @returns true if same root domain\n */\nconst isSameDomain = (hostname1: string, hostname2: string): boolean => {\n if (hostname1 === hostname2) {\n return true;\n }\n return getRootDomain(hostname1) === getRootDomain(hostname2);\n};\n\n/**\n * Returns the referrer if it's external, or 'Direct' if internal/empty.\n *\n * **Purpose**: Filter out internal referrers (same domain) to ensure\n * accurate traffic source attribution. Internal referrers occur when:\n * - Session expires and user navigates within the same site\n * - User opens new tab from an internal link\n * - Page refresh after session timeout\n *\n * **Logic**:\n * - Empty referrer → 'Direct'\n * - Referrer from same domain or subdomain → 'Direct' (internal navigation)\n * - External referrer → Returns original referrer\n *\n * **Subdomain Detection**:\n * - `www.example.com` → `example.com` ✓ (internal)\n * - `blog.example.com` → `example.com` ✓ (internal)\n * - `example.com` → `www.example.com` ✓ (internal)\n *\n * @returns External referrer URL or 'Direct'\n */\nexport const getExternalReferrer = (): string => {\n const referrer = document.referrer;\n if (!referrer) {\n return 'Direct';\n }\n try {\n const referrerHostname = new URL(referrer).hostname.toLowerCase();\n const currentHostname = window.location.hostname.toLowerCase();\n if (isSameDomain(referrerHostname, currentHostname)) {\n return 'Direct';\n }\n return referrer;\n } catch (error) {\n log('debug', 'Failed to parse referrer URL, using raw value', { error, data: { referrer } });\n return referrer;\n }\n};\n","import { UTM_PARAMS } from '../../constants';\nimport { UTM } from '../../types/event.types';\n\n/**\n * Extracts UTM parameters from the current URL\n * @returns UTM parameters object or undefined if none found\n */\nexport const getUTMParameters = (): UTM | undefined => {\n const urlParams = new URLSearchParams(window.location.search);\n const utmParams: Partial<Record<keyof UTM, string>> = {};\n\n UTM_PARAMS.forEach((param) => {\n const value = urlParams.get(param);\n\n if (value) {\n const key = param.split('utm_')[1] as keyof UTM;\n utmParams[key] = value;\n }\n });\n\n const result = Object.keys(utmParams).length ? utmParams : undefined;\n\n return result;\n};\n","/**\n * Generates a RFC4122 compliant UUID v4 using native crypto API with fallback\n * @returns A UUID string\n */\nexport const generateUUID = (): string => {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n};\n\n/**\n * Sequence counter for generating unique event IDs within the same millisecond.\n * Resets when timestamp changes, preventing collisions in high-frequency event bursts.\n */\nlet eventSequence = 0;\nlet lastEventTimestamp = 0;\n\n/**\n * Generates a unique event ID optimized for high-frequency event tracking.\n *\n * **Collision Prevention Strategy:**\n * - Timestamp: Millisecond precision for temporal ordering\n * - Sequence: Auto-incrementing counter (0-999) for same-millisecond events\n * - Random: Cryptographically secure random (3 bytes) for cross-tab/process uniqueness\n *\n * **Format:** `{timestamp}-{sequence}-{random}`\n * **Example:** `1704067200000-001-a3f9c2`\n *\n * **Guarantees:**\n * - ✅ No collisions within same millisecond (sequence counter)\n * - ✅ No collisions across tabs (crypto random)\n * - ✅ Temporal ordering preserved (timestamp first)\n * - ✅ Works in high-frequency bursts (1000 events/ms capacity)\n * - ✅ Clock skew protection (monotonic timestamp guarantee)\n *\n * @returns Unique event ID string\n */\nexport const generateEventId = (): string => {\n let timestamp = Date.now();\n\n // Protect against clock skew (NTP sync, manual time adjustments, timezone changes)\n // If clock moves backward, use last valid timestamp to prevent ID collisions\n if (timestamp < lastEventTimestamp) {\n timestamp = lastEventTimestamp;\n }\n\n // Increment sequence counter for events in same millisecond\n if (timestamp === lastEventTimestamp) {\n eventSequence = (eventSequence + 1) % 1000; // Reset at 1000 to keep 3 digits\n } else {\n eventSequence = 0;\n }\n\n // Always update lastEventTimestamp to track current (possibly adjusted) timestamp\n lastEventTimestamp = timestamp;\n\n const sequence = eventSequence.toString().padStart(3, '0');\n\n // Cryptographically secure random (3 bytes = 6 hex chars)\n // Reduced from 4 bytes since sequence provides uniqueness within millisecond\n let random = '';\n try {\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const bytes = crypto.getRandomValues(new Uint8Array(3));\n if (bytes) {\n random = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n }\n }\n } catch {\n /* empty */\n }\n\n // Fallback to Math.random() if crypto unavailable\n if (!random) {\n random = Math.floor(Math.random() * 0xffffff)\n .toString(16)\n .padStart(6, '0');\n }\n\n return `${timestamp}-${sequence}-${random}`;\n};\n","import { Config } from '../../types';\nimport { DEFAULT_SENSITIVE_QUERY_PARAMS } from '../../constants';\nimport { log } from '../logging.utils';\n\n/**\n * Validates if a URL is valid HTTPS\n */\nconst isValidUrl = (url: string): boolean => {\n try {\n return new URL(url).protocol === 'https:';\n } catch {\n return false;\n }\n};\n\n/**\n * Generates a SaaS API URL based on the given project ID and the current browser domain.\n * @param projectId - The project ID to use as a subdomain.\n * @returns The generated SaaS API URL.\n */\nconst generateSaasApiUrl = (projectId: string): string => {\n try {\n const url = new URL(window.location.href);\n const host = url.hostname;\n\n if (!host || typeof host !== 'string') {\n throw new Error('Invalid hostname');\n }\n\n if (host === 'localhost' || host === '127.0.0.1' || /^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(host)) {\n throw new Error(\n 'SaaS integration requires a domain hostname; localhost and IP addresses are not supported. ' +\n 'For local development, omit `integrations.tracelog` to run in standalone mode (events emitted locally, ' +\n 'no network requests), or test against a staging domain that resolves to your dev machine via /etc/hosts.',\n );\n }\n\n const parts = host.split('.');\n\n if (!parts || !Array.isArray(parts) || parts.length === 0 || (parts.length === 1 && parts[0] === '')) {\n throw new Error('Invalid hostname structure');\n }\n\n if (parts.length === 1) {\n throw new Error('Single-part domain not supported for SaaS integration');\n }\n\n const cleanDomain = parts.length === 2 ? parts.join('.') : parts.slice(-2).join('.');\n\n if (!cleanDomain || cleanDomain.split('.').length < 2) {\n throw new Error('Invalid domain structure for SaaS');\n }\n\n const collectApiUrl = `https://${projectId}.${cleanDomain}/collect`;\n\n if (!isValidUrl(collectApiUrl)) {\n throw new Error('Generated URL failed validation');\n }\n\n return collectApiUrl;\n } catch (error) {\n throw new Error(`Invalid SaaS URL configuration: ${error instanceof Error ? error.message : String(error)}`);\n }\n};\n\n/**\n * Generates collection API URLs for the configured TraceLog SaaS integration\n * @param config - The TraceLog configuration\n * @returns Object containing the SaaS API URL (if configured)\n */\nexport const getCollectApiUrls = (config: Config): { saas?: string } => {\n const urls: { saas?: string } = {};\n\n if (config.integrations?.tracelog?.projectId) {\n urls.saas = generateSaasApiUrl(config.integrations.tracelog.projectId);\n }\n\n return urls;\n};\n\n/**\n * Normalizes a URL by removing sensitive query parameters\n * Combines default sensitive parameters with custom ones provided by user\n * @param url - The URL to normalize\n * @param sensitiveQueryParams - Array of parameter names to remove (merged with defaults)\n * @returns The normalized URL\n */\nexport const normalizeUrl = (url: string, sensitiveQueryParams: string[] = []): string => {\n if (!url || typeof url !== 'string') {\n log('warn', 'Invalid URL provided to normalizeUrl', { data: { type: typeof url } });\n return url || '';\n }\n\n try {\n const urlObject = new URL(url);\n const searchParams = urlObject.searchParams;\n\n const allSensitiveParams = [...new Set([...DEFAULT_SENSITIVE_QUERY_PARAMS, ...sensitiveQueryParams])];\n\n let hasChanged = false;\n const removedParams: string[] = [];\n\n allSensitiveParams.forEach((param) => {\n if (searchParams.has(param)) {\n searchParams.delete(param);\n hasChanged = true;\n removedParams.push(param);\n }\n });\n\n if (!hasChanged && url.includes('?')) {\n return url;\n }\n\n urlObject.search = searchParams.toString();\n return urlObject.toString();\n } catch (error) {\n log('warn', 'URL normalization failed, returning original', { error, data: { urlLength: url?.length } });\n return url;\n }\n};\n","import {\n MAX_ARRAY_LENGTH,\n MAX_OBJECT_DEPTH,\n MAX_STRING_LENGTH,\n MAX_NESTED_OBJECT_KEYS,\n XSS_PATTERNS,\n} from '../../constants';\nimport { MetadataType } from '../../types';\nimport { log } from '../logging.utils';\n\n/**\n * Sanitizes a string value to prevent XSS attacks\n * @param value - The string to sanitize\n * @returns The sanitized string\n */\nexport const sanitizeString = (value: string): string => {\n if (!value || typeof value !== 'string' || value.trim().length === 0) {\n return '';\n }\n\n let sanitized = value;\n\n if (value.length > MAX_STRING_LENGTH) {\n sanitized = value.slice(0, Math.max(0, MAX_STRING_LENGTH));\n }\n\n let xssPatternMatches = 0;\n for (const pattern of XSS_PATTERNS) {\n const beforeReplace = sanitized;\n sanitized = sanitized.replace(pattern, '');\n if (beforeReplace !== sanitized) {\n xssPatternMatches++;\n }\n }\n\n if (xssPatternMatches > 0) {\n log('warn', 'XSS patterns detected and removed', {\n data: {\n patternMatches: xssPatternMatches,\n valueLength: value.length,\n },\n });\n }\n\n const result = sanitized.trim();\n\n return result;\n};\n\n/**\n * Sanitizes any value recursively with depth protection\n * @param value - The value to sanitize\n * @param depth - Current recursion depth\n * @returns The sanitized value\n */\nconst sanitizeValue = (value: unknown, depth = 0): unknown => {\n if (value === null || value === undefined) {\n return null;\n }\n\n // Primitives are always allowed regardless of depth\n if (typeof value === 'string') {\n return sanitizeString(value);\n }\n\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < -Number.MAX_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) {\n return 0;\n }\n\n return value;\n }\n\n if (typeof value === 'boolean') {\n return value;\n }\n\n // Depth check only applies to complex types (arrays and objects)\n if (depth > MAX_OBJECT_DEPTH) {\n return null;\n }\n\n if (Array.isArray(value)) {\n const limitedArray = value.slice(0, MAX_ARRAY_LENGTH);\n const sanitizedArray = limitedArray.map((item) => sanitizeValue(item, depth + 1)).filter((item) => item !== null);\n\n return sanitizedArray;\n }\n\n if (typeof value === 'object') {\n const sanitizedObject: Record<string, unknown> = {};\n const entries = Object.entries(value);\n const limitedEntries = entries.slice(0, MAX_NESTED_OBJECT_KEYS);\n\n for (const [key, value_] of limitedEntries) {\n const sanitizedKey = sanitizeString(key);\n\n if (sanitizedKey) {\n const sanitizedValue = sanitizeValue(value_, depth + 1);\n\n if (sanitizedValue !== null) {\n sanitizedObject[sanitizedKey] = sanitizedValue;\n }\n }\n }\n\n return sanitizedObject;\n }\n\n return null;\n};\n\n/**\n * Sanitizes user metadata for custom events\n * @param metadata - The metadata to sanitize\n * @returns The sanitized metadata\n */\nexport const sanitizeMetadata = (metadata: unknown): Record<string, MetadataType> => {\n if (typeof metadata !== 'object' || metadata === null) {\n return {};\n }\n\n try {\n const sanitized = sanitizeValue(metadata);\n const result =\n typeof sanitized === 'object' && sanitized !== null ? (sanitized as Record<string, MetadataType>) : {};\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`[TraceLog] Metadata sanitization failed: ${errorMessage}`);\n }\n};\n","/**\n * Regular expressions used to detect and redact common PII patterns from\n * free-form text (click text, error messages, stack traces, etc.).\n *\n * Mirrors the patterns relied on by `ClickHandler` and `ErrorHandler`. Adding\n * a pattern here automatically widens coverage for both handlers.\n */\nexport const PII_PATTERNS = [\n // Email addresses.\n // Quantifiers are bounded (local part ≤64, each label ≤63, TLD ≤63 per RFC/DNS limits)\n // and the domain is matched as discrete dot-separated labels so the local-part and\n // domain classes never overlap. This keeps matching linear and prevents catastrophic\n // backtracking (ReDoS) on long, dot-heavy inputs that contain no real email.\n /\\b[A-Za-z0-9._%+-]{1,64}@(?:[A-Za-z0-9-]{1,63}\\.)+[A-Za-z]{2,63}\\b/gi,\n\n // US Phone numbers (various formats)\n /\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b/g,\n\n // Credit card numbers (16 digits with optional separators)\n /\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b/g,\n\n // IBAN (International Bank Account Number)\n /\\b[A-Z]{2}\\d{2}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b/gi,\n\n // API keys / tokens (sk_test_, sk_live_, pk_test_, pk_live_, …)\n /\\b[sp]k_(test|live)_[a-zA-Z0-9]{10,}\\b/gi,\n\n // Bearer tokens (JWT-like patterns — matches complete and partial tokens)\n /Bearer\\s+[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)?(?:\\.[A-Za-z0-9_-]+)?/gi,\n\n // Passwords in connection strings (protocol://user:password@host)\n /:\\/\\/[^:/]+:([^@]+)@/gi,\n\n // Sensitive URL query parameters (token=, password=, auth=, secret=, api_key=, …)\n /[?&](token|password|passwd|auth|secret|secret_key|private_key|auth_key|api_key|apikey|access_token)=[^&\\s]+/gi,\n] as const;\n\n/**\n * Replaces every match of {@link PII_PATTERNS} with `[REDACTED]`.\n *\n * The function does not mutate the input. Inputs that don't contain PII are\n * returned untouched (after going through `String.prototype.replace`, which is\n * a no-op when no match exists).\n */\nexport const sanitizePii = (text: string): string => {\n let sanitized = text;\n\n for (const pattern of PII_PATTERNS) {\n sanitized = sanitized.replace(pattern, '[REDACTED]');\n }\n\n return sanitized;\n};\n","import {\n MAX_SESSION_TIMEOUT_MS,\n MIN_SESSION_TIMEOUT_MS,\n DEFAULT_SESSION_TIMEOUT,\n DEFAULT_SAMPLING_RATE,\n VALIDATION_MESSAGES,\n DEFAULT_PAGE_VIEW_THROTTLE_MS,\n DEFAULT_CLICK_THROTTLE_MS,\n MAX_SAME_EVENT_PER_MINUTE,\n DEFAULT_ERROR_SAMPLING_RATE,\n MIN_SEND_INTERVAL_MS,\n MAX_SEND_INTERVAL_MS_CONFIG,\n EVENT_SENT_INTERVAL_MS,\n} from '../../constants';\nimport {\n Config,\n AppConfigValidationError,\n SessionTimeoutValidationError,\n SamplingRateValidationError,\n IntegrationValidationError,\n} from '../../types';\n\n/**\n * Validates the app configuration object (before normalization)\n */\nexport const validateAppConfig = (config?: Config): void => {\n if (config !== undefined && (config === null || typeof config !== 'object')) {\n throw new AppConfigValidationError('Configuration must be an object', 'config');\n }\n\n if (!config) {\n return;\n }\n\n if (config.sessionTimeout !== undefined) {\n if (\n typeof config.sessionTimeout !== 'number' ||\n config.sessionTimeout < MIN_SESSION_TIMEOUT_MS ||\n config.sessionTimeout > MAX_SESSION_TIMEOUT_MS\n ) {\n throw new SessionTimeoutValidationError(VALIDATION_MESSAGES.INVALID_SESSION_TIMEOUT, 'config');\n }\n }\n\n if (config.globalMetadata !== undefined) {\n if (typeof config.globalMetadata !== 'object' || config.globalMetadata === null) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_GLOBAL_METADATA, 'config');\n }\n }\n\n if (config.integrations) {\n validateIntegrations(config.integrations);\n }\n\n if (config.sensitiveQueryParams !== undefined) {\n if (!Array.isArray(config.sensitiveQueryParams)) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_SENSITIVE_QUERY_PARAMS, 'config');\n }\n\n for (const param of config.sensitiveQueryParams) {\n if (typeof param !== 'string') {\n throw new AppConfigValidationError('All sensitive query params must be strings', 'config');\n }\n }\n }\n\n if (config.errorSampling !== undefined) {\n if (typeof config.errorSampling !== 'number' || config.errorSampling < 0 || config.errorSampling > 1) {\n throw new SamplingRateValidationError(VALIDATION_MESSAGES.INVALID_ERROR_SAMPLING_RATE, 'config');\n }\n }\n\n if (config.samplingRate !== undefined) {\n if (typeof config.samplingRate !== 'number' || config.samplingRate < 0 || config.samplingRate > 1) {\n throw new SamplingRateValidationError(VALIDATION_MESSAGES.INVALID_SAMPLING_RATE, 'config');\n }\n }\n\n if (config.pageViewThrottleMs !== undefined) {\n if (typeof config.pageViewThrottleMs !== 'number' || config.pageViewThrottleMs < 0) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_PAGE_VIEW_THROTTLE, 'config');\n }\n }\n\n if (config.clickThrottleMs !== undefined) {\n if (typeof config.clickThrottleMs !== 'number' || config.clickThrottleMs < 0) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_CLICK_THROTTLE, 'config');\n }\n }\n\n if (config.maxSameEventPerMinute !== undefined) {\n if (typeof config.maxSameEventPerMinute !== 'number' || config.maxSameEventPerMinute <= 0) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_MAX_SAME_EVENT_PER_MINUTE, 'config');\n }\n }\n\n if (config.sendIntervalMs !== undefined) {\n if (\n !Number.isFinite(config.sendIntervalMs) ||\n config.sendIntervalMs < MIN_SEND_INTERVAL_MS ||\n config.sendIntervalMs > MAX_SEND_INTERVAL_MS_CONFIG\n ) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_SEND_INTERVAL, 'config');\n }\n }\n\n if (config.flushOnSpaNavigation !== undefined && typeof config.flushOnSpaNavigation !== 'boolean') {\n throw new AppConfigValidationError(\n `Invalid flushOnSpaNavigation type: ${typeof config.flushOnSpaNavigation}. Must be a boolean`,\n 'config',\n );\n }\n\n if (config.flushOnPageHidden !== undefined && typeof config.flushOnPageHidden !== 'boolean') {\n throw new AppConfigValidationError(\n `Invalid flushOnPageHidden type: ${typeof config.flushOnPageHidden}. Must be a boolean`,\n 'config',\n );\n }\n\n if (config.webVitalsMode !== undefined) {\n if (typeof config.webVitalsMode !== 'string') {\n throw new AppConfigValidationError(\n `Invalid webVitalsMode type: ${typeof config.webVitalsMode}. Must be a string`,\n 'config',\n );\n }\n\n const validModes = ['all', 'needs-improvement', 'poor'];\n if (!validModes.includes(config.webVitalsMode)) {\n throw new AppConfigValidationError(\n `Invalid webVitalsMode: \"${config.webVitalsMode}\". Must be one of: ${validModes.join(', ')}`,\n 'config',\n );\n }\n }\n\n if (config.webVitalsThresholds !== undefined) {\n if (\n typeof config.webVitalsThresholds !== 'object' ||\n config.webVitalsThresholds === null ||\n Array.isArray(config.webVitalsThresholds)\n ) {\n throw new AppConfigValidationError('webVitalsThresholds must be an object', 'config');\n }\n\n const validKeys = ['LCP', 'FCP', 'CLS', 'INP', 'TTFB'];\n for (const [key, value] of Object.entries(config.webVitalsThresholds)) {\n if (!validKeys.includes(key)) {\n throw new AppConfigValidationError(\n `Invalid Web Vitals threshold key: \"${key}\". Must be one of: ${validKeys.join(', ')}`,\n 'config',\n );\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n throw new AppConfigValidationError(\n `Invalid Web Vitals threshold value for ${key}: ${value}. Must be a non-negative finite number`,\n 'config',\n );\n }\n }\n }\n};\n\n/**\n * Validates integrations configuration\n */\nconst validateIntegrations = (integrations: Config['integrations']): void => {\n if (!integrations) {\n return;\n }\n\n if (integrations.tracelog) {\n if (\n !integrations.tracelog.projectId ||\n typeof integrations.tracelog.projectId !== 'string' ||\n integrations.tracelog.projectId.trim() === ''\n ) {\n throw new IntegrationValidationError(VALIDATION_MESSAGES.INVALID_TRACELOG_PROJECT_ID, 'config');\n }\n\n if (integrations.tracelog.shopify !== undefined && typeof integrations.tracelog.shopify !== 'boolean') {\n throw new IntegrationValidationError('tracelog.shopify must be a boolean', 'config');\n }\n }\n};\n\n/**\n * Validates and normalizes the app configuration\n */\nexport const validateAndNormalizeConfig = (config?: Config): Config => {\n validateAppConfig(config);\n\n const normalizedConfig: Config = {\n ...(config ?? {}),\n sessionTimeout: config?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT,\n globalMetadata: config?.globalMetadata ?? {},\n sensitiveQueryParams: config?.sensitiveQueryParams ?? [],\n errorSampling: config?.errorSampling ?? DEFAULT_ERROR_SAMPLING_RATE,\n samplingRate: config?.samplingRate ?? DEFAULT_SAMPLING_RATE,\n pageViewThrottleMs: config?.pageViewThrottleMs ?? DEFAULT_PAGE_VIEW_THROTTLE_MS,\n clickThrottleMs: config?.clickThrottleMs ?? DEFAULT_CLICK_THROTTLE_MS,\n maxSameEventPerMinute: config?.maxSameEventPerMinute ?? MAX_SAME_EVENT_PER_MINUTE,\n sendIntervalMs: config?.sendIntervalMs ?? EVENT_SENT_INTERVAL_MS,\n flushOnSpaNavigation: config?.flushOnSpaNavigation ?? false,\n flushOnPageHidden: config?.flushOnPageHidden ?? true,\n };\n\n return normalizedConfig;\n};\n","/**\n * Checks if a value is JSON-serializable (primitives, arrays, or plain objects).\n * Rejects functions, symbols, circular references, and other non-serializable types.\n * @param value - The value to check\n * @param seen - Set of visited objects to detect circular references\n * @returns True if the value is JSON-serializable\n */\nconst isSerializable = (value: unknown, seen: Set<unknown> = new Set()): boolean => {\n if (value === null || value === undefined) {\n return true;\n }\n\n const type = typeof value;\n\n if (type === 'string' || type === 'number' || type === 'boolean') {\n return true;\n }\n\n if (type === 'function' || type === 'symbol' || type === 'bigint') {\n return false;\n }\n\n // Circular reference check for complex types\n if (seen.has(value)) {\n return false;\n }\n seen.add(value);\n\n if (Array.isArray(value)) {\n return value.every((item) => isSerializable(item, seen));\n }\n\n if (type === 'object') {\n return Object.values(value as Record<string, unknown>).every((v) => isSerializable(v, seen));\n }\n\n return false;\n};\n\n/**\n * Checks if an object contains only JSON-serializable fields.\n * Accepts any depth of nesting as long as all values are serializable.\n * @param object - The object to check\n * @returns True if the object contains only serializable fields\n */\nexport const isOnlyPrimitiveFields = (object: Record<string, unknown>): boolean => {\n if (typeof object !== 'object' || object === null) {\n return false;\n }\n\n return isSerializable(object);\n};\n\n/**\n * Extracts a plain `Record<string, string>` from an untrusted traits value.\n *\n * Rejects arrays, nulls, and non-string values (TS types erased at runtime).\n * Used by `identify(userId, traits?)` to defend against consumers passing\n * `null`, deeply-nested objects, or non-string fields.\n *\n * @returns Sanitized traits, or `undefined` if the input has no string fields.\n */\nexport const sanitizeTraits = (traits: unknown): Record<string, string> | undefined => {\n if (typeof traits !== 'object' || traits === null || Array.isArray(traits)) return undefined;\n\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(traits as Record<string, unknown>)) {\n if (typeof value === 'string') filtered[key] = value;\n }\n\n return Object.keys(filtered).length > 0 ? filtered : undefined;\n};\n","import {\n MAX_CUSTOM_EVENT_ARRAY_SIZE,\n MAX_CUSTOM_EVENT_KEYS,\n MAX_CUSTOM_EVENT_NAME_LENGTH,\n MAX_CUSTOM_EVENT_STRING_SIZE,\n MAX_STRING_LENGTH,\n MAX_STRING_LENGTH_IN_ARRAY,\n} from '../../constants';\nimport { MetadataType } from '../../types';\nimport { sanitizeMetadata } from '../security/sanitize.utils';\nimport { isOnlyPrimitiveFields } from './type-guards.utils';\n\n/**\n * Validates an event name\n * @param eventName - The event name to validate\n * @returns Validation result with error message if invalid\n */\nexport const isValidEventName = (eventName: string): { valid: boolean; error?: string } => {\n if (typeof eventName !== 'string') {\n return {\n valid: false,\n error: 'Event name must be a string',\n };\n }\n\n if (eventName.length === 0) {\n return {\n valid: false,\n error: 'Event name cannot be empty',\n };\n }\n\n if (eventName.length > MAX_CUSTOM_EVENT_NAME_LENGTH) {\n return {\n valid: false,\n error: `Event name is too long (max ${MAX_CUSTOM_EVENT_NAME_LENGTH} characters)`,\n };\n }\n\n if (eventName.includes('<') || eventName.includes('>') || eventName.includes('&')) {\n return {\n valid: false,\n error: 'Event name contains invalid characters',\n };\n }\n\n const reservedWords = ['constructor', 'prototype', '__proto__', 'eval', 'function', 'var', 'let', 'const'];\n\n if (reservedWords.includes(eventName.toLowerCase())) {\n return {\n valid: false,\n error: 'Event name cannot be a reserved word',\n };\n }\n\n return { valid: true };\n};\n\n/**\n * Validates a single metadata object\n * @param eventName - The event name (for error messages)\n * @param metadata - The metadata object to validate\n * @param type - Type of metadata (globalMetadata or customEvent)\n * @returns Validation result with sanitized metadata if valid\n */\nconst validateSingleMetadata = (\n eventName: string,\n metadata: Record<string, unknown>,\n type?: 'globalMetadata' | 'customEvent',\n): { valid: boolean; error?: string; sanitizedMetadata?: Record<string, MetadataType> } => {\n const sanitizedMetadata = sanitizeMetadata(metadata);\n const intro =\n type && type === 'customEvent' ? `${type} \"${eventName}\" metadata error` : `${eventName} metadata error`;\n\n if (!isOnlyPrimitiveFields(sanitizedMetadata)) {\n return {\n valid: false,\n error: `${intro}: object has invalid types. Valid types are string, number, boolean or string arrays.`,\n };\n }\n\n let jsonString: string;\n\n try {\n jsonString = JSON.stringify(sanitizedMetadata);\n } catch {\n return {\n valid: false,\n error: `${intro}: object contains circular references or cannot be serialized.`,\n };\n }\n\n const byteSize = new TextEncoder().encode(jsonString).byteLength;\n\n if (byteSize > MAX_CUSTOM_EVENT_STRING_SIZE) {\n return {\n valid: false,\n error: `${intro}: object is too large (max ${MAX_CUSTOM_EVENT_STRING_SIZE / 1024} KB).`,\n };\n }\n\n const keyCount = Object.keys(sanitizedMetadata).length;\n\n if (keyCount > MAX_CUSTOM_EVENT_KEYS) {\n return {\n valid: false,\n error: `${intro}: object has too many keys (max ${MAX_CUSTOM_EVENT_KEYS} keys).`,\n };\n }\n\n for (const [key, value] of Object.entries(sanitizedMetadata)) {\n if (Array.isArray(value)) {\n if (value.length > MAX_CUSTOM_EVENT_ARRAY_SIZE) {\n return {\n valid: false,\n error: `${intro}: array property \"${key}\" is too large (max ${MAX_CUSTOM_EVENT_ARRAY_SIZE} items).`,\n };\n }\n\n for (const item of value) {\n if (typeof item === 'string' && item.length > MAX_STRING_LENGTH_IN_ARRAY) {\n return {\n valid: false,\n error: `${intro}: array property \"${key}\" contains strings that are too long (max ${MAX_STRING_LENGTH_IN_ARRAY} characters).`,\n };\n }\n }\n }\n\n if (typeof value === 'string' && value.length > MAX_STRING_LENGTH) {\n return {\n valid: false,\n error: `${intro}: property \"${key}\" is too long (max ${MAX_STRING_LENGTH} characters).`,\n };\n }\n }\n\n return {\n valid: true,\n sanitizedMetadata,\n };\n};\n\n/**\n * Validates metadata for events (supports both objects and arrays of objects)\n * @param eventName - The event name (for error messages)\n * @param metadata - The metadata to validate\n * @param type - Type of metadata (globalMetadata or customEvent)\n * @returns Validation result with sanitized metadata if valid\n */\nexport const isValidMetadata = (\n eventName: string,\n metadata: Record<string, unknown> | Record<string, unknown>[],\n type?: 'globalMetadata' | 'customEvent',\n): {\n valid: boolean;\n error?: string;\n sanitizedMetadata?: Record<string, MetadataType> | Record<string, MetadataType>[];\n} => {\n if (Array.isArray(metadata)) {\n const sanitizedArray: Record<string, MetadataType>[] = [];\n const intro =\n type && type === 'customEvent' ? `${type} \"${eventName}\" metadata error` : `${eventName} metadata error`;\n\n for (let i = 0; i < metadata.length; i++) {\n const item = metadata[i];\n\n if (typeof item !== 'object' || item === null || Array.isArray(item)) {\n return {\n valid: false,\n error: `${intro}: array item at index ${i} must be an object.`,\n };\n }\n\n const itemValidation = validateSingleMetadata(eventName, item, type);\n\n if (!itemValidation.valid) {\n return {\n valid: false,\n error: `${intro}: array item at index ${i} is invalid: ${itemValidation.error}`,\n };\n }\n\n if (itemValidation.sanitizedMetadata) {\n sanitizedArray.push(itemValidation.sanitizedMetadata);\n }\n }\n\n return {\n valid: true,\n sanitizedMetadata: sanitizedArray,\n };\n }\n\n return validateSingleMetadata(eventName, metadata, type);\n};\n","import { MetadataType } from '../../types';\nimport { log } from '../logging.utils';\nimport { isValidEventName, isValidMetadata } from './metadata-validations.utils';\n\n/**\n * Validates a complete event with name and optional metadata\n * @param eventName - The event name to validate\n * @param metadata - Optional metadata to validate\n * @returns Validation result with sanitized metadata if valid\n */\nexport const isEventValid = (\n eventName: string,\n metadata?: Record<string, unknown> | Record<string, unknown>[],\n): {\n valid: boolean;\n error?: string;\n sanitizedMetadata?: Record<string, MetadataType> | Record<string, MetadataType>[];\n} => {\n const nameValidation = isValidEventName(eventName);\n\n if (!nameValidation.valid) {\n log('error', 'Event name validation failed', {\n data: { eventName, error: nameValidation.error },\n });\n\n return nameValidation;\n }\n\n if (!metadata) {\n return { valid: true };\n }\n\n const metadataValidation = isValidMetadata(eventName, metadata, 'customEvent');\n\n if (!metadataValidation.valid) {\n log('error', 'Event metadata validation failed', {\n data: {\n eventName,\n error: metadataValidation.error,\n },\n });\n }\n\n return metadataValidation;\n};\n","import { EmitterCallback, EmitterMap } from '../types';\n\n/**\n * Type-safe event emitter for TraceLog internal events\n *\n * **Purpose**: Provides pub/sub mechanism for internal library events with full TypeScript\n * type safety through `EmitterMap` interface.\n *\n * **Supported Events** (defined in `EmitterMap`):\n * - `event`: Individual events as they are tracked (EventData)\n * - `queue`: Complete event batches before transmission (EventsQueue)\n *\n * **Note**: Developers are responsible for implementing consent logic\n * before calling `init()` and filtering events as needed.\n *\n * **Key Features**:\n * - **Type Safety**: Callbacks receive correctly typed data based on event name\n * - **Memory Management**: Listeners stored in Map for efficient lookup and cleanup\n * - **Synchronous**: All callbacks execute immediately when event is emitted\n * - **No Error Isolation**: Errors in callbacks propagate to caller (by design)\n *\n * **Use Cases**:\n * - External event consumption via `tracelog.on('event', callback)`\n * - Integration testing via `window.__traceLogBridge.on('event', callback)`\n * - Custom analytics integrations\n * - Real-time event monitoring\n *\n * @example\n * ```typescript\n * const emitter = new Emitter();\n *\n * // Subscribe to events\n * const callback = (event: EventData) => {\n * console.log('Event tracked:', event.type);\n * };\n * emitter.on('event', callback);\n *\n * // Emit event (type-safe)\n * emitter.emit('event', {\n * id: '123',\n * type: EventType.CLICK,\n * page_url: 'https://example.com',\n * timestamp: Date.now(),\n * click_data: { x: 100, y: 200, tag: 'button' }\n * });\n *\n * // Unsubscribe\n * emitter.off('event', callback);\n *\n * // Clear all listeners (destroy/cleanup)\n * emitter.removeAllListeners();\n * ```\n *\n * @see EmitterMap for event type definitions\n * @see src/api.ts for public on/off API\n */\nexport class Emitter {\n private readonly listeners: Map<string, EmitterCallback[]> = new Map();\n\n /**\n * Subscribes to an event channel\n *\n * **Behavior**:\n * - Creates event channel if it doesn't exist\n * - Appends callback to list of listeners for this event\n * - Same callback can be registered multiple times (will fire multiple times)\n *\n * **Type Safety**: Callback receives data type matching the event name\n *\n * @param event - Event name to subscribe to\n * @param callback - Function to call when event is emitted\n *\n * @example\n * ```typescript\n * emitter.on('event', (eventData) => {\n * // eventData is typed as EventData\n * console.log(eventData.type);\n * });\n * ```\n */\n on<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, []);\n }\n\n this.listeners.get(event)!.push(callback);\n }\n\n /**\n * Unsubscribes from an event channel\n *\n * **Behavior**:\n * - Removes first occurrence of callback from listener list\n * - If callback not found, no error is thrown\n * - If callback was registered multiple times, only one instance is removed\n *\n * **Important**: Must use same function reference passed to `on()`\n *\n * @param event - Event name to unsubscribe from\n * @param callback - Function reference to remove (must match `on()` reference)\n *\n * @example\n * ```typescript\n * const callback = (data) => console.log(data);\n * emitter.on('event', callback);\n * emitter.off('event', callback); // Unsubscribes successfully\n *\n * // BAD: Won't work (different function reference)\n * emitter.on('event', (data) => console.log(data));\n * emitter.off('event', (data) => console.log(data)); // No effect\n * ```\n */\n off<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n const callbacks = this.listeners.get(event);\n\n if (callbacks) {\n const index = callbacks.indexOf(callback);\n\n if (index > -1) {\n callbacks.splice(index, 1);\n }\n }\n }\n\n /**\n * Emits an event with data to all subscribed listeners\n *\n * **Behavior**:\n * - Calls all registered callbacks for this event synchronously\n * - Callbacks execute in registration order\n * - If no listeners, no-op (no error thrown)\n * - Errors in callbacks are NOT caught (propagate to caller)\n *\n * **Type Safety**: Data type must match event name's expected type\n *\n * @param event - Event name to emit\n * @param data - Event data (type must match EmitterMap[event])\n *\n * @example\n * ```typescript\n * // Emit event data\n * emitter.emit('event', eventData);\n *\n * // Emit queue data\n * emitter.emit('queue', {\n * user_id: 'user-123',\n * session_id: 'session-456',\n * device: DeviceType.Desktop,\n * events: [event1, event2]\n * });\n * ```\n */\n emit<K extends keyof EmitterMap>(event: K, data: EmitterMap[K]): void {\n const callbacks = this.listeners.get(event);\n\n if (callbacks) {\n callbacks.forEach((callback) => {\n callback(data);\n });\n }\n }\n\n /**\n * Removes all listeners for all events\n *\n * **Purpose**: Cleanup method called during `App.destroy()` to prevent memory leaks\n *\n * **Behavior**:\n * - Clears all event channels\n * - Listeners cannot be restored (new subscriptions required)\n * - Called automatically during library teardown\n *\n * **Use Cases**:\n * - Application teardown\n * - Component unmounting in SPA frameworks\n * - Test cleanup\n *\n * @example\n * ```typescript\n * // During destroy\n * emitter.removeAllListeners();\n * // All subscriptions cleared\n * ```\n */\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Error signature utilities.\n *\n * SOURCE: tracelog-api/src/lib/error-classification/error-fingerprint.service.ts\n *\n * The regex set below MUST stay byte-identical to the API's `normalizeErrorMessage`\n * so that client-side throttling and server-side dedup agree on what counts as the\n * \"same\" error. The lib's variant skips the SHA-256 step the API performs after\n * normalization: the throttle map key is the composite string itself, avoiding a\n * `crypto` import and shaving bytes from the browser bundle.\n *\n * If either side changes the regex set, update both AND adjust the unit test that\n * codifies the expected outputs against fixed inputs (`error-signature.utils.test.ts`).\n */\n\nconst URL_PATTERN = /https?:\\/\\/\\S+/g;\nconst UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;\nconst HEX_ADDR_PATTERN = /0x[0-9a-fA-F]{4,}/g;\nconst LONG_NUMBER_PATTERN = /(?<!\\d)\\d{4,}(?!\\d)/g;\nconst LONG_QUOTED_PATTERN = /(['\"])[^'\"]{20,}\\1/g;\n\nexport interface ErrorSignatureInput {\n message: string;\n filename?: string;\n line?: number | string;\n}\n\nexport function normalizeErrorMessage(message: string): string {\n return message\n .replace(URL_PATTERN, '[URL]')\n .replace(UUID_PATTERN, '[ID]')\n .replace(HEX_ADDR_PATTERN, '[ADDR]')\n .replace(LONG_NUMBER_PATTERN, '[N]')\n .replace(LONG_QUOTED_PATTERN, '$1[VAR]$1')\n .toLowerCase()\n .trim();\n}\n\nexport function buildErrorSignatureKey(input: ErrorSignatureInput): string {\n const message = normalizeErrorMessage(input.message);\n const rawFilename = (input.filename ?? '').trim();\n // Strip query string and hash fragment so UTM / campaign params don't split signatures.\n const cut = rawFilename.search(/[?#]/);\n const filename = cut === -1 ? rawFilename : rawFilename.slice(0, cut);\n const line = input.line == null ? '' : String(input.line);\n return `${message}|${filename}|${line}`;\n}\n","import { State } from '../types';\n\n/**\n * Global in-memory state store shared across all TraceLog components.\n * Single source of truth for runtime application state.\n */\nconst globalState: State = { config: {} } as State;\n\n/**\n * Read-only snapshot of the global state.\n *\n * @internal Exposed for tests and the dev-only TestBridge — production code\n * should use `StateManager.get()` / `getState()` instead.\n */\nexport const getGlobalState = (): Readonly<State> => globalState;\n\n/**\n * Clears every property on the global state.\n *\n * @internal Test isolation only — `tests/setup.ts` invokes this from `afterEach`\n * to guarantee a clean slate between test runs. Calling this in production\n * code breaks the running application.\n */\nexport const resetGlobalState = (): void => {\n Object.keys(globalState).forEach((key) => {\n delete globalState[key as keyof State];\n });\n globalState.config = {};\n};\n\n/**\n * Abstract base class providing centralized, type-safe state management.\n *\n * All managers/handlers extend this class. The global state is shared\n * across every subclass instance.\n *\n * @example\n * ```typescript\n * class MyManager extends StateManager {\n * initialize() {\n * const userId = this.get('userId');\n * this.set('mode', 'qa');\n * }\n * }\n * ```\n */\nexport abstract class StateManager {\n /**\n * Retrieves a value from global state.\n */\n protected get<T extends keyof State>(key: T): State[T] {\n return globalState[key];\n }\n\n /**\n * Sets a value in global state.\n */\n protected set<T extends keyof State>(key: T, value: State[T]): void {\n globalState[key] = value;\n }\n\n /**\n * Returns an immutable snapshot of the entire global state.\n */\n protected getState(): Readonly<State> {\n return { ...globalState };\n }\n}\n","import {\n QUEUE_KEY,\n RATE_LIMIT_KEY,\n EVENT_EXPIRY_HOURS,\n MAX_EVENT_AGE_MS_ON_RECOVERY,\n REQUEST_TIMEOUT_MS,\n PERMANENT_ERROR_LOG_THROTTLE_MS,\n MAX_BEACON_PAYLOAD_SIZE,\n PERSISTENCE_THROTTLE_MS,\n MAX_SEND_RETRIES,\n RETRY_BACKOFF_BASE_MS,\n RETRY_BACKOFF_JITTER_MS,\n LIB_VERSION,\n MAX_CONSECUTIVE_NETWORK_FAILURES,\n MAX_RECOVERY_FAILURES,\n CIRCUIT_BREAKER_COOLDOWN_MS,\n RATE_LIMIT_COOLDOWN_MS,\n MAX_RESPONSE_CODE_LENGTH,\n} from '../constants';\nimport {\n PersistedEventsQueue,\n EventsQueue,\n SpecialApiUrl,\n PermanentError,\n RateLimitError,\n TimeoutError,\n} from '../types';\nimport { log } from '../utils';\nimport { StorageManager } from './storage.manager';\nimport { StateManager } from './state.manager';\n\ninterface SendCallbacks {\n onSuccess?: (eventCount?: number, events?: any[], body?: EventsQueue) => void;\n onFailure?: () => void;\n}\n\n/**\n * Manages sending event queues to the TraceLog SaaS endpoint with persistence,\n * recovery, retry, circuit breaker, and 429 cooldown.\n *\n * **Storage Keys**:\n * - Queue: `tlog:{userId}:queue`\n * - Rate limit cooldown: `tlog:{userId}:rate_limit`\n *\n * **Multi-Tab Protection**: Persisted events include `lastPersistTime`; recovery\n * skips events persisted within 1 second (active tab may retry).\n */\nexport class SenderManager extends StateManager {\n private readonly storeManager: StorageManager;\n private readonly apiUrl: string;\n private lastPermanentErrorLog: { key: string; timestamp: number } | null = null;\n private recoveryInProgress = false;\n private lastMetadataTimestamp = 0;\n private readonly pendingControllers = new Set<AbortController>();\n /**\n * Counts consecutive fetch() rejections where no HTTP response was received\n * (DNS failure, connection refused). Resets on success. When this reaches\n * MAX_CONSECUTIVE_NETWORK_FAILURES the circuit opens and further send attempts\n * are skipped until CIRCUIT_BREAKER_COOLDOWN_MS elapses.\n */\n private consecutiveNetworkFailures = 0;\n private circuitOpenedAt = 0;\n /**\n * Timestamp (epoch ms) before which `send()` must skip fetch() calls due to a\n * prior 429 response. Mirrored to `localStorage` (keyed by userId) so the\n * cooldown survives page navigations on traditional server-rendered sites and\n * is discoverable by other tabs on the same origin.\n */\n private rateLimitedUntil = 0;\n /**\n * Storage key used when the current in-memory cooldown was armed. Captured at\n * arm time so identity changes mid-cooldown can't make persist/clear\n * operations target the wrong key.\n */\n private rateLimitStorageKeyAtArm: string | null = null;\n\n constructor(storeManager: StorageManager, apiUrl: string) {\n super();\n\n this.storeManager = storeManager;\n this.apiUrl = apiUrl;\n this.migrateLegacyV2Keys();\n this.rateLimitedUntil = this.loadRateLimitCooldown();\n }\n\n /**\n * Migrates v2 multi-integration localStorage keys to the v3 single-integration layout.\n *\n * V2 stored per-integration suffixes (`tlog:{userId}:queue:saas`,\n * `tlog:{userId}:queue:custom`, plus matching `:rate_limit:saas` / `:rate_limit:custom`).\n * V3 is SaaS-only, so the `:saas` queue is merged into the new unscoped key\n * `tlog:{userId}:queue` while preserving the original `timestamp` (so the\n * expiry check still applies) and bumping `recoveryFailures` if both keys\n * carry one. The `:custom` queue is intentionally discarded — its events were\n * destined for a different backend that no longer exists in v3; forwarding\n * them to SaaS would be data leakage. The legacy keys are removed in all\n * cases so the migration is one-shot per browser.\n */\n private migrateLegacyV2Keys(): void {\n const userId = this.get('userId') || 'anonymous';\n const legacySaasQueueKey = `${QUEUE_KEY(userId)}:saas`;\n const legacyCustomQueueKey = `${QUEUE_KEY(userId)}:custom`;\n const legacySaasRateLimitKey = `${RATE_LIMIT_KEY(userId)}:saas`;\n const legacyCustomRateLimitKey = `${RATE_LIMIT_KEY(userId)}:custom`;\n\n try {\n const legacyRaw = this.storeManager.getItem(legacySaasQueueKey);\n if (legacyRaw) {\n const targetKey = this.getQueueStorageKey();\n const currentRaw = this.storeManager.getItem(targetKey);\n\n if (!currentRaw) {\n this.storeManager.setItem(targetKey, legacyRaw);\n log('debug', 'Migrated v2 SaaS queue to v3 unscoped key');\n } else {\n this.mergeLegacyIntoCurrent(targetKey, legacyRaw, currentRaw);\n }\n\n this.storeManager.removeItem(legacySaasQueueKey);\n }\n } catch (error) {\n log('debug', 'Failed to migrate v2 SaaS queue, discarding legacy key', { error });\n try {\n this.storeManager.removeItem(legacySaasQueueKey);\n } catch {\n // Already best-effort; nothing more to do.\n }\n }\n\n [legacyCustomQueueKey, legacySaasRateLimitKey, legacyCustomRateLimitKey].forEach((key) => {\n try {\n if (this.storeManager.getItem(key) !== null) {\n this.storeManager.removeItem(key);\n }\n } catch {\n // Best-effort cleanup — leaving an orphan key is harmless.\n }\n });\n }\n\n private mergeLegacyIntoCurrent(targetKey: string, legacyRaw: string, currentRaw: string): void {\n try {\n const legacy = JSON.parse(legacyRaw) as PersistedEventsQueue;\n const current = JSON.parse(currentRaw) as PersistedEventsQueue;\n\n if (!Array.isArray(legacy?.events) || !Array.isArray(current?.events)) {\n log('debug', 'Legacy or current queue malformed, keeping current only');\n return;\n }\n\n const seen = new Set<string>(current.events.map((e) => e.id));\n const mergedEvents = [\n ...current.events,\n ...legacy.events.filter((e) => typeof e.id === 'string' && !seen.has(e.id)),\n ];\n\n const merged: PersistedEventsQueue = {\n ...current,\n events: mergedEvents,\n timestamp:\n typeof current.timestamp === 'number' && typeof legacy.timestamp === 'number'\n ? Math.min(current.timestamp, legacy.timestamp)\n : (current.timestamp ?? legacy.timestamp ?? Date.now()),\n recoveryFailures: Math.max(current.recoveryFailures ?? 0, legacy.recoveryFailures ?? 0) || undefined,\n };\n\n this.storeManager.setItem(targetKey, JSON.stringify(merged));\n log('debug', 'Merged v2 SaaS queue into existing v3 queue', {\n data: { added: mergedEvents.length - current.events.length, total: mergedEvents.length },\n });\n } catch (error) {\n log('debug', 'Failed to merge legacy queue, keeping current', { error });\n }\n }\n\n private getQueueStorageKey(): string {\n const userId = this.get('userId') || 'anonymous';\n return QUEUE_KEY(userId);\n }\n\n private getRateLimitStorageKey(): string {\n const userId = this.get('userId') || 'anonymous';\n return RATE_LIMIT_KEY(userId);\n }\n\n private getActiveRateLimitKey(): string {\n return this.rateLimitStorageKeyAtArm ?? this.getRateLimitStorageKey();\n }\n\n private armRateLimitCooldown(until: number): void {\n this.rateLimitedUntil = until;\n this.rateLimitStorageKeyAtArm = this.getRateLimitStorageKey();\n this.persistRateLimitCooldown(until);\n }\n\n private loadRateLimitCooldown(): number {\n const key = this.getRateLimitStorageKey();\n try {\n const raw = this.storeManager.getItem(key);\n if (!raw) return 0;\n const value = Number(raw);\n if (!Number.isFinite(value) || value <= Date.now()) {\n this.storeManager.removeItem(key);\n return 0;\n }\n this.rateLimitStorageKeyAtArm = key;\n return value;\n } catch {\n return 0;\n }\n }\n\n private persistRateLimitCooldown(until: number): void {\n const key = this.getActiveRateLimitKey();\n try {\n const raw = this.storeManager.getItem(key);\n if (raw) {\n const existing = Number(raw);\n if (Number.isFinite(existing) && existing >= until) {\n return;\n }\n }\n this.storeManager.setItem(key, String(until));\n } catch {\n // Storage full or disabled — cooldown still works in-memory for this instance\n }\n }\n\n private clearRateLimitCooldown(): void {\n const key = this.getActiveRateLimitKey();\n try {\n const raw = this.storeManager.getItem(key);\n if (raw) {\n const stored = Number(raw);\n if (Number.isFinite(stored) && stored > Date.now()) {\n this.rateLimitedUntil = stored;\n return;\n }\n }\n this.storeManager.removeItem(key);\n } catch {\n // Ignore — cleared in-memory is enough\n }\n this.rateLimitedUntil = 0;\n this.rateLimitStorageKeyAtArm = null;\n }\n\n private isRateLimited(): boolean {\n if (this.rateLimitedUntil === 0) {\n this.rateLimitedUntil = this.loadRateLimitCooldown();\n }\n if (this.rateLimitedUntil === 0) return false;\n if (Date.now() >= this.rateLimitedUntil) {\n this.clearRateLimitCooldown();\n if (this.rateLimitedUntil === 0) return false;\n }\n return true;\n }\n\n /**\n * Sends events synchronously using `navigator.sendBeacon()`.\n *\n * Falls back to localStorage persistence on rate-limit cooldown, beacon\n * rejection, or oversized payloads.\n */\n sendEventsQueueSync(body: EventsQueue): boolean {\n if (this.isRateLimited()) {\n log('debug', 'Rate-limit cooldown active, skipping sync send', {\n data: {\n cooldownRemainingMs: this.rateLimitedUntil - Date.now(),\n events: body.events.length,\n },\n });\n const stableBody = this.ensureBatchMetadata(body);\n const existing = this.getPersistedData();\n const existingFailures =\n typeof existing?.recoveryFailures === 'number' && Number.isFinite(existing.recoveryFailures)\n ? existing.recoveryFailures\n : 0;\n this.persistEventsWithFailureCount(stableBody, existingFailures, true);\n return false;\n }\n\n if (this.apiUrl.includes(SpecialApiUrl.Fail)) {\n log('warn', 'Fail mode: simulating network failure (sync)', { data: { events: body.events.length } });\n return false;\n }\n\n if (this.apiUrl.includes(SpecialApiUrl.Localhost)) {\n log('debug', 'Success mode: simulating successful send (sync)', { data: { events: body.events.length } });\n return true;\n }\n\n return this.sendQueueSyncInternal(body);\n }\n\n /**\n * Sends events asynchronously using `fetch()` with retry, circuit breaker, and 429 cooldown.\n * Persists on failure for recovery on next page load.\n */\n async sendEventsQueue(body: EventsQueue, callbacks?: SendCallbacks): Promise<boolean> {\n const stableBody = this.ensureBatchMetadata(body);\n\n try {\n const success = await this.send(stableBody);\n\n if (success) {\n this.clearPersistedEvents();\n callbacks?.onSuccess?.(stableBody.events.length, stableBody.events, stableBody);\n } else {\n this.persistEvents(stableBody);\n callbacks?.onFailure?.();\n }\n\n return success;\n } catch (error) {\n if (error instanceof PermanentError) {\n this.logPermanentError('Permanent error, not retrying', error);\n this.clearPersistedEvents();\n callbacks?.onFailure?.();\n return false;\n }\n\n this.persistEvents(stableBody);\n callbacks?.onFailure?.();\n return false;\n }\n }\n\n /**\n * Recovers and attempts to resend events persisted from a previous session.\n *\n * Idempotent: safe to call multiple times (recovery flag prevents concurrent attempts).\n */\n async recoverPersistedEvents(callbacks?: SendCallbacks): Promise<void> {\n if (this.recoveryInProgress) {\n log('debug', 'Recovery already in progress, skipping duplicate attempt');\n return;\n }\n\n this.recoveryInProgress = true;\n\n let recoveryBody: EventsQueue | null = null;\n let recoveryFailures = 0;\n\n try {\n const persistedData = this.getPersistedData();\n\n if (!persistedData || !this.isDataRecent(persistedData) || persistedData.events.length === 0) {\n this.clearPersistedEvents();\n return;\n }\n\n const rawFailures = persistedData.recoveryFailures;\n recoveryFailures =\n typeof rawFailures === 'number' && Number.isFinite(rawFailures) && rawFailures >= 0 ? rawFailures : 0;\n if (recoveryFailures >= MAX_RECOVERY_FAILURES) {\n log('debug', `Discarding persisted events after ${recoveryFailures} failed recovery attempts`);\n this.clearPersistedEvents();\n callbacks?.onFailure?.();\n return;\n }\n\n if (this.isRateLimited()) {\n log('debug', 'Rate-limit cooldown active, deferring recovery', {\n data: { cooldownRemainingMs: this.rateLimitedUntil - Date.now() },\n });\n callbacks?.onFailure?.();\n return;\n }\n\n recoveryBody = this.ensureBatchMetadata(this.createRecoveryBody(persistedData));\n\n if (recoveryBody.events.length === 0) {\n log('debug', 'All persisted events exceeded the recovery age cutoff; discarding batch');\n this.clearPersistedEvents();\n return;\n }\n\n const success = await this.send(recoveryBody);\n\n if (success) {\n this.clearPersistedEvents();\n callbacks?.onSuccess?.(persistedData.events.length, persistedData.events, recoveryBody);\n } else {\n this.persistEventsWithFailureCount(recoveryBody, recoveryFailures + 1, true);\n callbacks?.onFailure?.();\n }\n } catch (error) {\n if (error instanceof PermanentError) {\n this.logPermanentError('Permanent error during recovery, clearing persisted events', error);\n this.clearPersistedEvents();\n callbacks?.onFailure?.();\n return;\n }\n\n log('error', 'Failed to recover persisted events', { error });\n if (recoveryBody) {\n this.persistEventsWithFailureCount(recoveryBody, recoveryFailures + 1, true);\n }\n callbacks?.onFailure?.();\n } finally {\n this.recoveryInProgress = false;\n }\n }\n\n /**\n * Cleanup method called during `App.destroy()`. No-op — persisted events\n * intentionally kept in localStorage for recovery.\n */\n stop(): void {}\n\n private async backoffDelay(attempt: number): Promise<void> {\n const exponentialDelay = RETRY_BACKOFF_BASE_MS * Math.pow(2, attempt);\n const jitter = Math.random() * RETRY_BACKOFF_JITTER_MS;\n return new Promise((resolve) => setTimeout(resolve, exponentialDelay + jitter));\n }\n\n private async send(body: EventsQueue): Promise<boolean> {\n const requestBody = this.ensureBatchMetadata(body, body._metadata?.idempotency_token);\n\n if (this.apiUrl.includes(SpecialApiUrl.Fail)) {\n log('debug', 'Fail mode: simulating network failure', { data: { events: requestBody.events.length } });\n return false;\n }\n\n if (this.apiUrl.includes(SpecialApiUrl.Localhost)) {\n log('debug', 'Success mode: simulating successful send', { data: { events: requestBody.events.length } });\n return true;\n }\n\n if (this.isRateLimited()) {\n log('debug', 'Rate-limit cooldown active, skipping send', {\n data: {\n cooldownRemainingMs: this.rateLimitedUntil - Date.now(),\n events: requestBody.events.length,\n },\n });\n return false;\n }\n\n if (this.consecutiveNetworkFailures >= MAX_CONSECUTIVE_NETWORK_FAILURES) {\n const elapsed = Date.now() - this.circuitOpenedAt;\n if (elapsed < CIRCUIT_BREAKER_COOLDOWN_MS) {\n log('debug', 'Network circuit open, skipping send', {\n data: {\n consecutiveNetworkFailures: this.consecutiveNetworkFailures,\n cooldownRemainingMs: CIRCUIT_BREAKER_COOLDOWN_MS - elapsed,\n },\n });\n return false;\n }\n // Half-open: allow one probe batch through.\n }\n\n const { url, payload } = this.prepareRequest(requestBody);\n let allTimeouts = true;\n let hadHttpResponse = false;\n\n for (let attempt = 1; attempt <= MAX_SEND_RETRIES + 1; attempt++) {\n try {\n const response = await this.sendWithTimeout(url, payload);\n\n if (response.ok) {\n if (attempt > 1) {\n log('info', `Send succeeded after ${attempt - 1} retry attempt(s)`, {\n data: { events: requestBody.events.length, attempt },\n });\n }\n\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n return true;\n }\n\n return false;\n } catch (error) {\n const isLastAttempt = attempt === MAX_SEND_RETRIES + 1;\n\n if (error instanceof PermanentError) {\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n throw error;\n }\n\n if (error instanceof RateLimitError) {\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n allTimeouts = false;\n hadHttpResponse = true;\n this.armRateLimitCooldown(Date.now() + RATE_LIMIT_COOLDOWN_MS);\n log('warn', 'Rate limited, skipping retries', {\n data: { events: body.events.length, attempt, cooldownMs: RATE_LIMIT_COOLDOWN_MS },\n });\n break;\n }\n\n if (!(error instanceof TimeoutError)) {\n allTimeouts = false;\n }\n\n if (!(error instanceof TypeError)) {\n hadHttpResponse = true;\n }\n\n log(\n isLastAttempt ? 'error' : 'warn',\n `Send attempt ${attempt} failed${isLastAttempt ? ' (all retries exhausted)' : ', will retry'}`,\n {\n error,\n data: {\n events: body.events.length,\n url: url.replace(/\\/\\/[^/]+/, '//[DOMAIN]'),\n attempt,\n maxAttempts: MAX_SEND_RETRIES + 1,\n },\n },\n );\n\n if (!isLastAttempt) {\n await this.backoffDelay(attempt);\n continue;\n }\n\n if (allTimeouts) {\n log('debug', 'All retry attempts timed out, preserving batch for retry', {\n data: { events: requestBody.events.length },\n });\n return false;\n }\n\n if (!hadHttpResponse) {\n this.consecutiveNetworkFailures = Math.min(\n this.consecutiveNetworkFailures + 1,\n MAX_CONSECUTIVE_NETWORK_FAILURES,\n );\n\n if (this.consecutiveNetworkFailures >= MAX_CONSECUTIVE_NETWORK_FAILURES) {\n this.circuitOpenedAt = Date.now();\n }\n } else {\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n }\n\n return false;\n }\n }\n\n return false;\n }\n\n private async sendWithTimeout(url: string, payload: string): Promise<Response> {\n const controller = new AbortController();\n this.pendingControllers.add(controller);\n let didTimeout = false;\n\n const timeoutId = setTimeout(() => {\n didTimeout = true;\n controller.abort();\n }, REQUEST_TIMEOUT_MS);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n body: payload,\n keepalive: true,\n credentials: 'include',\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n const isPermanentError =\n response.status >= 400 && response.status < 500 && response.status !== 408 && response.status !== 429;\n\n if (isPermanentError) {\n const responseCode = await this.readTraceLogErrorCode(response);\n const message = responseCode\n ? `HTTP ${response.status}: ${response.statusText} (${responseCode})`\n : `HTTP ${response.status}: ${response.statusText}`;\n throw new PermanentError(message, response.status, responseCode);\n }\n\n if (response.status === 429) {\n throw new RateLimitError(`HTTP 429: ${response.statusText}`);\n }\n\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response;\n } catch (error) {\n if (error instanceof PermanentError) {\n throw error;\n }\n if (didTimeout) {\n throw new TimeoutError('Request timed out');\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n this.pendingControllers.delete(controller);\n }\n }\n\n private async readTraceLogErrorCode(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.clone().json()) as { code?: unknown };\n if (typeof body.code === 'string' && body.code.length > 0 && body.code.length <= MAX_RESPONSE_CODE_LENGTH) {\n return body.code;\n }\n } catch {\n // Best-effort only. Status still determines permanence.\n }\n return undefined;\n }\n\n private sendQueueSyncInternal(body: EventsQueue): boolean {\n const stableBody = this.ensureBatchMetadata(body);\n const requestBody = this.ensureBatchMetadata(stableBody, stableBody._metadata?.idempotency_token);\n const { url, payload } = this.prepareRequest(requestBody);\n\n if (payload.length > MAX_BEACON_PAYLOAD_SIZE) {\n log('warn', 'Payload exceeds sendBeacon limit, persisting for recovery', {\n data: { size: payload.length, limit: MAX_BEACON_PAYLOAD_SIZE, events: requestBody.events.length },\n });\n this.persistEvents(stableBody);\n return false;\n }\n\n const blob = new Blob([payload], { type: 'application/json' });\n\n if (!this.isSendBeaconAvailable()) {\n log('warn', 'sendBeacon not available, persisting events for recovery');\n this.persistEvents(stableBody);\n return false;\n }\n\n const accepted = navigator.sendBeacon(url, blob);\n\n if (!accepted) {\n log('warn', 'sendBeacon rejected request, persisting events for recovery');\n this.persistEvents(stableBody);\n }\n\n return accepted;\n }\n\n private prepareRequest(body: EventsQueue): { url: string; payload: string } {\n let timestamp = Date.now();\n\n if (timestamp < this.lastMetadataTimestamp) {\n timestamp = this.lastMetadataTimestamp;\n }\n this.lastMetadataTimestamp = timestamp;\n\n const enrichedBody = {\n ...body,\n _metadata: {\n ...body._metadata,\n idempotency_token: body._metadata?.idempotency_token ?? this.computeContentToken(body),\n referer: typeof window !== 'undefined' ? window.location.href : undefined,\n timestamp,\n client_version: LIB_VERSION,\n },\n };\n\n return {\n url: this.apiUrl,\n payload: JSON.stringify(enrichedBody),\n };\n }\n\n private ensureBatchMetadata(body: EventsQueue, preferredToken?: string): EventsQueue {\n const idempotencyToken = body._metadata?.idempotency_token ?? preferredToken ?? this.computeContentToken(body);\n\n if (body._metadata?.idempotency_token === idempotencyToken) {\n return body;\n }\n\n return {\n ...body,\n _metadata: {\n ...body._metadata,\n idempotency_token: idempotencyToken,\n },\n };\n }\n\n /**\n * Deterministic 32-bit FNV-1a hash of sorted event IDs, salted with\n * `user_id` and `session_id`. Produces the same idempotency token for the\n * same set of events across retries.\n */\n private computeContentToken(body: EventsQueue): string {\n const ids = body.events\n .map((e) => e.id)\n .sort()\n .join(',');\n const input = `${body.user_id}|${body.session_id}|${ids}`;\n\n let hash = 2166136261;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 16777619) >>> 0;\n }\n\n return hash.toString(16).padStart(8, '0');\n }\n\n private getPersistedData(): PersistedEventsQueue | null {\n try {\n const storageKey = this.getQueueStorageKey();\n const persistedDataString = this.storeManager.getItem(storageKey);\n\n if (persistedDataString) {\n return JSON.parse(persistedDataString);\n }\n } catch (error) {\n log('debug', 'Failed to parse persisted data', { error });\n this.clearPersistedEvents();\n }\n\n return null;\n }\n\n private isDataRecent(data: PersistedEventsQueue): boolean {\n if (!data.timestamp || typeof data.timestamp !== 'number') {\n return false;\n }\n\n const ageInHours = (Date.now() - data.timestamp) / (1000 * 60 * 60);\n return ageInHours < EVENT_EXPIRY_HOURS;\n }\n\n private createRecoveryBody(data: PersistedEventsQueue): EventsQueue {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { timestamp, recoveryFailures, ...queue } = data;\n const originalEvents = queue.events ?? [];\n const cutoff = Date.now() - MAX_EVENT_AGE_MS_ON_RECOVERY;\n const filteredEvents = originalEvents.filter((event) => {\n const eventTimestamp =\n typeof event.timestamp === 'number'\n ? event.timestamp\n : new Date(event.timestamp as unknown as string).getTime();\n return Number.isFinite(eventTimestamp) && eventTimestamp >= cutoff;\n });\n\n if (filteredEvents.length < originalEvents.length) {\n log('debug', 'Recovery dropped stale events', {\n data: {\n dropped: originalEvents.length - filteredEvents.length,\n kept: filteredEvents.length,\n },\n });\n }\n\n return { ...queue, events: filteredEvents };\n }\n\n private persistEvents(body: EventsQueue): boolean {\n const existing = this.getPersistedData();\n const existingFailures =\n typeof existing?.recoveryFailures === 'number' && Number.isFinite(existing.recoveryFailures)\n ? existing.recoveryFailures\n : 0;\n return this.persistEventsWithFailureCount(body, existingFailures);\n }\n\n private persistEventsWithFailureCount(body: EventsQueue, recoveryFailures: number, skipThrottle = false): boolean {\n try {\n const existing = this.getPersistedData();\n\n if (!skipThrottle && existing && existing.timestamp) {\n const timeSinceExisting = Date.now() - existing.timestamp;\n\n if (timeSinceExisting < PERSISTENCE_THROTTLE_MS) {\n log('debug', 'Skipping persistence, another tab recently persisted events', {\n data: { timeSinceExisting },\n });\n return true;\n }\n }\n\n const persistedData: PersistedEventsQueue = {\n ...body,\n timestamp: Date.now(),\n ...(recoveryFailures > 0 && { recoveryFailures }),\n };\n\n const storageKey = this.getQueueStorageKey();\n this.storeManager.setItem(storageKey, JSON.stringify(persistedData));\n\n return !!this.storeManager.getItem(storageKey);\n } catch (error) {\n log('debug', 'Failed to persist events', { error });\n return false;\n }\n }\n\n private clearPersistedEvents(): void {\n try {\n const key = this.getQueueStorageKey();\n this.storeManager.removeItem(key);\n } catch (error) {\n log('debug', 'Failed to clear persisted events', { error });\n }\n }\n\n private isSendBeaconAvailable(): boolean {\n return typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function';\n }\n\n private logPermanentError(context: string, error: PermanentError): void {\n const now = Date.now();\n const key = `${error.statusCode ?? 'unknown'}:${error.responseCode ?? ''}`;\n const shouldLog =\n !this.lastPermanentErrorLog ||\n this.lastPermanentErrorLog.key !== key ||\n now - this.lastPermanentErrorLog.timestamp >= PERMANENT_ERROR_LOG_THROTTLE_MS;\n\n if (shouldLog) {\n log('error', context, {\n data: { status: error.statusCode, code: error.responseCode, message: error.message },\n });\n\n this.lastPermanentErrorLog = { key, timestamp: now };\n }\n }\n}\n","import { log } from '../utils';\nimport { StateManager } from './state.manager';\n\n/**\n * Manages accurate timestamp generation using monotonic clock (`performance.now()`)\n * to prevent issues from system clock changes during the session.\n *\n * - Boot reference: `performance.now()` + `Date.now()` captured at construction.\n * - `now()` returns `bootTimestamp + (performance.now() - bootTime)` (immune to clock changes during session).\n * - Falls back to `Date.now()` when `performance.now()` is unavailable (SSR / old browsers).\n */\nexport class TimeManager extends StateManager {\n private readonly bootTime: number;\n private readonly bootTimestamp: number;\n private readonly hasPerformanceNow: boolean;\n\n constructor() {\n super();\n\n if (typeof window === 'undefined') {\n this.hasPerformanceNow = false;\n this.bootTime = 0;\n this.bootTimestamp = 0;\n return;\n }\n\n this.hasPerformanceNow = typeof performance !== 'undefined' && typeof performance.now === 'function';\n\n if (this.hasPerformanceNow) {\n this.bootTime = performance.now();\n this.bootTimestamp = Date.now();\n } else {\n this.bootTime = 0;\n this.bootTimestamp = Date.now();\n log('debug', 'performance.now() not available, falling back to Date.now()');\n }\n }\n\n /**\n * Returns current timestamp in milliseconds since epoch, immune to clock\n * changes during the session.\n */\n now(): number {\n if (!this.hasPerformanceNow) {\n return Date.now();\n }\n\n const elapsed = performance.now() - this.bootTime;\n return Math.round(this.bootTimestamp + elapsed);\n }\n\n /**\n * Validates a timestamp is not more than 2 minutes in the future relative\n * to the monotonic clock. Backend allows 3 minutes — keep client tighter\n * so obvious clock-skew events are flagged before they hit the wire.\n */\n validateTimestamp(timestamp: number): { valid: boolean; error?: string } {\n const maxFutureOffset = 2 * 60 * 1000;\n const offset = timestamp - this.now();\n\n if (offset > maxFutureOffset) {\n return {\n valid: false,\n error: `Timestamp is ${(offset / 1000 / 60).toFixed(2)} minutes in the future (max allowed: 2 minutes)`,\n };\n }\n\n return { valid: true };\n }\n}\n","import {\n EVENT_SENT_INTERVAL_MS,\n MAX_EVENTS_QUEUE_LENGTH,\n DUPLICATE_EVENT_THRESHOLD_MS,\n RATE_LIMIT_WINDOW_MS,\n MAX_EVENTS_PER_SECOND,\n MAX_PENDING_EVENTS_BUFFER,\n BATCH_SIZE_THRESHOLD,\n MAX_SAME_EVENT_PER_MINUTE,\n PER_EVENT_RATE_LIMIT_WINDOW_MS,\n MAX_EVENTS_PER_SESSION,\n MAX_CLICKS_PER_SESSION,\n MAX_PAGE_VIEWS_PER_SESSION,\n MAX_CUSTOM_EVENTS_PER_SESSION,\n MAX_SCROLL_EVENTS_PER_SESSION,\n MAX_FINGERPRINTS,\n FINGERPRINT_CLEANUP_MULTIPLIER,\n MAX_FINGERPRINTS_HARD_LIMIT,\n MAX_SEND_INTERVAL_MS,\n MAX_CONSECUTIVE_SEND_FAILURES,\n} from '../constants/config.constants';\nimport {\n SESSION_COUNTS_KEY,\n SESSION_COUNTS_EXPIRY_MS,\n SESSION_COUNTS_LAST_CLEANUP_KEY,\n SESSION_COUNTS_CLEANUP_THROTTLE_MS,\n STORAGE_BASE_KEY,\n} from '../constants/storage.constants';\nimport { EventsQueue, EmitterEvent, EventData, EventType, Mode, SessionEventCounts, QueuedEvent } from '../types';\nimport { log, Emitter, generateEventId } from '../utils';\nimport { SenderManager } from './sender.manager';\nimport { StateManager } from './state.manager';\nimport { StorageManager } from './storage.manager';\nimport { TimeManager } from './time.manager';\n\nconst VALID_EVENT_TYPES: ReadonlySet<string> = new Set(Object.values(EventType));\n\n/**\n * Extended session counts structure with metadata for expiry tracking.\n *\n * Adds timestamp and version fields to SessionEventCounts for cleanup management.\n */\ninterface StoredSessionCounts extends SessionEventCounts {\n /** Timestamp when counts were last saved (for 7-day expiry) */\n _timestamp: number;\n /** Schema version for future migrations */\n _version: number;\n}\n\n/**\n * Core component responsible for event tracking, queue management, deduplication,\n * rate limiting, and multi-integration API communication coordination.\n *\n * **Purpose**: Central hub for all analytics events in TraceLog, managing the complete\n * event lifecycle from capture to transmission.\n *\n * **Core Functionality**:\n * - **Event Tracking**: Captures all user interactions (clicks, scrolls, page views, custom events, web vitals, errors)\n * - **Queue Management**: Batches events with 10-second base intervals (exponential backoff on failure) to optimize network requests\n * - **Deduplication**: LRU cache with 1000-entry fingerprint storage prevents duplicate events\n * - **Rate Limiting**: Client-side limits (50 events/second global, 60/minute per event name)\n * - **Per-Session Caps**: Configurable limits prevent runaway generation (1000 total, type-specific limits)\n * - **Dynamic Queue Flush**: Immediate send when 50-event batch threshold reached\n * - **Pending Events Buffer**: Buffers up to 100 events before session initialization\n * - **Event Recovery**: Recovers persisted events from localStorage after crashes (independent per integration)\n * - **Multi-Integration**: Manages 0-2 SenderManager instances (SaaS + Custom) with parallel async sending\n * - **Standalone Mode**: Emits queue events without network requests when no integrations configured\n *\n * **Key Features**:\n * - **LRU Deduplication**: 1000 fingerprints, 10px coordinate precision for clicks, 500ms time threshold\n * - **Smart Queue Overflow**: Fixed 100-event limit with priority preservation for SESSION_START/END\n * - **Rate Limiting**: 50 events/sec sliding window, critical events exempted\n * - **Per-Event-Name Limits**: 60 same event name per minute (configurable via `maxSameEventPerMinute`)\n * - **Per-Session Caps**: Total 1000/session, Clicks 500, Page views 100, Custom 500, Viewport 200, Scroll 120\n * - **Dynamic Flush**: Immediate send when 50-event batch threshold reached\n * - **Multi-Integration Sending**: Parallel async with `Promise.allSettled()`, independent error handling per integration\n * - **QA Mode**: Console logging for custom events without backend transmission\n *\n * **State Management**:\n * - **`hasStartSession` Flag**: Prevents duplicate SESSION_START events across init cycles\n * - Set to `true` when SESSION_START is tracked via `track()` method\n * - Reset to `false` in `stop()` method to allow subsequent init() → destroy() → init() cycles\n * - NOT set by SessionManager's BroadcastChannel message handler (secondary tabs don't track SESSION_START)\n *\n * **Transformer Support**:\n * - **`beforeSend`**: Applied conditionally based on integration mode\n * - Custom-only mode: Applied in EventManager before dedup/sampling/queueing\n * - Multi-integration mode: Skipped in EventManager, applied in SenderManager per-integration\n * - **`beforeBatch`**: Applied in SenderManager before network transmission\n *\n * @see src/managers/README.md for detailed documentation\n *\n * @example\n * ```typescript\n * const eventManager = new EventManager(storage, emitter, transformers);\n *\n * // Track event\n * eventManager.track({\n * type: 'click',\n * click_data: { x: 100, y: 200, tag: 'button' }\n * });\n *\n * // Flush immediately (async)\n * await eventManager.flushImmediately();\n *\n * // Synchronous flush (page unload)\n * eventManager.flushImmediatelySync();\n *\n * // Stop and cleanup\n * eventManager.stop();\n * ```\n */\nexport class EventManager extends StateManager {\n private readonly dataSenders: SenderManager[];\n private readonly emitter: Emitter | null;\n private readonly timeManager: TimeManager;\n private readonly recentEventFingerprints = new Map<string, number>();\n private readonly perEventRateLimits: Map<string, number[]> = new Map();\n\n private eventsQueue: QueuedEvent[] = [];\n private pendingEventsBuffer: Partial<EventData>[] = [];\n private sendTimeoutId: number | null = null;\n private sendInProgress = false;\n private consecutiveSendFailures = 0;\n private rateLimitCounter = 0;\n private rateLimitWindowStart = 0;\n private lastSessionId: string | null = null;\n // Set when a sync flush is requested mid-async-send; drained by the async\n // finally block. See `drainPendingSyncFlush` for the full rationale.\n private pendingSyncFlush = false;\n\n private sessionEventCounts: SessionEventCounts = {\n total: 0,\n [EventType.CLICK]: 0,\n [EventType.PAGE_VIEW]: 0,\n [EventType.CUSTOM]: 0,\n [EventType.SCROLL]: 0,\n };\n\n private readonly saveSessionCountsDebounced: ((sessionId: string) => void) | null = null;\n\n /**\n * Creates an EventManager instance.\n *\n * @param storeManager - Storage manager for persistence\n * @param emitter - Optional event emitter for local event consumption\n */\n constructor(storeManager: StorageManager, emitter: Emitter | null = null) {\n super();\n\n this.emitter = emitter;\n this.timeManager = new TimeManager();\n\n this.dataSenders = [];\n const collectApiUrls = this.get('collectApiUrls');\n\n if (collectApiUrls?.saas) {\n this.dataSenders.push(new SenderManager(storeManager, collectApiUrls.saas));\n }\n\n // Initialize debounced session counts saver (500ms delay, trailing edge)\n // Reduces localStorage writes from ~1000 per session to ~20-30 (96-97% reduction)\n this.saveSessionCountsDebounced = this.debounce((sessionId: string) => {\n this.saveSessionCounts(sessionId);\n }, 500);\n\n // Cleanup expired session counts on init (7-day TTL)\n this.cleanupExpiredSessionCounts();\n }\n\n /**\n * Recovers persisted events from localStorage after a crash or page reload.\n *\n * **Purpose**: Ensures zero data loss by recovering events that failed to send\n * in the previous session due to network errors or crashes.\n *\n * **Flow**:\n * 1. Calls `recoverPersistedEvents()` on all SenderManager instances in parallel\n * 2. Each SenderManager attempts to resend its persisted events to backend\n * 3. On success: Removes recovered events from consent/pending buffers\n * 4. On failure: Logs warning (events remain in localStorage for next attempt)\n *\n * **Multi-Integration**:\n * - Independent recovery per integration (SaaS + Custom backends)\n * - Parallel recovery via `Promise.allSettled()` (one failure doesn't block others)\n * - No cross-contamination (SaaS events don't go to Custom API)\n *\n * **Called by**: `App.init()` after initialization\n *\n * **Important**: Events are NOT removed from pending/consent buffers until\n * successful network transmission.\n *\n * @see src/managers/README.md (lines 5-75) for recovery details\n */\n async recoverPersistedEvents(): Promise<void> {\n const recoveryPromises = this.dataSenders.map(async (sender) =>\n sender.recoverPersistedEvents({\n onSuccess: (_eventCount, recoveredEvents, body) => {\n if (recoveredEvents && recoveredEvents.length > 0) {\n const eventIds = recoveredEvents.map((e) => e.id);\n this.removeProcessedEvents(eventIds);\n\n if (body) {\n this.emitEventsQueue(body);\n }\n }\n },\n onFailure: () => {\n log('debug', 'Failed to recover persisted events');\n },\n }),\n );\n\n await Promise.allSettled(recoveryPromises);\n }\n\n /**\n * Tracks a user interaction event and adds it to the event queue.\n *\n * **Purpose**: Central tracking method for all analytics events (clicks, page views,\n * custom events, web vitals, errors, scroll, viewport visibility, session start/end).\n *\n * **Validation & Buffering**:\n * - Validates `type` is provided (required)\n * - If session not initialized: Buffers in `pendingEventsBuffer` (max 100 events, FIFO)\n *\n * **Rate Limiting** (non-critical events only):\n * - Global: 50 events/second sliding window (critical events exempted)\n * - Per-event-name: 60/minute for custom events (configurable via `maxSameEventPerMinute`)\n * - Per-session total: 1000 events max\n * - Per-session by type: Clicks 500, Page views 100, Custom 500, Viewport 200, Scroll 120\n *\n * **Deduplication**:\n * - LRU cache with 1000 fingerprints (10px coordinate precision for clicks, 500ms time threshold)\n * - Prevents duplicate events within 500ms window\n * - SESSION_START protected by `hasStartSession` flag\n *\n * **Sampling**:\n * - Applied after validation and rate limiting\n * - Critical events (SESSION_START/END) always included\n * - Configurable via `samplingRate` (0-1)\n *\n * **Transformation**:\n * - `beforeSend` applied (if custom-only mode) before dedup/sampling/queueing\n * - Returning `null` from `beforeSend` filters out the event\n *\n * **Queue Management**:\n * - Events added to `eventsQueue` (max 100 events, FIFO with priority for session events)\n * - Dynamic flush: Immediate send when 50-event batch threshold reached\n * - Periodic flush: Every 10 seconds\n *\n * **Multi-Integration**:\n * - Backend integrations: Handled by SenderManager instances\n *\n * **QA Mode**:\n * - Custom events logged to console with styling\n * - Events NOT sent to backend (emitted locally only)\n *\n * @param eventData - Event data to track\n *\n * @example\n * ```typescript\n * eventManager.track({\n * type: EventType.CLICK,\n * click_data: { x: 0.5, y: 0.3, tag: 'button', text: 'Submit' }\n * });\n *\n * eventManager.track({\n * type: EventType.CUSTOM,\n * custom_event: { name: 'checkout_completed', metadata: { total: 99.99 } }\n * });\n * ```\n *\n * @see src/managers/README.md (lines 5-75) for detailed tracking logic\n */\n track({\n type,\n page_url,\n from_page_url,\n scroll_data,\n click_data,\n custom_event,\n web_vitals,\n error_data,\n page_view,\n }: Partial<EventData>): void {\n if (!type) {\n log('error', 'Event type is required - event will be ignored');\n return;\n }\n\n if (!VALID_EVENT_TYPES.has(type)) {\n log('error', 'Invalid event type - event will be ignored', {\n data: { type },\n });\n return;\n }\n\n const currentSessionId = this.get('sessionId');\n\n if (!currentSessionId) {\n if (this.pendingEventsBuffer.length >= MAX_PENDING_EVENTS_BUFFER) {\n this.pendingEventsBuffer.shift();\n log('debug', 'Pending events buffer full - dropping oldest event', {\n data: { maxBufferSize: MAX_PENDING_EVENTS_BUFFER },\n });\n }\n\n this.pendingEventsBuffer.push({\n type,\n page_url,\n from_page_url,\n scroll_data,\n click_data,\n custom_event,\n web_vitals,\n error_data,\n page_view,\n });\n\n return;\n }\n\n if (this.lastSessionId !== currentSessionId) {\n this.lastSessionId = currentSessionId;\n // Load persisted counts from localStorage instead of resetting to zero\n // This prevents count reset on page reload and allows proper enforcement of per-session limits\n this.sessionEventCounts = this.loadSessionCounts(currentSessionId);\n }\n\n const isCriticalEvent = type === EventType.SESSION_START;\n\n if (isCriticalEvent) {\n log('debug', 'Processing SESSION_START event', {\n data: { sessionId: currentSessionId },\n });\n }\n\n if (!isCriticalEvent && !this.checkRateLimit()) {\n return;\n }\n\n const eventType = type as EventType;\n\n if (!isCriticalEvent) {\n if (this.sessionEventCounts.total >= MAX_EVENTS_PER_SESSION) {\n log('warn', 'Session event limit reached', {\n data: {\n type: eventType,\n total: this.sessionEventCounts.total,\n limit: MAX_EVENTS_PER_SESSION,\n },\n });\n\n return;\n }\n\n const typeLimit = this.getTypeLimitForEvent(eventType);\n\n if (typeLimit) {\n const currentCount = this.sessionEventCounts[eventType];\n\n if (currentCount !== undefined && currentCount >= typeLimit) {\n log('warn', 'Session event type limit reached', {\n data: {\n type: eventType,\n count: currentCount,\n limit: typeLimit,\n },\n });\n\n return;\n }\n }\n }\n\n if (eventType === EventType.CUSTOM && custom_event?.name) {\n const maxSameEventPerMinute = this.get('config')?.maxSameEventPerMinute ?? MAX_SAME_EVENT_PER_MINUTE;\n\n if (!this.checkPerEventRateLimit(custom_event.name, maxSameEventPerMinute)) {\n return;\n }\n }\n const isSessionStart = eventType === EventType.SESSION_START;\n\n const currentPageUrl = (page_url as string) || this.get('pageUrl');\n const payload = this.buildEventPayload({\n type: eventType,\n page_url: currentPageUrl,\n from_page_url,\n scroll_data,\n click_data,\n custom_event,\n web_vitals,\n error_data,\n page_view,\n });\n\n // Handle event filtered by beforeSend transformer\n if (!payload) {\n return;\n }\n\n if (!isCriticalEvent && !this.shouldSample()) {\n return;\n }\n\n if (isSessionStart) {\n const currentSessionId = this.get('sessionId');\n\n if (!currentSessionId) {\n log('error', 'Session start event requires sessionId - event will be ignored');\n return;\n }\n\n if (this.get('hasStartSession')) {\n log('debug', 'Duplicate session_start detected', {\n data: { sessionId: currentSessionId },\n });\n\n return;\n }\n\n this.set('hasStartSession', true);\n }\n\n if (this.isDuplicateEvent(payload)) {\n return;\n }\n\n if (this.get('mode') === Mode.QA && eventType === EventType.CUSTOM && custom_event) {\n log('info', `Custom Event: ${custom_event.name}`, {\n visibility: 'qa',\n data: {\n name: custom_event.name,\n ...(custom_event.metadata && { metadata: custom_event.metadata }),\n },\n });\n\n this.emitEvent(payload);\n\n return;\n }\n\n this.addToQueue(payload);\n\n if (!isCriticalEvent) {\n this.sessionEventCounts.total++;\n\n if (this.sessionEventCounts[eventType] !== undefined) {\n this.sessionEventCounts[eventType]++;\n }\n\n // Persist updated counts to localStorage (debounced for performance)\n // Debouncing reduces localStorage writes from ~1000 to ~20-30 per session (96-97% reduction)\n // Trailing edge ensures no data loss - final counts always saved after 500ms silence\n const currentSessionId = this.get('sessionId');\n if (currentSessionId && this.saveSessionCountsDebounced) {\n this.saveSessionCountsDebounced(currentSessionId);\n }\n }\n }\n\n /**\n * Stops event tracking and clears all queues and buffers.\n *\n * **Purpose**: Cleanup method called during `App.destroy()` to reset EventManager state\n * and allow subsequent init() → destroy() → init() cycles.\n *\n * **Cleanup Actions**:\n * 1. **Clear send timeout**: Cancels pending queue flush timeout and resets backoff state\n * 2. **Clear all queues and buffers**:\n * - `eventsQueue`: Discarded (not sent)\n * - `pendingEventsBuffer`: Discarded (events before session init)\n * 3. **Reset rate limiting state**: Clears rate limit counters and per-event limits\n * 4. **Reset session counters**: Clears per-session event counts\n * 5. **Reset `hasStartSession` flag**: Allows SESSION_START in next init cycle\n * 6. **Stop SenderManagers**: Calls `stop()` on all SenderManager instances\n *\n * **Important Behavior**:\n * - **No final flush**: `stop()` itself does NOT send queued events\n * - `App.destroy()` calls `flushImmediatelySync()` before `stop()` automatically\n *\n * **Multi-Integration**:\n * - Stops all SenderManager instances (SaaS + Custom)\n *\n * **Called by**: `App.destroy()` during application teardown\n *\n * @example\n * ```typescript\n * // Proper cleanup with final flush\n * eventManager.flushImmediatelySync(); // Send pending events\n * eventManager.stop(); // Stop and clear\n * ```\n *\n * @see src/managers/README.md (lines 5-75) for cleanup details\n */\n stop(): void {\n // Note: customHeadersProvider is NOT cleared here since it's stored in SenderManagers\n // and those are destroyed during this stop() call anyway\n this.clearSendTimeout();\n this.sendInProgress = false;\n this.pendingSyncFlush = false;\n this.consecutiveSendFailures = 0;\n\n // Save session counts immediately before cleanup (bypass debounce)\n // This ensures final counts are persisted before destroying the EventManager\n const currentSessionId = this.get('sessionId');\n if (currentSessionId) {\n this.saveSessionCounts(currentSessionId);\n }\n\n this.eventsQueue = [];\n this.pendingEventsBuffer = [];\n this.recentEventFingerprints.clear();\n this.rateLimitCounter = 0;\n this.rateLimitWindowStart = 0;\n this.perEventRateLimits.clear();\n this.sessionEventCounts = {\n total: 0,\n [EventType.CLICK]: 0,\n [EventType.PAGE_VIEW]: 0,\n [EventType.CUSTOM]: 0,\n [EventType.SCROLL]: 0,\n };\n this.lastSessionId = null;\n this.set('hasStartSession', false);\n\n this.dataSenders.forEach((sender) => {\n sender.stop();\n });\n }\n\n /**\n * Flushes all events in the queue asynchronously.\n *\n * **Purpose**: Force immediate sending of queued events without waiting for\n * the scheduled queue flush timeout.\n *\n * **Use Cases**:\n * - Manual flush triggered by user action\n * - Before page unload (prefer `flushImmediatelySync()` for unload scenarios)\n * - Testing/debugging\n *\n * **Behavior**:\n * - Sends events via `fetch()` API (async, reliable, allows retries)\n * - Multi-integration: Sends to all configured backends in parallel\n * - Does NOT block (returns Promise that resolves when all sends complete)\n * - Clears queue only after successful transmission\n *\n * **Note**: For page unload, use `flushImmediatelySync()` instead,\n * which uses `sendBeacon()` for guaranteed delivery.\n *\n * @returns Promise resolving to `true` if at least one integration accepted\n * the batch during this call (optimistic removal — failures\n * persist per-integration for retry). `false` if no events, all\n * senders failed, or a flush is already in flight.\n *\n * @example\n * ```typescript\n * // Before critical user action\n * await eventManager.flushImmediately();\n * ```\n *\n * @see flushImmediatelySync for synchronous page unload flush\n * @see src/managers/README.md (lines 5-75) for flush details\n */\n async flushImmediately(): Promise<boolean> {\n return this.flushEvents(false);\n }\n\n /**\n * Flushes all events in the queue synchronously using `sendBeacon()`.\n *\n * **Purpose**: Ensure events are sent before page unload, even if network is slow.\n *\n * **Use Cases**:\n * - Page unload (`beforeunload`, `pagehide` events)\n * - Tab close detection\n * - Any scenario where async flush might be interrupted\n *\n * **Behavior**:\n * - Uses `navigator.sendBeacon()` API (synchronous, queued by browser)\n * - Payload size limited to 64KB per beacon\n * - Browser guarantees delivery attempt (queued even if page closes)\n * - Clears queue immediately (no retry mechanism)\n *\n * **Multi-Integration**:\n * - Sends to all configured backends (SaaS + Custom) in parallel\n * - Independent success tracking per integration\n *\n * **Limitations**:\n * - No retry on failure (sendBeacon is fire-and-forget)\n * - 64KB payload limit (large batches may be truncated)\n *\n * **In-flight contract**: if an async send is already running this call is\n * deferred (queued for replay in the async send's `finally` block) and\n * returns `false` — nothing has been delivered yet at the point of return.\n * Mirrors `flushImmediately()`'s behaviour for the same condition.\n *\n * @returns `true` if at least one integration accepted the beacon batch\n * *during this call*, `false` otherwise (no events, all senders\n * failed, or the call was deferred behind an in-flight async send)\n *\n * @example\n * ```typescript\n * // Page unload handler\n * window.addEventListener('beforeunload', () => {\n * eventManager.flushImmediatelySync();\n * });\n * ```\n *\n * @see flushImmediately for async flush with retries\n * @see src/managers/README.md (lines 5-75) for flush details\n */\n flushImmediatelySync(): boolean {\n return this.flushEvents(true) as boolean;\n }\n\n /**\n * Returns the current number of events in the main queue.\n *\n * **Purpose**: Debugging and monitoring utility to check queue length.\n *\n * **Note**: This does NOT include:\n * - Pending events buffer (events before session init)\n * - Consent events buffer (events awaiting consent)\n * - Persisted events (events in localStorage from previous sessions)\n *\n * @returns Number of events currently in the main queue\n *\n * @example\n * ```typescript\n * const queueSize = eventManager.getQueueLength();\n * console.log(`Queue has ${queueSize} events`);\n * ```\n */\n getQueueLength(): number {\n return this.eventsQueue.length;\n }\n\n /**\n * Returns a copy of current events in the queue.\n *\n * **Purpose**: Test utility to inspect queued events for validation.\n *\n * **Note**: Only available in development mode via TestBridge.\n *\n * @returns Shallow copy of events queue\n * @internal Used by test-bridge.ts for test inspection\n */\n getQueueEvents(): EventData[] {\n // Strip the internal `_session_id` field — it's queue bookkeeping, not a\n // public contract. Tests and TestBridge consumers should see plain EventData.\n return this.eventsQueue.map(({ _session_id, ...rest }) => {\n void _session_id;\n return rest;\n });\n }\n\n /**\n * Triggers immediate queue flush (test utility).\n *\n * **Purpose**: Test utility to manually flush event queue for validation.\n *\n * **Note**: Only available in development mode via TestBridge.\n *\n * @returns Promise that resolves when flush completes\n * @internal Used by test-bridge.ts for test control\n */\n async flushQueue(): Promise<void> {\n await this.flushImmediately();\n }\n\n /**\n * Clears the event queue (test utility - use with caution).\n *\n * **Purpose**: Test utility to reset queue state between tests.\n *\n * **Warning**: This will discard all queued events without sending them.\n * Only use in test cleanup or when explicitly required.\n *\n * **Note**: Only available in development mode via TestBridge.\n *\n * @internal Used by test-bridge.ts for test cleanup\n */\n clearQueue(): void {\n this.eventsQueue = [];\n }\n\n /**\n * Flushes buffered events to the main queue after session initialization.\n *\n * **Purpose**: Re-tracks events that were captured before session initialization\n * (e.g., events fired during `App.init()` before SessionManager completes).\n *\n * **Pending Events Buffer**:\n * - Holds up to 100 events captured before `sessionId` is available\n * - FIFO eviction when buffer full (oldest events dropped with warning)\n * - Cleared and re-tracked when session becomes available\n *\n * **Flow**:\n * 1. Check if session is initialized (`sessionId` exists in global state)\n * 2. If not initialized: Log warning and keep events in buffer\n * 3. If initialized: Copy buffer, clear it, and re-track each event via `track()`\n * 4. Each event goes through full validation/dedup/rate limiting pipeline\n *\n * **Called by**:\n * - `SessionManager.startTracking()` after session initialization\n * - Ensures no events are lost during initialization phase\n *\n * **Important**: Events are re-tracked through `track()` method, so they go\n * through all validation, deduplication, rate limiting, and consent checks again.\n *\n * @example\n * ```typescript\n * // In SessionManager after session creation\n * this.set('sessionId', newSessionId);\n * eventManager.flushPendingEvents(); // Re-track buffered events\n * ```\n *\n * @see src/managers/README.md (lines 5-75) for pending buffer details\n */\n flushPendingEvents(): void {\n if (this.pendingEventsBuffer.length === 0) {\n return;\n }\n\n const currentSessionId = this.get('sessionId');\n if (!currentSessionId) {\n log('debug', 'Cannot flush pending events: session not initialized - keeping in buffer', {\n data: { bufferedEventCount: this.pendingEventsBuffer.length },\n });\n\n return;\n }\n\n const bufferedEvents = [...this.pendingEventsBuffer];\n this.pendingEventsBuffer = [];\n\n bufferedEvents.forEach((event) => {\n this.track(event);\n });\n }\n\n private clearSendTimeout(): void {\n if (this.sendTimeoutId !== null) {\n clearTimeout(this.sendTimeoutId);\n this.sendTimeoutId = null;\n }\n }\n\n private isSuccessfulResult(result: PromiseSettledResult<boolean>): boolean {\n return result.status === 'fulfilled' && result.value === true;\n }\n\n /**\n * Groups the queue by frozen `_session_id`, preserving insertion order.\n * Single pass — `buildBatchesWithIds()` builds one batch + one eventIds list\n * per group, so the grouping cost is O(N) per flush regardless of session\n * count.\n *\n * **Self-heal**: any entry missing `_session_id` (an internal invariant\n * violation — `buildEventPayload` always stamps it) is removed from the\n * queue rather than left behind, otherwise a single corrupted entry would\n * keep `eventsQueue.length > 0` forever and re-trigger periodic sends.\n */\n private groupQueuedEventsBySession(): Map<string, QueuedEvent[]> {\n const groups = new Map<string, QueuedEvent[]>();\n const corruptedIds: string[] = [];\n for (const event of this.eventsQueue) {\n if (!event._session_id) {\n // Logged at `debug` because no integrating-developer action can fix\n // it — it's a TraceLog bug. The entry is dropped from the queue\n // below to keep the system flushable.\n log('debug', 'Queued event missing _session_id, dropping', {\n data: { eventId: event.id, type: event.type },\n });\n corruptedIds.push(event.id);\n continue;\n }\n const group = groups.get(event._session_id);\n if (group) {\n group.push(event);\n } else {\n groups.set(event._session_id, [event]);\n }\n }\n if (corruptedIds.length > 0) {\n this.removeProcessedEvents(corruptedIds);\n }\n return groups;\n }\n\n /**\n * Builds a parallel list of `(batch, eventIds)` for sending. The eventIds are\n * the original `_session_id`-tagged event IDs in the queue that map to this\n * batch — used for optimistic removal. We can't read them off the wrapper's\n * `events[]` because dedup may have removed some signatures.\n */\n private buildBatchesWithIds(): Array<{ batch: EventsQueue; eventIds: string[] }> {\n const groups = this.groupQueuedEventsBySession();\n if (groups.size === 0) return [];\n\n const result: Array<{ batch: EventsQueue; eventIds: string[] }> = [];\n for (const [sessionId, groupEvents] of groups) {\n result.push({\n batch: this.buildBatchFromGroup(sessionId, groupEvents),\n eventIds: groupEvents.map((e) => e.id),\n });\n }\n return result;\n }\n\n private flushEvents(isSync: boolean): boolean | Promise<boolean> {\n if (this.eventsQueue.length === 0) {\n return isSync ? true : Promise.resolve(true);\n }\n\n // Prevent concurrent async sends — if a send is already in-flight\n // (e.g., sendEventsQueue with retries), skip to avoid duplicate requests.\n if (!isSync && this.sendInProgress) {\n log('debug', 'Async flush skipped: send already in progress');\n return Promise.resolve(false);\n }\n\n const planned = this.buildBatchesWithIds();\n if (planned.length === 0) {\n return isSync ? true : Promise.resolve(true);\n }\n\n if (this.dataSenders.length === 0) {\n // Standalone mode: emit each batch locally and clear the queue.\n for (const { batch, eventIds } of planned) {\n this.removeProcessedEvents(eventIds);\n this.emitEventsQueue(batch);\n }\n this.clearSendTimeout();\n return isSync ? true : Promise.resolve(true);\n }\n\n // The in-flight async fetch already owns these events: on success it clears\n // its persisted snapshot, on failure it writes one. Persisting again here\n // would resurrect the same events next session and the API would reject\n // them on the unique-id index, flagging every page-unload race as a\n // duplicate. Trade-off: if the page dies before the fetch flushes its body\n // to the kernel send buffer, those events are lost — accepted as the rarer\n // outcome versus systematic duplicate-flagging.\n if (isSync && this.sendInProgress) {\n const totalEvents = planned.reduce((acc, p) => acc + p.eventIds.length, 0);\n // Defer the sync flush until the in-flight async send settles. The async\n // fetch was built before the event(s) prompting this sync call, so it\n // does NOT include them; without the deferred re-flush the events would\n // sit in the queue until the next periodic tick (potentially lost on\n // navigation). The sendBatchAsync/sendEventsQueue finally blocks re-call\n // flushImmediatelySync when this flag is set.\n //\n // Return `false`: nothing has been delivered yet at this point. Mirrors\n // `flushImmediately()`'s in-flight contract (also returns `false` when a\n // send is already in progress) so callers can read both methods the\n // same way: `true` ⇒ at least one integration received the batch *now*.\n this.pendingSyncFlush = true;\n log('debug', 'Sync flush deferred: async send in-flight, will retry on settle', {\n data: { eventCount: totalEvents },\n });\n return false;\n }\n\n if (isSync) {\n // sendBeacon path — already non-blocking at the browser level.\n const results = planned.map(({ batch, eventIds }) => this.sendBatchSync(batch, eventIds));\n this.settleSendTimeout();\n return results.some(Boolean);\n }\n\n // Claim sendInProgress for the lifetime of the async fetch. Two rapid\n // flushImmediately() calls (e.g., SPA navigation followed by pagehide,\n // or visibilitychange firing alongside an explicit flush) would otherwise\n // both pass the guard above, build the same `planned`, and fire duplicate\n // network requests. The periodic sender uses the same flag, so periodic\n // and explicit flushes serialize correctly against each other too.\n this.sendInProgress = true;\n return (async (): Promise<boolean> => {\n try {\n // Multi-session is rare; when it happens we send all batches in parallel.\n // Per-batch optimistic removal + per-integration persistence are\n // independent, so concurrent completion is race-safe (single-threaded JS,\n // disjoint event-id sets).\n const results = await Promise.all(\n planned.map(async ({ batch, eventIds }) => this.sendBatchAsync(batch, eventIds)),\n );\n this.settleSendTimeout();\n return results.some(Boolean);\n } finally {\n this.sendInProgress = false;\n this.drainPendingSyncFlush();\n }\n })();\n }\n\n /**\n * Reconciles the periodic send timer after a flush attempt. Clears the\n * timer when the queue is empty, otherwise (re)schedules a retry tick.\n *\n * **Why**: a `flushImmediately()` / `flushImmediatelySync()` call where all\n * integrations fail leaves events in `eventsQueue` for retry. The periodic\n * timer is the safety net that drains them when the backend recovers — if\n * we cleared it unconditionally here, the queue would sit untouched until\n * the next tracked event resurrects the timer in `addToQueue`. Mirrors the\n * pattern in `sendEventsQueue()` (the periodic path).\n */\n private settleSendTimeout(): void {\n if (this.eventsQueue.length === 0) {\n this.clearSendTimeout();\n } else {\n this.scheduleSendTimeout();\n }\n }\n\n /**\n * Re-runs a sync flush that was deferred while an async send was in flight.\n *\n * Called from the `finally` blocks of `flushEvents(false)` and\n * `sendEventsQueue()`. If `pendingSyncFlush` is set, clears the flag and\n * invokes `flushImmediatelySync()` synchronously so any events that arrived\n * after the deferred sync call are delivered before the next event loop\n * tick. Critical for high-stakes events tracked mid-async-send.\n */\n private drainPendingSyncFlush(): void {\n if (!this.pendingSyncFlush) return;\n this.pendingSyncFlush = false;\n this.flushImmediatelySync();\n }\n\n /**\n * Sends one batch synchronously across all integrations (sendBeacon path).\n * Optimistic removal: if any integration succeeds, we remove the batch's\n * events from the queue and emit it locally. Failures persist per-integration.\n */\n private sendBatchSync(batch: EventsQueue, eventIds: string[]): boolean {\n const results = this.dataSenders.map((sender) => sender.sendEventsQueueSync(batch));\n const anySucceeded = results.some((success) => success);\n\n if (anySucceeded) {\n this.removeProcessedEvents(eventIds);\n this.emitEventsQueue(batch);\n } else {\n log('debug', 'Sync send complete failure, events kept in queue for retry', {\n data: { eventCount: eventIds.length, sessionId: batch.session_id },\n });\n }\n\n return anySucceeded;\n }\n\n /**\n * Sends one batch asynchronously across all integrations (fetch path).\n */\n private async sendBatchAsync(batch: EventsQueue, eventIds: string[]): Promise<boolean> {\n const sendPromises = this.dataSenders.map(async (sender) =>\n sender.sendEventsQueue(batch, {\n onSuccess: () => {},\n onFailure: () => {},\n }),\n );\n\n const results = await Promise.allSettled(sendPromises);\n const anySucceeded = results.some((result) => this.isSuccessfulResult(result));\n\n if (anySucceeded) {\n this.removeProcessedEvents(eventIds);\n this.emitEventsQueue(batch);\n\n const failedCount = results.filter((result) => !this.isSuccessfulResult(result)).length;\n if (failedCount > 0) {\n log('debug', 'Async send completed with some failures, removed from queue and persisted per-integration', {\n data: { eventCount: eventIds.length, failedCount, sessionId: batch.session_id },\n });\n }\n } else {\n log('debug', 'Async send complete failure, events kept in queue for retry', {\n data: { eventCount: eventIds.length, sessionId: batch.session_id },\n });\n }\n\n return anySucceeded;\n }\n\n private async sendEventsQueue(): Promise<void> {\n if (this.eventsQueue.length === 0 || this.sendInProgress) {\n return;\n }\n\n this.sendInProgress = true;\n\n try {\n const planned = this.buildBatchesWithIds();\n if (planned.length === 0) return;\n\n if (this.dataSenders.length === 0) {\n // Standalone mode: emit each batch locally, but DO NOT clear the queue.\n // Periodic send is best-effort visibility for local listeners; events\n // remain queued so that an explicit `flushImmediately()` (manual\n // intent) can still consume them. Mirrors pre-refactor behaviour.\n for (const { batch } of planned) {\n this.emitEventsQueue(batch);\n }\n return;\n }\n\n const results = await Promise.all(\n planned.map(async ({ batch, eventIds }) => this.sendBatchAsync(batch, eventIds)),\n );\n const anySucceededOverall = results.some(Boolean);\n\n if (anySucceededOverall) {\n this.consecutiveSendFailures = 0;\n } else {\n this.consecutiveSendFailures = Math.min(this.consecutiveSendFailures + 1, MAX_CONSECUTIVE_SEND_FAILURES);\n }\n\n if (this.eventsQueue.length === 0) {\n this.clearSendTimeout();\n } else {\n this.scheduleSendTimeout();\n }\n } finally {\n this.sendInProgress = false;\n this.drainPendingSyncFlush();\n }\n }\n\n /**\n * Builds a single batch from a per-session group: dedup by signature,\n * SESSION_START first, then timestamp order, strip `_session_id`, apply\n * `beforeBatch` transformer when running standalone.\n *\n * **Why N batches per flush**: events freeze their `_session_id` at `track()`\n * time. If the session was renewed (idle timeout) between two `track()`\n * calls, the queue contains events from multiple sessions. `buildBatchesWithIds()`\n * emits one batch per session so the backend's `EventsQueueDto.session_id`\n * remains the single source of truth and stays consistent with the events it\n * carries.\n *\n * **Strip**: `_session_id` is removed from each event in the wrapper's\n * `events[]` because the backend uses `forbidNonWhitelisted: true` and would\n * reject the batch if the field leaked through.\n *\n * **Transformer note**: `beforeBatch` is invoked **once per session-batch**,\n * not once per flush. A queue spanning N sessions triggers N invocations.\n */\n private buildBatchFromGroup(sessionId: string, groupEvents: QueuedEvent[]): EventsQueue {\n // Per-batch dedup: collapse events sharing a signature into one slot.\n // `order` preserves the first occurrence's position, but `eventMap.set`\n // keeps the latest payload — so the most recent timestamp/metadata wins\n // while the batch's overall event sequence stays stable. Matches the\n // pre-refactor behavior in `buildEventsPayload`.\n const eventMap = new Map<string, QueuedEvent>();\n const order: string[] = [];\n for (const event of groupEvents) {\n const signature = this.createEventSignature(event);\n if (!eventMap.has(signature)) {\n order.push(signature);\n }\n eventMap.set(signature, event);\n }\n\n const events: EventData[] = order\n .map((signature) => eventMap.get(signature))\n .filter((event): event is QueuedEvent => Boolean(event))\n .sort((a, b) => {\n if (a.type === EventType.SESSION_START && b.type !== EventType.SESSION_START) return -1;\n if (b.type === EventType.SESSION_START && a.type !== EventType.SESSION_START) return 1;\n return a.timestamp - b.timestamp;\n })\n .map(({ _session_id, ...rest }) => {\n void _session_id;\n return rest;\n });\n\n const globalMetadata = this.get('config')?.globalMetadata;\n const identity = this.get('identity');\n\n const queue: EventsQueue = {\n user_id: this.get('userId'),\n session_id: sessionId,\n device: this.get('device'),\n events,\n ...(globalMetadata && { global_metadata: globalMetadata }),\n ...(identity && { identify: identity }),\n };\n\n return queue;\n }\n\n private buildEventPayload(data: Partial<EventData>): QueuedEvent | null {\n // Freeze the session ID at track() time. This is the single source of\n // truth for batch attribution — `groupQueuedEventsBySession()` groups by\n // `_session_id`, so the event survives session renewal (idle timeout)\n // without orphaning. Caller (`track()`) already routed events without a\n // sessionId to `pendingEventsBuffer`, so reaching this point with no\n // sessionId is an internal bug — log critical and bail.\n const currentSessionId = this.get('sessionId');\n if (!currentSessionId) {\n log('error', 'buildEventPayload reached without sessionId — event dropped', {\n data: { type: data.type },\n visibility: 'critical',\n });\n return null;\n }\n\n // Defense-in-depth: backend rejects page_url with @IsNotEmpty + URL validator\n // (accepts 'unknown' / 'excluded' as sentinels). normalizeUrl() can return ''\n // for sandboxed iframes / exotic browser contexts where window.location.href\n // is falsy — fall back to 'unknown' rather than ship an empty string and lose\n // the whole batch.\n const rawPageUrl = data.page_url ?? this.get('pageUrl');\n const currentPageUrl = typeof rawPageUrl === 'string' && rawPageUrl.length > 0 ? rawPageUrl : 'unknown';\n\n // Use TimeManager for accurate timestamps (immune to clock skew during session)\n const timestamp = this.timeManager.now();\n\n // Validate timestamp before creating event\n const validation = this.timeManager.validateTimestamp(timestamp);\n if (!validation.valid) {\n log('warn', 'Event timestamp validation failed', {\n data: { type: data.type, error: validation.error },\n });\n // Continue anyway with adjusted timestamp to avoid data loss\n // Backend has 3-minute tolerance as safety net\n }\n\n // Get session-level attribution from global state (captured once at SESSION_START)\n const sessionReferrer = this.get('sessionReferrer');\n const sessionUtm = this.get('sessionUtm');\n\n const payload: EventData = {\n id: generateEventId(),\n type: data.type as EventType,\n page_url: currentPageUrl,\n timestamp,\n ...(sessionReferrer && { referrer: sessionReferrer }),\n ...(data.from_page_url && { from_page_url: data.from_page_url }),\n ...(data.scroll_data && { scroll_data: data.scroll_data }),\n ...(data.click_data && { click_data: data.click_data }),\n ...(data.custom_event && { custom_event: data.custom_event }),\n ...(data.web_vitals && { web_vitals: data.web_vitals }),\n ...(data.error_data && { error_data: data.error_data }),\n ...(data.page_view && { page_view: data.page_view }),\n ...(sessionUtm && { utm: sessionUtm }),\n };\n\n return { ...payload, _session_id: currentSessionId };\n }\n\n private isDuplicateEvent(event: EventData): boolean {\n const now = Date.now();\n const fingerprint = this.createEventFingerprint(event);\n\n const lastSeen = this.recentEventFingerprints.get(fingerprint);\n\n if (lastSeen && now - lastSeen < DUPLICATE_EVENT_THRESHOLD_MS) {\n this.recentEventFingerprints.set(fingerprint, now);\n return true;\n }\n\n this.recentEventFingerprints.set(fingerprint, now);\n\n if (this.recentEventFingerprints.size > MAX_FINGERPRINTS) {\n this.pruneOldFingerprints();\n }\n\n if (this.recentEventFingerprints.size > MAX_FINGERPRINTS_HARD_LIMIT) {\n this.recentEventFingerprints.clear();\n this.recentEventFingerprints.set(fingerprint, now);\n\n log('debug', 'Event fingerprint cache exceeded hard limit, cleared', {\n data: { hardLimit: MAX_FINGERPRINTS_HARD_LIMIT },\n });\n }\n\n return false;\n }\n\n private pruneOldFingerprints(): void {\n const now = Date.now();\n const cutoff = DUPLICATE_EVENT_THRESHOLD_MS * FINGERPRINT_CLEANUP_MULTIPLIER;\n\n for (const [fingerprint, timestamp] of this.recentEventFingerprints.entries()) {\n if (now - timestamp > cutoff) {\n this.recentEventFingerprints.delete(fingerprint);\n }\n }\n\n log('debug', 'Pruned old event fingerprints', {\n data: {\n remaining: this.recentEventFingerprints.size,\n cutoffMs: cutoff,\n },\n });\n }\n\n private createEventFingerprint(event: EventData): string {\n let fingerprint = `${event.type}_${event.page_url}`;\n\n if (event.click_data) {\n const x = Math.round((event.click_data.x || 0) / 10) * 10;\n const y = Math.round((event.click_data.y || 0) / 10) * 10;\n fingerprint += `_click_${x}_${y}`;\n }\n\n if (event.scroll_data) {\n fingerprint += `_scroll_${event.scroll_data.depth}_${event.scroll_data.direction}`;\n }\n\n if (event.custom_event) {\n fingerprint += `_custom_${event.custom_event.name}`;\n if (event.custom_event.metadata) {\n fingerprint += `_${this.stableStringify(event.custom_event.metadata)}`;\n }\n }\n\n if (event.web_vitals) {\n fingerprint += `_vitals_${event.web_vitals.type}`;\n }\n\n if (event.error_data) {\n fingerprint += `_error_${event.error_data.type}_${event.error_data.message}`;\n }\n\n return fingerprint;\n }\n\n private createEventSignature(event: EventData): string {\n return this.createEventFingerprint(event);\n }\n\n /** Deterministic JSON string with sorted keys to ensure consistent fingerprints regardless of property insertion order */\n private stableStringify(value: unknown): string {\n return JSON.stringify(value, (_, v: unknown) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n return Object.keys(v as Record<string, unknown>)\n .sort()\n .reduce<Record<string, unknown>>((sorted, key) => {\n sorted[key] = (v as Record<string, unknown>)[key];\n return sorted;\n }, {});\n }\n return v;\n });\n }\n\n private addToQueue(event: QueuedEvent): void {\n this.emitEvent(event);\n\n this.eventsQueue.push(event);\n\n if (this.eventsQueue.length > MAX_EVENTS_QUEUE_LENGTH) {\n const nonCriticalIndex = this.eventsQueue.findIndex((e) => e.type !== EventType.SESSION_START);\n\n const removedEvent =\n nonCriticalIndex >= 0 ? this.eventsQueue.splice(nonCriticalIndex, 1)[0] : this.eventsQueue.shift();\n\n log('warn', 'Event queue overflow, oldest non-critical event removed', {\n data: {\n maxLength: MAX_EVENTS_QUEUE_LENGTH,\n currentLength: this.eventsQueue.length,\n removedEventType: removedEvent?.type,\n wasCritical: removedEvent?.type === EventType.SESSION_START,\n },\n });\n }\n\n this.scheduleSendTimeout();\n\n if (\n this.eventsQueue.length >= BATCH_SIZE_THRESHOLD &&\n this.consecutiveSendFailures < MAX_CONSECUTIVE_SEND_FAILURES\n ) {\n void this.sendEventsQueue();\n }\n }\n\n private scheduleSendTimeout(): void {\n if (this.sendTimeoutId !== null) return;\n\n const delay = this.calculateSendDelay();\n this.sendTimeoutId = window.setTimeout(() => {\n this.sendTimeoutId = null;\n if (this.eventsQueue.length > 0) {\n void this.sendEventsQueue();\n }\n }, delay);\n }\n\n private calculateSendDelay(): number {\n const baseInterval = this.get('config')?.sendIntervalMs ?? EVENT_SENT_INTERVAL_MS;\n if (this.consecutiveSendFailures === 0) return baseInterval;\n const backoff = baseInterval * Math.pow(2, this.consecutiveSendFailures);\n return Math.min(backoff, MAX_SEND_INTERVAL_MS);\n }\n\n private shouldSample(): boolean {\n const samplingRate = this.get('config')?.samplingRate ?? 1;\n return Math.random() < samplingRate;\n }\n\n private checkRateLimit(): boolean {\n const now = Date.now();\n\n if (now - this.rateLimitWindowStart > RATE_LIMIT_WINDOW_MS) {\n this.rateLimitCounter = 0;\n this.rateLimitWindowStart = now;\n }\n\n if (this.rateLimitCounter >= MAX_EVENTS_PER_SECOND) {\n return false;\n }\n\n this.rateLimitCounter++;\n return true;\n }\n\n private checkPerEventRateLimit(eventName: string, maxSameEventPerMinute: number): boolean {\n const now = Date.now();\n const timestamps = this.perEventRateLimits.get(eventName) ?? [];\n\n const validTimestamps = timestamps.filter((ts) => now - ts < PER_EVENT_RATE_LIMIT_WINDOW_MS);\n\n if (validTimestamps.length >= maxSameEventPerMinute) {\n log('warn', 'Per-event rate limit exceeded for custom event', {\n data: {\n eventName,\n limit: maxSameEventPerMinute,\n window: `${PER_EVENT_RATE_LIMIT_WINDOW_MS / 1000}s`,\n },\n });\n return false;\n }\n\n validTimestamps.push(now);\n this.perEventRateLimits.set(eventName, validTimestamps);\n\n return true;\n }\n\n private getTypeLimitForEvent(type: EventType): number | null {\n const limits: Partial<Record<EventType, number>> = {\n [EventType.CLICK]: MAX_CLICKS_PER_SESSION,\n [EventType.PAGE_VIEW]: MAX_PAGE_VIEWS_PER_SESSION,\n [EventType.CUSTOM]: MAX_CUSTOM_EVENTS_PER_SESSION,\n [EventType.SCROLL]: MAX_SCROLL_EVENTS_PER_SESSION,\n };\n return limits[type] ?? null;\n }\n\n private removeProcessedEvents(eventIds: string[]): void {\n const eventIdSet = new Set(eventIds);\n\n this.eventsQueue = this.eventsQueue.filter((event) => {\n return !eventIdSet.has(event.id);\n });\n }\n\n private emitEvent(eventData: EventData | QueuedEvent): void {\n if (this.emitter) {\n // Strip the internal `_session_id` field before exposing the event to\n // public listeners. The wrapper EventsQueue carries session_id separately.\n const { _session_id, ...publicEvent } = eventData as QueuedEvent;\n void _session_id;\n this.emitter.emit(EmitterEvent.EVENT, publicEvent as EventData);\n }\n }\n\n private emitEventsQueue(queue: EventsQueue): void {\n if (this.emitter) {\n this.emitter.emit(EmitterEvent.QUEUE, queue);\n }\n }\n\n /**\n * Creates a debounced version of a function that delays execution until after\n * a specified wait time has elapsed since the last invocation.\n *\n * **Purpose**: Reduces frequency of expensive operations (localStorage writes)\n * while ensuring no data is lost (trailing edge execution).\n *\n * **Behavior**:\n * - Each call resets the timer\n * - Function executes only after `delay` ms of silence\n * - Last invocation always executes (trailing edge)\n *\n * **Use Case**: Batches rapid successive calls into a single execution\n *\n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced version of the function\n *\n * @internal\n */\n private debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return ((...args: Parameters<T>) => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n }) as T;\n }\n\n /**\n * Returns initial zero counts for session event tracking.\n *\n * **Purpose**: DRY helper to avoid duplicating initial counts structure\n * across multiple methods (loadSessionCounts, validation fallbacks).\n *\n * @returns Fresh SessionEventCounts object with all counters at zero\n *\n * @internal\n */\n private getInitialCounts(): SessionEventCounts {\n return {\n total: 0,\n [EventType.CLICK]: 0,\n [EventType.PAGE_VIEW]: 0,\n [EventType.CUSTOM]: 0,\n [EventType.SCROLL]: 0,\n };\n }\n\n /**\n * Loads persisted session event counts from localStorage.\n *\n * **Purpose**: Restore per-session event counts after page reload to maintain\n * accurate rate limiting across page navigations within the same session.\n *\n * **Behavior**:\n * - Attempts to load counts from localStorage using session ID as key\n * - If no persisted data found: Returns initial zero counts\n * - If corrupted data found: Returns initial zero counts with warning\n *\n * **Storage Key**: `tlog:{userId}:session_counts:{sessionId}`\n *\n * **Why This Matters**:\n * - Without persistence, counts reset on every page reload\n * - This allows users to bypass per-session limits by refreshing the page\n * - Example: 100 PAGE_VIEW limit could be bypassed by reloading after 99 events\n *\n * @param sessionId - Current session identifier\n * @returns Session event counts object (either persisted or initial state)\n *\n * @internal\n */\n private loadSessionCounts(sessionId: string): SessionEventCounts {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n return this.getInitialCounts();\n }\n\n const userId = this.get('userId') || 'anonymous';\n const storageKey = SESSION_COUNTS_KEY(userId, sessionId);\n\n try {\n const stored = localStorage.getItem(storageKey);\n\n if (!stored) {\n // No persisted data - this is a new session or first page load\n return this.getInitialCounts();\n }\n\n const parsed = JSON.parse(stored) as Partial<StoredSessionCounts>;\n\n // Check for expiry (7 days)\n if (parsed._timestamp && Date.now() - parsed._timestamp > SESSION_COUNTS_EXPIRY_MS) {\n log('debug', 'Session counts expired, clearing', {\n data: { sessionId, age: Date.now() - parsed._timestamp },\n });\n localStorage.removeItem(storageKey);\n return this.getInitialCounts();\n }\n\n // Robust validation: check all required fields (not just 'total')\n // Prevents partial/corrupted data from causing runtime errors\n if (\n typeof parsed.total === 'number' &&\n typeof parsed[EventType.CLICK] === 'number' &&\n typeof parsed[EventType.PAGE_VIEW] === 'number' &&\n typeof parsed[EventType.CUSTOM] === 'number' &&\n typeof parsed[EventType.SCROLL] === 'number'\n ) {\n // Return only the SessionEventCounts fields (exclude _timestamp, _version)\n return {\n total: parsed.total,\n [EventType.CLICK]: parsed[EventType.CLICK],\n [EventType.PAGE_VIEW]: parsed[EventType.PAGE_VIEW],\n [EventType.CUSTOM]: parsed[EventType.CUSTOM],\n [EventType.SCROLL]: parsed[EventType.SCROLL],\n };\n }\n\n log('warn', 'Invalid session counts structure in localStorage, resetting', {\n data: { sessionId, parsed },\n });\n localStorage.removeItem(storageKey);\n log('debug', 'Session counts removed due to invalid/corrupted data', {\n data: { sessionId, parsed },\n });\n\n return this.getInitialCounts();\n } catch (error) {\n log('warn', 'Failed to load session counts from localStorage', {\n error,\n data: { sessionId },\n });\n\n return this.getInitialCounts();\n }\n }\n\n /**\n * Cleans up expired session counts from localStorage.\n *\n * **Purpose**: Prevents localStorage pollution from abandoned sessions by removing\n * counts older than 7 days.\n *\n * **Behavior**:\n * - Checks if cleanup was run recently (within last hour) and skips if so\n * - Iterates all localStorage keys matching the session counts prefix pattern\n * - Parses each entry and checks `_timestamp` field\n * - Removes entries where age exceeds SESSION_COUNTS_EXPIRY_MS (7 days)\n * - Silently ignores parse errors (corrupted entries cleaned on next load)\n * - Updates last cleanup timestamp after successful run\n *\n * **When Called**: Automatically on EventManager constructor initialization\n *\n * **Performance**: O(n) scan where n = localStorage keys (typically <100), but throttled\n * to run at most once per hour to prevent impact on rapid page reloads\n *\n * @internal\n */\n private cleanupExpiredSessionCounts(): void {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n return;\n }\n\n try {\n // Throttling: Skip if cleanup ran recently (within last hour)\n const lastCleanup = localStorage.getItem(SESSION_COUNTS_LAST_CLEANUP_KEY);\n\n if (lastCleanup) {\n const timeSinceLastCleanup = Date.now() - parseInt(lastCleanup, 10);\n\n if (timeSinceLastCleanup < SESSION_COUNTS_CLEANUP_THROTTLE_MS) {\n log('debug', 'Skipping session counts cleanup (throttled)', {\n data: { timeSinceLastCleanup, throttleMs: SESSION_COUNTS_CLEANUP_THROTTLE_MS },\n });\n\n return;\n }\n }\n\n const userId = this.get('userId') || 'anonymous';\n const prefix = `${STORAGE_BASE_KEY}:${userId}:session_counts:`;\n\n // Collect keys to remove (can't modify during iteration)\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n\n if (key?.startsWith(prefix)) {\n try {\n const stored = localStorage.getItem(key);\n\n if (stored) {\n const parsed = JSON.parse(stored) as Partial<StoredSessionCounts>;\n\n // Check expiry\n if (parsed._timestamp && Date.now() - parsed._timestamp > SESSION_COUNTS_EXPIRY_MS) {\n keysToRemove.push(key);\n }\n }\n } catch {\n // Ignore parse errors, will be cleaned on next load\n }\n }\n }\n\n // Remove expired entries\n keysToRemove.forEach((key) => {\n localStorage.removeItem(key);\n log('debug', 'Cleaned up expired session counts', { data: { key } });\n });\n\n if (keysToRemove.length > 0) {\n log('info', `Cleaned up ${keysToRemove.length} expired session counts entries`);\n }\n\n // Update last cleanup timestamp\n localStorage.setItem(SESSION_COUNTS_LAST_CLEANUP_KEY, Date.now().toString());\n } catch (error) {\n log('warn', 'Failed to cleanup expired session counts', { error });\n }\n }\n\n /**\n * Persists current session event counts to localStorage (debounced).\n *\n * **Purpose**: Save event counts to ensure they survive page reloads and\n * maintain accurate per-session rate limiting across navigations.\n *\n * **Behavior**:\n * - Saves current `sessionEventCounts` to localStorage using session ID as key\n * - Overwrites previous counts (always reflects latest state)\n * - Fails silently if localStorage quota exceeded or unavailable\n *\n * **Storage Key**: `tlog:{userId}:session_counts:{sessionId}`\n *\n * **Debouncing**: This method is called via `saveSessionCountsDebounced()`\n * with 500ms debounce delay. Direct calls are for immediate saves (e.g., stop()).\n *\n * **Performance**: Debouncing reduces localStorage writes from ~1000 per session\n * to ~20-30 (96-97% reduction) while maintaining data integrity.\n *\n * **Cleanup**: Counts persist across page reloads for rate limiting enforcement.\n * Automatic cleanup on page reload removes expired counts (older than 7 days).\n * Note: SESSION_COUNTS_KEY entries are intentionally persistent to maintain\n * rate limits across sessions (~100 bytes per session).\n *\n * @param sessionId - Current session identifier\n *\n * @internal\n */\n private saveSessionCounts(sessionId: string): void {\n const userId = this.get('userId') || 'anonymous';\n const storageKey = SESSION_COUNTS_KEY(userId, sessionId);\n\n try {\n const dataToStore: StoredSessionCounts = {\n ...this.sessionEventCounts,\n _timestamp: Date.now(),\n _version: 1,\n };\n\n localStorage.setItem(storageKey, JSON.stringify(dataToStore));\n } catch (error) {\n log('warn', 'Failed to persist session counts to localStorage', {\n error,\n data: { sessionId },\n });\n }\n }\n}\n","import { USER_ID_KEY } from '../constants';\nimport { generateUUID } from '../utils';\nimport { StorageManager } from './storage.manager';\n\n/**\n * Simple utility for managing unique user identification for analytics tracking.\n *\n * **Purpose**: Creates and persists RFC4122-compliant UUID v4 identifiers\n * for tracking users across browser sessions.\n *\n * **Core Functionality**:\n * - **User ID Generation**: Creates UUID v4 identifiers\n * - **Persistence**: Stores user IDs in localStorage with automatic fallback\n * - **Session Continuity**: Reuses existing user IDs across browser sessions\n * - **Global User Identity**: Single user ID shared across all TraceLog projects in the same browser\n *\n * **Key Features**:\n * - Static utility method pattern (no object instantiation required)\n * - UUID v4 generation for globally unique identifiers\n * - Fixed storage key (`tlog:uid`) for consistent identification across projects\n * - Automatic fallback to memory storage when localStorage unavailable\n * - Minimal dependencies and zero allocation approach\n *\n * **Storage**: `tlog:uid` (fixed, not project-scoped)\n *\n * @see src/managers/README.md (lines 227-252) for detailed documentation\n *\n * @example\n * ```typescript\n * const userId = UserManager.getId(storageManager);\n * // Returns: '550e8400-e29b-41d4-a716-446655440000' (UUID v4)\n *\n * // Subsequent calls return the same ID\n * const sameUserId = UserManager.getId(storageManager);\n * // Returns: '550e8400-e29b-41d4-a716-446655440000' (persisted)\n * ```\n */\nexport class UserManager {\n /**\n * Gets or creates a unique user ID.\n *\n * **Behavior**:\n * 1. Checks localStorage for existing user ID\n * 2. Returns existing ID if found\n * 3. Generates new RFC4122-compliant UUID v4 if not found\n * 4. Persists new ID to localStorage\n *\n * **Storage Key**: `tlog:uid` (fixed, shared across all TraceLog projects)\n *\n * **ID Format**: UUID v4 (e.g., `550e8400-e29b-41d4-a716-446655440000`)\n *\n * @param storageManager - Storage manager instance for persistence\n * @returns Persistent unique user ID (UUID v4 format)\n */\n static getId(storageManager: StorageManager): string {\n const storedUserId = storageManager.getItem(USER_ID_KEY);\n\n if (storedUserId) {\n return storedUserId;\n }\n\n const newUserId = generateUUID();\n storageManager.setItem(USER_ID_KEY, newUserId);\n\n return newUserId;\n }\n}\n","import { BROADCAST_CHANNEL_NAME, DEFAULT_SESSION_TIMEOUT, SESSION_STORAGE_KEY } from '../constants';\nimport { EventType, UTM } from '../types';\nimport { getExternalReferrer, getUTMParameters, log } from '../utils';\nimport { StateManager } from './state.manager';\nimport { StorageManager } from './storage.manager';\nimport { EventManager } from './event.manager';\n\ninterface StoredSessionData {\n id: string;\n lastActivity: number;\n referrer?: string;\n utm?: UTM;\n}\n\n/**\n * Session ID validation pattern: {timestamp}-{9-char-base36}\n *\n * Validates recovered session IDs to prevent data corruption from:\n * - Manual localStorage manipulation by users or browser extensions\n * - Race conditions in cross-tab scenarios (partial/truncated writes)\n * - Storage corruption or quota-based truncation\n * - Malformed IDs from older library versions or external interference\n *\n * Format: 13-digit timestamp + hyphen + 9 lowercase alphanumeric characters\n * Example: 1704067200000-a3f9c2b5d\n */\nconst SESSION_ID_PATTERN = /^\\d{13}-[a-z0-9]{9}$/;\n\n/**\n * Manages user sessions with cross-tab synchronization, inactivity detection,\n * and automatic lifecycle tracking.\n *\n * **Purpose**: Creates and manages user sessions, tracking session start\n * with automatic persistence, recovery, and multi-tab synchronization.\n * Does not track session end events (removed in v2.0.0).\n *\n * **Core Functionality**:\n * - **Session Generation**: Creates unique session IDs (`{timestamp}-{9-char-base36}`)\n * - **Activity Tracking**: Monitors user interactions to extend session timeout\n * - **Cross-Tab Sync**: BroadcastChannel synchronization across browser tabs\n * - **Persistence**: Stores session data in localStorage for recovery\n * - **Session Mirror**: Automatically mirrors session to sessionStorage for recovery after external redirects\n * - **Inactivity Detection**: Automatic timeout after inactivity (default 15 minutes)\n * - **Lifecycle Events**: Emits SESSION_START event only (SESSION_END removed in v2.0.0)\n *\n * **Key Features**:\n * - **Session ID Format**: `{timestamp}-{9-char-base36}` (e.g., `1704896400000-a3b4c5d6e`)\n * - **Default Timeout**: 15 minutes (900,000 ms), configurable via `sessionTimeout`\n * - **Cross-Tab Sharing**: Primary tab creates session, shares via BroadcastChannel\n * - **Secondary Tab Behavior**: Receives session from primary tab, no SESSION_START event\n * - **No Session End Events**: Library only tracks SESSION_START (v2.0.0+)\n *\n * **BroadcastChannel Integration**:\n * - **Initialized BEFORE SESSION_START**: Prevents race condition with secondary tabs\n * - **Messages**: Only `session_start` action (share session across tabs)\n * - **Fallback**: Logs warning if BroadcastChannel not supported (no cross-tab sync)\n *\n * **Activity Detection**:\n * - Tracks user interactions via listener managers (mouse, keyboard, touch, scroll, etc.)\n * - Resets inactivity timeout on each activity\n * - Updates `lastActivity` timestamp in localStorage\n *\n * **State Management**:\n * - **`sessionId`**: Current session ID stored in global state\n *\n * @see src/managers/README.md (lines 140-169) for detailed documentation\n *\n * @example\n * ```typescript\n * const sessionManager = new SessionManager(storage, eventManager, 'project123');\n *\n * // Start session tracking\n * sessionManager.startTracking();\n * // → Creates session ID: '1704896400000-a3b4c5d6e'\n * // → Emits SESSION_START event\n * // → Sets up activity listeners\n * // → Initializes cross-tab sync\n *\n * // User activity extends session\n * // (automatic via activity listeners)\n *\n * // Stop tracking (cleanup only)\n * sessionManager.stopTracking();\n * // → Cleans up listeners and timers\n * // → No SESSION_END event emitted\n * ```\n */\nexport class SessionManager extends StateManager {\n private readonly storageManager: StorageManager;\n private readonly eventManager: EventManager;\n private readonly projectId: string;\n\n private activityHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n private sessionTimeoutId: ReturnType<typeof setTimeout> | null = null;\n private broadcastChannel: BroadcastChannel | null = null;\n private isTracking = false;\n private needsRenewal = false;\n\n /**\n * Creates a SessionManager instance.\n *\n * @param storageManager - Storage manager for session persistence\n * @param eventManager - Event manager for SESSION_START events\n * @param projectId - Project identifier for namespacing session storage\n */\n constructor(storageManager: StorageManager, eventManager: EventManager, projectId: string) {\n super();\n this.storageManager = storageManager;\n this.eventManager = eventManager;\n this.projectId = projectId;\n }\n\n private initCrossTabSync(): void {\n if (typeof BroadcastChannel === 'undefined') {\n log('debug', 'BroadcastChannel not supported');\n return;\n }\n\n const projectId = this.getProjectId();\n this.broadcastChannel = new BroadcastChannel(BROADCAST_CHANNEL_NAME(projectId));\n\n this.broadcastChannel.onmessage = (event): void => {\n const { action, sessionId, timestamp, projectId: messageProjectId } = event.data ?? {};\n\n if (messageProjectId !== projectId) {\n return;\n }\n\n if (action === 'session_start' && sessionId && typeof timestamp === 'number' && timestamp > Date.now() - 5000) {\n this.set('sessionId', sessionId);\n this.persistSession(sessionId, timestamp);\n if (this.isTracking) {\n this.setupSessionTimeout();\n }\n } else if (action && action !== 'session_start') {\n // Log unknown action types to help with debugging cross-tab synchronization issues\n log('debug', 'Ignored BroadcastChannel message with unknown action', { data: { action } });\n }\n };\n }\n\n private shareSession(sessionId: string): void {\n if (this.broadcastChannel && typeof this.broadcastChannel.postMessage === 'function') {\n this.broadcastChannel.postMessage({\n action: 'session_start',\n projectId: this.getProjectId(),\n sessionId,\n timestamp: Date.now(),\n });\n }\n }\n\n private cleanupCrossTabSync(): void {\n if (this.broadcastChannel) {\n if (typeof this.broadcastChannel.close === 'function') {\n this.broadcastChannel.close();\n }\n this.broadcastChannel = null;\n }\n }\n\n private recoverSession(): string | null {\n const storedSession = this.loadStoredSession();\n\n if (!storedSession) {\n return null;\n }\n\n // Validate session ID format: {timestamp}-{9-char-base36}\n if (!SESSION_ID_PATTERN.test(storedSession.id)) {\n log('warn', 'Invalid session ID format recovered from storage, clearing', {\n data: { sessionId: storedSession.id },\n });\n this.clearStoredSession();\n return null;\n }\n\n const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n\n if (Date.now() - storedSession.lastActivity > sessionTimeout) {\n this.clearStoredSession();\n return null;\n }\n\n return storedSession.id;\n }\n\n private persistSession(sessionId: string, lastActivity: number = Date.now(), referrer?: string, utm?: UTM): void {\n this.saveStoredSession({\n id: sessionId,\n lastActivity,\n ...(referrer && { referrer }),\n ...(utm && { utm }),\n });\n }\n\n private clearStoredSession(): void {\n const storageKey = this.getSessionStorageKey();\n this.storageManager.removeItem(storageKey);\n // sessionStorage mirror is intentionally NOT cleared here.\n // It persists to enable recovery after external redirects (payment processors, OAuth).\n // Stale data is rejected by the timeout check in recoverSession().\n // sessionStorage auto-clears when the tab closes.\n }\n\n private loadStoredSession(): StoredSessionData | null {\n const storageKey = this.getSessionStorageKey();\n\n // Primary: localStorage (cross-tab, persistent)\n const localData = this.storageManager.getItem(storageKey);\n if (localData !== null) {\n try {\n const parsed = JSON.parse(localData) as StoredSessionData;\n if (parsed.id && typeof parsed.lastActivity === 'number') {\n return parsed;\n }\n } catch {\n this.storageManager.removeItem(storageKey);\n }\n }\n\n // Fallback: sessionStorage (survives same-tab external redirects)\n const sessionData = this.storageManager.getSessionItem(storageKey);\n if (sessionData !== null) {\n try {\n const parsed = JSON.parse(sessionData) as StoredSessionData;\n if (parsed.id && typeof parsed.lastActivity === 'number') {\n return parsed;\n }\n } catch {\n this.storageManager.removeSessionItem(storageKey);\n }\n }\n\n return null;\n }\n\n private saveStoredSession(session: StoredSessionData): void {\n const storageKey = this.getSessionStorageKey();\n const data = JSON.stringify(session);\n this.storageManager.setItem(storageKey, data);\n this.storageManager.setSessionItem(storageKey, data);\n }\n\n private getSessionStorageKey(): string {\n return SESSION_STORAGE_KEY(this.getProjectId());\n }\n\n private getProjectId(): string {\n return this.projectId;\n }\n\n /**\n * Starts session tracking with lifecycle management and cross-tab synchronization.\n *\n * **Purpose**: Initializes session tracking, creating or recovering a session ID,\n * setting up activity listeners, and enabling cross-tab synchronization.\n *\n * **Flow**:\n * 1. Checks if tracking already active (idempotent)\n * 2. Attempts to recover session from localStorage\n * 3. If no recovery: Generates new session ID (`{timestamp}-{9-char-base36}`)\n * 4. Sets `sessionId` in global state\n * 5. Persists session to localStorage\n * 6. Initializes BroadcastChannel for cross-tab sync (BEFORE SESSION_START)\n * 7. Shares session via BroadcastChannel (notifies other tabs)\n * 8. If NOT recovered: Tracks SESSION_START event\n * 9. Sets up inactivity timeout (default 15 minutes)\n * 10. Sets up activity listeners (click, keydown, scroll)\n * 11. Sets up lifecycle listeners (visibilitychange, beforeunload)\n *\n * **Session Recovery**:\n * - Checks localStorage for existing session (primary)\n * - Falls back to sessionStorage mirror (survives external redirects)\n * - Recovers if session exists and is recent (within timeout window)\n * - NO SESSION_START event if session recovered\n *\n * **Error Handling**:\n * - On error: Rolls back all setup (cleanup listeners, timers, state)\n * - Re-throws error to caller (App.init() handles failure)\n *\n * **BroadcastChannel Initialization Order**:\n * - CRITICAL: BroadcastChannel initialized BEFORE SESSION_START event\n * - Prevents race condition with secondary tabs\n * - Ensures secondary tabs can receive session_start message\n *\n * **Called by**: `SessionHandler.startTracking()` during `App.init()`\n *\n * **Important**: After successful call, `sessionId` is available in global state\n * and EventManager can flush pending events via `flushPendingEvents()`.\n *\n * @throws Error if initialization fails (rolled back automatically)\n *\n * @example\n * ```typescript\n * sessionManager.startTracking();\n * // → Session created: '1704896400000-a3b4c5d6e'\n * // → SESSION_START event tracked\n * // → Activity listeners active\n * // → Cross-tab sync enabled\n * ```\n *\n * @see src/managers/README.md (lines 140-169) for session management details\n */\n startTracking(): void {\n if (this.isTracking) {\n log('debug', 'Session tracking already active');\n return;\n }\n\n const recoveredSessionId = this.recoverSession();\n const sessionId = recoveredSessionId ?? this.generateSessionId();\n\n // Capture or recover attribution data (referrer/UTM)\n let sessionReferrer: string;\n let sessionUtm: UTM | undefined;\n\n if (recoveredSessionId) {\n // Session recovered: load attribution from storage (with fallback to current context)\n const storedSession = this.loadStoredSession();\n sessionReferrer = storedSession?.referrer ?? getExternalReferrer();\n sessionUtm = storedSession?.utm ?? getUTMParameters();\n } else {\n // New session: capture from current page context\n sessionReferrer = getExternalReferrer();\n sessionUtm = getUTMParameters();\n }\n\n log('debug', 'Session tracking initialized', {\n data: {\n sessionId,\n wasRecovered: !!recoveredSessionId,\n willEmitSessionStart: !recoveredSessionId,\n sessionReferrer,\n hasUtm: !!sessionUtm,\n },\n });\n\n this.isTracking = true;\n\n try {\n this.set('sessionId', sessionId);\n this.set('sessionReferrer', sessionReferrer);\n this.set('sessionUtm', sessionUtm);\n this.persistSession(sessionId, Date.now(), sessionReferrer, sessionUtm);\n this.initCrossTabSync();\n this.shareSession(sessionId);\n\n // Only emit SESSION_START for NEW sessions (not recovered)\n // Recovered sessions already had their SESSION_START tracked on initial creation\n // Server infers session end from last event timestamp (v2.0+)\n if (!recoveredSessionId) {\n log('debug', 'Emitting SESSION_START event', {\n data: { sessionId },\n });\n\n this.eventManager.track({\n type: EventType.SESSION_START,\n });\n } else {\n log('debug', 'Session recovered, skipping SESSION_START', {\n data: { sessionId },\n });\n }\n\n this.setupSessionTimeout();\n this.setupActivityListeners();\n this.setupLifecycleListeners();\n } catch (error) {\n this.isTracking = false;\n this.clearSessionTimeout();\n this.cleanupActivityListeners();\n this.cleanupLifecycleListeners();\n this.cleanupCrossTabSync();\n this.set('sessionId', null);\n\n throw error;\n }\n }\n\n private generateSessionId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private setupSessionTimeout(): void {\n this.clearSessionTimeout();\n\n const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n\n this.sessionTimeoutId = setTimeout(() => {\n this.enterRenewalMode();\n }, sessionTimeout);\n }\n\n private resetSessionTimeout(): void {\n this.setupSessionTimeout();\n const sessionId = this.get('sessionId') as string;\n if (sessionId) {\n this.persistSession(sessionId, Date.now(), this.get('sessionReferrer'), this.get('sessionUtm'));\n }\n }\n\n private clearSessionTimeout(): void {\n if (this.sessionTimeoutId) {\n clearTimeout(this.sessionTimeoutId);\n this.sessionTimeoutId = null;\n }\n }\n\n private setupActivityListeners(): void {\n this.activityHandler = (): void => {\n if (this.needsRenewal) {\n this.renewSession();\n } else {\n this.resetSessionTimeout();\n }\n };\n\n document.addEventListener('click', this.activityHandler, { passive: true });\n document.addEventListener('keydown', this.activityHandler, { passive: true });\n document.addEventListener('scroll', this.activityHandler, { passive: true });\n }\n\n /**\n * Renews the session after timeout when user returns.\n * Creates a new session ID and emits SESSION_START.\n */\n private renewSession(): void {\n this.needsRenewal = false;\n\n const newSessionId = this.generateSessionId();\n const sessionReferrer = getExternalReferrer();\n const sessionUtm = getUTMParameters();\n\n log('debug', 'Renewing session after timeout', {\n data: { newSessionId },\n });\n\n this.set('sessionId', newSessionId);\n this.set('sessionReferrer', sessionReferrer);\n this.set('sessionUtm', sessionUtm);\n this.persistSession(newSessionId, Date.now(), sessionReferrer, sessionUtm);\n\n // Re-initialize cross-tab sync with new session\n this.cleanupCrossTabSync();\n this.initCrossTabSync();\n this.shareSession(newSessionId);\n\n this.eventManager.track({\n type: EventType.SESSION_START,\n });\n\n // Flush any events that were buffered during renewal mode\n this.eventManager.flushPendingEvents();\n\n this.setupSessionTimeout();\n }\n\n private cleanupActivityListeners(): void {\n if (this.activityHandler) {\n document.removeEventListener('click', this.activityHandler);\n document.removeEventListener('keydown', this.activityHandler);\n document.removeEventListener('scroll', this.activityHandler);\n this.activityHandler = null;\n }\n }\n\n private setupLifecycleListeners(): void {\n if (this.visibilityChangeHandler) {\n return;\n }\n\n this.visibilityChangeHandler = (): void => {\n if (document.hidden) {\n this.clearSessionTimeout();\n } else {\n // Check if session expired during browser suspend/hibernate\n if (this.isSessionStale()) {\n log('debug', 'Session expired during suspend, entering renewal mode');\n this.enterRenewalMode();\n return;\n }\n\n const sessionId = this.get('sessionId');\n if (sessionId) {\n this.setupSessionTimeout();\n }\n }\n };\n\n document.addEventListener('visibilitychange', this.visibilityChangeHandler);\n }\n\n /**\n * Checks if the current session has become stale (expired during browser suspend).\n * This handles the case where JavaScript timers are paused during suspend/hibernate.\n */\n private isSessionStale(): boolean {\n // If already in renewal mode, no need to check\n if (this.needsRenewal) {\n return false;\n }\n\n const sessionId = this.get('sessionId');\n if (!sessionId) {\n return false;\n }\n\n const storedSession = this.loadStoredSession();\n if (!storedSession) {\n return false;\n }\n\n const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n return Date.now() - storedSession.lastActivity > sessionTimeout;\n }\n\n private cleanupLifecycleListeners(): void {\n if (this.visibilityChangeHandler) {\n document.removeEventListener('visibilitychange', this.visibilityChangeHandler);\n this.visibilityChangeHandler = null;\n }\n }\n\n /**\n * Enters renewal mode after session timeout.\n * Keeps activity listeners active to detect when user returns.\n * Called by session timeout timer.\n */\n private enterRenewalMode(): void {\n this.clearSessionTimeout();\n this.cleanupCrossTabSync();\n this.clearStoredSession();\n\n this.set('sessionId', null);\n this.set('hasStartSession', false);\n this.set('sessionReferrer', undefined);\n this.set('sessionUtm', undefined);\n\n // Keep activity listeners active but switch to renewal mode\n this.needsRenewal = true;\n\n log('debug', 'Session timed out, entering renewal mode');\n }\n\n /**\n * Fully resets session state and cleans up all resources.\n * Called by stopTracking() for explicit session termination.\n */\n private resetSessionState(): void {\n this.clearSessionTimeout();\n this.cleanupActivityListeners();\n this.cleanupLifecycleListeners();\n this.cleanupCrossTabSync();\n this.clearStoredSession();\n\n this.set('sessionId', null);\n this.set('hasStartSession', false);\n this.set('sessionReferrer', undefined);\n this.set('sessionUtm', undefined);\n\n this.needsRenewal = false;\n this.isTracking = false;\n }\n\n /**\n * Stops session tracking and cleans up all resources.\n *\n * **Purpose**: Manually stops the current session tracking and cleans up\n * all listeners and timers. Does not emit SESSION_END event (removed in v2.0.0).\n *\n * **Flow**:\n * 1. Clears inactivity timeout\n * 2. Removes activity listeners (click, keydown, scroll)\n * 3. Removes lifecycle listeners (visibilitychange)\n * 4. Closes BroadcastChannel\n * 5. Clears session from localStorage\n * 6. Resets `sessionId` and `hasStartSession` in global state\n * 7. Sets `isTracking` to false\n *\n * **Called by**: `App.destroy()` during application teardown or when session times out\n *\n * **Important**: After calling, session tracking is terminated and cannot be resumed.\n * A new session will be created on next `startTracking()` call.\n *\n * @example\n * ```typescript\n * // Stop session tracking\n * sessionManager.stopTracking();\n * // → All listeners cleaned up\n * // → Session cleared from localStorage\n * // → BroadcastChannel closed\n * // → No SESSION_END event emitted\n * ```\n *\n * @see src/managers/README.md (lines 140-169) for session management details\n */\n stopTracking(): void {\n this.resetSessionState();\n }\n\n /**\n * Destroys the session manager and cleans up all resources.\n *\n * **Purpose**: Performs deep cleanup of session manager resources during\n * application teardown. Preserves session in localStorage for recovery.\n *\n * **Differences from stopTracking()**:\n * - Does NOT clear localStorage (preserves session for recovery)\n * - Used for internal cleanup during teardown\n *\n * **Cleanup Flow**:\n * 1. Clears inactivity timeout\n * 2. Removes activity listeners (click, keydown, scroll)\n * 3. Closes BroadcastChannel\n * 4. Removes lifecycle listeners (visibilitychange)\n * 5. Resets tracking flag (`isTracking`)\n *\n * **Called by**: `App.destroy()` during application teardown\n *\n * @returns void\n *\n * @example\n * ```typescript\n * sessionManager.destroy();\n * // → All resources cleaned up\n * // → Session preserved in localStorage for recovery\n * ```\n */\n destroy(): void {\n this.clearSessionTimeout();\n this.cleanupActivityListeners();\n this.cleanupCrossTabSync();\n this.cleanupLifecycleListeners();\n this.isTracking = false;\n this.needsRenewal = false;\n this.set('hasStartSession', false);\n }\n}\n","import { EventManager } from '../managers/event.manager';\nimport { SessionManager } from '../managers/session.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { StorageManager } from '../managers/storage.manager';\nimport { log } from '../utils';\n\n/**\n * Wrapper around SessionManager providing consistent handler interface with robust error handling.\n *\n * **Purpose**: Manages user session lifecycle through delegation to SessionManager,\n * adding error recovery, state validation, and event buffer flushing.\n *\n * **Core Functionality**:\n * - Extracts projectId from config (tracelog/custom integrations or 'default')\n * - Creates SessionManager with storage, event manager, and projectId\n * - Flushes pending events after successful session initialization\n * - Automatic cleanup on initialization failures with nested try-catch\n *\n * **Key Features**:\n * - Idempotent operations (safe to call startTracking() multiple times)\n * - Double-destroy protection via destroyed flag\n * - State validation prevents operations on destroyed instances\n * - Centralized cleanup via cleanupSessionManager()\n *\n * **Lifecycle**:\n * - startTracking(): Creates session, sends SESSION_START event\n * - stopTracking(): Cleans up session tracking (no events emitted)\n * - destroy(): Same as stopTracking() (no events emitted in v2.0.0+)\n *\n * @example\n * ```typescript\n * const handler = new SessionHandler(storage, eventManager);\n * handler.startTracking(); // Creates session\n * handler.stopTracking(); // Ends session + cleanup\n * handler.destroy(); // Cleanup only\n * ```\n */\nexport class SessionHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly storageManager: StorageManager;\n private sessionManager: SessionManager | null = null;\n private destroyed = false;\n\n constructor(storageManager: StorageManager, eventManager: EventManager) {\n super();\n this.eventManager = eventManager;\n this.storageManager = storageManager;\n }\n\n /**\n * Starts session tracking by creating SessionManager and initializing session.\n *\n * **Behavior**:\n * - Extracts projectId from config (tracelog projectId or custom collectApiUrl or 'default')\n * - Creates SessionManager instance with storage, event manager, and projectId\n * - Calls SessionManager.startTracking() to begin session lifecycle\n * - Flushes pending events buffered during initialization\n * - Idempotent: Early return if session already active\n * - Validates state: Warns and returns if handler destroyed\n *\n * **Error Handling**:\n * - On failure: Automatically cleans up SessionManager via nested try-catch\n * - Leaves handler in clean, reusable state after error\n * - Re-throws error after logging\n *\n * @throws {Error} If SessionManager initialization fails\n */\n startTracking(): void {\n if (this.isActive()) {\n return;\n }\n\n if (this.destroyed) {\n log('debug', 'Cannot start tracking on destroyed handler');\n return;\n }\n\n const config = this.get('config');\n const projectId = config?.integrations?.tracelog?.projectId ?? 'custom';\n\n try {\n this.sessionManager = new SessionManager(this.storageManager, this.eventManager, projectId);\n this.sessionManager.startTracking();\n\n this.eventManager.flushPendingEvents();\n } catch (error) {\n if (this.sessionManager) {\n try {\n this.sessionManager.destroy();\n } catch {\n /* empty */\n }\n this.sessionManager = null;\n }\n\n log('error', 'Failed to start session tracking', { error });\n throw error;\n }\n }\n\n private isActive(): boolean {\n return this.sessionManager !== null && !this.destroyed;\n }\n\n private cleanupSessionManager(): void {\n if (this.sessionManager) {\n this.sessionManager.stopTracking();\n this.sessionManager.destroy();\n this.sessionManager = null;\n }\n }\n\n /**\n * Stops session tracking by cleaning up resources.\n *\n * **Purpose**: Terminates session tracking and removes all listeners and timers.\n * No events are emitted (SESSION_END removed in v2.0.0).\n *\n * **Behavior**:\n * - Calls SessionManager.stopTracking() to clean up listeners\n * - Calls SessionManager.destroy() to finalize cleanup\n * - Safe to call multiple times (idempotent via cleanupSessionManager)\n *\n * **Note**: In v2.0.0+, this method only performs cleanup without emitting events.\n * Session end time is inferred server-side from last event timestamp.\n */\n stopTracking(): void {\n this.cleanupSessionManager();\n }\n\n /**\n * Destroys handler and cleans up SessionManager.\n *\n * **Purpose**: Same as stopTracking() in v2.0.0+. Both methods perform cleanup\n * without emitting events.\n *\n * **Behavior**:\n * - Idempotent: Early return if already destroyed\n * - Calls SessionManager.destroy() to clean up listeners and timers\n * - Sets sessionManager to null and destroyed flag to true\n *\n * **Note**: In v2.0.0+, there is no functional difference between stopTracking()\n * and destroy(). Both perform cleanup without emitting SESSION_END events.\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n\n if (this.sessionManager) {\n this.sessionManager.destroy();\n this.sessionManager = null;\n }\n\n this.destroyed = true;\n }\n}\n","import { EventType, PageViewData } from '../types';\nimport { normalizeUrl } from '../utils';\nimport { StateManager } from '../managers/state.manager';\nimport { EventManager } from '../managers/event.manager';\nimport { DEFAULT_PAGE_VIEW_THROTTLE_MS } from '../constants/config.constants';\n\n/**\n * Tracks page navigation and route changes in single-page applications.\n *\n * **Events Generated**: `page_view`\n *\n * **Features**:\n * - Tracks initial page load, browser navigation, hash changes, and History API calls\n * - URL normalization with automatic filtering of sensitive query parameters\n * - Deduplication to prevent consecutive duplicate events\n * - Configurable throttling (default: 1 second)\n * - SPA navigation detection via History API patching\n *\n * **Privacy Protection**:\n * - Automatically removes 15 common sensitive params (token, auth, key, password, etc.)\n * - User-configurable additional sensitive parameters via config\n *\n * @see src/handlers/README.md (lines 5-63) for detailed documentation\n */\nexport class PageViewHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly onTrack: () => void;\n\n private originalPushState?: typeof window.history.pushState;\n private originalReplaceState?: typeof window.history.replaceState;\n private lastPageViewTime = 0;\n\n constructor(eventManager: EventManager, onTrack: () => void) {\n super();\n\n this.eventManager = eventManager;\n this.onTrack = onTrack;\n }\n\n /**\n * Starts tracking page views.\n *\n * - Tracks initial page load first (via trackInitialPageView)\n * - Attaches popstate and hashchange event listeners\n * - Patches History API methods (pushState, replaceState) for SPA navigation\n * - All setup happens synchronously\n *\n * **Note**: onTrack() callback is invoked AFTER initial page view but BEFORE\n * subsequent navigation events for session management coordination.\n */\n startTracking(): void {\n this.trackInitialPageView();\n\n window.addEventListener('popstate', this.trackCurrentPage, true);\n window.addEventListener('hashchange', this.trackCurrentPage, true);\n\n this.patchHistory('pushState');\n this.patchHistory('replaceState');\n }\n\n /**\n * Stops tracking page views and restores original History API methods.\n *\n * - Removes event listeners (popstate, hashchange)\n * - Restores original pushState and replaceState methods\n * - Resets throttling state\n */\n stopTracking(): void {\n window.removeEventListener('popstate', this.trackCurrentPage, true);\n window.removeEventListener('hashchange', this.trackCurrentPage, true);\n\n if (this.originalPushState) {\n window.history.pushState = this.originalPushState;\n }\n\n if (this.originalReplaceState) {\n window.history.replaceState = this.originalReplaceState;\n }\n\n this.lastPageViewTime = 0;\n }\n\n private patchHistory(method: 'pushState' | 'replaceState'): void {\n const original = window.history[method];\n\n if (method === 'pushState' && !this.originalPushState) {\n this.originalPushState = original;\n } else if (method === 'replaceState' && !this.originalReplaceState) {\n this.originalReplaceState = original;\n }\n\n window.history[method] = (...args: [unknown, string, string | URL | null | undefined]): void => {\n original.apply(window.history, args);\n this.trackCurrentPage();\n };\n }\n\n private readonly trackCurrentPage = (): void => {\n const rawUrl = window.location.href;\n const normalizedUrl = normalizeUrl(rawUrl, this.get('config').sensitiveQueryParams);\n\n if (this.get('pageUrl') === normalizedUrl) {\n return;\n }\n\n const now = Date.now();\n const throttleMs = this.get('config').pageViewThrottleMs ?? DEFAULT_PAGE_VIEW_THROTTLE_MS;\n\n if (now - this.lastPageViewTime < throttleMs) {\n return;\n }\n\n this.lastPageViewTime = now;\n\n this.onTrack();\n\n const fromUrl = this.get('pageUrl');\n\n this.set('pageUrl', normalizedUrl);\n\n const pageViewData = this.extractPageViewData();\n this.eventManager.track({\n type: EventType.PAGE_VIEW,\n page_url: this.get('pageUrl'),\n from_page_url: fromUrl,\n ...(pageViewData && { page_view: pageViewData }),\n });\n\n // Flush the queue now (previous-route events + this PAGE_VIEW) so the\n // batch lands before the user can close the tab on the next route.\n if (this.get('config').flushOnSpaNavigation === true) {\n void this.eventManager.flushImmediately();\n }\n };\n\n private trackInitialPageView(): void {\n const normalizedUrl = normalizeUrl(window.location.href, this.get('config').sensitiveQueryParams);\n const pageViewData = this.extractPageViewData();\n\n this.lastPageViewTime = Date.now();\n\n this.eventManager.track({\n type: EventType.PAGE_VIEW,\n page_url: normalizedUrl,\n ...(pageViewData && { page_view: pageViewData }),\n });\n\n this.onTrack();\n }\n\n private extractPageViewData(): PageViewData | undefined {\n const { referrer } = document;\n const { title } = document;\n\n if (!referrer && !title) {\n return undefined;\n }\n\n return {\n ...(referrer && { referrer }),\n ...(title && { title }),\n };\n }\n}\n","import {\n HTML_DATA_ATTR_PREFIX,\n MAX_TEXT_LENGTH,\n INTERACTIVE_SELECTORS,\n DEFAULT_CLICK_THROTTLE_MS,\n MAX_THROTTLE_CACHE_ENTRIES,\n THROTTLE_ENTRY_TTL_MS,\n THROTTLE_PRUNE_INTERVAL_MS,\n} from '../constants';\nimport { ClickCoordinates, ClickData, ClickTrackingElementData, EventType } from '../types';\nimport { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { log, sanitizePii } from '../utils';\n\n/**\n * Captures mouse clicks and converts them into analytics events with element context and coordinates.\n *\n * **Features**:\n * - Smart element detection via INTERACTIVE_SELECTORS (29 selectors including buttons, links, form elements, ARIA roles)\n * - Relative coordinates calculation (0-1 scale, 3 decimal precision, clamped)\n * - Custom event tracking via `data-tlog-name` attributes\n * - Text extraction with length limits (255 chars max) and priority logic\n * - PII sanitization (emails, phone numbers, credit cards, API keys, tokens)\n * - Privacy controls via `data-tlog-ignore` attribute\n * - Per-element click throttling (default 300ms) with memory management (TTL + LRU)\n *\n * **Events Generated**: `click`, `custom` (for elements with data-tlog-name)\n *\n * **Triggers**: Capture-phase click events on document\n *\n * **Memory Management**:\n * - TTL-based pruning: 5-minute TTL with automatic cleanup\n * - LRU eviction: Maintains maximum 1000 throttle entries\n * - Rate-limited pruning: Runs every 30 seconds\n *\n * @example\n * ```typescript\n * const handler = new ClickHandler(eventManager);\n * handler.startTracking();\n * // Clicks are now tracked automatically\n * handler.stopTracking();\n * ```\n */\nexport class ClickHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly lastClickTimes: Map<string, number> = new Map();\n private clickHandler?: (event: Event) => void;\n private lastPruneTime = 0;\n\n constructor(eventManager: EventManager) {\n super();\n\n this.eventManager = eventManager;\n }\n\n /**\n * Starts tracking click events on the document.\n *\n * Attaches a single capture-phase click listener to window that:\n * - Detects interactive elements or falls back to clicked element\n * - Applies click throttling per element (configurable, default 300ms)\n * - Extracts custom tracking data from data-tlog-name attributes\n * - Generates both custom events (for tracked elements) and click events\n * - Respects data-tlog-ignore privacy controls\n * - Sanitizes text content for PII protection\n *\n * Idempotent: Safe to call multiple times (early return if already tracking).\n */\n startTracking(): void {\n if (this.clickHandler) {\n return;\n }\n\n this.clickHandler = (event: Event): void => {\n const mouseEvent = event as MouseEvent;\n const target = mouseEvent.target;\n const clickedElement =\n typeof HTMLElement !== 'undefined' && target instanceof HTMLElement\n ? target\n : typeof HTMLElement !== 'undefined' && target instanceof Node && target.parentElement instanceof HTMLElement\n ? target.parentElement\n : null;\n\n if (!clickedElement) {\n log('debug', 'Click target not found or not an element');\n return;\n }\n\n if (this.shouldIgnoreElement(clickedElement)) {\n return;\n }\n\n // Throttle clicks per element to prevent double-clicks and spam\n const clickThrottleMs = this.get('config')?.clickThrottleMs ?? DEFAULT_CLICK_THROTTLE_MS;\n if (clickThrottleMs > 0 && !this.checkClickThrottle(clickedElement, clickThrottleMs)) {\n return;\n }\n\n const trackingElement = this.findTrackingElement(clickedElement);\n const relevantClickElement = this.getRelevantClickElement(clickedElement);\n const coordinates = this.calculateClickCoordinates(mouseEvent);\n\n // Order matters: the `data-tlog-name` CUSTOM event runs BEFORE the\n // synthetic-coordinate guard below, so a programmatic `element.click()`\n // on a tracked element still fires its explicit opt-in custom event. The\n // implicit CLICK event is suppressed only for synthetic clicks to avoid\n // polluting heatmaps with `(0, 0)` coordinates.\n if (trackingElement) {\n const trackingData = this.extractTrackingData(trackingElement);\n\n if (trackingData) {\n const attributeData = this.createCustomEventData(trackingData);\n\n this.eventManager.track({\n type: EventType.CUSTOM,\n custom_event: {\n name: attributeData.name,\n ...(attributeData.value && { metadata: { value: attributeData.value } }),\n },\n });\n }\n }\n\n if (!coordinates) {\n log('debug', 'Click skipped: invalid coordinates (likely synthetic)');\n return;\n }\n\n const clickData = this.generateClickData(clickedElement, relevantClickElement, coordinates);\n\n this.eventManager.track({\n type: EventType.CLICK,\n click_data: clickData,\n });\n };\n\n window.addEventListener('click', this.clickHandler, true);\n }\n\n /**\n * Stops tracking click events and cleans up resources.\n *\n * Removes the click event listener, clears throttle cache, and resets prune timer.\n * Prevents memory leaks by properly cleaning up all state.\n */\n stopTracking(): void {\n if (this.clickHandler) {\n window.removeEventListener('click', this.clickHandler, true);\n this.clickHandler = undefined;\n }\n this.lastClickTimes.clear();\n this.lastPruneTime = 0;\n }\n\n private shouldIgnoreElement(element: HTMLElement): boolean {\n if (element.hasAttribute(`${HTML_DATA_ATTR_PREFIX}-ignore`)) {\n return true;\n }\n\n const parent = element.closest(`[${HTML_DATA_ATTR_PREFIX}-ignore]`);\n\n return parent !== null;\n }\n\n /**\n * Checks per-element click throttling to prevent double-clicks and rapid spam\n * Returns true if the click should be tracked, false if throttled\n */\n private checkClickThrottle(element: HTMLElement, throttleMs: number): boolean {\n const signature = this.getElementSignature(element);\n const now = Date.now();\n\n this.pruneThrottleCache(now);\n\n const lastClickTime = this.lastClickTimes.get(signature);\n\n if (lastClickTime !== undefined && now - lastClickTime < throttleMs) {\n log('debug', 'ClickHandler: Click suppressed by throttle', {\n data: {\n signature,\n throttleRemaining: throttleMs - (now - lastClickTime),\n },\n });\n return false;\n }\n\n this.lastClickTimes.set(signature, now);\n return true;\n }\n\n /**\n * Prunes stale entries from the throttle cache to prevent memory leaks\n * Uses TTL-based eviction (5 minutes) and enforces max size limit\n * Called during checkClickThrottle with built-in rate limiting (every 30 seconds)\n */\n private pruneThrottleCache(now: number): void {\n if (now - this.lastPruneTime < THROTTLE_PRUNE_INTERVAL_MS) {\n return;\n }\n\n this.lastPruneTime = now;\n const cutoff = now - THROTTLE_ENTRY_TTL_MS;\n\n for (const [key, timestamp] of this.lastClickTimes.entries()) {\n if (timestamp < cutoff) {\n this.lastClickTimes.delete(key);\n }\n }\n\n if (this.lastClickTimes.size > MAX_THROTTLE_CACHE_ENTRIES) {\n const entries = Array.from(this.lastClickTimes.entries()).sort((a, b) => a[1] - b[1]);\n\n const excessCount = this.lastClickTimes.size - MAX_THROTTLE_CACHE_ENTRIES;\n const toDelete = entries.slice(0, excessCount);\n\n for (const [key] of toDelete) {\n this.lastClickTimes.delete(key);\n }\n\n log('debug', 'ClickHandler: Pruned throttle cache', {\n data: {\n removed: toDelete.length,\n remaining: this.lastClickTimes.size,\n },\n });\n }\n }\n\n /**\n * Creates a stable signature for an element to track throttling\n * Priority: id > data-testid > data-tlog-name > DOM path\n */\n private getElementSignature(element: HTMLElement): string {\n if (element.id) {\n return `#${element.id}`;\n }\n\n const testId = element.getAttribute('data-testid');\n if (testId) {\n return `[data-testid=\"${testId}\"]`;\n }\n\n const tlogName = element.getAttribute(`${HTML_DATA_ATTR_PREFIX}-name`);\n if (tlogName) {\n return `[${HTML_DATA_ATTR_PREFIX}-name=\"${tlogName}\"]`;\n }\n\n return this.getElementPath(element);\n }\n\n /**\n * Generates a DOM path for an element (e.g., \"body>div>button\")\n */\n private getElementPath(element: HTMLElement): string {\n const path: string[] = [];\n let current: HTMLElement | null = element;\n\n while (current && current !== document.body) {\n let selector = current.tagName.toLowerCase();\n\n if (current.className) {\n const firstClass = current.className.split(' ')[0];\n if (firstClass) {\n selector += `.${firstClass}`;\n }\n }\n\n path.unshift(selector);\n current = current.parentElement;\n }\n\n return path.join('>') || 'unknown';\n }\n\n private findTrackingElement(element: HTMLElement): HTMLElement | undefined {\n if (element.hasAttribute(`${HTML_DATA_ATTR_PREFIX}-name`)) {\n return element;\n }\n\n const closest = element.closest(`[${HTML_DATA_ATTR_PREFIX}-name]`) as HTMLElement;\n\n return closest;\n }\n\n private getRelevantClickElement(element: HTMLElement): HTMLElement {\n for (const selector of INTERACTIVE_SELECTORS) {\n try {\n if (element.matches(selector)) {\n return element;\n }\n\n const parent = element.closest(selector) as HTMLElement;\n\n if (parent) {\n return parent;\n }\n } catch (error) {\n log('debug', 'Invalid selector in element search', { error, data: { selector } });\n continue;\n }\n }\n\n return element;\n }\n\n private calculateClickCoordinates(event: MouseEvent): ClickCoordinates | null {\n const x = event.clientX;\n const y = event.clientY;\n\n if (typeof x !== 'number' || typeof y !== 'number' || !Number.isFinite(x) || !Number.isFinite(y)) {\n return null;\n }\n\n // Reject (0, 0) only when the event was not user-generated. `element.click()`\n // and `dispatchEvent(new MouseEvent(...))` both produce `isTrusted === false`\n // and default coords to (0, 0). A real user clicking the top-left pixel\n // produces `isTrusted === true`, so this preserves legitimate corner clicks\n // while filtering programmatic noise.\n if (x === 0 && y === 0 && !event.isTrusted) {\n return null;\n }\n\n return { x, y };\n }\n\n private extractTrackingData(trackingElement: HTMLElement): ClickTrackingElementData | undefined {\n const name = trackingElement.getAttribute(`${HTML_DATA_ATTR_PREFIX}-name`);\n const value = trackingElement.getAttribute(`${HTML_DATA_ATTR_PREFIX}-value`);\n\n if (!name) {\n return undefined;\n }\n\n return {\n element: trackingElement,\n name,\n ...(value && { value }),\n };\n }\n\n private generateClickData(\n clickedElement: HTMLElement,\n relevantElement: HTMLElement,\n coordinates: ClickCoordinates,\n ): ClickData {\n const { x, y } = coordinates;\n const text = this.getRelevantText(clickedElement, relevantElement);\n const href = relevantElement.getAttribute('href') ?? undefined;\n\n return {\n x,\n y,\n tag: relevantElement.tagName.toLowerCase(),\n ...(relevantElement.id && { id: relevantElement.id }),\n ...(relevantElement.className && { class: relevantElement.className }),\n ...(text && { text }),\n ...(href && { href }),\n };\n }\n\n private getRelevantText(clickedElement: HTMLElement, relevantElement: HTMLElement): string {\n const clickedText = clickedElement.textContent?.trim() ?? '';\n const relevantText = relevantElement.textContent?.trim() ?? '';\n\n if (!clickedText && !relevantText) {\n return '';\n }\n\n let finalText = '';\n\n if (clickedText && clickedText.length <= MAX_TEXT_LENGTH) {\n finalText = clickedText;\n } else if (relevantText.length <= MAX_TEXT_LENGTH) {\n finalText = relevantText;\n } else {\n finalText = relevantText.slice(0, MAX_TEXT_LENGTH - 3) + '...';\n }\n\n return sanitizePii(finalText);\n }\n\n private createCustomEventData(trackingData: ClickTrackingElementData): { name: string; value?: string } {\n return {\n name: trackingData.name,\n ...(trackingData.value && { value: trackingData.value }),\n };\n }\n}\n","import {\n MAX_SCROLL_EVENTS_PER_SESSION,\n MIN_SCROLL_DEPTH_CHANGE,\n SCROLL_DEBOUNCE_TIME_MS,\n SCROLL_MIN_EVENT_INTERVAL_MS,\n SIGNIFICANT_SCROLL_DELTA,\n} from '../constants';\nimport { EventType, ScrollData, ScrollDirection } from '../types';\nimport { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { log } from '../utils';\n\ninterface ScrollContainer {\n element: Window | HTMLElement;\n selector: string;\n lastScrollPos: number;\n lastDepth: number;\n lastEventTime: number;\n debounceTimer: number | null;\n listener: EventListener;\n}\n\n/**\n * Tracks scroll depth and direction across the window and any detected scrollable containers.\n *\n * **Captured fields**: `depth` (0-100), `direction` (up/down), `container_selector`.\n *\n * **Guardrails**:\n * - Significant movement (minimum 10px position delta)\n * - Depth change (minimum 5% delta between events)\n * - Rate limiting (minimum 500ms interval between events per container)\n * - Session cap (maximum 120 events per session)\n * - Multi-container support with 250ms per-container debouncing\n */\nexport class ScrollHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly containers: ScrollContainer[] = [];\n private limitWarningLogged = false;\n private containerDiscoveryTimeoutId: number | null = null;\n\n constructor(eventManager: EventManager) {\n super();\n this.eventManager = eventManager;\n }\n\n startTracking(): void {\n this.limitWarningLogged = false;\n this.set('scrollEventCount', 0);\n this.tryDetectScrollContainers(0);\n }\n\n stopTracking(): void {\n if (this.containerDiscoveryTimeoutId !== null) {\n clearTimeout(this.containerDiscoveryTimeoutId);\n this.containerDiscoveryTimeoutId = null;\n }\n\n for (const container of this.containers) {\n this.clearContainerTimer(container);\n\n if (container.element === window) {\n window.removeEventListener('scroll', container.listener);\n } else {\n (container.element as HTMLElement).removeEventListener('scroll', container.listener);\n }\n }\n\n this.containers.length = 0;\n this.set('scrollEventCount', 0);\n this.limitWarningLogged = false;\n }\n\n private tryDetectScrollContainers(attempt: number): void {\n const elements = this.findScrollableElements();\n\n if (this.isWindowScrollable()) {\n this.setupScrollContainer(window, 'window');\n }\n\n if (elements.length > 0) {\n for (const element of elements) {\n const selector = this.getElementSelector(element);\n this.setupScrollContainer(element, selector);\n }\n return;\n }\n\n if (attempt < 5) {\n this.containerDiscoveryTimeoutId = window.setTimeout(() => {\n this.containerDiscoveryTimeoutId = null;\n this.tryDetectScrollContainers(attempt + 1);\n }, 200);\n\n return;\n }\n\n if (this.containers.length === 0) {\n this.setupScrollContainer(window, 'window');\n }\n }\n\n private findScrollableElements(): HTMLElement[] {\n if (!document.body) {\n return [];\n }\n\n const elements: HTMLElement[] = [];\n\n const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node) => {\n const element = node as HTMLElement;\n\n if (!element.isConnected || !element.offsetParent) {\n return NodeFilter.FILTER_SKIP;\n }\n\n const style = getComputedStyle(element);\n\n const hasVerticalScrollableStyle =\n style.overflowY === 'auto' ||\n style.overflowY === 'scroll' ||\n style.overflow === 'auto' ||\n style.overflow === 'scroll';\n\n return hasVerticalScrollableStyle ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n },\n });\n\n let node: Node | null;\n\n while ((node = walker.nextNode()) && elements.length < 10) {\n const element = node as HTMLElement;\n\n if (this.isElementScrollable(element)) {\n elements.push(element);\n }\n }\n\n return elements;\n }\n\n private getElementSelector(element: Window | HTMLElement): string {\n if (element === window) {\n return 'window';\n }\n\n const htmlElement = element as HTMLElement;\n\n if (htmlElement.id) {\n return `#${htmlElement.id}`;\n }\n\n if (htmlElement.className && typeof htmlElement.className === 'string') {\n const firstClass = htmlElement.className.split(' ').filter((c) => c.trim())[0];\n\n if (firstClass) {\n return `.${firstClass}`;\n }\n }\n\n return htmlElement.tagName.toLowerCase();\n }\n\n private setupScrollContainer(element: Window | HTMLElement, selector: string): void {\n const alreadyTracking = this.containers.some((c) => c.element === element);\n\n if (alreadyTracking) {\n return;\n }\n\n if (element !== window && !this.isElementScrollable(element as HTMLElement)) {\n return;\n }\n\n const initialScrollTop = this.getScrollTop(element);\n\n const initialDepth = this.calculateScrollDepth(\n initialScrollTop,\n this.getScrollHeight(element),\n this.getViewportHeight(element),\n );\n\n const container: ScrollContainer = {\n element,\n selector,\n lastScrollPos: initialScrollTop,\n lastDepth: initialDepth,\n lastEventTime: 0,\n debounceTimer: null,\n listener: null as unknown as EventListener,\n };\n\n const handleScroll = (): void => {\n if (this.get('suppressNextScroll')) {\n return;\n }\n\n this.clearContainerTimer(container);\n\n container.debounceTimer = window.setTimeout(() => {\n const scrollData = this.calculateScrollData(container);\n\n if (scrollData) {\n this.processScrollEvent(container, scrollData, Date.now());\n }\n\n container.debounceTimer = null;\n }, SCROLL_DEBOUNCE_TIME_MS);\n };\n\n container.listener = handleScroll;\n this.containers.push(container);\n\n if (element === window) {\n window.addEventListener('scroll', handleScroll, { passive: true });\n } else {\n (element as HTMLElement).addEventListener('scroll', handleScroll, { passive: true });\n }\n }\n\n private processScrollEvent(\n container: ScrollContainer,\n scrollData: Omit<ScrollData, 'container_selector'>,\n timestamp: number,\n ): void {\n if (!this.shouldEmitScrollEvent(container, scrollData, timestamp)) {\n return;\n }\n\n container.lastEventTime = timestamp;\n container.lastDepth = scrollData.depth;\n\n const currentCount = this.get('scrollEventCount') ?? 0;\n this.set('scrollEventCount', currentCount + 1);\n\n this.eventManager.track({\n type: EventType.SCROLL,\n scroll_data: {\n ...scrollData,\n container_selector: container.selector,\n },\n });\n }\n\n private shouldEmitScrollEvent(\n container: ScrollContainer,\n scrollData: Omit<ScrollData, 'container_selector'>,\n timestamp: number,\n ): boolean {\n if (this.hasReachedSessionLimit()) {\n this.logLimitOnce();\n return false;\n }\n\n if (!this.hasElapsedMinimumInterval(container, timestamp)) {\n return false;\n }\n\n if (!this.hasSignificantDepthChange(container, scrollData.depth)) {\n return false;\n }\n\n return true;\n }\n\n private hasReachedSessionLimit(): boolean {\n const currentCount = this.get('scrollEventCount') ?? 0;\n return currentCount >= MAX_SCROLL_EVENTS_PER_SESSION;\n }\n\n private hasElapsedMinimumInterval(container: ScrollContainer, timestamp: number): boolean {\n if (container.lastEventTime === 0) {\n return true;\n }\n return timestamp - container.lastEventTime >= SCROLL_MIN_EVENT_INTERVAL_MS;\n }\n\n private hasSignificantDepthChange(container: ScrollContainer, newDepth: number): boolean {\n return Math.abs(newDepth - container.lastDepth) >= MIN_SCROLL_DEPTH_CHANGE;\n }\n\n private logLimitOnce(): void {\n if (this.limitWarningLogged) {\n return;\n }\n\n this.limitWarningLogged = true;\n\n log('debug', 'Max scroll events per session reached', {\n data: { limit: MAX_SCROLL_EVENTS_PER_SESSION },\n });\n }\n\n private isWindowScrollable(): boolean {\n return document.documentElement.scrollHeight > window.innerHeight;\n }\n\n private clearContainerTimer(container: ScrollContainer): void {\n if (container.debounceTimer !== null) {\n clearTimeout(container.debounceTimer);\n container.debounceTimer = null;\n }\n }\n\n private getScrollDirection(current: number, previous: number): ScrollDirection {\n return current > previous ? ScrollDirection.DOWN : ScrollDirection.UP;\n }\n\n private calculateScrollDepth(scrollTop: number, scrollHeight: number, viewportHeight: number): number {\n if (scrollHeight <= viewportHeight) {\n return 0;\n }\n\n const maxScrollTop = scrollHeight - viewportHeight;\n return Math.min(100, Math.max(0, Math.floor((scrollTop / maxScrollTop) * 100)));\n }\n\n private calculateScrollData(container: ScrollContainer): Omit<ScrollData, 'container_selector'> | null {\n const { element, lastScrollPos } = container;\n const scrollTop = this.getScrollTop(element);\n\n const positionDelta = Math.abs(scrollTop - lastScrollPos);\n if (positionDelta < SIGNIFICANT_SCROLL_DELTA) {\n return null;\n }\n\n if (element === window && !this.isWindowScrollable()) {\n return null;\n }\n\n const viewportHeight = this.getViewportHeight(element);\n const scrollHeight = this.getScrollHeight(element);\n const direction = this.getScrollDirection(scrollTop, lastScrollPos);\n const depth = this.calculateScrollDepth(scrollTop, scrollHeight, viewportHeight);\n\n container.lastScrollPos = scrollTop;\n\n return { depth, direction };\n }\n\n private getScrollTop(element: Window | HTMLElement): number {\n return element === window ? window.scrollY : (element as HTMLElement).scrollTop;\n }\n\n private getViewportHeight(element: Window | HTMLElement): number {\n return element === window ? window.innerHeight : (element as HTMLElement).clientHeight;\n }\n\n private getScrollHeight(element: Window | HTMLElement): number {\n return element === window ? document.documentElement.scrollHeight : (element as HTMLElement).scrollHeight;\n }\n\n private isElementScrollable(element: HTMLElement): boolean {\n const style = getComputedStyle(element);\n\n const hasVerticalScrollableOverflow =\n style.overflowY === 'auto' ||\n style.overflowY === 'scroll' ||\n style.overflow === 'auto' ||\n style.overflow === 'scroll';\n\n const hasVerticalOverflowContent = element.scrollHeight > element.clientHeight;\n\n return hasVerticalScrollableOverflow && hasVerticalOverflowContent;\n }\n}\n","import { StateManager } from '../managers/state.manager';\nimport { log } from '../utils';\n\nconst SHOPIFY_SESSION_ATTR = 'tracelog_session_id';\nconst SHOPIFY_USER_ATTR = 'tracelog_user_id';\n\n/**\n * Writes TraceLog identifiers to Shopify cart attributes via the storefront AJAX API.\n *\n * Two attributes are propagated:\n * - `tracelog_session_id`: surfaces in the `orders/paid` webhook `note_attributes`,\n * letting the API attribute revenue to the original session's UTM data.\n * - `tracelog_user_id`: surfaces in Customer Events `event.data.checkout.attributes`\n * (cart_viewed / checkout_started / checkout_completed), letting the Web Pixel\n * Extension carry the same user_id used on the storefront into the checkout sandbox.\n *\n * Without `tracelog_user_id` propagation, checkout-funnel events would derive a\n * different user_id from Shopify's per-visitor `clientId`, splitting one buyer\n * into two TraceLog users (pre-checkout and post-checkout) and breaking funnel\n * conversion / repeat-purchase metrics.\n */\nexport class ShopifyCartLinker extends StateManager {\n private visibilityHandler: (() => void) | null = null;\n private pageshowHandler: ((event: PageTransitionEvent) => void) | null = null;\n private lastSyncedKey: string | null = null;\n\n activate(): void {\n this.cleanupListeners();\n this.syncCartAttribute();\n this.setupListeners();\n }\n\n deactivate(): void {\n this.cleanupListeners();\n this.lastSyncedKey = null;\n }\n\n /** Re-syncs cart attributes when session rotates (called by App on SESSION_START). */\n onSessionChange(): void {\n this.syncCartAttribute();\n }\n\n private syncCartAttribute(): void {\n const sessionId = this.get('sessionId');\n if (!sessionId) return;\n\n const rawUserId = this.get('userId');\n const userId = typeof rawUserId === 'string' && rawUserId.length > 0 ? rawUserId : '';\n const dedupKey = `${sessionId}|${userId}`;\n if (dedupKey === this.lastSyncedKey) return;\n\n this.lastSyncedKey = dedupKey;\n this.postCartUpdate(sessionId, userId);\n }\n\n private postCartUpdate(sessionId: string, userId: string): void {\n const attributes: Record<string, string> = { [SHOPIFY_SESSION_ATTR]: sessionId };\n if (userId.length > 0) attributes[SHOPIFY_USER_ATTR] = userId;\n\n try {\n fetch('/cart/update.js', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ attributes }),\n credentials: 'same-origin',\n })\n .then((response) => {\n if (!response.ok) {\n this.lastSyncedKey = null;\n log('debug', 'Shopify cart attribute update failed', { data: { status: response.status } });\n }\n })\n .catch(() => {\n this.lastSyncedKey = null;\n log('debug', 'Shopify cart attribute update failed');\n });\n } catch {\n this.lastSyncedKey = null;\n log('debug', 'Shopify cart attribute update failed');\n }\n }\n\n /**\n * Sync triggers (theme-agnostic):\n * - `visibilitychange`: catches tab refocus (long sessions, OAuth round-trips).\n * - `pageshow` with `event.persisted === true`: catches bfcache restore so a\n * user returning from an external checkout / Shop Pay window picks up the\n * current sessionId before any further interaction.\n *\n * Both triggers go through `syncCartAttribute()` which dedupes by\n * `sessionId|userId`, so spurious calls cost nothing.\n */\n private setupListeners(): void {\n this.visibilityHandler = (): void => {\n if (!document.hidden) {\n this.syncCartAttribute();\n }\n };\n document.addEventListener('visibilitychange', this.visibilityHandler);\n\n this.pageshowHandler = (event: PageTransitionEvent): void => {\n if (event.persisted) this.syncCartAttribute();\n };\n window.addEventListener('pageshow', this.pageshowHandler);\n }\n\n private cleanupListeners(): void {\n if (this.visibilityHandler) {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n if (this.pageshowHandler) {\n window.removeEventListener('pageshow', this.pageshowHandler);\n this.pageshowHandler = null;\n }\n }\n}\n","import { log } from '../utils';\n\n/**\n * Robust localStorage and sessionStorage wrapper with automatic fallback to in-memory storage.\n *\n * - Dual storage: localStorage (persistent) + sessionStorage (tab-scoped).\n * - Automatic in-memory fallback when browser storage unavailable (privacy modes, SSR).\n * - On `QuotaExceededError`: single-pass cleanup — purges `tracelog_persisted_events_*`\n * first (largest, recoverable), retries once. Preserves session/user/device/config keys.\n */\nexport class StorageManager {\n private readonly storage: Storage | null;\n private readonly sessionStorageRef: Storage | null;\n private readonly fallbackStorage = new Map<string, string>();\n private readonly fallbackSessionStorage = new Map<string, string>();\n\n constructor() {\n this.storage = this.initializeStorage('localStorage');\n this.sessionStorageRef = this.initializeStorage('sessionStorage');\n\n if (!this.storage) {\n log('debug', 'localStorage not available, using memory fallback');\n }\n if (!this.sessionStorageRef) {\n log('debug', 'sessionStorage not available, using memory fallback');\n }\n }\n\n getItem(key: string): string | null {\n try {\n if (this.storage) {\n return this.storage.getItem(key);\n }\n return this.fallbackStorage.get(key) ?? null;\n } catch {\n return this.fallbackStorage.get(key) ?? null;\n }\n }\n\n setItem(key: string, value: string): void {\n this.fallbackStorage.set(key, value);\n\n if (!this.storage) {\n return;\n }\n\n try {\n this.storage.setItem(key, value);\n return;\n } catch (error) {\n const isQuotaError =\n (error instanceof DOMException && error.name === 'QuotaExceededError') ||\n (error instanceof Error && error.name === 'QuotaExceededError');\n\n if (!isQuotaError) {\n return;\n }\n\n log('warn', 'localStorage quota exceeded, attempting cleanup', {\n data: { key, valueSize: value.length },\n });\n\n if (!this.cleanupOldData()) {\n log('error', 'localStorage quota exceeded and no data to cleanup - data will not persist', {\n error,\n data: { key, valueSize: value.length },\n });\n return;\n }\n\n try {\n this.storage.setItem(key, value);\n } catch (retryError) {\n log('error', 'localStorage quota exceeded even after cleanup - data will not persist', {\n error: retryError,\n data: { key, valueSize: value.length },\n });\n }\n }\n }\n\n removeItem(key: string): void {\n try {\n if (this.storage) {\n this.storage.removeItem(key);\n }\n } catch {\n /* empty */\n }\n\n this.fallbackStorage.delete(key);\n }\n\n /**\n * Single-pass cleanup for QuotaExceededError. Purges persisted-events keys\n * (largest, safe to discard — recoverable) and up to 5 other non-critical\n * tracelog_* keys in one pass. Preserves session/user/device/config keys.\n */\n private cleanupOldData(): boolean {\n if (!this.storage) {\n return false;\n }\n\n try {\n const criticalPrefixes = ['tracelog_session_', 'tracelog_user_id', 'tracelog_device_id', 'tracelog_config'];\n const persistedKeys: string[] = [];\n const nonCriticalKeys: string[] = [];\n\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (!key?.startsWith('tracelog_')) continue;\n\n if (key.startsWith('tracelog_persisted_events_')) {\n persistedKeys.push(key);\n } else if (!criticalPrefixes.some((prefix) => key.startsWith(prefix))) {\n nonCriticalKeys.push(key);\n }\n }\n\n const keysToRemove = [...persistedKeys, ...nonCriticalKeys.slice(0, 5)];\n\n if (keysToRemove.length === 0) {\n return false;\n }\n\n keysToRemove.forEach((key) => {\n try {\n this.storage!.removeItem(key);\n } catch {\n /* empty */\n }\n });\n\n return true;\n } catch (error) {\n log('error', 'Failed to cleanup old data', { error });\n return false;\n }\n }\n\n private initializeStorage(type: 'localStorage' | 'sessionStorage'): Storage | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n const testKey = '__tracelog_test__';\n\n storage.setItem(testKey, 'test');\n storage.removeItem(testKey);\n\n return storage;\n } catch {\n return null;\n }\n }\n\n getSessionItem(key: string): string | null {\n try {\n if (this.sessionStorageRef) {\n return this.sessionStorageRef.getItem(key);\n }\n return this.fallbackSessionStorage.get(key) ?? null;\n } catch {\n return this.fallbackSessionStorage.get(key) ?? null;\n }\n }\n\n setSessionItem(key: string, value: string): void {\n this.fallbackSessionStorage.set(key, value);\n\n try {\n if (this.sessionStorageRef) {\n this.sessionStorageRef.setItem(key, value);\n return;\n }\n } catch (error) {\n const isQuotaError =\n (error instanceof DOMException && error.name === 'QuotaExceededError') ||\n (error instanceof Error && error.name === 'QuotaExceededError');\n\n if (isQuotaError) {\n log('error', 'sessionStorage quota exceeded - data will not persist', {\n error,\n data: { key, valueSize: value.length },\n });\n }\n }\n }\n\n removeSessionItem(key: string): void {\n try {\n if (this.sessionStorageRef) {\n this.sessionStorageRef.removeItem(key);\n }\n } catch {\n /* empty */\n }\n\n this.fallbackSessionStorage.delete(key);\n }\n}\n","import { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { EventType, WebVitalType } from '../types';\nimport {\n MAX_NAVIGATION_HISTORY,\n PRECISION_TWO_DECIMALS,\n getWebVitalsThresholds,\n DEFAULT_WEB_VITALS_MODE,\n} from '../constants';\nimport { log } from '../utils';\n\ntype LayoutShiftEntry = PerformanceEntry & { value?: number; hadRecentInput?: boolean };\n\n/**\n * Captures Web Vitals and performance metrics using the web-vitals library with fallback to native Performance Observer API.\n *\n * **Features**:\n * - Configurable filtering modes: 'all', 'needs-improvement' (default), 'poor'\n * - Custom threshold overrides via webVitalsThresholds config\n * - Navigation-based deduplication with 50-navigation FIFO history\n * - CLS accumulation with reset on navigation change\n * - Automatic fallback to Performance Observer if web-vitals library fails\n * - Final values only (reportAllChanges: false for all metrics)\n *\n * **Events Generated**: `web_vitals`\n *\n * **Metrics Captured**:\n * - LCP (Largest Contentful Paint): Main content loading time\n * - CLS (Cumulative Layout Shift): Visual stability score\n * - FCP (First Contentful Paint): Initial rendering time\n * - TTFB (Time to First Byte): Server response time\n * - INP (Interaction to Next Paint): Responsiveness measure\n *\n * **Filtering Modes**:\n * - 'all': Track all positive metric values (threshold = 0)\n * - 'needs-improvement': Track metrics exceeding good thresholds (default)\n * - 'poor': Track only critical performance issues\n *\n * @example\n * ```typescript\n * const handler = new PerformanceHandler(eventManager);\n * await handler.startTracking();\n * // Web Vitals are now being tracked with default 'needs-improvement' mode\n * handler.stopTracking();\n * ```\n */\nexport class PerformanceHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly reportedByNav: Map<string, Set<string>> = new Map();\n private readonly navigationHistory: string[] = []; // FIFO queue for tracking navigation order\n private readonly observers: PerformanceObserver[] = [];\n private vitalThresholds: Record<WebVitalType, number>;\n private navigationCounter = 0; // Counter for handling simultaneous navigations edge case\n\n constructor(eventManager: EventManager) {\n super();\n this.eventManager = eventManager;\n this.vitalThresholds = getWebVitalsThresholds(DEFAULT_WEB_VITALS_MODE);\n }\n\n /**\n * Starts tracking Web Vitals and performance metrics.\n *\n * Asynchronously loads the web-vitals library and initializes performance tracking.\n * Falls back to native Performance Observer API if web-vitals fails to load.\n *\n * **Configuration**:\n * - Reads webVitalsMode from config ('all', 'needs-improvement', 'poor')\n * - Merges webVitalsThresholds with mode defaults for custom thresholds\n * - Initializes web-vitals library observers (LCP, CLS, FCP, TTFB, INP)\n *\n * @returns Promise that resolves when tracking is initialized\n */\n async startTracking(): Promise<void> {\n const config = this.get('config');\n const mode = config?.webVitalsMode ?? DEFAULT_WEB_VITALS_MODE;\n\n this.vitalThresholds = getWebVitalsThresholds(mode);\n\n if (config?.webVitalsThresholds) {\n this.vitalThresholds = { ...this.vitalThresholds, ...config.webVitalsThresholds };\n }\n\n await this.initWebVitals();\n }\n\n /**\n * Stops tracking Web Vitals and cleans up resources.\n *\n * Disconnects all Performance Observers and clears internal state:\n * - Disconnects all active observers (web-vitals and long task)\n * - Clears navigation-based deduplication map\n * - Clears navigation history array\n * - Prevents memory leaks in long-running applications\n */\n stopTracking(): void {\n this.observers.forEach((obs, index) => {\n try {\n obs.disconnect();\n } catch (error) {\n log('debug', 'Failed to disconnect performance observer', { error, data: { observerIndex: index } });\n }\n });\n\n this.observers.length = 0;\n this.reportedByNav.clear();\n this.navigationHistory.length = 0;\n }\n\n private observeWebVitalsFallback(): void {\n this.reportTTFB();\n\n this.safeObserve(\n 'largest-contentful-paint',\n (list) => {\n const entries = list.getEntries();\n const last = entries[entries.length - 1] as (PerformanceEntry & { startTime: number }) | undefined;\n\n if (!last) {\n return;\n }\n\n this.sendVital({ type: 'LCP', value: Number(last.startTime.toFixed(PRECISION_TWO_DECIMALS)) });\n },\n { type: 'largest-contentful-paint', buffered: true },\n true,\n );\n\n let clsValue = 0;\n let currentNavId = this.getNavigationId();\n\n this.safeObserve(\n 'layout-shift',\n (list) => {\n const navId = this.getNavigationId();\n\n if (navId !== currentNavId) {\n clsValue = 0;\n currentNavId = navId;\n }\n\n const entries = list.getEntries() as LayoutShiftEntry[];\n\n for (const entry of entries) {\n if (entry.hadRecentInput === true) {\n continue;\n }\n\n const value = typeof entry.value === 'number' ? entry.value : 0;\n clsValue += value;\n }\n\n this.sendVital({ type: 'CLS', value: Number(clsValue.toFixed(PRECISION_TWO_DECIMALS)) });\n },\n { type: 'layout-shift', buffered: true },\n );\n\n this.safeObserve(\n 'paint',\n (list) => {\n for (const entry of list.getEntries()) {\n if (entry.name === 'first-contentful-paint') {\n this.sendVital({ type: 'FCP', value: Number(entry.startTime.toFixed(PRECISION_TWO_DECIMALS)) });\n }\n }\n },\n { type: 'paint', buffered: true },\n true,\n );\n\n this.safeObserve(\n 'event',\n (list) => {\n let worst = 0;\n const entries = list.getEntries() as Array<{ startTime: number; processingEnd?: number }>;\n\n for (const entry of entries) {\n const dur = (entry.processingEnd ?? 0) - (entry.startTime ?? 0);\n worst = Math.max(worst, dur);\n }\n\n if (worst > 0) {\n this.sendVital({ type: 'INP', value: Number(worst.toFixed(PRECISION_TWO_DECIMALS)) });\n }\n },\n { type: 'event', buffered: true },\n );\n }\n\n private async initWebVitals(): Promise<void> {\n try {\n const { onLCP, onCLS, onFCP, onTTFB, onINP } = await import('web-vitals');\n\n const report =\n (type: WebVitalType) =>\n (metric: { value: number }): void => {\n const value = Number(metric.value.toFixed(PRECISION_TWO_DECIMALS));\n this.sendVital({ type, value });\n };\n\n onLCP(report('LCP'), { reportAllChanges: false });\n onCLS(report('CLS'), { reportAllChanges: false });\n onFCP(report('FCP'), { reportAllChanges: false });\n onTTFB(report('TTFB'), { reportAllChanges: false });\n onINP(report('INP'), { reportAllChanges: false });\n } catch (error) {\n log('debug', 'Failed to load web-vitals library, using fallback', { error });\n this.observeWebVitalsFallback();\n }\n }\n\n private reportTTFB(): void {\n try {\n const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n\n if (!nav) {\n return;\n }\n\n const ttfb = nav.responseStart;\n\n // TTFB can be 0 in Mobile Safari when response is served from cache\n if (typeof ttfb === 'number' && Number.isFinite(ttfb)) {\n this.sendVital({ type: 'TTFB', value: Number(ttfb.toFixed(PRECISION_TWO_DECIMALS)) });\n }\n } catch (error) {\n log('debug', 'Failed to report TTFB', { error });\n }\n }\n\n private sendVital(sample: { type: WebVitalType; value: number }): void {\n if (!this.shouldSendVital(sample.type, sample.value)) {\n return;\n }\n\n const navId = this.getNavigationId();\n\n if (navId) {\n const reportedForNav = this.reportedByNav.get(navId);\n const isDuplicate = reportedForNav?.has(sample.type);\n\n if (isDuplicate) {\n return;\n }\n\n if (!reportedForNav) {\n this.reportedByNav.set(navId, new Set([sample.type]));\n this.navigationHistory.push(navId);\n\n if (this.navigationHistory.length > MAX_NAVIGATION_HISTORY) {\n const oldestNav = this.navigationHistory.shift();\n if (oldestNav) {\n this.reportedByNav.delete(oldestNav);\n }\n }\n } else {\n reportedForNav.add(sample.type);\n }\n }\n\n this.trackWebVital(sample.type, sample.value);\n }\n\n private trackWebVital(type: WebVitalType, value: number): void {\n if (!Number.isFinite(value)) {\n log('debug', 'Invalid web vital value', { data: { type, value } });\n return;\n }\n\n this.eventManager.track({\n type: EventType.WEB_VITALS,\n web_vitals: {\n type,\n value,\n },\n });\n }\n\n /**\n * Generates a unique navigation identifier for deduplication.\n *\n * **Purpose**: Creates deterministic IDs to prevent duplicate Web Vitals reporting\n * across multiple metrics for the same navigation event.\n *\n * **ID Format**: `{timestamp}_{pathname}` or `{timestamp}_{pathname}_{counter}`\n *\n * **Edge Case Handling**:\n * - If multiple navigations occur to the same pathname in the same millisecond,\n * a counter suffix is appended (e.g., `1234.56_/home_2`)\n * - Counter only added when > 1 to minimize ID length for common case\n *\n * **Why Deterministic**:\n * - Previous implementation used random string → duplicate metrics on page reload\n * - Now: Same navigation = same ID = proper deduplication via reportedByNav Map\n *\n * @returns Navigation ID string or null if navigation timing unavailable\n *\n * @internal\n */\n private getNavigationId(): string | null {\n try {\n const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n\n if (!nav) {\n return null;\n }\n\n const timestamp = nav.startTime || performance.now();\n const counter = ++this.navigationCounter;\n\n // Base ID: timestamp + pathname (deterministic for deduplication)\n const baseId = `${timestamp.toFixed(2)}_${window.location.pathname}`;\n\n // Append counter only if > 1 (edge case: simultaneous navigations)\n // This prevents collisions if two navigations occur in the same millisecond to the same path\n return counter > 1 ? `${baseId}_${counter}` : baseId;\n } catch (error) {\n log('debug', 'Failed to get navigation ID', { error });\n return null;\n }\n }\n\n private isObserverSupported(type: string): boolean {\n if (typeof PerformanceObserver === 'undefined') return false;\n const supported = PerformanceObserver.supportedEntryTypes;\n return !supported || supported.includes(type);\n }\n\n private safeObserve(\n type: string,\n cb: PerformanceObserverCallback,\n options?: PerformanceObserverInit,\n once = false,\n ): boolean {\n try {\n if (!this.isObserverSupported(type)) {\n return false;\n }\n\n const obs = new PerformanceObserver((list, observer) => {\n try {\n cb(list, observer);\n } catch (callbackError) {\n log('debug', 'Observer callback failed', {\n error: callbackError,\n data: { type },\n });\n }\n\n if (once) {\n try {\n observer.disconnect();\n } catch {\n /* empty */\n }\n }\n });\n\n obs.observe(options ?? { type, buffered: true });\n\n if (!once) {\n this.observers.push(obs);\n }\n\n return true;\n } catch (error) {\n log('debug', 'Failed to create performance observer', {\n error,\n data: { type },\n });\n return false;\n }\n }\n\n private shouldSendVital(type: WebVitalType, value?: number): boolean {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n log('debug', 'Invalid web vital value', { data: { type, value } });\n return false;\n }\n\n const threshold = this.vitalThresholds[type];\n\n if (typeof threshold === 'number' && value <= threshold) {\n return false;\n }\n\n return true;\n }\n}\n","import { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { EmitterEvent, ErrorType, EventType } from '../types';\nimport type { EmitterCallback, EmitterMap } from '../types';\nimport type { Emitter } from '../utils';\nimport { buildErrorSignatureKey, log, sanitizePii } from '../utils';\nimport {\n MAX_ERROR_MESSAGE_LENGTH,\n MAX_STACK_TRACE_LENGTH,\n ERROR_SUPPRESSION_WINDOW_MS,\n MAX_TRACKED_ERRORS,\n MAX_TRACKED_ERRORS_HARD_LIMIT,\n DEFAULT_ERROR_SAMPLING_RATE,\n ERROR_BURST_WINDOW_MS,\n ERROR_BURST_THRESHOLD,\n ERROR_BURST_BACKOFF_MS,\n MAX_ERRORS_PER_SIGNATURE_PER_PAGEVIEW,\n MAX_PAGEVIEW_SIGNATURE_KEYS,\n} from '../constants/error.constants';\n\n/**\n * Captures JavaScript errors and unhandled promise rejections for debugging and monitoring.\n *\n * **Events Generated**: `error`\n *\n * **Features**:\n * - Tracks JavaScript runtime errors and unhandled promise rejections\n * - Configurable error sampling rate (default: 100%)\n * - PII sanitization (emails, phone numbers, credit cards, API keys, tokens)\n * - Message truncation (500 character limit)\n * - Burst detection (>10 errors/second triggers 5-second cooldown)\n * - Deduplication within 5-second window per error type+message\n * - Per-pageview signature cap (`MAX_ERRORS_PER_SIGNATURE_PER_PAGEVIEW`): after the\n * 5s dedup window expires, the same `(normalizedMessage, filename, line)` may only\n * recur up to N times per pageview. Counter resets on `pagehide`, `SESSION_START`,\n * and `PAGE_VIEW` (covers SPA navigation via patched History API + popstate/hashchange).\n *\n * **Privacy Protection**:\n * - Automatically redacts PII from error messages before storage\n * - Sanitizes emails, phone numbers, credit cards, IBAN, API keys, Bearer tokens\n *\n * @see src/handlers/README.md (`## ErrorHandler` section) for detailed documentation\n */\nexport class ErrorHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly emitter?: Emitter;\n private readonly recentErrors = new Map<string, number>();\n private readonly pageviewSignatureCounts = new Map<string, number>();\n private errorBurstCounter = 0;\n private burstWindowStart = 0;\n private burstBackoffUntil = 0;\n private pagehideHandler: (() => void) | null = null;\n private pageviewResetListener: EmitterCallback<EmitterMap[EmitterEvent.EVENT]> | null = null;\n\n constructor(eventManager: EventManager, emitter?: Emitter) {\n super();\n this.eventManager = eventManager;\n this.emitter = emitter;\n }\n\n /**\n * Starts tracking JavaScript errors and promise rejections.\n *\n * - Registers global error event listener\n * - Registers unhandledrejection event listener\n * - Registers pagehide listener to reset the per-pageview signature counter\n * - Subscribes to emitter SESSION_START + PAGE_VIEW to reset the counter on new\n * sessions and SPA route changes (the only signal `pagehide` does not cover)\n */\n startTracking(): void {\n window.addEventListener('error', this.handleError);\n window.addEventListener('unhandledrejection', this.handleRejection);\n\n this.pagehideHandler = (): void => {\n this.resetPageviewCounter();\n };\n window.addEventListener('pagehide', this.pagehideHandler, { passive: true });\n\n if (this.emitter) {\n this.pageviewResetListener = (event): void => {\n if (event.type === EventType.SESSION_START || event.type === EventType.PAGE_VIEW) {\n this.resetPageviewCounter();\n }\n };\n this.emitter.on(EmitterEvent.EVENT, this.pageviewResetListener);\n }\n }\n\n /**\n * Stops tracking errors and cleans up resources.\n *\n * - Removes error event listeners\n * - Removes pagehide listener and unsubscribes from emitter\n * - Clears recent errors and pageview signature counters\n * - Resets burst detection counters\n */\n stopTracking(): void {\n window.removeEventListener('error', this.handleError);\n window.removeEventListener('unhandledrejection', this.handleRejection);\n\n if (this.pagehideHandler) {\n window.removeEventListener('pagehide', this.pagehideHandler);\n this.pagehideHandler = null;\n }\n\n if (this.emitter && this.pageviewResetListener) {\n this.emitter.off(EmitterEvent.EVENT, this.pageviewResetListener);\n this.pageviewResetListener = null;\n }\n\n this.recentErrors.clear();\n this.pageviewSignatureCounts.clear();\n this.errorBurstCounter = 0;\n this.burstWindowStart = 0;\n this.burstBackoffUntil = 0;\n }\n\n /**\n * Clears the per-pageview signature counter.\n *\n * Public so `App` or tests can drive a reset explicitly; the handler itself wires\n * `pagehide` and emitter `SESSION_START` / `PAGE_VIEW` in `startTracking()`.\n */\n resetPageviewCounter(): void {\n this.pageviewSignatureCounts.clear();\n }\n\n /**\n * Checks sampling rate and burst detection\n * Returns false if in cooldown period after burst detection\n */\n private shouldSample(): boolean {\n const now = Date.now();\n\n if (now < this.burstBackoffUntil) {\n return false;\n }\n\n if (now - this.burstWindowStart > ERROR_BURST_WINDOW_MS) {\n this.errorBurstCounter = 0;\n this.burstWindowStart = now;\n }\n\n this.errorBurstCounter++;\n\n if (this.errorBurstCounter > ERROR_BURST_THRESHOLD) {\n this.burstBackoffUntil = now + ERROR_BURST_BACKOFF_MS;\n log('debug', 'Error burst detected - entering cooldown', {\n data: {\n errorsInWindow: this.errorBurstCounter,\n cooldownMs: ERROR_BURST_BACKOFF_MS,\n },\n });\n return false;\n }\n\n const config = this.get('config');\n const samplingRate = config.errorSampling ?? DEFAULT_ERROR_SAMPLING_RATE;\n return Math.random() < samplingRate;\n }\n\n /**\n * Returns true when the per-pageview signature cap has been hit for this error.\n * Dropped errors do not increment the counter — the 5s suppression window already\n * silences identical repeats, and double-counting here would skew the cap for any\n * later signature that recycles the same map key after a counter reset.\n */\n private shouldThrottleBySignature(input: { message: string; filename?: string; line?: number }): boolean {\n const key = buildErrorSignatureKey({\n message: input.message,\n filename: input.filename,\n line: input.line,\n });\n const current = this.pageviewSignatureCounts.get(key) ?? 0;\n\n if (current >= MAX_ERRORS_PER_SIGNATURE_PER_PAGEVIEW) {\n log('debug', 'Error throttled (pageview cap)', {\n data: { signature: key, count: current },\n });\n return true;\n }\n\n const nextCount = current + 1;\n this.pageviewSignatureCounts.set(key, nextCount);\n\n if (this.pageviewSignatureCounts.size > MAX_PAGEVIEW_SIGNATURE_KEYS) {\n this.pageviewSignatureCounts.clear();\n this.pageviewSignatureCounts.set(key, nextCount);\n }\n\n return false;\n }\n\n private readonly handleError = (event: ErrorEvent): void => {\n if (!this.shouldSample()) {\n return;\n }\n\n const sanitizedMessage = this.sanitize(event.message || 'Unknown error');\n\n if (this.shouldSuppressError(ErrorType.JS_ERROR, sanitizedMessage)) {\n return;\n }\n\n if (\n this.shouldThrottleBySignature({\n message: sanitizedMessage,\n filename: event.filename,\n line: event.lineno,\n })\n ) {\n return;\n }\n\n const stack = typeof event.error?.stack === 'string' ? this.truncateStack(event.error.stack) : undefined;\n const errorName =\n typeof event.error?.name === 'string' && event.error.name !== 'Error' ? event.error.name : undefined;\n this.eventManager.track({\n type: EventType.ERROR,\n error_data: {\n type: ErrorType.JS_ERROR,\n message: sanitizedMessage,\n ...(errorName !== undefined && { name: errorName }),\n ...(event.filename !== '' && { filename: event.filename }),\n ...(event.lineno !== 0 && { line: event.lineno }),\n ...(event.colno !== 0 && { column: event.colno }),\n ...(stack !== undefined && { stack }),\n },\n });\n };\n\n private readonly handleRejection = (event: PromiseRejectionEvent): void => {\n if (!this.shouldSample()) {\n return;\n }\n\n const message = this.extractRejectionMessage(event.reason);\n const sanitizedMessage = this.sanitize(message);\n\n if (this.shouldSuppressError(ErrorType.PROMISE_REJECTION, sanitizedMessage)) {\n return;\n }\n\n // PromiseRejectionEvent carries no filename/line, so the signature collapses\n // to `(normalizedMessage, '', '')` — rejections from different code paths that\n // happen to share a message hit the same cap. Accepted trade-off: the message\n // alone is usually distinctive enough, and over-throttling rejections is safer\n // than letting them flood the queue.\n if (this.shouldThrottleBySignature({ message: sanitizedMessage })) {\n return;\n }\n\n const stack =\n event.reason instanceof Error && typeof event.reason.stack === 'string'\n ? this.truncateStack(event.reason.stack)\n : undefined;\n const errorName = event.reason instanceof Error && event.reason.name !== 'Error' ? event.reason.name : undefined;\n this.eventManager.track({\n type: EventType.ERROR,\n error_data: {\n type: ErrorType.PROMISE_REJECTION,\n message: sanitizedMessage,\n ...(errorName !== undefined && { name: errorName }),\n ...(stack !== undefined && { stack }),\n },\n });\n };\n\n private extractRejectionMessage(reason: unknown): string {\n if (reason == null) return 'Unknown rejection';\n\n if (typeof reason === 'string') return reason;\n\n if (reason instanceof Error) {\n return reason.message;\n }\n\n if (typeof reason === 'object' && 'message' in reason) {\n return String(reason.message);\n }\n\n try {\n return JSON.stringify(reason);\n } catch {\n return 'Unserializable rejection';\n }\n }\n\n private sanitize(text: string): string {\n const truncated = text.length > MAX_ERROR_MESSAGE_LENGTH ? text.slice(0, MAX_ERROR_MESSAGE_LENGTH) + '...' : text;\n return sanitizePii(truncated);\n }\n\n private shouldSuppressError(type: ErrorType, message: string): boolean {\n const now = Date.now();\n const key = `${type}:${message}`;\n const lastSeenAt = this.recentErrors.get(key);\n\n if (lastSeenAt !== undefined && now - lastSeenAt < ERROR_SUPPRESSION_WINDOW_MS) {\n this.recentErrors.set(key, now);\n return true;\n }\n\n this.recentErrors.set(key, now);\n\n if (this.recentErrors.size > MAX_TRACKED_ERRORS_HARD_LIMIT) {\n this.recentErrors.clear();\n this.recentErrors.set(key, now);\n\n return false;\n }\n\n if (this.recentErrors.size > MAX_TRACKED_ERRORS) {\n this.pruneOldErrors();\n }\n\n return false;\n }\n\n private static readonly TRUNCATION_SUFFIX = '\\n...truncated';\n\n private truncateStack(stack: string): string {\n if (stack.length <= MAX_STACK_TRACE_LENGTH) return sanitizePii(stack);\n const limit = MAX_STACK_TRACE_LENGTH - ErrorHandler.TRUNCATION_SUFFIX.length;\n const truncated = stack.slice(0, limit) + ErrorHandler.TRUNCATION_SUFFIX;\n return sanitizePii(truncated);\n }\n\n private pruneOldErrors(): void {\n const now = Date.now();\n for (const [key, timestamp] of this.recentErrors.entries()) {\n if (now - timestamp > ERROR_SUPPRESSION_WINDOW_MS) {\n this.recentErrors.delete(key);\n }\n }\n\n if (this.recentErrors.size <= MAX_TRACKED_ERRORS) {\n return;\n }\n\n const entries = Array.from(this.recentErrors.entries()).sort((a, b) => a[1] - b[1]);\n const excess = this.recentErrors.size - MAX_TRACKED_ERRORS;\n\n for (let index = 0; index < excess; index += 1) {\n const entry = entries[index];\n if (entry) {\n this.recentErrors.delete(entry[0]);\n }\n }\n }\n}\n","import { EventManager } from './managers/event.manager';\nimport { UserManager } from './managers/user.manager';\nimport { StateManager } from './managers/state.manager';\nimport { SessionHandler } from './handlers/session.handler';\nimport { PageViewHandler } from './handlers/page-view.handler';\nimport { ClickHandler } from './handlers/click.handler';\nimport { ScrollHandler } from './handlers/scroll.handler';\nimport { ShopifyCartLinker } from './ecommerce';\nimport {\n Config,\n EventType,\n EmitterCallback,\n EmitterEvent,\n EmitterMap,\n EventOptions,\n IdentifyData,\n Mode,\n InitResult,\n} from './types';\nimport {\n isEventValid,\n getDeviceInfo,\n normalizeUrl,\n Emitter,\n getCollectApiUrls,\n detectQaMode,\n log,\n generateUUID,\n sanitizeTraits,\n} from './utils';\nimport { StorageManager } from './managers/storage.manager';\nimport { SCROLL_DEBOUNCE_TIME_MS, SCROLL_SUPPRESS_MULTIPLIER } from './constants/config.constants';\nimport { IDENTITY_KEY, PENDING_IDENTITY_KEY, USER_ID_KEY } from './constants/storage.constants';\nimport { PerformanceHandler } from './handlers/performance.handler';\nimport { ErrorHandler } from './handlers/error.handler';\n\nexport class App extends StateManager {\n private isInitialized = false;\n private suppressNextScrollTimer: number | null = null;\n private pageUnloadHandler: (() => void) | null = null;\n private pageShowHandler: ((event: PageTransitionEvent) => void) | null = null;\n private visibilityFlushHandler: (() => void) | null = null;\n\n private readonly emitter = new Emitter();\n\n protected managers: {\n storage?: StorageManager;\n event?: EventManager;\n } = {};\n\n protected handlers: {\n session?: SessionHandler;\n pageView?: PageViewHandler;\n click?: ClickHandler;\n scroll?: ScrollHandler;\n performance?: PerformanceHandler;\n error?: ErrorHandler;\n } = {};\n\n private integrationInstances: {\n shopifyCartLinker?: ShopifyCartLinker;\n } = {};\n\n get initialized(): boolean {\n return this.isInitialized;\n }\n\n /**\n * Initializes TraceLog with configuration.\n *\n * @internal Called from api.init()\n */\n async init(config: Config = {}): Promise<InitResult> {\n if (this.isInitialized) {\n return { sessionId: this.get('sessionId') ?? '' };\n }\n\n this.managers.storage = new StorageManager();\n\n try {\n this.setupState(config);\n\n this.managers.event = new EventManager(this.managers.storage, this.emitter);\n\n this.loadPersistedIdentity();\n\n this.initializeHandlers();\n this.setupPageLifecycleListeners();\n\n await this.managers.event.recoverPersistedEvents().catch((error) => {\n log('warn', 'Failed to recover persisted events', { error });\n });\n\n this.isInitialized = true;\n\n return { sessionId: this.get('sessionId') ?? '' };\n } catch (error) {\n this.destroy(true);\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`[TraceLog] TraceLog initialization failed: ${errorMessage}`);\n }\n }\n\n /**\n * Sends a custom event with optional metadata and options.\n *\n * @internal Called from api.event()\n */\n sendCustomEvent(\n name: string,\n metadata?: Record<string, unknown> | Record<string, unknown>[],\n options?: EventOptions,\n ): void {\n if (!this.managers.event) {\n log('warn', 'Cannot send custom event: TraceLog not initialized', { data: { name } });\n return;\n }\n\n let normalizedMetadata = metadata;\n\n if (metadata && typeof metadata === 'object' && !Array.isArray(metadata)) {\n if (Object.getPrototypeOf(metadata) !== Object.prototype) {\n normalizedMetadata = Object.assign({}, metadata);\n }\n }\n\n const { valid, error, sanitizedMetadata } = isEventValid(name, normalizedMetadata);\n\n if (!valid) {\n if (this.get('mode') === Mode.QA) {\n throw new Error(`[TraceLog] Custom event \"${name}\" validation failed: ${error}`);\n }\n\n log('warn', `Custom event \"${name}\" dropped: ${error}`);\n return;\n }\n\n this.managers.event.track({\n type: EventType.CUSTOM,\n custom_event: {\n name,\n ...(sanitizedMetadata && { metadata: sanitizedMetadata }),\n },\n });\n\n if (options?.critical === true) {\n const ok = this.managers.event.flushImmediatelySync();\n if (!ok) {\n log('debug', 'Critical event flush returned false (deferred to in-flight send or empty queue)', {\n data: { name },\n });\n }\n }\n }\n\n on<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n this.emitter.on(event, callback);\n }\n\n off<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n this.emitter.off(event, callback);\n }\n\n /**\n * Destroys the TraceLog instance and cleans up all resources.\n *\n * @internal Called from api.destroy()\n */\n destroy(force = false): void {\n if (!this.isInitialized && !force) {\n return;\n }\n\n Object.values(this.handlers)\n .filter(Boolean)\n .forEach((handler) => {\n try {\n handler.stopTracking();\n } catch (error) {\n log('warn', 'Failed to stop tracking', { error });\n }\n });\n\n if (this.suppressNextScrollTimer) {\n clearTimeout(this.suppressNextScrollTimer);\n this.suppressNextScrollTimer = null;\n }\n\n if (this.pageUnloadHandler) {\n window.removeEventListener('pagehide', this.pageUnloadHandler);\n window.removeEventListener('beforeunload', this.pageUnloadHandler);\n this.pageUnloadHandler = null;\n }\n\n if (this.pageShowHandler) {\n window.removeEventListener('pageshow', this.pageShowHandler);\n this.pageShowHandler = null;\n }\n\n if (this.visibilityFlushHandler) {\n document.removeEventListener('visibilitychange', this.visibilityFlushHandler);\n this.visibilityFlushHandler = null;\n }\n\n this.managers.event?.flushImmediatelySync();\n this.managers.event?.stop();\n\n this.emitter.removeAllListeners();\n\n this.set('suppressNextScroll', false);\n this.set('sessionId', null);\n this.set('identity', undefined);\n this.clearPersistedIdentity();\n\n this.integrationInstances.shopifyCartLinker?.deactivate();\n this.integrationInstances = {};\n\n this.isInitialized = false;\n this.handlers = {};\n this.managers = {};\n }\n\n private setupState(config: Config = {}): void {\n this.set('config', config);\n\n const userId = UserManager.getId(this.managers.storage as StorageManager);\n this.set('userId', userId);\n\n const collectApiUrls = getCollectApiUrls(config);\n this.set('collectApiUrls', collectApiUrls);\n\n const device = getDeviceInfo();\n this.set('device', device);\n\n const pageUrl = normalizeUrl(window.location.href, config.sensitiveQueryParams);\n this.set('pageUrl', pageUrl);\n\n const isQaMode = detectQaMode();\n\n if (isQaMode) {\n this.set('mode', Mode.QA);\n }\n }\n\n /**\n * @internal Used by api.ts for configuration access\n */\n public getConfig(): Config {\n return this.get('config');\n }\n\n /**\n * @internal Used by api.ts for backend URL access\n */\n public getCollectApiUrls(): { saas?: string } {\n return this.get('collectApiUrls');\n }\n\n /**\n * @internal Used by api.ts for event operations\n */\n public getEventManager(): EventManager | undefined {\n return this.managers.event;\n }\n\n /**\n * @internal Used by api.getSessionId()\n */\n public getSessionId(): string | null {\n return this.get('sessionId');\n }\n\n /**\n * @internal Used by api.getUserId()\n */\n public getUserId(): string | null {\n return this.get('userId');\n }\n\n /**\n * Associates the current anonymous visitor with a known user identity.\n *\n * Identity is persisted to localStorage (project-scoped) and included in every\n * subsequent batch payload so the backend always has the latest identity.\n *\n * @param userId - External user identifier (email, customer_id, etc.). Trimmed; max 256 chars.\n * @param traits - Optional user attributes (name, email, plan, etc.). Only string values\n * are kept; non-string fields, arrays, and null are dropped silently.\n *\n * @internal Called from api.identify()\n */\n public identify(userId: string, traits?: Record<string, string>): void {\n if (!userId || typeof userId !== 'string' || userId.trim().length === 0) {\n log('warn', 'identify() called with invalid userId', {\n data: { type: typeof userId, length: typeof userId === 'string' ? userId.trim().length : 0 },\n });\n return;\n }\n\n if (userId.trim().length > 256) {\n log('warn', 'identify() userId exceeds 256 characters', { data: { length: userId.trim().length } });\n return;\n }\n\n const trimmedUserId = userId.trim();\n const validTraits = sanitizeTraits(traits);\n const identity: IdentifyData = {\n userId: trimmedUserId,\n ...(validTraits ? { traits: validTraits } : {}),\n };\n\n this.set('identity', identity);\n this.persistIdentity(identity);\n\n log('debug', 'Visitor identified', {\n data: { userIdLength: trimmedUserId.length, traitKeys: validTraits ? Object.keys(validTraits) : [] },\n });\n }\n\n /**\n * Clears identity, regenerates UUID, and starts a fresh session.\n *\n * Use for logout flows: the previous visitor profile remains in the backend,\n * and the next user in the same browser gets a clean anonymous profile.\n *\n * Pending events are flushed under the OLD identity first via async fetch\n * (so any in-flight authentication headers are preserved). Then the identity\n * is cleared, the userId is regenerated, and the session handler is\n * restarted to emit a new `SESSION_START`.\n *\n * @internal Called from api.resetIdentity()\n */\n public async resetIdentity(): Promise<void> {\n await this.managers.event?.flushImmediately().catch((error) => {\n log('debug', 'Failed to flush before identity reset', { error });\n return false;\n });\n\n this.set('identity', undefined);\n this.clearPersistedIdentity();\n\n const newUserId = generateUUID();\n (this.managers.storage as StorageManager).setItem(USER_ID_KEY, newUserId);\n this.set('userId', newUserId);\n\n this.set('hasStartSession', false);\n this.set('sessionId', null);\n this.handlers.session?.stopTracking();\n this.handlers.session?.startTracking();\n\n log('debug', 'Identity reset, new UUID generated');\n }\n\n /**\n * Returns the project ID used for identity storage scoping.\n */\n private getProjectId(): string {\n const config = this.get('config');\n return config?.integrations?.tracelog?.projectId ?? 'custom';\n }\n\n /**\n * Persists identity to localStorage under the project-scoped key.\n */\n private persistIdentity(identity: IdentifyData): void {\n try {\n const projectId = this.getProjectId();\n const key = IDENTITY_KEY(projectId);\n (this.managers.storage as StorageManager).setItem(key, JSON.stringify(identity));\n } catch {\n log('debug', 'Failed to persist identity to localStorage');\n }\n }\n\n /**\n * Loads identity from localStorage on init.\n * Also migrates pending identity (set before init) to the project-scoped key.\n */\n private loadPersistedIdentity(): void {\n const storage = this.managers.storage as StorageManager;\n const projectId = this.getProjectId();\n const projectKey = IDENTITY_KEY(projectId);\n\n try {\n const pendingRaw = storage.getItem(PENDING_IDENTITY_KEY);\n if (pendingRaw) {\n const pending = JSON.parse(pendingRaw) as IdentifyData;\n storage.removeItem(PENDING_IDENTITY_KEY);\n\n if (!this.isValidIdentityData(pending)) {\n log('debug', 'Invalid pending identity in localStorage, discarded');\n return;\n }\n\n const normalizedPending = this.normalizePersistedIdentity(pending);\n storage.setItem(projectKey, JSON.stringify(normalizedPending));\n this.set('identity', normalizedPending);\n log('debug', 'Migrated pending identity to project-scoped key');\n return;\n }\n } catch {\n storage.removeItem(PENDING_IDENTITY_KEY);\n }\n\n try {\n const raw = storage.getItem(projectKey);\n if (raw) {\n const identity = JSON.parse(raw) as IdentifyData;\n\n if (!this.isValidIdentityData(identity)) {\n storage.removeItem(projectKey);\n log('debug', 'Invalid persisted identity in localStorage, discarded');\n return;\n }\n\n const normalizedIdentity = this.normalizePersistedIdentity(identity);\n this.set('identity', normalizedIdentity);\n log('debug', 'Loaded persisted identity');\n }\n } catch {\n log('debug', 'Failed to load persisted identity');\n }\n }\n\n /**\n * Validates identity data loaded from localStorage. `traits` is intentionally\n * accepted as `unknown` here: `normalizePersistedIdentity()` runs it through\n * `sanitizeTraits()` so tampered values are dropped silently instead of\n * rejecting an otherwise-valid identity.\n */\n private isValidIdentityData(data: unknown): data is IdentifyData {\n if (!data || typeof data !== 'object') return false;\n const { userId } = data as Record<string, unknown>;\n\n if (typeof userId !== 'string' || userId.trim().length === 0 || userId.trim().length > 256) return false;\n\n return true;\n }\n\n /**\n * Trims the `userId` and re-sanitizes `traits` through the same gate\n * `identify()` uses at call time, defending later batches against tampered\n * localStorage values.\n */\n private normalizePersistedIdentity(identity: IdentifyData): IdentifyData {\n const validTraits = sanitizeTraits(identity.traits);\n return {\n userId: identity.userId.trim(),\n ...(validTraits ? { traits: validTraits } : {}),\n };\n }\n\n /**\n * Clears persisted identity from localStorage.\n */\n private clearPersistedIdentity(): void {\n try {\n const storage = this.managers.storage as StorageManager;\n const projectId = this.getProjectId();\n storage.removeItem(IDENTITY_KEY(projectId));\n storage.removeItem(PENDING_IDENTITY_KEY);\n } catch {\n log('debug', 'Failed to clear persisted identity');\n }\n }\n\n private setupPageLifecycleListeners(): void {\n this.pageUnloadHandler = (): void => {\n this.managers.event?.flushImmediatelySync();\n };\n\n this.pageShowHandler = (event: PageTransitionEvent): void => {\n if (event.persisted) {\n void this.managers.event?.recoverPersistedEvents().catch((error) => {\n log('warn', 'Failed to recover persisted events on bfcache restore', { error });\n });\n }\n };\n\n this.visibilityFlushHandler = (): void => {\n if (typeof document === 'undefined' || !document.hidden) {\n return;\n }\n if (this.get('config').flushOnPageHidden === false) {\n return;\n }\n this.managers.event?.flushImmediatelySync();\n };\n\n window.addEventListener('pagehide', this.pageUnloadHandler);\n window.addEventListener('beforeunload', this.pageUnloadHandler);\n window.addEventListener('pageshow', this.pageShowHandler);\n document.addEventListener('visibilitychange', this.visibilityFlushHandler);\n }\n\n private initializeHandlers(): void {\n const config = this.get('config');\n\n this.handlers.session = new SessionHandler(\n this.managers.storage as StorageManager,\n this.managers.event as EventManager,\n );\n\n this.handlers.session.startTracking();\n\n const onPageView = (): void => {\n this.set('suppressNextScroll', true);\n\n if (this.suppressNextScrollTimer) {\n clearTimeout(this.suppressNextScrollTimer);\n }\n\n this.suppressNextScrollTimer = window.setTimeout(() => {\n this.set('suppressNextScroll', false);\n }, SCROLL_DEBOUNCE_TIME_MS * SCROLL_SUPPRESS_MULTIPLIER);\n };\n\n this.handlers.pageView = new PageViewHandler(this.managers.event as EventManager, onPageView);\n this.handlers.pageView.startTracking();\n\n this.handlers.click = new ClickHandler(this.managers.event as EventManager);\n this.handlers.click.startTracking();\n\n this.handlers.scroll = new ScrollHandler(this.managers.event as EventManager);\n this.handlers.scroll.startTracking();\n\n this.handlers.performance = new PerformanceHandler(this.managers.event as EventManager);\n this.handlers.performance.startTracking().catch((error) => {\n log('warn', 'Failed to start performance tracking', { error });\n });\n\n this.handlers.error = new ErrorHandler(this.managers.event as EventManager, this.emitter);\n this.handlers.error.startTracking();\n\n if (config.integrations?.tracelog?.shopify) {\n const linker = new ShopifyCartLinker();\n linker.activate();\n this.integrationInstances.shopifyCartLinker = linker;\n\n this.emitter.on(EmitterEvent.EVENT, (event) => {\n if (event.type === EventType.SESSION_START) {\n linker.onSessionChange();\n }\n });\n }\n }\n}\n","import { App } from './app';\nimport { MetadataType, IdentifyData, Config, EmitterCallback, EmitterMap, InitResult, EventOptions } from './types';\nimport { log, validateAndNormalizeConfig, sanitizeTraits } from './utils';\nimport { INITIALIZATION_TIMEOUT_MS } from './constants';\nimport { PENDING_IDENTITY_KEY } from './constants/storage.constants';\nimport './types/window.types';\n\ninterface PendingListener {\n event: keyof EmitterMap;\n callback: EmitterCallback<EmitterMap[keyof EmitterMap]>;\n}\n\nconst pendingListeners: PendingListener[] = [];\n\nlet app: App | null = null;\nlet isInitializing = false;\nlet isDestroying = false;\nlet initPromise: Promise<InitResult> | null = null;\n\n/**\n * Initializes TraceLog and begins tracking user interactions.\n *\n * Important: Register listeners with on() before calling init() to capture initial events.\n *\n * @param config - Optional configuration object\n * @returns Promise with sessionId (empty string in SSR/disabled environments)\n * @throws {Error} If initialization fails or times out\n *\n * @example\n * ```typescript\n * const { sessionId } = await tracelog.init({\n * integrations: {\n * tracelog: { projectId: 'your-project-id' }\n * }\n * });\n * console.log('Session:', sessionId);\n * ```\n */\nexport const init = async (config?: Config): Promise<InitResult> => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return { sessionId: '' };\n }\n\n isDestroying = false;\n\n if (window.__traceLogDisabled === true) {\n return { sessionId: '' };\n }\n\n if (app) {\n return { sessionId: app.getSessionId() ?? '' };\n }\n\n if (isInitializing && initPromise) {\n return initPromise;\n }\n\n isInitializing = true;\n\n initPromise = (async (): Promise<InitResult> => {\n try {\n const validatedConfig = validateAndNormalizeConfig(config ?? {});\n const instance = new App();\n\n try {\n pendingListeners.forEach(({ event, callback }) => {\n instance.on(event, callback);\n });\n\n pendingListeners.length = 0;\n\n const appInitPromise = instance.init(validatedConfig);\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`[TraceLog] Initialization timeout after ${INITIALIZATION_TIMEOUT_MS}ms`));\n }, INITIALIZATION_TIMEOUT_MS);\n });\n\n const result = await Promise.race([appInitPromise, timeoutPromise]);\n\n app = instance;\n\n return result;\n } catch (error) {\n try {\n instance.destroy(true);\n } catch (cleanupError) {\n log('error', 'Failed to cleanup partially initialized app', { error: cleanupError });\n }\n\n throw error;\n }\n } catch (error) {\n app = null;\n throw error;\n } finally {\n isInitializing = false;\n initPromise = null;\n }\n })();\n\n return initPromise;\n};\n\n/**\n * Tracks a custom analytics event with optional metadata.\n *\n * @param name - Event identifier (e.g., 'checkout_completed')\n * @param metadata - Optional event data (object or array of objects)\n * @param options - Optional event options. Pass `{ critical: true }` for\n * high-value events that must survive an imminent page unload (e.g., a\n * purchase tracked right before `window.location.href = '/thanks'`).\n * Critical events flush via `sendBeacon`, which the browser guarantees\n * to queue for delivery even if the page closes immediately after.\n * @throws {Error} If called before init() or during destroy()\n *\n * @example\n * ```typescript\n * tracelog.event('product_viewed', { productId: 'abc-123', price: 299.99 });\n *\n * // Critical event (e.g., right before redirecting to a thank-you page)\n * tracelog.event('purchase_completed', { orderId: 'ord-789' }, { critical: true });\n * window.location.href = '/thanks';\n * ```\n */\nexport const event = (\n name: string,\n metadata?: Record<string, MetadataType> | Record<string, MetadataType>[],\n options?: EventOptions,\n): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app) {\n throw new Error('[TraceLog] TraceLog not initialized. Please call init() first.');\n }\n\n if (isDestroying) {\n throw new Error('[TraceLog] Cannot send events while TraceLog is being destroyed');\n }\n\n app.sendCustomEvent(name, metadata, options);\n};\n\n/**\n * Subscribes to TraceLog events for real-time consumption.\n *\n * Important: Register listeners BEFORE calling init() to capture SESSION_START and PAGE_VIEW.\n *\n * @param event - Event type ('event' or 'queue')\n * @param callback - Handler function called when event fires\n *\n * @example\n * ```typescript\n * tracelog.on('event', (event) => console.log(event.type));\n * await tracelog.init();\n * ```\n */\nexport const on = <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app || isInitializing) {\n pendingListeners.push({ event, callback } as PendingListener);\n return;\n }\n\n app.on(event, callback);\n};\n\n/**\n * Unsubscribes from TraceLog events.\n *\n * @param event - Event type to unsubscribe from\n * @param callback - Exact callback function reference used in on()\n */\nexport const off = <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app) {\n const index = pendingListeners.findIndex((l) => l.event === event && l.callback === callback);\n if (index !== -1) {\n pendingListeners.splice(index, 1);\n }\n return;\n }\n\n app.off(event, callback);\n};\n\n/**\n * Checks if TraceLog is currently initialized.\n */\nexport const isInitialized = (): boolean => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return false;\n }\n\n return app !== null;\n};\n\n/**\n * Returns the current session ID.\n *\n * Session ID is generated during init() and persists across page refreshes\n * within the session timeout window (default 15 minutes).\n *\n * @returns Session ID string, or null if not initialized\n */\nexport const getSessionId = (): string | null => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return null;\n }\n\n if (!app) {\n return null;\n }\n\n return app.getSessionId();\n};\n\n/**\n * Returns the current user ID, or null if `init()` has not been called yet.\n */\nexport const getUserId = (): string | null => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return null;\n }\n\n if (!app) {\n return null;\n }\n\n return app.getUserId();\n};\n\n/**\n * Stops all tracking, cleans up listeners, and flushes pending events.\n *\n * Sends remaining events with sendBeacon before cleanup.\n *\n * @throws {Error} If destroy operation is already in progress\n */\nexport const destroy = (): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (isDestroying) {\n throw new Error('[TraceLog] Destroy operation already in progress');\n }\n\n if (!app) {\n isDestroying = false;\n\n return;\n }\n\n isDestroying = true;\n\n try {\n app.destroy();\n app = null;\n isInitializing = false;\n initPromise = null;\n pendingListeners.length = 0;\n\n if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined' && window.__traceLogBridge) {\n window.__traceLogBridge = undefined;\n }\n\n isDestroying = false;\n } catch (error) {\n app = null;\n isInitializing = false;\n initPromise = null;\n\n pendingListeners.length = 0;\n\n isDestroying = false;\n\n log('warn', 'Error during destroy, forced cleanup completed', { error });\n }\n};\n\n/**\n * Associates the current anonymous visitor with a known user identity.\n *\n * Can be called before or after init(). If called before init(), the identity is\n * persisted to localStorage and applied automatically when init() runs.\n *\n * Identity is included in every event batch (piggyback), so the backend always\n * receives the latest identity. Calling identify() multiple times overwrites\n * (last-write-wins).\n *\n * @param userId - External user identifier (email, customer_id, etc.). Max 256 chars.\n * @param traits - Optional user attributes (name, email, plan, etc.). Only string\n * values are kept; non-string fields, arrays, and null are dropped silently.\n *\n * @example\n * ```typescript\n * // After login, with traits\n * tracelog.identify('cust_123', { name: 'Maria Garcia', plan: 'pro' });\n *\n * // Before init (identity queued, applied on init)\n * tracelog.identify('cust_123');\n * await tracelog.init({ integrations: { tracelog: { projectId: '...' } } });\n * ```\n */\nexport const identify = (userId: string, traits?: Record<string, string>): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!userId || typeof userId !== 'string' || userId.trim().length === 0) {\n log('warn', 'identify() called with invalid userId');\n return;\n }\n\n if (userId.trim().length > 256) {\n log('warn', 'identify() userId exceeds 256 characters');\n return;\n }\n\n if (isDestroying) {\n log('warn', 'Cannot identify while TraceLog is being destroyed');\n return;\n }\n\n if (app) {\n app.identify(userId, traits);\n return;\n }\n\n try {\n const validTraits = sanitizeTraits(traits);\n const identity: IdentifyData = {\n userId: userId.trim(),\n ...(validTraits ? { traits: validTraits } : {}),\n };\n localStorage.setItem(PENDING_IDENTITY_KEY, JSON.stringify(identity));\n log('debug', 'Identity persisted pre-init (will be applied on init)');\n } catch {\n log('debug', 'Failed to persist pre-init identity');\n }\n};\n\n/**\n * Clears identity, regenerates the visitor UUID, and starts a new session.\n *\n * Use for logout flows. The previous visitor profile remains intact in the\n * backend; the next user in the same browser gets a clean anonymous profile.\n *\n * Pending events are flushed under the OLD identity first via async fetch\n * (so any in-flight authentication headers are preserved), then the identity\n * is cleared, a fresh `user_id` is generated, and a new `SESSION_START` is\n * emitted.\n *\n * Safe to call before init(): clears any pending pre-init identity silently.\n *\n * @throws {Error} If called during destroy()\n *\n * @example\n * ```typescript\n * // On logout\n * await tracelog.resetIdentity();\n * ```\n */\nexport const resetIdentity = async (): Promise<void> => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app) {\n try {\n localStorage.removeItem(PENDING_IDENTITY_KEY);\n } catch {\n // Silent — storage may be disabled or full\n }\n return;\n }\n\n if (isDestroying) {\n throw new Error('[TraceLog] Cannot reset identity while TraceLog is being destroyed');\n }\n\n await app.resetIdentity();\n};\n\n/**\n * @internal TestBridge API - development only\n */\nexport const __setAppInstance = (instance: App | null): void => {\n if (process.env.NODE_ENV !== 'development') {\n return;\n }\n\n if (instance !== null) {\n const hasRequiredMethods =\n typeof instance === 'object' &&\n 'init' in instance &&\n 'destroy' in instance &&\n 'on' in instance &&\n 'off' in instance;\n\n if (!hasRequiredMethods) {\n throw new Error('[TraceLog] Invalid app instance type');\n }\n }\n\n if (app !== null && instance !== null && app !== instance) {\n throw new Error('[TraceLog] Cannot overwrite existing app instance. Call destroy() first.');\n }\n\n app = instance;\n};\n\n/**\n * @internal TestBridge state accessors - development only\n */\nexport const __getInitState = (): { isInitializing: boolean; isDestroying: boolean } => {\n if (process.env.NODE_ENV !== 'development') {\n return { isInitializing: false, isDestroying: false };\n }\n return { isInitializing, isDestroying };\n};\n\nif (process.env.NODE_ENV === 'development' && typeof window !== 'undefined' && typeof document !== 'undefined') {\n void import('./test-bridge')\n .then((module) => {\n if (typeof module.injectTestBridge === 'function') {\n module.injectTestBridge();\n }\n })\n .catch(() => {\n // Silent fail - TestBridge is optional in test environments\n });\n\n void import('./utils/browser/mode.utils')\n .then((module) => {\n if (typeof module.detectQaMode === 'function') {\n module.detectQaMode();\n }\n })\n .catch(() => {\n // Silent fail - mode detection is optional\n });\n}\n","import { init, event, on, off, isInitialized, getSessionId, getUserId, destroy, identify, resetIdentity } from './api';\n\n// Constants\nexport * from './app.constants';\n\n// Types\nexport * from './types';\n\n// TraceLog namespace containing all API methods\nexport const tracelog = {\n init,\n event,\n on,\n off,\n isInitialized,\n getSessionId,\n getUserId,\n destroy,\n identify,\n resetIdentity,\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants/config.constants.ts","../src/constants/storage.constants.ts","../src/types/config.types.ts","../src/types/device.types.ts","../src/types/emitter.types.ts","../src/types/error.types.ts","../src/types/event.types.ts","../src/types/mode.types.ts","../src/types/validation-error.types.ts","../src/utils/browser/click-ids.utils.ts","../src/constants/app.constants.ts","../src/utils/logging.utils.ts","../src/utils/browser/device-detector.utils.ts","../src/constants/performance.constants.ts","../package.json","../src/constants/version.constants.ts","../src/utils/browser/mode.utils.ts","../src/utils/browser/prerender.utils.ts","../src/utils/browser/referrer.utils.ts","../src/utils/browser/utm-params.utils.ts","../src/utils/data/uuid.utils.ts","../src/utils/network/url.utils.ts","../src/utils/security/sanitize.utils.ts","../src/utils/security/pii.utils.ts","../src/utils/validations/config-validations.utils.ts","../src/utils/validations/type-guards.utils.ts","../src/utils/validations/metadata-validations.utils.ts","../src/utils/validations/event-validations.utils.ts","../src/utils/emitter.utils.ts","../src/utils/error-signature.utils.ts","../src/managers/state.manager.ts","../src/managers/sender.manager.ts","../src/managers/time.manager.ts","../src/managers/event.manager.ts","../src/managers/user.manager.ts","../src/managers/session.manager.ts","../src/handlers/session.handler.ts","../src/handlers/page-view.handler.ts","../src/handlers/click.handler.ts","../src/handlers/scroll.handler.ts","../src/ecommerce/shopify-cart-linker.ts","../src/managers/storage.manager.ts","../src/handlers/performance.handler.ts","../src/handlers/error.handler.ts","../src/app.ts","../src/api.ts","../src/public-api.ts"],"names":["DEFAULT_SESSION_TIMEOUT","MAX_CUSTOM_EVENT_NAME_LENGTH","MAX_CUSTOM_EVENT_STRING_SIZE","MAX_CUSTOM_EVENT_KEYS","MAX_CUSTOM_EVENT_ARRAY_SIZE","MAX_NESTED_OBJECT_KEYS","MAX_STRING_LENGTH","MAX_STRING_LENGTH_IN_ARRAY","MAX_ARRAY_LENGTH","HTML_DATA_ATTR_PREFIX","INTERACTIVE_SELECTORS","UTM_PARAMS","DEFAULT_SENSITIVE_QUERY_PARAMS","VALIDATION_MESSAGES","XSS_PATTERNS","STORAGE_BASE_KEY","QA_MODE_KEY","USER_ID_KEY","QA_MODE_URL_PARAM","QA_MODE_ENABLE_VALUE","QA_MODE_DISABLE_VALUE","QUEUE_KEY","id","RATE_LIMIT_KEY","SESSION_STORAGE_KEY","BROADCAST_CHANNEL_NAME","SESSION_COUNTS_KEY","userId","sessionId","SESSION_COUNTS_EXPIRY_MS","SESSION_COUNTS_LAST_CLEANUP_KEY","SESSION_COUNTS_CLEANUP_THROTTLE_MS","IDENTITY_KEY","projectId","PENDING_IDENTITY_KEY","SpecialApiUrl","DeviceType","EmitterEvent","PermanentError","_PermanentError","message","statusCode","responseCode","RateLimitError","_RateLimitError","TimeoutError","_TimeoutError","EventType","ScrollDirection","ErrorType","Mode","TraceLogValidationError","errorCode","layer","AppConfigValidationError","SessionTimeoutValidationError","SamplingRateValidationError","IntegrationValidationError","InitializationTimeoutError","timeoutMs","CLICK_ID_PARAMS","getClickIds","urlParams","clickIds","param","value","LOG_STYLE_ACTIVE","LOG_STYLE_DISABLED","LOG_STYLE_CRITICAL","formatLogMsg","msg","error","sanitizedMessage","isQaModeActive","log","type","extra","data","showToClient","style","visibility","formattedMsg","method","shouldShowLog","effectiveStyle","getEffectiveStyle","sanitizedData","sanitizeLogData","outputLog","providedStyle","hasStyle","styledMsg","sanitized","sensitiveKeys","key","lowerKey","sensitiveKey","item","coarsePointerQuery","noHoverQuery","initMediaQueries","UNKNOWN","detectOS","nav","platform","ua","detectBrowser","brands","firstBrand","b","brand","getDeviceType","uaPlatform","width","hasCoarsePointer","hasNoHover","hasTouchSupport","isMobileUA","isTabletUA","getDeviceInfo","WEB_VITALS_GOOD_THRESHOLDS","WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS","WEB_VITALS_POOR_THRESHOLDS","DEFAULT_WEB_VITALS_MODE","getWebVitalsThresholds","mode","MAX_NAVIGATION_HISTORY","version","LIB_VERSION","isBrowserEnvironment","cleanUrlParameter","params","search","url","detectQaMode","urlParam","storedState","newState","isPrerendering","COMPOUND_TLDS","getRootDomain","hostname","parts","lastTwo","isSameDomain","hostname1","hostname2","getExternalReferrer","referrer","referrerHostname","currentHostname","getUTMParameters","utmParams","generateUUID","c","r","eventSequence","lastEventTimestamp","generateEventId","timestamp","sequence","random","bytes","isValidUrl","generateSaasApiUrl","host","cleanDomain","collectApiUrl","getCollectApiUrls","config","urls","normalizeUrl","sensitiveQueryParams","urlObject","searchParams","allSensitiveParams","hasChanged","removedParams","sanitizeString","xssPatternMatches","pattern","beforeReplace","sanitizeValue","depth","sanitizedObject","limitedEntries","value_","sanitizedKey","sanitizedValue","sanitizeMetadata","metadata","errorMessage","PII_PATTERNS","sanitizePii","text","validateAppConfig","validateIntegrations","validModes","validKeys","integrations","validateAndNormalizeConfig","isSerializable","seen","v","isOnlyPrimitiveFields","object","sanitizeTraits","traits","filtered","isValidEventName","eventName","validateSingleMetadata","sanitizedMetadata","intro","jsonString","isValidMetadata","sanitizedArray","i","itemValidation","isEventValid","nameValidation","metadataValidation","Emitter","event","callback","callbacks","index","URL_PATTERN","UUID_PATTERN","HEX_ADDR_PATTERN","LONG_NUMBER_PATTERN","LONG_QUOTED_PATTERN","normalizeErrorMessage","stripQueryHash","cut","normalizeFilename","filename","pageUrl","raw","parsed","page","buildErrorSignatureKey","input","line","globalState","StateManager","SenderManager","storeManager","apiUrl","legacySaasQueueKey","legacyCustomQueueKey","legacySaasRateLimitKey","legacyCustomRateLimitKey","legacyRaw","targetKey","currentRaw","legacy","current","e","mergedEvents","merged","until","existing","stored","body","stableBody","existingFailures","success","recoveryBody","recoveryFailures","persistedData","rawFailures","attempt","exponentialDelay","jitter","resolve","requestBody","elapsed","payload","allTimeouts","hadHttpResponse","isLastAttempt","controller","didTimeout","timeoutId","response","blob","accepted","enrichedBody","preferredToken","idempotencyToken","ids","hash","storageKey","persistedDataString","queue","originalEvents","cutoff","filteredEvents","eventTimestamp","skipThrottle","timeSinceExisting","context","now","TimeManager","offset","VALID_EVENT_TYPES","EventManager","emitter","collectApiUrls","recoveryPromises","sender","_eventCount","recoveredEvents","eventIds","page_url","from_page_url","scroll_data","click_data","custom_event","web_vitals","error_data","page_view","currentSessionId","isCriticalEvent","eventType","typeLimit","currentCount","maxSameEventPerMinute","isSessionStart","currentPageUrl","_session_id","rest","bufferedEvents","result","groups","corruptedIds","group","groupEvents","isSync","planned","batch","totalEvents","acc","p","results","anySucceeded","sendPromises","failedCount","eventMap","order","signature","events","a","globalMetadata","identity","rawPageUrl","validation","sessionReferrer","sessionUtm","sessionClickIds","fingerprint","lastSeen","x","y","_","sorted","nonCriticalIndex","removedEvent","delay","baseInterval","backoff","samplingRate","validTimestamps","ts","eventIdSet","eventData","publicEvent","fn","args","lastCleanup","timeSinceLastCleanup","prefix","keysToRemove","dataToStore","UserManager","storageManager","storedUserId","newUserId","SESSION_ID_PATTERN","SessionManager","eventManager","action","messageProjectId","storedSession","sessionTimeout","lastActivity","utm","localData","sessionData","session","recoveredSessionId","newSessionId","SessionHandler","PageViewHandler","onTrack","original","rawUrl","normalizedUrl","throttleMs","fromUrl","pageViewData","title","ClickHandler","mouseEvent","target","clickedElement","clickThrottleMs","trackingElement","relevantClickElement","coordinates","trackingData","attributeData","clickData","element","lastClickTime","entries","excessCount","toDelete","testId","tlogName","path","selector","firstClass","parent","name","relevantElement","href","clickedText","relevantText","finalText","ScrollHandler","container","elements","walker","node","htmlElement","initialScrollTop","initialDepth","handleScroll","scrollData","newDepth","previous","scrollTop","scrollHeight","viewportHeight","maxScrollTop","lastScrollPos","direction","hasVerticalScrollableOverflow","hasVerticalOverflowContent","SHOPIFY_SESSION_ATTR","SHOPIFY_USER_ATTR","ShopifyCartLinker","rawUserId","dedupKey","attributes","StorageManager","retryError","criticalPrefixes","persistedKeys","nonCriticalKeys","storage","testKey","PerformanceHandler","obs","list","last","clsValue","currentNavId","navId","entry","worst","dur","onLCP","onCLS","onFCP","onTTFB","onINP","report","metric","ttfb","sample","reportedForNav","oldestNav","counter","baseId","supported","cb","options","once","observer","callbackError","threshold","ErrorHandler","_ErrorHandler","nextCount","stack","errorName","reason","truncated","lastSeenAt","limit","excess","App","normalizedMetadata","valid","force","handler","device","trimmedUserId","validTraits","projectKey","pendingRaw","pending","normalizedPending","normalizedIdentity","onPageView","startInteractionTracking","linker","pendingListeners","app","isInitializing","isDestroying","initPromise","init","validatedConfig","instance","appInitPromise","timeoutPromise","reject","cleanupError","on","off","l","isInitialized","getSessionId","getUserId","destroy","identify","resetIdentity","tracelog"],"mappings":"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","file":"public-api.cjs","sourcesContent":["/**\n * Consolidated configuration constants for TraceLog\n * This file centralizes all timing, limits, browser, and initialization constants\n */\n\n// ============================================================================\n// SESSION & TIMING\n// ============================================================================\n\nexport const DEFAULT_SESSION_TIMEOUT = 15 * 60 * 1000; // 15 minutes\nexport const DUPLICATE_EVENT_THRESHOLD_MS = 1000; // 1 second (increased from 500ms to reduce duplicate events)\nexport const EVENT_SENT_INTERVAL_MS = 10000; // 10 seconds\nexport const MIN_SEND_INTERVAL_MS = 1000; // 1 second\nexport const MAX_SEND_INTERVAL_MS_CONFIG = 60000; // 60 seconds\n\n// Throttling and debouncing\nexport const SCROLL_DEBOUNCE_TIME_MS = 250;\nexport const DEFAULT_PAGE_VIEW_THROTTLE_MS = 1000; // 1 second throttle for page views\nexport const DEFAULT_CLICK_THROTTLE_MS = 300; // 300ms throttle for clicks per element\n\n// Click throttle cache limits\nexport const MAX_THROTTLE_CACHE_ENTRIES = 1000; // Maximum element signatures to track\nexport const THROTTLE_ENTRY_TTL_MS = 300000; // 5 minutes TTL for throttle entries\nexport const THROTTLE_PRUNE_INTERVAL_MS = 30000; // 30 seconds interval for cache pruning\n\n// Event expiry\nexport const EVENT_EXPIRY_HOURS = 2;\nexport const EVENT_PERSISTENCE_MAX_AGE_MS = 2 * 60 * 60 * 1000; // 2 hours\nexport const PERSISTENCE_THROTTLE_MS = 1000; // 1 second throttle for cross-tab persistence coordination\n\n/**\n * Maximum age (in ms) of individual events recovered from localStorage before\n * they are dropped during replay. Distinct from `EVENT_EXPIRY_HOURS` which\n * gates the whole persisted envelope — this gates each `events[].timestamp`.\n *\n * Why this matters: a batch can survive across multiple persist/recover/fail\n * cycles (the envelope timestamp is reset on every re-persist, but inner\n * event timestamps stay frozen at their original capture time). A device with\n * a stale clock or a long offline session can accumulate events older than the\n * server's `MAX_PAST_OFFSET_DAYS` window, which the API would clamp or reject.\n * Dropping them here avoids shipping data the server cannot trust.\n *\n * Kept one day under the server's 7-day past-window so borderline cases never\n * leave the client.\n */\nexport const MAX_EVENT_AGE_MS_ON_RECOVERY = 6 * 24 * 60 * 60 * 1000; // 6 days\n\n// ============================================================================\n// LIMITS & REQUESTS\n// ============================================================================\n\nexport const MAX_EVENTS_QUEUE_LENGTH = 100;\nexport const REQUEST_TIMEOUT_MS = 15000; // 15 seconds (to ensure requests complete before tab close/navigation)\nexport const MAX_METADATA_SIZE = 5000;\n\n// Motion and interaction thresholds\nexport const DEFAULT_MOTION_THRESHOLD = 2;\nexport const SIGNIFICANT_SCROLL_DELTA = 10;\nexport const MIN_SCROLL_DEPTH_CHANGE = 5;\nexport const SCROLL_MIN_EVENT_INTERVAL_MS = 500;\nexport const MAX_SCROLL_EVENTS_PER_SESSION = 120;\n\n// Sampling and rate limits\nexport const DEFAULT_SAMPLING_RATE = 1;\nexport const MIN_SAMPLING_RATE = 0;\nexport const MAX_SAMPLING_RATE = 1;\nexport const RATE_LIMIT_WINDOW_MS = 1000; // 1 second window\nexport const MAX_EVENTS_PER_SECOND = 50; // Maximum 50 events per second (Phase 3: reduced from 200)\nexport const MAX_SAME_EVENT_PER_MINUTE = 60; // Maximum same custom event name per minute (prevents infinite loops)\nexport const PER_EVENT_RATE_LIMIT_WINDOW_MS = 60000; // 60 second window for per-event-name rate limiting\n\n// Per-session event caps (Phase 3)\nexport const MAX_EVENTS_PER_SESSION = 1000;\nexport const MAX_CLICKS_PER_SESSION = 500;\nexport const MAX_PAGE_VIEWS_PER_SESSION = 100;\nexport const MAX_CUSTOM_EVENTS_PER_SESSION = 500;\n\n// Queue and batch limits\nexport const BATCH_SIZE_THRESHOLD = 50;\nexport const MAX_PENDING_EVENTS_BUFFER = 100; // Maximum events to buffer before session init\n\n// Session timeout validation limits\nexport const MIN_SESSION_TIMEOUT_MS = 30000; // 30 seconds minimum\nexport const MAX_SESSION_TIMEOUT_MS = 86400000; // 24 hours maximum\n\n// Custom event validation limits\nexport const MAX_CUSTOM_EVENT_NAME_LENGTH = 120;\nexport const MAX_CUSTOM_EVENT_STRING_SIZE = 48 * 1024; // 48KB (leaves headroom below 64KB sendBeacon limit)\nexport const MAX_CUSTOM_EVENT_KEYS = 100;\nexport const MAX_CUSTOM_EVENT_ARRAY_SIZE = 500;\nexport const MAX_NESTED_OBJECT_KEYS = 200; // Safety bound for sanitizer (must be > MAX_CUSTOM_EVENT_KEYS)\n\n// Text content limits\nexport const MAX_TEXT_LENGTH = 255; // For click tracking text content\n\n// Data sanitization limits\nexport const MAX_STRING_LENGTH = 1000;\nexport const MAX_STRING_LENGTH_IN_ARRAY = 500; // Strings within arrays are more limited\nexport const MAX_ARRAY_LENGTH = 1000; // Safety bound for sanitizer (must be > MAX_CUSTOM_EVENT_ARRAY_SIZE)\nexport const MAX_OBJECT_DEPTH = 10; // Sufficient for any real use case, protects against circular/malicious structures\n\n// Precision for numeric metrics\nexport const PRECISION_TWO_DECIMALS = 2 as const;\n\n// Sync XHR timeout\nexport const SYNC_XHR_TIMEOUT_MS = 2000; // 2 seconds\n\n// sendBeacon payload size limit (Phase 3)\nexport const MAX_BEACON_PAYLOAD_SIZE = 64 * 1024; // 64KB browser limit\n\n// Event fingerprint management (moderately increased for high-frequency sessions)\nexport const MAX_FINGERPRINTS = 1500; // Maximum fingerprints stored before cleanup (increased from 1000)\nexport const FINGERPRINT_CLEANUP_MULTIPLIER = 10; // Cleanup fingerprints older than 10x threshold (10 seconds)\nexport const MAX_FINGERPRINTS_HARD_LIMIT = 3000; // Hard limit for aggressive cleanup (increased from 2000)\n\n// ============================================================================\n// BROWSER & HTML\n// ============================================================================\n\n/**\n * Prefix for all HTML data attributes used by TraceLog\n * Used for features like data-tlog-ignore, data-tlog-event, etc.\n */\nexport const HTML_DATA_ATTR_PREFIX = 'data-tlog';\n\n/**\n * CSS selectors for interactive elements to track in click events\n *\n * Covers:\n * - Standard HTML interactive elements (button, a, input, select, textarea)\n * - ARIA roles (button, link, tab, menuitem, option, checkbox, radio, switch)\n * - Framework-specific attributes (routerLink, ng-click)\n * - Data attributes for actions (data-action, data-click, data-navigate, data-toggle)\n * - Common CSS classes (.btn, .button, .clickable, .nav-link, .menu-item)\n * - Testing attributes (data-testid)\n * - Accessibility (tabindex=\"0\")\n */\nexport const INTERACTIVE_SELECTORS = [\n 'button',\n 'a',\n 'input[type=\"button\"]',\n 'input[type=\"submit\"]',\n 'input[type=\"reset\"]',\n 'input[type=\"checkbox\"]',\n 'input[type=\"radio\"]',\n 'select',\n 'textarea',\n '[role=\"button\"]',\n '[role=\"link\"]',\n '[role=\"tab\"]',\n '[role=\"menuitem\"]',\n '[role=\"option\"]',\n '[role=\"checkbox\"]',\n '[role=\"radio\"]',\n '[role=\"switch\"]',\n '[routerLink]',\n '[ng-click]',\n '[data-action]',\n '[data-click]',\n '[data-navigate]',\n '[data-toggle]',\n '[onclick]',\n '.btn',\n '.button',\n '.clickable',\n '.nav-link',\n '.menu-item',\n '[data-testid]',\n '[tabindex=\"0\"]',\n] as const;\n\n/**\n * Standard UTM (Urchin Tracking Module) parameters for marketing attribution\n * These parameters are preserved in URLs for campaign tracking\n */\nexport const UTM_PARAMS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];\n\n/**\n * Default list of sensitive URL query parameters to filter out for privacy protection\n *\n * Includes:\n * - Authentication tokens (token, auth, key, session, access_token, refresh_token)\n * - Password reset links (reset, password, verification, code, otp)\n * - API keys (api_key, apikey, secret)\n *\n * These parameters are removed from tracked URLs to prevent PII leakage\n */\nexport const DEFAULT_SENSITIVE_QUERY_PARAMS = [\n 'token',\n 'auth',\n 'key',\n 'session',\n 'reset',\n 'password',\n 'api_key',\n 'apikey',\n 'secret',\n 'access_token',\n 'refresh_token',\n 'verification',\n 'code',\n 'otp',\n] as const;\n\n// ============================================================================\n// ============================================================================\n// INITIALIZATION\n// ============================================================================\n\nexport const INITIALIZATION_MAX_CONCURRENT_RETRIES = 20;\nexport const INITIALIZATION_CONCURRENT_RETRY_DELAY_MS = 50;\nexport const INITIALIZATION_TIMEOUT_MS = 10000;\n\n// ============================================================================\n// SESSION MANAGEMENT\n// ============================================================================\n\nexport const SESSION_SYNC_TIMEOUT_MS = 2000;\nexport const SESSION_MAX_RETRY_ATTEMPTS = 3;\nexport const SESSION_CLEANUP_DELAY_MS = 100;\n\n// Cross-tab coordination\nexport const CROSS_TAB_INITIALIZATION_LOCK_TIMEOUT_MS = 5000;\nexport const TAB_HEARTBEAT_INTERVAL_MS = 5000; // 5 seconds\nexport const TAB_ELECTION_TIMEOUT_MS = 2000; // 2 seconds\nexport const TAB_CLEANUP_DELAY_MS = 1000; // 1 second\n\n// Session recovery\nexport const SESSION_RECOVERY_WINDOW_MULTIPLIER = 2; // 2x session timeout\nexport const MAX_SESSION_RECOVERY_ATTEMPTS = 3;\nexport const MAX_SESSION_RECOVERY_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours max\nexport const MIN_SESSION_RECOVERY_WINDOW_MS = 2 * 60 * 1000; // 2 minutes minimum\n\n// ============================================================================\n// SCROLL SUPPRESSION\n// ============================================================================\n\nexport const SCROLL_SUPPRESS_MULTIPLIER = 2;\n\n// ============================================================================\n// NETWORK TIMING\n// ============================================================================\n\nexport const RATE_LIMIT_INTERVAL = 1000; // 1 second\n\n// ============================================================================\n// RETRY CONFIGURATION\n// ============================================================================\n\n/**\n * Maximum number of retry attempts for failed event transmissions\n * Applied to 5xx errors and network timeouts (transient failures)\n * 4xx errors (permanent) are not retried\n */\nexport const MAX_SEND_RETRIES = 2;\n\n/**\n * Base delay for exponential backoff retry strategy (in milliseconds)\n * Formula: RETRY_BACKOFF_BASE_MS * (2 ^ attempt) + jitter\n * Example: attempt 1 = 100ms + jitter, attempt 2 = 200ms + jitter\n */\nexport const RETRY_BACKOFF_BASE_MS = 100;\n\n/**\n * Maximum random jitter added to retry backoff delay (in milliseconds)\n * Prevents thundering herd problem when multiple clients retry simultaneously\n * Jitter range: 0 to RETRY_BACKOFF_JITTER_MS\n */\nexport const RETRY_BACKOFF_JITTER_MS = 100;\n\n/**\n * Maximum delay between send attempts when using exponential backoff (in milliseconds)\n * Caps the backoff at 2 minutes to prevent excessively long delays\n * Formula: min(EVENT_SENT_INTERVAL_MS * 2^failures, MAX_SEND_INTERVAL_MS)\n */\nexport const MAX_SEND_INTERVAL_MS = 120000;\n\n/**\n * Maximum consecutive network-level failures (DNS, connection refused) before the\n * circuit opens and further send attempts are skipped for the current session.\n *\n * Network failures are fetch() rejections where no HTTP response was received —\n * distinct from timeout errors and HTTP status errors. Three consecutive failures\n * strongly indicate a permanently misconfigured URL rather than a transient outage.\n *\n * After CIRCUIT_BREAKER_COOLDOWN_MS elapses, the circuit transitions to half-open\n * and allows a single probe batch through. A successful probe closes the circuit;\n * a failed probe re-opens it for another cooldown period.\n */\nexport const MAX_CONSECUTIVE_NETWORK_FAILURES = 3;\n\n/**\n * Cooldown period before the network circuit breaker transitions to half-open\n * and allows a single probe request through. Aligned with MAX_SEND_INTERVAL_MS\n * so the EventManager's backoff scheduler naturally triggers the probe.\n */\nexport const CIRCUIT_BREAKER_COOLDOWN_MS = 120_000; // 2 minutes\n\n/**\n * Cooldown period applied after receiving a 429 response from the ingestion API.\n *\n * During this window the sender skips fetch() calls (events are persisted to\n * localStorage instead). Aligned with the server's session rate-limit window\n * (1 minute) so the next attempt arrives after the server's counter resets.\n *\n * **Why persisted**: Traditional server-rendered sites fire a full page load\n * on every navigation, and users frequently open multiple tabs on the same\n * origin. The cooldown lives in `localStorage` so it survives navigations and\n * is discoverable by any tab on the same origin — otherwise each fresh\n * SenderManager starts with `consecutiveSendFailures = 0` and hammers the API\n * through the 429 with no shared backoff memory.\n */\nexport const RATE_LIMIT_COOLDOWN_MS = 60_000; // 1 minute, matches server RATE_LIMIT_WINDOW_MS\n\n/**\n * Maximum number of cross-session recovery attempts for a persisted event batch.\n *\n * Each page load that attempts to recover a persisted batch and fails increments\n * this counter inside the stored data. When the counter reaches this limit the\n * batch is discarded instead of being kept for another attempt, breaking the\n * infinite persistence loop caused by permanently unreachable backend URLs.\n */\nexport const MAX_RECOVERY_FAILURES = 3;\n\n/**\n * Maximum consecutive send failures before entering cooldown mode.\n *\n * After this many failures:\n * - **Batch-threshold sends** (50+ events) are suppressed to prevent rapid retries\n * - **Periodic scheduler** continues at MAX_SEND_INTERVAL_MS (2 min) for auto-recovery\n * - Counter resets on successful send, stop(), or page reload\n *\n * This implements a circuit breaker pattern:\n * - Closed (0 failures): normal 10s send interval\n * - Half-open (1-4 failures): exponential backoff (20s, 40s, 80s, 120s)\n * - Open (5 failures): cooldown retries every 2 min, batch threshold blocked\n *\n * Each failure includes up to 2 per-request retries in SenderManager.\n */\nexport const MAX_CONSECUTIVE_SEND_FAILURES = 5;\n\n// ============================================================================\n// VALIDATION\n// ============================================================================\n\n/**\n * Standardized validation error messages for TraceLog configuration\n *\n * Centralizes all validation messages to ensure consistency across:\n * - API layer validation\n * - Configuration validation\n * - Runtime validation\n *\n * Messages include contextual information (e.g., min/max values) to help developers\n * quickly identify and fix configuration issues.\n */\nexport const VALIDATION_MESSAGES = {\n MISSING_PROJECT_ID: 'Project ID is required',\n PROJECT_ID_EMPTY_AFTER_TRIM: 'Project ID is required',\n INVALID_SESSION_TIMEOUT: `Session timeout must be between ${MIN_SESSION_TIMEOUT_MS}ms (30 seconds) and ${MAX_SESSION_TIMEOUT_MS}ms (24 hours)`,\n INVALID_SAMPLING_RATE: 'Sampling rate must be between 0 and 1',\n INVALID_ERROR_SAMPLING_RATE: 'Error sampling must be between 0 and 1',\n INVALID_TRACELOG_PROJECT_ID: 'TraceLog project ID is required when integration is enabled',\n INVALID_SCROLL_CONTAINER_SELECTORS: 'Scroll container selectors must be valid CSS selectors',\n INVALID_GLOBAL_METADATA: 'Global metadata must be an object',\n INVALID_SENSITIVE_QUERY_PARAMS: 'Sensitive query params must be an array of strings',\n INVALID_PAGE_VIEW_THROTTLE: 'Page view throttle must be a non-negative number',\n INVALID_CLICK_THROTTLE: 'Click throttle must be a non-negative number',\n INVALID_MAX_SAME_EVENT_PER_MINUTE: 'Max same event per minute must be a positive number',\n INVALID_SEND_INTERVAL: `Send interval must be between ${MIN_SEND_INTERVAL_MS}ms (1 second) and ${MAX_SEND_INTERVAL_MS_CONFIG}ms (60 seconds)`,\n} as const;\n\n// ============================================================================\n// SECURITY\n// ============================================================================\n\n/**\n * Regular expressions for detecting and sanitizing XSS (Cross-Site Scripting) attacks\n *\n * Patterns detect:\n * - Script tags (<script>)\n * - JavaScript protocol URLs (javascript:)\n * - Inline event handlers (onclick=, onload=, etc.)\n * - Embedded content (<iframe>, <embed>, <object>)\n *\n * Used to sanitize user-provided data before storage or transmission\n */\nexport const XSS_PATTERNS = [\n /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n /javascript:/gi,\n /on\\w+\\s*=/gi,\n /<iframe\\b[^<]*(?:(?!<\\/iframe>)<[^<]*)*<\\/iframe>/gi,\n /<embed\\b[^>]*>/gi,\n /<object\\b[^<]*(?:(?!<\\/object>)<[^<]*)*<\\/object>/gi,\n] as const;\n","/**\n * Storage key management constants for TraceLog\n * All keys are namespaced with 'tlog' prefix to avoid conflicts\n */\n\n/**\n * Base key prefix for all TraceLog localStorage items\n * Used as namespace to prevent conflicts with other libraries\n */\nexport const STORAGE_BASE_KEY = 'tlog';\n\n/**\n * Storage key for QA mode flag in sessionStorage\n * Format: 'tlog:qa_mode'\n */\nexport const QA_MODE_KEY = `${STORAGE_BASE_KEY}:qa_mode`;\n\n/**\n * Storage key for user ID in localStorage\n * Format: 'tlog:uid'\n */\nexport const USER_ID_KEY = `${STORAGE_BASE_KEY}:uid`;\n\n/**\n * URL parameter name for activating/deactivating QA mode\n * Example: ?tlog_mode=qa or ?tlog_mode=qa_off\n */\nexport const QA_MODE_URL_PARAM = 'tlog_mode';\n\n/**\n * URL parameter value to enable QA mode\n */\nexport const QA_MODE_ENABLE_VALUE = 'qa';\n\n/**\n * URL parameter value to disable QA mode\n */\nexport const QA_MODE_DISABLE_VALUE = 'qa_off';\n\n/**\n * Generates storage key for event queue\n *\n * @param id - User ID or project identifier\n * @returns localStorage key for event queue (e.g., 'tlog:user123:queue')\n */\nexport const QUEUE_KEY = (id: string): string => (id ? `${STORAGE_BASE_KEY}:${id}:queue` : `${STORAGE_BASE_KEY}:queue`);\n\n/**\n * Generates storage key for per-sender rate-limit cooldown timestamp.\n *\n * Persisted in `localStorage` so the cooldown survives both page navigations\n * and is visible to other tabs/windows on the same origin. Without this, each\n * fresh `SenderManager` starts with a zeroed backoff and would continue\n * hammering the API through the server's 429 window.\n *\n * @param id - User ID\n * @returns localStorage key (e.g., 'tlog:user123:rate_limit')\n */\nexport const RATE_LIMIT_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:rate_limit` : `${STORAGE_BASE_KEY}:rate_limit`;\n\n/**\n * Generates storage key for session data\n *\n * @param id - Project identifier\n * @returns localStorage key for session (e.g., 'tlog:project123:session')\n */\nexport const SESSION_STORAGE_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:session` : `${STORAGE_BASE_KEY}:session`;\n\n/**\n * Generates storage key for cross-tab session synchronization\n *\n * @param id - Project identifier\n * @returns localStorage key for cross-tab session (e.g., 'tlog:project123:cross_tab_session')\n */\nexport const CROSS_TAB_SESSION_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:cross_tab_session` : `${STORAGE_BASE_KEY}:cross_tab_session`;\n\n/**\n * Generates storage key for tab information registry\n *\n * @param id - Project identifier\n * @returns localStorage key for tab info (e.g., 'tlog:project123:tab_info')\n */\nexport const TAB_INFO_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:tab_info` : `${STORAGE_BASE_KEY}:tab_info`;\n\n/**\n * Generates storage key for specific tab information\n *\n * @param projectId - Project identifier\n * @param tabId - Unique tab identifier\n * @returns localStorage key for tab-specific info (e.g., 'tlog:project123:tab:abc456:info')\n */\nexport const TAB_SPECIFIC_INFO_KEY = (projectId: string, tabId: string): string =>\n `${STORAGE_BASE_KEY}:${projectId}:tab:${tabId}:info`;\n\n/**\n * Generates storage key for session recovery data\n *\n * @param id - Project identifier\n * @returns localStorage key for session recovery (e.g., 'tlog:project123:recovery')\n */\nexport const SESSION_RECOVERY_KEY = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:recovery` : `${STORAGE_BASE_KEY}:recovery`;\n\n/**\n * Generates BroadcastChannel name for cross-tab communication\n *\n * @param id - Project identifier\n * @returns BroadcastChannel name (e.g., 'tlog:project123:broadcast')\n */\nexport const BROADCAST_CHANNEL_NAME = (id: string): string =>\n id ? `${STORAGE_BASE_KEY}:${id}:broadcast` : `${STORAGE_BASE_KEY}:broadcast`;\n\n/**\n * Generates storage key for per-session event counts\n *\n * Used to persist rate limiting counters across page reloads within the same session.\n * This prevents users from bypassing per-session event limits by refreshing the page.\n *\n * @param userId - User identifier\n * @param sessionId - Session identifier\n * @returns localStorage key for session counts (e.g., 'tlog:user123:session_counts:session456')\n */\nexport const SESSION_COUNTS_KEY = (userId: string, sessionId: string): string =>\n `${STORAGE_BASE_KEY}:${userId}:session_counts:${sessionId}`;\n\n/**\n * Session counts expiry duration (7 days in milliseconds).\n *\n * Session counts are automatically cleaned up after this duration to prevent\n * localStorage pollution. Counts older than 7 days are considered stale and\n * are removed on next page load.\n *\n * **Rationale**: 7 days provides sufficient buffer for:\n * - Long-running sessions (rare but possible)\n * - Users returning after extended inactivity\n * - While preventing indefinite accumulation (~100 bytes per session)\n */\nexport const SESSION_COUNTS_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\n/**\n * Storage key for tracking last session counts cleanup timestamp.\n *\n * Used to throttle cleanup operations and prevent performance impact\n * from scanning localStorage on every EventManager initialization.\n *\n * Format: 'tlog:session_counts_last_cleanup'\n */\nexport const SESSION_COUNTS_LAST_CLEANUP_KEY = `${STORAGE_BASE_KEY}:session_counts_last_cleanup`;\n\n/**\n * Minimum interval between session counts cleanup runs (1 hour in milliseconds).\n *\n * Cleanup will only run if at least this much time has elapsed since the\n * last cleanup. This prevents performance degradation from frequent localStorage\n * scans while still ensuring regular cleanup of stale data.\n *\n * **Rationale**: 1 hour provides a good balance between:\n * - Preventing frequent scans on rapid page reloads\n * - Ensuring cleanup runs at least once per typical browsing session\n * - Minimal localStorage overhead (~100 entries typical, <1ms scan time)\n */\nexport const SESSION_COUNTS_CLEANUP_THROTTLE_MS = 60 * 60 * 1000; // 1 hour\n\n// ============================================================\n// Identity Storage Keys\n// ============================================================\n\n/**\n * Generates storage key for visitor identity data (project-scoped).\n *\n * Identity is scoped per project because the same user may have different\n * external IDs across different projects from the same site owner.\n *\n * @param projectId - Project identifier\n * @returns localStorage key for identity (e.g., 'tlog:project123:identity')\n */\nexport const IDENTITY_KEY = (projectId: string): string =>\n projectId ? `${STORAGE_BASE_KEY}:${projectId}:identity` : `${STORAGE_BASE_KEY}:identity`;\n\n/**\n * Temporary storage key for identity set before init().\n *\n * When identify() is called before init(), the projectId is unknown.\n * Identity is stored under this key and moved to the project-scoped key\n * once init() resolves the projectId.\n */\nexport const PENDING_IDENTITY_KEY = `${STORAGE_BASE_KEY}:pending_identity`;\n","import { MetadataType } from './common.types';\nimport { WebVitalType } from './event.types';\n\n/**\n * Web Vitals filtering mode\n * - 'all': Track all Web Vitals metrics (full analytics)\n * - 'needs-improvement': Track metrics that need improvement or are poor (default, balanced)\n * - 'poor': Track only poor metrics (minimal data)\n */\nexport type WebVitalsMode = 'all' | 'needs-improvement' | 'poor';\n\nexport interface Config {\n /** Session inactivity timeout in milliseconds. @default 900000 */\n sessionTimeout?: number;\n /** Metadata appended to every tracked event. */\n globalMetadata?: Record<string, MetadataType>;\n /** Query parameters to remove before tracking URLs. */\n sensitiveQueryParams?: string[];\n /** Error event sampling rate between 0 and 1. @default 1 */\n errorSampling?: number;\n /** Event sampling rate between 0 and 1. @default 1 */\n samplingRate?: number;\n /** Page view throttle duration in milliseconds to prevent rapid navigation spam. @default 1000 */\n pageViewThrottleMs?: number;\n /** Click throttle duration in milliseconds to prevent double-clicks and rapid spam. @default 300 */\n clickThrottleMs?: number;\n /** Maximum number of same custom event name allowed per minute to prevent infinite loops. @default 60 */\n maxSameEventPerMinute?: number;\n /**\n * Web Vitals filtering mode. @default 'needs-improvement'\n * - 'all': Track all metrics (good, needs-improvement, poor) - full trend analysis\n * - 'needs-improvement': Track metrics that need improvement or are poor - balanced approach\n * - 'poor': Track only poor metrics - minimal data, focus on problems\n */\n webVitalsMode?: WebVitalsMode;\n /**\n * Custom Web Vitals thresholds in milliseconds (except CLS which is unitless).\n * Only applies when webVitalsMode is set. Overrides default thresholds for the selected mode.\n */\n webVitalsThresholds?: Partial<Record<WebVitalType, number>>;\n /** Interval in milliseconds between event batch sends. @default 10000 (10 seconds) */\n sendIntervalMs?: number;\n /**\n * Opt-in: when `true`, the event queue is flushed after every SPA navigation\n * (`pushState`, `replaceState`, `popstate`, `hashchange`). Defaults to `false`\n * because per-route flushing can multiply request volume on SPA-heavy apps.\n * @default false\n */\n flushOnSpaNavigation?: boolean;\n /**\n * If true, the event queue is flushed when `document.hidden` becomes `true`\n * (tab switch, lock screen, app backgrounding). Especially relevant on mobile Safari\n * where `pagehide`/`beforeunload` may not fire reliably.\n * @default true\n */\n flushOnPageHidden?: boolean;\n /** TraceLog SaaS integration. */\n integrations?: {\n tracelog?: {\n /** Required project ID for TraceLog SaaS integration. */\n projectId: string;\n /** Enable Shopify cart attribute linking for webhook revenue attribution. */\n shopify?: boolean;\n };\n };\n}\n\nexport enum SpecialApiUrl {\n Localhost = 'localhost:8080',\n Fail = 'localhost:9999',\n}\n","/**\n * Device type classification for analytics segmentation\n *\n * **Detection Logic**:\n * - Mobile: User agent contains mobile keywords (iPhone, Android phone, etc.)\n * - Tablet: User agent contains tablet keywords (iPad, Android tablet, etc.)\n * - Desktop: Default fallback for non-mobile/tablet devices\n * - Unknown: User agent not detectable or SSR environment\n *\n * **Use Cases**:\n * - Device-specific analytics and dashboards\n * - User experience optimization by device type\n * - Performance monitoring segmented by device\n *\n * @see src/utils/browser/device-detector.utils.ts for detection implementation\n */\nexport enum DeviceType {\n /** Mobile phones (iPhone, Android phones) */\n Mobile = 'mobile',\n /** Tablet devices (iPad, Android tablets) */\n Tablet = 'tablet',\n /** Desktop computers and laptops */\n Desktop = 'desktop',\n /** Unable to determine device type */\n Unknown = 'unknown',\n}\n\n/**\n * Comprehensive device and environment information\n *\n * **Purpose**: Provides rich device context for analytics segmentation,\n * debugging, and user experience optimization.\n *\n * **Detection**: Uses navigator.userAgentData (modern) with UA string fallback.\n *\n * @see src/utils/browser/device-detector.utils.ts for detection implementation\n */\nexport interface DeviceInfo {\n /** Device form factor classification */\n type: DeviceType;\n /** OS name: \"Windows\", \"macOS\", \"iOS\", \"Android\", \"Linux\", \"ChromeOS\", \"Unknown\" */\n os: string;\n /** Browser name: \"Chrome\", \"Firefox\", \"Safari\", \"Edge\", \"Opera\", \"Unknown\" */\n browser: string;\n}\n","import { EventData } from './event.types';\nimport { EventsQueue } from './queue.types';\n\n/**\n * Generic callback function for event emitter subscriptions\n *\n * @template T - Type of data passed to the callback\n * @param data - Event data passed to the callback\n */\nexport type EmitterCallback<T = any> = (data: T) => void;\n\n/**\n * Available event emitter channels for TraceLog\n *\n * **Purpose**: Type-safe event subscription system for external integrations\n *\n * **Event Channels**:\n * - `event`: Individual events as they are tracked (real-time)\n * - `queue`: Complete event batches before network transmission (every 10s or 50 events)\n *\n * **Use Cases**:\n * - Real-time event processing\n * - Custom analytics integrations\n * - Debugging and monitoring\n *\n * @example\n * ```typescript\n * // Subscribe to individual events\n * tracelog.on('event', (event) => {\n * console.log('Event tracked:', event.type, event);\n * });\n *\n * // Subscribe to event batches\n * tracelog.on('queue', (batch) => {\n * console.log('Sending batch:', batch.events.length, 'events');\n * });\n * ```\n */\nexport enum EmitterEvent {\n /** Individual events as they are tracked */\n EVENT = 'event',\n /** Complete event batches before transmission */\n QUEUE = 'queue',\n}\n\n/**\n * Type mapping for event emitter channels\n *\n * **Purpose**: Ensures type safety when subscribing to events\n *\n * Maps each EmitterEvent to its corresponding payload type:\n * - `event` → `EventData`: Single event data\n * - `queue` → `EventsQueue`: Batch of events with metadata\n */\nexport interface EmitterMap {\n [EmitterEvent.EVENT]: EventData;\n [EmitterEvent.QUEUE]: EventsQueue;\n}\n","/**\n * Custom error types for TraceLog\n */\n\n/**\n * Represents a permanent HTTP error (4xx) that should not be retried\n * Examples: 400 Bad Request, 403 Forbidden, 404 Not Found\n */\nexport class PermanentError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly responseCode?: string,\n ) {\n super(message);\n this.name = 'PermanentError';\n\n // Maintain proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, PermanentError);\n }\n }\n}\n\n/**\n * Represents a rate limit error (429) that should not be retried in the\n * inner send loop. Events are persisted for periodic retry via EventManager\n * backoff. Deduplication of retried events is integration-specific (e.g.\n * TraceLog SaaS handles it server-side; custom backends should implement\n * their own idempotency).\n */\nexport class RateLimitError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RateLimitError';\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, RateLimitError);\n }\n }\n}\n\n/**\n * Represents a timeout error where the server likely received the request\n * but the response took too long. Events should NOT be persisted for retry\n * since the server most likely already processed them.\n */\nexport class TimeoutError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'TimeoutError';\n\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, TimeoutError);\n }\n }\n}\n","import { MetadataType } from './common.types';\n\n/**\n * Coordinate information from a click event\n */\nexport type ClickCoordinates = Pick<ClickData, 'x' | 'y'>;\n\n/**\n * Web performance metric types tracked by the library\n * - LCP: Largest Contentful Paint\n * - CLS: Cumulative Layout Shift\n * - INP: Interaction to Next Paint\n * - FCP: First Contentful Paint\n * - TTFB: Time to First Byte\n */\nexport type WebVitalType = 'LCP' | 'CLS' | 'INP' | 'FCP' | 'TTFB';\n\n/**\n * Event type name\n */\nexport type EventTypeName = (typeof EventType)[keyof typeof EventType];\n\n/**\n * Event type enum\n */\nexport enum EventType {\n /** Page navigation and view tracking */\n PAGE_VIEW = 'page_view',\n /** User click interactions */\n CLICK = 'click',\n /** Scroll depth and behavior */\n SCROLL = 'scroll',\n /** Session initialization */\n SESSION_START = 'session_start',\n /** Custom business events */\n CUSTOM = 'custom',\n /** Performance metrics */\n WEB_VITALS = 'web_vitals',\n /** JavaScript errors and rejections */\n ERROR = 'error',\n}\n\n/**\n * Per-session event counts structure for rate limiting.\n *\n * Persisted to localStorage: `tlog:{userId}:session_counts:{sessionId}`\n * Restored on page reload to maintain limits across navigations.\n */\nexport interface SessionEventCounts {\n /** Total events across all types */\n total: number;\n /** Click events count */\n [EventType.CLICK]: number;\n /** Page view events count */\n [EventType.PAGE_VIEW]: number;\n /** Custom events count */\n [EventType.CUSTOM]: number;\n /** Scroll events count */\n [EventType.SCROLL]: number;\n /** Index signature for dynamic event type access */\n [key: string]: number;\n}\n\n/**\n * Scroll direction indicators\n */\nexport enum ScrollDirection {\n /** Scrolling upward */\n UP = 'up',\n /** Scrolling downward */\n DOWN = 'down',\n}\n\n/**\n * JavaScript error classification\n */\nexport enum ErrorType {\n /** Runtime JavaScript errors */\n JS_ERROR = 'js_error',\n /** Unhandled promise rejections */\n PROMISE_REJECTION = 'promise_rejection',\n}\n\n/**\n * Scroll event data captured during user scrolling\n */\nexport interface ScrollData {\n /** Current scroll depth as percentage (0-100) */\n depth: number;\n /** Direction of scroll movement */\n direction: ScrollDirection;\n /** CSS selector of the scrolled container */\n container_selector: string;\n}\n\n/**\n * Click event data capturing user interaction details\n */\nexport interface ClickData {\n /** Absolute X coordinate in viewport (pixels) */\n x: number;\n /** Absolute Y coordinate in viewport (pixels) */\n y: number;\n /** Element ID attribute */\n id?: string;\n /** Element class attribute */\n class?: string;\n /** HTML tag name */\n tag?: string;\n /** Element text content (truncated) */\n text?: string;\n /** Link href for anchor elements */\n href?: string;\n}\n\n/**\n * Element data for specialized click tracking\n * Used for form inputs and interactive elements\n */\nexport interface ClickTrackingElementData {\n /** DOM element being tracked */\n element: HTMLElement;\n /** Descriptive name for the element */\n name: string;\n /** Element value (for inputs) */\n value?: string;\n}\n\n/**\n * Custom event data for business-specific tracking\n */\nexport interface CustomEventData {\n /** Event name identifier */\n name: string;\n /** Additional event metadata */\n metadata?: Record<string, MetadataType> | Record<string, MetadataType>[];\n}\n\n/**\n * Optional flags for `tracelog.event()`.\n */\nexport interface EventOptions {\n /**\n * If `true`, the event queue is flushed via `navigator.sendBeacon()`\n * immediately after this event is tracked. The browser guarantees the\n * request is queued for delivery even if the page is about to unload.\n *\n * Use for high-value events where loss is unacceptable (Purchase, Signup,\n * AddPaymentInfo).\n *\n * @default false\n */\n critical?: boolean;\n}\n\n/**\n * Web performance metrics data\n */\nexport interface WebVitalsData {\n /** Type of performance metric */\n type: WebVitalType;\n /** Metric value (varies by type) */\n value: number;\n}\n\n/**\n * JavaScript error details\n */\nexport interface ErrorData {\n /** Error classification */\n type: ErrorType;\n /** Error message text */\n message: string;\n /** Error constructor name (TypeError, ReferenceError, etc.) when available */\n name?: string;\n /** Source file where error occurred */\n filename?: string;\n /** Line number in source file */\n line?: number;\n /** Column number in source file */\n column?: number;\n /** Error stack trace (truncated to 2000 chars) */\n stack?: string;\n}\n\n/**\n * UTM campaign tracking parameters\n */\nexport interface UTM {\n /** Campaign source (e.g., google, newsletter) */\n source?: string;\n /** Campaign medium (e.g., cpc, email) */\n medium?: string;\n /** Campaign name identifier */\n campaign?: string;\n /** Campaign search term */\n term?: string;\n /** Campaign content variation */\n content?: string;\n}\n\n/**\n * Ad-network click identifiers auto-appended to landing URLs by ad platforms.\n * Used by the backend to classify a session's traffic source as Paid when no\n * manual UTM source/medium is present. Captured but never logged.\n */\nexport interface ClickIds {\n /** Google Ads click id */\n gclid?: string;\n /** Google Ads iOS-privacy click id (app campaigns) */\n gbraid?: string;\n /** Google Ads iOS-privacy click id (web-to-app) */\n wbraid?: string;\n /** Meta (Facebook/Instagram) Ads click id */\n fbclid?: string;\n /** TikTok Ads click id */\n ttclid?: string;\n}\n\n/**\n * Page view navigation data\n */\nexport interface PageViewData {\n /** Previous page URL */\n referrer?: string;\n /** Page title from document */\n title?: string;\n}\n\n/**\n * Complete event data structure\n * All events share base properties with type-specific data\n */\nexport interface EventData {\n /** Unique event identifier */\n id: string;\n /** Event type classification */\n type: EventType;\n /** Current page URL where event occurred */\n page_url: string;\n /** Unix timestamp (milliseconds) */\n timestamp: number;\n /** HTTP referrer header */\n referrer?: string;\n /** Previous page URL for navigation events */\n from_page_url?: string;\n /** Scroll event details (when type is SCROLL) */\n scroll_data?: ScrollData;\n /** Click event details (when type is CLICK) */\n click_data?: ClickData;\n /** Custom event details (when type is CUSTOM) */\n custom_event?: CustomEventData;\n /** Performance metrics (when type is WEB_VITALS) */\n web_vitals?: WebVitalsData;\n /** Page view details (when type is PAGE_VIEW) */\n page_view?: PageViewData;\n /** Error details (when type is ERROR) */\n error_data?: ErrorData;\n /** Campaign tracking parameters */\n utm?: UTM;\n /** Ad-network click identifiers (gclid, fbclid, ttclid, ...) captured at session start */\n click_ids?: ClickIds;\n}\n\n/**\n * Internal queue entry: an `EventData` enriched with the session ID frozen at\n * `track()` time. Survives session renewal — when the user is idle past the\n * timeout, `state.sessionId` is nulled but events already in the queue keep\n * their original `_session_id`, so `EventManager.buildBatchesWithIds()` can\n * still attribute them correctly instead of emitting `session_id: null` to the\n * wire.\n */\nexport interface QueuedEvent extends EventData {\n _session_id: string;\n}\n","/**\n * App modes for the TraceLog Library\n *\n * - QA: Quality Assurance mode - logs custom events to console for verification\n */\nexport enum Mode {\n QA = 'qa',\n}\n","/**\n * Custom error classes for TraceLog validation errors\n * Provides better error handling and consistency across validation layers\n */\n\n/**\n * Base class for all TraceLog validation errors\n */\nexport abstract class TraceLogValidationError extends Error {\n constructor(\n message: string,\n public readonly errorCode: string,\n public readonly layer: 'config' | 'app' | 'runtime',\n ) {\n super(message);\n this.name = this.constructor.name;\n\n // Maintains proper stack trace for where error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when app configuration validation fails\n */\nexport class AppConfigValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'APP_CONFIG_INVALID', layer);\n }\n}\n\n/**\n * Thrown when session timeout validation fails\n */\nexport class SessionTimeoutValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'SESSION_TIMEOUT_INVALID', layer);\n }\n}\n\n/**\n * Thrown when sampling rate validation fails\n */\nexport class SamplingRateValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'SAMPLING_RATE_INVALID', layer);\n }\n}\n\n/**\n * Thrown when integrations validation fails\n */\nexport class IntegrationValidationError extends TraceLogValidationError {\n constructor(message: string, layer: 'config' | 'app' | 'runtime' = 'config') {\n super(message, 'INTEGRATION_INVALID', layer);\n }\n}\n\n/**\n * Thrown when initialization exceeds the maximum allowed timeout\n */\nexport class InitializationTimeoutError extends TraceLogValidationError {\n constructor(\n message: string,\n public readonly timeoutMs: number,\n layer: 'config' | 'app' | 'runtime' = 'runtime',\n ) {\n super(message, 'INITIALIZATION_TIMEOUT', layer);\n }\n}\n","import { ClickIds } from '../../types/event.types';\n\n/**\n * Ad-network click identifiers auto-appended to landing URLs: Google Ads\n * (`gclid`, plus the iOS-privacy variants `gbraid`/`wbraid`), Meta (`fbclid`),\n * and TikTok (`ttclid`). Param name and `ClickIds` key are identical, so no\n * prefix transformation is needed (unlike UTM).\n */\nconst CLICK_ID_PARAMS = ['gclid', 'gbraid', 'wbraid', 'fbclid', 'ttclid'] as const;\n\n/**\n * Extracts ad-network click identifiers from the current URL.\n * @returns ClickIds object or undefined if none are present\n */\nexport const getClickIds = (): ClickIds | undefined => {\n const urlParams = new URLSearchParams(window.location.search);\n const clickIds: ClickIds = {};\n\n CLICK_ID_PARAMS.forEach((param) => {\n const value = urlParams.get(param);\n\n if (value) {\n clickIds[param] = value;\n }\n });\n\n const result = Object.keys(clickIds).length ? clickIds : undefined;\n\n return result;\n};\n","/**\n * Console log style for active TraceLog operations\n * Used for visual highlighting in browser console during QA mode\n */\nexport const LOG_STYLE_ACTIVE =\n 'background: #ff9800; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;';\n\n/**\n * Console log style for disabled TraceLog operations\n * Used for visual indication when features are disabled\n */\nexport const LOG_STYLE_DISABLED =\n 'background: #9e9e9e; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;';\n\n/**\n * Console log style for critical errors (always visible)\n * Used for errors that must reach monitoring platforms like Sentry\n */\nexport const LOG_STYLE_CRITICAL =\n 'background: #d32f2f; color: white; font-weight: bold; padding: 2px 8px; border-radius: 3px;';\n","import { QA_MODE_KEY } from '../constants/storage.constants';\nimport { LOG_STYLE_CRITICAL } from '../constants/app.constants';\n\n/**\n * Log visibility level determining when logs are shown\n *\n * - 'critical': Always visible (production included) - for Sentry/monitoring\n * - 'qa': Only visible when QA mode is active (equivalent to showToClient: true)\n * - undefined: Only visible in NODE_ENV=development\n */\nexport type LogVisibility = 'critical' | 'qa';\n\n/**\n * Formats log messages with optional error information and environment-specific sanitization\n *\n * **Purpose**: Creates consistent log message format across the library with automatic\n * error handling and production-safe output.\n *\n * **Behavior**:\n * - **Production**: Sanitizes stack traces and file paths from error messages\n * - **Development**: Preserves full error messages for debugging\n * - **Fallback**: Handles non-Error objects gracefully\n *\n * **Format**: `[TraceLog] {message}: {error}` or `[TraceLog] {message}` if no error\n *\n * **Stack Trace Sanitization** (production only):\n * - Removes \"at function (...)\" stack lines\n * - Removes file paths with line/column numbers\n * - Prevents leaking internal file structure\n *\n * @param msg - Base log message\n * @param error - Optional error object (Error, string, object, or unknown type)\n * @returns Formatted log message string\n *\n * @example\n * ```typescript\n * // Basic message\n * formatLogMsg('Session started');\n * // → \"[TraceLog] Session started\"\n *\n * // With Error object (development)\n * formatLogMsg('Failed to init', new Error('Network timeout'));\n * // → \"[TraceLog] Failed to init: Network timeout\"\n *\n * // With Error object (production - sanitized)\n * formatLogMsg('Failed to init', new Error('Network timeout at handleRequest (app.ts:42:10)'));\n * // → \"[TraceLog] Failed to init: Network timeout\"\n *\n * // With string error\n * formatLogMsg('Config invalid', 'Missing API key');\n * // → \"[TraceLog] Config invalid: Missing API key\"\n * ```\n */\nexport const formatLogMsg = (msg: string, error?: unknown): string => {\n if (error) {\n if (process.env.NODE_ENV !== 'development' && error instanceof Error) {\n const sanitizedMessage = error.message.replace(/\\s+at\\s+.*$/gm, '').replace(/\\s*\\([^()]+:\\d+:\\d+\\)/g, '');\n return `[TraceLog] ${msg}: ${sanitizedMessage}`;\n }\n\n if (error instanceof Error) {\n return `[TraceLog] ${msg}: ${error.message}`;\n }\n\n if (typeof error === 'string') {\n return `[TraceLog] ${msg}: ${error}`;\n }\n\n if (typeof error === 'object') {\n try {\n return `[TraceLog] ${msg}: ${JSON.stringify(error)}`;\n } catch {\n return `[TraceLog] ${msg}: [Unable to serialize error]`;\n }\n }\n\n return `[TraceLog] ${msg}: ${String(error)}`;\n }\n\n return `[TraceLog] ${msg}`;\n};\n\n/**\n * Check if QA mode is active by reading sessionStorage directly\n *\n * NOTE: Intentional duplication of mode.utils.ts:isQaModeActive() to avoid\n * circular dependency (mode.utils.ts imports log() from this file).\n * Changes to QA mode detection logic must be synchronized in both files.\n *\n * @see src/utils/browser/mode.utils.ts - Canonical public implementation\n */\nconst isQaModeActive = (): boolean => {\n if (typeof window === 'undefined' || typeof sessionStorage === 'undefined') {\n return false;\n }\n try {\n return sessionStorage.getItem(QA_MODE_KEY) === 'true';\n } catch {\n return false;\n }\n};\n\n/**\n * Safe logging utility that enforces zero logs in production\n *\n * @param type - Log level (info, warn, error, debug)\n * @param msg - Message to log\n * @param extra - Optional extra data\n * @param extra.error - Error object to include in the log message\n * @param extra.data - Additional data object to log (will be sanitized in production)\n * @param extra.showToClient - If true, log will be shown in production (QA mode only) - DEPRECATED: use visibility: 'qa'\n * @param extra.style - CSS styles to apply to the console message (browser only, uses %c formatting)\n * @param extra.visibility - Controls when log is visible:\n * - 'critical': Always visible (production included) - for monitoring/Sentry\n * - 'qa': Only visible when QA mode is active\n * - undefined: Only visible in NODE_ENV=development\n *\n * Visibility hierarchy (production):\n * - CRITICAL: Always shown - errors that must reach monitoring platforms\n * - QA: Shown with ?tlog_mode=qa - custom event verification\n * - Default: Never shown in production\n *\n * Development behavior (NODE_ENV=development):\n * - All logs visible regardless of visibility level\n * - Full error messages and stack traces preserved\n */\nexport const log = (\n type: 'info' | 'warn' | 'error' | 'debug',\n msg: string,\n extra?: {\n error?: unknown;\n data?: Record<string, unknown>;\n showToClient?: boolean;\n style?: string;\n visibility?: LogVisibility;\n },\n): void => {\n const { error, data, showToClient = false, style, visibility } = extra ?? {};\n const formattedMsg = error ? formatLogMsg(msg, error) : `[TraceLog] ${msg}`;\n const method = type === 'error' ? 'error' : type === 'warn' ? 'warn' : 'log';\n const isProduction = process.env.NODE_ENV !== 'development';\n\n // Development: All logs visible\n if (!isProduction) {\n outputLog(method, formattedMsg, style, data);\n return;\n }\n\n // Production: Check visibility level\n const shouldShow = shouldShowLog(visibility, showToClient);\n\n if (!shouldShow) {\n return;\n }\n\n // Apply appropriate style for visibility level\n const effectiveStyle = getEffectiveStyle(visibility, style);\n const sanitizedData = data !== undefined ? sanitizeLogData(data) : undefined;\n\n outputLog(method, formattedMsg, effectiveStyle, sanitizedData);\n};\n\n/**\n * Determines if a log should be shown based on visibility level\n */\nconst shouldShowLog = (visibility: LogVisibility | undefined, showToClient: boolean): boolean => {\n // Critical logs are always shown\n if (visibility === 'critical') {\n return true;\n }\n\n // QA mode logs (including legacy showToClient)\n if (visibility === 'qa' || showToClient) {\n return isQaModeActive();\n }\n\n // Default: not shown in production\n return false;\n};\n\n/**\n * Gets the appropriate style for the visibility level\n */\nconst getEffectiveStyle = (visibility: LogVisibility | undefined, providedStyle: string | undefined): string => {\n if (providedStyle !== undefined && providedStyle !== '') {\n return providedStyle;\n }\n\n if (visibility === 'critical') {\n return LOG_STYLE_CRITICAL;\n }\n\n return '';\n};\n\n/**\n * Outputs the log message to console\n */\nconst outputLog = (\n method: 'log' | 'warn' | 'error',\n formattedMsg: string,\n style: string | undefined,\n data: Record<string, unknown> | undefined,\n): void => {\n const hasStyle = style !== undefined && style !== '';\n const styledMsg = hasStyle ? `%c${formattedMsg}` : formattedMsg;\n\n if (data !== undefined) {\n if (hasStyle) {\n console[method](styledMsg, style, data);\n } else {\n console[method](styledMsg, data);\n }\n } else {\n if (hasStyle) {\n console[method](styledMsg, style);\n } else {\n console[method](styledMsg);\n }\n }\n};\n\n/**\n * Sanitizes log data in production to prevent sensitive information leakage\n *\n * **Purpose**: Recursively redacts sensitive keys while preserving object expandability\n * in browser console for debugging.\n *\n * **Sensitive Keys Detected** (case-insensitive substring matching):\n * - `token`, `password`, `secret`, `key`, `apikey`, `api_key`, `sessionid`, `session_id`\n *\n * **Behavior**:\n * - **Redaction**: Replaces values with `'[REDACTED]'` string\n * - **Deep Cloning**: Creates new objects to avoid mutating originals\n * - **Recursive**: Handles nested objects and arrays\n * - **Preserves Structure**: Maintains object hierarchy for console inspection\n *\n * **Use Cases**:\n * - Production logging where sensitive data might be present\n * - Debugging with potentially sensitive configuration objects\n * - Error logging with user or session data\n *\n * @param data - Object to sanitize\n * @returns New object with sensitive keys redacted\n *\n * @example\n * ```typescript\n * const data = {\n * userId: '123',\n * apiKey: 'secret-key-123',\n * config: {\n * endpoint: 'https://api.com',\n * token: 'bearer-xyz'\n * }\n * };\n *\n * const sanitized = sanitizeLogData(data);\n * // {\n * // userId: '123',\n * // apiKey: '[REDACTED]',\n * // config: {\n * // endpoint: 'https://api.com',\n * // token: '[REDACTED]'\n * // }\n * // }\n * ```\n */\nconst sanitizeLogData = (data: Record<string, unknown>): Record<string, unknown> => {\n const sanitized: Record<string, unknown> = {};\n const sensitiveKeys = ['token', 'password', 'secret', 'key', 'apikey', 'api_key', 'sessionid', 'session_id'];\n\n for (const [key, value] of Object.entries(data)) {\n const lowerKey = key.toLowerCase();\n\n if (sensitiveKeys.some((sensitiveKey) => lowerKey.includes(sensitiveKey))) {\n sanitized[key] = '[REDACTED]';\n continue;\n }\n\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n sanitized[key] = sanitizeLogData(value as Record<string, unknown>);\n } else if (Array.isArray(value)) {\n sanitized[key] = value.map((item) =>\n item !== null && typeof item === 'object' && !Array.isArray(item)\n ? sanitizeLogData(item as Record<string, unknown>)\n : item,\n );\n } else {\n sanitized[key] = value;\n }\n }\n\n return sanitized;\n};\n","import { DeviceInfo, DeviceType } from '../../types/device.types';\nimport { log } from '../logging.utils';\n\nlet coarsePointerQuery: MediaQueryList | undefined;\nlet noHoverQuery: MediaQueryList | undefined;\n\nconst initMediaQueries = (): void => {\n if (typeof window !== 'undefined' && !coarsePointerQuery) {\n coarsePointerQuery = window.matchMedia('(pointer: coarse)');\n noHoverQuery = window.matchMedia('(hover: none)');\n }\n};\n\ninterface UserAgentBrand {\n brand: string;\n version: string;\n}\n\ninterface NavigatorWithUserAgentData extends Navigator {\n userAgentData?: {\n mobile: boolean;\n platform?: string;\n brands?: UserAgentBrand[];\n };\n}\n\nconst UNKNOWN = 'Unknown';\n\n/**\n * Detects OS name from navigator.userAgentData (modern) or userAgent string (fallback)\n */\nconst detectOS = (nav: NavigatorWithUserAgentData): string => {\n // Modern API (Chromium 90+)\n const platform = nav.userAgentData?.platform;\n if (platform != null && platform !== '') {\n if (/windows/i.test(platform)) return 'Windows';\n if (/macos/i.test(platform)) return 'macOS';\n if (/android/i.test(platform)) return 'Android';\n if (/linux/i.test(platform)) return 'Linux';\n if (/chromeos/i.test(platform)) return 'ChromeOS';\n if (/ios/i.test(platform)) return 'iOS';\n }\n\n // Fallback: Parse userAgent string\n const ua = navigator.userAgent;\n if (/Windows/i.test(ua)) return 'Windows';\n if (/iPhone|iPad|iPod/i.test(ua)) return 'iOS';\n if (/Mac OS X|Macintosh/i.test(ua)) return 'macOS';\n if (/Android/i.test(ua)) return 'Android';\n if (/CrOS/i.test(ua)) return 'ChromeOS';\n if (/Linux/i.test(ua)) return 'Linux';\n\n return UNKNOWN;\n};\n\n/**\n * Detects browser name from navigator.userAgentData.brands (modern) or userAgent string (fallback)\n */\nconst detectBrowser = (nav: NavigatorWithUserAgentData): string => {\n // Modern API (Chromium 90+)\n const brands = nav.userAgentData?.brands;\n if (brands != null && brands.length > 0) {\n // Filter out generic brands and find the actual browser\n const validBrands = brands.filter((b) => !/not.?a.?brand|chromium/i.test(b.brand));\n const firstBrand = validBrands[0];\n if (firstBrand != null) {\n const brand = firstBrand.brand;\n // Normalize brand names\n if (/google chrome/i.test(brand)) return 'Chrome';\n if (/microsoft edge/i.test(brand)) return 'Edge';\n if (/opera/i.test(brand)) return 'Opera';\n return brand;\n }\n }\n\n // Fallback: Parse userAgent string (order matters!)\n const ua = navigator.userAgent;\n if (/Edg\\//i.test(ua)) return 'Edge';\n if (/OPR\\//i.test(ua)) return 'Opera';\n if (/Chrome/i.test(ua)) return 'Chrome';\n if (/Firefox/i.test(ua)) return 'Firefox';\n if (/Safari/i.test(ua) && !/Chrome/i.test(ua)) return 'Safari';\n\n return UNKNOWN;\n};\n\n/**\n * Detects the device type based on screen size, user agent, and browser capabilities\n * @returns The detected device type\n */\nexport const getDeviceType = (): DeviceType => {\n try {\n const nav = navigator as NavigatorWithUserAgentData;\n\n if (nav.userAgentData != null && typeof nav.userAgentData.mobile === 'boolean') {\n const uaPlatform = nav.userAgentData.platform;\n if (uaPlatform != null && uaPlatform !== '' && /ipad|tablet/i.test(uaPlatform)) {\n return DeviceType.Tablet;\n }\n\n const result = nav.userAgentData.mobile ? DeviceType.Mobile : DeviceType.Desktop;\n return result;\n }\n\n initMediaQueries();\n\n const width = window.innerWidth;\n const hasCoarsePointer = coarsePointerQuery?.matches ?? false;\n const hasNoHover = noHoverQuery?.matches ?? false;\n const hasTouchSupport = 'ontouchstart' in window || navigator.maxTouchPoints > 0;\n const ua = navigator.userAgent.toLowerCase();\n const isMobileUA = /mobile|android|iphone|ipod|blackberry|iemobile|opera mini/.test(ua);\n const isTabletUA = /tablet|ipad|android(?!.*mobile)/.test(ua);\n\n if (width <= 767 || (isMobileUA && hasTouchSupport)) {\n return DeviceType.Mobile;\n }\n\n if ((width >= 768 && width <= 1024) || isTabletUA || (hasCoarsePointer && hasNoHover && hasTouchSupport)) {\n return DeviceType.Tablet;\n }\n\n return DeviceType.Desktop;\n } catch (error) {\n log('debug', 'Device detection failed, defaulting to desktop', { error });\n\n return DeviceType.Desktop;\n }\n};\n\n/**\n * Detects comprehensive device information including type, OS, and browser.\n *\n * Uses navigator.userAgentData (modern Chromium 90+) with userAgent string fallback.\n *\n * @returns DeviceInfo object with type, os, and browser fields\n */\nexport const getDeviceInfo = (): DeviceInfo => {\n try {\n const nav = navigator as NavigatorWithUserAgentData;\n\n return {\n type: getDeviceType(),\n os: detectOS(nav),\n browser: detectBrowser(nav),\n };\n } catch (error) {\n log('debug', 'Device info detection failed, using defaults', { error });\n\n return {\n type: DeviceType.Desktop,\n os: UNKNOWN,\n browser: UNKNOWN,\n };\n }\n};\n","/**\n * Performance monitoring and web vitals constants for TraceLog\n * Centralizes thresholds and configuration for performance tracking\n */\n\nimport { WebVitalType } from '../types';\nimport type { WebVitalsMode } from '../types/config.types';\n\n// ============================================================================\n// WEB VITALS THRESHOLDS\n// ============================================================================\n\n/**\n * Web Vitals \"good\" thresholds (75th percentile boundaries)\n * Reference: https://web.dev/articles/vitals\n */\nexport const WEB_VITALS_GOOD_THRESHOLDS: Record<WebVitalType, number> = {\n LCP: 2500,\n FCP: 1800,\n CLS: 0.1,\n INP: 200,\n TTFB: 800,\n} as const;\n\n/**\n * Web Vitals \"needs improvement\" thresholds\n */\nexport const WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS: Record<WebVitalType, number> = {\n LCP: 2500,\n FCP: 1800,\n CLS: 0.1,\n INP: 200,\n TTFB: 800,\n} as const;\n\n/**\n * Web Vitals \"poor\" thresholds\n */\nexport const WEB_VITALS_POOR_THRESHOLDS: Record<WebVitalType, number> = {\n LCP: 4000,\n FCP: 3000,\n CLS: 0.25,\n INP: 500,\n TTFB: 1800,\n} as const;\n\n/**\n * Default Web Vitals mode\n * 'needs-improvement' provides balanced approach - captures metrics that need attention\n * while filtering out good performance (reduces noise and costs)\n */\nexport const DEFAULT_WEB_VITALS_MODE: WebVitalsMode = 'needs-improvement';\n\n/**\n * Get Web Vitals thresholds for the specified mode\n */\nexport const getWebVitalsThresholds = (mode: WebVitalsMode = DEFAULT_WEB_VITALS_MODE): Record<WebVitalType, number> => {\n switch (mode) {\n case 'all':\n return { LCP: 0, FCP: 0, CLS: 0, INP: 0, TTFB: 0 };\n case 'needs-improvement':\n return WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS;\n case 'poor':\n return WEB_VITALS_POOR_THRESHOLDS;\n default:\n return WEB_VITALS_NEEDS_IMPROVEMENT_THRESHOLDS;\n }\n};\n\n// ============================================================================\n// PERFORMANCE MONITORING LIMITS\n// ============================================================================\n\n/**\n * Maximum number of navigation history entries to keep in memory\n * Prevents unbounded growth of reportedByNav Map in long-running SPAs\n * Uses FIFO eviction when limit is exceeded\n */\nexport const MAX_NAVIGATION_HISTORY = 50;\n\n/**\n * Precision for performance metric values\n * All performance metrics are rounded to 2 decimal places\n */\nexport const PERFORMANCE_PRECISION_DECIMALS = 2 as const;\n","{\n \"name\": \"@tracelog/lib\",\n \"description\": \"JavaScript library for web analytics and real-time event tracking\",\n \"license\": \"MIT\",\n \"version\": \"3.1.0\",\n \"main\": \"./dist/public-api.cjs\",\n \"module\": \"./dist/public-api.js\",\n \"types\": \"./dist/public-api.d.ts\",\n \"files\": [\n \"dist\"\n ],\n \"exports\": {\n \".\": {\n \"types\": \"./dist/public-api.d.ts\",\n \"import\": \"./dist/public-api.js\",\n \"require\": \"./dist/public-api.cjs\"\n },\n \"./pixel\": {\n \"types\": \"./dist/pixel/index.d.ts\",\n \"import\": \"./dist/pixel/index.js\",\n \"require\": \"./dist/pixel/index.cjs\"\n },\n \"./package.json\": \"./package.json\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/nacorga/tracelog-lib.git\"\n },\n \"homepage\": \"https://github.com/nacorga/tracelog-lib#readme\",\n \"bugs\": {\n \"url\": \"https://github.com/nacorga/tracelog-lib/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"build:browser\": \"NODE_ENV=production vite build\",\n \"build:browser:dev\": \"NODE_ENV=development vite build\",\n \"build:pixel\": \"NODE_ENV=production vite build -c vite.pixel.config.mjs\",\n \"build:all\": \"npm run build && npm run build:browser && npm run build:pixel\",\n \"type-check\": \"npx tsc --noEmit\",\n \"type-check:watch\": \"npx tsc --noEmit --watch\",\n \"lint\": \"npx eslint \\\"src/**/*.ts\\\" \\\"tests/**/*.ts\\\"\",\n \"lint:fix\": \"npx eslint \\\"src/**/*.ts\\\" \\\"tests/**/*.ts\\\" --fix\",\n \"format\": \"prettier --write \\\"src/**/*.ts\\\" \\\"tests/**/*{.ts,.js,.html,.css,.json,.md}\\\" \\\"scripts/**/*.js\\\"\",\n \"format:check\": \"prettier --check \\\"src/**/*.ts\\\" \\\"tests/**/*{.ts,.js,.html,.css,.json,.md}\\\" \\\"scripts/**/*.js\\\"\",\n \"check\": \"npm run lint && npm run format:check\",\n \"fix\": \"npm run lint:fix && npm run format\",\n \"test\": \"npm run test:unit && npm run test:integration\",\n \"test:unit\": \"vitest run\",\n \"test:unit:ci\": \"NODE_OPTIONS=\\\"--max-old-space-size=4096 --no-experimental-fetch\\\" vitest run --pool=threads\",\n \"test:unit:watch\": \"vitest\",\n \"test:coverage\": \"vitest run --coverage\",\n \"test:integration\": \"vitest run --config vitest.integration.config.mjs\",\n \"serve\": \"http-server docs -p 3000 --cors\",\n \"docs:setup\": \"npm run build:browser:dev && cp dist/browser/tracelog.esm.js docs/tracelog.js\",\n \"docs:dev\": \"npm run docs:setup && npm run serve\",\n \"docs:gh-pages\": \"npm run build:browser && cp dist/browser/tracelog.esm.js docs/tracelog.js\",\n \"test:e2e\": \"npm run docs:setup && NODE_ENV=development playwright test\",\n \"test:e2e:ci\": \"npm run docs:setup && NODE_ENV=development playwright test\",\n \"test:critical:ingestion\": \"npm run test:unit -- sender-manager.test.ts session-manager.test.ts\",\n \"ci:build\": \"npm run build:all\",\n \"prepare\": \"husky\",\n \"release\": \"node scripts/release.js\",\n \"release:patch\": \"node scripts/release.js --force-version $(node -p \\\"const v=require('./package.json').version.split('.').map(Number); v[2]++; v.join('.')\\\")\",\n \"release:minor\": \"node scripts/release.js --force-version $(node -p \\\"const v=require('./package.json').version.split('.').map(Number); v[1]++; v[2]=0; v.join('.')\\\")\",\n \"release:major\": \"node scripts/release.js --force-version $(node -p \\\"const v=require('./package.json').version.split('.').map(Number); v[0]++; v[1]=0; v[2]=0; v.join('.')\\\")\",\n \"release:dry-run\": \"node scripts/release.js --dry-run\",\n \"changelog:generate\": \"node scripts/generate-changelog.js --full\",\n \"changelog:preview\": \"node scripts/generate-changelog.js --dry-run\"\n },\n \"dependencies\": {\n \"web-vitals\": \"4.2.4\"\n },\n \"devDependencies\": {\n \"@commitlint/config-conventional\": \"^19.8.1\",\n \"@eslint/js\": \"^9.30.1\",\n \"@playwright/test\": \"^1.54.0\",\n \"@types/jest\": \"^30.0.0\",\n \"@types/node\": \"^24.5.2\",\n \"@typescript-eslint/eslint-plugin\": \"^8.36.0\",\n \"@typescript-eslint/parser\": \"^8.36.0\",\n \"@vitest/coverage-v8\": \"^3.2.4\",\n \"commitlint\": \"^19.8.1\",\n \"cross-env\": \"^10.0.0\",\n \"eslint\": \"^8.57.1\",\n \"eslint-config-prettier\": \"^10.1.5\",\n \"eslint-plugin-prettier\": \"^5.5.1\",\n \"globals\": \"^16.3.0\",\n \"http-server\": \"^14.1.1\",\n \"husky\": \"^9.1.6\",\n \"jsdom\": \"^27.0.0\",\n \"lint-staged\": \"^15.2.10\",\n \"prettier\": \"^3.4.2\",\n \"terser\": \"^5.44.0\",\n \"tsup\": \"^8.5.0\",\n \"typescript\": \"^5.7.3\",\n \"typescript-eslint\": \"^8.36.0\",\n \"uglify-js\": \"^3.19.3\",\n \"vite\": \"^7.0.4\",\n \"vitest\": \"^3.2.4\"\n }\n}\n","import { version } from '../../package.json';\n\nexport const LIB_VERSION = version;\n","import {\n QA_MODE_KEY,\n QA_MODE_URL_PARAM,\n QA_MODE_ENABLE_VALUE,\n QA_MODE_DISABLE_VALUE,\n LOG_STYLE_ACTIVE,\n LOG_STYLE_DISABLED,\n} from '../../constants';\nimport { log } from '../logging.utils';\n\n// ============================================================================\n// Internal Helpers\n// ============================================================================\n\n/**\n * Check if browser environment is available\n */\nconst isBrowserEnvironment = (): boolean => {\n return typeof window !== 'undefined' && typeof sessionStorage !== 'undefined';\n};\n\n/**\n * Clean URL parameter from the current URL\n */\nconst cleanUrlParameter = (): void => {\n try {\n const params = new URLSearchParams(window.location.search);\n params.delete(QA_MODE_URL_PARAM);\n\n const search = params.toString();\n const url = window.location.pathname + (search ? '?' + search : '') + window.location.hash;\n\n window.history.replaceState({}, '', url);\n } catch {\n // Continue without cleaning URL\n }\n};\n\n// ============================================================================\n// QA Mode Public API\n// ============================================================================\n\n/**\n * Detects QA mode from URL parameter or sessionStorage.\n *\n * QA mode shows custom event logs to help verify tracking implementation.\n *\n * Activation:\n * - URL: `?tlog_mode=qa` to enable, `?tlog_mode=qa_off` to disable.\n *\n * @returns True if QA mode is active, false otherwise\n */\nexport const detectQaMode = (): boolean => {\n if (!isBrowserEnvironment()) {\n return false;\n }\n\n try {\n const params = new URLSearchParams(window.location.search);\n const urlParam = params.get(QA_MODE_URL_PARAM);\n const storedState = sessionStorage.getItem(QA_MODE_KEY);\n\n let newState: boolean | null = null;\n\n if (urlParam === QA_MODE_ENABLE_VALUE) {\n newState = true;\n sessionStorage.setItem(QA_MODE_KEY, 'true');\n\n log('info', 'QA Mode ACTIVE', {\n visibility: 'qa',\n style: LOG_STYLE_ACTIVE,\n });\n } else if (urlParam === QA_MODE_DISABLE_VALUE) {\n newState = false;\n sessionStorage.setItem(QA_MODE_KEY, 'false');\n\n log('info', 'QA Mode DISABLED', {\n visibility: 'qa',\n style: LOG_STYLE_DISABLED,\n });\n }\n\n if (urlParam === QA_MODE_ENABLE_VALUE || urlParam === QA_MODE_DISABLE_VALUE) {\n cleanUrlParameter();\n }\n\n return newState ?? storedState === 'true';\n } catch {\n return false;\n }\n};\n","/**\n * Returns `true` while the current document is being pre-rendered or prefetched\n * via the Speculation Rules API (e.g. Shopify themes prerendering a PDP on hover).\n *\n * Tracking must stay fully dormant during this phase: a page that is pre-rendered\n * but never activated must emit **zero** events so it never creates a server-side\n * session. Emission resumes on the `prerenderingchange` event (activation).\n *\n * SSR-safe and degrades gracefully: on browsers without the API,\n * `document.prerendering` is `undefined` (falsy) → treated as \"not pre-rendering\",\n * so the normal tracking path runs unchanged. `=== true` is the correct idiom.\n */\nexport const isPrerendering = (): boolean => typeof document !== 'undefined' && document.prerendering === true;\n","import { log } from '../logging.utils';\n\n/**\n * List of compound TLDs that require special handling for root domain extraction.\n * Keep in sync with tracelog-api/src/utils/common/utils.ts\n */\nconst COMPOUND_TLDS = [\n 'co.uk',\n 'org.uk',\n 'com.au',\n 'net.au',\n 'com.br',\n 'co.nz',\n 'co.jp',\n 'com.mx',\n 'co.in',\n 'com.cn',\n 'co.za',\n];\n\n/**\n * Extracts the root (registrable) domain from a hostname.\n * Handles both standard TLDs (.com, .org) and compound TLDs (.co.uk, .com.br).\n *\n * @example\n * getRootDomain('www.example.com') // 'example.com'\n * getRootDomain('app.blog.example.com') // 'example.com'\n * getRootDomain('shop.example.co.uk') // 'example.co.uk'\n */\nconst getRootDomain = (hostname: string): string => {\n const parts = hostname.toLowerCase().split('.');\n if (parts.length <= 2) {\n return hostname.toLowerCase();\n }\n const lastTwo = parts.slice(-2).join('.');\n if (COMPOUND_TLDS.includes(lastTwo)) {\n return parts.slice(-3).join('.');\n }\n return parts.slice(-2).join('.');\n};\n\n/**\n * Checks if two hostnames belong to the same domain (including subdomains).\n * Extracts root domain and compares to handle cross-subdomain navigation.\n *\n * @example\n * isSameDomain('www.example.com', 'example.com') // true\n * isSameDomain('app.example.com', 'www.example.com') // true\n * isSameDomain('example.co.uk', 'app.example.co.uk') // true\n *\n * @param hostname1 - First hostname (e.g., 'www.example.com')\n * @param hostname2 - Second hostname (e.g., 'app.example.com')\n * @returns true if same root domain\n */\nconst isSameDomain = (hostname1: string, hostname2: string): boolean => {\n if (hostname1 === hostname2) {\n return true;\n }\n return getRootDomain(hostname1) === getRootDomain(hostname2);\n};\n\n/**\n * Returns the referrer if it's external, or 'Direct' if internal/empty.\n *\n * **Purpose**: Filter out internal referrers (same domain) to ensure\n * accurate traffic source attribution. Internal referrers occur when:\n * - Session expires and user navigates within the same site\n * - User opens new tab from an internal link\n * - Page refresh after session timeout\n *\n * **Logic**:\n * - Empty referrer → 'Direct'\n * - Referrer from same domain or subdomain → 'Direct' (internal navigation)\n * - External referrer → Returns original referrer\n *\n * **Subdomain Detection**:\n * - `www.example.com` → `example.com` ✓ (internal)\n * - `blog.example.com` → `example.com` ✓ (internal)\n * - `example.com` → `www.example.com` ✓ (internal)\n *\n * @returns External referrer URL or 'Direct'\n */\nexport const getExternalReferrer = (): string => {\n const referrer = document.referrer;\n if (!referrer) {\n return 'Direct';\n }\n try {\n const referrerHostname = new URL(referrer).hostname.toLowerCase();\n const currentHostname = window.location.hostname.toLowerCase();\n if (isSameDomain(referrerHostname, currentHostname)) {\n return 'Direct';\n }\n return referrer;\n } catch (error) {\n log('debug', 'Failed to parse referrer URL, using raw value', { error, data: { referrer } });\n return referrer;\n }\n};\n","import { UTM_PARAMS } from '../../constants';\nimport { UTM } from '../../types/event.types';\n\n/**\n * Extracts UTM parameters from the current URL\n * @returns UTM parameters object or undefined if none found\n */\nexport const getUTMParameters = (): UTM | undefined => {\n const urlParams = new URLSearchParams(window.location.search);\n const utmParams: Partial<Record<keyof UTM, string>> = {};\n\n UTM_PARAMS.forEach((param) => {\n const value = urlParams.get(param);\n\n if (value) {\n const key = param.split('utm_')[1] as keyof UTM;\n utmParams[key] = value;\n }\n });\n\n const result = Object.keys(utmParams).length ? utmParams : undefined;\n\n return result;\n};\n","/**\n * Generates a RFC4122 compliant UUID v4 using native crypto API with fallback\n * @returns A UUID string\n */\nexport const generateUUID = (): string => {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n};\n\n/**\n * Sequence counter for generating unique event IDs within the same millisecond.\n * Resets when timestamp changes, preventing collisions in high-frequency event bursts.\n */\nlet eventSequence = 0;\nlet lastEventTimestamp = 0;\n\n/**\n * Generates a unique event ID optimized for high-frequency event tracking.\n *\n * **Collision Prevention Strategy:**\n * - Timestamp: Millisecond precision for temporal ordering\n * - Sequence: Auto-incrementing counter (0-999) for same-millisecond events\n * - Random: Cryptographically secure random (3 bytes) for cross-tab/process uniqueness\n *\n * **Format:** `{timestamp}-{sequence}-{random}`\n * **Example:** `1704067200000-001-a3f9c2`\n *\n * **Guarantees:**\n * - ✅ No collisions within same millisecond (sequence counter)\n * - ✅ No collisions across tabs (crypto random)\n * - ✅ Temporal ordering preserved (timestamp first)\n * - ✅ Works in high-frequency bursts (1000 events/ms capacity)\n * - ✅ Clock skew protection (monotonic timestamp guarantee)\n *\n * @returns Unique event ID string\n */\nexport const generateEventId = (): string => {\n let timestamp = Date.now();\n\n // Protect against clock skew (NTP sync, manual time adjustments, timezone changes)\n // If clock moves backward, use last valid timestamp to prevent ID collisions\n if (timestamp < lastEventTimestamp) {\n timestamp = lastEventTimestamp;\n }\n\n // Increment sequence counter for events in same millisecond\n if (timestamp === lastEventTimestamp) {\n eventSequence = (eventSequence + 1) % 1000; // Reset at 1000 to keep 3 digits\n } else {\n eventSequence = 0;\n }\n\n // Always update lastEventTimestamp to track current (possibly adjusted) timestamp\n lastEventTimestamp = timestamp;\n\n const sequence = eventSequence.toString().padStart(3, '0');\n\n // Cryptographically secure random (3 bytes = 6 hex chars)\n // Reduced from 4 bytes since sequence provides uniqueness within millisecond\n let random = '';\n try {\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const bytes = crypto.getRandomValues(new Uint8Array(3));\n if (bytes) {\n random = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n }\n }\n } catch {\n /* empty */\n }\n\n // Fallback to Math.random() if crypto unavailable\n if (!random) {\n random = Math.floor(Math.random() * 0xffffff)\n .toString(16)\n .padStart(6, '0');\n }\n\n return `${timestamp}-${sequence}-${random}`;\n};\n","import { Config } from '../../types';\nimport { DEFAULT_SENSITIVE_QUERY_PARAMS } from '../../constants';\nimport { log } from '../logging.utils';\n\n/**\n * Validates if a URL is valid HTTPS\n */\nconst isValidUrl = (url: string): boolean => {\n try {\n return new URL(url).protocol === 'https:';\n } catch {\n return false;\n }\n};\n\n/**\n * Generates a SaaS API URL based on the given project ID and the current browser domain.\n * @param projectId - The project ID to use as a subdomain.\n * @returns The generated SaaS API URL.\n */\nconst generateSaasApiUrl = (projectId: string): string => {\n try {\n const url = new URL(window.location.href);\n const host = url.hostname;\n\n if (!host || typeof host !== 'string') {\n throw new Error('Invalid hostname');\n }\n\n if (host === 'localhost' || host === '127.0.0.1' || /^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$/.test(host)) {\n throw new Error(\n 'SaaS integration requires a domain hostname; localhost and IP addresses are not supported. ' +\n 'For local development, omit `integrations.tracelog` to run in standalone mode (events emitted locally, ' +\n 'no network requests), or test against a staging domain that resolves to your dev machine via /etc/hosts.',\n );\n }\n\n const parts = host.split('.');\n\n if (!parts || !Array.isArray(parts) || parts.length === 0 || (parts.length === 1 && parts[0] === '')) {\n throw new Error('Invalid hostname structure');\n }\n\n if (parts.length === 1) {\n throw new Error('Single-part domain not supported for SaaS integration');\n }\n\n const cleanDomain = parts.length === 2 ? parts.join('.') : parts.slice(-2).join('.');\n\n if (!cleanDomain || cleanDomain.split('.').length < 2) {\n throw new Error('Invalid domain structure for SaaS');\n }\n\n const collectApiUrl = `https://${projectId}.${cleanDomain}/collect`;\n\n if (!isValidUrl(collectApiUrl)) {\n throw new Error('Generated URL failed validation');\n }\n\n return collectApiUrl;\n } catch (error) {\n throw new Error(`Invalid SaaS URL configuration: ${error instanceof Error ? error.message : String(error)}`);\n }\n};\n\n/**\n * Generates collection API URLs for the configured TraceLog SaaS integration\n * @param config - The TraceLog configuration\n * @returns Object containing the SaaS API URL (if configured)\n */\nexport const getCollectApiUrls = (config: Config): { saas?: string } => {\n const urls: { saas?: string } = {};\n\n if (config.integrations?.tracelog?.projectId) {\n urls.saas = generateSaasApiUrl(config.integrations.tracelog.projectId);\n }\n\n return urls;\n};\n\n/**\n * Normalizes a URL by removing sensitive query parameters\n * Combines default sensitive parameters with custom ones provided by user\n * @param url - The URL to normalize\n * @param sensitiveQueryParams - Array of parameter names to remove (merged with defaults)\n * @returns The normalized URL\n */\nexport const normalizeUrl = (url: string, sensitiveQueryParams: string[] = []): string => {\n if (!url || typeof url !== 'string') {\n log('warn', 'Invalid URL provided to normalizeUrl', { data: { type: typeof url } });\n return url || '';\n }\n\n try {\n const urlObject = new URL(url);\n const searchParams = urlObject.searchParams;\n\n const allSensitiveParams = [...new Set([...DEFAULT_SENSITIVE_QUERY_PARAMS, ...sensitiveQueryParams])];\n\n let hasChanged = false;\n const removedParams: string[] = [];\n\n allSensitiveParams.forEach((param) => {\n if (searchParams.has(param)) {\n searchParams.delete(param);\n hasChanged = true;\n removedParams.push(param);\n }\n });\n\n if (!hasChanged && url.includes('?')) {\n return url;\n }\n\n urlObject.search = searchParams.toString();\n return urlObject.toString();\n } catch (error) {\n log('warn', 'URL normalization failed, returning original', { error, data: { urlLength: url?.length } });\n return url;\n }\n};\n","import {\n MAX_ARRAY_LENGTH,\n MAX_OBJECT_DEPTH,\n MAX_STRING_LENGTH,\n MAX_NESTED_OBJECT_KEYS,\n XSS_PATTERNS,\n} from '../../constants';\nimport { MetadataType } from '../../types';\nimport { log } from '../logging.utils';\n\n/**\n * Sanitizes a string value to prevent XSS attacks\n * @param value - The string to sanitize\n * @returns The sanitized string\n */\nexport const sanitizeString = (value: string): string => {\n if (!value || typeof value !== 'string' || value.trim().length === 0) {\n return '';\n }\n\n let sanitized = value;\n\n if (value.length > MAX_STRING_LENGTH) {\n sanitized = value.slice(0, Math.max(0, MAX_STRING_LENGTH));\n }\n\n let xssPatternMatches = 0;\n for (const pattern of XSS_PATTERNS) {\n const beforeReplace = sanitized;\n sanitized = sanitized.replace(pattern, '');\n if (beforeReplace !== sanitized) {\n xssPatternMatches++;\n }\n }\n\n if (xssPatternMatches > 0) {\n log('warn', 'XSS patterns detected and removed', {\n data: {\n patternMatches: xssPatternMatches,\n valueLength: value.length,\n },\n });\n }\n\n const result = sanitized.trim();\n\n return result;\n};\n\n/**\n * Sanitizes any value recursively with depth protection\n * @param value - The value to sanitize\n * @param depth - Current recursion depth\n * @returns The sanitized value\n */\nconst sanitizeValue = (value: unknown, depth = 0): unknown => {\n if (value === null || value === undefined) {\n return null;\n }\n\n // Primitives are always allowed regardless of depth\n if (typeof value === 'string') {\n return sanitizeString(value);\n }\n\n if (typeof value === 'number') {\n if (!Number.isFinite(value) || value < -Number.MAX_SAFE_INTEGER || value > Number.MAX_SAFE_INTEGER) {\n return 0;\n }\n\n return value;\n }\n\n if (typeof value === 'boolean') {\n return value;\n }\n\n // Depth check only applies to complex types (arrays and objects)\n if (depth > MAX_OBJECT_DEPTH) {\n return null;\n }\n\n if (Array.isArray(value)) {\n const limitedArray = value.slice(0, MAX_ARRAY_LENGTH);\n const sanitizedArray = limitedArray.map((item) => sanitizeValue(item, depth + 1)).filter((item) => item !== null);\n\n return sanitizedArray;\n }\n\n if (typeof value === 'object') {\n const sanitizedObject: Record<string, unknown> = {};\n const entries = Object.entries(value);\n const limitedEntries = entries.slice(0, MAX_NESTED_OBJECT_KEYS);\n\n for (const [key, value_] of limitedEntries) {\n const sanitizedKey = sanitizeString(key);\n\n if (sanitizedKey) {\n const sanitizedValue = sanitizeValue(value_, depth + 1);\n\n if (sanitizedValue !== null) {\n sanitizedObject[sanitizedKey] = sanitizedValue;\n }\n }\n }\n\n return sanitizedObject;\n }\n\n return null;\n};\n\n/**\n * Sanitizes user metadata for custom events\n * @param metadata - The metadata to sanitize\n * @returns The sanitized metadata\n */\nexport const sanitizeMetadata = (metadata: unknown): Record<string, MetadataType> => {\n if (typeof metadata !== 'object' || metadata === null) {\n return {};\n }\n\n try {\n const sanitized = sanitizeValue(metadata);\n const result =\n typeof sanitized === 'object' && sanitized !== null ? (sanitized as Record<string, MetadataType>) : {};\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`[TraceLog] Metadata sanitization failed: ${errorMessage}`);\n }\n};\n","/**\n * Regular expressions used to detect and redact common PII patterns from\n * free-form text (click text, error messages, stack traces, etc.).\n *\n * Mirrors the patterns relied on by `ClickHandler` and `ErrorHandler`. Adding\n * a pattern here automatically widens coverage for both handlers.\n */\nexport const PII_PATTERNS = [\n // Email addresses.\n // Quantifiers are bounded (local part ≤64, each label ≤63, TLD ≤63 per RFC/DNS limits)\n // and the domain is matched as discrete dot-separated labels so the local-part and\n // domain classes never overlap. This keeps matching linear and prevents catastrophic\n // backtracking (ReDoS) on long, dot-heavy inputs that contain no real email.\n /\\b[A-Za-z0-9._%+-]{1,64}@(?:[A-Za-z0-9-]{1,63}\\.)+[A-Za-z]{2,63}\\b/gi,\n\n // US Phone numbers (various formats)\n /\\b\\d{3}[-.]?\\d{3}[-.]?\\d{4}\\b/g,\n\n // Credit card numbers (16 digits with optional separators)\n /\\b\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b/g,\n\n // IBAN (International Bank Account Number)\n /\\b[A-Z]{2}\\d{2}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}[-\\s]?\\d{4}\\b/gi,\n\n // API keys / tokens (sk_test_, sk_live_, pk_test_, pk_live_, …)\n /\\b[sp]k_(test|live)_[a-zA-Z0-9]{10,}\\b/gi,\n\n // Bearer tokens (JWT-like patterns — matches complete and partial tokens)\n /Bearer\\s+[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)?(?:\\.[A-Za-z0-9_-]+)?/gi,\n\n // Passwords in connection strings (protocol://user:password@host)\n /:\\/\\/[^:/]+:([^@]+)@/gi,\n\n // Sensitive URL query parameters (token=, password=, auth=, secret=, api_key=, …)\n /[?&](token|password|passwd|auth|secret|secret_key|private_key|auth_key|api_key|apikey|access_token)=[^&\\s]+/gi,\n] as const;\n\n/**\n * Replaces every match of {@link PII_PATTERNS} with `[REDACTED]`.\n *\n * The function does not mutate the input. Inputs that don't contain PII are\n * returned untouched (after going through `String.prototype.replace`, which is\n * a no-op when no match exists).\n */\nexport const sanitizePii = (text: string): string => {\n let sanitized = text;\n\n for (const pattern of PII_PATTERNS) {\n sanitized = sanitized.replace(pattern, '[REDACTED]');\n }\n\n return sanitized;\n};\n","import {\n MAX_SESSION_TIMEOUT_MS,\n MIN_SESSION_TIMEOUT_MS,\n DEFAULT_SESSION_TIMEOUT,\n DEFAULT_SAMPLING_RATE,\n VALIDATION_MESSAGES,\n DEFAULT_PAGE_VIEW_THROTTLE_MS,\n DEFAULT_CLICK_THROTTLE_MS,\n MAX_SAME_EVENT_PER_MINUTE,\n DEFAULT_ERROR_SAMPLING_RATE,\n MIN_SEND_INTERVAL_MS,\n MAX_SEND_INTERVAL_MS_CONFIG,\n EVENT_SENT_INTERVAL_MS,\n} from '../../constants';\nimport {\n Config,\n AppConfigValidationError,\n SessionTimeoutValidationError,\n SamplingRateValidationError,\n IntegrationValidationError,\n} from '../../types';\n\n/**\n * Validates the app configuration object (before normalization)\n */\nexport const validateAppConfig = (config?: Config): void => {\n if (config !== undefined && (config === null || typeof config !== 'object')) {\n throw new AppConfigValidationError('Configuration must be an object', 'config');\n }\n\n if (!config) {\n return;\n }\n\n if (config.sessionTimeout !== undefined) {\n if (\n typeof config.sessionTimeout !== 'number' ||\n config.sessionTimeout < MIN_SESSION_TIMEOUT_MS ||\n config.sessionTimeout > MAX_SESSION_TIMEOUT_MS\n ) {\n throw new SessionTimeoutValidationError(VALIDATION_MESSAGES.INVALID_SESSION_TIMEOUT, 'config');\n }\n }\n\n if (config.globalMetadata !== undefined) {\n if (typeof config.globalMetadata !== 'object' || config.globalMetadata === null) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_GLOBAL_METADATA, 'config');\n }\n }\n\n if (config.integrations) {\n validateIntegrations(config.integrations);\n }\n\n if (config.sensitiveQueryParams !== undefined) {\n if (!Array.isArray(config.sensitiveQueryParams)) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_SENSITIVE_QUERY_PARAMS, 'config');\n }\n\n for (const param of config.sensitiveQueryParams) {\n if (typeof param !== 'string') {\n throw new AppConfigValidationError('All sensitive query params must be strings', 'config');\n }\n }\n }\n\n if (config.errorSampling !== undefined) {\n if (typeof config.errorSampling !== 'number' || config.errorSampling < 0 || config.errorSampling > 1) {\n throw new SamplingRateValidationError(VALIDATION_MESSAGES.INVALID_ERROR_SAMPLING_RATE, 'config');\n }\n }\n\n if (config.samplingRate !== undefined) {\n if (typeof config.samplingRate !== 'number' || config.samplingRate < 0 || config.samplingRate > 1) {\n throw new SamplingRateValidationError(VALIDATION_MESSAGES.INVALID_SAMPLING_RATE, 'config');\n }\n }\n\n if (config.pageViewThrottleMs !== undefined) {\n if (typeof config.pageViewThrottleMs !== 'number' || config.pageViewThrottleMs < 0) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_PAGE_VIEW_THROTTLE, 'config');\n }\n }\n\n if (config.clickThrottleMs !== undefined) {\n if (typeof config.clickThrottleMs !== 'number' || config.clickThrottleMs < 0) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_CLICK_THROTTLE, 'config');\n }\n }\n\n if (config.maxSameEventPerMinute !== undefined) {\n if (typeof config.maxSameEventPerMinute !== 'number' || config.maxSameEventPerMinute <= 0) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_MAX_SAME_EVENT_PER_MINUTE, 'config');\n }\n }\n\n if (config.sendIntervalMs !== undefined) {\n if (\n !Number.isFinite(config.sendIntervalMs) ||\n config.sendIntervalMs < MIN_SEND_INTERVAL_MS ||\n config.sendIntervalMs > MAX_SEND_INTERVAL_MS_CONFIG\n ) {\n throw new AppConfigValidationError(VALIDATION_MESSAGES.INVALID_SEND_INTERVAL, 'config');\n }\n }\n\n if (config.flushOnSpaNavigation !== undefined && typeof config.flushOnSpaNavigation !== 'boolean') {\n throw new AppConfigValidationError(\n `Invalid flushOnSpaNavigation type: ${typeof config.flushOnSpaNavigation}. Must be a boolean`,\n 'config',\n );\n }\n\n if (config.flushOnPageHidden !== undefined && typeof config.flushOnPageHidden !== 'boolean') {\n throw new AppConfigValidationError(\n `Invalid flushOnPageHidden type: ${typeof config.flushOnPageHidden}. Must be a boolean`,\n 'config',\n );\n }\n\n if (config.webVitalsMode !== undefined) {\n if (typeof config.webVitalsMode !== 'string') {\n throw new AppConfigValidationError(\n `Invalid webVitalsMode type: ${typeof config.webVitalsMode}. Must be a string`,\n 'config',\n );\n }\n\n const validModes = ['all', 'needs-improvement', 'poor'];\n if (!validModes.includes(config.webVitalsMode)) {\n throw new AppConfigValidationError(\n `Invalid webVitalsMode: \"${config.webVitalsMode}\". Must be one of: ${validModes.join(', ')}`,\n 'config',\n );\n }\n }\n\n if (config.webVitalsThresholds !== undefined) {\n if (\n typeof config.webVitalsThresholds !== 'object' ||\n config.webVitalsThresholds === null ||\n Array.isArray(config.webVitalsThresholds)\n ) {\n throw new AppConfigValidationError('webVitalsThresholds must be an object', 'config');\n }\n\n const validKeys = ['LCP', 'FCP', 'CLS', 'INP', 'TTFB'];\n for (const [key, value] of Object.entries(config.webVitalsThresholds)) {\n if (!validKeys.includes(key)) {\n throw new AppConfigValidationError(\n `Invalid Web Vitals threshold key: \"${key}\". Must be one of: ${validKeys.join(', ')}`,\n 'config',\n );\n }\n\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n throw new AppConfigValidationError(\n `Invalid Web Vitals threshold value for ${key}: ${value}. Must be a non-negative finite number`,\n 'config',\n );\n }\n }\n }\n};\n\n/**\n * Validates integrations configuration\n */\nconst validateIntegrations = (integrations: Config['integrations']): void => {\n if (!integrations) {\n return;\n }\n\n if (integrations.tracelog) {\n if (\n !integrations.tracelog.projectId ||\n typeof integrations.tracelog.projectId !== 'string' ||\n integrations.tracelog.projectId.trim() === ''\n ) {\n throw new IntegrationValidationError(VALIDATION_MESSAGES.INVALID_TRACELOG_PROJECT_ID, 'config');\n }\n\n if (integrations.tracelog.shopify !== undefined && typeof integrations.tracelog.shopify !== 'boolean') {\n throw new IntegrationValidationError('tracelog.shopify must be a boolean', 'config');\n }\n }\n};\n\n/**\n * Validates and normalizes the app configuration\n */\nexport const validateAndNormalizeConfig = (config?: Config): Config => {\n validateAppConfig(config);\n\n const normalizedConfig: Config = {\n ...(config ?? {}),\n sessionTimeout: config?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT,\n globalMetadata: config?.globalMetadata ?? {},\n sensitiveQueryParams: config?.sensitiveQueryParams ?? [],\n errorSampling: config?.errorSampling ?? DEFAULT_ERROR_SAMPLING_RATE,\n samplingRate: config?.samplingRate ?? DEFAULT_SAMPLING_RATE,\n pageViewThrottleMs: config?.pageViewThrottleMs ?? DEFAULT_PAGE_VIEW_THROTTLE_MS,\n clickThrottleMs: config?.clickThrottleMs ?? DEFAULT_CLICK_THROTTLE_MS,\n maxSameEventPerMinute: config?.maxSameEventPerMinute ?? MAX_SAME_EVENT_PER_MINUTE,\n sendIntervalMs: config?.sendIntervalMs ?? EVENT_SENT_INTERVAL_MS,\n flushOnSpaNavigation: config?.flushOnSpaNavigation ?? false,\n flushOnPageHidden: config?.flushOnPageHidden ?? true,\n };\n\n return normalizedConfig;\n};\n","/**\n * Checks if a value is JSON-serializable (primitives, arrays, or plain objects).\n * Rejects functions, symbols, circular references, and other non-serializable types.\n * @param value - The value to check\n * @param seen - Set of visited objects to detect circular references\n * @returns True if the value is JSON-serializable\n */\nconst isSerializable = (value: unknown, seen: Set<unknown> = new Set()): boolean => {\n if (value === null || value === undefined) {\n return true;\n }\n\n const type = typeof value;\n\n if (type === 'string' || type === 'number' || type === 'boolean') {\n return true;\n }\n\n if (type === 'function' || type === 'symbol' || type === 'bigint') {\n return false;\n }\n\n // Circular reference check for complex types\n if (seen.has(value)) {\n return false;\n }\n seen.add(value);\n\n if (Array.isArray(value)) {\n return value.every((item) => isSerializable(item, seen));\n }\n\n if (type === 'object') {\n return Object.values(value as Record<string, unknown>).every((v) => isSerializable(v, seen));\n }\n\n return false;\n};\n\n/**\n * Checks if an object contains only JSON-serializable fields.\n * Accepts any depth of nesting as long as all values are serializable.\n * @param object - The object to check\n * @returns True if the object contains only serializable fields\n */\nexport const isOnlyPrimitiveFields = (object: Record<string, unknown>): boolean => {\n if (typeof object !== 'object' || object === null) {\n return false;\n }\n\n return isSerializable(object);\n};\n\n/**\n * Extracts a plain `Record<string, string>` from an untrusted traits value.\n *\n * Rejects arrays, nulls, and non-string values (TS types erased at runtime).\n * Used by `identify(userId, traits?)` to defend against consumers passing\n * `null`, deeply-nested objects, or non-string fields.\n *\n * @returns Sanitized traits, or `undefined` if the input has no string fields.\n */\nexport const sanitizeTraits = (traits: unknown): Record<string, string> | undefined => {\n if (typeof traits !== 'object' || traits === null || Array.isArray(traits)) return undefined;\n\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(traits as Record<string, unknown>)) {\n if (typeof value === 'string') filtered[key] = value;\n }\n\n return Object.keys(filtered).length > 0 ? filtered : undefined;\n};\n","import {\n MAX_CUSTOM_EVENT_ARRAY_SIZE,\n MAX_CUSTOM_EVENT_KEYS,\n MAX_CUSTOM_EVENT_NAME_LENGTH,\n MAX_CUSTOM_EVENT_STRING_SIZE,\n MAX_STRING_LENGTH,\n MAX_STRING_LENGTH_IN_ARRAY,\n} from '../../constants';\nimport { MetadataType } from '../../types';\nimport { sanitizeMetadata } from '../security/sanitize.utils';\nimport { isOnlyPrimitiveFields } from './type-guards.utils';\n\n/**\n * Validates an event name\n * @param eventName - The event name to validate\n * @returns Validation result with error message if invalid\n */\nexport const isValidEventName = (eventName: string): { valid: boolean; error?: string } => {\n if (typeof eventName !== 'string') {\n return {\n valid: false,\n error: 'Event name must be a string',\n };\n }\n\n if (eventName.length === 0) {\n return {\n valid: false,\n error: 'Event name cannot be empty',\n };\n }\n\n if (eventName.length > MAX_CUSTOM_EVENT_NAME_LENGTH) {\n return {\n valid: false,\n error: `Event name is too long (max ${MAX_CUSTOM_EVENT_NAME_LENGTH} characters)`,\n };\n }\n\n if (eventName.includes('<') || eventName.includes('>') || eventName.includes('&')) {\n return {\n valid: false,\n error: 'Event name contains invalid characters',\n };\n }\n\n const reservedWords = ['constructor', 'prototype', '__proto__', 'eval', 'function', 'var', 'let', 'const'];\n\n if (reservedWords.includes(eventName.toLowerCase())) {\n return {\n valid: false,\n error: 'Event name cannot be a reserved word',\n };\n }\n\n return { valid: true };\n};\n\n/**\n * Validates a single metadata object\n * @param eventName - The event name (for error messages)\n * @param metadata - The metadata object to validate\n * @param type - Type of metadata (globalMetadata or customEvent)\n * @returns Validation result with sanitized metadata if valid\n */\nconst validateSingleMetadata = (\n eventName: string,\n metadata: Record<string, unknown>,\n type?: 'globalMetadata' | 'customEvent',\n): { valid: boolean; error?: string; sanitizedMetadata?: Record<string, MetadataType> } => {\n const sanitizedMetadata = sanitizeMetadata(metadata);\n const intro =\n type && type === 'customEvent' ? `${type} \"${eventName}\" metadata error` : `${eventName} metadata error`;\n\n if (!isOnlyPrimitiveFields(sanitizedMetadata)) {\n return {\n valid: false,\n error: `${intro}: object has invalid types. Valid types are string, number, boolean or string arrays.`,\n };\n }\n\n let jsonString: string;\n\n try {\n jsonString = JSON.stringify(sanitizedMetadata);\n } catch {\n return {\n valid: false,\n error: `${intro}: object contains circular references or cannot be serialized.`,\n };\n }\n\n const byteSize = new TextEncoder().encode(jsonString).byteLength;\n\n if (byteSize > MAX_CUSTOM_EVENT_STRING_SIZE) {\n return {\n valid: false,\n error: `${intro}: object is too large (max ${MAX_CUSTOM_EVENT_STRING_SIZE / 1024} KB).`,\n };\n }\n\n const keyCount = Object.keys(sanitizedMetadata).length;\n\n if (keyCount > MAX_CUSTOM_EVENT_KEYS) {\n return {\n valid: false,\n error: `${intro}: object has too many keys (max ${MAX_CUSTOM_EVENT_KEYS} keys).`,\n };\n }\n\n for (const [key, value] of Object.entries(sanitizedMetadata)) {\n if (Array.isArray(value)) {\n if (value.length > MAX_CUSTOM_EVENT_ARRAY_SIZE) {\n return {\n valid: false,\n error: `${intro}: array property \"${key}\" is too large (max ${MAX_CUSTOM_EVENT_ARRAY_SIZE} items).`,\n };\n }\n\n for (const item of value) {\n if (typeof item === 'string' && item.length > MAX_STRING_LENGTH_IN_ARRAY) {\n return {\n valid: false,\n error: `${intro}: array property \"${key}\" contains strings that are too long (max ${MAX_STRING_LENGTH_IN_ARRAY} characters).`,\n };\n }\n }\n }\n\n if (typeof value === 'string' && value.length > MAX_STRING_LENGTH) {\n return {\n valid: false,\n error: `${intro}: property \"${key}\" is too long (max ${MAX_STRING_LENGTH} characters).`,\n };\n }\n }\n\n return {\n valid: true,\n sanitizedMetadata,\n };\n};\n\n/**\n * Validates metadata for events (supports both objects and arrays of objects)\n * @param eventName - The event name (for error messages)\n * @param metadata - The metadata to validate\n * @param type - Type of metadata (globalMetadata or customEvent)\n * @returns Validation result with sanitized metadata if valid\n */\nexport const isValidMetadata = (\n eventName: string,\n metadata: Record<string, unknown> | Record<string, unknown>[],\n type?: 'globalMetadata' | 'customEvent',\n): {\n valid: boolean;\n error?: string;\n sanitizedMetadata?: Record<string, MetadataType> | Record<string, MetadataType>[];\n} => {\n if (Array.isArray(metadata)) {\n const sanitizedArray: Record<string, MetadataType>[] = [];\n const intro =\n type && type === 'customEvent' ? `${type} \"${eventName}\" metadata error` : `${eventName} metadata error`;\n\n for (let i = 0; i < metadata.length; i++) {\n const item = metadata[i];\n\n if (typeof item !== 'object' || item === null || Array.isArray(item)) {\n return {\n valid: false,\n error: `${intro}: array item at index ${i} must be an object.`,\n };\n }\n\n const itemValidation = validateSingleMetadata(eventName, item, type);\n\n if (!itemValidation.valid) {\n return {\n valid: false,\n error: `${intro}: array item at index ${i} is invalid: ${itemValidation.error}`,\n };\n }\n\n if (itemValidation.sanitizedMetadata) {\n sanitizedArray.push(itemValidation.sanitizedMetadata);\n }\n }\n\n return {\n valid: true,\n sanitizedMetadata: sanitizedArray,\n };\n }\n\n return validateSingleMetadata(eventName, metadata, type);\n};\n","import { MetadataType } from '../../types';\nimport { log } from '../logging.utils';\nimport { isValidEventName, isValidMetadata } from './metadata-validations.utils';\n\n/**\n * Validates a complete event with name and optional metadata\n * @param eventName - The event name to validate\n * @param metadata - Optional metadata to validate\n * @returns Validation result with sanitized metadata if valid\n */\nexport const isEventValid = (\n eventName: string,\n metadata?: Record<string, unknown> | Record<string, unknown>[],\n): {\n valid: boolean;\n error?: string;\n sanitizedMetadata?: Record<string, MetadataType> | Record<string, MetadataType>[];\n} => {\n const nameValidation = isValidEventName(eventName);\n\n if (!nameValidation.valid) {\n log('error', 'Event name validation failed', {\n data: { eventName, error: nameValidation.error },\n });\n\n return nameValidation;\n }\n\n if (!metadata) {\n return { valid: true };\n }\n\n const metadataValidation = isValidMetadata(eventName, metadata, 'customEvent');\n\n if (!metadataValidation.valid) {\n log('error', 'Event metadata validation failed', {\n data: {\n eventName,\n error: metadataValidation.error,\n },\n });\n }\n\n return metadataValidation;\n};\n","import { EmitterCallback, EmitterMap } from '../types';\n\n/**\n * Type-safe event emitter for TraceLog internal events\n *\n * **Purpose**: Provides pub/sub mechanism for internal library events with full TypeScript\n * type safety through `EmitterMap` interface.\n *\n * **Supported Events** (defined in `EmitterMap`):\n * - `event`: Individual events as they are tracked (EventData)\n * - `queue`: Complete event batches before transmission (EventsQueue)\n *\n * **Note**: Developers are responsible for implementing consent logic\n * before calling `init()` and filtering events as needed.\n *\n * **Key Features**:\n * - **Type Safety**: Callbacks receive correctly typed data based on event name\n * - **Memory Management**: Listeners stored in Map for efficient lookup and cleanup\n * - **Synchronous**: All callbacks execute immediately when event is emitted\n * - **No Error Isolation**: Errors in callbacks propagate to caller (by design)\n *\n * **Use Cases**:\n * - External event consumption via `tracelog.on('event', callback)`\n * - Integration testing via `window.__traceLogBridge.on('event', callback)`\n * - Custom analytics integrations\n * - Real-time event monitoring\n *\n * @example\n * ```typescript\n * const emitter = new Emitter();\n *\n * // Subscribe to events\n * const callback = (event: EventData) => {\n * console.log('Event tracked:', event.type);\n * };\n * emitter.on('event', callback);\n *\n * // Emit event (type-safe)\n * emitter.emit('event', {\n * id: '123',\n * type: EventType.CLICK,\n * page_url: 'https://example.com',\n * timestamp: Date.now(),\n * click_data: { x: 100, y: 200, tag: 'button' }\n * });\n *\n * // Unsubscribe\n * emitter.off('event', callback);\n *\n * // Clear all listeners (destroy/cleanup)\n * emitter.removeAllListeners();\n * ```\n *\n * @see EmitterMap for event type definitions\n * @see src/api.ts for public on/off API\n */\nexport class Emitter {\n private readonly listeners: Map<string, EmitterCallback[]> = new Map();\n\n /**\n * Subscribes to an event channel\n *\n * **Behavior**:\n * - Creates event channel if it doesn't exist\n * - Appends callback to list of listeners for this event\n * - Same callback can be registered multiple times (will fire multiple times)\n *\n * **Type Safety**: Callback receives data type matching the event name\n *\n * @param event - Event name to subscribe to\n * @param callback - Function to call when event is emitted\n *\n * @example\n * ```typescript\n * emitter.on('event', (eventData) => {\n * // eventData is typed as EventData\n * console.log(eventData.type);\n * });\n * ```\n */\n on<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, []);\n }\n\n this.listeners.get(event)!.push(callback);\n }\n\n /**\n * Unsubscribes from an event channel\n *\n * **Behavior**:\n * - Removes first occurrence of callback from listener list\n * - If callback not found, no error is thrown\n * - If callback was registered multiple times, only one instance is removed\n *\n * **Important**: Must use same function reference passed to `on()`\n *\n * @param event - Event name to unsubscribe from\n * @param callback - Function reference to remove (must match `on()` reference)\n *\n * @example\n * ```typescript\n * const callback = (data) => console.log(data);\n * emitter.on('event', callback);\n * emitter.off('event', callback); // Unsubscribes successfully\n *\n * // BAD: Won't work (different function reference)\n * emitter.on('event', (data) => console.log(data));\n * emitter.off('event', (data) => console.log(data)); // No effect\n * ```\n */\n off<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n const callbacks = this.listeners.get(event);\n\n if (callbacks) {\n const index = callbacks.indexOf(callback);\n\n if (index > -1) {\n callbacks.splice(index, 1);\n }\n }\n }\n\n /**\n * Emits an event with data to all subscribed listeners\n *\n * **Behavior**:\n * - Calls all registered callbacks for this event synchronously\n * - Callbacks execute in registration order\n * - If no listeners, no-op (no error thrown)\n * - Errors in callbacks are NOT caught (propagate to caller)\n *\n * **Type Safety**: Data type must match event name's expected type\n *\n * @param event - Event name to emit\n * @param data - Event data (type must match EmitterMap[event])\n *\n * @example\n * ```typescript\n * // Emit event data\n * emitter.emit('event', eventData);\n *\n * // Emit queue data\n * emitter.emit('queue', {\n * user_id: 'user-123',\n * session_id: 'session-456',\n * device: DeviceType.Desktop,\n * events: [event1, event2]\n * });\n * ```\n */\n emit<K extends keyof EmitterMap>(event: K, data: EmitterMap[K]): void {\n const callbacks = this.listeners.get(event);\n\n if (callbacks) {\n callbacks.forEach((callback) => {\n callback(data);\n });\n }\n }\n\n /**\n * Removes all listeners for all events\n *\n * **Purpose**: Cleanup method called during `App.destroy()` to prevent memory leaks\n *\n * **Behavior**:\n * - Clears all event channels\n * - Listeners cannot be restored (new subscriptions required)\n * - Called automatically during library teardown\n *\n * **Use Cases**:\n * - Application teardown\n * - Component unmounting in SPA frameworks\n * - Test cleanup\n *\n * @example\n * ```typescript\n * // During destroy\n * emitter.removeAllListeners();\n * // All subscriptions cleared\n * ```\n */\n removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Error signature utilities.\n *\n * SOURCE: tracelog-api/src/lib/error-classification/error-fingerprint.service.ts\n *\n * The regex set AND `normalizeFilename` below MUST stay byte-identical to the API's\n * `normalizeErrorMessage` / `normalizeFilename` so that client-side throttling and\n * server-side cap/dedup agree on what counts as the \"same\" error. The lib's variant\n * skips the SHA-256 step the API performs after normalization: the throttle map key is\n * the composite string itself, avoiding a `crypto` import and shaving bytes from the\n * browser bundle.\n *\n * If either side changes the regex set, `normalizeFilename`, or the `ErrorSignatureInput`\n * shape, update both AND adjust the unit tests that codify the expected outputs against\n * fixed inputs (`error-signature.utils.test.ts` here, `error-fingerprint.service.spec.ts`\n * in the API).\n */\n\nconst URL_PATTERN = /https?:\\/\\/\\S+/g;\nconst UUID_PATTERN = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;\nconst HEX_ADDR_PATTERN = /0x[0-9a-fA-F]{4,}/g;\nconst LONG_NUMBER_PATTERN = /(?<!\\d)\\d{4,}(?!\\d)/g;\nconst LONG_QUOTED_PATTERN = /(['\"])[^'\"]{20,}\\1/g;\n\nexport interface ErrorSignatureInput {\n message: string;\n filename?: string;\n line?: number | string;\n /**\n * Full page URL where the error fired. Inline-script errors report the page URL as\n * `filename`; when they match, the signature collapses to the URL origin (see\n * `normalizeFilename`). Optional — omit when no page context is available.\n */\n page_url?: string;\n}\n\nexport function normalizeErrorMessage(message: string): string {\n return message\n .replace(URL_PATTERN, '[URL]')\n .replace(UUID_PATTERN, '[ID]')\n .replace(HEX_ADDR_PATTERN, '[ADDR]')\n .replace(LONG_NUMBER_PATTERN, '[N]')\n .replace(LONG_QUOTED_PATTERN, '$1[VAR]$1')\n .toLowerCase()\n .trim();\n}\n\n/** Cut a string at the first `?` or `#`. Shared by filename + page-URL normalization. */\nfunction stripQueryHash(value: string): string {\n const cut = value.search(/[?#]/);\n return cut === -1 ? value : value.slice(0, cut);\n}\n\n/**\n * MIRROR of `ErrorFingerprintService.normalizeFilename` in the API — MUST produce\n * byte-identical output.\n *\n * Browsers report the PAGE URL as `filename` for inline-script errors. When\n * `filename === page_url` (query/hash stripped from both) collapse to the URL `origin`\n * so one theme bug = one signature across pages. Real asset URLs on another path keep\n * their full URL (the path discriminates distinct assets); `data:` / `blob:` and other\n * non-http(s) schemes have no stable identity → `''`; relative / bare names (`bundle.js`)\n * and malformed input fall back to the query/hash-stripped raw string.\n */\nfunction normalizeFilename(filename: string | undefined, pageUrl?: string): string {\n const raw = stripQueryHash((filename ?? '').trim());\n if (!raw) return '';\n let parsed: URL;\n try {\n parsed = new URL(raw);\n } catch {\n return raw;\n }\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return '';\n const page = stripQueryHash((pageUrl ?? '').trim());\n if (page && raw === page) return parsed.origin;\n return raw;\n}\n\nexport function buildErrorSignatureKey(input: ErrorSignatureInput): string {\n const message = normalizeErrorMessage(input.message);\n const filename = normalizeFilename(input.filename, input.page_url);\n const line = input.line == null ? '' : String(input.line);\n return `${message}|${filename}|${line}`;\n}\n","import { State } from '../types';\n\n/**\n * Global in-memory state store shared across all TraceLog components.\n * Single source of truth for runtime application state.\n */\nconst globalState: State = { config: {} } as State;\n\n/**\n * Read-only snapshot of the global state.\n *\n * @internal Exposed for tests and the dev-only TestBridge — production code\n * should use `StateManager.get()` / `getState()` instead.\n */\nexport const getGlobalState = (): Readonly<State> => globalState;\n\n/**\n * Clears every property on the global state.\n *\n * @internal Test isolation only — `tests/setup.ts` invokes this from `afterEach`\n * to guarantee a clean slate between test runs. Calling this in production\n * code breaks the running application.\n */\nexport const resetGlobalState = (): void => {\n Object.keys(globalState).forEach((key) => {\n delete globalState[key as keyof State];\n });\n globalState.config = {};\n};\n\n/**\n * Abstract base class providing centralized, type-safe state management.\n *\n * All managers/handlers extend this class. The global state is shared\n * across every subclass instance.\n *\n * @example\n * ```typescript\n * class MyManager extends StateManager {\n * initialize() {\n * const userId = this.get('userId');\n * this.set('mode', 'qa');\n * }\n * }\n * ```\n */\nexport abstract class StateManager {\n /**\n * Retrieves a value from global state.\n */\n protected get<T extends keyof State>(key: T): State[T] {\n return globalState[key];\n }\n\n /**\n * Sets a value in global state.\n */\n protected set<T extends keyof State>(key: T, value: State[T]): void {\n globalState[key] = value;\n }\n\n /**\n * Returns an immutable snapshot of the entire global state.\n */\n protected getState(): Readonly<State> {\n return { ...globalState };\n }\n}\n","import {\n QUEUE_KEY,\n RATE_LIMIT_KEY,\n EVENT_EXPIRY_HOURS,\n MAX_EVENT_AGE_MS_ON_RECOVERY,\n REQUEST_TIMEOUT_MS,\n PERMANENT_ERROR_LOG_THROTTLE_MS,\n MAX_BEACON_PAYLOAD_SIZE,\n PERSISTENCE_THROTTLE_MS,\n MAX_SEND_RETRIES,\n RETRY_BACKOFF_BASE_MS,\n RETRY_BACKOFF_JITTER_MS,\n LIB_VERSION,\n MAX_CONSECUTIVE_NETWORK_FAILURES,\n MAX_RECOVERY_FAILURES,\n CIRCUIT_BREAKER_COOLDOWN_MS,\n RATE_LIMIT_COOLDOWN_MS,\n MAX_RESPONSE_CODE_LENGTH,\n} from '../constants';\nimport {\n PersistedEventsQueue,\n EventsQueue,\n SpecialApiUrl,\n PermanentError,\n RateLimitError,\n TimeoutError,\n} from '../types';\nimport { log } from '../utils';\nimport { StorageManager } from './storage.manager';\nimport { StateManager } from './state.manager';\n\ninterface SendCallbacks {\n onSuccess?: (eventCount?: number, events?: any[], body?: EventsQueue) => void;\n onFailure?: () => void;\n}\n\n/**\n * Manages sending event queues to the TraceLog SaaS endpoint with persistence,\n * recovery, retry, circuit breaker, and 429 cooldown.\n *\n * **Storage Keys**:\n * - Queue: `tlog:{userId}:queue`\n * - Rate limit cooldown: `tlog:{userId}:rate_limit`\n *\n * **Multi-Tab Protection**: Persisted events include `lastPersistTime`; recovery\n * skips events persisted within 1 second (active tab may retry).\n */\nexport class SenderManager extends StateManager {\n private readonly storeManager: StorageManager;\n private readonly apiUrl: string;\n private lastPermanentErrorLog: { key: string; timestamp: number } | null = null;\n private recoveryInProgress = false;\n private lastMetadataTimestamp = 0;\n private readonly pendingControllers = new Set<AbortController>();\n /**\n * Counts consecutive fetch() rejections where no HTTP response was received\n * (DNS failure, connection refused). Resets on success. When this reaches\n * MAX_CONSECUTIVE_NETWORK_FAILURES the circuit opens and further send attempts\n * are skipped until CIRCUIT_BREAKER_COOLDOWN_MS elapses.\n */\n private consecutiveNetworkFailures = 0;\n private circuitOpenedAt = 0;\n /**\n * Timestamp (epoch ms) before which `send()` must skip fetch() calls due to a\n * prior 429 response. Mirrored to `localStorage` (keyed by userId) so the\n * cooldown survives page navigations on traditional server-rendered sites and\n * is discoverable by other tabs on the same origin.\n */\n private rateLimitedUntil = 0;\n /**\n * Storage key used when the current in-memory cooldown was armed. Captured at\n * arm time so identity changes mid-cooldown can't make persist/clear\n * operations target the wrong key.\n */\n private rateLimitStorageKeyAtArm: string | null = null;\n\n constructor(storeManager: StorageManager, apiUrl: string) {\n super();\n\n this.storeManager = storeManager;\n this.apiUrl = apiUrl;\n this.migrateLegacyV2Keys();\n this.rateLimitedUntil = this.loadRateLimitCooldown();\n }\n\n /**\n * Migrates v2 multi-integration localStorage keys to the v3 single-integration layout.\n *\n * V2 stored per-integration suffixes (`tlog:{userId}:queue:saas`,\n * `tlog:{userId}:queue:custom`, plus matching `:rate_limit:saas` / `:rate_limit:custom`).\n * V3 is SaaS-only, so the `:saas` queue is merged into the new unscoped key\n * `tlog:{userId}:queue` while preserving the original `timestamp` (so the\n * expiry check still applies) and bumping `recoveryFailures` if both keys\n * carry one. The `:custom` queue is intentionally discarded — its events were\n * destined for a different backend that no longer exists in v3; forwarding\n * them to SaaS would be data leakage. The legacy keys are removed in all\n * cases so the migration is one-shot per browser.\n */\n private migrateLegacyV2Keys(): void {\n const userId = this.get('userId') || 'anonymous';\n const legacySaasQueueKey = `${QUEUE_KEY(userId)}:saas`;\n const legacyCustomQueueKey = `${QUEUE_KEY(userId)}:custom`;\n const legacySaasRateLimitKey = `${RATE_LIMIT_KEY(userId)}:saas`;\n const legacyCustomRateLimitKey = `${RATE_LIMIT_KEY(userId)}:custom`;\n\n try {\n const legacyRaw = this.storeManager.getItem(legacySaasQueueKey);\n if (legacyRaw) {\n const targetKey = this.getQueueStorageKey();\n const currentRaw = this.storeManager.getItem(targetKey);\n\n if (!currentRaw) {\n this.storeManager.setItem(targetKey, legacyRaw);\n log('debug', 'Migrated v2 SaaS queue to v3 unscoped key');\n } else {\n this.mergeLegacyIntoCurrent(targetKey, legacyRaw, currentRaw);\n }\n\n this.storeManager.removeItem(legacySaasQueueKey);\n }\n } catch (error) {\n log('debug', 'Failed to migrate v2 SaaS queue, discarding legacy key', { error });\n try {\n this.storeManager.removeItem(legacySaasQueueKey);\n } catch {\n // Already best-effort; nothing more to do.\n }\n }\n\n [legacyCustomQueueKey, legacySaasRateLimitKey, legacyCustomRateLimitKey].forEach((key) => {\n try {\n if (this.storeManager.getItem(key) !== null) {\n this.storeManager.removeItem(key);\n }\n } catch {\n // Best-effort cleanup — leaving an orphan key is harmless.\n }\n });\n }\n\n private mergeLegacyIntoCurrent(targetKey: string, legacyRaw: string, currentRaw: string): void {\n try {\n const legacy = JSON.parse(legacyRaw) as PersistedEventsQueue;\n const current = JSON.parse(currentRaw) as PersistedEventsQueue;\n\n if (!Array.isArray(legacy?.events) || !Array.isArray(current?.events)) {\n log('debug', 'Legacy or current queue malformed, keeping current only');\n return;\n }\n\n const seen = new Set<string>(current.events.map((e) => e.id));\n const mergedEvents = [\n ...current.events,\n ...legacy.events.filter((e) => typeof e.id === 'string' && !seen.has(e.id)),\n ];\n\n const merged: PersistedEventsQueue = {\n ...current,\n events: mergedEvents,\n timestamp:\n typeof current.timestamp === 'number' && typeof legacy.timestamp === 'number'\n ? Math.min(current.timestamp, legacy.timestamp)\n : (current.timestamp ?? legacy.timestamp ?? Date.now()),\n recoveryFailures: Math.max(current.recoveryFailures ?? 0, legacy.recoveryFailures ?? 0) || undefined,\n };\n\n this.storeManager.setItem(targetKey, JSON.stringify(merged));\n log('debug', 'Merged v2 SaaS queue into existing v3 queue', {\n data: { added: mergedEvents.length - current.events.length, total: mergedEvents.length },\n });\n } catch (error) {\n log('debug', 'Failed to merge legacy queue, keeping current', { error });\n }\n }\n\n private getQueueStorageKey(): string {\n const userId = this.get('userId') || 'anonymous';\n return QUEUE_KEY(userId);\n }\n\n private getRateLimitStorageKey(): string {\n const userId = this.get('userId') || 'anonymous';\n return RATE_LIMIT_KEY(userId);\n }\n\n private getActiveRateLimitKey(): string {\n return this.rateLimitStorageKeyAtArm ?? this.getRateLimitStorageKey();\n }\n\n private armRateLimitCooldown(until: number): void {\n this.rateLimitedUntil = until;\n this.rateLimitStorageKeyAtArm = this.getRateLimitStorageKey();\n this.persistRateLimitCooldown(until);\n }\n\n private loadRateLimitCooldown(): number {\n const key = this.getRateLimitStorageKey();\n try {\n const raw = this.storeManager.getItem(key);\n if (!raw) return 0;\n const value = Number(raw);\n if (!Number.isFinite(value) || value <= Date.now()) {\n this.storeManager.removeItem(key);\n return 0;\n }\n this.rateLimitStorageKeyAtArm = key;\n return value;\n } catch {\n return 0;\n }\n }\n\n private persistRateLimitCooldown(until: number): void {\n const key = this.getActiveRateLimitKey();\n try {\n const raw = this.storeManager.getItem(key);\n if (raw) {\n const existing = Number(raw);\n if (Number.isFinite(existing) && existing >= until) {\n return;\n }\n }\n this.storeManager.setItem(key, String(until));\n } catch {\n // Storage full or disabled — cooldown still works in-memory for this instance\n }\n }\n\n private clearRateLimitCooldown(): void {\n const key = this.getActiveRateLimitKey();\n try {\n const raw = this.storeManager.getItem(key);\n if (raw) {\n const stored = Number(raw);\n if (Number.isFinite(stored) && stored > Date.now()) {\n this.rateLimitedUntil = stored;\n return;\n }\n }\n this.storeManager.removeItem(key);\n } catch {\n // Ignore — cleared in-memory is enough\n }\n this.rateLimitedUntil = 0;\n this.rateLimitStorageKeyAtArm = null;\n }\n\n private isRateLimited(): boolean {\n if (this.rateLimitedUntil === 0) {\n this.rateLimitedUntil = this.loadRateLimitCooldown();\n }\n if (this.rateLimitedUntil === 0) return false;\n if (Date.now() >= this.rateLimitedUntil) {\n this.clearRateLimitCooldown();\n if (this.rateLimitedUntil === 0) return false;\n }\n return true;\n }\n\n /**\n * Sends events synchronously using `navigator.sendBeacon()`.\n *\n * Falls back to localStorage persistence on rate-limit cooldown, beacon\n * rejection, or oversized payloads.\n */\n sendEventsQueueSync(body: EventsQueue): boolean {\n if (this.isRateLimited()) {\n log('debug', 'Rate-limit cooldown active, skipping sync send', {\n data: {\n cooldownRemainingMs: this.rateLimitedUntil - Date.now(),\n events: body.events.length,\n },\n });\n const stableBody = this.ensureBatchMetadata(body);\n const existing = this.getPersistedData();\n const existingFailures =\n typeof existing?.recoveryFailures === 'number' && Number.isFinite(existing.recoveryFailures)\n ? existing.recoveryFailures\n : 0;\n this.persistEventsWithFailureCount(stableBody, existingFailures, true);\n return false;\n }\n\n if (this.apiUrl.includes(SpecialApiUrl.Fail)) {\n log('warn', 'Fail mode: simulating network failure (sync)', { data: { events: body.events.length } });\n return false;\n }\n\n if (this.apiUrl.includes(SpecialApiUrl.Localhost)) {\n log('debug', 'Success mode: simulating successful send (sync)', { data: { events: body.events.length } });\n return true;\n }\n\n return this.sendQueueSyncInternal(body);\n }\n\n /**\n * Sends events asynchronously using `fetch()` with retry, circuit breaker, and 429 cooldown.\n * Persists on failure for recovery on next page load.\n */\n async sendEventsQueue(body: EventsQueue, callbacks?: SendCallbacks): Promise<boolean> {\n const stableBody = this.ensureBatchMetadata(body);\n\n try {\n const success = await this.send(stableBody);\n\n if (success) {\n this.clearPersistedEvents();\n callbacks?.onSuccess?.(stableBody.events.length, stableBody.events, stableBody);\n } else {\n this.persistEvents(stableBody);\n callbacks?.onFailure?.();\n }\n\n return success;\n } catch (error) {\n if (error instanceof PermanentError) {\n this.logPermanentError('Permanent error, not retrying', error);\n this.clearPersistedEvents();\n callbacks?.onFailure?.();\n return false;\n }\n\n this.persistEvents(stableBody);\n callbacks?.onFailure?.();\n return false;\n }\n }\n\n /**\n * Recovers and attempts to resend events persisted from a previous session.\n *\n * Idempotent: safe to call multiple times (recovery flag prevents concurrent attempts).\n */\n async recoverPersistedEvents(callbacks?: SendCallbacks): Promise<void> {\n if (this.recoveryInProgress) {\n log('debug', 'Recovery already in progress, skipping duplicate attempt');\n return;\n }\n\n this.recoveryInProgress = true;\n\n let recoveryBody: EventsQueue | null = null;\n let recoveryFailures = 0;\n\n try {\n const persistedData = this.getPersistedData();\n\n if (!persistedData || !this.isDataRecent(persistedData) || persistedData.events.length === 0) {\n this.clearPersistedEvents();\n return;\n }\n\n const rawFailures = persistedData.recoveryFailures;\n recoveryFailures =\n typeof rawFailures === 'number' && Number.isFinite(rawFailures) && rawFailures >= 0 ? rawFailures : 0;\n if (recoveryFailures >= MAX_RECOVERY_FAILURES) {\n log('debug', `Discarding persisted events after ${recoveryFailures} failed recovery attempts`);\n this.clearPersistedEvents();\n callbacks?.onFailure?.();\n return;\n }\n\n if (this.isRateLimited()) {\n log('debug', 'Rate-limit cooldown active, deferring recovery', {\n data: { cooldownRemainingMs: this.rateLimitedUntil - Date.now() },\n });\n callbacks?.onFailure?.();\n return;\n }\n\n recoveryBody = this.ensureBatchMetadata(this.createRecoveryBody(persistedData));\n\n if (recoveryBody.events.length === 0) {\n log('debug', 'All persisted events exceeded the recovery age cutoff; discarding batch');\n this.clearPersistedEvents();\n return;\n }\n\n const success = await this.send(recoveryBody);\n\n if (success) {\n this.clearPersistedEvents();\n callbacks?.onSuccess?.(persistedData.events.length, persistedData.events, recoveryBody);\n } else {\n this.persistEventsWithFailureCount(recoveryBody, recoveryFailures + 1, true);\n callbacks?.onFailure?.();\n }\n } catch (error) {\n if (error instanceof PermanentError) {\n this.logPermanentError('Permanent error during recovery, clearing persisted events', error);\n this.clearPersistedEvents();\n callbacks?.onFailure?.();\n return;\n }\n\n log('error', 'Failed to recover persisted events', { error });\n if (recoveryBody) {\n this.persistEventsWithFailureCount(recoveryBody, recoveryFailures + 1, true);\n }\n callbacks?.onFailure?.();\n } finally {\n this.recoveryInProgress = false;\n }\n }\n\n /**\n * Cleanup method called during `App.destroy()`. No-op — persisted events\n * intentionally kept in localStorage for recovery.\n */\n stop(): void {}\n\n private async backoffDelay(attempt: number): Promise<void> {\n const exponentialDelay = RETRY_BACKOFF_BASE_MS * Math.pow(2, attempt);\n const jitter = Math.random() * RETRY_BACKOFF_JITTER_MS;\n return new Promise((resolve) => setTimeout(resolve, exponentialDelay + jitter));\n }\n\n private async send(body: EventsQueue): Promise<boolean> {\n const requestBody = this.ensureBatchMetadata(body, body._metadata?.idempotency_token);\n\n if (this.apiUrl.includes(SpecialApiUrl.Fail)) {\n log('debug', 'Fail mode: simulating network failure', { data: { events: requestBody.events.length } });\n return false;\n }\n\n if (this.apiUrl.includes(SpecialApiUrl.Localhost)) {\n log('debug', 'Success mode: simulating successful send', { data: { events: requestBody.events.length } });\n return true;\n }\n\n if (this.isRateLimited()) {\n log('debug', 'Rate-limit cooldown active, skipping send', {\n data: {\n cooldownRemainingMs: this.rateLimitedUntil - Date.now(),\n events: requestBody.events.length,\n },\n });\n return false;\n }\n\n if (this.consecutiveNetworkFailures >= MAX_CONSECUTIVE_NETWORK_FAILURES) {\n const elapsed = Date.now() - this.circuitOpenedAt;\n if (elapsed < CIRCUIT_BREAKER_COOLDOWN_MS) {\n log('debug', 'Network circuit open, skipping send', {\n data: {\n consecutiveNetworkFailures: this.consecutiveNetworkFailures,\n cooldownRemainingMs: CIRCUIT_BREAKER_COOLDOWN_MS - elapsed,\n },\n });\n return false;\n }\n // Half-open: allow one probe batch through.\n }\n\n const { url, payload } = this.prepareRequest(requestBody);\n let allTimeouts = true;\n let hadHttpResponse = false;\n\n for (let attempt = 1; attempt <= MAX_SEND_RETRIES + 1; attempt++) {\n try {\n const response = await this.sendWithTimeout(url, payload);\n\n if (response.ok) {\n if (attempt > 1) {\n log('info', `Send succeeded after ${attempt - 1} retry attempt(s)`, {\n data: { events: requestBody.events.length, attempt },\n });\n }\n\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n return true;\n }\n\n return false;\n } catch (error) {\n const isLastAttempt = attempt === MAX_SEND_RETRIES + 1;\n\n if (error instanceof PermanentError) {\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n throw error;\n }\n\n if (error instanceof RateLimitError) {\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n allTimeouts = false;\n hadHttpResponse = true;\n this.armRateLimitCooldown(Date.now() + RATE_LIMIT_COOLDOWN_MS);\n log('warn', 'Rate limited, skipping retries', {\n data: { events: body.events.length, attempt, cooldownMs: RATE_LIMIT_COOLDOWN_MS },\n });\n break;\n }\n\n if (!(error instanceof TimeoutError)) {\n allTimeouts = false;\n }\n\n if (!(error instanceof TypeError)) {\n hadHttpResponse = true;\n }\n\n log(\n isLastAttempt ? 'error' : 'warn',\n `Send attempt ${attempt} failed${isLastAttempt ? ' (all retries exhausted)' : ', will retry'}`,\n {\n error,\n data: {\n events: body.events.length,\n url: url.replace(/\\/\\/[^/]+/, '//[DOMAIN]'),\n attempt,\n maxAttempts: MAX_SEND_RETRIES + 1,\n },\n },\n );\n\n if (!isLastAttempt) {\n await this.backoffDelay(attempt);\n continue;\n }\n\n if (allTimeouts) {\n log('debug', 'All retry attempts timed out, preserving batch for retry', {\n data: { events: requestBody.events.length },\n });\n return false;\n }\n\n if (!hadHttpResponse) {\n this.consecutiveNetworkFailures = Math.min(\n this.consecutiveNetworkFailures + 1,\n MAX_CONSECUTIVE_NETWORK_FAILURES,\n );\n\n if (this.consecutiveNetworkFailures >= MAX_CONSECUTIVE_NETWORK_FAILURES) {\n this.circuitOpenedAt = Date.now();\n }\n } else {\n this.consecutiveNetworkFailures = 0;\n this.circuitOpenedAt = 0;\n }\n\n return false;\n }\n }\n\n return false;\n }\n\n private async sendWithTimeout(url: string, payload: string): Promise<Response> {\n const controller = new AbortController();\n this.pendingControllers.add(controller);\n let didTimeout = false;\n\n const timeoutId = setTimeout(() => {\n didTimeout = true;\n controller.abort();\n }, REQUEST_TIMEOUT_MS);\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n body: payload,\n keepalive: true,\n credentials: 'include',\n signal: controller.signal,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n if (!response.ok) {\n const isPermanentError =\n response.status >= 400 && response.status < 500 && response.status !== 408 && response.status !== 429;\n\n if (isPermanentError) {\n const responseCode = await this.readTraceLogErrorCode(response);\n const message = responseCode\n ? `HTTP ${response.status}: ${response.statusText} (${responseCode})`\n : `HTTP ${response.status}: ${response.statusText}`;\n throw new PermanentError(message, response.status, responseCode);\n }\n\n if (response.status === 429) {\n throw new RateLimitError(`HTTP 429: ${response.statusText}`);\n }\n\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n return response;\n } catch (error) {\n if (error instanceof PermanentError) {\n throw error;\n }\n if (didTimeout) {\n throw new TimeoutError('Request timed out');\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n this.pendingControllers.delete(controller);\n }\n }\n\n private async readTraceLogErrorCode(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.clone().json()) as { code?: unknown };\n if (typeof body.code === 'string' && body.code.length > 0 && body.code.length <= MAX_RESPONSE_CODE_LENGTH) {\n return body.code;\n }\n } catch {\n // Best-effort only. Status still determines permanence.\n }\n return undefined;\n }\n\n private sendQueueSyncInternal(body: EventsQueue): boolean {\n const stableBody = this.ensureBatchMetadata(body);\n const requestBody = this.ensureBatchMetadata(stableBody, stableBody._metadata?.idempotency_token);\n const { url, payload } = this.prepareRequest(requestBody);\n\n if (payload.length > MAX_BEACON_PAYLOAD_SIZE) {\n log('warn', 'Payload exceeds sendBeacon limit, persisting for recovery', {\n data: { size: payload.length, limit: MAX_BEACON_PAYLOAD_SIZE, events: requestBody.events.length },\n });\n this.persistEvents(stableBody);\n return false;\n }\n\n const blob = new Blob([payload], { type: 'application/json' });\n\n if (!this.isSendBeaconAvailable()) {\n log('warn', 'sendBeacon not available, persisting events for recovery');\n this.persistEvents(stableBody);\n return false;\n }\n\n const accepted = navigator.sendBeacon(url, blob);\n\n if (!accepted) {\n log('warn', 'sendBeacon rejected request, persisting events for recovery');\n this.persistEvents(stableBody);\n }\n\n return accepted;\n }\n\n private prepareRequest(body: EventsQueue): { url: string; payload: string } {\n let timestamp = Date.now();\n\n if (timestamp < this.lastMetadataTimestamp) {\n timestamp = this.lastMetadataTimestamp;\n }\n this.lastMetadataTimestamp = timestamp;\n\n const enrichedBody = {\n ...body,\n _metadata: {\n ...body._metadata,\n idempotency_token: body._metadata?.idempotency_token ?? this.computeContentToken(body),\n referer: typeof window !== 'undefined' ? window.location.href : undefined,\n timestamp,\n client_version: LIB_VERSION,\n },\n };\n\n return {\n url: this.apiUrl,\n payload: JSON.stringify(enrichedBody),\n };\n }\n\n private ensureBatchMetadata(body: EventsQueue, preferredToken?: string): EventsQueue {\n const idempotencyToken = body._metadata?.idempotency_token ?? preferredToken ?? this.computeContentToken(body);\n\n if (body._metadata?.idempotency_token === idempotencyToken) {\n return body;\n }\n\n return {\n ...body,\n _metadata: {\n ...body._metadata,\n idempotency_token: idempotencyToken,\n },\n };\n }\n\n /**\n * Deterministic 32-bit FNV-1a hash of sorted event IDs, salted with\n * `user_id` and `session_id`. Produces the same idempotency token for the\n * same set of events across retries.\n */\n private computeContentToken(body: EventsQueue): string {\n const ids = body.events\n .map((e) => e.id)\n .sort()\n .join(',');\n const input = `${body.user_id}|${body.session_id}|${ids}`;\n\n let hash = 2166136261;\n for (let i = 0; i < input.length; i++) {\n hash ^= input.charCodeAt(i);\n hash = Math.imul(hash, 16777619) >>> 0;\n }\n\n return hash.toString(16).padStart(8, '0');\n }\n\n private getPersistedData(): PersistedEventsQueue | null {\n try {\n const storageKey = this.getQueueStorageKey();\n const persistedDataString = this.storeManager.getItem(storageKey);\n\n if (persistedDataString) {\n return JSON.parse(persistedDataString);\n }\n } catch (error) {\n log('debug', 'Failed to parse persisted data', { error });\n this.clearPersistedEvents();\n }\n\n return null;\n }\n\n private isDataRecent(data: PersistedEventsQueue): boolean {\n if (!data.timestamp || typeof data.timestamp !== 'number') {\n return false;\n }\n\n const ageInHours = (Date.now() - data.timestamp) / (1000 * 60 * 60);\n return ageInHours < EVENT_EXPIRY_HOURS;\n }\n\n private createRecoveryBody(data: PersistedEventsQueue): EventsQueue {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { timestamp, recoveryFailures, ...queue } = data;\n const originalEvents = queue.events ?? [];\n const cutoff = Date.now() - MAX_EVENT_AGE_MS_ON_RECOVERY;\n const filteredEvents = originalEvents.filter((event) => {\n const eventTimestamp =\n typeof event.timestamp === 'number'\n ? event.timestamp\n : new Date(event.timestamp as unknown as string).getTime();\n return Number.isFinite(eventTimestamp) && eventTimestamp >= cutoff;\n });\n\n if (filteredEvents.length < originalEvents.length) {\n log('debug', 'Recovery dropped stale events', {\n data: {\n dropped: originalEvents.length - filteredEvents.length,\n kept: filteredEvents.length,\n },\n });\n }\n\n return { ...queue, events: filteredEvents };\n }\n\n private persistEvents(body: EventsQueue): boolean {\n const existing = this.getPersistedData();\n const existingFailures =\n typeof existing?.recoveryFailures === 'number' && Number.isFinite(existing.recoveryFailures)\n ? existing.recoveryFailures\n : 0;\n return this.persistEventsWithFailureCount(body, existingFailures);\n }\n\n private persistEventsWithFailureCount(body: EventsQueue, recoveryFailures: number, skipThrottle = false): boolean {\n try {\n const existing = this.getPersistedData();\n\n if (!skipThrottle && existing && existing.timestamp) {\n const timeSinceExisting = Date.now() - existing.timestamp;\n\n if (timeSinceExisting < PERSISTENCE_THROTTLE_MS) {\n log('debug', 'Skipping persistence, another tab recently persisted events', {\n data: { timeSinceExisting },\n });\n return true;\n }\n }\n\n const persistedData: PersistedEventsQueue = {\n ...body,\n timestamp: Date.now(),\n ...(recoveryFailures > 0 && { recoveryFailures }),\n };\n\n const storageKey = this.getQueueStorageKey();\n this.storeManager.setItem(storageKey, JSON.stringify(persistedData));\n\n return !!this.storeManager.getItem(storageKey);\n } catch (error) {\n log('debug', 'Failed to persist events', { error });\n return false;\n }\n }\n\n private clearPersistedEvents(): void {\n try {\n const key = this.getQueueStorageKey();\n this.storeManager.removeItem(key);\n } catch (error) {\n log('debug', 'Failed to clear persisted events', { error });\n }\n }\n\n private isSendBeaconAvailable(): boolean {\n return typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function';\n }\n\n private logPermanentError(context: string, error: PermanentError): void {\n const now = Date.now();\n const key = `${error.statusCode ?? 'unknown'}:${error.responseCode ?? ''}`;\n const shouldLog =\n !this.lastPermanentErrorLog ||\n this.lastPermanentErrorLog.key !== key ||\n now - this.lastPermanentErrorLog.timestamp >= PERMANENT_ERROR_LOG_THROTTLE_MS;\n\n if (shouldLog) {\n log('error', context, {\n data: { status: error.statusCode, code: error.responseCode, message: error.message },\n });\n\n this.lastPermanentErrorLog = { key, timestamp: now };\n }\n }\n}\n","import { log } from '../utils';\nimport { StateManager } from './state.manager';\n\n/**\n * Manages accurate timestamp generation using monotonic clock (`performance.now()`)\n * to prevent issues from system clock changes during the session.\n *\n * - Boot reference: `performance.now()` + `Date.now()` captured at construction.\n * - `now()` returns `bootTimestamp + (performance.now() - bootTime)` (immune to clock changes during session).\n * - Falls back to `Date.now()` when `performance.now()` is unavailable (SSR / old browsers).\n */\nexport class TimeManager extends StateManager {\n private readonly bootTime: number;\n private readonly bootTimestamp: number;\n private readonly hasPerformanceNow: boolean;\n\n constructor() {\n super();\n\n if (typeof window === 'undefined') {\n this.hasPerformanceNow = false;\n this.bootTime = 0;\n this.bootTimestamp = 0;\n return;\n }\n\n this.hasPerformanceNow = typeof performance !== 'undefined' && typeof performance.now === 'function';\n\n if (this.hasPerformanceNow) {\n this.bootTime = performance.now();\n this.bootTimestamp = Date.now();\n } else {\n this.bootTime = 0;\n this.bootTimestamp = Date.now();\n log('debug', 'performance.now() not available, falling back to Date.now()');\n }\n }\n\n /**\n * Returns current timestamp in milliseconds since epoch, immune to clock\n * changes during the session.\n */\n now(): number {\n if (!this.hasPerformanceNow) {\n return Date.now();\n }\n\n const elapsed = performance.now() - this.bootTime;\n return Math.round(this.bootTimestamp + elapsed);\n }\n\n /**\n * Validates a timestamp is not more than 2 minutes in the future relative\n * to the monotonic clock. Backend allows 3 minutes — keep client tighter\n * so obvious clock-skew events are flagged before they hit the wire.\n */\n validateTimestamp(timestamp: number): { valid: boolean; error?: string } {\n const maxFutureOffset = 2 * 60 * 1000;\n const offset = timestamp - this.now();\n\n if (offset > maxFutureOffset) {\n return {\n valid: false,\n error: `Timestamp is ${(offset / 1000 / 60).toFixed(2)} minutes in the future (max allowed: 2 minutes)`,\n };\n }\n\n return { valid: true };\n }\n}\n","import {\n EVENT_SENT_INTERVAL_MS,\n MAX_EVENTS_QUEUE_LENGTH,\n DUPLICATE_EVENT_THRESHOLD_MS,\n RATE_LIMIT_WINDOW_MS,\n MAX_EVENTS_PER_SECOND,\n MAX_PENDING_EVENTS_BUFFER,\n BATCH_SIZE_THRESHOLD,\n MAX_SAME_EVENT_PER_MINUTE,\n PER_EVENT_RATE_LIMIT_WINDOW_MS,\n MAX_EVENTS_PER_SESSION,\n MAX_CLICKS_PER_SESSION,\n MAX_PAGE_VIEWS_PER_SESSION,\n MAX_CUSTOM_EVENTS_PER_SESSION,\n MAX_SCROLL_EVENTS_PER_SESSION,\n MAX_FINGERPRINTS,\n FINGERPRINT_CLEANUP_MULTIPLIER,\n MAX_FINGERPRINTS_HARD_LIMIT,\n MAX_SEND_INTERVAL_MS,\n MAX_CONSECUTIVE_SEND_FAILURES,\n} from '../constants/config.constants';\nimport {\n SESSION_COUNTS_KEY,\n SESSION_COUNTS_EXPIRY_MS,\n SESSION_COUNTS_LAST_CLEANUP_KEY,\n SESSION_COUNTS_CLEANUP_THROTTLE_MS,\n STORAGE_BASE_KEY,\n} from '../constants/storage.constants';\nimport { EventsQueue, EmitterEvent, EventData, EventType, Mode, SessionEventCounts, QueuedEvent } from '../types';\nimport { log, Emitter, generateEventId } from '../utils';\nimport { SenderManager } from './sender.manager';\nimport { StateManager } from './state.manager';\nimport { StorageManager } from './storage.manager';\nimport { TimeManager } from './time.manager';\n\nconst VALID_EVENT_TYPES: ReadonlySet<string> = new Set(Object.values(EventType));\n\n/**\n * Extended session counts structure with metadata for expiry tracking.\n *\n * Adds timestamp and version fields to SessionEventCounts for cleanup management.\n */\ninterface StoredSessionCounts extends SessionEventCounts {\n /** Timestamp when counts were last saved (for 7-day expiry) */\n _timestamp: number;\n /** Schema version for future migrations */\n _version: number;\n}\n\n/**\n * Core component responsible for event tracking, queue management, deduplication,\n * rate limiting, and multi-integration API communication coordination.\n *\n * **Purpose**: Central hub for all analytics events in TraceLog, managing the complete\n * event lifecycle from capture to transmission.\n *\n * **Core Functionality**:\n * - **Event Tracking**: Captures all user interactions (clicks, scrolls, page views, custom events, web vitals, errors)\n * - **Queue Management**: Batches events with 10-second base intervals (exponential backoff on failure) to optimize network requests\n * - **Deduplication**: LRU cache with 1000-entry fingerprint storage prevents duplicate events\n * - **Rate Limiting**: Client-side limits (50 events/second global, 60/minute per event name)\n * - **Per-Session Caps**: Configurable limits prevent runaway generation (1000 total, type-specific limits)\n * - **Dynamic Queue Flush**: Immediate send when 50-event batch threshold reached\n * - **Pending Events Buffer**: Buffers up to 100 events before session initialization\n * - **Event Recovery**: Recovers persisted events from localStorage after crashes (independent per integration)\n * - **Multi-Integration**: Manages 0-2 SenderManager instances (SaaS + Custom) with parallel async sending\n * - **Standalone Mode**: Emits queue events without network requests when no integrations configured\n *\n * **Key Features**:\n * - **LRU Deduplication**: 1000 fingerprints, 10px coordinate precision for clicks, 500ms time threshold\n * - **Smart Queue Overflow**: Fixed 100-event limit with priority preservation for SESSION_START/END\n * - **Rate Limiting**: 50 events/sec sliding window, critical events exempted\n * - **Per-Event-Name Limits**: 60 same event name per minute (configurable via `maxSameEventPerMinute`)\n * - **Per-Session Caps**: Total 1000/session, Clicks 500, Page views 100, Custom 500, Viewport 200, Scroll 120\n * - **Dynamic Flush**: Immediate send when 50-event batch threshold reached\n * - **Multi-Integration Sending**: Parallel async with `Promise.allSettled()`, independent error handling per integration\n * - **QA Mode**: Console logging for custom events without backend transmission\n *\n * **State Management**:\n * - **`hasStartSession` Flag**: Prevents duplicate SESSION_START events across init cycles\n * - Set to `true` when SESSION_START is tracked via `track()` method\n * - Reset to `false` in `stop()` method to allow subsequent init() → destroy() → init() cycles\n * - NOT set by SessionManager's BroadcastChannel message handler (secondary tabs don't track SESSION_START)\n *\n * **Transformer Support**:\n * - **`beforeSend`**: Applied conditionally based on integration mode\n * - Custom-only mode: Applied in EventManager before dedup/sampling/queueing\n * - Multi-integration mode: Skipped in EventManager, applied in SenderManager per-integration\n * - **`beforeBatch`**: Applied in SenderManager before network transmission\n *\n * @see src/managers/README.md for detailed documentation\n *\n * @example\n * ```typescript\n * const eventManager = new EventManager(storage, emitter, transformers);\n *\n * // Track event\n * eventManager.track({\n * type: 'click',\n * click_data: { x: 100, y: 200, tag: 'button' }\n * });\n *\n * // Flush immediately (async)\n * await eventManager.flushImmediately();\n *\n * // Synchronous flush (page unload)\n * eventManager.flushImmediatelySync();\n *\n * // Stop and cleanup\n * eventManager.stop();\n * ```\n */\nexport class EventManager extends StateManager {\n private readonly dataSenders: SenderManager[];\n private readonly emitter: Emitter | null;\n private readonly timeManager: TimeManager;\n private readonly recentEventFingerprints = new Map<string, number>();\n private readonly perEventRateLimits: Map<string, number[]> = new Map();\n\n private eventsQueue: QueuedEvent[] = [];\n private pendingEventsBuffer: Partial<EventData>[] = [];\n private sendTimeoutId: number | null = null;\n private sendInProgress = false;\n private consecutiveSendFailures = 0;\n private rateLimitCounter = 0;\n private rateLimitWindowStart = 0;\n private lastSessionId: string | null = null;\n // Set when a sync flush is requested mid-async-send; drained by the async\n // finally block. See `drainPendingSyncFlush` for the full rationale.\n private pendingSyncFlush = false;\n\n private sessionEventCounts: SessionEventCounts = {\n total: 0,\n [EventType.CLICK]: 0,\n [EventType.PAGE_VIEW]: 0,\n [EventType.CUSTOM]: 0,\n [EventType.SCROLL]: 0,\n };\n\n private readonly saveSessionCountsDebounced: ((sessionId: string) => void) | null = null;\n\n /**\n * Creates an EventManager instance.\n *\n * @param storeManager - Storage manager for persistence\n * @param emitter - Optional event emitter for local event consumption\n */\n constructor(storeManager: StorageManager, emitter: Emitter | null = null) {\n super();\n\n this.emitter = emitter;\n this.timeManager = new TimeManager();\n\n this.dataSenders = [];\n const collectApiUrls = this.get('collectApiUrls');\n\n if (collectApiUrls?.saas) {\n this.dataSenders.push(new SenderManager(storeManager, collectApiUrls.saas));\n }\n\n // Initialize debounced session counts saver (500ms delay, trailing edge)\n // Reduces localStorage writes from ~1000 per session to ~20-30 (96-97% reduction)\n this.saveSessionCountsDebounced = this.debounce((sessionId: string) => {\n this.saveSessionCounts(sessionId);\n }, 500);\n\n // Cleanup expired session counts on init (7-day TTL)\n this.cleanupExpiredSessionCounts();\n }\n\n /**\n * Recovers persisted events from localStorage after a crash or page reload.\n *\n * **Purpose**: Ensures zero data loss by recovering events that failed to send\n * in the previous session due to network errors or crashes.\n *\n * **Flow**:\n * 1. Calls `recoverPersistedEvents()` on all SenderManager instances in parallel\n * 2. Each SenderManager attempts to resend its persisted events to backend\n * 3. On success: Removes recovered events from consent/pending buffers\n * 4. On failure: Logs warning (events remain in localStorage for next attempt)\n *\n * **Multi-Integration**:\n * - Independent recovery per integration (SaaS + Custom backends)\n * - Parallel recovery via `Promise.allSettled()` (one failure doesn't block others)\n * - No cross-contamination (SaaS events don't go to Custom API)\n *\n * **Called by**: `App.init()` after initialization\n *\n * **Important**: Events are NOT removed from pending/consent buffers until\n * successful network transmission.\n *\n * @see src/managers/README.md (lines 5-75) for recovery details\n */\n async recoverPersistedEvents(): Promise<void> {\n const recoveryPromises = this.dataSenders.map(async (sender) =>\n sender.recoverPersistedEvents({\n onSuccess: (_eventCount, recoveredEvents, body) => {\n if (recoveredEvents && recoveredEvents.length > 0) {\n const eventIds = recoveredEvents.map((e) => e.id);\n this.removeProcessedEvents(eventIds);\n\n if (body) {\n this.emitEventsQueue(body);\n }\n }\n },\n onFailure: () => {\n log('debug', 'Failed to recover persisted events');\n },\n }),\n );\n\n await Promise.allSettled(recoveryPromises);\n }\n\n /**\n * Tracks a user interaction event and adds it to the event queue.\n *\n * **Purpose**: Central tracking method for all analytics events (clicks, page views,\n * custom events, web vitals, errors, scroll, viewport visibility, session start/end).\n *\n * **Validation & Buffering**:\n * - Validates `type` is provided (required)\n * - If session not initialized: Buffers in `pendingEventsBuffer` (max 100 events, FIFO)\n *\n * **Rate Limiting** (non-critical events only):\n * - Global: 50 events/second sliding window (critical events exempted)\n * - Per-event-name: 60/minute for custom events (configurable via `maxSameEventPerMinute`)\n * - Per-session total: 1000 events max\n * - Per-session by type: Clicks 500, Page views 100, Custom 500, Viewport 200, Scroll 120\n *\n * **Deduplication**:\n * - LRU cache with 1000 fingerprints (10px coordinate precision for clicks, 500ms time threshold)\n * - Prevents duplicate events within 500ms window\n * - SESSION_START protected by `hasStartSession` flag\n *\n * **Sampling**:\n * - Applied after validation and rate limiting\n * - Critical events (SESSION_START/END) always included\n * - Configurable via `samplingRate` (0-1)\n *\n * **Transformation**:\n * - `beforeSend` applied (if custom-only mode) before dedup/sampling/queueing\n * - Returning `null` from `beforeSend` filters out the event\n *\n * **Queue Management**:\n * - Events added to `eventsQueue` (max 100 events, FIFO with priority for session events)\n * - Dynamic flush: Immediate send when 50-event batch threshold reached\n * - Periodic flush: Every 10 seconds\n *\n * **Multi-Integration**:\n * - Backend integrations: Handled by SenderManager instances\n *\n * **QA Mode**:\n * - Custom events logged to console with styling\n * - Events NOT sent to backend (emitted locally only)\n *\n * @param eventData - Event data to track\n *\n * @example\n * ```typescript\n * eventManager.track({\n * type: EventType.CLICK,\n * click_data: { x: 0.5, y: 0.3, tag: 'button', text: 'Submit' }\n * });\n *\n * eventManager.track({\n * type: EventType.CUSTOM,\n * custom_event: { name: 'checkout_completed', metadata: { total: 99.99 } }\n * });\n * ```\n *\n * @see src/managers/README.md (lines 5-75) for detailed tracking logic\n */\n track({\n type,\n page_url,\n from_page_url,\n scroll_data,\n click_data,\n custom_event,\n web_vitals,\n error_data,\n page_view,\n }: Partial<EventData>): void {\n if (!type) {\n log('error', 'Event type is required - event will be ignored');\n return;\n }\n\n if (!VALID_EVENT_TYPES.has(type)) {\n log('error', 'Invalid event type - event will be ignored', {\n data: { type },\n });\n return;\n }\n\n const currentSessionId = this.get('sessionId');\n\n if (!currentSessionId) {\n if (this.pendingEventsBuffer.length >= MAX_PENDING_EVENTS_BUFFER) {\n this.pendingEventsBuffer.shift();\n log('debug', 'Pending events buffer full - dropping oldest event', {\n data: { maxBufferSize: MAX_PENDING_EVENTS_BUFFER },\n });\n }\n\n this.pendingEventsBuffer.push({\n type,\n page_url,\n from_page_url,\n scroll_data,\n click_data,\n custom_event,\n web_vitals,\n error_data,\n page_view,\n });\n\n return;\n }\n\n if (this.lastSessionId !== currentSessionId) {\n this.lastSessionId = currentSessionId;\n // Load persisted counts from localStorage instead of resetting to zero\n // This prevents count reset on page reload and allows proper enforcement of per-session limits\n this.sessionEventCounts = this.loadSessionCounts(currentSessionId);\n }\n\n const isCriticalEvent = type === EventType.SESSION_START;\n\n if (isCriticalEvent) {\n log('debug', 'Processing SESSION_START event', {\n data: { sessionId: currentSessionId },\n });\n }\n\n if (!isCriticalEvent && !this.checkRateLimit()) {\n return;\n }\n\n const eventType = type as EventType;\n\n if (!isCriticalEvent) {\n if (this.sessionEventCounts.total >= MAX_EVENTS_PER_SESSION) {\n log('warn', 'Session event limit reached', {\n data: {\n type: eventType,\n total: this.sessionEventCounts.total,\n limit: MAX_EVENTS_PER_SESSION,\n },\n });\n\n return;\n }\n\n const typeLimit = this.getTypeLimitForEvent(eventType);\n\n if (typeLimit) {\n const currentCount = this.sessionEventCounts[eventType];\n\n if (currentCount !== undefined && currentCount >= typeLimit) {\n log('warn', 'Session event type limit reached', {\n data: {\n type: eventType,\n count: currentCount,\n limit: typeLimit,\n },\n });\n\n return;\n }\n }\n }\n\n if (eventType === EventType.CUSTOM && custom_event?.name) {\n const maxSameEventPerMinute = this.get('config')?.maxSameEventPerMinute ?? MAX_SAME_EVENT_PER_MINUTE;\n\n if (!this.checkPerEventRateLimit(custom_event.name, maxSameEventPerMinute)) {\n return;\n }\n }\n const isSessionStart = eventType === EventType.SESSION_START;\n\n const currentPageUrl = (page_url as string) || this.get('pageUrl');\n const payload = this.buildEventPayload({\n type: eventType,\n page_url: currentPageUrl,\n from_page_url,\n scroll_data,\n click_data,\n custom_event,\n web_vitals,\n error_data,\n page_view,\n });\n\n // Handle event filtered by beforeSend transformer\n if (!payload) {\n return;\n }\n\n if (!isCriticalEvent && !this.shouldSample()) {\n return;\n }\n\n if (isSessionStart) {\n const currentSessionId = this.get('sessionId');\n\n if (!currentSessionId) {\n log('error', 'Session start event requires sessionId - event will be ignored');\n return;\n }\n\n if (this.get('hasStartSession')) {\n log('debug', 'Duplicate session_start detected', {\n data: { sessionId: currentSessionId },\n });\n\n return;\n }\n\n this.set('hasStartSession', true);\n }\n\n if (this.isDuplicateEvent(payload)) {\n return;\n }\n\n if (this.get('mode') === Mode.QA && eventType === EventType.CUSTOM && custom_event) {\n log('info', `Custom Event: ${custom_event.name}`, {\n visibility: 'qa',\n data: {\n name: custom_event.name,\n ...(custom_event.metadata && { metadata: custom_event.metadata }),\n },\n });\n\n this.emitEvent(payload);\n\n return;\n }\n\n this.addToQueue(payload);\n\n if (!isCriticalEvent) {\n this.sessionEventCounts.total++;\n\n if (this.sessionEventCounts[eventType] !== undefined) {\n this.sessionEventCounts[eventType]++;\n }\n\n // Persist updated counts to localStorage (debounced for performance)\n // Debouncing reduces localStorage writes from ~1000 to ~20-30 per session (96-97% reduction)\n // Trailing edge ensures no data loss - final counts always saved after 500ms silence\n const currentSessionId = this.get('sessionId');\n if (currentSessionId && this.saveSessionCountsDebounced) {\n this.saveSessionCountsDebounced(currentSessionId);\n }\n }\n }\n\n /**\n * Stops event tracking and clears all queues and buffers.\n *\n * **Purpose**: Cleanup method called during `App.destroy()` to reset EventManager state\n * and allow subsequent init() → destroy() → init() cycles.\n *\n * **Cleanup Actions**:\n * 1. **Clear send timeout**: Cancels pending queue flush timeout and resets backoff state\n * 2. **Clear all queues and buffers**:\n * - `eventsQueue`: Discarded (not sent)\n * - `pendingEventsBuffer`: Discarded (events before session init)\n * 3. **Reset rate limiting state**: Clears rate limit counters and per-event limits\n * 4. **Reset session counters**: Clears per-session event counts\n * 5. **Reset `hasStartSession` flag**: Allows SESSION_START in next init cycle\n * 6. **Stop SenderManagers**: Calls `stop()` on all SenderManager instances\n *\n * **Important Behavior**:\n * - **No final flush**: `stop()` itself does NOT send queued events\n * - `App.destroy()` calls `flushImmediatelySync()` before `stop()` automatically\n *\n * **Multi-Integration**:\n * - Stops all SenderManager instances (SaaS + Custom)\n *\n * **Called by**: `App.destroy()` during application teardown\n *\n * @example\n * ```typescript\n * // Proper cleanup with final flush\n * eventManager.flushImmediatelySync(); // Send pending events\n * eventManager.stop(); // Stop and clear\n * ```\n *\n * @see src/managers/README.md (lines 5-75) for cleanup details\n */\n stop(): void {\n // Note: customHeadersProvider is NOT cleared here since it's stored in SenderManagers\n // and those are destroyed during this stop() call anyway\n this.clearSendTimeout();\n this.sendInProgress = false;\n this.pendingSyncFlush = false;\n this.consecutiveSendFailures = 0;\n\n // Save session counts immediately before cleanup (bypass debounce)\n // This ensures final counts are persisted before destroying the EventManager\n const currentSessionId = this.get('sessionId');\n if (currentSessionId) {\n this.saveSessionCounts(currentSessionId);\n }\n\n this.eventsQueue = [];\n this.pendingEventsBuffer = [];\n this.recentEventFingerprints.clear();\n this.rateLimitCounter = 0;\n this.rateLimitWindowStart = 0;\n this.perEventRateLimits.clear();\n this.sessionEventCounts = {\n total: 0,\n [EventType.CLICK]: 0,\n [EventType.PAGE_VIEW]: 0,\n [EventType.CUSTOM]: 0,\n [EventType.SCROLL]: 0,\n };\n this.lastSessionId = null;\n this.set('hasStartSession', false);\n\n this.dataSenders.forEach((sender) => {\n sender.stop();\n });\n }\n\n /**\n * Flushes all events in the queue asynchronously.\n *\n * **Purpose**: Force immediate sending of queued events without waiting for\n * the scheduled queue flush timeout.\n *\n * **Use Cases**:\n * - Manual flush triggered by user action\n * - Before page unload (prefer `flushImmediatelySync()` for unload scenarios)\n * - Testing/debugging\n *\n * **Behavior**:\n * - Sends events via `fetch()` API (async, reliable, allows retries)\n * - Multi-integration: Sends to all configured backends in parallel\n * - Does NOT block (returns Promise that resolves when all sends complete)\n * - Clears queue only after successful transmission\n *\n * **Note**: For page unload, use `flushImmediatelySync()` instead,\n * which uses `sendBeacon()` for guaranteed delivery.\n *\n * @returns Promise resolving to `true` if at least one integration accepted\n * the batch during this call (optimistic removal — failures\n * persist per-integration for retry). `false` if no events, all\n * senders failed, or a flush is already in flight.\n *\n * @example\n * ```typescript\n * // Before critical user action\n * await eventManager.flushImmediately();\n * ```\n *\n * @see flushImmediatelySync for synchronous page unload flush\n * @see src/managers/README.md (lines 5-75) for flush details\n */\n async flushImmediately(): Promise<boolean> {\n return this.flushEvents(false);\n }\n\n /**\n * Flushes all events in the queue synchronously using `sendBeacon()`.\n *\n * **Purpose**: Ensure events are sent before page unload, even if network is slow.\n *\n * **Use Cases**:\n * - Page unload (`beforeunload`, `pagehide` events)\n * - Tab close detection\n * - Any scenario where async flush might be interrupted\n *\n * **Behavior**:\n * - Uses `navigator.sendBeacon()` API (synchronous, queued by browser)\n * - Payload size limited to 64KB per beacon\n * - Browser guarantees delivery attempt (queued even if page closes)\n * - Clears queue immediately (no retry mechanism)\n *\n * **Multi-Integration**:\n * - Sends to all configured backends (SaaS + Custom) in parallel\n * - Independent success tracking per integration\n *\n * **Limitations**:\n * - No retry on failure (sendBeacon is fire-and-forget)\n * - 64KB payload limit (large batches may be truncated)\n *\n * **In-flight contract**: if an async send is already running this call is\n * deferred (queued for replay in the async send's `finally` block) and\n * returns `false` — nothing has been delivered yet at the point of return.\n * Mirrors `flushImmediately()`'s behaviour for the same condition.\n *\n * @returns `true` if at least one integration accepted the beacon batch\n * *during this call*, `false` otherwise (no events, all senders\n * failed, or the call was deferred behind an in-flight async send)\n *\n * @example\n * ```typescript\n * // Page unload handler\n * window.addEventListener('beforeunload', () => {\n * eventManager.flushImmediatelySync();\n * });\n * ```\n *\n * @see flushImmediately for async flush with retries\n * @see src/managers/README.md (lines 5-75) for flush details\n */\n flushImmediatelySync(): boolean {\n return this.flushEvents(true) as boolean;\n }\n\n /**\n * Returns the current number of events in the main queue.\n *\n * **Purpose**: Debugging and monitoring utility to check queue length.\n *\n * **Note**: This does NOT include:\n * - Pending events buffer (events before session init)\n * - Consent events buffer (events awaiting consent)\n * - Persisted events (events in localStorage from previous sessions)\n *\n * @returns Number of events currently in the main queue\n *\n * @example\n * ```typescript\n * const queueSize = eventManager.getQueueLength();\n * console.log(`Queue has ${queueSize} events`);\n * ```\n */\n getQueueLength(): number {\n return this.eventsQueue.length;\n }\n\n /**\n * Returns a copy of current events in the queue.\n *\n * **Purpose**: Test utility to inspect queued events for validation.\n *\n * **Note**: Only available in development mode via TestBridge.\n *\n * @returns Shallow copy of events queue\n * @internal Used by test-bridge.ts for test inspection\n */\n getQueueEvents(): EventData[] {\n // Strip the internal `_session_id` field — it's queue bookkeeping, not a\n // public contract. Tests and TestBridge consumers should see plain EventData.\n return this.eventsQueue.map(({ _session_id, ...rest }) => {\n void _session_id;\n return rest;\n });\n }\n\n /**\n * Triggers immediate queue flush (test utility).\n *\n * **Purpose**: Test utility to manually flush event queue for validation.\n *\n * **Note**: Only available in development mode via TestBridge.\n *\n * @returns Promise that resolves when flush completes\n * @internal Used by test-bridge.ts for test control\n */\n async flushQueue(): Promise<void> {\n await this.flushImmediately();\n }\n\n /**\n * Clears the event queue (test utility - use with caution).\n *\n * **Purpose**: Test utility to reset queue state between tests.\n *\n * **Warning**: This will discard all queued events without sending them.\n * Only use in test cleanup or when explicitly required.\n *\n * **Note**: Only available in development mode via TestBridge.\n *\n * @internal Used by test-bridge.ts for test cleanup\n */\n clearQueue(): void {\n this.eventsQueue = [];\n }\n\n /**\n * Flushes buffered events to the main queue after session initialization.\n *\n * **Purpose**: Re-tracks events that were captured before session initialization\n * (e.g., events fired during `App.init()` before SessionManager completes).\n *\n * **Pending Events Buffer**:\n * - Holds up to 100 events captured before `sessionId` is available\n * - FIFO eviction when buffer full (oldest events dropped with warning)\n * - Cleared and re-tracked when session becomes available\n *\n * **Flow**:\n * 1. Check if session is initialized (`sessionId` exists in global state)\n * 2. If not initialized: Log warning and keep events in buffer\n * 3. If initialized: Copy buffer, clear it, and re-track each event via `track()`\n * 4. Each event goes through full validation/dedup/rate limiting pipeline\n *\n * **Called by**:\n * - `SessionManager.startTracking()` after session initialization\n * - Ensures no events are lost during initialization phase\n *\n * **Important**: Events are re-tracked through `track()` method, so they go\n * through all validation, deduplication, rate limiting, and consent checks again.\n *\n * @example\n * ```typescript\n * // In SessionManager after session creation\n * this.set('sessionId', newSessionId);\n * eventManager.flushPendingEvents(); // Re-track buffered events\n * ```\n *\n * @see src/managers/README.md (lines 5-75) for pending buffer details\n */\n flushPendingEvents(): void {\n if (this.pendingEventsBuffer.length === 0) {\n return;\n }\n\n const currentSessionId = this.get('sessionId');\n if (!currentSessionId) {\n log('debug', 'Cannot flush pending events: session not initialized - keeping in buffer', {\n data: { bufferedEventCount: this.pendingEventsBuffer.length },\n });\n\n return;\n }\n\n const bufferedEvents = [...this.pendingEventsBuffer];\n this.pendingEventsBuffer = [];\n\n bufferedEvents.forEach((event) => {\n this.track(event);\n });\n }\n\n private clearSendTimeout(): void {\n if (this.sendTimeoutId !== null) {\n clearTimeout(this.sendTimeoutId);\n this.sendTimeoutId = null;\n }\n }\n\n private isSuccessfulResult(result: PromiseSettledResult<boolean>): boolean {\n return result.status === 'fulfilled' && result.value === true;\n }\n\n /**\n * Groups the queue by frozen `_session_id`, preserving insertion order.\n * Single pass — `buildBatchesWithIds()` builds one batch + one eventIds list\n * per group, so the grouping cost is O(N) per flush regardless of session\n * count.\n *\n * **Self-heal**: any entry missing `_session_id` (an internal invariant\n * violation — `buildEventPayload` always stamps it) is removed from the\n * queue rather than left behind, otherwise a single corrupted entry would\n * keep `eventsQueue.length > 0` forever and re-trigger periodic sends.\n */\n private groupQueuedEventsBySession(): Map<string, QueuedEvent[]> {\n const groups = new Map<string, QueuedEvent[]>();\n const corruptedIds: string[] = [];\n for (const event of this.eventsQueue) {\n if (!event._session_id) {\n // Logged at `debug` because no integrating-developer action can fix\n // it — it's a TraceLog bug. The entry is dropped from the queue\n // below to keep the system flushable.\n log('debug', 'Queued event missing _session_id, dropping', {\n data: { eventId: event.id, type: event.type },\n });\n corruptedIds.push(event.id);\n continue;\n }\n const group = groups.get(event._session_id);\n if (group) {\n group.push(event);\n } else {\n groups.set(event._session_id, [event]);\n }\n }\n if (corruptedIds.length > 0) {\n this.removeProcessedEvents(corruptedIds);\n }\n return groups;\n }\n\n /**\n * Builds a parallel list of `(batch, eventIds)` for sending. The eventIds are\n * the original `_session_id`-tagged event IDs in the queue that map to this\n * batch — used for optimistic removal. We can't read them off the wrapper's\n * `events[]` because dedup may have removed some signatures.\n */\n private buildBatchesWithIds(): Array<{ batch: EventsQueue; eventIds: string[] }> {\n const groups = this.groupQueuedEventsBySession();\n if (groups.size === 0) return [];\n\n const result: Array<{ batch: EventsQueue; eventIds: string[] }> = [];\n for (const [sessionId, groupEvents] of groups) {\n result.push({\n batch: this.buildBatchFromGroup(sessionId, groupEvents),\n eventIds: groupEvents.map((e) => e.id),\n });\n }\n return result;\n }\n\n private flushEvents(isSync: boolean): boolean | Promise<boolean> {\n if (this.eventsQueue.length === 0) {\n return isSync ? true : Promise.resolve(true);\n }\n\n // Prevent concurrent async sends — if a send is already in-flight\n // (e.g., sendEventsQueue with retries), skip to avoid duplicate requests.\n if (!isSync && this.sendInProgress) {\n log('debug', 'Async flush skipped: send already in progress');\n return Promise.resolve(false);\n }\n\n const planned = this.buildBatchesWithIds();\n if (planned.length === 0) {\n return isSync ? true : Promise.resolve(true);\n }\n\n if (this.dataSenders.length === 0) {\n // Standalone mode: emit each batch locally and clear the queue.\n for (const { batch, eventIds } of planned) {\n this.removeProcessedEvents(eventIds);\n this.emitEventsQueue(batch);\n }\n this.clearSendTimeout();\n return isSync ? true : Promise.resolve(true);\n }\n\n // The in-flight async fetch already owns these events: on success it clears\n // its persisted snapshot, on failure it writes one. Persisting again here\n // would resurrect the same events next session and the API would reject\n // them on the unique-id index, flagging every page-unload race as a\n // duplicate. Trade-off: if the page dies before the fetch flushes its body\n // to the kernel send buffer, those events are lost — accepted as the rarer\n // outcome versus systematic duplicate-flagging.\n if (isSync && this.sendInProgress) {\n const totalEvents = planned.reduce((acc, p) => acc + p.eventIds.length, 0);\n // Defer the sync flush until the in-flight async send settles. The async\n // fetch was built before the event(s) prompting this sync call, so it\n // does NOT include them; without the deferred re-flush the events would\n // sit in the queue until the next periodic tick (potentially lost on\n // navigation). The sendBatchAsync/sendEventsQueue finally blocks re-call\n // flushImmediatelySync when this flag is set.\n //\n // Return `false`: nothing has been delivered yet at this point. Mirrors\n // `flushImmediately()`'s in-flight contract (also returns `false` when a\n // send is already in progress) so callers can read both methods the\n // same way: `true` ⇒ at least one integration received the batch *now*.\n this.pendingSyncFlush = true;\n log('debug', 'Sync flush deferred: async send in-flight, will retry on settle', {\n data: { eventCount: totalEvents },\n });\n return false;\n }\n\n if (isSync) {\n // sendBeacon path — already non-blocking at the browser level.\n const results = planned.map(({ batch, eventIds }) => this.sendBatchSync(batch, eventIds));\n this.settleSendTimeout();\n return results.some(Boolean);\n }\n\n // Claim sendInProgress for the lifetime of the async fetch. Two rapid\n // flushImmediately() calls (e.g., SPA navigation followed by pagehide,\n // or visibilitychange firing alongside an explicit flush) would otherwise\n // both pass the guard above, build the same `planned`, and fire duplicate\n // network requests. The periodic sender uses the same flag, so periodic\n // and explicit flushes serialize correctly against each other too.\n this.sendInProgress = true;\n return (async (): Promise<boolean> => {\n try {\n // Multi-session is rare; when it happens we send all batches in parallel.\n // Per-batch optimistic removal + per-integration persistence are\n // independent, so concurrent completion is race-safe (single-threaded JS,\n // disjoint event-id sets).\n const results = await Promise.all(\n planned.map(async ({ batch, eventIds }) => this.sendBatchAsync(batch, eventIds)),\n );\n this.settleSendTimeout();\n return results.some(Boolean);\n } finally {\n this.sendInProgress = false;\n this.drainPendingSyncFlush();\n }\n })();\n }\n\n /**\n * Reconciles the periodic send timer after a flush attempt. Clears the\n * timer when the queue is empty, otherwise (re)schedules a retry tick.\n *\n * **Why**: a `flushImmediately()` / `flushImmediatelySync()` call where all\n * integrations fail leaves events in `eventsQueue` for retry. The periodic\n * timer is the safety net that drains them when the backend recovers — if\n * we cleared it unconditionally here, the queue would sit untouched until\n * the next tracked event resurrects the timer in `addToQueue`. Mirrors the\n * pattern in `sendEventsQueue()` (the periodic path).\n */\n private settleSendTimeout(): void {\n if (this.eventsQueue.length === 0) {\n this.clearSendTimeout();\n } else {\n this.scheduleSendTimeout();\n }\n }\n\n /**\n * Re-runs a sync flush that was deferred while an async send was in flight.\n *\n * Called from the `finally` blocks of `flushEvents(false)` and\n * `sendEventsQueue()`. If `pendingSyncFlush` is set, clears the flag and\n * invokes `flushImmediatelySync()` synchronously so any events that arrived\n * after the deferred sync call are delivered before the next event loop\n * tick. Critical for high-stakes events tracked mid-async-send.\n */\n private drainPendingSyncFlush(): void {\n if (!this.pendingSyncFlush) return;\n this.pendingSyncFlush = false;\n this.flushImmediatelySync();\n }\n\n /**\n * Sends one batch synchronously across all integrations (sendBeacon path).\n * Optimistic removal: if any integration succeeds, we remove the batch's\n * events from the queue and emit it locally. Failures persist per-integration.\n */\n private sendBatchSync(batch: EventsQueue, eventIds: string[]): boolean {\n const results = this.dataSenders.map((sender) => sender.sendEventsQueueSync(batch));\n const anySucceeded = results.some((success) => success);\n\n if (anySucceeded) {\n this.removeProcessedEvents(eventIds);\n this.emitEventsQueue(batch);\n } else {\n log('debug', 'Sync send complete failure, events kept in queue for retry', {\n data: { eventCount: eventIds.length, sessionId: batch.session_id },\n });\n }\n\n return anySucceeded;\n }\n\n /**\n * Sends one batch asynchronously across all integrations (fetch path).\n */\n private async sendBatchAsync(batch: EventsQueue, eventIds: string[]): Promise<boolean> {\n const sendPromises = this.dataSenders.map(async (sender) =>\n sender.sendEventsQueue(batch, {\n onSuccess: () => {},\n onFailure: () => {},\n }),\n );\n\n const results = await Promise.allSettled(sendPromises);\n const anySucceeded = results.some((result) => this.isSuccessfulResult(result));\n\n if (anySucceeded) {\n this.removeProcessedEvents(eventIds);\n this.emitEventsQueue(batch);\n\n const failedCount = results.filter((result) => !this.isSuccessfulResult(result)).length;\n if (failedCount > 0) {\n log('debug', 'Async send completed with some failures, removed from queue and persisted per-integration', {\n data: { eventCount: eventIds.length, failedCount, sessionId: batch.session_id },\n });\n }\n } else {\n log('debug', 'Async send complete failure, events kept in queue for retry', {\n data: { eventCount: eventIds.length, sessionId: batch.session_id },\n });\n }\n\n return anySucceeded;\n }\n\n private async sendEventsQueue(): Promise<void> {\n if (this.eventsQueue.length === 0 || this.sendInProgress) {\n return;\n }\n\n this.sendInProgress = true;\n\n try {\n const planned = this.buildBatchesWithIds();\n if (planned.length === 0) return;\n\n if (this.dataSenders.length === 0) {\n // Standalone mode: emit each batch locally, but DO NOT clear the queue.\n // Periodic send is best-effort visibility for local listeners; events\n // remain queued so that an explicit `flushImmediately()` (manual\n // intent) can still consume them. Mirrors pre-refactor behaviour.\n for (const { batch } of planned) {\n this.emitEventsQueue(batch);\n }\n return;\n }\n\n const results = await Promise.all(\n planned.map(async ({ batch, eventIds }) => this.sendBatchAsync(batch, eventIds)),\n );\n const anySucceededOverall = results.some(Boolean);\n\n if (anySucceededOverall) {\n this.consecutiveSendFailures = 0;\n } else {\n this.consecutiveSendFailures = Math.min(this.consecutiveSendFailures + 1, MAX_CONSECUTIVE_SEND_FAILURES);\n }\n\n if (this.eventsQueue.length === 0) {\n this.clearSendTimeout();\n } else {\n this.scheduleSendTimeout();\n }\n } finally {\n this.sendInProgress = false;\n this.drainPendingSyncFlush();\n }\n }\n\n /**\n * Builds a single batch from a per-session group: dedup by signature,\n * SESSION_START first, then timestamp order, strip `_session_id`, apply\n * `beforeBatch` transformer when running standalone.\n *\n * **Why N batches per flush**: events freeze their `_session_id` at `track()`\n * time. If the session was renewed (idle timeout) between two `track()`\n * calls, the queue contains events from multiple sessions. `buildBatchesWithIds()`\n * emits one batch per session so the backend's `EventsQueueDto.session_id`\n * remains the single source of truth and stays consistent with the events it\n * carries.\n *\n * **Strip**: `_session_id` is removed from each event in the wrapper's\n * `events[]` because the backend uses `forbidNonWhitelisted: true` and would\n * reject the batch if the field leaked through.\n *\n * **Transformer note**: `beforeBatch` is invoked **once per session-batch**,\n * not once per flush. A queue spanning N sessions triggers N invocations.\n */\n private buildBatchFromGroup(sessionId: string, groupEvents: QueuedEvent[]): EventsQueue {\n // Per-batch dedup: collapse events sharing a signature into one slot.\n // `order` preserves the first occurrence's position, but `eventMap.set`\n // keeps the latest payload — so the most recent timestamp/metadata wins\n // while the batch's overall event sequence stays stable. Matches the\n // pre-refactor behavior in `buildEventsPayload`.\n const eventMap = new Map<string, QueuedEvent>();\n const order: string[] = [];\n for (const event of groupEvents) {\n const signature = this.createEventSignature(event);\n if (!eventMap.has(signature)) {\n order.push(signature);\n }\n eventMap.set(signature, event);\n }\n\n const events: EventData[] = order\n .map((signature) => eventMap.get(signature))\n .filter((event): event is QueuedEvent => Boolean(event))\n .sort((a, b) => {\n if (a.type === EventType.SESSION_START && b.type !== EventType.SESSION_START) return -1;\n if (b.type === EventType.SESSION_START && a.type !== EventType.SESSION_START) return 1;\n return a.timestamp - b.timestamp;\n })\n .map(({ _session_id, ...rest }) => {\n void _session_id;\n return rest;\n });\n\n const globalMetadata = this.get('config')?.globalMetadata;\n const identity = this.get('identity');\n\n const queue: EventsQueue = {\n user_id: this.get('userId'),\n session_id: sessionId,\n device: this.get('device'),\n events,\n ...(globalMetadata && { global_metadata: globalMetadata }),\n ...(identity && { identify: identity }),\n };\n\n return queue;\n }\n\n private buildEventPayload(data: Partial<EventData>): QueuedEvent | null {\n // Freeze the session ID at track() time. This is the single source of\n // truth for batch attribution — `groupQueuedEventsBySession()` groups by\n // `_session_id`, so the event survives session renewal (idle timeout)\n // without orphaning. Caller (`track()`) already routed events without a\n // sessionId to `pendingEventsBuffer`, so reaching this point with no\n // sessionId is an internal bug — log critical and bail.\n const currentSessionId = this.get('sessionId');\n if (!currentSessionId) {\n log('error', 'buildEventPayload reached without sessionId — event dropped', {\n data: { type: data.type },\n visibility: 'critical',\n });\n return null;\n }\n\n // Defense-in-depth: backend rejects page_url with @IsNotEmpty + URL validator\n // (accepts 'unknown' / 'excluded' as sentinels). normalizeUrl() can return ''\n // for sandboxed iframes / exotic browser contexts where window.location.href\n // is falsy — fall back to 'unknown' rather than ship an empty string and lose\n // the whole batch.\n const rawPageUrl = data.page_url ?? this.get('pageUrl');\n const currentPageUrl = typeof rawPageUrl === 'string' && rawPageUrl.length > 0 ? rawPageUrl : 'unknown';\n\n // Use TimeManager for accurate timestamps (immune to clock skew during session)\n const timestamp = this.timeManager.now();\n\n // Validate timestamp before creating event\n const validation = this.timeManager.validateTimestamp(timestamp);\n if (!validation.valid) {\n log('warn', 'Event timestamp validation failed', {\n data: { type: data.type, error: validation.error },\n });\n // Continue anyway with adjusted timestamp to avoid data loss\n // Backend has 3-minute tolerance as safety net\n }\n\n // Get session-level attribution from global state (captured once at SESSION_START)\n const sessionReferrer = this.get('sessionReferrer');\n const sessionUtm = this.get('sessionUtm');\n const sessionClickIds = this.get('sessionClickIds');\n\n const payload: EventData = {\n id: generateEventId(),\n type: data.type as EventType,\n page_url: currentPageUrl,\n timestamp,\n ...(sessionReferrer && { referrer: sessionReferrer }),\n ...(data.from_page_url && { from_page_url: data.from_page_url }),\n ...(data.scroll_data && { scroll_data: data.scroll_data }),\n ...(data.click_data && { click_data: data.click_data }),\n ...(data.custom_event && { custom_event: data.custom_event }),\n ...(data.web_vitals && { web_vitals: data.web_vitals }),\n ...(data.error_data && { error_data: data.error_data }),\n ...(data.page_view && { page_view: data.page_view }),\n ...(sessionUtm && { utm: sessionUtm }),\n ...(sessionClickIds && { click_ids: sessionClickIds }),\n };\n\n return { ...payload, _session_id: currentSessionId };\n }\n\n private isDuplicateEvent(event: EventData): boolean {\n const now = Date.now();\n const fingerprint = this.createEventFingerprint(event);\n\n const lastSeen = this.recentEventFingerprints.get(fingerprint);\n\n if (lastSeen && now - lastSeen < DUPLICATE_EVENT_THRESHOLD_MS) {\n this.recentEventFingerprints.set(fingerprint, now);\n return true;\n }\n\n this.recentEventFingerprints.set(fingerprint, now);\n\n if (this.recentEventFingerprints.size > MAX_FINGERPRINTS) {\n this.pruneOldFingerprints();\n }\n\n if (this.recentEventFingerprints.size > MAX_FINGERPRINTS_HARD_LIMIT) {\n this.recentEventFingerprints.clear();\n this.recentEventFingerprints.set(fingerprint, now);\n\n log('debug', 'Event fingerprint cache exceeded hard limit, cleared', {\n data: { hardLimit: MAX_FINGERPRINTS_HARD_LIMIT },\n });\n }\n\n return false;\n }\n\n private pruneOldFingerprints(): void {\n const now = Date.now();\n const cutoff = DUPLICATE_EVENT_THRESHOLD_MS * FINGERPRINT_CLEANUP_MULTIPLIER;\n\n for (const [fingerprint, timestamp] of this.recentEventFingerprints.entries()) {\n if (now - timestamp > cutoff) {\n this.recentEventFingerprints.delete(fingerprint);\n }\n }\n\n log('debug', 'Pruned old event fingerprints', {\n data: {\n remaining: this.recentEventFingerprints.size,\n cutoffMs: cutoff,\n },\n });\n }\n\n private createEventFingerprint(event: EventData): string {\n let fingerprint = `${event.type}_${event.page_url}`;\n\n if (event.click_data) {\n const x = Math.round((event.click_data.x || 0) / 10) * 10;\n const y = Math.round((event.click_data.y || 0) / 10) * 10;\n fingerprint += `_click_${x}_${y}`;\n }\n\n if (event.scroll_data) {\n fingerprint += `_scroll_${event.scroll_data.depth}_${event.scroll_data.direction}`;\n }\n\n if (event.custom_event) {\n fingerprint += `_custom_${event.custom_event.name}`;\n if (event.custom_event.metadata) {\n fingerprint += `_${this.stableStringify(event.custom_event.metadata)}`;\n }\n }\n\n if (event.web_vitals) {\n fingerprint += `_vitals_${event.web_vitals.type}`;\n }\n\n if (event.error_data) {\n fingerprint += `_error_${event.error_data.type}_${event.error_data.message}`;\n }\n\n return fingerprint;\n }\n\n private createEventSignature(event: EventData): string {\n return this.createEventFingerprint(event);\n }\n\n /** Deterministic JSON string with sorted keys to ensure consistent fingerprints regardless of property insertion order */\n private stableStringify(value: unknown): string {\n return JSON.stringify(value, (_, v: unknown) => {\n if (v && typeof v === 'object' && !Array.isArray(v)) {\n return Object.keys(v as Record<string, unknown>)\n .sort()\n .reduce<Record<string, unknown>>((sorted, key) => {\n sorted[key] = (v as Record<string, unknown>)[key];\n return sorted;\n }, {});\n }\n return v;\n });\n }\n\n private addToQueue(event: QueuedEvent): void {\n this.emitEvent(event);\n\n this.eventsQueue.push(event);\n\n if (this.eventsQueue.length > MAX_EVENTS_QUEUE_LENGTH) {\n const nonCriticalIndex = this.eventsQueue.findIndex((e) => e.type !== EventType.SESSION_START);\n\n const removedEvent =\n nonCriticalIndex >= 0 ? this.eventsQueue.splice(nonCriticalIndex, 1)[0] : this.eventsQueue.shift();\n\n log('warn', 'Event queue overflow, oldest non-critical event removed', {\n data: {\n maxLength: MAX_EVENTS_QUEUE_LENGTH,\n currentLength: this.eventsQueue.length,\n removedEventType: removedEvent?.type,\n wasCritical: removedEvent?.type === EventType.SESSION_START,\n },\n });\n }\n\n this.scheduleSendTimeout();\n\n if (\n this.eventsQueue.length >= BATCH_SIZE_THRESHOLD &&\n this.consecutiveSendFailures < MAX_CONSECUTIVE_SEND_FAILURES\n ) {\n void this.sendEventsQueue();\n }\n }\n\n private scheduleSendTimeout(): void {\n if (this.sendTimeoutId !== null) return;\n\n const delay = this.calculateSendDelay();\n this.sendTimeoutId = window.setTimeout(() => {\n this.sendTimeoutId = null;\n if (this.eventsQueue.length > 0) {\n void this.sendEventsQueue();\n }\n }, delay);\n }\n\n private calculateSendDelay(): number {\n const baseInterval = this.get('config')?.sendIntervalMs ?? EVENT_SENT_INTERVAL_MS;\n if (this.consecutiveSendFailures === 0) return baseInterval;\n const backoff = baseInterval * Math.pow(2, this.consecutiveSendFailures);\n return Math.min(backoff, MAX_SEND_INTERVAL_MS);\n }\n\n private shouldSample(): boolean {\n const samplingRate = this.get('config')?.samplingRate ?? 1;\n return Math.random() < samplingRate;\n }\n\n private checkRateLimit(): boolean {\n const now = Date.now();\n\n if (now - this.rateLimitWindowStart > RATE_LIMIT_WINDOW_MS) {\n this.rateLimitCounter = 0;\n this.rateLimitWindowStart = now;\n }\n\n if (this.rateLimitCounter >= MAX_EVENTS_PER_SECOND) {\n return false;\n }\n\n this.rateLimitCounter++;\n return true;\n }\n\n private checkPerEventRateLimit(eventName: string, maxSameEventPerMinute: number): boolean {\n const now = Date.now();\n const timestamps = this.perEventRateLimits.get(eventName) ?? [];\n\n const validTimestamps = timestamps.filter((ts) => now - ts < PER_EVENT_RATE_LIMIT_WINDOW_MS);\n\n if (validTimestamps.length >= maxSameEventPerMinute) {\n log('warn', 'Per-event rate limit exceeded for custom event', {\n data: {\n eventName,\n limit: maxSameEventPerMinute,\n window: `${PER_EVENT_RATE_LIMIT_WINDOW_MS / 1000}s`,\n },\n });\n return false;\n }\n\n validTimestamps.push(now);\n this.perEventRateLimits.set(eventName, validTimestamps);\n\n return true;\n }\n\n private getTypeLimitForEvent(type: EventType): number | null {\n const limits: Partial<Record<EventType, number>> = {\n [EventType.CLICK]: MAX_CLICKS_PER_SESSION,\n [EventType.PAGE_VIEW]: MAX_PAGE_VIEWS_PER_SESSION,\n [EventType.CUSTOM]: MAX_CUSTOM_EVENTS_PER_SESSION,\n [EventType.SCROLL]: MAX_SCROLL_EVENTS_PER_SESSION,\n };\n return limits[type] ?? null;\n }\n\n private removeProcessedEvents(eventIds: string[]): void {\n const eventIdSet = new Set(eventIds);\n\n this.eventsQueue = this.eventsQueue.filter((event) => {\n return !eventIdSet.has(event.id);\n });\n }\n\n private emitEvent(eventData: EventData | QueuedEvent): void {\n if (this.emitter) {\n // Strip the internal `_session_id` field before exposing the event to\n // public listeners. The wrapper EventsQueue carries session_id separately.\n const { _session_id, ...publicEvent } = eventData as QueuedEvent;\n void _session_id;\n this.emitter.emit(EmitterEvent.EVENT, publicEvent as EventData);\n }\n }\n\n private emitEventsQueue(queue: EventsQueue): void {\n if (this.emitter) {\n this.emitter.emit(EmitterEvent.QUEUE, queue);\n }\n }\n\n /**\n * Creates a debounced version of a function that delays execution until after\n * a specified wait time has elapsed since the last invocation.\n *\n * **Purpose**: Reduces frequency of expensive operations (localStorage writes)\n * while ensuring no data is lost (trailing edge execution).\n *\n * **Behavior**:\n * - Each call resets the timer\n * - Function executes only after `delay` ms of silence\n * - Last invocation always executes (trailing edge)\n *\n * **Use Case**: Batches rapid successive calls into a single execution\n *\n * @param fn - Function to debounce\n * @param delay - Delay in milliseconds\n * @returns Debounced version of the function\n *\n * @internal\n */\n private debounce<T extends (...args: any[]) => void>(fn: T, delay: number): T {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return ((...args: Parameters<T>) => {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n timeoutId = setTimeout(() => {\n fn(...args);\n timeoutId = null;\n }, delay);\n }) as T;\n }\n\n /**\n * Returns initial zero counts for session event tracking.\n *\n * **Purpose**: DRY helper to avoid duplicating initial counts structure\n * across multiple methods (loadSessionCounts, validation fallbacks).\n *\n * @returns Fresh SessionEventCounts object with all counters at zero\n *\n * @internal\n */\n private getInitialCounts(): SessionEventCounts {\n return {\n total: 0,\n [EventType.CLICK]: 0,\n [EventType.PAGE_VIEW]: 0,\n [EventType.CUSTOM]: 0,\n [EventType.SCROLL]: 0,\n };\n }\n\n /**\n * Loads persisted session event counts from localStorage.\n *\n * **Purpose**: Restore per-session event counts after page reload to maintain\n * accurate rate limiting across page navigations within the same session.\n *\n * **Behavior**:\n * - Attempts to load counts from localStorage using session ID as key\n * - If no persisted data found: Returns initial zero counts\n * - If corrupted data found: Returns initial zero counts with warning\n *\n * **Storage Key**: `tlog:{userId}:session_counts:{sessionId}`\n *\n * **Why This Matters**:\n * - Without persistence, counts reset on every page reload\n * - This allows users to bypass per-session limits by refreshing the page\n * - Example: 100 PAGE_VIEW limit could be bypassed by reloading after 99 events\n *\n * @param sessionId - Current session identifier\n * @returns Session event counts object (either persisted or initial state)\n *\n * @internal\n */\n private loadSessionCounts(sessionId: string): SessionEventCounts {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n return this.getInitialCounts();\n }\n\n const userId = this.get('userId') || 'anonymous';\n const storageKey = SESSION_COUNTS_KEY(userId, sessionId);\n\n try {\n const stored = localStorage.getItem(storageKey);\n\n if (!stored) {\n // No persisted data - this is a new session or first page load\n return this.getInitialCounts();\n }\n\n const parsed = JSON.parse(stored) as Partial<StoredSessionCounts>;\n\n // Check for expiry (7 days)\n if (parsed._timestamp && Date.now() - parsed._timestamp > SESSION_COUNTS_EXPIRY_MS) {\n log('debug', 'Session counts expired, clearing', {\n data: { sessionId, age: Date.now() - parsed._timestamp },\n });\n localStorage.removeItem(storageKey);\n return this.getInitialCounts();\n }\n\n // Robust validation: check all required fields (not just 'total')\n // Prevents partial/corrupted data from causing runtime errors\n if (\n typeof parsed.total === 'number' &&\n typeof parsed[EventType.CLICK] === 'number' &&\n typeof parsed[EventType.PAGE_VIEW] === 'number' &&\n typeof parsed[EventType.CUSTOM] === 'number' &&\n typeof parsed[EventType.SCROLL] === 'number'\n ) {\n // Return only the SessionEventCounts fields (exclude _timestamp, _version)\n return {\n total: parsed.total,\n [EventType.CLICK]: parsed[EventType.CLICK],\n [EventType.PAGE_VIEW]: parsed[EventType.PAGE_VIEW],\n [EventType.CUSTOM]: parsed[EventType.CUSTOM],\n [EventType.SCROLL]: parsed[EventType.SCROLL],\n };\n }\n\n log('warn', 'Invalid session counts structure in localStorage, resetting', {\n data: { sessionId, parsed },\n });\n localStorage.removeItem(storageKey);\n log('debug', 'Session counts removed due to invalid/corrupted data', {\n data: { sessionId, parsed },\n });\n\n return this.getInitialCounts();\n } catch (error) {\n log('warn', 'Failed to load session counts from localStorage', {\n error,\n data: { sessionId },\n });\n\n return this.getInitialCounts();\n }\n }\n\n /**\n * Cleans up expired session counts from localStorage.\n *\n * **Purpose**: Prevents localStorage pollution from abandoned sessions by removing\n * counts older than 7 days.\n *\n * **Behavior**:\n * - Checks if cleanup was run recently (within last hour) and skips if so\n * - Iterates all localStorage keys matching the session counts prefix pattern\n * - Parses each entry and checks `_timestamp` field\n * - Removes entries where age exceeds SESSION_COUNTS_EXPIRY_MS (7 days)\n * - Silently ignores parse errors (corrupted entries cleaned on next load)\n * - Updates last cleanup timestamp after successful run\n *\n * **When Called**: Automatically on EventManager constructor initialization\n *\n * **Performance**: O(n) scan where n = localStorage keys (typically <100), but throttled\n * to run at most once per hour to prevent impact on rapid page reloads\n *\n * @internal\n */\n private cleanupExpiredSessionCounts(): void {\n if (typeof window === 'undefined' || typeof localStorage === 'undefined') {\n return;\n }\n\n try {\n // Throttling: Skip if cleanup ran recently (within last hour)\n const lastCleanup = localStorage.getItem(SESSION_COUNTS_LAST_CLEANUP_KEY);\n\n if (lastCleanup) {\n const timeSinceLastCleanup = Date.now() - parseInt(lastCleanup, 10);\n\n if (timeSinceLastCleanup < SESSION_COUNTS_CLEANUP_THROTTLE_MS) {\n log('debug', 'Skipping session counts cleanup (throttled)', {\n data: { timeSinceLastCleanup, throttleMs: SESSION_COUNTS_CLEANUP_THROTTLE_MS },\n });\n\n return;\n }\n }\n\n const userId = this.get('userId') || 'anonymous';\n const prefix = `${STORAGE_BASE_KEY}:${userId}:session_counts:`;\n\n // Collect keys to remove (can't modify during iteration)\n const keysToRemove: string[] = [];\n\n for (let i = 0; i < localStorage.length; i++) {\n const key = localStorage.key(i);\n\n if (key?.startsWith(prefix)) {\n try {\n const stored = localStorage.getItem(key);\n\n if (stored) {\n const parsed = JSON.parse(stored) as Partial<StoredSessionCounts>;\n\n // Check expiry\n if (parsed._timestamp && Date.now() - parsed._timestamp > SESSION_COUNTS_EXPIRY_MS) {\n keysToRemove.push(key);\n }\n }\n } catch {\n // Ignore parse errors, will be cleaned on next load\n }\n }\n }\n\n // Remove expired entries\n keysToRemove.forEach((key) => {\n localStorage.removeItem(key);\n log('debug', 'Cleaned up expired session counts', { data: { key } });\n });\n\n if (keysToRemove.length > 0) {\n log('info', `Cleaned up ${keysToRemove.length} expired session counts entries`);\n }\n\n // Update last cleanup timestamp\n localStorage.setItem(SESSION_COUNTS_LAST_CLEANUP_KEY, Date.now().toString());\n } catch (error) {\n log('warn', 'Failed to cleanup expired session counts', { error });\n }\n }\n\n /**\n * Persists current session event counts to localStorage (debounced).\n *\n * **Purpose**: Save event counts to ensure they survive page reloads and\n * maintain accurate per-session rate limiting across navigations.\n *\n * **Behavior**:\n * - Saves current `sessionEventCounts` to localStorage using session ID as key\n * - Overwrites previous counts (always reflects latest state)\n * - Fails silently if localStorage quota exceeded or unavailable\n *\n * **Storage Key**: `tlog:{userId}:session_counts:{sessionId}`\n *\n * **Debouncing**: This method is called via `saveSessionCountsDebounced()`\n * with 500ms debounce delay. Direct calls are for immediate saves (e.g., stop()).\n *\n * **Performance**: Debouncing reduces localStorage writes from ~1000 per session\n * to ~20-30 (96-97% reduction) while maintaining data integrity.\n *\n * **Cleanup**: Counts persist across page reloads for rate limiting enforcement.\n * Automatic cleanup on page reload removes expired counts (older than 7 days).\n * Note: SESSION_COUNTS_KEY entries are intentionally persistent to maintain\n * rate limits across sessions (~100 bytes per session).\n *\n * @param sessionId - Current session identifier\n *\n * @internal\n */\n private saveSessionCounts(sessionId: string): void {\n const userId = this.get('userId') || 'anonymous';\n const storageKey = SESSION_COUNTS_KEY(userId, sessionId);\n\n try {\n const dataToStore: StoredSessionCounts = {\n ...this.sessionEventCounts,\n _timestamp: Date.now(),\n _version: 1,\n };\n\n localStorage.setItem(storageKey, JSON.stringify(dataToStore));\n } catch (error) {\n log('warn', 'Failed to persist session counts to localStorage', {\n error,\n data: { sessionId },\n });\n }\n }\n}\n","import { USER_ID_KEY } from '../constants';\nimport { generateUUID } from '../utils';\nimport { StorageManager } from './storage.manager';\n\n/**\n * Simple utility for managing unique user identification for analytics tracking.\n *\n * **Purpose**: Creates and persists RFC4122-compliant UUID v4 identifiers\n * for tracking users across browser sessions.\n *\n * **Core Functionality**:\n * - **User ID Generation**: Creates UUID v4 identifiers\n * - **Persistence**: Stores user IDs in localStorage with automatic fallback\n * - **Session Continuity**: Reuses existing user IDs across browser sessions\n * - **Global User Identity**: Single user ID shared across all TraceLog projects in the same browser\n *\n * **Key Features**:\n * - Static utility method pattern (no object instantiation required)\n * - UUID v4 generation for globally unique identifiers\n * - Fixed storage key (`tlog:uid`) for consistent identification across projects\n * - Automatic fallback to memory storage when localStorage unavailable\n * - Minimal dependencies and zero allocation approach\n *\n * **Storage**: `tlog:uid` (fixed, not project-scoped)\n *\n * @see src/managers/README.md (lines 227-252) for detailed documentation\n *\n * @example\n * ```typescript\n * const userId = UserManager.getId(storageManager);\n * // Returns: '550e8400-e29b-41d4-a716-446655440000' (UUID v4)\n *\n * // Subsequent calls return the same ID\n * const sameUserId = UserManager.getId(storageManager);\n * // Returns: '550e8400-e29b-41d4-a716-446655440000' (persisted)\n * ```\n */\nexport class UserManager {\n /**\n * Gets or creates a unique user ID.\n *\n * **Behavior**:\n * 1. Checks localStorage for existing user ID\n * 2. Returns existing ID if found\n * 3. Generates new RFC4122-compliant UUID v4 if not found\n * 4. Persists new ID to localStorage\n *\n * **Storage Key**: `tlog:uid` (fixed, shared across all TraceLog projects)\n *\n * **ID Format**: UUID v4 (e.g., `550e8400-e29b-41d4-a716-446655440000`)\n *\n * @param storageManager - Storage manager instance for persistence\n * @returns Persistent unique user ID (UUID v4 format)\n */\n static getId(storageManager: StorageManager): string {\n const storedUserId = storageManager.getItem(USER_ID_KEY);\n\n if (storedUserId) {\n return storedUserId;\n }\n\n const newUserId = generateUUID();\n storageManager.setItem(USER_ID_KEY, newUserId);\n\n return newUserId;\n }\n}\n","import { BROADCAST_CHANNEL_NAME, DEFAULT_SESSION_TIMEOUT, SESSION_STORAGE_KEY } from '../constants';\nimport { ClickIds, EventType, UTM } from '../types';\nimport { getClickIds, getExternalReferrer, getUTMParameters, isPrerendering, log } from '../utils';\nimport { StateManager } from './state.manager';\nimport { StorageManager } from './storage.manager';\nimport { EventManager } from './event.manager';\n\ninterface StoredSessionData {\n id: string;\n lastActivity: number;\n referrer?: string;\n utm?: UTM;\n clickIds?: ClickIds;\n}\n\n/**\n * Session ID validation pattern: {timestamp}-{9-char-base36}\n *\n * Validates recovered session IDs to prevent data corruption from:\n * - Manual localStorage manipulation by users or browser extensions\n * - Race conditions in cross-tab scenarios (partial/truncated writes)\n * - Storage corruption or quota-based truncation\n * - Malformed IDs from older library versions or external interference\n *\n * Format: 13-digit timestamp + hyphen + 9 lowercase alphanumeric characters\n * Example: 1704067200000-a3f9c2b5d\n */\nconst SESSION_ID_PATTERN = /^\\d{13}-[a-z0-9]{9}$/;\n\n/**\n * Manages user sessions with cross-tab synchronization, inactivity detection,\n * and automatic lifecycle tracking.\n *\n * **Purpose**: Creates and manages user sessions, tracking session start\n * with automatic persistence, recovery, and multi-tab synchronization.\n * Does not track session end events (removed in v2.0.0).\n *\n * **Core Functionality**:\n * - **Session Generation**: Creates unique session IDs (`{timestamp}-{9-char-base36}`)\n * - **Activity Tracking**: Monitors user interactions to extend session timeout\n * - **Cross-Tab Sync**: BroadcastChannel synchronization across browser tabs\n * - **Persistence**: Stores session data in localStorage for recovery\n * - **Session Mirror**: Automatically mirrors session to sessionStorage for recovery after external redirects\n * - **Inactivity Detection**: Automatic timeout after inactivity (default 15 minutes)\n * - **Lifecycle Events**: Emits SESSION_START event only (SESSION_END removed in v2.0.0)\n *\n * **Key Features**:\n * - **Session ID Format**: `{timestamp}-{9-char-base36}` (e.g., `1704896400000-a3b4c5d6e`)\n * - **Default Timeout**: 15 minutes (900,000 ms), configurable via `sessionTimeout`\n * - **Cross-Tab Sharing**: Primary tab creates session, shares via BroadcastChannel\n * - **Secondary Tab Behavior**: Receives session from primary tab, no SESSION_START event\n * - **No Session End Events**: Library only tracks SESSION_START (v2.0.0+)\n *\n * **BroadcastChannel Integration**:\n * - **Initialized BEFORE SESSION_START**: Prevents race condition with secondary tabs\n * - **Messages**: Only `session_start` action (share session across tabs)\n * - **Fallback**: Logs warning if BroadcastChannel not supported (no cross-tab sync)\n *\n * **Activity Detection**:\n * - Tracks user interactions via listener managers (mouse, keyboard, touch, scroll, etc.)\n * - Resets inactivity timeout on each activity\n * - Updates `lastActivity` timestamp in localStorage\n *\n * **State Management**:\n * - **`sessionId`**: Current session ID stored in global state\n *\n * @see src/managers/README.md (lines 140-169) for detailed documentation\n *\n * @example\n * ```typescript\n * const sessionManager = new SessionManager(storage, eventManager, 'project123');\n *\n * // Start session tracking\n * sessionManager.startTracking();\n * // → Creates session ID: '1704896400000-a3b4c5d6e'\n * // → Emits SESSION_START event\n * // → Sets up activity listeners\n * // → Initializes cross-tab sync\n *\n * // User activity extends session\n * // (automatic via activity listeners)\n *\n * // Stop tracking (cleanup only)\n * sessionManager.stopTracking();\n * // → Cleans up listeners and timers\n * // → No SESSION_END event emitted\n * ```\n */\nexport class SessionManager extends StateManager {\n private readonly storageManager: StorageManager;\n private readonly eventManager: EventManager;\n private readonly projectId: string;\n\n private activityHandler: (() => void) | null = null;\n private visibilityChangeHandler: (() => void) | null = null;\n private sessionTimeoutId: ReturnType<typeof setTimeout> | null = null;\n private broadcastChannel: BroadcastChannel | null = null;\n private isTracking = false;\n private needsRenewal = false;\n private prerenderActivationHandler: (() => void) | null = null;\n\n /**\n * Creates a SessionManager instance.\n *\n * @param storageManager - Storage manager for session persistence\n * @param eventManager - Event manager for SESSION_START events\n * @param projectId - Project identifier for namespacing session storage\n */\n constructor(storageManager: StorageManager, eventManager: EventManager, projectId: string) {\n super();\n this.storageManager = storageManager;\n this.eventManager = eventManager;\n this.projectId = projectId;\n }\n\n private initCrossTabSync(): void {\n if (typeof BroadcastChannel === 'undefined') {\n log('debug', 'BroadcastChannel not supported');\n return;\n }\n\n const projectId = this.getProjectId();\n this.broadcastChannel = new BroadcastChannel(BROADCAST_CHANNEL_NAME(projectId));\n\n this.broadcastChannel.onmessage = (event): void => {\n const { action, sessionId, timestamp, projectId: messageProjectId } = event.data ?? {};\n\n if (messageProjectId !== projectId) {\n return;\n }\n\n if (action === 'session_start' && sessionId && typeof timestamp === 'number' && timestamp > Date.now() - 5000) {\n this.set('sessionId', sessionId);\n\n // Adopt the session-level attribution persisted by the tab that created the\n // session. localStorage is shared across tabs, so re-persisting with only\n // id + timestamp would wipe referrer/utm/clickIds for every tab and any later\n // recovery. Syncing it into state also keeps this tab's events attributed to\n // the session's origin rather than to this tab's own landing URL.\n const stored = this.loadStoredSession();\n this.set('sessionReferrer', stored?.referrer);\n this.set('sessionUtm', stored?.utm);\n this.set('sessionClickIds', stored?.clickIds);\n this.persistSession(sessionId, timestamp, stored?.referrer, stored?.utm, stored?.clickIds);\n\n if (this.isTracking) {\n this.setupSessionTimeout();\n }\n } else if (action && action !== 'session_start') {\n // Log unknown action types to help with debugging cross-tab synchronization issues\n log('debug', 'Ignored BroadcastChannel message with unknown action', { data: { action } });\n }\n };\n }\n\n private shareSession(sessionId: string): void {\n if (this.broadcastChannel && typeof this.broadcastChannel.postMessage === 'function') {\n this.broadcastChannel.postMessage({\n action: 'session_start',\n projectId: this.getProjectId(),\n sessionId,\n timestamp: Date.now(),\n });\n }\n }\n\n private cleanupCrossTabSync(): void {\n if (this.broadcastChannel) {\n if (typeof this.broadcastChannel.close === 'function') {\n this.broadcastChannel.close();\n }\n this.broadcastChannel = null;\n }\n }\n\n private recoverSession(): string | null {\n const storedSession = this.loadStoredSession();\n\n if (!storedSession) {\n return null;\n }\n\n // Validate session ID format: {timestamp}-{9-char-base36}\n if (!SESSION_ID_PATTERN.test(storedSession.id)) {\n log('warn', 'Invalid session ID format recovered from storage, clearing', {\n data: { sessionId: storedSession.id },\n });\n this.clearStoredSession();\n return null;\n }\n\n const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n\n if (Date.now() - storedSession.lastActivity > sessionTimeout) {\n this.clearStoredSession();\n return null;\n }\n\n return storedSession.id;\n }\n\n private persistSession(\n sessionId: string,\n lastActivity: number = Date.now(),\n referrer?: string,\n utm?: UTM,\n clickIds?: ClickIds,\n ): void {\n this.saveStoredSession({\n id: sessionId,\n lastActivity,\n ...(referrer && { referrer }),\n ...(utm && { utm }),\n ...(clickIds && { clickIds }),\n });\n }\n\n private clearStoredSession(): void {\n const storageKey = this.getSessionStorageKey();\n this.storageManager.removeItem(storageKey);\n // sessionStorage mirror is intentionally NOT cleared here.\n // It persists to enable recovery after external redirects (payment processors, OAuth).\n // Stale data is rejected by the timeout check in recoverSession().\n // sessionStorage auto-clears when the tab closes.\n }\n\n private loadStoredSession(): StoredSessionData | null {\n const storageKey = this.getSessionStorageKey();\n\n // Primary: localStorage (cross-tab, persistent)\n const localData = this.storageManager.getItem(storageKey);\n if (localData !== null) {\n try {\n const parsed = JSON.parse(localData) as StoredSessionData;\n if (parsed.id && typeof parsed.lastActivity === 'number') {\n return parsed;\n }\n } catch {\n this.storageManager.removeItem(storageKey);\n }\n }\n\n // Fallback: sessionStorage (survives same-tab external redirects)\n const sessionData = this.storageManager.getSessionItem(storageKey);\n if (sessionData !== null) {\n try {\n const parsed = JSON.parse(sessionData) as StoredSessionData;\n if (parsed.id && typeof parsed.lastActivity === 'number') {\n return parsed;\n }\n } catch {\n this.storageManager.removeSessionItem(storageKey);\n }\n }\n\n return null;\n }\n\n private saveStoredSession(session: StoredSessionData): void {\n const storageKey = this.getSessionStorageKey();\n const data = JSON.stringify(session);\n this.storageManager.setItem(storageKey, data);\n this.storageManager.setSessionItem(storageKey, data);\n }\n\n private getSessionStorageKey(): string {\n return SESSION_STORAGE_KEY(this.getProjectId());\n }\n\n private getProjectId(): string {\n return this.projectId;\n }\n\n /**\n * Starts session tracking with lifecycle management and cross-tab synchronization.\n *\n * **Purpose**: Initializes session tracking, creating or recovering a session ID,\n * setting up activity listeners, and enabling cross-tab synchronization.\n *\n * **Flow**:\n * 1. Checks if tracking already active (idempotent)\n * 2. Attempts to recover session from localStorage\n * 3. If no recovery: Generates new session ID (`{timestamp}-{9-char-base36}`)\n * 4. Sets `sessionId` in global state\n * 5. Persists session to localStorage\n * 6. Initializes BroadcastChannel for cross-tab sync (BEFORE SESSION_START)\n * 7. Shares session via BroadcastChannel (notifies other tabs)\n * 8. If NOT recovered: Tracks SESSION_START event\n * 9. Sets up inactivity timeout (default 15 minutes)\n * 10. Sets up activity listeners (click, keydown, scroll)\n * 11. Sets up lifecycle listeners (visibilitychange, beforeunload)\n *\n * **Session Recovery**:\n * - Checks localStorage for existing session (primary)\n * - Falls back to sessionStorage mirror (survives external redirects)\n * - Recovers if session exists and is recent (within timeout window)\n * - NO SESSION_START event if session recovered\n *\n * **Error Handling**:\n * - On error: Rolls back all setup (cleanup listeners, timers, state)\n * - Re-throws error to caller (App.init() handles failure)\n *\n * **BroadcastChannel Initialization Order**:\n * - CRITICAL: BroadcastChannel initialized BEFORE SESSION_START event\n * - Prevents race condition with secondary tabs\n * - Ensures secondary tabs can receive session_start message\n *\n * **Pre-rendering**:\n * - On a pre-rendered page (`document.prerendering === true`), every observable side\n * effect (persistence, cross-tab sync, SESSION_START, listeners) is deferred to the\n * `prerenderingchange` activation event via `activateSession()`. `sessionId` is still\n * set in state synchronously so `init()` returns a real id. A prerender that is never\n * activated persists and emits nothing.\n *\n * **Called by**: `SessionHandler.startTracking()` during `App.init()`\n *\n * **Important**: After successful call, `sessionId` is available in global state\n * and EventManager can flush pending events via `flushPendingEvents()`.\n *\n * @throws Error if initialization fails (rolled back automatically)\n *\n * @example\n * ```typescript\n * sessionManager.startTracking();\n * // → Session created: '1704896400000-a3b4c5d6e'\n * // → SESSION_START event tracked\n * // → Activity listeners active\n * // → Cross-tab sync enabled\n * ```\n *\n * @see src/managers/README.md (lines 140-169) for session management details\n */\n startTracking(): void {\n if (this.isTracking) {\n log('debug', 'Session tracking already active');\n return;\n }\n\n const recoveredSessionId = this.recoverSession();\n const sessionId = recoveredSessionId ?? this.generateSessionId();\n\n // Capture or recover attribution data (referrer/UTM/click-ids)\n let sessionReferrer: string;\n let sessionUtm: UTM | undefined;\n let sessionClickIds: ClickIds | undefined;\n\n if (recoveredSessionId) {\n // Session recovered: load attribution from storage (with fallback to current context)\n const storedSession = this.loadStoredSession();\n sessionReferrer = storedSession?.referrer ?? getExternalReferrer();\n sessionUtm = storedSession?.utm ?? getUTMParameters();\n sessionClickIds = storedSession?.clickIds ?? getClickIds();\n } else {\n // New session: capture from current page context\n sessionReferrer = getExternalReferrer();\n sessionUtm = getUTMParameters();\n sessionClickIds = getClickIds();\n }\n\n log('debug', 'Session tracking initialized', {\n data: {\n sessionId,\n wasRecovered: !!recoveredSessionId,\n willEmitSessionStart: !recoveredSessionId,\n sessionReferrer,\n hasUtm: !!sessionUtm,\n hasClickIds: !!sessionClickIds,\n },\n });\n\n this.isTracking = true;\n\n try {\n this.set('sessionId', sessionId);\n this.set('sessionReferrer', sessionReferrer);\n this.set('sessionUtm', sessionUtm);\n this.set('sessionClickIds', sessionClickIds);\n\n // Pre-render guard. The server upserts a session from ANY event carrying a\n // session_id (not only SESSION_START), and a persisted session can later be\n // recovered by a real visit — so a page that is pre-rendered but never activated\n // must persist NOTHING and emit NOTHING until activation, or it would seed a\n // phantom server-side session (via the first PAGE_VIEW) or be mis-attributed when\n // a later real navigation recovers the stale record. We defer every observable\n // side effect — persistence, cross-tab broadcast, SESSION_START and the session\n // listeners — to the `prerenderingchange` event; App.initializeHandlers() defers\n // the interaction handlers (page view, click, ...) in parallel. `sessionId` is set\n // in state above so init() still returns a real id synchronously. This listener is\n // registered before App's, so SESSION_START is emitted before the first PAGE_VIEW\n // on activation.\n if (isPrerendering()) {\n this.prerenderActivationHandler = (): void => {\n this.prerenderActivationHandler = null;\n this.activateSession(sessionId, recoveredSessionId, sessionReferrer, sessionUtm, sessionClickIds);\n };\n document.addEventListener('prerenderingchange', this.prerenderActivationHandler, { once: true });\n return;\n }\n\n this.activateSession(sessionId, recoveredSessionId, sessionReferrer, sessionUtm, sessionClickIds);\n } catch (error) {\n this.isTracking = false;\n this.clearSessionTimeout();\n this.cleanupActivityListeners();\n this.cleanupLifecycleListeners();\n this.cleanupCrossTabSync();\n this.set('sessionId', null);\n\n throw error;\n }\n }\n\n /**\n * Commits all observable session side effects: persistence, cross-tab sync, the\n * SESSION_START emit (new sessions only) and the activity/lifecycle/timeout listeners.\n *\n * Runs synchronously on a normal page load. On a pre-rendered page it is deferred to\n * the `prerenderingchange` (activation) event, so a prerender that is never activated\n * persists nothing and emits nothing.\n *\n * BroadcastChannel is initialized before SESSION_START so secondary tabs can receive\n * the `session_start` message (avoids a cross-tab race).\n */\n private activateSession(\n sessionId: string,\n recoveredSessionId: string | null,\n referrer: string,\n utm: UTM | undefined,\n clickIds: ClickIds | undefined,\n ): void {\n this.persistSession(sessionId, Date.now(), referrer, utm, clickIds);\n this.initCrossTabSync();\n this.shareSession(sessionId);\n\n // Only emit SESSION_START for NEW sessions (not recovered). Recovered sessions\n // already had their SESSION_START tracked on initial creation; the server infers\n // session end from the last event timestamp (v2.0+).\n if (!recoveredSessionId) {\n log('debug', 'Emitting SESSION_START event', { data: { sessionId } });\n this.eventManager.track({ type: EventType.SESSION_START });\n } else {\n log('debug', 'Session recovered, skipping SESSION_START', { data: { sessionId } });\n }\n\n this.setupSessionTimeout();\n this.setupActivityListeners();\n this.setupLifecycleListeners();\n }\n\n private generateSessionId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private setupSessionTimeout(): void {\n this.clearSessionTimeout();\n\n const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n\n this.sessionTimeoutId = setTimeout(() => {\n this.enterRenewalMode();\n }, sessionTimeout);\n }\n\n private resetSessionTimeout(): void {\n this.setupSessionTimeout();\n const sessionId = this.get('sessionId') as string;\n if (sessionId) {\n this.persistSession(\n sessionId,\n Date.now(),\n this.get('sessionReferrer'),\n this.get('sessionUtm'),\n this.get('sessionClickIds'),\n );\n }\n }\n\n private clearSessionTimeout(): void {\n if (this.sessionTimeoutId) {\n clearTimeout(this.sessionTimeoutId);\n this.sessionTimeoutId = null;\n }\n }\n\n private setupActivityListeners(): void {\n this.activityHandler = (): void => {\n if (this.needsRenewal) {\n this.renewSession();\n } else {\n this.resetSessionTimeout();\n }\n };\n\n document.addEventListener('click', this.activityHandler, { passive: true });\n document.addEventListener('keydown', this.activityHandler, { passive: true });\n document.addEventListener('scroll', this.activityHandler, { passive: true });\n }\n\n /**\n * Renews the session after timeout when user returns.\n * Creates a new session ID and emits SESSION_START.\n */\n private renewSession(): void {\n this.needsRenewal = false;\n\n const newSessionId = this.generateSessionId();\n const sessionReferrer = getExternalReferrer();\n const sessionUtm = getUTMParameters();\n const sessionClickIds = getClickIds();\n\n log('debug', 'Renewing session after timeout', {\n data: { newSessionId },\n });\n\n this.set('sessionId', newSessionId);\n this.set('sessionReferrer', sessionReferrer);\n this.set('sessionUtm', sessionUtm);\n this.set('sessionClickIds', sessionClickIds);\n this.persistSession(newSessionId, Date.now(), sessionReferrer, sessionUtm, sessionClickIds);\n\n // Re-initialize cross-tab sync with new session\n this.cleanupCrossTabSync();\n this.initCrossTabSync();\n this.shareSession(newSessionId);\n\n this.eventManager.track({\n type: EventType.SESSION_START,\n });\n\n // Flush any events that were buffered during renewal mode\n this.eventManager.flushPendingEvents();\n\n this.setupSessionTimeout();\n }\n\n private cleanupActivityListeners(): void {\n if (this.activityHandler) {\n document.removeEventListener('click', this.activityHandler);\n document.removeEventListener('keydown', this.activityHandler);\n document.removeEventListener('scroll', this.activityHandler);\n this.activityHandler = null;\n }\n }\n\n private setupLifecycleListeners(): void {\n if (this.visibilityChangeHandler) {\n return;\n }\n\n this.visibilityChangeHandler = (): void => {\n if (document.hidden) {\n this.clearSessionTimeout();\n } else {\n // Check if session expired during browser suspend/hibernate\n if (this.isSessionStale()) {\n log('debug', 'Session expired during suspend, entering renewal mode');\n this.enterRenewalMode();\n return;\n }\n\n const sessionId = this.get('sessionId');\n if (sessionId) {\n this.setupSessionTimeout();\n }\n }\n };\n\n document.addEventListener('visibilitychange', this.visibilityChangeHandler);\n }\n\n /**\n * Checks if the current session has become stale (expired during browser suspend).\n * This handles the case where JavaScript timers are paused during suspend/hibernate.\n */\n private isSessionStale(): boolean {\n // If already in renewal mode, no need to check\n if (this.needsRenewal) {\n return false;\n }\n\n const sessionId = this.get('sessionId');\n if (!sessionId) {\n return false;\n }\n\n const storedSession = this.loadStoredSession();\n if (!storedSession) {\n return false;\n }\n\n const sessionTimeout = this.get('config')?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;\n return Date.now() - storedSession.lastActivity > sessionTimeout;\n }\n\n private cleanupLifecycleListeners(): void {\n if (this.visibilityChangeHandler) {\n document.removeEventListener('visibilitychange', this.visibilityChangeHandler);\n this.visibilityChangeHandler = null;\n }\n }\n\n /**\n * Enters renewal mode after session timeout.\n * Keeps activity listeners active to detect when user returns.\n * Called by session timeout timer.\n */\n private enterRenewalMode(): void {\n this.clearSessionTimeout();\n this.cleanupCrossTabSync();\n this.clearStoredSession();\n\n this.set('sessionId', null);\n this.set('hasStartSession', false);\n this.set('sessionReferrer', undefined);\n this.set('sessionUtm', undefined);\n this.set('sessionClickIds', undefined);\n\n // Keep activity listeners active but switch to renewal mode\n this.needsRenewal = true;\n\n log('debug', 'Session timed out, entering renewal mode');\n }\n\n /**\n * Fully resets session state and cleans up all resources.\n * Called by stopTracking() for explicit session termination.\n */\n private resetSessionState(): void {\n this.clearSessionTimeout();\n this.cleanupActivityListeners();\n this.cleanupLifecycleListeners();\n this.cleanupCrossTabSync();\n this.cleanupPrerenderActivation();\n this.clearStoredSession();\n\n this.set('sessionId', null);\n this.set('hasStartSession', false);\n this.set('sessionReferrer', undefined);\n this.set('sessionUtm', undefined);\n this.set('sessionClickIds', undefined);\n\n this.needsRenewal = false;\n this.isTracking = false;\n }\n\n /**\n * Stops session tracking and cleans up all resources.\n *\n * **Purpose**: Manually stops the current session tracking and cleans up\n * all listeners and timers. Does not emit SESSION_END event (removed in v2.0.0).\n *\n * **Flow**:\n * 1. Clears inactivity timeout\n * 2. Removes activity listeners (click, keydown, scroll)\n * 3. Removes lifecycle listeners (visibilitychange)\n * 4. Closes BroadcastChannel\n * 5. Clears session from localStorage\n * 6. Resets `sessionId` and `hasStartSession` in global state\n * 7. Sets `isTracking` to false\n *\n * **Called by**: `App.destroy()` during application teardown or when session times out\n *\n * **Important**: After calling, session tracking is terminated and cannot be resumed.\n * A new session will be created on next `startTracking()` call.\n *\n * @example\n * ```typescript\n * // Stop session tracking\n * sessionManager.stopTracking();\n * // → All listeners cleaned up\n * // → Session cleared from localStorage\n * // → BroadcastChannel closed\n * // → No SESSION_END event emitted\n * ```\n *\n * @see src/managers/README.md (lines 140-169) for session management details\n */\n stopTracking(): void {\n this.resetSessionState();\n }\n\n /**\n * Destroys the session manager and cleans up all resources.\n *\n * **Purpose**: Performs deep cleanup of session manager resources during\n * application teardown. Preserves session in localStorage for recovery.\n *\n * **Differences from stopTracking()**:\n * - Does NOT clear localStorage (preserves session for recovery)\n * - Used for internal cleanup during teardown\n *\n * **Cleanup Flow**:\n * 1. Clears inactivity timeout\n * 2. Removes activity listeners (click, keydown, scroll)\n * 3. Closes BroadcastChannel\n * 4. Removes lifecycle listeners (visibilitychange)\n * 5. Resets tracking flag (`isTracking`)\n *\n * **Called by**: `App.destroy()` during application teardown\n *\n * @returns void\n *\n * @example\n * ```typescript\n * sessionManager.destroy();\n * // → All resources cleaned up\n * // → Session preserved in localStorage for recovery\n * ```\n */\n destroy(): void {\n this.clearSessionTimeout();\n this.cleanupActivityListeners();\n this.cleanupCrossTabSync();\n this.cleanupLifecycleListeners();\n this.cleanupPrerenderActivation();\n this.isTracking = false;\n this.needsRenewal = false;\n this.set('hasStartSession', false);\n }\n\n /**\n * Removes the pending `prerenderingchange` listener when the manager is torn\n * down before activation (the discarded-prerender case). On the activation path\n * `{ once: true }` removes the listener and the handler nulls its own reference,\n * so this is a no-op then.\n */\n private cleanupPrerenderActivation(): void {\n if (this.prerenderActivationHandler) {\n document.removeEventListener('prerenderingchange', this.prerenderActivationHandler);\n this.prerenderActivationHandler = null;\n }\n }\n}\n","import { EventManager } from '../managers/event.manager';\nimport { SessionManager } from '../managers/session.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { StorageManager } from '../managers/storage.manager';\nimport { log } from '../utils';\n\n/**\n * Wrapper around SessionManager providing consistent handler interface with robust error handling.\n *\n * **Purpose**: Manages user session lifecycle through delegation to SessionManager,\n * adding error recovery, state validation, and event buffer flushing.\n *\n * **Core Functionality**:\n * - Extracts projectId from config (tracelog/custom integrations or 'default')\n * - Creates SessionManager with storage, event manager, and projectId\n * - Flushes pending events after successful session initialization\n * - Automatic cleanup on initialization failures with nested try-catch\n *\n * **Key Features**:\n * - Idempotent operations (safe to call startTracking() multiple times)\n * - Double-destroy protection via destroyed flag\n * - State validation prevents operations on destroyed instances\n * - Centralized cleanup via cleanupSessionManager()\n *\n * **Lifecycle**:\n * - startTracking(): Creates session, sends SESSION_START event\n * - stopTracking(): Cleans up session tracking (no events emitted)\n * - destroy(): Same as stopTracking() (no events emitted in v2.0.0+)\n *\n * @example\n * ```typescript\n * const handler = new SessionHandler(storage, eventManager);\n * handler.startTracking(); // Creates session\n * handler.stopTracking(); // Ends session + cleanup\n * handler.destroy(); // Cleanup only\n * ```\n */\nexport class SessionHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly storageManager: StorageManager;\n private sessionManager: SessionManager | null = null;\n private destroyed = false;\n\n constructor(storageManager: StorageManager, eventManager: EventManager) {\n super();\n this.eventManager = eventManager;\n this.storageManager = storageManager;\n }\n\n /**\n * Starts session tracking by creating SessionManager and initializing session.\n *\n * **Behavior**:\n * - Extracts projectId from config (tracelog projectId or custom collectApiUrl or 'default')\n * - Creates SessionManager instance with storage, event manager, and projectId\n * - Calls SessionManager.startTracking() to begin session lifecycle\n * - Flushes pending events buffered during initialization\n * - Idempotent: Early return if session already active\n * - Validates state: Warns and returns if handler destroyed\n *\n * **Error Handling**:\n * - On failure: Automatically cleans up SessionManager via nested try-catch\n * - Leaves handler in clean, reusable state after error\n * - Re-throws error after logging\n *\n * @throws {Error} If SessionManager initialization fails\n */\n startTracking(): void {\n if (this.isActive()) {\n return;\n }\n\n if (this.destroyed) {\n log('debug', 'Cannot start tracking on destroyed handler');\n return;\n }\n\n const config = this.get('config');\n const projectId = config?.integrations?.tracelog?.projectId ?? 'custom';\n\n try {\n this.sessionManager = new SessionManager(this.storageManager, this.eventManager, projectId);\n this.sessionManager.startTracking();\n\n this.eventManager.flushPendingEvents();\n } catch (error) {\n if (this.sessionManager) {\n try {\n this.sessionManager.destroy();\n } catch {\n /* empty */\n }\n this.sessionManager = null;\n }\n\n log('error', 'Failed to start session tracking', { error });\n throw error;\n }\n }\n\n private isActive(): boolean {\n return this.sessionManager !== null && !this.destroyed;\n }\n\n private cleanupSessionManager(): void {\n if (this.sessionManager) {\n this.sessionManager.stopTracking();\n this.sessionManager.destroy();\n this.sessionManager = null;\n }\n }\n\n /**\n * Stops session tracking by cleaning up resources.\n *\n * **Purpose**: Terminates session tracking and removes all listeners and timers.\n * No events are emitted (SESSION_END removed in v2.0.0).\n *\n * **Behavior**:\n * - Calls SessionManager.stopTracking() to clean up listeners\n * - Calls SessionManager.destroy() to finalize cleanup\n * - Safe to call multiple times (idempotent via cleanupSessionManager)\n *\n * **Note**: In v2.0.0+, this method only performs cleanup without emitting events.\n * Session end time is inferred server-side from last event timestamp.\n */\n stopTracking(): void {\n this.cleanupSessionManager();\n }\n\n /**\n * Destroys handler and cleans up SessionManager.\n *\n * **Purpose**: Same as stopTracking() in v2.0.0+. Both methods perform cleanup\n * without emitting events.\n *\n * **Behavior**:\n * - Idempotent: Early return if already destroyed\n * - Calls SessionManager.destroy() to clean up listeners and timers\n * - Sets sessionManager to null and destroyed flag to true\n *\n * **Note**: In v2.0.0+, there is no functional difference between stopTracking()\n * and destroy(). Both perform cleanup without emitting SESSION_END events.\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n\n if (this.sessionManager) {\n this.sessionManager.destroy();\n this.sessionManager = null;\n }\n\n this.destroyed = true;\n }\n}\n","import { EventType, PageViewData } from '../types';\nimport { normalizeUrl } from '../utils';\nimport { StateManager } from '../managers/state.manager';\nimport { EventManager } from '../managers/event.manager';\nimport { DEFAULT_PAGE_VIEW_THROTTLE_MS } from '../constants/config.constants';\n\n/**\n * Tracks page navigation and route changes in single-page applications.\n *\n * **Events Generated**: `page_view`\n *\n * **Features**:\n * - Tracks initial page load, browser navigation, hash changes, and History API calls\n * - URL normalization with automatic filtering of sensitive query parameters\n * - Deduplication to prevent consecutive duplicate events\n * - Configurable throttling (default: 1 second)\n * - SPA navigation detection via History API patching\n *\n * **Privacy Protection**:\n * - Automatically removes 15 common sensitive params (token, auth, key, password, etc.)\n * - User-configurable additional sensitive parameters via config\n *\n * @see src/handlers/README.md (lines 5-63) for detailed documentation\n */\nexport class PageViewHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly onTrack: () => void;\n\n private originalPushState?: typeof window.history.pushState;\n private originalReplaceState?: typeof window.history.replaceState;\n private lastPageViewTime = 0;\n\n constructor(eventManager: EventManager, onTrack: () => void) {\n super();\n\n this.eventManager = eventManager;\n this.onTrack = onTrack;\n }\n\n /**\n * Starts tracking page views.\n *\n * - Tracks initial page load first (via trackInitialPageView)\n * - Attaches popstate and hashchange event listeners\n * - Patches History API methods (pushState, replaceState) for SPA navigation\n * - All setup happens synchronously\n *\n * **Note**: onTrack() callback is invoked AFTER initial page view but BEFORE\n * subsequent navigation events for session management coordination.\n */\n startTracking(): void {\n this.trackInitialPageView();\n\n window.addEventListener('popstate', this.trackCurrentPage, true);\n window.addEventListener('hashchange', this.trackCurrentPage, true);\n\n this.patchHistory('pushState');\n this.patchHistory('replaceState');\n }\n\n /**\n * Stops tracking page views and restores original History API methods.\n *\n * - Removes event listeners (popstate, hashchange)\n * - Restores original pushState and replaceState methods\n * - Resets throttling state\n */\n stopTracking(): void {\n window.removeEventListener('popstate', this.trackCurrentPage, true);\n window.removeEventListener('hashchange', this.trackCurrentPage, true);\n\n if (this.originalPushState) {\n window.history.pushState = this.originalPushState;\n }\n\n if (this.originalReplaceState) {\n window.history.replaceState = this.originalReplaceState;\n }\n\n this.lastPageViewTime = 0;\n }\n\n private patchHistory(method: 'pushState' | 'replaceState'): void {\n const original = window.history[method];\n\n if (method === 'pushState' && !this.originalPushState) {\n this.originalPushState = original;\n } else if (method === 'replaceState' && !this.originalReplaceState) {\n this.originalReplaceState = original;\n }\n\n window.history[method] = (...args: [unknown, string, string | URL | null | undefined]): void => {\n original.apply(window.history, args);\n this.trackCurrentPage();\n };\n }\n\n private readonly trackCurrentPage = (): void => {\n const rawUrl = window.location.href;\n const normalizedUrl = normalizeUrl(rawUrl, this.get('config').sensitiveQueryParams);\n\n if (this.get('pageUrl') === normalizedUrl) {\n return;\n }\n\n const now = Date.now();\n const throttleMs = this.get('config').pageViewThrottleMs ?? DEFAULT_PAGE_VIEW_THROTTLE_MS;\n\n if (now - this.lastPageViewTime < throttleMs) {\n return;\n }\n\n this.lastPageViewTime = now;\n\n this.onTrack();\n\n const fromUrl = this.get('pageUrl');\n\n this.set('pageUrl', normalizedUrl);\n\n const pageViewData = this.extractPageViewData();\n this.eventManager.track({\n type: EventType.PAGE_VIEW,\n page_url: this.get('pageUrl'),\n from_page_url: fromUrl,\n ...(pageViewData && { page_view: pageViewData }),\n });\n\n // Flush the queue now (previous-route events + this PAGE_VIEW) so the\n // batch lands before the user can close the tab on the next route.\n if (this.get('config').flushOnSpaNavigation === true) {\n void this.eventManager.flushImmediately();\n }\n };\n\n private trackInitialPageView(): void {\n const normalizedUrl = normalizeUrl(window.location.href, this.get('config').sensitiveQueryParams);\n const pageViewData = this.extractPageViewData();\n\n this.lastPageViewTime = Date.now();\n\n this.eventManager.track({\n type: EventType.PAGE_VIEW,\n page_url: normalizedUrl,\n ...(pageViewData && { page_view: pageViewData }),\n });\n\n this.onTrack();\n }\n\n private extractPageViewData(): PageViewData | undefined {\n const { referrer } = document;\n const { title } = document;\n\n if (!referrer && !title) {\n return undefined;\n }\n\n return {\n ...(referrer && { referrer }),\n ...(title && { title }),\n };\n }\n}\n","import {\n HTML_DATA_ATTR_PREFIX,\n MAX_TEXT_LENGTH,\n INTERACTIVE_SELECTORS,\n DEFAULT_CLICK_THROTTLE_MS,\n MAX_THROTTLE_CACHE_ENTRIES,\n THROTTLE_ENTRY_TTL_MS,\n THROTTLE_PRUNE_INTERVAL_MS,\n} from '../constants';\nimport { ClickCoordinates, ClickData, ClickTrackingElementData, EventType } from '../types';\nimport { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { log, sanitizePii } from '../utils';\n\n/**\n * Captures mouse clicks and converts them into analytics events with element context and coordinates.\n *\n * **Features**:\n * - Smart element detection via INTERACTIVE_SELECTORS (29 selectors including buttons, links, form elements, ARIA roles)\n * - Relative coordinates calculation (0-1 scale, 3 decimal precision, clamped)\n * - Custom event tracking via `data-tlog-name` attributes\n * - Text extraction with length limits (255 chars max) and priority logic\n * - PII sanitization (emails, phone numbers, credit cards, API keys, tokens)\n * - Privacy controls via `data-tlog-ignore` attribute\n * - Per-element click throttling (default 300ms) with memory management (TTL + LRU)\n *\n * **Events Generated**: `click`, `custom` (for elements with data-tlog-name)\n *\n * **Triggers**: Capture-phase click events on document\n *\n * **Memory Management**:\n * - TTL-based pruning: 5-minute TTL with automatic cleanup\n * - LRU eviction: Maintains maximum 1000 throttle entries\n * - Rate-limited pruning: Runs every 30 seconds\n *\n * @example\n * ```typescript\n * const handler = new ClickHandler(eventManager);\n * handler.startTracking();\n * // Clicks are now tracked automatically\n * handler.stopTracking();\n * ```\n */\nexport class ClickHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly lastClickTimes: Map<string, number> = new Map();\n private clickHandler?: (event: Event) => void;\n private lastPruneTime = 0;\n\n constructor(eventManager: EventManager) {\n super();\n\n this.eventManager = eventManager;\n }\n\n /**\n * Starts tracking click events on the document.\n *\n * Attaches a single capture-phase click listener to window that:\n * - Detects interactive elements or falls back to clicked element\n * - Applies click throttling per element (configurable, default 300ms)\n * - Extracts custom tracking data from data-tlog-name attributes\n * - Generates both custom events (for tracked elements) and click events\n * - Respects data-tlog-ignore privacy controls\n * - Sanitizes text content for PII protection\n *\n * Idempotent: Safe to call multiple times (early return if already tracking).\n */\n startTracking(): void {\n if (this.clickHandler) {\n return;\n }\n\n this.clickHandler = (event: Event): void => {\n const mouseEvent = event as MouseEvent;\n const target = mouseEvent.target;\n const clickedElement =\n typeof HTMLElement !== 'undefined' && target instanceof HTMLElement\n ? target\n : typeof HTMLElement !== 'undefined' && target instanceof Node && target.parentElement instanceof HTMLElement\n ? target.parentElement\n : null;\n\n if (!clickedElement) {\n log('debug', 'Click target not found or not an element');\n return;\n }\n\n if (this.shouldIgnoreElement(clickedElement)) {\n return;\n }\n\n // Throttle clicks per element to prevent double-clicks and spam\n const clickThrottleMs = this.get('config')?.clickThrottleMs ?? DEFAULT_CLICK_THROTTLE_MS;\n if (clickThrottleMs > 0 && !this.checkClickThrottle(clickedElement, clickThrottleMs)) {\n return;\n }\n\n const trackingElement = this.findTrackingElement(clickedElement);\n const relevantClickElement = this.getRelevantClickElement(clickedElement);\n const coordinates = this.calculateClickCoordinates(mouseEvent);\n\n // Order matters: the `data-tlog-name` CUSTOM event runs BEFORE the\n // synthetic-coordinate guard below, so a programmatic `element.click()`\n // on a tracked element still fires its explicit opt-in custom event. The\n // implicit CLICK event is suppressed only for synthetic clicks to avoid\n // polluting heatmaps with `(0, 0)` coordinates.\n if (trackingElement) {\n const trackingData = this.extractTrackingData(trackingElement);\n\n if (trackingData) {\n const attributeData = this.createCustomEventData(trackingData);\n\n this.eventManager.track({\n type: EventType.CUSTOM,\n custom_event: {\n name: attributeData.name,\n ...(attributeData.value && { metadata: { value: attributeData.value } }),\n },\n });\n }\n }\n\n if (!coordinates) {\n log('debug', 'Click skipped: invalid coordinates (likely synthetic)');\n return;\n }\n\n const clickData = this.generateClickData(clickedElement, relevantClickElement, coordinates);\n\n this.eventManager.track({\n type: EventType.CLICK,\n click_data: clickData,\n });\n };\n\n window.addEventListener('click', this.clickHandler, true);\n }\n\n /**\n * Stops tracking click events and cleans up resources.\n *\n * Removes the click event listener, clears throttle cache, and resets prune timer.\n * Prevents memory leaks by properly cleaning up all state.\n */\n stopTracking(): void {\n if (this.clickHandler) {\n window.removeEventListener('click', this.clickHandler, true);\n this.clickHandler = undefined;\n }\n this.lastClickTimes.clear();\n this.lastPruneTime = 0;\n }\n\n private shouldIgnoreElement(element: HTMLElement): boolean {\n if (element.hasAttribute(`${HTML_DATA_ATTR_PREFIX}-ignore`)) {\n return true;\n }\n\n const parent = element.closest(`[${HTML_DATA_ATTR_PREFIX}-ignore]`);\n\n return parent !== null;\n }\n\n /**\n * Checks per-element click throttling to prevent double-clicks and rapid spam\n * Returns true if the click should be tracked, false if throttled\n */\n private checkClickThrottle(element: HTMLElement, throttleMs: number): boolean {\n const signature = this.getElementSignature(element);\n const now = Date.now();\n\n this.pruneThrottleCache(now);\n\n const lastClickTime = this.lastClickTimes.get(signature);\n\n if (lastClickTime !== undefined && now - lastClickTime < throttleMs) {\n log('debug', 'ClickHandler: Click suppressed by throttle', {\n data: {\n signature,\n throttleRemaining: throttleMs - (now - lastClickTime),\n },\n });\n return false;\n }\n\n this.lastClickTimes.set(signature, now);\n return true;\n }\n\n /**\n * Prunes stale entries from the throttle cache to prevent memory leaks\n * Uses TTL-based eviction (5 minutes) and enforces max size limit\n * Called during checkClickThrottle with built-in rate limiting (every 30 seconds)\n */\n private pruneThrottleCache(now: number): void {\n if (now - this.lastPruneTime < THROTTLE_PRUNE_INTERVAL_MS) {\n return;\n }\n\n this.lastPruneTime = now;\n const cutoff = now - THROTTLE_ENTRY_TTL_MS;\n\n for (const [key, timestamp] of this.lastClickTimes.entries()) {\n if (timestamp < cutoff) {\n this.lastClickTimes.delete(key);\n }\n }\n\n if (this.lastClickTimes.size > MAX_THROTTLE_CACHE_ENTRIES) {\n const entries = Array.from(this.lastClickTimes.entries()).sort((a, b) => a[1] - b[1]);\n\n const excessCount = this.lastClickTimes.size - MAX_THROTTLE_CACHE_ENTRIES;\n const toDelete = entries.slice(0, excessCount);\n\n for (const [key] of toDelete) {\n this.lastClickTimes.delete(key);\n }\n\n log('debug', 'ClickHandler: Pruned throttle cache', {\n data: {\n removed: toDelete.length,\n remaining: this.lastClickTimes.size,\n },\n });\n }\n }\n\n /**\n * Creates a stable signature for an element to track throttling\n * Priority: id > data-testid > data-tlog-name > DOM path\n */\n private getElementSignature(element: HTMLElement): string {\n if (element.id) {\n return `#${element.id}`;\n }\n\n const testId = element.getAttribute('data-testid');\n if (testId) {\n return `[data-testid=\"${testId}\"]`;\n }\n\n const tlogName = element.getAttribute(`${HTML_DATA_ATTR_PREFIX}-name`);\n if (tlogName) {\n return `[${HTML_DATA_ATTR_PREFIX}-name=\"${tlogName}\"]`;\n }\n\n return this.getElementPath(element);\n }\n\n /**\n * Generates a DOM path for an element (e.g., \"body>div>button\")\n */\n private getElementPath(element: HTMLElement): string {\n const path: string[] = [];\n let current: HTMLElement | null = element;\n\n while (current && current !== document.body) {\n let selector = current.tagName.toLowerCase();\n\n if (current.className) {\n const firstClass = current.className.split(' ')[0];\n if (firstClass) {\n selector += `.${firstClass}`;\n }\n }\n\n path.unshift(selector);\n current = current.parentElement;\n }\n\n return path.join('>') || 'unknown';\n }\n\n private findTrackingElement(element: HTMLElement): HTMLElement | undefined {\n if (element.hasAttribute(`${HTML_DATA_ATTR_PREFIX}-name`)) {\n return element;\n }\n\n const closest = element.closest(`[${HTML_DATA_ATTR_PREFIX}-name]`) as HTMLElement;\n\n return closest;\n }\n\n private getRelevantClickElement(element: HTMLElement): HTMLElement {\n for (const selector of INTERACTIVE_SELECTORS) {\n try {\n if (element.matches(selector)) {\n return element;\n }\n\n const parent = element.closest(selector) as HTMLElement;\n\n if (parent) {\n return parent;\n }\n } catch (error) {\n log('debug', 'Invalid selector in element search', { error, data: { selector } });\n continue;\n }\n }\n\n return element;\n }\n\n private calculateClickCoordinates(event: MouseEvent): ClickCoordinates | null {\n const x = event.clientX;\n const y = event.clientY;\n\n if (typeof x !== 'number' || typeof y !== 'number' || !Number.isFinite(x) || !Number.isFinite(y)) {\n return null;\n }\n\n // Reject (0, 0) only when the event was not user-generated. `element.click()`\n // and `dispatchEvent(new MouseEvent(...))` both produce `isTrusted === false`\n // and default coords to (0, 0). A real user clicking the top-left pixel\n // produces `isTrusted === true`, so this preserves legitimate corner clicks\n // while filtering programmatic noise.\n if (x === 0 && y === 0 && !event.isTrusted) {\n return null;\n }\n\n return { x, y };\n }\n\n private extractTrackingData(trackingElement: HTMLElement): ClickTrackingElementData | undefined {\n const name = trackingElement.getAttribute(`${HTML_DATA_ATTR_PREFIX}-name`);\n const value = trackingElement.getAttribute(`${HTML_DATA_ATTR_PREFIX}-value`);\n\n if (!name) {\n return undefined;\n }\n\n return {\n element: trackingElement,\n name,\n ...(value && { value }),\n };\n }\n\n private generateClickData(\n clickedElement: HTMLElement,\n relevantElement: HTMLElement,\n coordinates: ClickCoordinates,\n ): ClickData {\n const { x, y } = coordinates;\n const text = this.getRelevantText(clickedElement, relevantElement);\n const href = relevantElement.getAttribute('href') ?? undefined;\n\n return {\n x,\n y,\n tag: relevantElement.tagName.toLowerCase(),\n ...(relevantElement.id && { id: relevantElement.id }),\n ...(relevantElement.className && { class: relevantElement.className }),\n ...(text && { text }),\n ...(href && { href }),\n };\n }\n\n private getRelevantText(clickedElement: HTMLElement, relevantElement: HTMLElement): string {\n const clickedText = clickedElement.textContent?.trim() ?? '';\n const relevantText = relevantElement.textContent?.trim() ?? '';\n\n if (!clickedText && !relevantText) {\n return '';\n }\n\n let finalText = '';\n\n if (clickedText && clickedText.length <= MAX_TEXT_LENGTH) {\n finalText = clickedText;\n } else if (relevantText.length <= MAX_TEXT_LENGTH) {\n finalText = relevantText;\n } else {\n finalText = relevantText.slice(0, MAX_TEXT_LENGTH - 3) + '...';\n }\n\n return sanitizePii(finalText);\n }\n\n private createCustomEventData(trackingData: ClickTrackingElementData): { name: string; value?: string } {\n return {\n name: trackingData.name,\n ...(trackingData.value && { value: trackingData.value }),\n };\n }\n}\n","import {\n MAX_SCROLL_EVENTS_PER_SESSION,\n MIN_SCROLL_DEPTH_CHANGE,\n SCROLL_DEBOUNCE_TIME_MS,\n SCROLL_MIN_EVENT_INTERVAL_MS,\n SIGNIFICANT_SCROLL_DELTA,\n} from '../constants';\nimport { EventType, ScrollData, ScrollDirection } from '../types';\nimport { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { log } from '../utils';\n\ninterface ScrollContainer {\n element: Window | HTMLElement;\n selector: string;\n lastScrollPos: number;\n lastDepth: number;\n lastEventTime: number;\n debounceTimer: number | null;\n listener: EventListener;\n}\n\n/**\n * Tracks scroll depth and direction across the window and any detected scrollable containers.\n *\n * **Captured fields**: `depth` (0-100), `direction` (up/down), `container_selector`.\n *\n * **Guardrails**:\n * - Significant movement (minimum 10px position delta)\n * - Depth change (minimum 5% delta between events)\n * - Rate limiting (minimum 500ms interval between events per container)\n * - Session cap (maximum 120 events per session)\n * - Multi-container support with 250ms per-container debouncing\n */\nexport class ScrollHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly containers: ScrollContainer[] = [];\n private limitWarningLogged = false;\n private containerDiscoveryTimeoutId: number | null = null;\n\n constructor(eventManager: EventManager) {\n super();\n this.eventManager = eventManager;\n }\n\n startTracking(): void {\n this.limitWarningLogged = false;\n this.set('scrollEventCount', 0);\n this.tryDetectScrollContainers(0);\n }\n\n stopTracking(): void {\n if (this.containerDiscoveryTimeoutId !== null) {\n clearTimeout(this.containerDiscoveryTimeoutId);\n this.containerDiscoveryTimeoutId = null;\n }\n\n for (const container of this.containers) {\n this.clearContainerTimer(container);\n\n if (container.element === window) {\n window.removeEventListener('scroll', container.listener);\n } else {\n (container.element as HTMLElement).removeEventListener('scroll', container.listener);\n }\n }\n\n this.containers.length = 0;\n this.set('scrollEventCount', 0);\n this.limitWarningLogged = false;\n }\n\n private tryDetectScrollContainers(attempt: number): void {\n const elements = this.findScrollableElements();\n\n if (this.isWindowScrollable()) {\n this.setupScrollContainer(window, 'window');\n }\n\n if (elements.length > 0) {\n for (const element of elements) {\n const selector = this.getElementSelector(element);\n this.setupScrollContainer(element, selector);\n }\n return;\n }\n\n if (attempt < 5) {\n this.containerDiscoveryTimeoutId = window.setTimeout(() => {\n this.containerDiscoveryTimeoutId = null;\n this.tryDetectScrollContainers(attempt + 1);\n }, 200);\n\n return;\n }\n\n if (this.containers.length === 0) {\n this.setupScrollContainer(window, 'window');\n }\n }\n\n private findScrollableElements(): HTMLElement[] {\n if (!document.body) {\n return [];\n }\n\n const elements: HTMLElement[] = [];\n\n const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {\n acceptNode: (node) => {\n const element = node as HTMLElement;\n\n if (!element.isConnected || !element.offsetParent) {\n return NodeFilter.FILTER_SKIP;\n }\n\n const style = getComputedStyle(element);\n\n const hasVerticalScrollableStyle =\n style.overflowY === 'auto' ||\n style.overflowY === 'scroll' ||\n style.overflow === 'auto' ||\n style.overflow === 'scroll';\n\n return hasVerticalScrollableStyle ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;\n },\n });\n\n let node: Node | null;\n\n while ((node = walker.nextNode()) && elements.length < 10) {\n const element = node as HTMLElement;\n\n if (this.isElementScrollable(element)) {\n elements.push(element);\n }\n }\n\n return elements;\n }\n\n private getElementSelector(element: Window | HTMLElement): string {\n if (element === window) {\n return 'window';\n }\n\n const htmlElement = element as HTMLElement;\n\n if (htmlElement.id) {\n return `#${htmlElement.id}`;\n }\n\n if (htmlElement.className && typeof htmlElement.className === 'string') {\n const firstClass = htmlElement.className.split(' ').filter((c) => c.trim())[0];\n\n if (firstClass) {\n return `.${firstClass}`;\n }\n }\n\n return htmlElement.tagName.toLowerCase();\n }\n\n private setupScrollContainer(element: Window | HTMLElement, selector: string): void {\n const alreadyTracking = this.containers.some((c) => c.element === element);\n\n if (alreadyTracking) {\n return;\n }\n\n if (element !== window && !this.isElementScrollable(element as HTMLElement)) {\n return;\n }\n\n const initialScrollTop = this.getScrollTop(element);\n\n const initialDepth = this.calculateScrollDepth(\n initialScrollTop,\n this.getScrollHeight(element),\n this.getViewportHeight(element),\n );\n\n const container: ScrollContainer = {\n element,\n selector,\n lastScrollPos: initialScrollTop,\n lastDepth: initialDepth,\n lastEventTime: 0,\n debounceTimer: null,\n listener: null as unknown as EventListener,\n };\n\n const handleScroll = (): void => {\n if (this.get('suppressNextScroll')) {\n return;\n }\n\n this.clearContainerTimer(container);\n\n container.debounceTimer = window.setTimeout(() => {\n const scrollData = this.calculateScrollData(container);\n\n if (scrollData) {\n this.processScrollEvent(container, scrollData, Date.now());\n }\n\n container.debounceTimer = null;\n }, SCROLL_DEBOUNCE_TIME_MS);\n };\n\n container.listener = handleScroll;\n this.containers.push(container);\n\n if (element === window) {\n window.addEventListener('scroll', handleScroll, { passive: true });\n } else {\n (element as HTMLElement).addEventListener('scroll', handleScroll, { passive: true });\n }\n }\n\n private processScrollEvent(\n container: ScrollContainer,\n scrollData: Omit<ScrollData, 'container_selector'>,\n timestamp: number,\n ): void {\n if (!this.shouldEmitScrollEvent(container, scrollData, timestamp)) {\n return;\n }\n\n container.lastEventTime = timestamp;\n container.lastDepth = scrollData.depth;\n\n const currentCount = this.get('scrollEventCount') ?? 0;\n this.set('scrollEventCount', currentCount + 1);\n\n this.eventManager.track({\n type: EventType.SCROLL,\n scroll_data: {\n ...scrollData,\n container_selector: container.selector,\n },\n });\n }\n\n private shouldEmitScrollEvent(\n container: ScrollContainer,\n scrollData: Omit<ScrollData, 'container_selector'>,\n timestamp: number,\n ): boolean {\n if (this.hasReachedSessionLimit()) {\n this.logLimitOnce();\n return false;\n }\n\n if (!this.hasElapsedMinimumInterval(container, timestamp)) {\n return false;\n }\n\n if (!this.hasSignificantDepthChange(container, scrollData.depth)) {\n return false;\n }\n\n return true;\n }\n\n private hasReachedSessionLimit(): boolean {\n const currentCount = this.get('scrollEventCount') ?? 0;\n return currentCount >= MAX_SCROLL_EVENTS_PER_SESSION;\n }\n\n private hasElapsedMinimumInterval(container: ScrollContainer, timestamp: number): boolean {\n if (container.lastEventTime === 0) {\n return true;\n }\n return timestamp - container.lastEventTime >= SCROLL_MIN_EVENT_INTERVAL_MS;\n }\n\n private hasSignificantDepthChange(container: ScrollContainer, newDepth: number): boolean {\n return Math.abs(newDepth - container.lastDepth) >= MIN_SCROLL_DEPTH_CHANGE;\n }\n\n private logLimitOnce(): void {\n if (this.limitWarningLogged) {\n return;\n }\n\n this.limitWarningLogged = true;\n\n log('debug', 'Max scroll events per session reached', {\n data: { limit: MAX_SCROLL_EVENTS_PER_SESSION },\n });\n }\n\n private isWindowScrollable(): boolean {\n return document.documentElement.scrollHeight > window.innerHeight;\n }\n\n private clearContainerTimer(container: ScrollContainer): void {\n if (container.debounceTimer !== null) {\n clearTimeout(container.debounceTimer);\n container.debounceTimer = null;\n }\n }\n\n private getScrollDirection(current: number, previous: number): ScrollDirection {\n return current > previous ? ScrollDirection.DOWN : ScrollDirection.UP;\n }\n\n private calculateScrollDepth(scrollTop: number, scrollHeight: number, viewportHeight: number): number {\n if (scrollHeight <= viewportHeight) {\n return 0;\n }\n\n const maxScrollTop = scrollHeight - viewportHeight;\n return Math.min(100, Math.max(0, Math.floor((scrollTop / maxScrollTop) * 100)));\n }\n\n private calculateScrollData(container: ScrollContainer): Omit<ScrollData, 'container_selector'> | null {\n const { element, lastScrollPos } = container;\n const scrollTop = this.getScrollTop(element);\n\n const positionDelta = Math.abs(scrollTop - lastScrollPos);\n if (positionDelta < SIGNIFICANT_SCROLL_DELTA) {\n return null;\n }\n\n if (element === window && !this.isWindowScrollable()) {\n return null;\n }\n\n const viewportHeight = this.getViewportHeight(element);\n const scrollHeight = this.getScrollHeight(element);\n const direction = this.getScrollDirection(scrollTop, lastScrollPos);\n const depth = this.calculateScrollDepth(scrollTop, scrollHeight, viewportHeight);\n\n container.lastScrollPos = scrollTop;\n\n return { depth, direction };\n }\n\n private getScrollTop(element: Window | HTMLElement): number {\n return element === window ? window.scrollY : (element as HTMLElement).scrollTop;\n }\n\n private getViewportHeight(element: Window | HTMLElement): number {\n return element === window ? window.innerHeight : (element as HTMLElement).clientHeight;\n }\n\n private getScrollHeight(element: Window | HTMLElement): number {\n return element === window ? document.documentElement.scrollHeight : (element as HTMLElement).scrollHeight;\n }\n\n private isElementScrollable(element: HTMLElement): boolean {\n const style = getComputedStyle(element);\n\n const hasVerticalScrollableOverflow =\n style.overflowY === 'auto' ||\n style.overflowY === 'scroll' ||\n style.overflow === 'auto' ||\n style.overflow === 'scroll';\n\n const hasVerticalOverflowContent = element.scrollHeight > element.clientHeight;\n\n return hasVerticalScrollableOverflow && hasVerticalOverflowContent;\n }\n}\n","import { StateManager } from '../managers/state.manager';\nimport { log } from '../utils';\n\nconst SHOPIFY_SESSION_ATTR = 'tracelog_session_id';\nconst SHOPIFY_USER_ATTR = 'tracelog_user_id';\n\n/**\n * Writes TraceLog identifiers to Shopify cart attributes via the storefront AJAX API.\n *\n * Two attributes are propagated:\n * - `tracelog_session_id`: surfaces in the `orders/paid` webhook `note_attributes`,\n * letting the API attribute revenue to the original session's UTM data.\n * - `tracelog_user_id`: surfaces in Customer Events `event.data.checkout.attributes`\n * (cart_viewed / checkout_started / checkout_completed), letting the Web Pixel\n * Extension carry the same user_id used on the storefront into the checkout sandbox.\n *\n * Without `tracelog_user_id` propagation, checkout-funnel events would derive a\n * different user_id from Shopify's per-visitor `clientId`, splitting one buyer\n * into two TraceLog users (pre-checkout and post-checkout) and breaking funnel\n * conversion / repeat-purchase metrics.\n */\nexport class ShopifyCartLinker extends StateManager {\n private visibilityHandler: (() => void) | null = null;\n private pageshowHandler: ((event: PageTransitionEvent) => void) | null = null;\n private lastSyncedKey: string | null = null;\n\n activate(): void {\n this.cleanupListeners();\n this.syncCartAttribute();\n this.setupListeners();\n }\n\n deactivate(): void {\n this.cleanupListeners();\n this.lastSyncedKey = null;\n }\n\n /** Re-syncs cart attributes when session rotates (called by App on SESSION_START). */\n onSessionChange(): void {\n this.syncCartAttribute();\n }\n\n private syncCartAttribute(): void {\n const sessionId = this.get('sessionId');\n if (!sessionId) return;\n\n const rawUserId = this.get('userId');\n const userId = typeof rawUserId === 'string' && rawUserId.length > 0 ? rawUserId : '';\n const dedupKey = `${sessionId}|${userId}`;\n if (dedupKey === this.lastSyncedKey) return;\n\n this.lastSyncedKey = dedupKey;\n this.postCartUpdate(sessionId, userId);\n }\n\n private postCartUpdate(sessionId: string, userId: string): void {\n const attributes: Record<string, string> = { [SHOPIFY_SESSION_ATTR]: sessionId };\n if (userId.length > 0) attributes[SHOPIFY_USER_ATTR] = userId;\n\n try {\n fetch('/cart/update.js', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ attributes }),\n credentials: 'same-origin',\n })\n .then((response) => {\n if (!response.ok) {\n this.lastSyncedKey = null;\n log('debug', 'Shopify cart attribute update failed', { data: { status: response.status } });\n }\n })\n .catch(() => {\n this.lastSyncedKey = null;\n log('debug', 'Shopify cart attribute update failed');\n });\n } catch {\n this.lastSyncedKey = null;\n log('debug', 'Shopify cart attribute update failed');\n }\n }\n\n /**\n * Sync triggers (theme-agnostic):\n * - `visibilitychange`: catches tab refocus (long sessions, OAuth round-trips).\n * - `pageshow` with `event.persisted === true`: catches bfcache restore so a\n * user returning from an external checkout / Shop Pay window picks up the\n * current sessionId before any further interaction.\n *\n * Both triggers go through `syncCartAttribute()` which dedupes by\n * `sessionId|userId`, so spurious calls cost nothing.\n */\n private setupListeners(): void {\n this.visibilityHandler = (): void => {\n if (!document.hidden) {\n this.syncCartAttribute();\n }\n };\n document.addEventListener('visibilitychange', this.visibilityHandler);\n\n this.pageshowHandler = (event: PageTransitionEvent): void => {\n if (event.persisted) this.syncCartAttribute();\n };\n window.addEventListener('pageshow', this.pageshowHandler);\n }\n\n private cleanupListeners(): void {\n if (this.visibilityHandler) {\n document.removeEventListener('visibilitychange', this.visibilityHandler);\n this.visibilityHandler = null;\n }\n if (this.pageshowHandler) {\n window.removeEventListener('pageshow', this.pageshowHandler);\n this.pageshowHandler = null;\n }\n }\n}\n","import { log } from '../utils';\n\n/**\n * Robust localStorage and sessionStorage wrapper with automatic fallback to in-memory storage.\n *\n * - Dual storage: localStorage (persistent) + sessionStorage (tab-scoped).\n * - Automatic in-memory fallback when browser storage unavailable (privacy modes, SSR).\n * - On `QuotaExceededError`: single-pass cleanup — purges `tracelog_persisted_events_*`\n * first (largest, recoverable), retries once. Preserves session/user/device/config keys.\n */\nexport class StorageManager {\n private readonly storage: Storage | null;\n private readonly sessionStorageRef: Storage | null;\n private readonly fallbackStorage = new Map<string, string>();\n private readonly fallbackSessionStorage = new Map<string, string>();\n\n constructor() {\n this.storage = this.initializeStorage('localStorage');\n this.sessionStorageRef = this.initializeStorage('sessionStorage');\n\n if (!this.storage) {\n log('debug', 'localStorage not available, using memory fallback');\n }\n if (!this.sessionStorageRef) {\n log('debug', 'sessionStorage not available, using memory fallback');\n }\n }\n\n getItem(key: string): string | null {\n try {\n if (this.storage) {\n return this.storage.getItem(key);\n }\n return this.fallbackStorage.get(key) ?? null;\n } catch {\n return this.fallbackStorage.get(key) ?? null;\n }\n }\n\n setItem(key: string, value: string): void {\n this.fallbackStorage.set(key, value);\n\n if (!this.storage) {\n return;\n }\n\n try {\n this.storage.setItem(key, value);\n return;\n } catch (error) {\n const isQuotaError =\n (error instanceof DOMException && error.name === 'QuotaExceededError') ||\n (error instanceof Error && error.name === 'QuotaExceededError');\n\n if (!isQuotaError) {\n return;\n }\n\n log('warn', 'localStorage quota exceeded, attempting cleanup', {\n data: { key, valueSize: value.length },\n });\n\n if (!this.cleanupOldData()) {\n log('error', 'localStorage quota exceeded and no data to cleanup - data will not persist', {\n error,\n data: { key, valueSize: value.length },\n });\n return;\n }\n\n try {\n this.storage.setItem(key, value);\n } catch (retryError) {\n log('error', 'localStorage quota exceeded even after cleanup - data will not persist', {\n error: retryError,\n data: { key, valueSize: value.length },\n });\n }\n }\n }\n\n removeItem(key: string): void {\n try {\n if (this.storage) {\n this.storage.removeItem(key);\n }\n } catch {\n /* empty */\n }\n\n this.fallbackStorage.delete(key);\n }\n\n /**\n * Single-pass cleanup for QuotaExceededError. Purges persisted-events keys\n * (largest, safe to discard — recoverable) and up to 5 other non-critical\n * tracelog_* keys in one pass. Preserves session/user/device/config keys.\n */\n private cleanupOldData(): boolean {\n if (!this.storage) {\n return false;\n }\n\n try {\n const criticalPrefixes = ['tracelog_session_', 'tracelog_user_id', 'tracelog_device_id', 'tracelog_config'];\n const persistedKeys: string[] = [];\n const nonCriticalKeys: string[] = [];\n\n for (let i = 0; i < this.storage.length; i++) {\n const key = this.storage.key(i);\n if (!key?.startsWith('tracelog_')) continue;\n\n if (key.startsWith('tracelog_persisted_events_')) {\n persistedKeys.push(key);\n } else if (!criticalPrefixes.some((prefix) => key.startsWith(prefix))) {\n nonCriticalKeys.push(key);\n }\n }\n\n const keysToRemove = [...persistedKeys, ...nonCriticalKeys.slice(0, 5)];\n\n if (keysToRemove.length === 0) {\n return false;\n }\n\n keysToRemove.forEach((key) => {\n try {\n this.storage!.removeItem(key);\n } catch {\n /* empty */\n }\n });\n\n return true;\n } catch (error) {\n log('error', 'Failed to cleanup old data', { error });\n return false;\n }\n }\n\n private initializeStorage(type: 'localStorage' | 'sessionStorage'): Storage | null {\n if (typeof window === 'undefined') {\n return null;\n }\n\n try {\n const storage = type === 'localStorage' ? window.localStorage : window.sessionStorage;\n const testKey = '__tracelog_test__';\n\n storage.setItem(testKey, 'test');\n storage.removeItem(testKey);\n\n return storage;\n } catch {\n return null;\n }\n }\n\n getSessionItem(key: string): string | null {\n try {\n if (this.sessionStorageRef) {\n return this.sessionStorageRef.getItem(key);\n }\n return this.fallbackSessionStorage.get(key) ?? null;\n } catch {\n return this.fallbackSessionStorage.get(key) ?? null;\n }\n }\n\n setSessionItem(key: string, value: string): void {\n this.fallbackSessionStorage.set(key, value);\n\n try {\n if (this.sessionStorageRef) {\n this.sessionStorageRef.setItem(key, value);\n return;\n }\n } catch (error) {\n const isQuotaError =\n (error instanceof DOMException && error.name === 'QuotaExceededError') ||\n (error instanceof Error && error.name === 'QuotaExceededError');\n\n if (isQuotaError) {\n log('error', 'sessionStorage quota exceeded - data will not persist', {\n error,\n data: { key, valueSize: value.length },\n });\n }\n }\n }\n\n removeSessionItem(key: string): void {\n try {\n if (this.sessionStorageRef) {\n this.sessionStorageRef.removeItem(key);\n }\n } catch {\n /* empty */\n }\n\n this.fallbackSessionStorage.delete(key);\n }\n}\n","import { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { EventType, WebVitalType } from '../types';\nimport {\n MAX_NAVIGATION_HISTORY,\n PRECISION_TWO_DECIMALS,\n getWebVitalsThresholds,\n DEFAULT_WEB_VITALS_MODE,\n} from '../constants';\nimport { log } from '../utils';\n\ntype LayoutShiftEntry = PerformanceEntry & { value?: number; hadRecentInput?: boolean };\n\n/**\n * Captures Web Vitals and performance metrics using the web-vitals library with fallback to native Performance Observer API.\n *\n * **Features**:\n * - Configurable filtering modes: 'all', 'needs-improvement' (default), 'poor'\n * - Custom threshold overrides via webVitalsThresholds config\n * - Navigation-based deduplication with 50-navigation FIFO history\n * - CLS accumulation with reset on navigation change\n * - Automatic fallback to Performance Observer if web-vitals library fails\n * - Final values only (reportAllChanges: false for all metrics)\n *\n * **Events Generated**: `web_vitals`\n *\n * **Metrics Captured**:\n * - LCP (Largest Contentful Paint): Main content loading time\n * - CLS (Cumulative Layout Shift): Visual stability score\n * - FCP (First Contentful Paint): Initial rendering time\n * - TTFB (Time to First Byte): Server response time\n * - INP (Interaction to Next Paint): Responsiveness measure\n *\n * **Filtering Modes**:\n * - 'all': Track all positive metric values (threshold = 0)\n * - 'needs-improvement': Track metrics exceeding good thresholds (default)\n * - 'poor': Track only critical performance issues\n *\n * @example\n * ```typescript\n * const handler = new PerformanceHandler(eventManager);\n * await handler.startTracking();\n * // Web Vitals are now being tracked with default 'needs-improvement' mode\n * handler.stopTracking();\n * ```\n */\nexport class PerformanceHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly reportedByNav: Map<string, Set<string>> = new Map();\n private readonly navigationHistory: string[] = []; // FIFO queue for tracking navigation order\n private readonly observers: PerformanceObserver[] = [];\n private vitalThresholds: Record<WebVitalType, number>;\n private navigationCounter = 0; // Counter for handling simultaneous navigations edge case\n\n constructor(eventManager: EventManager) {\n super();\n this.eventManager = eventManager;\n this.vitalThresholds = getWebVitalsThresholds(DEFAULT_WEB_VITALS_MODE);\n }\n\n /**\n * Starts tracking Web Vitals and performance metrics.\n *\n * Asynchronously loads the web-vitals library and initializes performance tracking.\n * Falls back to native Performance Observer API if web-vitals fails to load.\n *\n * **Configuration**:\n * - Reads webVitalsMode from config ('all', 'needs-improvement', 'poor')\n * - Merges webVitalsThresholds with mode defaults for custom thresholds\n * - Initializes web-vitals library observers (LCP, CLS, FCP, TTFB, INP)\n *\n * @returns Promise that resolves when tracking is initialized\n */\n async startTracking(): Promise<void> {\n const config = this.get('config');\n const mode = config?.webVitalsMode ?? DEFAULT_WEB_VITALS_MODE;\n\n this.vitalThresholds = getWebVitalsThresholds(mode);\n\n if (config?.webVitalsThresholds) {\n this.vitalThresholds = { ...this.vitalThresholds, ...config.webVitalsThresholds };\n }\n\n await this.initWebVitals();\n }\n\n /**\n * Stops tracking Web Vitals and cleans up resources.\n *\n * Disconnects all Performance Observers and clears internal state:\n * - Disconnects all active observers (web-vitals and long task)\n * - Clears navigation-based deduplication map\n * - Clears navigation history array\n * - Prevents memory leaks in long-running applications\n */\n stopTracking(): void {\n this.observers.forEach((obs, index) => {\n try {\n obs.disconnect();\n } catch (error) {\n log('debug', 'Failed to disconnect performance observer', { error, data: { observerIndex: index } });\n }\n });\n\n this.observers.length = 0;\n this.reportedByNav.clear();\n this.navigationHistory.length = 0;\n }\n\n private observeWebVitalsFallback(): void {\n this.reportTTFB();\n\n this.safeObserve(\n 'largest-contentful-paint',\n (list) => {\n const entries = list.getEntries();\n const last = entries[entries.length - 1] as (PerformanceEntry & { startTime: number }) | undefined;\n\n if (!last) {\n return;\n }\n\n this.sendVital({ type: 'LCP', value: Number(last.startTime.toFixed(PRECISION_TWO_DECIMALS)) });\n },\n { type: 'largest-contentful-paint', buffered: true },\n true,\n );\n\n let clsValue = 0;\n let currentNavId = this.getNavigationId();\n\n this.safeObserve(\n 'layout-shift',\n (list) => {\n const navId = this.getNavigationId();\n\n if (navId !== currentNavId) {\n clsValue = 0;\n currentNavId = navId;\n }\n\n const entries = list.getEntries() as LayoutShiftEntry[];\n\n for (const entry of entries) {\n if (entry.hadRecentInput === true) {\n continue;\n }\n\n const value = typeof entry.value === 'number' ? entry.value : 0;\n clsValue += value;\n }\n\n this.sendVital({ type: 'CLS', value: Number(clsValue.toFixed(PRECISION_TWO_DECIMALS)) });\n },\n { type: 'layout-shift', buffered: true },\n );\n\n this.safeObserve(\n 'paint',\n (list) => {\n for (const entry of list.getEntries()) {\n if (entry.name === 'first-contentful-paint') {\n this.sendVital({ type: 'FCP', value: Number(entry.startTime.toFixed(PRECISION_TWO_DECIMALS)) });\n }\n }\n },\n { type: 'paint', buffered: true },\n true,\n );\n\n this.safeObserve(\n 'event',\n (list) => {\n let worst = 0;\n const entries = list.getEntries() as Array<{ startTime: number; processingEnd?: number }>;\n\n for (const entry of entries) {\n const dur = (entry.processingEnd ?? 0) - (entry.startTime ?? 0);\n worst = Math.max(worst, dur);\n }\n\n if (worst > 0) {\n this.sendVital({ type: 'INP', value: Number(worst.toFixed(PRECISION_TWO_DECIMALS)) });\n }\n },\n { type: 'event', buffered: true },\n );\n }\n\n private async initWebVitals(): Promise<void> {\n try {\n const { onLCP, onCLS, onFCP, onTTFB, onINP } = await import('web-vitals');\n\n const report =\n (type: WebVitalType) =>\n (metric: { value: number }): void => {\n const value = Number(metric.value.toFixed(PRECISION_TWO_DECIMALS));\n this.sendVital({ type, value });\n };\n\n onLCP(report('LCP'), { reportAllChanges: false });\n onCLS(report('CLS'), { reportAllChanges: false });\n onFCP(report('FCP'), { reportAllChanges: false });\n onTTFB(report('TTFB'), { reportAllChanges: false });\n onINP(report('INP'), { reportAllChanges: false });\n } catch (error) {\n log('debug', 'Failed to load web-vitals library, using fallback', { error });\n this.observeWebVitalsFallback();\n }\n }\n\n private reportTTFB(): void {\n try {\n const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n\n if (!nav) {\n return;\n }\n\n const ttfb = nav.responseStart;\n\n // TTFB can be 0 in Mobile Safari when response is served from cache\n if (typeof ttfb === 'number' && Number.isFinite(ttfb)) {\n this.sendVital({ type: 'TTFB', value: Number(ttfb.toFixed(PRECISION_TWO_DECIMALS)) });\n }\n } catch (error) {\n log('debug', 'Failed to report TTFB', { error });\n }\n }\n\n private sendVital(sample: { type: WebVitalType; value: number }): void {\n if (!this.shouldSendVital(sample.type, sample.value)) {\n return;\n }\n\n const navId = this.getNavigationId();\n\n if (navId) {\n const reportedForNav = this.reportedByNav.get(navId);\n const isDuplicate = reportedForNav?.has(sample.type);\n\n if (isDuplicate) {\n return;\n }\n\n if (!reportedForNav) {\n this.reportedByNav.set(navId, new Set([sample.type]));\n this.navigationHistory.push(navId);\n\n if (this.navigationHistory.length > MAX_NAVIGATION_HISTORY) {\n const oldestNav = this.navigationHistory.shift();\n if (oldestNav) {\n this.reportedByNav.delete(oldestNav);\n }\n }\n } else {\n reportedForNav.add(sample.type);\n }\n }\n\n this.trackWebVital(sample.type, sample.value);\n }\n\n private trackWebVital(type: WebVitalType, value: number): void {\n if (!Number.isFinite(value)) {\n log('debug', 'Invalid web vital value', { data: { type, value } });\n return;\n }\n\n this.eventManager.track({\n type: EventType.WEB_VITALS,\n web_vitals: {\n type,\n value,\n },\n });\n }\n\n /**\n * Generates a unique navigation identifier for deduplication.\n *\n * **Purpose**: Creates deterministic IDs to prevent duplicate Web Vitals reporting\n * across multiple metrics for the same navigation event.\n *\n * **ID Format**: `{timestamp}_{pathname}` or `{timestamp}_{pathname}_{counter}`\n *\n * **Edge Case Handling**:\n * - If multiple navigations occur to the same pathname in the same millisecond,\n * a counter suffix is appended (e.g., `1234.56_/home_2`)\n * - Counter only added when > 1 to minimize ID length for common case\n *\n * **Why Deterministic**:\n * - Previous implementation used random string → duplicate metrics on page reload\n * - Now: Same navigation = same ID = proper deduplication via reportedByNav Map\n *\n * @returns Navigation ID string or null if navigation timing unavailable\n *\n * @internal\n */\n private getNavigationId(): string | null {\n try {\n const nav = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming | undefined;\n\n if (!nav) {\n return null;\n }\n\n const timestamp = nav.startTime || performance.now();\n const counter = ++this.navigationCounter;\n\n // Base ID: timestamp + pathname (deterministic for deduplication)\n const baseId = `${timestamp.toFixed(2)}_${window.location.pathname}`;\n\n // Append counter only if > 1 (edge case: simultaneous navigations)\n // This prevents collisions if two navigations occur in the same millisecond to the same path\n return counter > 1 ? `${baseId}_${counter}` : baseId;\n } catch (error) {\n log('debug', 'Failed to get navigation ID', { error });\n return null;\n }\n }\n\n private isObserverSupported(type: string): boolean {\n if (typeof PerformanceObserver === 'undefined') return false;\n const supported = PerformanceObserver.supportedEntryTypes;\n return !supported || supported.includes(type);\n }\n\n private safeObserve(\n type: string,\n cb: PerformanceObserverCallback,\n options?: PerformanceObserverInit,\n once = false,\n ): boolean {\n try {\n if (!this.isObserverSupported(type)) {\n return false;\n }\n\n const obs = new PerformanceObserver((list, observer) => {\n try {\n cb(list, observer);\n } catch (callbackError) {\n log('debug', 'Observer callback failed', {\n error: callbackError,\n data: { type },\n });\n }\n\n if (once) {\n try {\n observer.disconnect();\n } catch {\n /* empty */\n }\n }\n });\n\n obs.observe(options ?? { type, buffered: true });\n\n if (!once) {\n this.observers.push(obs);\n }\n\n return true;\n } catch (error) {\n log('debug', 'Failed to create performance observer', {\n error,\n data: { type },\n });\n return false;\n }\n }\n\n private shouldSendVital(type: WebVitalType, value?: number): boolean {\n if (typeof value !== 'number' || !Number.isFinite(value)) {\n log('debug', 'Invalid web vital value', { data: { type, value } });\n return false;\n }\n\n const threshold = this.vitalThresholds[type];\n\n if (typeof threshold === 'number' && value <= threshold) {\n return false;\n }\n\n return true;\n }\n}\n","import { EventManager } from '../managers/event.manager';\nimport { StateManager } from '../managers/state.manager';\nimport { EmitterEvent, ErrorType, EventType } from '../types';\nimport type { EmitterCallback, EmitterMap } from '../types';\nimport type { Emitter } from '../utils';\nimport { buildErrorSignatureKey, log, sanitizePii } from '../utils';\nimport {\n MAX_ERROR_MESSAGE_LENGTH,\n MAX_STACK_TRACE_LENGTH,\n ERROR_SUPPRESSION_WINDOW_MS,\n MAX_TRACKED_ERRORS,\n MAX_TRACKED_ERRORS_HARD_LIMIT,\n DEFAULT_ERROR_SAMPLING_RATE,\n ERROR_BURST_WINDOW_MS,\n ERROR_BURST_THRESHOLD,\n ERROR_BURST_BACKOFF_MS,\n MAX_ERRORS_PER_SIGNATURE_PER_PAGEVIEW,\n MAX_PAGEVIEW_SIGNATURE_KEYS,\n} from '../constants/error.constants';\n\n/**\n * Captures JavaScript errors and unhandled promise rejections for debugging and monitoring.\n *\n * **Events Generated**: `error`\n *\n * **Features**:\n * - Tracks JavaScript runtime errors and unhandled promise rejections\n * - Configurable error sampling rate (default: 100%)\n * - PII sanitization (emails, phone numbers, credit cards, API keys, tokens)\n * - Message truncation (500 character limit)\n * - Burst detection (>10 errors/second triggers 5-second cooldown)\n * - Deduplication within 5-second window per error type+message\n * - Per-pageview signature cap (`MAX_ERRORS_PER_SIGNATURE_PER_PAGEVIEW`): after the\n * 5s dedup window expires, the same `(normalizedMessage, filename, line)` may only\n * recur up to N times per pageview. Counter resets on `pagehide`, `SESSION_START`,\n * and `PAGE_VIEW` (covers SPA navigation via patched History API + popstate/hashchange).\n *\n * **Privacy Protection**:\n * - Automatically redacts PII from error messages before storage\n * - Sanitizes emails, phone numbers, credit cards, IBAN, API keys, Bearer tokens\n *\n * @see src/handlers/README.md (`## ErrorHandler` section) for detailed documentation\n */\nexport class ErrorHandler extends StateManager {\n private readonly eventManager: EventManager;\n private readonly emitter?: Emitter;\n private readonly recentErrors = new Map<string, number>();\n private readonly pageviewSignatureCounts = new Map<string, number>();\n private errorBurstCounter = 0;\n private burstWindowStart = 0;\n private burstBackoffUntil = 0;\n private pagehideHandler: (() => void) | null = null;\n private pageviewResetListener: EmitterCallback<EmitterMap[EmitterEvent.EVENT]> | null = null;\n\n constructor(eventManager: EventManager, emitter?: Emitter) {\n super();\n this.eventManager = eventManager;\n this.emitter = emitter;\n }\n\n /**\n * Starts tracking JavaScript errors and promise rejections.\n *\n * - Registers global error event listener\n * - Registers unhandledrejection event listener\n * - Registers pagehide listener to reset the per-pageview signature counter\n * - Subscribes to emitter SESSION_START + PAGE_VIEW to reset the counter on new\n * sessions and SPA route changes (the only signal `pagehide` does not cover)\n */\n startTracking(): void {\n window.addEventListener('error', this.handleError);\n window.addEventListener('unhandledrejection', this.handleRejection);\n\n this.pagehideHandler = (): void => {\n this.resetPageviewCounter();\n };\n window.addEventListener('pagehide', this.pagehideHandler, { passive: true });\n\n if (this.emitter) {\n this.pageviewResetListener = (event): void => {\n if (event.type === EventType.SESSION_START || event.type === EventType.PAGE_VIEW) {\n this.resetPageviewCounter();\n }\n };\n this.emitter.on(EmitterEvent.EVENT, this.pageviewResetListener);\n }\n }\n\n /**\n * Stops tracking errors and cleans up resources.\n *\n * - Removes error event listeners\n * - Removes pagehide listener and unsubscribes from emitter\n * - Clears recent errors and pageview signature counters\n * - Resets burst detection counters\n */\n stopTracking(): void {\n window.removeEventListener('error', this.handleError);\n window.removeEventListener('unhandledrejection', this.handleRejection);\n\n if (this.pagehideHandler) {\n window.removeEventListener('pagehide', this.pagehideHandler);\n this.pagehideHandler = null;\n }\n\n if (this.emitter && this.pageviewResetListener) {\n this.emitter.off(EmitterEvent.EVENT, this.pageviewResetListener);\n this.pageviewResetListener = null;\n }\n\n this.recentErrors.clear();\n this.pageviewSignatureCounts.clear();\n this.errorBurstCounter = 0;\n this.burstWindowStart = 0;\n this.burstBackoffUntil = 0;\n }\n\n /**\n * Clears the per-pageview signature counter.\n *\n * Public so `App` or tests can drive a reset explicitly; the handler itself wires\n * `pagehide` and emitter `SESSION_START` / `PAGE_VIEW` in `startTracking()`.\n */\n resetPageviewCounter(): void {\n this.pageviewSignatureCounts.clear();\n }\n\n /**\n * Checks sampling rate and burst detection\n * Returns false if in cooldown period after burst detection\n */\n private shouldSample(): boolean {\n const now = Date.now();\n\n if (now < this.burstBackoffUntil) {\n return false;\n }\n\n if (now - this.burstWindowStart > ERROR_BURST_WINDOW_MS) {\n this.errorBurstCounter = 0;\n this.burstWindowStart = now;\n }\n\n this.errorBurstCounter++;\n\n if (this.errorBurstCounter > ERROR_BURST_THRESHOLD) {\n this.burstBackoffUntil = now + ERROR_BURST_BACKOFF_MS;\n log('debug', 'Error burst detected - entering cooldown', {\n data: {\n errorsInWindow: this.errorBurstCounter,\n cooldownMs: ERROR_BURST_BACKOFF_MS,\n },\n });\n return false;\n }\n\n const config = this.get('config');\n const samplingRate = config.errorSampling ?? DEFAULT_ERROR_SAMPLING_RATE;\n return Math.random() < samplingRate;\n }\n\n /**\n * Returns true when the per-pageview signature cap has been hit for this error.\n * Dropped errors do not increment the counter — the 5s suppression window already\n * silences identical repeats, and double-counting here would skew the cap for any\n * later signature that recycles the same map key after a counter reset.\n */\n private shouldThrottleBySignature(input: {\n message: string;\n filename?: string;\n line?: number;\n page_url?: string;\n }): boolean {\n const key = buildErrorSignatureKey(input);\n const current = this.pageviewSignatureCounts.get(key) ?? 0;\n\n if (current >= MAX_ERRORS_PER_SIGNATURE_PER_PAGEVIEW) {\n log('debug', 'Error throttled (pageview cap)', {\n data: { signature: key, count: current },\n });\n return true;\n }\n\n const nextCount = current + 1;\n this.pageviewSignatureCounts.set(key, nextCount);\n\n if (this.pageviewSignatureCounts.size > MAX_PAGEVIEW_SIGNATURE_KEYS) {\n this.pageviewSignatureCounts.clear();\n this.pageviewSignatureCounts.set(key, nextCount);\n }\n\n return false;\n }\n\n private readonly handleError = (event: ErrorEvent): void => {\n if (!this.shouldSample()) {\n return;\n }\n\n const sanitizedMessage = this.sanitize(event.message || 'Unknown error');\n\n if (this.shouldSuppressError(ErrorType.JS_ERROR, sanitizedMessage)) {\n return;\n }\n\n if (\n this.shouldThrottleBySignature({\n message: sanitizedMessage,\n filename: event.filename,\n line: event.lineno,\n // Inline-script errors report the page URL as `filename`; passing the current\n // page URL lets buildErrorSignatureKey collapse them to origin, matching the\n // normalized input the server hashes for cap/dedup. normalizeFilename strips\n // query/hash internally.\n page_url: window.location.href,\n })\n ) {\n return;\n }\n\n const stack = typeof event.error?.stack === 'string' ? this.truncateStack(event.error.stack) : undefined;\n const errorName =\n typeof event.error?.name === 'string' && event.error.name !== 'Error' ? event.error.name : undefined;\n this.eventManager.track({\n type: EventType.ERROR,\n error_data: {\n type: ErrorType.JS_ERROR,\n message: sanitizedMessage,\n ...(errorName !== undefined && { name: errorName }),\n ...(event.filename !== '' && { filename: event.filename }),\n ...(event.lineno !== 0 && { line: event.lineno }),\n ...(event.colno !== 0 && { column: event.colno }),\n ...(stack !== undefined && { stack }),\n },\n });\n };\n\n private readonly handleRejection = (event: PromiseRejectionEvent): void => {\n if (!this.shouldSample()) {\n return;\n }\n\n const message = this.extractRejectionMessage(event.reason);\n const sanitizedMessage = this.sanitize(message);\n\n if (this.shouldSuppressError(ErrorType.PROMISE_REJECTION, sanitizedMessage)) {\n return;\n }\n\n // PromiseRejectionEvent carries no filename/line, so the signature collapses\n // to `(normalizedMessage, '', '')` — rejections from different code paths that\n // happen to share a message hit the same cap. Accepted trade-off: the message\n // alone is usually distinctive enough, and over-throttling rejections is safer\n // than letting them flood the queue.\n if (this.shouldThrottleBySignature({ message: sanitizedMessage })) {\n return;\n }\n\n const stack =\n event.reason instanceof Error && typeof event.reason.stack === 'string'\n ? this.truncateStack(event.reason.stack)\n : undefined;\n const errorName = event.reason instanceof Error && event.reason.name !== 'Error' ? event.reason.name : undefined;\n this.eventManager.track({\n type: EventType.ERROR,\n error_data: {\n type: ErrorType.PROMISE_REJECTION,\n message: sanitizedMessage,\n ...(errorName !== undefined && { name: errorName }),\n ...(stack !== undefined && { stack }),\n },\n });\n };\n\n private extractRejectionMessage(reason: unknown): string {\n if (reason == null) return 'Unknown rejection';\n\n if (typeof reason === 'string') return reason;\n\n if (reason instanceof Error) {\n return reason.message;\n }\n\n if (typeof reason === 'object' && 'message' in reason) {\n return String(reason.message);\n }\n\n try {\n return JSON.stringify(reason);\n } catch {\n return 'Unserializable rejection';\n }\n }\n\n private sanitize(text: string): string {\n const truncated = text.length > MAX_ERROR_MESSAGE_LENGTH ? text.slice(0, MAX_ERROR_MESSAGE_LENGTH) + '...' : text;\n return sanitizePii(truncated);\n }\n\n private shouldSuppressError(type: ErrorType, message: string): boolean {\n const now = Date.now();\n const key = `${type}:${message}`;\n const lastSeenAt = this.recentErrors.get(key);\n\n if (lastSeenAt !== undefined && now - lastSeenAt < ERROR_SUPPRESSION_WINDOW_MS) {\n this.recentErrors.set(key, now);\n return true;\n }\n\n this.recentErrors.set(key, now);\n\n if (this.recentErrors.size > MAX_TRACKED_ERRORS_HARD_LIMIT) {\n this.recentErrors.clear();\n this.recentErrors.set(key, now);\n\n return false;\n }\n\n if (this.recentErrors.size > MAX_TRACKED_ERRORS) {\n this.pruneOldErrors();\n }\n\n return false;\n }\n\n private static readonly TRUNCATION_SUFFIX = '\\n...truncated';\n\n private truncateStack(stack: string): string {\n if (stack.length <= MAX_STACK_TRACE_LENGTH) return sanitizePii(stack);\n const limit = MAX_STACK_TRACE_LENGTH - ErrorHandler.TRUNCATION_SUFFIX.length;\n const truncated = stack.slice(0, limit) + ErrorHandler.TRUNCATION_SUFFIX;\n return sanitizePii(truncated);\n }\n\n private pruneOldErrors(): void {\n const now = Date.now();\n for (const [key, timestamp] of this.recentErrors.entries()) {\n if (now - timestamp > ERROR_SUPPRESSION_WINDOW_MS) {\n this.recentErrors.delete(key);\n }\n }\n\n if (this.recentErrors.size <= MAX_TRACKED_ERRORS) {\n return;\n }\n\n const entries = Array.from(this.recentErrors.entries()).sort((a, b) => a[1] - b[1]);\n const excess = this.recentErrors.size - MAX_TRACKED_ERRORS;\n\n for (let index = 0; index < excess; index += 1) {\n const entry = entries[index];\n if (entry) {\n this.recentErrors.delete(entry[0]);\n }\n }\n }\n}\n","import { EventManager } from './managers/event.manager';\nimport { UserManager } from './managers/user.manager';\nimport { StateManager } from './managers/state.manager';\nimport { SessionHandler } from './handlers/session.handler';\nimport { PageViewHandler } from './handlers/page-view.handler';\nimport { ClickHandler } from './handlers/click.handler';\nimport { ScrollHandler } from './handlers/scroll.handler';\nimport { ShopifyCartLinker } from './ecommerce';\nimport {\n Config,\n EventType,\n EmitterCallback,\n EmitterEvent,\n EmitterMap,\n EventOptions,\n IdentifyData,\n Mode,\n InitResult,\n} from './types';\nimport {\n isEventValid,\n getDeviceInfo,\n normalizeUrl,\n Emitter,\n getCollectApiUrls,\n detectQaMode,\n isPrerendering,\n log,\n generateUUID,\n sanitizeTraits,\n} from './utils';\nimport { StorageManager } from './managers/storage.manager';\nimport { SCROLL_DEBOUNCE_TIME_MS, SCROLL_SUPPRESS_MULTIPLIER } from './constants/config.constants';\nimport { IDENTITY_KEY, PENDING_IDENTITY_KEY, USER_ID_KEY } from './constants/storage.constants';\nimport { PerformanceHandler } from './handlers/performance.handler';\nimport { ErrorHandler } from './handlers/error.handler';\n\nexport class App extends StateManager {\n private isInitialized = false;\n private suppressNextScrollTimer: number | null = null;\n private pageUnloadHandler: (() => void) | null = null;\n private pageShowHandler: ((event: PageTransitionEvent) => void) | null = null;\n private visibilityFlushHandler: (() => void) | null = null;\n private prerenderActivationHandler: (() => void) | null = null;\n\n private readonly emitter = new Emitter();\n\n protected managers: {\n storage?: StorageManager;\n event?: EventManager;\n } = {};\n\n protected handlers: {\n session?: SessionHandler;\n pageView?: PageViewHandler;\n click?: ClickHandler;\n scroll?: ScrollHandler;\n performance?: PerformanceHandler;\n error?: ErrorHandler;\n } = {};\n\n private integrationInstances: {\n shopifyCartLinker?: ShopifyCartLinker;\n } = {};\n\n get initialized(): boolean {\n return this.isInitialized;\n }\n\n /**\n * Initializes TraceLog with configuration.\n *\n * @internal Called from api.init()\n */\n async init(config: Config = {}): Promise<InitResult> {\n if (this.isInitialized) {\n return { sessionId: this.get('sessionId') ?? '' };\n }\n\n this.managers.storage = new StorageManager();\n\n try {\n this.setupState(config);\n\n this.managers.event = new EventManager(this.managers.storage, this.emitter);\n\n this.loadPersistedIdentity();\n\n this.initializeHandlers();\n this.setupPageLifecycleListeners();\n\n await this.managers.event.recoverPersistedEvents().catch((error) => {\n log('warn', 'Failed to recover persisted events', { error });\n });\n\n this.isInitialized = true;\n\n return { sessionId: this.get('sessionId') ?? '' };\n } catch (error) {\n this.destroy(true);\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`[TraceLog] TraceLog initialization failed: ${errorMessage}`);\n }\n }\n\n /**\n * Sends a custom event with optional metadata and options.\n *\n * @internal Called from api.event()\n */\n sendCustomEvent(\n name: string,\n metadata?: Record<string, unknown> | Record<string, unknown>[],\n options?: EventOptions,\n ): void {\n if (!this.managers.event) {\n log('warn', 'Cannot send custom event: TraceLog not initialized', { data: { name } });\n return;\n }\n\n let normalizedMetadata = metadata;\n\n if (metadata && typeof metadata === 'object' && !Array.isArray(metadata)) {\n if (Object.getPrototypeOf(metadata) !== Object.prototype) {\n normalizedMetadata = Object.assign({}, metadata);\n }\n }\n\n const { valid, error, sanitizedMetadata } = isEventValid(name, normalizedMetadata);\n\n if (!valid) {\n if (this.get('mode') === Mode.QA) {\n throw new Error(`[TraceLog] Custom event \"${name}\" validation failed: ${error}`);\n }\n\n log('warn', `Custom event \"${name}\" dropped: ${error}`);\n return;\n }\n\n this.managers.event.track({\n type: EventType.CUSTOM,\n custom_event: {\n name,\n ...(sanitizedMetadata && { metadata: sanitizedMetadata }),\n },\n });\n\n if (options?.critical === true) {\n const ok = this.managers.event.flushImmediatelySync();\n if (!ok) {\n log('debug', 'Critical event flush returned false (deferred to in-flight send or empty queue)', {\n data: { name },\n });\n }\n }\n }\n\n on<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n this.emitter.on(event, callback);\n }\n\n off<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void {\n this.emitter.off(event, callback);\n }\n\n /**\n * Destroys the TraceLog instance and cleans up all resources.\n *\n * @internal Called from api.destroy()\n */\n destroy(force = false): void {\n if (!this.isInitialized && !force) {\n return;\n }\n\n Object.values(this.handlers)\n .filter(Boolean)\n .forEach((handler) => {\n try {\n handler.stopTracking();\n } catch (error) {\n log('warn', 'Failed to stop tracking', { error });\n }\n });\n\n if (this.suppressNextScrollTimer) {\n clearTimeout(this.suppressNextScrollTimer);\n this.suppressNextScrollTimer = null;\n }\n\n if (this.pageUnloadHandler) {\n window.removeEventListener('pagehide', this.pageUnloadHandler);\n window.removeEventListener('beforeunload', this.pageUnloadHandler);\n this.pageUnloadHandler = null;\n }\n\n if (this.pageShowHandler) {\n window.removeEventListener('pageshow', this.pageShowHandler);\n this.pageShowHandler = null;\n }\n\n if (this.visibilityFlushHandler) {\n document.removeEventListener('visibilitychange', this.visibilityFlushHandler);\n this.visibilityFlushHandler = null;\n }\n\n if (this.prerenderActivationHandler) {\n document.removeEventListener('prerenderingchange', this.prerenderActivationHandler);\n this.prerenderActivationHandler = null;\n }\n\n this.managers.event?.flushImmediatelySync();\n this.managers.event?.stop();\n\n this.emitter.removeAllListeners();\n\n this.set('suppressNextScroll', false);\n this.set('sessionId', null);\n this.set('identity', undefined);\n this.clearPersistedIdentity();\n\n this.integrationInstances.shopifyCartLinker?.deactivate();\n this.integrationInstances = {};\n\n this.isInitialized = false;\n this.handlers = {};\n this.managers = {};\n }\n\n private setupState(config: Config = {}): void {\n this.set('config', config);\n\n const userId = UserManager.getId(this.managers.storage as StorageManager);\n this.set('userId', userId);\n\n const collectApiUrls = getCollectApiUrls(config);\n this.set('collectApiUrls', collectApiUrls);\n\n const device = getDeviceInfo();\n this.set('device', device);\n\n const pageUrl = normalizeUrl(window.location.href, config.sensitiveQueryParams);\n this.set('pageUrl', pageUrl);\n\n const isQaMode = detectQaMode();\n\n if (isQaMode) {\n this.set('mode', Mode.QA);\n }\n }\n\n /**\n * @internal Used by api.ts for configuration access\n */\n public getConfig(): Config {\n return this.get('config');\n }\n\n /**\n * @internal Used by api.ts for backend URL access\n */\n public getCollectApiUrls(): { saas?: string } {\n return this.get('collectApiUrls');\n }\n\n /**\n * @internal Used by api.ts for event operations\n */\n public getEventManager(): EventManager | undefined {\n return this.managers.event;\n }\n\n /**\n * @internal Used by api.getSessionId()\n */\n public getSessionId(): string | null {\n return this.get('sessionId');\n }\n\n /**\n * @internal Used by api.getUserId()\n */\n public getUserId(): string | null {\n return this.get('userId');\n }\n\n /**\n * Associates the current anonymous visitor with a known user identity.\n *\n * Identity is persisted to localStorage (project-scoped) and included in every\n * subsequent batch payload so the backend always has the latest identity.\n *\n * @param userId - External user identifier (email, customer_id, etc.). Trimmed; max 256 chars.\n * @param traits - Optional user attributes (name, email, plan, etc.). Only string values\n * are kept; non-string fields, arrays, and null are dropped silently.\n *\n * @internal Called from api.identify()\n */\n public identify(userId: string, traits?: Record<string, string>): void {\n if (!userId || typeof userId !== 'string' || userId.trim().length === 0) {\n log('warn', 'identify() called with invalid userId', {\n data: { type: typeof userId, length: typeof userId === 'string' ? userId.trim().length : 0 },\n });\n return;\n }\n\n if (userId.trim().length > 256) {\n log('warn', 'identify() userId exceeds 256 characters', { data: { length: userId.trim().length } });\n return;\n }\n\n const trimmedUserId = userId.trim();\n const validTraits = sanitizeTraits(traits);\n const identity: IdentifyData = {\n userId: trimmedUserId,\n ...(validTraits ? { traits: validTraits } : {}),\n };\n\n this.set('identity', identity);\n this.persistIdentity(identity);\n\n log('debug', 'Visitor identified', {\n data: { userIdLength: trimmedUserId.length, traitKeys: validTraits ? Object.keys(validTraits) : [] },\n });\n }\n\n /**\n * Clears identity, regenerates UUID, and starts a fresh session.\n *\n * Use for logout flows: the previous visitor profile remains in the backend,\n * and the next user in the same browser gets a clean anonymous profile.\n *\n * Pending events are flushed under the OLD identity first via async fetch\n * (so any in-flight authentication headers are preserved). Then the identity\n * is cleared, the userId is regenerated, and the session handler is\n * restarted to emit a new `SESSION_START`.\n *\n * @internal Called from api.resetIdentity()\n */\n public async resetIdentity(): Promise<void> {\n await this.managers.event?.flushImmediately().catch((error) => {\n log('debug', 'Failed to flush before identity reset', { error });\n return false;\n });\n\n this.set('identity', undefined);\n this.clearPersistedIdentity();\n\n const newUserId = generateUUID();\n (this.managers.storage as StorageManager).setItem(USER_ID_KEY, newUserId);\n this.set('userId', newUserId);\n\n this.set('hasStartSession', false);\n this.set('sessionId', null);\n this.handlers.session?.stopTracking();\n this.handlers.session?.startTracking();\n\n log('debug', 'Identity reset, new UUID generated');\n }\n\n /**\n * Returns the project ID used for identity storage scoping.\n */\n private getProjectId(): string {\n const config = this.get('config');\n return config?.integrations?.tracelog?.projectId ?? 'custom';\n }\n\n /**\n * Persists identity to localStorage under the project-scoped key.\n */\n private persistIdentity(identity: IdentifyData): void {\n try {\n const projectId = this.getProjectId();\n const key = IDENTITY_KEY(projectId);\n (this.managers.storage as StorageManager).setItem(key, JSON.stringify(identity));\n } catch {\n log('debug', 'Failed to persist identity to localStorage');\n }\n }\n\n /**\n * Loads identity from localStorage on init.\n * Also migrates pending identity (set before init) to the project-scoped key.\n */\n private loadPersistedIdentity(): void {\n const storage = this.managers.storage as StorageManager;\n const projectId = this.getProjectId();\n const projectKey = IDENTITY_KEY(projectId);\n\n try {\n const pendingRaw = storage.getItem(PENDING_IDENTITY_KEY);\n if (pendingRaw) {\n const pending = JSON.parse(pendingRaw) as IdentifyData;\n storage.removeItem(PENDING_IDENTITY_KEY);\n\n if (!this.isValidIdentityData(pending)) {\n log('debug', 'Invalid pending identity in localStorage, discarded');\n return;\n }\n\n const normalizedPending = this.normalizePersistedIdentity(pending);\n storage.setItem(projectKey, JSON.stringify(normalizedPending));\n this.set('identity', normalizedPending);\n log('debug', 'Migrated pending identity to project-scoped key');\n return;\n }\n } catch {\n storage.removeItem(PENDING_IDENTITY_KEY);\n }\n\n try {\n const raw = storage.getItem(projectKey);\n if (raw) {\n const identity = JSON.parse(raw) as IdentifyData;\n\n if (!this.isValidIdentityData(identity)) {\n storage.removeItem(projectKey);\n log('debug', 'Invalid persisted identity in localStorage, discarded');\n return;\n }\n\n const normalizedIdentity = this.normalizePersistedIdentity(identity);\n this.set('identity', normalizedIdentity);\n log('debug', 'Loaded persisted identity');\n }\n } catch {\n log('debug', 'Failed to load persisted identity');\n }\n }\n\n /**\n * Validates identity data loaded from localStorage. `traits` is intentionally\n * accepted as `unknown` here: `normalizePersistedIdentity()` runs it through\n * `sanitizeTraits()` so tampered values are dropped silently instead of\n * rejecting an otherwise-valid identity.\n */\n private isValidIdentityData(data: unknown): data is IdentifyData {\n if (!data || typeof data !== 'object') return false;\n const { userId } = data as Record<string, unknown>;\n\n if (typeof userId !== 'string' || userId.trim().length === 0 || userId.trim().length > 256) return false;\n\n return true;\n }\n\n /**\n * Trims the `userId` and re-sanitizes `traits` through the same gate\n * `identify()` uses at call time, defending later batches against tampered\n * localStorage values.\n */\n private normalizePersistedIdentity(identity: IdentifyData): IdentifyData {\n const validTraits = sanitizeTraits(identity.traits);\n return {\n userId: identity.userId.trim(),\n ...(validTraits ? { traits: validTraits } : {}),\n };\n }\n\n /**\n * Clears persisted identity from localStorage.\n */\n private clearPersistedIdentity(): void {\n try {\n const storage = this.managers.storage as StorageManager;\n const projectId = this.getProjectId();\n storage.removeItem(IDENTITY_KEY(projectId));\n storage.removeItem(PENDING_IDENTITY_KEY);\n } catch {\n log('debug', 'Failed to clear persisted identity');\n }\n }\n\n private setupPageLifecycleListeners(): void {\n this.pageUnloadHandler = (): void => {\n this.managers.event?.flushImmediatelySync();\n };\n\n this.pageShowHandler = (event: PageTransitionEvent): void => {\n if (event.persisted) {\n void this.managers.event?.recoverPersistedEvents().catch((error) => {\n log('warn', 'Failed to recover persisted events on bfcache restore', { error });\n });\n }\n };\n\n this.visibilityFlushHandler = (): void => {\n if (typeof document === 'undefined' || !document.hidden) {\n return;\n }\n if (this.get('config').flushOnPageHidden === false) {\n return;\n }\n this.managers.event?.flushImmediatelySync();\n };\n\n window.addEventListener('pagehide', this.pageUnloadHandler);\n window.addEventListener('beforeunload', this.pageUnloadHandler);\n window.addEventListener('pageshow', this.pageShowHandler);\n document.addEventListener('visibilitychange', this.visibilityFlushHandler);\n }\n\n private initializeHandlers(): void {\n const config = this.get('config');\n\n this.handlers.session = new SessionHandler(\n this.managers.storage as StorageManager,\n this.managers.event as EventManager,\n );\n\n // Allocates `sessionId` synchronously (so init() returns a real id) and, while\n // pre-rendering, internally defers its SESSION_START emit + session listeners.\n this.handlers.session.startTracking();\n\n const onPageView = (): void => {\n this.set('suppressNextScroll', true);\n\n if (this.suppressNextScrollTimer) {\n clearTimeout(this.suppressNextScrollTimer);\n }\n\n this.suppressNextScrollTimer = window.setTimeout(() => {\n this.set('suppressNextScroll', false);\n }, SCROLL_DEBOUNCE_TIME_MS * SCROLL_SUPPRESS_MULTIPLIER);\n };\n\n this.handlers.pageView = new PageViewHandler(this.managers.event as EventManager, onPageView);\n this.handlers.click = new ClickHandler(this.managers.event as EventManager);\n this.handlers.scroll = new ScrollHandler(this.managers.event as EventManager);\n this.handlers.performance = new PerformanceHandler(this.managers.event as EventManager);\n this.handlers.error = new ErrorHandler(this.managers.event as EventManager, this.emitter);\n\n const startInteractionTracking = (): void => {\n this.handlers.pageView?.startTracking();\n this.handlers.click?.startTracking();\n this.handlers.scroll?.startTracking();\n this.handlers.performance?.startTracking().catch((error) => {\n log('warn', 'Failed to start performance tracking', { error });\n });\n this.handlers.error?.startTracking();\n\n if (config.integrations?.tracelog?.shopify) {\n const linker = new ShopifyCartLinker();\n linker.activate();\n this.integrationInstances.shopifyCartLinker = linker;\n\n this.emitter.on(EmitterEvent.EVENT, (event) => {\n if (event.type === EventType.SESSION_START) {\n linker.onSessionChange();\n }\n });\n }\n };\n\n // Pre-render guard (orchestration half): defer all interaction tracking until\n // activation so a pre-rendered page emits zero PAGE_VIEW/CLICK/... events. The\n // server upserts a session from ANY event with a session_id, so deferring only\n // SESSION_START would still create a phantom session from the initial PAGE_VIEW.\n // SessionManager defers its own SESSION_START in parallel; its listener is\n // registered above (in session.startTracking()), so it fires first and\n // SESSION_START precedes the first PAGE_VIEW on activation.\n if (isPrerendering()) {\n this.prerenderActivationHandler = (): void => {\n this.prerenderActivationHandler = null;\n startInteractionTracking();\n };\n document.addEventListener('prerenderingchange', this.prerenderActivationHandler, { once: true });\n } else {\n startInteractionTracking();\n }\n }\n}\n","import { App } from './app';\nimport { MetadataType, IdentifyData, Config, EmitterCallback, EmitterMap, InitResult, EventOptions } from './types';\nimport { log, validateAndNormalizeConfig, sanitizeTraits } from './utils';\nimport { INITIALIZATION_TIMEOUT_MS } from './constants';\nimport { PENDING_IDENTITY_KEY } from './constants/storage.constants';\nimport './types/window.types';\n\ninterface PendingListener {\n event: keyof EmitterMap;\n callback: EmitterCallback<EmitterMap[keyof EmitterMap]>;\n}\n\nconst pendingListeners: PendingListener[] = [];\n\nlet app: App | null = null;\nlet isInitializing = false;\nlet isDestroying = false;\nlet initPromise: Promise<InitResult> | null = null;\n\n/**\n * Initializes TraceLog and begins tracking user interactions.\n *\n * Important: Register listeners with on() before calling init() to capture initial events.\n *\n * @param config - Optional configuration object\n * @returns Promise with sessionId (empty string in SSR/disabled environments)\n * @throws {Error} If initialization fails or times out\n *\n * @example\n * ```typescript\n * const { sessionId } = await tracelog.init({\n * integrations: {\n * tracelog: { projectId: 'your-project-id' }\n * }\n * });\n * console.log('Session:', sessionId);\n * ```\n */\nexport const init = async (config?: Config): Promise<InitResult> => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return { sessionId: '' };\n }\n\n isDestroying = false;\n\n if (window.__traceLogDisabled === true) {\n return { sessionId: '' };\n }\n\n if (app) {\n return { sessionId: app.getSessionId() ?? '' };\n }\n\n if (isInitializing && initPromise) {\n return initPromise;\n }\n\n isInitializing = true;\n\n initPromise = (async (): Promise<InitResult> => {\n try {\n const validatedConfig = validateAndNormalizeConfig(config ?? {});\n const instance = new App();\n\n try {\n pendingListeners.forEach(({ event, callback }) => {\n instance.on(event, callback);\n });\n\n pendingListeners.length = 0;\n\n const appInitPromise = instance.init(validatedConfig);\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`[TraceLog] Initialization timeout after ${INITIALIZATION_TIMEOUT_MS}ms`));\n }, INITIALIZATION_TIMEOUT_MS);\n });\n\n const result = await Promise.race([appInitPromise, timeoutPromise]);\n\n app = instance;\n\n return result;\n } catch (error) {\n try {\n instance.destroy(true);\n } catch (cleanupError) {\n log('error', 'Failed to cleanup partially initialized app', { error: cleanupError });\n }\n\n throw error;\n }\n } catch (error) {\n app = null;\n throw error;\n } finally {\n isInitializing = false;\n initPromise = null;\n }\n })();\n\n return initPromise;\n};\n\n/**\n * Tracks a custom analytics event with optional metadata.\n *\n * @param name - Event identifier (e.g., 'checkout_completed')\n * @param metadata - Optional event data (object or array of objects)\n * @param options - Optional event options. Pass `{ critical: true }` for\n * high-value events that must survive an imminent page unload (e.g., a\n * purchase tracked right before `window.location.href = '/thanks'`).\n * Critical events flush via `sendBeacon`, which the browser guarantees\n * to queue for delivery even if the page closes immediately after.\n * @throws {Error} If called before init() or during destroy()\n *\n * @example\n * ```typescript\n * tracelog.event('product_viewed', { productId: 'abc-123', price: 299.99 });\n *\n * // Critical event (e.g., right before redirecting to a thank-you page)\n * tracelog.event('purchase_completed', { orderId: 'ord-789' }, { critical: true });\n * window.location.href = '/thanks';\n * ```\n */\nexport const event = (\n name: string,\n metadata?: Record<string, MetadataType> | Record<string, MetadataType>[],\n options?: EventOptions,\n): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app) {\n throw new Error('[TraceLog] TraceLog not initialized. Please call init() first.');\n }\n\n if (isDestroying) {\n throw new Error('[TraceLog] Cannot send events while TraceLog is being destroyed');\n }\n\n app.sendCustomEvent(name, metadata, options);\n};\n\n/**\n * Subscribes to TraceLog events for real-time consumption.\n *\n * Important: Register listeners BEFORE calling init() to capture SESSION_START and PAGE_VIEW.\n *\n * @param event - Event type ('event' or 'queue')\n * @param callback - Handler function called when event fires\n *\n * @example\n * ```typescript\n * tracelog.on('event', (event) => console.log(event.type));\n * await tracelog.init();\n * ```\n */\nexport const on = <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app || isInitializing) {\n pendingListeners.push({ event, callback } as PendingListener);\n return;\n }\n\n app.on(event, callback);\n};\n\n/**\n * Unsubscribes from TraceLog events.\n *\n * @param event - Event type to unsubscribe from\n * @param callback - Exact callback function reference used in on()\n */\nexport const off = <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app) {\n const index = pendingListeners.findIndex((l) => l.event === event && l.callback === callback);\n if (index !== -1) {\n pendingListeners.splice(index, 1);\n }\n return;\n }\n\n app.off(event, callback);\n};\n\n/**\n * Checks if TraceLog is currently initialized.\n */\nexport const isInitialized = (): boolean => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return false;\n }\n\n return app !== null;\n};\n\n/**\n * Returns the current session ID.\n *\n * Session ID is generated during init() and persists across page refreshes\n * within the session timeout window (default 15 minutes).\n *\n * @returns Session ID string, or null if not initialized\n */\nexport const getSessionId = (): string | null => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return null;\n }\n\n if (!app) {\n return null;\n }\n\n return app.getSessionId();\n};\n\n/**\n * Returns the current user ID, or null if `init()` has not been called yet.\n */\nexport const getUserId = (): string | null => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return null;\n }\n\n if (!app) {\n return null;\n }\n\n return app.getUserId();\n};\n\n/**\n * Stops all tracking, cleans up listeners, and flushes pending events.\n *\n * Sends remaining events with sendBeacon before cleanup.\n *\n * @throws {Error} If destroy operation is already in progress\n */\nexport const destroy = (): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (isDestroying) {\n throw new Error('[TraceLog] Destroy operation already in progress');\n }\n\n if (!app) {\n isDestroying = false;\n\n return;\n }\n\n isDestroying = true;\n\n try {\n app.destroy();\n app = null;\n isInitializing = false;\n initPromise = null;\n pendingListeners.length = 0;\n\n if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined' && window.__traceLogBridge) {\n window.__traceLogBridge = undefined;\n }\n\n isDestroying = false;\n } catch (error) {\n app = null;\n isInitializing = false;\n initPromise = null;\n\n pendingListeners.length = 0;\n\n isDestroying = false;\n\n log('warn', 'Error during destroy, forced cleanup completed', { error });\n }\n};\n\n/**\n * Associates the current anonymous visitor with a known user identity.\n *\n * Can be called before or after init(). If called before init(), the identity is\n * persisted to localStorage and applied automatically when init() runs.\n *\n * Identity is included in every event batch (piggyback), so the backend always\n * receives the latest identity. Calling identify() multiple times overwrites\n * (last-write-wins).\n *\n * @param userId - External user identifier (email, customer_id, etc.). Max 256 chars.\n * @param traits - Optional user attributes (name, email, plan, etc.). Only string\n * values are kept; non-string fields, arrays, and null are dropped silently.\n *\n * @example\n * ```typescript\n * // After login, with traits\n * tracelog.identify('cust_123', { name: 'Maria Garcia', plan: 'pro' });\n *\n * // Before init (identity queued, applied on init)\n * tracelog.identify('cust_123');\n * await tracelog.init({ integrations: { tracelog: { projectId: '...' } } });\n * ```\n */\nexport const identify = (userId: string, traits?: Record<string, string>): void => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!userId || typeof userId !== 'string' || userId.trim().length === 0) {\n log('warn', 'identify() called with invalid userId');\n return;\n }\n\n if (userId.trim().length > 256) {\n log('warn', 'identify() userId exceeds 256 characters');\n return;\n }\n\n if (isDestroying) {\n log('warn', 'Cannot identify while TraceLog is being destroyed');\n return;\n }\n\n if (app) {\n app.identify(userId, traits);\n return;\n }\n\n try {\n const validTraits = sanitizeTraits(traits);\n const identity: IdentifyData = {\n userId: userId.trim(),\n ...(validTraits ? { traits: validTraits } : {}),\n };\n localStorage.setItem(PENDING_IDENTITY_KEY, JSON.stringify(identity));\n log('debug', 'Identity persisted pre-init (will be applied on init)');\n } catch {\n log('debug', 'Failed to persist pre-init identity');\n }\n};\n\n/**\n * Clears identity, regenerates the visitor UUID, and starts a new session.\n *\n * Use for logout flows. The previous visitor profile remains intact in the\n * backend; the next user in the same browser gets a clean anonymous profile.\n *\n * Pending events are flushed under the OLD identity first via async fetch\n * (so any in-flight authentication headers are preserved), then the identity\n * is cleared, a fresh `user_id` is generated, and a new `SESSION_START` is\n * emitted.\n *\n * Safe to call before init(): clears any pending pre-init identity silently.\n *\n * @throws {Error} If called during destroy()\n *\n * @example\n * ```typescript\n * // On logout\n * await tracelog.resetIdentity();\n * ```\n */\nexport const resetIdentity = async (): Promise<void> => {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n if (!app) {\n try {\n localStorage.removeItem(PENDING_IDENTITY_KEY);\n } catch {\n // Silent — storage may be disabled or full\n }\n return;\n }\n\n if (isDestroying) {\n throw new Error('[TraceLog] Cannot reset identity while TraceLog is being destroyed');\n }\n\n await app.resetIdentity();\n};\n\n/**\n * @internal TestBridge API - development only\n */\nexport const __setAppInstance = (instance: App | null): void => {\n if (process.env.NODE_ENV !== 'development') {\n return;\n }\n\n if (instance !== null) {\n const hasRequiredMethods =\n typeof instance === 'object' &&\n 'init' in instance &&\n 'destroy' in instance &&\n 'on' in instance &&\n 'off' in instance;\n\n if (!hasRequiredMethods) {\n throw new Error('[TraceLog] Invalid app instance type');\n }\n }\n\n if (app !== null && instance !== null && app !== instance) {\n throw new Error('[TraceLog] Cannot overwrite existing app instance. Call destroy() first.');\n }\n\n app = instance;\n};\n\n/**\n * @internal TestBridge state accessors - development only\n */\nexport const __getInitState = (): { isInitializing: boolean; isDestroying: boolean } => {\n if (process.env.NODE_ENV !== 'development') {\n return { isInitializing: false, isDestroying: false };\n }\n return { isInitializing, isDestroying };\n};\n\nif (process.env.NODE_ENV === 'development' && typeof window !== 'undefined' && typeof document !== 'undefined') {\n void import('./test-bridge')\n .then((module) => {\n if (typeof module.injectTestBridge === 'function') {\n module.injectTestBridge();\n }\n })\n .catch(() => {\n // Silent fail - TestBridge is optional in test environments\n });\n\n void import('./utils/browser/mode.utils')\n .then((module) => {\n if (typeof module.detectQaMode === 'function') {\n module.detectQaMode();\n }\n })\n .catch(() => {\n // Silent fail - mode detection is optional\n });\n}\n","import { init, event, on, off, isInitialized, getSessionId, getUserId, destroy, identify, resetIdentity } from './api';\n\n// Constants\nexport * from './app.constants';\n\n// Types\nexport * from './types';\n\n// TraceLog namespace containing all API methods\nexport const tracelog = {\n init,\n event,\n on,\n off,\n isInitialized,\n getSessionId,\n getUserId,\n destroy,\n identify,\n resetIdentity,\n};\n"]}
|