@prosdevlab/experience-sdk 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/memory.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/cookie.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/localStorage.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/sessionStorage.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/storage.ts","../../plugins/src/utils/sanitize.ts","../../plugins/src/banner/banner.ts","../../plugins/src/debug/debug.ts","../../plugins/src/frequency/frequency.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/util/deep-merge.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/config.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/emitter.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/expose.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/namespace.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/sdk.ts","../src/runtime.ts","../src/singleton.ts"],"names":["MemoryBackend","key","value","_options","CookieBackend","defaultOptions","name","cookies","cookie","error","options","opts","parts","expires","eqPos","testKey","supported","LocalStorageBackend","SessionStorageBackend","storagePlugin","plugin","instance","config","getBackendType","getNamespace","getDefaultTTL","getCookieDefaults","backends","getBackend","type","buildKey","namespace","ns","isExpired","stored","set","backendType","backend","namespacedKey","ttl","serialized","get","remove","clear","storage","prefix","keysToRemove","i","trimmed","eqIndex","encodedKey","isSupported","ALLOWED_TAGS","ALLOWED_ATTRIBUTES","sanitizeHTML","html","temp","sanitizeNode","node","escapeHTML","element","tagName","allowedAttrs","attrs","attr","sanitizedHref","sanitizeURL","escapeAttribute","attrString","innerHTML","child","sanitized","text","div","url","decoded","bannerPlugin","activeBanners","injectDefaultStyles","styleId","style","createBannerElement","experience","content","position","dismissable","zIndex","banner","baseClasses","container","contentDiv","title","message","buttonContainer","buttonsDiv","createButton","buttonConfig","button","variant","buttonClasses","closeButton","show","experienceId","id","isShowing","payload","items","item","typedItem","decision","debugPlugin","isEnabled","shouldLogConsole","shouldEmitWindow","log","data","logData","event","frequencyPlugin","experienceFrequencyMap","getStorageBackend","per","getStorageKey","getImpressionData","raw","saveImpressionData","getTimeWindow","getImpressionCount","hasReachedCap","max","timeWindow","now","timestamp","recordImpression","sevenDaysAgo","ts","freqStep","t","deepMerge","target","source","result","sourceValue","targetValue","isPlainObject","Config","initialConfig","path","keys","current","lastKey","Emitter","handler","subscription","sub","args","err","pattern","withWildcards","str","Expose","sdk","api","Namespace","SDK","pluginFn","expose","requiredNamespaces","ExperienceRuntime","exp","context","startTime","evalContext","buildContext","matchedExperience","allReasons","allTrace","evaluateExperience","freqStart","hasReached","count","sortedExperiences","a","b","priorityA","decisions","expStartTime","reasons","trace","matchedDecisions","d","matchedExperiences","index","partial","matched","urlStart","urlMatch","evaluateUrlRule","rule","createInstance","defaultInstance","init","register","evaluate","evaluateAll","explain","getState","on","destroy","experiences"],"mappings":"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","file":"experience-sdk.global.js","sourcesContent":["/**\n * Memory Storage Backend\n *\n * In-memory storage fallback when other storage mechanisms are unavailable.\n * Data is lost when the page is refreshed.\n */\n\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class MemoryBackend implements StorageBackend {\n private storage = new Map<string, string>();\n\n get(key: string): string | null {\n return this.storage.get(key) ?? null;\n }\n\n set(key: string, value: string, _options?: StorageBackendOptions): void {\n this.storage.set(key, value);\n }\n\n remove(key: string): void {\n this.storage.delete(key);\n }\n\n clear(): void {\n this.storage.clear();\n }\n\n isSupported(): boolean {\n // Memory storage is always supported\n return true;\n }\n}\n","/**\n * Cookie Storage Backend\n *\n * Stores data in browser cookies with support for domain, path, secure, and sameSite options.\n * Falls back to memory storage if cookies are unavailable.\n */\n\nimport { MemoryBackend } from './memory';\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class CookieBackend implements StorageBackend {\n private fallback: MemoryBackend | null = null;\n private defaultOptions: StorageBackendOptions;\n\n constructor(defaultOptions: StorageBackendOptions = {}) {\n this.defaultOptions = {\n path: '/',\n sameSite: 'lax',\n ...defaultOptions,\n };\n }\n\n get(key: string): string | null {\n if (!this.isSupported()) {\n return this.getFallback().get(key);\n }\n\n try {\n const name = `${encodeURIComponent(key)}=`;\n const cookies = document.cookie.split(';');\n\n for (let cookie of cookies) {\n cookie = cookie.trim();\n if (cookie.startsWith(name)) {\n return decodeURIComponent(cookie.substring(name.length));\n }\n }\n\n return null;\n } catch (error) {\n console.warn('Cookie get failed:', error);\n return this.getFallback().get(key);\n }\n }\n\n set(key: string, value: string, options?: StorageBackendOptions): void {\n if (!this.isSupported()) {\n this.getFallback().set(key, value);\n return;\n }\n\n try {\n const opts = { ...this.defaultOptions, ...options };\n const parts: string[] = [`${encodeURIComponent(key)}=${encodeURIComponent(value)}`];\n\n // Add expiration if TTL is provided\n if (opts.ttl) {\n const expires = new Date();\n expires.setTime(expires.getTime() + opts.ttl * 1000);\n parts.push(`expires=${expires.toUTCString()}`);\n }\n\n if (opts.path) {\n parts.push(`path=${opts.path}`);\n }\n\n if (opts.domain) {\n parts.push(`domain=${opts.domain}`);\n }\n\n if (opts.secure) {\n parts.push('secure');\n }\n\n if (opts.sameSite) {\n parts.push(`samesite=${opts.sameSite}`);\n }\n\n document.cookie = parts.join('; ');\n } catch (error) {\n console.warn('Cookie set failed, falling back to memory:', error);\n this.getFallback().set(key, value);\n }\n }\n\n remove(key: string): void {\n if (!this.isSupported()) {\n this.getFallback().remove(key);\n return;\n }\n\n try {\n // Set cookie with past expiration date to delete it\n const opts = this.defaultOptions;\n const parts: string[] = [\n `${encodeURIComponent(key)}=`,\n 'expires=Thu, 01 Jan 1970 00:00:00 UTC',\n ];\n\n if (opts.path) {\n parts.push(`path=${opts.path}`);\n }\n\n if (opts.domain) {\n parts.push(`domain=${opts.domain}`);\n }\n\n document.cookie = parts.join('; ');\n } catch (error) {\n console.warn('Cookie remove failed:', error);\n this.getFallback().remove(key);\n }\n }\n\n clear(): void {\n if (!this.isSupported()) {\n this.getFallback().clear();\n return;\n }\n\n try {\n // Get all cookie names and delete them\n const cookies = document.cookie.split(';');\n\n for (let cookie of cookies) {\n cookie = cookie.trim();\n const eqPos = cookie.indexOf('=');\n const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;\n this.remove(decodeURIComponent(name));\n }\n } catch (error) {\n console.warn('Cookie clear failed:', error);\n this.getFallback().clear();\n }\n }\n\n isSupported(): boolean {\n try {\n // Check if cookies are enabled\n if (typeof document === 'undefined' || !document.cookie) {\n return false;\n }\n\n // Try to set and remove a test cookie\n const testKey = '__cookie_test__';\n document.cookie = `${testKey}=test; path=/`;\n const supported = document.cookie.indexOf(testKey) !== -1;\n\n // Clean up test cookie\n document.cookie = `${testKey}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;\n\n return supported;\n } catch {\n return false;\n }\n }\n\n private getFallback(): MemoryBackend {\n if (!this.fallback) {\n this.fallback = new MemoryBackend();\n }\n return this.fallback;\n }\n}\n","/**\n * LocalStorage Backend\n *\n * Persistent storage that survives page refreshes and browser restarts.\n * Falls back to memory storage if localStorage is unavailable or quota is exceeded.\n */\n\nimport { MemoryBackend } from './memory';\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class LocalStorageBackend implements StorageBackend {\n private fallback: MemoryBackend | null = null;\n\n get(key: string): string | null {\n if (!this.isSupported()) {\n return this.getFallback().get(key);\n }\n\n try {\n return localStorage.getItem(key);\n } catch (error) {\n console.warn('localStorage.getItem failed:', error);\n return this.getFallback().get(key);\n }\n }\n\n set(key: string, value: string, _options?: StorageBackendOptions): void {\n if (!this.isSupported()) {\n this.getFallback().set(key, value);\n return;\n }\n\n try {\n localStorage.setItem(key, value);\n } catch (error) {\n // Quota exceeded or other error\n console.warn('localStorage.setItem failed, falling back to memory:', error);\n this.getFallback().set(key, value);\n }\n }\n\n remove(key: string): void {\n if (!this.isSupported()) {\n this.getFallback().remove(key);\n return;\n }\n\n try {\n localStorage.removeItem(key);\n } catch (error) {\n console.warn('localStorage.removeItem failed:', error);\n this.getFallback().remove(key);\n }\n }\n\n clear(): void {\n if (!this.isSupported()) {\n this.getFallback().clear();\n return;\n }\n\n try {\n localStorage.clear();\n } catch (error) {\n console.warn('localStorage.clear failed:', error);\n this.getFallback().clear();\n }\n }\n\n isSupported(): boolean {\n try {\n const testKey = '__storage_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n\n private getFallback(): MemoryBackend {\n if (!this.fallback) {\n this.fallback = new MemoryBackend();\n }\n return this.fallback;\n }\n}\n","/**\n * SessionStorage Backend\n *\n * Session-scoped storage that persists only for the duration of the page session.\n * Data is cleared when the tab/window is closed.\n * Falls back to memory storage if sessionStorage is unavailable.\n */\n\nimport { MemoryBackend } from './memory';\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class SessionStorageBackend implements StorageBackend {\n private fallback: MemoryBackend | null = null;\n\n get(key: string): string | null {\n if (!this.isSupported()) {\n return this.getFallback().get(key);\n }\n\n try {\n return sessionStorage.getItem(key);\n } catch (error) {\n console.warn('sessionStorage.getItem failed:', error);\n return this.getFallback().get(key);\n }\n }\n\n set(key: string, value: string, _options?: StorageBackendOptions): void {\n if (!this.isSupported()) {\n this.getFallback().set(key, value);\n return;\n }\n\n try {\n sessionStorage.setItem(key, value);\n } catch (error) {\n console.warn('sessionStorage.setItem failed, falling back to memory:', error);\n this.getFallback().set(key, value);\n }\n }\n\n remove(key: string): void {\n if (!this.isSupported()) {\n this.getFallback().remove(key);\n return;\n }\n\n try {\n sessionStorage.removeItem(key);\n } catch (error) {\n console.warn('sessionStorage.removeItem failed:', error);\n this.getFallback().remove(key);\n }\n }\n\n clear(): void {\n if (!this.isSupported()) {\n this.getFallback().clear();\n return;\n }\n\n try {\n sessionStorage.clear();\n } catch (error) {\n console.warn('sessionStorage.clear failed:', error);\n this.getFallback().clear();\n }\n }\n\n isSupported(): boolean {\n try {\n const testKey = '__storage_test__';\n sessionStorage.setItem(testKey, 'test');\n sessionStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n\n private getFallback(): MemoryBackend {\n if (!this.fallback) {\n this.fallback = new MemoryBackend();\n }\n return this.fallback;\n }\n}\n","/**\n * Storage Plugin\n *\n * Universal storage abstraction with support for:\n * - Multiple backends (localStorage, sessionStorage, cookie, memory)\n * - TTL/expiration\n * - Namespace isolation\n * - JSON serialization\n * - Automatic fallback\n */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport { CookieBackend } from './backends/cookie';\nimport { LocalStorageBackend } from './backends/localStorage';\nimport { MemoryBackend } from './backends/memory';\nimport { SessionStorageBackend } from './backends/sessionStorage';\nimport type { StorageBackend, StorageBackendOptions } from './backends/types';\n\nexport type StorageBackendType = 'localStorage' | 'sessionStorage' | 'cookie' | 'memory';\n\nexport interface StoragePluginConfig {\n storage?: {\n backend?: StorageBackendType;\n namespace?: string;\n ttl?: number;\n // Cookie-specific defaults\n domain?: string;\n path?: string;\n secure?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n };\n}\n\nexport interface StorageOptions extends StorageBackendOptions {\n backend?: StorageBackendType;\n namespace?: string;\n}\n\nexport interface StoragePlugin {\n set(key: string, value: unknown, options?: StorageOptions): void;\n get<T = unknown>(key: string, options?: Pick<StorageOptions, 'backend'>): T | null;\n remove(key: string, options?: Pick<StorageOptions, 'backend'>): void;\n clear(options?: Pick<StorageOptions, 'backend'>): void;\n}\n\ninterface StoredValue<T> {\n value: T;\n expires?: number; // Timestamp in milliseconds\n}\n\n/**\n * Storage Plugin\n *\n * Provides a universal storage API with automatic backend selection and fallback.\n *\n * @example\n * ```typescript\n * const sdk = new SDK({ storage: { backend: 'localStorage', namespace: 'myapp' } });\n * sdk.use(storagePlugin);\n *\n * sdk.storage.set('user', { id: 123, name: 'Alice' });\n * const user = sdk.storage.get('user');\n * ```\n */\nexport const storagePlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('storage');\n\n // Set default config\n plugin.defaults({\n storage: {\n backend: 'localStorage',\n namespace: '',\n path: '/',\n sameSite: 'lax',\n },\n });\n\n // Get config values\n const getBackendType = (): StorageBackendType => config.get('storage.backend') ?? 'localStorage';\n const getNamespace = (): string => config.get('storage.namespace') ?? '';\n const getDefaultTTL = (): number | undefined => config.get('storage.ttl');\n const getCookieDefaults = (): StorageBackendOptions => ({\n domain: config.get('storage.domain'),\n path: config.get('storage.path') ?? '/',\n secure: config.get('storage.secure'),\n sameSite: config.get('storage.sameSite') ?? 'lax',\n });\n\n // Backend instances (lazy-initialized)\n const backends: Partial<Record<StorageBackendType, StorageBackend>> = {};\n\n /**\n * Get or create a backend instance\n */\n function getBackend(type: StorageBackendType): StorageBackend {\n if (!backends[type]) {\n switch (type) {\n case 'localStorage':\n backends[type] = new LocalStorageBackend();\n break;\n case 'sessionStorage':\n backends[type] = new SessionStorageBackend();\n break;\n case 'cookie':\n backends[type] = new CookieBackend(getCookieDefaults());\n break;\n case 'memory':\n backends[type] = new MemoryBackend();\n break;\n }\n }\n return backends[type] as StorageBackend;\n }\n\n /**\n * Build a namespaced key\n */\n function buildKey(key: string, namespace?: string): string {\n const ns = namespace ?? getNamespace();\n return ns ? `${ns}:${key}` : key;\n }\n\n /**\n * Check if a stored value has expired\n */\n function isExpired(stored: StoredValue<any>): boolean {\n if (!stored.expires) {\n return false;\n }\n return Date.now() > stored.expires;\n }\n\n /**\n * Set a value in storage\n */\n function set<T>(key: string, value: T, options?: StorageOptions): void {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespacedKey = buildKey(key, options?.namespace);\n\n // Wrap value with expiration metadata\n const stored: StoredValue<T> = { value };\n const ttl = options?.ttl ?? getDefaultTTL();\n\n if (ttl) {\n stored.expires = Date.now() + ttl * 1000;\n }\n\n // Serialize to JSON\n const serialized = JSON.stringify(stored);\n\n // Store using backend\n backend.set(namespacedKey, serialized, options);\n\n // Emit event\n plugin.emit('storage:set', { key, value, backend: backendType });\n }\n\n /**\n * Get a value from storage\n */\n function get<T>(key: string, options?: StorageOptions): T | null {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespacedKey = buildKey(key, options?.namespace);\n\n // Get from backend\n const serialized = backend.get(namespacedKey);\n\n if (!serialized) {\n return null;\n }\n\n try {\n // Deserialize from JSON\n const stored: StoredValue<T> = JSON.parse(serialized);\n\n // Check expiration\n if (isExpired(stored)) {\n // Remove expired value\n backend.remove(namespacedKey);\n plugin.emit('storage:expired', { key, backend: backendType });\n return null;\n }\n\n plugin.emit('storage:get', { key, backend: backendType });\n return stored.value;\n } catch (error) {\n console.warn('Failed to parse stored value:', error);\n // Remove corrupted value\n backend.remove(namespacedKey);\n return null;\n }\n }\n\n /**\n * Remove a value from storage\n */\n function remove(key: string, options?: StorageOptions): void {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespacedKey = buildKey(key, options?.namespace);\n\n backend.remove(namespacedKey);\n plugin.emit('storage:remove', { key, backend: backendType });\n }\n\n /**\n * Clear all values from storage\n *\n * If a namespace is provided, only clears values with that namespace prefix.\n * Otherwise, clears all values in the backend.\n */\n function clear(options?: StorageOptions): void {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespace = options?.namespace;\n\n // If no namespace specified, clear everything\n if (!namespace) {\n backend.clear();\n plugin.emit('storage:clear', { backend: backendType });\n return;\n }\n\n // Namespace-specific clearing: iterate through all keys and remove matching ones\n if (backendType === 'localStorage' || backendType === 'sessionStorage') {\n const storage = backendType === 'localStorage' ? localStorage : sessionStorage;\n const prefix = `${namespace}:`;\n const keysToRemove: string[] = [];\n\n // Collect keys to remove\n for (let i = 0; i < storage.length; i++) {\n const key = storage.key(i);\n if (key?.startsWith(prefix)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove them\n for (const key of keysToRemove) {\n backend.remove(key);\n }\n } else if (backendType === 'cookie') {\n // For cookies, parse document.cookie and remove matching keys\n const prefix = `${namespace}:`;\n const cookies = document.cookie.split(';');\n\n for (const cookie of cookies) {\n const trimmed = cookie.trim();\n const eqIndex = trimmed.indexOf('=');\n if (eqIndex === -1) continue;\n\n const encodedKey = trimmed.substring(0, eqIndex);\n const key = decodeURIComponent(encodedKey);\n\n if (key.startsWith(prefix)) {\n backend.remove(key);\n }\n }\n } else {\n // For memory backend, just clear everything (it's in-memory anyway)\n backend.clear();\n }\n\n plugin.emit('storage:clear', { backend: backendType, namespace });\n }\n\n /**\n * Check if a backend is supported\n */\n function isSupported(backendType?: StorageBackendType): boolean {\n const type = backendType ?? getBackendType();\n const backend = getBackend(type);\n return backend.isSupported();\n }\n\n // Expose public API\n plugin.expose({\n storage: {\n set,\n get,\n remove,\n clear,\n isSupported,\n },\n });\n\n // Log initialization\n instance.on('sdk:ready', () => {\n const backendType = getBackendType();\n const backend = getBackend(backendType);\n const supported = backend.isSupported();\n\n if (!supported) {\n console.warn(\n `Storage backend '${backendType}' is not supported, falling back to memory storage`\n );\n }\n });\n};\n\nexport { CookieBackend } from './backends/cookie';\nexport { LocalStorageBackend } from './backends/localStorage';\nexport { MemoryBackend } from './backends/memory';\nexport { SessionStorageBackend } from './backends/sessionStorage';\n// Export types and backends for testing\nexport type { StorageBackend, StorageBackendOptions } from './backends/types';\n","/**\n * HTML Sanitizer\n *\n * Lightweight HTML sanitizer for experience content (messages, titles).\n * Whitelist-based approach that only allows safe formatting tags.\n *\n * Security: Prevents XSS attacks by stripping dangerous tags and attributes.\n */\n\n/**\n * Allowed HTML tags for sanitization\n * Only safe formatting tags are permitted\n */\nconst ALLOWED_TAGS = ['strong', 'em', 'a', 'br', 'span', 'b', 'i', 'p'] as const;\n\n/**\n * Allowed attributes per tag\n */\nconst ALLOWED_ATTRIBUTES: Record<string, string[]> = {\n a: ['href', 'class', 'style', 'title'],\n span: ['class', 'style'],\n p: ['class', 'style'],\n // Other tags have no attributes allowed\n};\n\n/**\n * Sanitize HTML string by removing dangerous tags and attributes\n *\n * @param html - HTML string to sanitize\n * @returns Sanitized HTML string safe for innerHTML\n *\n * @example\n * ```typescript\n * sanitizeHTML('<strong>Hello</strong><script>alert(\"xss\")</script>');\n * // Returns: '<strong>Hello</strong>'\n * ```\n */\nexport function sanitizeHTML(html: string): string {\n if (!html || typeof html !== 'string') {\n return '';\n }\n\n // Create a temporary DOM element to parse HTML\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n /**\n * Recursively sanitize a DOM node\n */\n function sanitizeNode(node: Node): string {\n // Text nodes - escape HTML entities\n if (node.nodeType === Node.TEXT_NODE) {\n return escapeHTML(node.textContent || '');\n }\n\n // Element nodes\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n const tagName = element.tagName.toLowerCase();\n\n // Handle tags with whitespace (malformed HTML like \"< script >\")\n // Browser normalizes these, but if we see a tag that's not in our list,\n // it might be a dangerous tag that was normalized\n if (!tagName || tagName.includes(' ')) {\n return '';\n }\n\n // If tag is not allowed, return empty string\n if (!ALLOWED_TAGS.includes(tagName as any)) {\n return '';\n }\n\n // Get allowed attributes for this tag\n const allowedAttrs = ALLOWED_ATTRIBUTES[tagName] || [];\n\n // Build attribute string\n const attrs: string[] = [];\n for (const attr of allowedAttrs) {\n const value = element.getAttribute(attr);\n if (value !== null) {\n // Sanitize attribute values\n if (attr === 'href') {\n // Only allow safe URLs (http, https, mailto, tel, relative)\n const sanitizedHref = sanitizeURL(value);\n if (sanitizedHref) {\n attrs.push(`href=\"${escapeAttribute(sanitizedHref)}\"`);\n }\n } else {\n // For all other attributes (title, class, style), escape HTML entities\n attrs.push(`${attr}=\"${escapeAttribute(value)}\"`);\n }\n }\n }\n\n const attrString = attrs.length > 0 ? ' ' + attrs.join(' ') : '';\n\n // Process child nodes\n let innerHTML = '';\n for (const child of Array.from(element.childNodes)) {\n innerHTML += sanitizeNode(child);\n }\n\n // Self-closing tags\n if (tagName === 'br') {\n return `<br${attrString} />`;\n }\n\n return `<${tagName}${attrString}>${innerHTML}</${tagName}>`;\n }\n\n return '';\n }\n\n // Sanitize all nodes\n let sanitized = '';\n for (const child of Array.from(temp.childNodes)) {\n sanitized += sanitizeNode(child);\n }\n\n return sanitized;\n}\n\n/**\n * Escape HTML entities to prevent XSS in text content\n */\nfunction escapeHTML(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Escape HTML entities for use in attribute values\n */\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Sanitize URL to prevent javascript: and data: XSS attacks\n *\n * @param url - URL to sanitize\n * @returns Sanitized URL or empty string if unsafe\n */\nfunction sanitizeURL(url: string): string {\n if (!url || typeof url !== 'string') {\n return '';\n }\n\n // Decode URL-encoded characters to check for encoded attacks\n let decoded: string;\n try {\n decoded = decodeURIComponent(url);\n } catch {\n // If decoding fails, use original\n decoded = url;\n }\n\n const trimmed = decoded.trim().toLowerCase();\n\n // Block javascript: and data: protocols (check both original and decoded)\n if (\n trimmed.startsWith('javascript:') ||\n trimmed.startsWith('data:') ||\n url.toLowerCase().trim().startsWith('javascript:') ||\n url.toLowerCase().trim().startsWith('data:')\n ) {\n return '';\n }\n\n // Allow http, https, mailto, tel, and relative URLs\n if (\n trimmed.startsWith('http://') ||\n trimmed.startsWith('https://') ||\n trimmed.startsWith('mailto:') ||\n trimmed.startsWith('tel:') ||\n trimmed.startsWith('/') ||\n trimmed.startsWith('#') ||\n trimmed.startsWith('?')\n ) {\n return url; // Return original (case preserved)\n }\n\n // Allow relative paths without protocol\n if (!trimmed.includes(':')) {\n return url;\n }\n\n // Block everything else\n return '';\n}\n","/**\n * Banner Plugin\n *\n * Renders banner experiences at the top or bottom of the page.\n * Auto-shows banners when experiences are evaluated.\n */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport type { BannerContent, Decision, Experience } from '../types';\nimport { sanitizeHTML } from '../utils/sanitize';\n\nexport interface BannerPluginConfig {\n banner?: {\n position?: 'top' | 'bottom';\n dismissable?: boolean;\n zIndex?: number;\n };\n}\n\nexport interface BannerPlugin {\n show(experience: Experience): void;\n remove(): void;\n isShowing(): boolean;\n}\n\n/**\n * Banner Plugin\n *\n * Automatically renders banner experiences when they are evaluated.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n * import { bannerPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * const sdk = createInstance({ banner: { position: 'top', dismissable: true } });\n * sdk.use(bannerPlugin);\n * ```\n */\nexport const bannerPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('banner');\n\n // Set defaults\n plugin.defaults({\n banner: {\n position: 'top',\n dismissable: true,\n zIndex: 10000,\n },\n });\n\n // Track multiple active banners by experience ID\n const activeBanners = new Map<string, HTMLElement>();\n\n /**\n * Inject default banner styles if not already present\n */\n function injectDefaultStyles(): void {\n const styleId = 'xp-banner-styles';\n if (document.getElementById(styleId)) {\n return; // Already injected\n }\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .xp-banner {\n position: fixed;\n left: 0;\n right: 0;\n width: 100%;\n padding: 16px 20px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n display: flex;\n align-items: center;\n justify-content: space-between;\n box-sizing: border-box;\n z-index: 10000;\n background: #f9fafb;\n color: #111827;\n border-bottom: 1px solid #e5e7eb;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05);\n }\n \n .xp-banner--top {\n top: 0;\n }\n \n .xp-banner--bottom {\n bottom: 0;\n border-bottom: none;\n border-top: 1px solid #e5e7eb;\n box-shadow: 0 -1px 3px 0 rgba(0, 0, 0, 0.05);\n }\n \n .xp-banner__container {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 20px;\n width: 100%;\n }\n \n .xp-banner__content {\n flex: 1;\n min-width: 0;\n }\n \n .xp-banner__title {\n font-weight: 600;\n margin-bottom: 4px;\n margin-top: 0;\n font-size: 14px;\n }\n \n .xp-banner__message {\n margin: 0;\n font-size: 14px;\n }\n \n .xp-banner__buttons {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n flex-shrink: 0;\n }\n \n .xp-banner__button {\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n text-decoration: none;\n }\n \n .xp-banner__button--primary {\n background: #2563eb;\n color: #ffffff;\n }\n \n .xp-banner__button--primary:hover {\n background: #1d4ed8;\n }\n \n .xp-banner__button--secondary {\n background: #ffffff;\n color: #374151;\n border: 1px solid #d1d5db;\n }\n \n .xp-banner__button--secondary:hover {\n background: #f9fafb;\n }\n \n .xp-banner__button--link {\n background: transparent;\n color: #2563eb;\n padding: 4px 8px;\n font-weight: 400;\n text-decoration: underline;\n }\n \n .xp-banner__button--link:hover {\n background: rgba(0, 0, 0, 0.05);\n }\n \n .xp-banner__close {\n background: transparent;\n border: none;\n color: #6b7280;\n font-size: 24px;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n margin: 0;\n opacity: 0.7;\n transition: opacity 0.2s;\n flex-shrink: 0;\n }\n \n .xp-banner__close:hover {\n opacity: 1;\n }\n \n @media (max-width: 640px) {\n .xp-banner__container {\n flex-direction: column;\n align-items: stretch;\n }\n \n .xp-banner__buttons {\n width: 100%;\n flex-direction: column;\n }\n \n .xp-banner__button {\n width: 100%;\n }\n }\n \n /* Dark mode support */\n @media (prefers-color-scheme: dark) {\n .xp-banner {\n background: #1f2937;\n color: #f3f4f6;\n border-bottom-color: #374151;\n }\n \n .xp-banner--bottom {\n border-top-color: #374151;\n }\n \n .xp-banner__button--primary {\n background: #3b82f6;\n }\n \n .xp-banner__button--primary:hover {\n background: #2563eb;\n }\n \n .xp-banner__button--secondary {\n background: #374151;\n color: #f3f4f6;\n border-color: #4b5563;\n }\n \n .xp-banner__button--secondary:hover {\n background: #4b5563;\n }\n \n .xp-banner__button--link {\n color: #93c5fd;\n }\n \n .xp-banner__close {\n color: #9ca3af;\n }\n }\n `;\n document.head.appendChild(style);\n }\n\n /**\n * Create banner DOM element\n */\n function createBannerElement(experience: Experience): HTMLElement {\n const content = experience.content as BannerContent;\n // Allow per-experience position override, fall back to global config\n const position = content.position ?? config.get('banner.position') ?? 'top';\n const dismissable = content.dismissable ?? config.get('banner.dismissable') ?? true;\n const zIndex = config.get('banner.zIndex') ?? 10000;\n\n // Inject default styles if needed\n injectDefaultStyles();\n\n // Create banner container\n const banner = document.createElement('div');\n banner.setAttribute('data-experience-id', experience.id);\n\n // Build className: base classes + position + user's custom class\n const baseClasses = ['xp-banner', `xp-banner--${position}`];\n if (content.className) {\n baseClasses.push(content.className);\n }\n banner.className = baseClasses.join(' ');\n\n // Apply user's custom styles\n if (content.style) {\n Object.assign(banner.style, content.style);\n }\n\n // Override z-index if configured\n if (zIndex !== 10000) {\n banner.style.zIndex = String(zIndex);\n }\n\n // Create container\n const container = document.createElement('div');\n container.className = 'xp-banner__container';\n banner.appendChild(container);\n\n // Create content container\n const contentDiv = document.createElement('div');\n contentDiv.className = 'xp-banner__content';\n\n // Add title if present\n if (content.title) {\n const title = document.createElement('h3');\n title.className = 'xp-banner__title';\n // Sanitize HTML to prevent XSS attacks\n title.innerHTML = sanitizeHTML(content.title);\n contentDiv.appendChild(title);\n }\n\n // Add message\n const message = document.createElement('p');\n message.className = 'xp-banner__message';\n // Sanitize HTML to prevent XSS attacks\n message.innerHTML = sanitizeHTML(content.message);\n contentDiv.appendChild(message);\n\n container.appendChild(contentDiv);\n\n banner.appendChild(contentDiv);\n\n // Create button container for actions and/or dismiss\n const buttonContainer = document.createElement('div');\n buttonContainer.style.cssText = `\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n `;\n\n // Create buttons container\n const buttonsDiv = document.createElement('div');\n buttonsDiv.className = 'xp-banner__buttons';\n\n // Helper function to create button with variant styling\n function createButton(buttonConfig: {\n text: string;\n action?: string;\n url?: string;\n variant?: 'primary' | 'secondary' | 'link';\n metadata?: Record<string, unknown>;\n className?: string;\n style?: Record<string, string>;\n }): HTMLButtonElement {\n const button = document.createElement('button');\n button.textContent = buttonConfig.text;\n\n const variant = buttonConfig.variant || 'primary';\n\n // Build className: base class + variant + user's custom class\n const buttonClasses = ['xp-banner__button', `xp-banner__button--${variant}`];\n if (buttonConfig.className) {\n buttonClasses.push(buttonConfig.className);\n }\n button.className = buttonClasses.join(' ');\n\n // Apply user's custom styles\n if (buttonConfig.style) {\n Object.assign(button.style, buttonConfig.style);\n }\n\n button.addEventListener('click', () => {\n // Emit action event\n instance.emit('experiences:action', {\n experienceId: experience.id,\n type: 'banner',\n action: buttonConfig.action,\n url: buttonConfig.url,\n metadata: buttonConfig.metadata,\n variant: variant,\n timestamp: Date.now(),\n });\n\n // Navigate if URL provided\n if (buttonConfig.url) {\n window.location.href = buttonConfig.url;\n }\n });\n\n return button;\n }\n\n // Add buttons from buttons array\n if (content.buttons && content.buttons.length > 0) {\n content.buttons.forEach((buttonConfig) => {\n const button = createButton(buttonConfig);\n buttonsDiv.appendChild(button);\n });\n }\n\n // Add dismiss button if dismissable\n if (dismissable) {\n const closeButton = document.createElement('button');\n closeButton.className = 'xp-banner__close';\n closeButton.innerHTML = '×';\n closeButton.setAttribute('aria-label', 'Close banner');\n\n closeButton.addEventListener('click', () => {\n remove(experience.id);\n instance.emit('experiences:dismissed', {\n experienceId: experience.id,\n type: 'banner',\n });\n });\n\n buttonsDiv.appendChild(closeButton);\n }\n\n container.appendChild(buttonsDiv);\n\n return banner;\n }\n\n /**\n * Show a banner experience\n */\n function show(experience: Experience): void {\n // If banner already showing for this experience, skip\n if (activeBanners.has(experience.id)) {\n return;\n }\n\n // Only show if we're in a browser environment\n if (typeof document === 'undefined') {\n return;\n }\n\n const banner = createBannerElement(experience);\n document.body.appendChild(banner);\n activeBanners.set(experience.id, banner);\n\n instance.emit('experiences:shown', {\n experienceId: experience.id,\n type: 'banner',\n timestamp: Date.now(),\n });\n }\n\n /**\n * Remove a banner by experience ID (or all if no ID provided)\n */\n function remove(experienceId?: string): void {\n if (experienceId) {\n // Remove specific banner\n const banner = activeBanners.get(experienceId);\n if (banner?.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n activeBanners.delete(experienceId);\n } else {\n // Remove all banners\n for (const [id, banner] of activeBanners.entries()) {\n if (banner?.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n activeBanners.delete(id);\n }\n }\n }\n\n /**\n * Check if any banner is currently showing\n */\n function isShowing(): boolean {\n return activeBanners.size > 0;\n }\n\n // Expose banner API\n plugin.expose({\n banner: {\n show,\n remove,\n isShowing,\n },\n });\n\n // Auto-show banner on experiences:evaluated event\n instance.on('experiences:evaluated', (payload: unknown) => {\n // Handle both single decision and array of decisions\n // evaluate() emits: { decision, experience }\n // evaluateAll() emits: [{ decision, experience }, ...]\n const items = Array.isArray(payload) ? payload : [payload];\n\n for (const item of items) {\n // Item is { decision, experience }\n const typedItem = item as { decision?: Decision; experience?: Experience };\n const decision = typedItem.decision;\n const experience = typedItem.experience;\n\n // Only handle banner-type experiences\n if (experience?.type === 'banner') {\n if (decision?.show) {\n show(experience);\n } else if (experience.id && activeBanners.has(experience.id)) {\n // Hide specific banner if decision says don't show\n remove(experience.id);\n }\n }\n }\n });\n\n // Cleanup on destroy\n instance.on('sdk:destroy', () => {\n remove();\n });\n};\n","/**\n * Debug Plugin\n *\n * Emits structured debug events to window and optionally logs to console.\n * Useful for debugging and Chrome extension integration.\n */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\n\nexport interface DebugPluginConfig {\n debug?: {\n enabled?: boolean;\n console?: boolean;\n window?: boolean;\n };\n}\n\nexport interface DebugPlugin {\n log(message: string, data?: unknown): void;\n isEnabled(): boolean;\n}\n\n/**\n * Debug Plugin\n *\n * Listens to all SDK events and emits them as window events for debugging.\n * Also optionally logs to console.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n * import { debugPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * const sdk = createInstance({ debug: { enabled: true, console: true } });\n * sdk.use(debugPlugin);\n * ```\n */\nexport const debugPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('debug');\n\n // Set defaults\n plugin.defaults({\n debug: {\n enabled: false,\n console: false,\n window: true,\n },\n });\n\n // Helper to check if debug is enabled\n const isEnabled = (): boolean => config.get('debug.enabled') ?? false;\n const shouldLogConsole = (): boolean => config.get('debug.console') ?? false;\n const shouldEmitWindow = (): boolean => config.get('debug.window') ?? true;\n\n // Log function\n const log = (message: string, data?: unknown): void => {\n if (!isEnabled()) return;\n\n const timestamp = new Date().toISOString();\n const logData = {\n timestamp,\n message,\n data,\n };\n\n // Console logging\n if (shouldLogConsole()) {\n console.log(`[experiences] ${message}`, data || '');\n }\n\n // Window event emission\n if (shouldEmitWindow() && typeof window !== 'undefined') {\n const event = new CustomEvent('experience-sdk:debug', {\n detail: logData,\n });\n window.dispatchEvent(event);\n }\n };\n\n // Expose debug API\n plugin.expose({\n debug: {\n log,\n isEnabled,\n },\n });\n\n // If debug is enabled, listen to all events\n if (isEnabled()) {\n // Listen to experiences:* events\n instance.on('experiences:ready', () => {\n if (!isEnabled()) return;\n log('SDK initialized and ready');\n });\n\n instance.on('experiences:registered', (payload) => {\n if (!isEnabled()) return;\n log('Experience registered', payload);\n });\n\n instance.on('experiences:evaluated', (payload) => {\n if (!isEnabled()) return;\n log('Experience evaluated', payload);\n });\n }\n};\n","/**\n * Frequency Capping Plugin\n *\n * Tracks experience impressions and enforces frequency caps.\n * Uses sdk-kit's storage plugin for persistence.\n */\n\nimport type { PluginFunction, SDK } from '@lytics/sdk-kit';\nimport { type StoragePlugin, storagePlugin } from '@lytics/sdk-kit-plugins';\nimport type { Decision, TraceStep } from '../types';\n\nexport interface FrequencyPluginConfig {\n frequency?: {\n enabled?: boolean;\n namespace?: string;\n };\n}\n\nexport interface FrequencyPlugin {\n getImpressionCount(experienceId: string): number;\n hasReachedCap(experienceId: string, max: number, per: 'session' | 'day' | 'week'): boolean;\n recordImpression(experienceId: string): void;\n}\n\ninterface ImpressionData {\n count: number;\n lastImpression: number;\n impressions: number[];\n per?: 'session' | 'day' | 'week'; // Track which storage type this uses\n}\n\n/**\n * Frequency Capping Plugin\n *\n * Automatically tracks impressions and enforces frequency caps.\n * Requires storage plugin for persistence.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n * import { frequencyPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * const sdk = createInstance({ frequency: { enabled: true } });\n * sdk.use(frequencyPlugin);\n * ```\n */\nexport const frequencyPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('frequency');\n\n // Set defaults\n plugin.defaults({\n frequency: {\n enabled: true,\n namespace: 'experiences:frequency',\n },\n });\n\n // Track experience frequency configs\n const experienceFrequencyMap = new Map<string, 'session' | 'day' | 'week'>();\n\n // Auto-load storage plugin if not already loaded\n if (!(instance as SDK & { storage?: StoragePlugin }).storage) {\n instance.use(storagePlugin);\n }\n\n const isEnabled = (): boolean => config.get('frequency.enabled') ?? true;\n const getNamespace = (): string => config.get('frequency.namespace') ?? 'experiences:frequency';\n\n // Helper to get the right storage backend based on frequency type\n const getStorageBackend = (per: 'session' | 'day' | 'week'): Storage => {\n return per === 'session' ? sessionStorage : localStorage;\n };\n\n // Helper to get storage key\n const getStorageKey = (experienceId: string): string => {\n return `${getNamespace()}:${experienceId}`;\n };\n\n // Helper to get impression data\n const getImpressionData = (\n experienceId: string,\n per: 'session' | 'day' | 'week'\n ): ImpressionData => {\n const storage = getStorageBackend(per);\n const key = getStorageKey(experienceId);\n const raw = storage.getItem(key);\n\n if (!raw) {\n return {\n count: 0,\n lastImpression: 0,\n impressions: [],\n per,\n };\n }\n\n try {\n return JSON.parse(raw) as ImpressionData;\n } catch {\n return {\n count: 0,\n lastImpression: 0,\n impressions: [],\n per,\n };\n }\n };\n\n // Helper to save impression data\n const saveImpressionData = (experienceId: string, data: ImpressionData): void => {\n const per = data.per || 'session'; // Default to session if not specified\n const storage = getStorageBackend(per);\n const key = getStorageKey(experienceId);\n storage.setItem(key, JSON.stringify(data));\n };\n\n // Get time window in milliseconds\n const getTimeWindow = (per: 'session' | 'day' | 'week'): number => {\n switch (per) {\n case 'session':\n return Number.POSITIVE_INFINITY; // Session storage handles this\n case 'day':\n return 24 * 60 * 60 * 1000; // 24 hours\n case 'week':\n return 7 * 24 * 60 * 60 * 1000; // 7 days\n }\n };\n\n /**\n * Get impression count for an experience\n */\n const getImpressionCount = (\n experienceId: string,\n per: 'session' | 'day' | 'week' = 'session'\n ): number => {\n if (!isEnabled()) return 0;\n const data = getImpressionData(experienceId, per);\n return data.count;\n };\n\n /**\n * Check if an experience has reached its frequency cap\n */\n const hasReachedCap = (\n experienceId: string,\n max: number,\n per: 'session' | 'day' | 'week'\n ): boolean => {\n if (!isEnabled()) return false;\n\n const data = getImpressionData(experienceId, per);\n const timeWindow = getTimeWindow(per);\n const now = Date.now();\n\n // For session caps, just check total count\n if (per === 'session') {\n return data.count >= max;\n }\n\n // For time-based caps, count impressions within the window\n const recentImpressions = data.impressions.filter((timestamp) => now - timestamp < timeWindow);\n\n return recentImpressions.length >= max;\n };\n\n /**\n * Record an impression for an experience\n */\n const recordImpression = (\n experienceId: string,\n per: 'session' | 'day' | 'week' = 'session'\n ): void => {\n if (!isEnabled()) return;\n\n const data = getImpressionData(experienceId, per);\n const now = Date.now();\n\n // Update count and add timestamp\n data.count += 1;\n data.lastImpression = now;\n data.impressions.push(now);\n data.per = per; // Store the frequency type\n\n // Keep only recent impressions (last 7 days)\n const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;\n data.impressions = data.impressions.filter((ts) => ts > sevenDaysAgo);\n\n // Save updated data\n saveImpressionData(experienceId, data);\n\n // Emit event\n instance.emit('experiences:impression-recorded', {\n experienceId,\n count: data.count,\n timestamp: now,\n });\n };\n\n // Expose frequency API\n plugin.expose({\n frequency: {\n getImpressionCount,\n hasReachedCap,\n recordImpression,\n // Internal method to register experience frequency config\n _registerExperience: (experienceId: string, per: 'session' | 'day' | 'week') => {\n experienceFrequencyMap.set(experienceId, per);\n },\n },\n });\n\n // Listen to evaluation events and record impressions\n if (isEnabled()) {\n instance.on('experiences:evaluated', (payload: unknown) => {\n // Handle both single decision and array of decisions\n // evaluate() emits: { decision, experience }\n // evaluateAll() emits: [{ decision, experience }, ...]\n const items = Array.isArray(payload) ? payload : [payload];\n\n for (const item of items) {\n // Item is { decision, experience }\n const decision = (item as { decision?: Decision }).decision;\n\n // Only record if experience was shown\n if (decision?.show && decision.experienceId) {\n // Try to get the 'per' value from our map, fall back to checking the input in trace\n let per: 'session' | 'day' | 'week' =\n experienceFrequencyMap.get(decision.experienceId) || 'session';\n\n // If not in map, try to infer from the decision trace\n if (!experienceFrequencyMap.has(decision.experienceId)) {\n const freqStep = decision.trace.find(\n (t: TraceStep) => t.step === 'check-frequency-cap'\n );\n if (freqStep?.input && typeof freqStep.input === 'object' && 'per' in freqStep.input) {\n per = (freqStep.input as { per: 'session' | 'day' | 'week' }).per;\n // Cache it for next time\n experienceFrequencyMap.set(decision.experienceId, per);\n }\n }\n\n recordImpression(decision.experienceId, per);\n }\n }\n });\n }\n};\n","/**\n * Deep merge utility with \"underwrite\" pattern.\n *\n * Recursively merges source into target, where **target values take precedence**.\n * This is the opposite of typical deep merge where source wins.\n *\n * This \"underwrite\" pattern is used for config defaults:\n * - User config (target) always wins\n * - Defaults (source) only fill in missing values\n *\n * @param target - Target object (takes precedence)\n * @param source - Source object (provides defaults)\n * @returns New merged object (does not mutate inputs)\n *\n * @example\n * ```typescript\n * const userConfig = { api: { timeout: 5000 } };\n * const defaults = { api: { timeout: 3000, retries: 3 }, debug: false };\n *\n * const result = deepMerge(target, source);\n * // Result: {\n * // api: { timeout: 5000, retries: 3 }, // timeout from user, retries from defaults\n * // debug: false // debug from defaults\n * // }\n * ```\n */\nexport function deepMerge(\n target: Record<string, any>,\n source: Record<string, any>\n): Record<string, any> {\n // Create a new object to avoid mutation\n const result: Record<string, any> = { ...target };\n\n for (const key in source) {\n if (!Object.hasOwn(source, key)) {\n continue;\n }\n\n const sourceValue = source[key];\n const targetValue = result[key];\n\n // If target has no value for this key, use source value\n if (targetValue === undefined) {\n result[key] = sourceValue;\n continue;\n }\n\n // If both are plain objects, recursively merge\n if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {\n result[key] = deepMerge(targetValue, sourceValue);\n }\n\n // Otherwise, target value wins (underwrite pattern)\n // result[key] is already set from { ...target }\n }\n\n return result;\n}\n\n/**\n * Check if value is a plain object (not array, null, Date, etc.)\n *\n * @param value - Value to check\n * @returns True if value is a plain object\n * @private\n */\nfunction isPlainObject(value: any): value is Record<string, any> {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n // Check if it's a plain object (not Array, Date, RegExp, etc.)\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n","/**\n * Configuration management with dot-notation paths and underwrite pattern.\n *\n * The Config class manages SDK configuration with support for:\n * - Dot-notation paths for nested access (`'my.plugin.setting'`)\n * - Underwrite pattern for defaults (existing values take precedence)\n * - Required plugin tracking\n * - Immutable getAll() for safe access\n *\n * @example\n * ```typescript\n * const config = new Config({ api: { timeout: 5000 } });\n *\n * // Set defaults (won't overwrite existing timeout)\n * config.defaults({ api: { timeout: 3000, retries: 3 } });\n *\n * // Get with dot-notation\n * config.get('api.timeout'); // 5000\n * config.get('api.retries'); // 3\n *\n * // Set values\n * config.set('api.baseUrl', 'https://api.example.com');\n * ```\n */\n\nimport { deepMerge } from '../util/deep-merge';\n\nexport class Config {\n private data: Record<string, any> = {};\n private required = new Set<string>();\n\n /**\n * Create a new Config instance.\n *\n * @param initialConfig - Initial configuration object\n */\n constructor(initialConfig: Record<string, any> = {}) {\n // Deep copy to prevent external mutation\n this.data = JSON.parse(JSON.stringify(initialConfig));\n }\n\n /**\n * Set default configuration values using underwrite pattern.\n *\n * Existing configuration values always take precedence over defaults.\n * This allows plugins to provide defaults without overwriting user config.\n *\n * @param config - Default configuration object\n *\n * @example\n * ```typescript\n * // User provided config\n * const config = new Config({ api: { timeout: 10000 } });\n *\n * // Plugin sets defaults\n * config.defaults({\n * api: { timeout: 3000, retries: 3 },\n * debug: false\n * });\n *\n * // Result: { api: { timeout: 10000, retries: 3 }, debug: false }\n * // User's timeout (10000) wins over default (3000)\n * ```\n */\n defaults(config: Record<string, any>): void {\n // Use underwrite pattern: existing config (target) wins over defaults (source)\n this.data = deepMerge(this.data, config);\n }\n\n /**\n * Merge configuration values (new values override existing).\n *\n * New configuration values take precedence over existing values.\n * This is the opposite of defaults() and is used when user provides new config.\n *\n * @param config - Configuration object to merge\n *\n * @example\n * ```typescript\n * const config = new Config({ api: { timeout: 3000 } });\n *\n * // User provides new config\n * config.merge({ api: { timeout: 10000, retries: 3 } });\n *\n * // Result: { api: { timeout: 10000, retries: 3 } }\n * // New timeout (10000) wins over existing (3000)\n * ```\n */\n merge(config: Record<string, any>): void {\n // Regular merge: new config (source) wins over existing (target)\n this.data = deepMerge(config, this.data);\n }\n\n /**\n * Get a configuration value by dot-notation path.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @returns Configuration value or undefined if not found\n *\n * @example\n * ```typescript\n * config.get('api.timeout'); // 5000\n * config.get('api.retries'); // 3\n * config.get('nonexistent.path'); // undefined\n * config.get<number>('api.timeout'); // Type-safe access\n * ```\n */\n get<T = any>(path: string): T {\n const keys = path.split('.');\n let current: any = this.data;\n\n for (const key of keys) {\n if (current == null) {\n return undefined as T;\n }\n current = current[key];\n }\n\n return current as T;\n }\n\n /**\n * Set a configuration value by dot-notation path.\n *\n * Creates intermediate objects as needed.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @param value - Value to set\n *\n * @example\n * ```typescript\n * config.set('api.timeout', 5000);\n * config.set('api.headers.authorization', 'Bearer token');\n * config.set('deeply.nested.value', 42);\n * ```\n */\n set(path: string, value: any): void {\n const keys = path.split('.');\n const lastKey = keys.pop();\n if (!lastKey) return; // Guard against empty path\n\n let current: any = this.data;\n\n // Create intermediate objects\n for (const key of keys) {\n if (current[key] == null || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key];\n }\n\n current[lastKey] = value;\n }\n\n /**\n * Mark a plugin namespace as required.\n *\n * Required plugins must be properly enabled for the SDK to function.\n *\n * @param namespace - Plugin namespace to mark as required\n *\n * @example\n * ```typescript\n * config.markRequired('analytics');\n * config.isRequired('analytics'); // true\n * ```\n */\n markRequired(namespace?: string): void {\n if (namespace !== undefined && namespace !== null) {\n this.required.add(namespace);\n }\n }\n\n /**\n * Check if a plugin namespace is marked as required.\n *\n * @param namespace - Plugin namespace to check\n * @returns True if the namespace is required\n */\n isRequired(namespace: string): boolean {\n return this.required.has(namespace);\n }\n\n /**\n * Get all configuration as an immutable copy.\n *\n * Returns a deep copy to prevent external mutation of config.\n *\n * @returns Copy of the entire configuration object\n *\n * @example\n * ```typescript\n * const allConfig = config.getAll();\n * allConfig.api = {}; // Does not affect internal config\n * ```\n */\n getAll(): Record<string, any> {\n // Deep copy to prevent nested mutation\n return JSON.parse(JSON.stringify(this.data));\n }\n}\n","/**\n * Event Emitter with wildcard pattern matching support.\n *\n * Supports wildcard patterns for event subscriptions:\n * - Exact match: `'widget.show'` matches `'widget.show'`\n * - Wildcard suffix: `'widget.*'` matches `'widget.show'`, `'widget.hide'`, etc.\n * - Wildcard prefix: `'*.show'` matches `'widget.show'`, `'modal.show'`, etc.\n * - Match all: `'*'` matches all events\n *\n * Uses regex compilation for efficient pattern matching.\n */\n\n/**\n * Subscription entry storing the pattern and handler.\n */\ninterface Subscription {\n pattern: string;\n compiledPattern: RegExp;\n handler: (...args: any[]) => void;\n}\n\nexport class Emitter {\n private subscriptions: Subscription[] = [];\n\n /**\n * Subscribe to an event or pattern.\n *\n * @param event - Event name or wildcard pattern\n * @param handler - Event handler function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const emitter = new Emitter();\n *\n * // Exact match\n * emitter.on('user.login', (data) => console.log('Login:', data));\n *\n * // Wildcard patterns\n * emitter.on('user.*', (data) => console.log('User event:', data));\n * emitter.on('*.error', (data) => console.log('Error:', data));\n * emitter.on('*', (data) => console.log('Any event:', data));\n *\n * // Unsubscribe\n * const unsub = emitter.on('test', handler);\n * unsub();\n * ```\n */\n on(event: string, handler: (...args: any[]) => void): () => void {\n if (typeof handler !== 'function') {\n throw new TypeError('handler must be a function');\n }\n\n const subscription: Subscription = {\n pattern: event,\n compiledPattern: this.compilePattern(event),\n handler,\n };\n\n this.subscriptions.push(subscription);\n\n // Return unsubscribe function\n return () => this.off(event, handler);\n }\n\n /**\n * Unsubscribe from an event or pattern.\n *\n * @param event - Event name or pattern to unsubscribe from\n * @param handler - Handler function to remove\n */\n off(event: string, handler: (...args: any[]) => void): void {\n this.subscriptions = this.subscriptions.filter(\n (sub) => !(sub.pattern === event && sub.handler === handler)\n );\n }\n\n /**\n * Emit an event to all matching subscribers.\n *\n * Notifies all handlers whose patterns match the event name.\n * Handlers are called with the provided arguments.\n * Errors in handlers are caught and logged to prevent breaking the event flow.\n *\n * @param event - Event name to emit\n * @param args - Arguments to pass to handlers\n *\n * @example\n * ```typescript\n * emitter.emit('user.login', { userId: '123' });\n * emitter.emit('widget.show', { id: 'modal-1' }, { animate: true });\n * ```\n */\n emit(event: string, ...args: any[]): void {\n for (const subscription of this.subscriptions) {\n if (subscription.compiledPattern.test(event)) {\n try {\n subscription.handler(...args);\n } catch (err) {\n console.error(`Error in event handler for \"${event}\":`, err);\n }\n }\n }\n }\n\n /**\n * Remove all event listeners.\n *\n * Useful for cleanup when destroying the SDK instance.\n */\n removeAllListeners(): void {\n this.subscriptions = [];\n }\n\n /**\n * Compile a pattern string into a RegExp for matching.\n *\n * Converts wildcard patterns into regular expressions:\n * - `widget.*` → `/^widget\\.(.*?)$/`\n * - `*.show` → `/^(.*?)\\.show$/`\n * - `*` → `/^(.*?)$/`\n *\n * @param pattern - Pattern string with optional wildcards\n * @returns Compiled RegExp for matching event names\n * @private\n */\n private compilePattern(pattern: string): RegExp {\n // Escape all regex special characters\n const escaped = this.escapeRegExp(pattern);\n // Replace escaped asterisks with wildcard capture groups\n const withWildcards = escaped.replace(/\\\\\\*/g, '(.*?)');\n // Anchor to start and end\n return new RegExp(`^${withWildcards}$`);\n }\n\n /**\n * Escape special regex characters in a string.\n *\n * @param str - String to escape\n * @returns Escaped string safe for use in RegExp\n * @private\n */\n private escapeRegExp(str: string): string {\n return str.replace(/[\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n }\n}\n","/**\n * Expose capability for adding plugin methods to SDK instance.\n *\n * Plugins use this to add public API methods that users can call.\n * Methods are merged directly onto the SDK instance.\n *\n * @example\n * ```typescript\n * const expose = new Expose(sdkInstance);\n * expose.expose({\n * track(event: string) {\n * console.log('Tracking:', event);\n * }\n * });\n * // Now: sdk.track('page_view');\n * ```\n */\n\nexport class Expose {\n private sdk: any;\n\n /**\n * Create an Expose instance.\n *\n * @param sdk - SDK instance to expose methods on\n */\n constructor(sdk: any) {\n this.sdk = sdk;\n }\n\n /**\n * Expose methods on the SDK instance.\n *\n * Merges the provided methods object onto the SDK instance,\n * making them available as `sdk.methodName()`.\n *\n * @param api - Object containing methods to expose\n *\n * @example\n * ```typescript\n * expose.expose({\n * track(event: string, properties?: any) {\n * // Track event\n * },\n * identify(userId: string) {\n * // Identify user\n * }\n * });\n *\n * // Available as:\n * // sdk.track('page_view', { path: '/home' });\n * // sdk.identify('user-123');\n * ```\n */\n expose(api: Record<string, any>): void {\n Object.assign(this.sdk, api);\n }\n}\n","/**\n * Namespace capability for plugin identification.\n *\n * Each plugin must claim a unique namespace to identify itself.\n * Namespaces are used for config scoping and event namespacing.\n *\n * @example\n * ```typescript\n * const namespace = new Namespace();\n * namespace.ns('analytics.tracking');\n * console.log(namespace.name); // 'analytics.tracking'\n * ```\n */\n\nexport class Namespace {\n /**\n * The plugin's namespace.\n * Set via the `ns()` method, can only be set once.\n */\n name: string = '';\n\n /**\n * Set the plugin's namespace.\n *\n * Can only be called once per instance. Attempting to set it again throws an error.\n *\n * @param namespace - Dot-notation namespace (e.g., 'analytics.tracking')\n * @throws Error if namespace is already set\n *\n * @example\n * ```typescript\n * const ns = new Namespace();\n * ns.ns('my.plugin'); // Success\n * ns.ns('other.plugin'); // Throws Error\n * ```\n */\n ns(namespace: string): void {\n if (this.name) {\n throw new Error(\n `Namespace already set to \"${this.name}\". Cannot reassign to \"${namespace}\".`\n );\n }\n this.name = namespace;\n }\n}\n","/**\n * Main SDK class\n *\n * Composes all capabilities and provides the plugin registration system.\n * Follows functional plugin architecture with capability injection.\n */\n\nimport { Config } from './capabilities/config';\nimport { Emitter } from './capabilities/emitter';\nimport { Expose } from './capabilities/expose';\nimport { Namespace } from './capabilities/namespace';\nimport type { Plugin, PluginFunction, SDKConfig } from './types';\n\n/**\n * SDK - Main SDK class for building JavaScript SDKs\n *\n * @example\n * ```typescript\n * const sdk = new SDK({ name: 'my-sdk' });\n *\n * sdk.use((plugin, instance, config) => {\n * plugin.ns('my.plugin');\n * plugin.defaults({ my: { setting: 'value' } });\n * plugin.expose({\n * track(event: string) {\n * console.log('Tracking:', event);\n * }\n * });\n * });\n *\n * await sdk.init();\n * sdk.track('page_view');\n * ```\n */\nexport class SDK {\n /**\n * Configuration instance\n * @private\n */\n private configInstance: Config;\n\n /**\n * Event emitter instance\n * @private\n */\n private emitter: Emitter;\n\n /**\n * Registered plugins by namespace\n * @private\n */\n private plugins: Map<string, { namespace: Namespace; plugin: Plugin }> = new Map();\n\n /**\n * Initialization state\n * @private\n */\n private isInitialized = false;\n\n /**\n * Create a new SDK instance.\n *\n * @param config - Initial SDK configuration\n *\n * @example\n * ```typescript\n * const sdk = new SDK({\n * name: 'my-sdk',\n * api: {\n * endpoint: 'https://api.example.com',\n * timeout: 5000\n * }\n * });\n * ```\n */\n constructor(config: SDKConfig = {}) {\n this.emitter = new Emitter();\n this.configInstance = new Config(config);\n }\n\n /**\n * Register a plugin.\n *\n * Plugins receive capability-injected objects and can extend the SDK.\n * Supports method chaining.\n *\n * @param pluginFn - Plugin function\n * @returns this - For method chaining\n *\n * @example\n * ```typescript\n * sdk\n * .use(analyticsPlugin)\n * .use(trackingPlugin)\n * .init();\n * ```\n */\n use(pluginFn: PluginFunction): this {\n // Create capability instances for this plugin\n const namespace = new Namespace();\n const expose = new Expose(this);\n\n // Compose Plugin object with all capabilities\n const plugin: Plugin = {\n // Namespace capability\n ns: namespace.ns.bind(namespace),\n\n // Config capabilities\n defaults: this.configInstance.defaults.bind(this.configInstance),\n\n // Emitter capabilities\n on: this.emitter.on.bind(this.emitter),\n off: this.emitter.off.bind(this.emitter),\n emit: this.emitter.emit.bind(this.emitter),\n\n // Expose capability\n expose: expose.expose.bind(expose),\n\n // Requirer capability\n mustEnable: () => {\n if (namespace.name) {\n this.configInstance.markRequired(namespace.name);\n }\n },\n };\n\n // Execute plugin function with capability injection\n pluginFn(plugin, this, this.configInstance);\n\n // Store plugin by namespace\n if (namespace.name) {\n this.plugins.set(namespace.name, { namespace, plugin });\n }\n\n return this;\n }\n\n /**\n * Initialize the SDK.\n *\n * Emits `sdk:init` event before initialization and `sdk:ready` after.\n * Idempotent - can be called multiple times safely.\n *\n * @param config - Optional configuration to merge with existing config\n * @returns Promise that resolves when initialization is complete\n *\n * @example\n * ```typescript\n * sdk.on('sdk:ready', () => {\n * console.log('SDK is ready!');\n * });\n *\n * await sdk.init({ api: { timeout: 5000 } });\n * ```\n */\n async init(config?: SDKConfig): Promise<void> {\n if (this.isInitialized) {\n console.warn('SDK already initialized');\n return;\n }\n\n // Merge config if provided (new values override defaults)\n if (config) {\n this.configInstance.merge(config);\n }\n\n // Emit init event (plugins can listen for this)\n this.emitter.emit('sdk:init');\n\n // Check for required plugins\n const requiredNamespaces = Array.from(this.plugins.keys()).filter((ns) =>\n this.configInstance.isRequired(ns)\n );\n\n for (const ns of requiredNamespaces) {\n if (!this.plugins.has(ns)) {\n throw new Error(`Required plugin \"${ns}\" is not registered`);\n }\n }\n\n this.isInitialized = true;\n\n // Emit ready event\n this.emitter.emit('sdk:ready');\n }\n\n /**\n * Destroy the SDK.\n *\n * Emits `sdk:destroy` event, clears all plugins, and removes all event listeners.\n *\n * @returns Promise that resolves when destruction is complete\n *\n * @example\n * ```typescript\n * await sdk.destroy();\n * ```\n */\n async destroy(): Promise<void> {\n // Emit destroy event (plugins can listen for cleanup)\n this.emitter.emit('sdk:destroy');\n\n // Clear plugins\n this.plugins.clear();\n\n // Remove all event listeners\n this.emitter.removeAllListeners();\n\n this.isInitialized = false;\n }\n\n /**\n * Get a configuration value by path.\n *\n * Delegates to the Config capability.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @returns Configuration value\n *\n * @example\n * ```typescript\n * const timeout = sdk.get('api.timeout');\n * const nested = sdk.get('my.plugin.setting');\n * ```\n */\n get<T = any>(path: string): T {\n return this.configInstance.get(path);\n }\n\n /**\n * Set a configuration value by path.\n *\n * Delegates to the Config capability.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @param value - Value to set\n *\n * @example\n * ```typescript\n * sdk.set('api.timeout', 10000);\n * sdk.set('my.plugin.enabled', true);\n * ```\n */\n set(path: string, value: any): void {\n this.configInstance.set(path, value);\n }\n\n /**\n * Subscribe to an event.\n *\n * Delegates to the Emitter capability. Supports wildcard patterns.\n *\n * @param event - Event name or pattern (e.g., 'track:*')\n * @param handler - Event handler function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = sdk.on('track:page', (data) => {\n * console.log('Page tracked:', data);\n * });\n *\n * // Later...\n * unsubscribe();\n * ```\n */\n on(event: string, handler: (...args: any[]) => void): () => void {\n return this.emitter.on(event, handler);\n }\n\n /**\n * Unsubscribe from an event.\n *\n * Delegates to the Emitter capability.\n *\n * @param event - Event name\n * @param handler - Event handler function to remove\n *\n * @example\n * ```typescript\n * const handler = () => console.log('ready');\n * sdk.on('sdk:ready', handler);\n * sdk.off('sdk:ready', handler);\n * ```\n */\n off(event: string, handler: (...args: any[]) => void): void {\n this.emitter.off(event, handler);\n }\n\n /**\n * Emit an event.\n *\n * Delegates to the Emitter capability.\n *\n * @param event - Event name\n * @param args - Event arguments\n *\n * @example\n * ```typescript\n * sdk.emit('custom:event', { data: 'value' });\n * ```\n */\n emit(event: string, ...args: any[]): void {\n this.emitter.emit(event, ...args);\n }\n\n /**\n * Get all configuration as an immutable copy.\n *\n * Delegates to the Config capability.\n *\n * @returns Copy of the entire configuration object\n *\n * @example\n * ```typescript\n * const allConfig = sdk.getAll();\n * console.log(allConfig);\n * ```\n */\n getAll(): Record<string, any> {\n return this.configInstance.getAll();\n }\n\n /**\n * Check if SDK is initialized.\n *\n * @returns true if initialized, false otherwise\n *\n * @example\n * ```typescript\n * if (sdk.isReady()) {\n * sdk.track('event');\n * }\n * ```\n */\n isReady(): boolean {\n return this.isInitialized;\n }\n}\n","import { SDK } from '@lytics/sdk-kit';\nimport { storagePlugin } from '@lytics/sdk-kit-plugins';\nimport { bannerPlugin, debugPlugin, frequencyPlugin } from '@prosdevlab/experience-sdk-plugins';\nimport type {\n Context,\n Decision,\n Experience,\n ExperienceConfig,\n RuntimeState,\n TraceStep,\n UrlRule,\n} from './types';\n\n/**\n * Experience Runtime\n *\n * Core class that manages experience registration and evaluation.\n * Built on @lytics/sdk-kit for plugin system and lifecycle management.\n *\n * Design principles:\n * - Pure functions for evaluation logic (easy to test)\n * - Event-driven architecture (extensible via plugins)\n * - Explainability-first (every decision has reasons)\n */\nexport class ExperienceRuntime {\n private sdk: SDK;\n private experiences: Map<string, Experience> = new Map();\n private decisions: Decision[] = [];\n private initialized = false;\n private destroyed = false;\n\n constructor(config: ExperienceConfig = {}) {\n // Create SDK instance\n this.sdk = new SDK({\n name: 'experience-sdk',\n ...config,\n });\n\n // Auto-register plugins\n this.sdk.use(storagePlugin);\n this.sdk.use(debugPlugin);\n this.sdk.use(frequencyPlugin);\n this.sdk.use(bannerPlugin);\n }\n\n /**\n * Initialize the runtime\n */\n async init(config?: ExperienceConfig): Promise<void> {\n if (this.initialized) {\n console.warn('[experiences] Already initialized');\n return;\n }\n\n // Recreate SDK if it was destroyed\n if (this.destroyed) {\n this.sdk = new SDK({\n name: 'experience-sdk',\n ...config,\n });\n\n // Re-register core plugins\n this.sdk.use(storagePlugin);\n this.sdk.use(debugPlugin);\n this.sdk.use(frequencyPlugin);\n this.sdk.use(bannerPlugin);\n\n this.destroyed = false;\n }\n\n if (config) {\n // Merge config if provided\n Object.entries(config).forEach(([key, value]) => {\n this.sdk.set(key, value);\n });\n }\n\n // Initialize SDK (will init all plugins)\n await this.sdk.init();\n\n this.initialized = true;\n\n // Emit ready event\n this.sdk.emit('experiences:ready');\n }\n\n /**\n * Register an experience\n */\n register(id: string, experience: Omit<Experience, 'id'>): void {\n const exp: Experience = { id, ...experience };\n this.experiences.set(id, exp);\n\n // Register frequency config with frequency plugin if it exists\n if (exp.frequency && (this.sdk as any).frequency?._registerExperience) {\n (this.sdk as any).frequency._registerExperience(id, exp.frequency.per);\n }\n\n this.sdk.emit('experiences:registered', { id, experience: exp });\n }\n\n /**\n * Evaluate experiences against context\n * Returns decision with explainability\n * First match wins (use evaluateAll() for multiple experiences)\n */\n evaluate(context?: Partial<Context>): Decision {\n const startTime = Date.now();\n const evalContext = buildContext(context);\n\n // Find matching experience\n let matchedExperience: Experience | undefined;\n const allReasons: string[] = [];\n const allTrace: TraceStep[] = [];\n\n for (const [, experience] of this.experiences) {\n const result = evaluateExperience(experience, evalContext);\n\n allReasons.push(...result.reasons);\n allTrace.push(...result.trace);\n\n if (result.matched) {\n // Check frequency cap if experience has frequency rules\n if (experience.frequency && (this.sdk as any).frequency) {\n const freqStart = Date.now();\n const hasReached = (this.sdk as any).frequency.hasReachedCap(\n experience.id,\n experience.frequency.max,\n experience.frequency.per\n );\n\n allTrace.push({\n step: 'check-frequency-cap',\n timestamp: freqStart,\n duration: Date.now() - freqStart,\n input: experience.frequency,\n output: hasReached,\n passed: !hasReached,\n });\n\n if (hasReached) {\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n allReasons.push(\n `Frequency cap reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n continue; // Skip this experience, check next\n }\n\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n allReasons.push(\n `Frequency cap not reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n }\n\n matchedExperience = experience;\n break; // First match wins\n }\n }\n\n const decision: Decision = {\n show: !!matchedExperience,\n experienceId: matchedExperience?.id,\n reasons: allReasons,\n trace: allTrace,\n context: evalContext,\n metadata: {\n evaluatedAt: Date.now(),\n totalDuration: Date.now() - startTime,\n experiencesEvaluated: this.experiences.size,\n },\n };\n\n // Store decision for inspection\n this.decisions.push(decision);\n\n // Emit for plugins to react (include matched experience for rendering)\n this.sdk.emit('experiences:evaluated', {\n decision,\n experience: matchedExperience,\n });\n\n return decision;\n }\n\n /**\n * Evaluate all experiences against context\n * Returns multiple decisions (sorted by priority)\n * All matching experiences will be shown\n */\n evaluateAll(context?: Partial<Context>): Decision[] {\n const evalContext = buildContext(context);\n\n // Sort experiences by priority (higher = more important)\n // Ties maintain registration order (Map preserves insertion order)\n const sortedExperiences = Array.from(this.experiences.values()).sort((a, b) => {\n const priorityA = a.priority ?? 0;\n const priorityB = b.priority ?? 0;\n return priorityB - priorityA; // Descending order\n });\n\n const decisions: Decision[] = [];\n\n // Evaluate each experience\n for (const experience of sortedExperiences) {\n const expStartTime = Date.now();\n const result = evaluateExperience(experience, evalContext);\n\n let show = result.matched;\n const reasons = [...result.reasons];\n const trace = [...result.trace];\n\n // Check frequency cap if experience has frequency rules\n if (show && experience.frequency && (this.sdk as any).frequency) {\n const freqStart = Date.now();\n const hasReached = (this.sdk as any).frequency.hasReachedCap(\n experience.id,\n experience.frequency.max,\n experience.frequency.per\n );\n\n trace.push({\n step: 'check-frequency-cap',\n timestamp: freqStart,\n duration: Date.now() - freqStart,\n input: experience.frequency,\n output: hasReached,\n passed: !hasReached,\n });\n\n if (hasReached) {\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n reasons.push(\n `Frequency cap reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n show = false;\n } else {\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n reasons.push(\n `Frequency cap not reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n }\n }\n\n const decision: Decision = {\n show,\n experienceId: experience.id,\n reasons,\n trace,\n context: evalContext,\n metadata: {\n evaluatedAt: Date.now(),\n totalDuration: Date.now() - expStartTime,\n experiencesEvaluated: 1,\n },\n };\n\n decisions.push(decision);\n this.decisions.push(decision);\n }\n\n // Emit single event with all decisions (array)\n // Plugins can filter to their relevant experiences\n const matchedDecisions = decisions.filter((d) => d.show);\n const matchedExperiences = matchedDecisions\n .map((d) => d.experienceId && this.experiences.get(d.experienceId))\n .filter((exp): exp is Experience => exp !== undefined);\n\n this.sdk.emit(\n 'experiences:evaluated',\n matchedDecisions.map((decision, index) => ({\n decision,\n experience: matchedExperiences[index],\n }))\n );\n\n return decisions;\n }\n\n /**\n * Explain a specific experience\n */\n explain(experienceId: string): Decision | null {\n const experience = this.experiences.get(experienceId);\n if (!experience) {\n return null;\n }\n\n const context = buildContext();\n const result = evaluateExperience(experience, context);\n\n return {\n show: result.matched,\n experienceId,\n reasons: result.reasons,\n trace: result.trace,\n context,\n metadata: {\n evaluatedAt: Date.now(),\n totalDuration: 0,\n experiencesEvaluated: 1,\n },\n };\n }\n\n /**\n * Get runtime state (for inspection)\n */\n getState(): RuntimeState {\n return {\n initialized: this.initialized,\n experiences: new Map(this.experiences),\n decisions: [...this.decisions],\n config: this.sdk ? this.sdk.getAll() : {},\n };\n }\n\n /**\n * Event subscription (proxy to SDK)\n */\n on(event: string, handler: (...args: any[]) => void): () => void {\n return this.sdk.on(event, handler);\n }\n\n /**\n * Destroy runtime\n */\n async destroy(): Promise<void> {\n if (this.sdk) {\n await this.sdk.destroy();\n }\n this.destroyed = true;\n this.experiences.clear();\n this.decisions = [];\n this.initialized = false;\n }\n}\n\n// Pure functions for evaluation logic (easy to test, no dependencies)\n\n/**\n * Build evaluation context from partial input\n * Pure function - no side effects\n */\nexport function buildContext(partial?: Partial<Context>): Context {\n return {\n url: partial?.url ?? (typeof window !== 'undefined' ? window.location.href : ''),\n timestamp: Date.now(),\n user: partial?.user,\n custom: partial?.custom,\n };\n}\n\n/**\n * Evaluate an experience against context\n * Pure function - returns reasons and trace\n */\nexport function evaluateExperience(\n experience: Experience,\n context: Context\n): { matched: boolean; reasons: string[]; trace: TraceStep[] } {\n const reasons: string[] = [];\n const trace: TraceStep[] = [];\n let matched = true;\n\n // Evaluate URL rule\n if (experience.targeting.url) {\n const urlStart = Date.now();\n const urlMatch = evaluateUrlRule(experience.targeting.url, context.url);\n\n trace.push({\n step: 'evaluate-url-rule',\n timestamp: urlStart,\n duration: Date.now() - urlStart,\n input: { rule: experience.targeting.url, url: context.url },\n output: urlMatch,\n passed: urlMatch,\n });\n\n if (urlMatch) {\n reasons.push('URL matches targeting rule');\n } else {\n reasons.push('URL does not match targeting rule');\n matched = false;\n }\n }\n\n return { matched, reasons, trace };\n}\n\n/**\n * Evaluate URL targeting rule\n * Pure function - deterministic output\n */\nexport function evaluateUrlRule(rule: UrlRule, url: string = ''): boolean {\n // Check equals (exact match)\n if (rule.equals !== undefined) {\n return url === rule.equals;\n }\n\n // Check contains (substring match)\n if (rule.contains !== undefined) {\n return url.includes(rule.contains);\n }\n\n // Check matches (regex match)\n if (rule.matches !== undefined) {\n return rule.matches.test(url);\n }\n\n // No rules specified = match all\n return true;\n}\n","/**\n * Singleton Pattern Implementation\n *\n * Provides a default singleton instance with convenient wrapper functions\n * for simple use cases, plus createInstance() for advanced scenarios.\n */\n\nimport { ExperienceRuntime } from './runtime';\nimport type { Context, Decision, Experience, ExperienceConfig, RuntimeState } from './types';\n\n/**\n * Create a new Experience SDK instance\n *\n * Use this for advanced scenarios where you need multiple isolated runtimes.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n *\n * const exp = createInstance({ debug: true });\n * await exp.init();\n * exp.register('welcome', { ... });\n * ```\n */\nexport function createInstance(config?: ExperienceConfig): ExperienceRuntime {\n return new ExperienceRuntime(config);\n}\n\n/**\n * Default singleton instance\n *\n * Provides a convenient global instance for simple use cases.\n * For script tag users, this is exposed as the global `experiences` object.\n */\nconst defaultInstance = createInstance();\n\n/**\n * Initialize the Experience SDK\n *\n * @example\n * ```typescript\n * import { init } from '@prosdevlab/experience-sdk';\n * await init({ debug: true });\n * ```\n */\nexport async function init(config?: ExperienceConfig): Promise<void> {\n return defaultInstance.init(config);\n}\n\n/**\n * Register an experience\n *\n * @example\n * ```typescript\n * import { register } from '@prosdevlab/experience-sdk';\n *\n * register('welcome-banner', {\n * type: 'banner',\n * targeting: { url: { contains: '/' } },\n * content: { title: 'Welcome!', message: 'Thanks for visiting' }\n * });\n * ```\n */\nexport function register(id: string, experience: Omit<Experience, 'id'>): void {\n defaultInstance.register(id, experience);\n}\n\n/**\n * Evaluate experiences against current context\n * First match wins (use evaluateAll() for multiple experiences)\n *\n * @example\n * ```typescript\n * import { evaluate } from '@prosdevlab/experience-sdk';\n *\n * const decision = evaluate({ url: window.location.href });\n * if (decision.show) {\n * console.log('Show experience:', decision.experienceId);\n * console.log('Reasons:', decision.reasons);\n * }\n * ```\n */\nexport function evaluate(context?: Partial<Context>): Decision {\n return defaultInstance.evaluate(context);\n}\n\n/**\n * Evaluate all experiences against current context\n * Returns array of decisions sorted by priority (higher = more important)\n * All matching experiences will be shown\n *\n * @example\n * ```typescript\n * import { evaluateAll } from '@prosdevlab/experience-sdk';\n *\n * const decisions = evaluateAll({ url: window.location.href });\n * decisions.forEach(decision => {\n * if (decision.show) {\n * console.log('Show:', decision.experienceId);\n * console.log('Reasons:', decision.reasons);\n * }\n * });\n * ```\n */\nexport function evaluateAll(context?: Partial<Context>): Decision[] {\n return defaultInstance.evaluateAll(context);\n}\n\n/**\n * Explain why a specific experience would/wouldn't show\n *\n * @example\n * ```typescript\n * import { explain } from '@prosdevlab/experience-sdk';\n *\n * const explanation = explain('welcome-banner');\n * console.log('Would show?', explanation?.show);\n * console.log('Reasons:', explanation?.reasons);\n * ```\n */\nexport function explain(experienceId: string): Decision | null {\n return defaultInstance.explain(experienceId);\n}\n\n/**\n * Get current runtime state\n *\n * @example\n * ```typescript\n * import { getState } from '@prosdevlab/experience-sdk';\n *\n * const state = getState();\n * console.log('Initialized?', state.initialized);\n * console.log('Experiences:', Array.from(state.experiences.keys()));\n * ```\n */\nexport function getState(): RuntimeState {\n return defaultInstance.getState();\n}\n\n/**\n * Subscribe to SDK events\n *\n * @example\n * ```typescript\n * import { on } from '@prosdevlab/experience-sdk';\n *\n * const unsubscribe = on('experiences:evaluated', (decision) => {\n * console.log('Evaluation:', decision);\n * });\n *\n * // Later: unsubscribe()\n * ```\n */\nexport function on(event: string, handler: (...args: unknown[]) => void): () => void {\n return defaultInstance.on(event, handler);\n}\n\n/**\n * Destroy the SDK instance\n *\n * @example\n * ```typescript\n * import { destroy } from '@prosdevlab/experience-sdk';\n * await destroy();\n * ```\n */\nexport async function destroy(): Promise<void> {\n return defaultInstance.destroy();\n}\n\n/**\n * Default export for convenient importing\n *\n * @example\n * ```typescript\n * import experiences from '@prosdevlab/experience-sdk';\n * await experiences.init();\n * ```\n */\nexport const experiences = {\n createInstance,\n init,\n register,\n evaluate,\n evaluateAll,\n explain,\n getState,\n on,\n destroy,\n};\n\n/**\n * Global singleton instance for IIFE builds\n *\n * When loaded via script tag, this object is available as `window.experiences`\n */\nif (typeof window !== 'undefined') {\n (window as unknown as Record<string, unknown>).experiences = experiences;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/memory.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/cookie.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/localStorage.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/backends/sessionStorage.ts","../../../node_modules/.pnpm/@lytics+sdk-kit-plugins@0.1.2_@lytics+sdk-kit@0.1.1_typescript@5.9.3_/node_modules/@lytics/sdk-kit-plugins/src/storage/storage.ts","../../plugins/src/utils/sanitize.ts","../../plugins/src/banner/banner.ts","../../plugins/src/debug/debug.ts","../../plugins/src/exit-intent/exit-intent.ts","../../plugins/src/frequency/frequency.ts","../../plugins/src/page-visits/page-visits.ts","../../plugins/src/scroll-depth/scroll-depth.ts","../../plugins/src/time-delay/time-delay.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/util/deep-merge.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/config.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/emitter.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/expose.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/capabilities/namespace.ts","../../../node_modules/.pnpm/@lytics+sdk-kit@0.1.1_typescript@5.9.3/node_modules/@lytics/sdk-kit/src/sdk.ts","../src/runtime.ts","../src/singleton.ts"],"names":["MemoryBackend","key","value","_options","CookieBackend","defaultOptions","name","cookies","cookie","error","options","opts","parts","expires","eqPos","testKey","supported","LocalStorageBackend","SessionStorageBackend","storagePlugin","plugin","instance","config","getBackendType","getNamespace","getDefaultTTL","getCookieDefaults","backends","getBackend","type","buildKey","namespace","ns","isExpired","stored","set","backendType","backend","namespacedKey","ttl","serialized","get","remove","clear","storage","prefix","keysToRemove","i","trimmed","eqIndex","encodedKey","isSupported","ALLOWED_TAGS","ALLOWED_ATTRIBUTES","sanitizeHTML","html","temp","sanitizeNode","node","escapeHTML","element","tagName","allowedAttrs","attrs","attr","sanitizedHref","sanitizeURL","escapeAttribute","attrString","innerHTML","child","sanitized","text","div","url","decoded","bannerPlugin","activeBanners","injectDefaultStyles","styleId","style","createBannerElement","experience","content","position","dismissable","zIndex","banner","baseClasses","container","contentDiv","title","message","buttonsDiv","createButton","buttonConfig","button","variant","buttonClasses","closeButton","applyPushDown","pushDownSelector","targetElement","height","removePushDown","show","experienceId","id","isShowing","payload","items","item","typedItem","decision","debugPlugin","isEnabled","shouldLogConsole","shouldEmitWindow","log","data","logData","event","isMobileDevice","userAgent","hasMinTimeElapsed","pageLoadTime","minTime","currentTime","addPositionToHistory","positions","newPosition","maxSize","updated","calculateVelocity","lastY","previousY","shouldTriggerExitIntent","sensitivity","relatedTarget","velocity","isMovingUp","isNearTop","createExitIntentEvent","timestamp","exitIntentPlugin","exitIntentConfig","triggered","mouseMoveListener","mouseOutListener","shouldDisable","trackPosition","e","handleExitIntent","result","eventPayload","delay","cleanup","initialize","destroyHandler","frequencyPlugin","experienceFrequencyMap","getStorageBackend","per","getStorageKey","getImpressionData","raw","saveImpressionData","getTimeWindow","getImpressionCount","hasReachedCap","max","timeWindow","now","recordImpression","sevenDaysAgo","ts","freqStep","t","respectsDNT","createVisitsEvent","isFirstVisit","totalVisits","sessionVisits","firstVisitTime","lastVisitTime","pageVisitsPlugin","sdkInstance","sessionCount","totalCount","isFirstVisitFlag","initialized","loadData","sessionKey","totalKey","storedTotal","saveData","totalData","increment","reset","getState","enabled","respectDNTConfig","autoIncrement","detectDevice","ua","isMobile","isTablet","width","throttle","func","wait","timeout","previous","args","remaining","calculateScrollPercent","includeViewportHeight","scrollingElement","scrollTop","scrollHeight","clientHeight","calculateEngagementScore","fastScrollThreshold","directionChanges","timeScrollingUp","totalTime","velocityScore","directionScore","seekingScore","scrollDepthPlugin","scrollConfig","cfg","device","maxScrollPercent","triggeredThresholds","lastScrollPosition","lastScrollTime","lastScrollDirection","directionChangesSinceLastThreshold","thresholdTimes","handleScroll","currentPercent","currentPosition","timeDelta","positionDelta","currentDirection","threshold","a","b","isFastScrolling","engagementScore","throttledScrollHandler","throttledResizeHandler","calculateElapsed","startTime","pausedDuration","isDocumentHidden","createTimeDelayEvent","wasPaused","visibilityChanges","elapsed","activeElapsed","timeDelayPlugin","timeDelayConfig","pauseWhenHidden","paused","lastPauseTime","timer","visibilityListener","trigger","scheduleTimer","remainingDelay","handleVisibilityChange","hidden","pauseDuration","currentPausedDuration","deepMerge","target","source","sourceValue","targetValue","isPlainObject","Config","initialConfig","path","keys","current","lastKey","Emitter","handler","subscription","sub","err","pattern","withWildcards","str","Expose","sdk","api","Namespace","SDK","pluginFn","expose","requiredNamespaces","ExperienceRuntime","eventName","triggerName","exp","context","evalContext","buildContext","matchedExperience","allReasons","allTrace","evaluateExperience","freqStart","hasReached","count","sortedExperiences","priorityA","decisions","expStartTime","reasons","trace","matchedDecisions","d","matchedExperiences","index","partial","matched","urlStart","urlMatch","evaluateUrlRule","rule","createInstance","defaultInstance","init","register","evaluate","evaluateAll","explain","on","destroy","experiences"],"mappings":"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","file":"experience-sdk.global.js","sourcesContent":["/**\n * Memory Storage Backend\n *\n * In-memory storage fallback when other storage mechanisms are unavailable.\n * Data is lost when the page is refreshed.\n */\n\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class MemoryBackend implements StorageBackend {\n private storage = new Map<string, string>();\n\n get(key: string): string | null {\n return this.storage.get(key) ?? null;\n }\n\n set(key: string, value: string, _options?: StorageBackendOptions): void {\n this.storage.set(key, value);\n }\n\n remove(key: string): void {\n this.storage.delete(key);\n }\n\n clear(): void {\n this.storage.clear();\n }\n\n isSupported(): boolean {\n // Memory storage is always supported\n return true;\n }\n}\n","/**\n * Cookie Storage Backend\n *\n * Stores data in browser cookies with support for domain, path, secure, and sameSite options.\n * Falls back to memory storage if cookies are unavailable.\n */\n\nimport { MemoryBackend } from './memory';\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class CookieBackend implements StorageBackend {\n private fallback: MemoryBackend | null = null;\n private defaultOptions: StorageBackendOptions;\n\n constructor(defaultOptions: StorageBackendOptions = {}) {\n this.defaultOptions = {\n path: '/',\n sameSite: 'lax',\n ...defaultOptions,\n };\n }\n\n get(key: string): string | null {\n if (!this.isSupported()) {\n return this.getFallback().get(key);\n }\n\n try {\n const name = `${encodeURIComponent(key)}=`;\n const cookies = document.cookie.split(';');\n\n for (let cookie of cookies) {\n cookie = cookie.trim();\n if (cookie.startsWith(name)) {\n return decodeURIComponent(cookie.substring(name.length));\n }\n }\n\n return null;\n } catch (error) {\n console.warn('Cookie get failed:', error);\n return this.getFallback().get(key);\n }\n }\n\n set(key: string, value: string, options?: StorageBackendOptions): void {\n if (!this.isSupported()) {\n this.getFallback().set(key, value);\n return;\n }\n\n try {\n const opts = { ...this.defaultOptions, ...options };\n const parts: string[] = [`${encodeURIComponent(key)}=${encodeURIComponent(value)}`];\n\n // Add expiration if TTL is provided\n if (opts.ttl) {\n const expires = new Date();\n expires.setTime(expires.getTime() + opts.ttl * 1000);\n parts.push(`expires=${expires.toUTCString()}`);\n }\n\n if (opts.path) {\n parts.push(`path=${opts.path}`);\n }\n\n if (opts.domain) {\n parts.push(`domain=${opts.domain}`);\n }\n\n if (opts.secure) {\n parts.push('secure');\n }\n\n if (opts.sameSite) {\n parts.push(`samesite=${opts.sameSite}`);\n }\n\n document.cookie = parts.join('; ');\n } catch (error) {\n console.warn('Cookie set failed, falling back to memory:', error);\n this.getFallback().set(key, value);\n }\n }\n\n remove(key: string): void {\n if (!this.isSupported()) {\n this.getFallback().remove(key);\n return;\n }\n\n try {\n // Set cookie with past expiration date to delete it\n const opts = this.defaultOptions;\n const parts: string[] = [\n `${encodeURIComponent(key)}=`,\n 'expires=Thu, 01 Jan 1970 00:00:00 UTC',\n ];\n\n if (opts.path) {\n parts.push(`path=${opts.path}`);\n }\n\n if (opts.domain) {\n parts.push(`domain=${opts.domain}`);\n }\n\n document.cookie = parts.join('; ');\n } catch (error) {\n console.warn('Cookie remove failed:', error);\n this.getFallback().remove(key);\n }\n }\n\n clear(): void {\n if (!this.isSupported()) {\n this.getFallback().clear();\n return;\n }\n\n try {\n // Get all cookie names and delete them\n const cookies = document.cookie.split(';');\n\n for (let cookie of cookies) {\n cookie = cookie.trim();\n const eqPos = cookie.indexOf('=');\n const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;\n this.remove(decodeURIComponent(name));\n }\n } catch (error) {\n console.warn('Cookie clear failed:', error);\n this.getFallback().clear();\n }\n }\n\n isSupported(): boolean {\n try {\n // Check if cookies are enabled\n if (typeof document === 'undefined' || !document.cookie) {\n return false;\n }\n\n // Try to set and remove a test cookie\n const testKey = '__cookie_test__';\n document.cookie = `${testKey}=test; path=/`;\n const supported = document.cookie.indexOf(testKey) !== -1;\n\n // Clean up test cookie\n document.cookie = `${testKey}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`;\n\n return supported;\n } catch {\n return false;\n }\n }\n\n private getFallback(): MemoryBackend {\n if (!this.fallback) {\n this.fallback = new MemoryBackend();\n }\n return this.fallback;\n }\n}\n","/**\n * LocalStorage Backend\n *\n * Persistent storage that survives page refreshes and browser restarts.\n * Falls back to memory storage if localStorage is unavailable or quota is exceeded.\n */\n\nimport { MemoryBackend } from './memory';\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class LocalStorageBackend implements StorageBackend {\n private fallback: MemoryBackend | null = null;\n\n get(key: string): string | null {\n if (!this.isSupported()) {\n return this.getFallback().get(key);\n }\n\n try {\n return localStorage.getItem(key);\n } catch (error) {\n console.warn('localStorage.getItem failed:', error);\n return this.getFallback().get(key);\n }\n }\n\n set(key: string, value: string, _options?: StorageBackendOptions): void {\n if (!this.isSupported()) {\n this.getFallback().set(key, value);\n return;\n }\n\n try {\n localStorage.setItem(key, value);\n } catch (error) {\n // Quota exceeded or other error\n console.warn('localStorage.setItem failed, falling back to memory:', error);\n this.getFallback().set(key, value);\n }\n }\n\n remove(key: string): void {\n if (!this.isSupported()) {\n this.getFallback().remove(key);\n return;\n }\n\n try {\n localStorage.removeItem(key);\n } catch (error) {\n console.warn('localStorage.removeItem failed:', error);\n this.getFallback().remove(key);\n }\n }\n\n clear(): void {\n if (!this.isSupported()) {\n this.getFallback().clear();\n return;\n }\n\n try {\n localStorage.clear();\n } catch (error) {\n console.warn('localStorage.clear failed:', error);\n this.getFallback().clear();\n }\n }\n\n isSupported(): boolean {\n try {\n const testKey = '__storage_test__';\n localStorage.setItem(testKey, 'test');\n localStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n\n private getFallback(): MemoryBackend {\n if (!this.fallback) {\n this.fallback = new MemoryBackend();\n }\n return this.fallback;\n }\n}\n","/**\n * SessionStorage Backend\n *\n * Session-scoped storage that persists only for the duration of the page session.\n * Data is cleared when the tab/window is closed.\n * Falls back to memory storage if sessionStorage is unavailable.\n */\n\nimport { MemoryBackend } from './memory';\nimport type { StorageBackend, StorageBackendOptions } from './types';\n\nexport class SessionStorageBackend implements StorageBackend {\n private fallback: MemoryBackend | null = null;\n\n get(key: string): string | null {\n if (!this.isSupported()) {\n return this.getFallback().get(key);\n }\n\n try {\n return sessionStorage.getItem(key);\n } catch (error) {\n console.warn('sessionStorage.getItem failed:', error);\n return this.getFallback().get(key);\n }\n }\n\n set(key: string, value: string, _options?: StorageBackendOptions): void {\n if (!this.isSupported()) {\n this.getFallback().set(key, value);\n return;\n }\n\n try {\n sessionStorage.setItem(key, value);\n } catch (error) {\n console.warn('sessionStorage.setItem failed, falling back to memory:', error);\n this.getFallback().set(key, value);\n }\n }\n\n remove(key: string): void {\n if (!this.isSupported()) {\n this.getFallback().remove(key);\n return;\n }\n\n try {\n sessionStorage.removeItem(key);\n } catch (error) {\n console.warn('sessionStorage.removeItem failed:', error);\n this.getFallback().remove(key);\n }\n }\n\n clear(): void {\n if (!this.isSupported()) {\n this.getFallback().clear();\n return;\n }\n\n try {\n sessionStorage.clear();\n } catch (error) {\n console.warn('sessionStorage.clear failed:', error);\n this.getFallback().clear();\n }\n }\n\n isSupported(): boolean {\n try {\n const testKey = '__storage_test__';\n sessionStorage.setItem(testKey, 'test');\n sessionStorage.removeItem(testKey);\n return true;\n } catch {\n return false;\n }\n }\n\n private getFallback(): MemoryBackend {\n if (!this.fallback) {\n this.fallback = new MemoryBackend();\n }\n return this.fallback;\n }\n}\n","/**\n * Storage Plugin\n *\n * Universal storage abstraction with support for:\n * - Multiple backends (localStorage, sessionStorage, cookie, memory)\n * - TTL/expiration\n * - Namespace isolation\n * - JSON serialization\n * - Automatic fallback\n */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport { CookieBackend } from './backends/cookie';\nimport { LocalStorageBackend } from './backends/localStorage';\nimport { MemoryBackend } from './backends/memory';\nimport { SessionStorageBackend } from './backends/sessionStorage';\nimport type { StorageBackend, StorageBackendOptions } from './backends/types';\n\nexport type StorageBackendType = 'localStorage' | 'sessionStorage' | 'cookie' | 'memory';\n\nexport interface StoragePluginConfig {\n storage?: {\n backend?: StorageBackendType;\n namespace?: string;\n ttl?: number;\n // Cookie-specific defaults\n domain?: string;\n path?: string;\n secure?: boolean;\n sameSite?: 'strict' | 'lax' | 'none';\n };\n}\n\nexport interface StorageOptions extends StorageBackendOptions {\n backend?: StorageBackendType;\n namespace?: string;\n}\n\nexport interface StoragePlugin {\n set(key: string, value: unknown, options?: StorageOptions): void;\n get<T = unknown>(key: string, options?: Pick<StorageOptions, 'backend'>): T | null;\n remove(key: string, options?: Pick<StorageOptions, 'backend'>): void;\n clear(options?: Pick<StorageOptions, 'backend'>): void;\n}\n\ninterface StoredValue<T> {\n value: T;\n expires?: number; // Timestamp in milliseconds\n}\n\n/**\n * Storage Plugin\n *\n * Provides a universal storage API with automatic backend selection and fallback.\n *\n * @example\n * ```typescript\n * const sdk = new SDK({ storage: { backend: 'localStorage', namespace: 'myapp' } });\n * sdk.use(storagePlugin);\n *\n * sdk.storage.set('user', { id: 123, name: 'Alice' });\n * const user = sdk.storage.get('user');\n * ```\n */\nexport const storagePlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('storage');\n\n // Set default config\n plugin.defaults({\n storage: {\n backend: 'localStorage',\n namespace: '',\n path: '/',\n sameSite: 'lax',\n },\n });\n\n // Get config values\n const getBackendType = (): StorageBackendType => config.get('storage.backend') ?? 'localStorage';\n const getNamespace = (): string => config.get('storage.namespace') ?? '';\n const getDefaultTTL = (): number | undefined => config.get('storage.ttl');\n const getCookieDefaults = (): StorageBackendOptions => ({\n domain: config.get('storage.domain'),\n path: config.get('storage.path') ?? '/',\n secure: config.get('storage.secure'),\n sameSite: config.get('storage.sameSite') ?? 'lax',\n });\n\n // Backend instances (lazy-initialized)\n const backends: Partial<Record<StorageBackendType, StorageBackend>> = {};\n\n /**\n * Get or create a backend instance\n */\n function getBackend(type: StorageBackendType): StorageBackend {\n if (!backends[type]) {\n switch (type) {\n case 'localStorage':\n backends[type] = new LocalStorageBackend();\n break;\n case 'sessionStorage':\n backends[type] = new SessionStorageBackend();\n break;\n case 'cookie':\n backends[type] = new CookieBackend(getCookieDefaults());\n break;\n case 'memory':\n backends[type] = new MemoryBackend();\n break;\n }\n }\n return backends[type] as StorageBackend;\n }\n\n /**\n * Build a namespaced key\n */\n function buildKey(key: string, namespace?: string): string {\n const ns = namespace ?? getNamespace();\n return ns ? `${ns}:${key}` : key;\n }\n\n /**\n * Check if a stored value has expired\n */\n function isExpired(stored: StoredValue<any>): boolean {\n if (!stored.expires) {\n return false;\n }\n return Date.now() > stored.expires;\n }\n\n /**\n * Set a value in storage\n */\n function set<T>(key: string, value: T, options?: StorageOptions): void {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespacedKey = buildKey(key, options?.namespace);\n\n // Wrap value with expiration metadata\n const stored: StoredValue<T> = { value };\n const ttl = options?.ttl ?? getDefaultTTL();\n\n if (ttl) {\n stored.expires = Date.now() + ttl * 1000;\n }\n\n // Serialize to JSON\n const serialized = JSON.stringify(stored);\n\n // Store using backend\n backend.set(namespacedKey, serialized, options);\n\n // Emit event\n plugin.emit('storage:set', { key, value, backend: backendType });\n }\n\n /**\n * Get a value from storage\n */\n function get<T>(key: string, options?: StorageOptions): T | null {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespacedKey = buildKey(key, options?.namespace);\n\n // Get from backend\n const serialized = backend.get(namespacedKey);\n\n if (!serialized) {\n return null;\n }\n\n try {\n // Deserialize from JSON\n const stored: StoredValue<T> = JSON.parse(serialized);\n\n // Check expiration\n if (isExpired(stored)) {\n // Remove expired value\n backend.remove(namespacedKey);\n plugin.emit('storage:expired', { key, backend: backendType });\n return null;\n }\n\n plugin.emit('storage:get', { key, backend: backendType });\n return stored.value;\n } catch (error) {\n console.warn('Failed to parse stored value:', error);\n // Remove corrupted value\n backend.remove(namespacedKey);\n return null;\n }\n }\n\n /**\n * Remove a value from storage\n */\n function remove(key: string, options?: StorageOptions): void {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespacedKey = buildKey(key, options?.namespace);\n\n backend.remove(namespacedKey);\n plugin.emit('storage:remove', { key, backend: backendType });\n }\n\n /**\n * Clear all values from storage\n *\n * If a namespace is provided, only clears values with that namespace prefix.\n * Otherwise, clears all values in the backend.\n */\n function clear(options?: StorageOptions): void {\n const backendType = options?.backend ?? getBackendType();\n const backend = getBackend(backendType);\n const namespace = options?.namespace;\n\n // If no namespace specified, clear everything\n if (!namespace) {\n backend.clear();\n plugin.emit('storage:clear', { backend: backendType });\n return;\n }\n\n // Namespace-specific clearing: iterate through all keys and remove matching ones\n if (backendType === 'localStorage' || backendType === 'sessionStorage') {\n const storage = backendType === 'localStorage' ? localStorage : sessionStorage;\n const prefix = `${namespace}:`;\n const keysToRemove: string[] = [];\n\n // Collect keys to remove\n for (let i = 0; i < storage.length; i++) {\n const key = storage.key(i);\n if (key?.startsWith(prefix)) {\n keysToRemove.push(key);\n }\n }\n\n // Remove them\n for (const key of keysToRemove) {\n backend.remove(key);\n }\n } else if (backendType === 'cookie') {\n // For cookies, parse document.cookie and remove matching keys\n const prefix = `${namespace}:`;\n const cookies = document.cookie.split(';');\n\n for (const cookie of cookies) {\n const trimmed = cookie.trim();\n const eqIndex = trimmed.indexOf('=');\n if (eqIndex === -1) continue;\n\n const encodedKey = trimmed.substring(0, eqIndex);\n const key = decodeURIComponent(encodedKey);\n\n if (key.startsWith(prefix)) {\n backend.remove(key);\n }\n }\n } else {\n // For memory backend, just clear everything (it's in-memory anyway)\n backend.clear();\n }\n\n plugin.emit('storage:clear', { backend: backendType, namespace });\n }\n\n /**\n * Check if a backend is supported\n */\n function isSupported(backendType?: StorageBackendType): boolean {\n const type = backendType ?? getBackendType();\n const backend = getBackend(type);\n return backend.isSupported();\n }\n\n // Expose public API\n plugin.expose({\n storage: {\n set,\n get,\n remove,\n clear,\n isSupported,\n },\n });\n\n // Log initialization\n instance.on('sdk:ready', () => {\n const backendType = getBackendType();\n const backend = getBackend(backendType);\n const supported = backend.isSupported();\n\n if (!supported) {\n console.warn(\n `Storage backend '${backendType}' is not supported, falling back to memory storage`\n );\n }\n });\n};\n\nexport { CookieBackend } from './backends/cookie';\nexport { LocalStorageBackend } from './backends/localStorage';\nexport { MemoryBackend } from './backends/memory';\nexport { SessionStorageBackend } from './backends/sessionStorage';\n// Export types and backends for testing\nexport type { StorageBackend, StorageBackendOptions } from './backends/types';\n","/**\n * HTML Sanitizer\n *\n * Lightweight HTML sanitizer for experience content (messages, titles).\n * Whitelist-based approach that only allows safe formatting tags.\n *\n * Security: Prevents XSS attacks by stripping dangerous tags and attributes.\n */\n\n/**\n * Allowed HTML tags for sanitization\n * Only safe formatting tags are permitted\n */\nconst ALLOWED_TAGS = ['strong', 'em', 'a', 'br', 'span', 'b', 'i', 'p'] as const;\n\n/**\n * Allowed attributes per tag\n */\nconst ALLOWED_ATTRIBUTES: Record<string, string[]> = {\n a: ['href', 'class', 'style', 'title'],\n span: ['class', 'style'],\n p: ['class', 'style'],\n // Other tags have no attributes allowed\n};\n\n/**\n * Sanitize HTML string by removing dangerous tags and attributes\n *\n * @param html - HTML string to sanitize\n * @returns Sanitized HTML string safe for innerHTML\n *\n * @example\n * ```typescript\n * sanitizeHTML('<strong>Hello</strong><script>alert(\"xss\")</script>');\n * // Returns: '<strong>Hello</strong>'\n * ```\n */\nexport function sanitizeHTML(html: string): string {\n if (!html || typeof html !== 'string') {\n return '';\n }\n\n // Create a temporary DOM element to parse HTML\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n /**\n * Recursively sanitize a DOM node\n */\n function sanitizeNode(node: Node): string {\n // Text nodes - escape HTML entities\n if (node.nodeType === Node.TEXT_NODE) {\n return escapeHTML(node.textContent || '');\n }\n\n // Element nodes\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n const tagName = element.tagName.toLowerCase();\n\n // Handle tags with whitespace (malformed HTML like \"< script >\")\n // Browser normalizes these, but if we see a tag that's not in our list,\n // it might be a dangerous tag that was normalized\n if (!tagName || tagName.includes(' ')) {\n return '';\n }\n\n // If tag is not allowed, return empty string\n if (!ALLOWED_TAGS.includes(tagName as any)) {\n return '';\n }\n\n // Get allowed attributes for this tag\n const allowedAttrs = ALLOWED_ATTRIBUTES[tagName] || [];\n\n // Build attribute string\n const attrs: string[] = [];\n for (const attr of allowedAttrs) {\n const value = element.getAttribute(attr);\n if (value !== null) {\n // Sanitize attribute values\n if (attr === 'href') {\n // Only allow safe URLs (http, https, mailto, tel, relative)\n const sanitizedHref = sanitizeURL(value);\n if (sanitizedHref) {\n attrs.push(`href=\"${escapeAttribute(sanitizedHref)}\"`);\n }\n } else {\n // For all other attributes (title, class, style), escape HTML entities\n attrs.push(`${attr}=\"${escapeAttribute(value)}\"`);\n }\n }\n }\n\n const attrString = attrs.length > 0 ? ` ${attrs.join(' ')}` : '';\n\n // Process child nodes\n let innerHTML = '';\n for (const child of Array.from(element.childNodes)) {\n innerHTML += sanitizeNode(child);\n }\n\n // Self-closing tags\n if (tagName === 'br') {\n return `<br${attrString} />`;\n }\n\n return `<${tagName}${attrString}>${innerHTML}</${tagName}>`;\n }\n\n return '';\n }\n\n // Sanitize all nodes\n let sanitized = '';\n for (const child of Array.from(temp.childNodes)) {\n sanitized += sanitizeNode(child);\n }\n\n return sanitized;\n}\n\n/**\n * Escape HTML entities to prevent XSS in text content\n */\nfunction escapeHTML(text: string): string {\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n}\n\n/**\n * Escape HTML entities for use in attribute values\n */\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Sanitize URL to prevent javascript: and data: XSS attacks\n *\n * @param url - URL to sanitize\n * @returns Sanitized URL or empty string if unsafe\n */\nfunction sanitizeURL(url: string): string {\n if (!url || typeof url !== 'string') {\n return '';\n }\n\n // Decode URL-encoded characters to check for encoded attacks\n let decoded: string;\n try {\n decoded = decodeURIComponent(url);\n } catch {\n // If decoding fails, use original\n decoded = url;\n }\n\n const trimmed = decoded.trim().toLowerCase();\n\n // Block javascript: and data: protocols (check both original and decoded)\n if (\n trimmed.startsWith('javascript:') ||\n trimmed.startsWith('data:') ||\n url.toLowerCase().trim().startsWith('javascript:') ||\n url.toLowerCase().trim().startsWith('data:')\n ) {\n return '';\n }\n\n // Allow http, https, mailto, tel, and relative URLs\n if (\n trimmed.startsWith('http://') ||\n trimmed.startsWith('https://') ||\n trimmed.startsWith('mailto:') ||\n trimmed.startsWith('tel:') ||\n trimmed.startsWith('/') ||\n trimmed.startsWith('#') ||\n trimmed.startsWith('?')\n ) {\n return url; // Return original (case preserved)\n }\n\n // Allow relative paths without protocol\n if (!trimmed.includes(':')) {\n return url;\n }\n\n // Block everything else\n return '';\n}\n","/**\n * Banner Plugin\n *\n * Renders banner experiences at the top or bottom of the page.\n * Auto-shows banners when experiences are evaluated.\n */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport type { BannerContent, Decision, Experience } from '../types';\nimport { sanitizeHTML } from '../utils/sanitize';\n\nexport interface BannerPluginConfig {\n banner?: {\n position?: 'top' | 'bottom';\n dismissable?: boolean;\n zIndex?: number;\n pushDown?: string; // CSS selector of element to push down (add margin-top)\n };\n}\n\nexport interface BannerPlugin {\n show(experience: Experience): void;\n remove(): void;\n isShowing(): boolean;\n}\n\n/**\n * Banner Plugin\n *\n * Automatically renders banner experiences when they are evaluated.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n * import { bannerPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * // Basic usage (banner overlays at top)\n * const sdk = createInstance({\n * banner: {\n * position: 'top',\n * dismissable: true\n * }\n * });\n * sdk.use(bannerPlugin);\n *\n * // With pushDown (pushes navigation down instead of overlaying)\n * const sdk = createInstance({\n * banner: {\n * position: 'top',\n * dismissable: true,\n * pushDown: 'header' // CSS selector of element to push down\n * }\n * });\n * sdk.use(bannerPlugin);\n * ```\n */\nexport const bannerPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('banner');\n\n // Set defaults\n plugin.defaults({\n banner: {\n position: 'top',\n dismissable: true,\n zIndex: 10000,\n },\n });\n\n // Track multiple active banners by experience ID\n const activeBanners = new Map<string, HTMLElement>();\n\n /**\n * Inject default banner styles if not already present\n */\n function injectDefaultStyles(): void {\n const styleId = 'xp-banner-styles';\n if (document.getElementById(styleId)) {\n return; // Already injected\n }\n\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = `\n .xp-banner {\n position: fixed;\n left: 0;\n right: 0;\n width: 100%;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n box-sizing: border-box;\n z-index: 10000;\n background: #ffffff;\n color: #111827;\n border-bottom: 1px solid #e5e7eb;\n box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05);\n }\n \n .xp-banner--top {\n top: 0;\n }\n \n .xp-banner--bottom {\n bottom: 0;\n border-bottom: none;\n border-top: 1px solid #e5e7eb;\n box-shadow: 0 -1px 3px 0 rgba(0, 0, 0, 0.05);\n }\n \n .xp-banner__container {\n display: flex;\n align-items: center;\n gap: 16px;\n max-width: 1280px;\n margin: 0 auto;\n padding: 14px 24px;\n }\n \n .xp-banner__content {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n \n .xp-banner__title {\n font-weight: 600;\n margin: 0;\n font-size: 15px;\n line-height: 1.4;\n }\n \n .xp-banner__message {\n margin: 0;\n font-size: 14px;\n line-height: 1.5;\n color: #6b7280;\n }\n \n .xp-banner__buttons {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n \n .xp-banner__button {\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n white-space: nowrap;\n }\n \n .xp-banner__button--primary {\n background: #2563eb;\n color: #ffffff;\n }\n \n .xp-banner__button--primary:hover {\n background: #1d4ed8;\n }\n \n .xp-banner__button--secondary {\n background: #f3f4f6;\n color: #374151;\n border: 1px solid #e5e7eb;\n }\n \n .xp-banner__button--secondary:hover {\n background: #e5e7eb;\n }\n \n .xp-banner__button--link {\n background: transparent;\n color: #2563eb;\n padding: 6px 12px;\n font-weight: 400;\n }\n \n .xp-banner__button--link:hover {\n background: #f3f4f6;\n text-decoration: underline;\n }\n \n .xp-banner__close {\n background: transparent;\n border: none;\n color: #9ca3af;\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n padding: 4px;\n margin: 0;\n transition: color 0.2s;\n flex-shrink: 0;\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 4px;\n }\n \n .xp-banner__close:hover {\n color: #111827;\n background: #f3f4f6;\n }\n \n @media (max-width: 640px) {\n .xp-banner__container {\n flex-wrap: wrap;\n padding: 14px 16px;\n position: relative;\n }\n \n .xp-banner__content {\n flex: 1 1 100%;\n padding-right: 32px;\n }\n \n .xp-banner__buttons {\n flex: 1 1 auto;\n width: 100%;\n }\n \n .xp-banner__button {\n flex: 1;\n }\n \n .xp-banner__close {\n position: absolute;\n top: 12px;\n right: 12px;\n }\n }\n \n /* Dark mode support */\n @media (prefers-color-scheme: dark) {\n .xp-banner {\n background: #111827;\n color: #f9fafb;\n border-bottom-color: #1f2937;\n }\n \n .xp-banner--bottom {\n border-top-color: #1f2937;\n }\n \n .xp-banner__message {\n color: #9ca3af;\n }\n \n .xp-banner__button--primary {\n background: #3b82f6;\n }\n \n .xp-banner__button--primary:hover {\n background: #2563eb;\n }\n \n .xp-banner__button--secondary {\n background: #1f2937;\n color: #f9fafb;\n border-color: #374151;\n }\n \n .xp-banner__button--secondary:hover {\n background: #374151;\n }\n \n .xp-banner__button--link {\n color: #60a5fa;\n }\n \n .xp-banner__button--link:hover {\n background: #1f2937;\n }\n \n .xp-banner__close {\n color: #6b7280;\n }\n \n .xp-banner__close:hover {\n color: #f9fafb;\n background: #1f2937;\n }\n }\n `;\n document.head.appendChild(style);\n }\n\n /**\n * Create banner DOM element\n */\n function createBannerElement(experience: Experience): HTMLElement {\n const content = experience.content as BannerContent;\n // Allow per-experience position override, fall back to global config\n const position = content.position ?? config.get('banner.position') ?? 'top';\n const dismissable = content.dismissable ?? config.get('banner.dismissable') ?? true;\n const zIndex = config.get('banner.zIndex') ?? 10000;\n\n // Inject default styles if needed\n injectDefaultStyles();\n\n // Create banner container\n const banner = document.createElement('div');\n banner.setAttribute('data-experience-id', experience.id);\n\n // Build className: base classes + position + user's custom class\n const baseClasses = ['xp-banner', `xp-banner--${position}`];\n if (content.className) {\n baseClasses.push(content.className);\n }\n banner.className = baseClasses.join(' ');\n\n // Apply user's custom styles\n if (content.style) {\n Object.assign(banner.style, content.style);\n }\n\n // Override z-index if configured\n if (zIndex !== 10000) {\n banner.style.zIndex = String(zIndex);\n }\n\n // Create container\n const container = document.createElement('div');\n container.className = 'xp-banner__container';\n banner.appendChild(container);\n\n // Create content container\n const contentDiv = document.createElement('div');\n contentDiv.className = 'xp-banner__content';\n\n // Add title if present\n if (content.title) {\n const title = document.createElement('h3');\n title.className = 'xp-banner__title';\n // Sanitize HTML to prevent XSS attacks\n title.innerHTML = sanitizeHTML(content.title);\n contentDiv.appendChild(title);\n }\n\n // Add message\n const message = document.createElement('p');\n message.className = 'xp-banner__message';\n // Sanitize HTML to prevent XSS attacks\n message.innerHTML = sanitizeHTML(content.message);\n contentDiv.appendChild(message);\n\n container.appendChild(contentDiv);\n\n // Create buttons container\n const buttonsDiv = document.createElement('div');\n buttonsDiv.className = 'xp-banner__buttons';\n\n // Helper function to create button with variant styling\n function createButton(buttonConfig: {\n text: string;\n action?: string;\n url?: string;\n variant?: 'primary' | 'secondary' | 'link';\n metadata?: Record<string, unknown>;\n className?: string;\n style?: Record<string, string>;\n }): HTMLButtonElement {\n const button = document.createElement('button');\n button.textContent = buttonConfig.text;\n\n const variant = buttonConfig.variant || 'primary';\n\n // Build className: base class + variant + user's custom class\n const buttonClasses = ['xp-banner__button', `xp-banner__button--${variant}`];\n if (buttonConfig.className) {\n buttonClasses.push(buttonConfig.className);\n }\n button.className = buttonClasses.join(' ');\n\n // Apply user's custom styles\n if (buttonConfig.style) {\n Object.assign(button.style, buttonConfig.style);\n }\n\n button.addEventListener('click', () => {\n // Emit action event\n instance.emit('experiences:action', {\n experienceId: experience.id,\n type: 'banner',\n action: buttonConfig.action,\n url: buttonConfig.url,\n metadata: buttonConfig.metadata,\n variant: variant,\n timestamp: Date.now(),\n });\n\n // Navigate if URL provided\n if (buttonConfig.url) {\n window.location.href = buttonConfig.url;\n }\n });\n\n return button;\n }\n\n // Add buttons from buttons array\n if (content.buttons && content.buttons.length > 0) {\n content.buttons.forEach((buttonConfig) => {\n const button = createButton(buttonConfig);\n buttonsDiv.appendChild(button);\n });\n }\n\n // Add dismiss button if dismissable\n if (dismissable) {\n const closeButton = document.createElement('button');\n closeButton.className = 'xp-banner__close';\n closeButton.innerHTML = '×';\n closeButton.setAttribute('aria-label', 'Close banner');\n\n closeButton.addEventListener('click', () => {\n remove(experience.id);\n instance.emit('experiences:dismissed', {\n experienceId: experience.id,\n type: 'banner',\n });\n });\n\n buttonsDiv.appendChild(closeButton);\n }\n\n container.appendChild(buttonsDiv);\n\n return banner;\n }\n\n /**\n * Apply pushDown margin to target element\n */\n function applyPushDown(banner: HTMLElement, position: 'top' | 'bottom'): void {\n const pushDownSelector = config.get('banner.pushDown');\n\n if (!pushDownSelector || position !== 'top') {\n return; // Only push down for top banners\n }\n\n const targetElement = document.querySelector(pushDownSelector);\n if (!targetElement || !(targetElement instanceof HTMLElement)) {\n return;\n }\n\n // Get banner height\n const height = banner.offsetHeight;\n\n // Apply margin-top with transition\n targetElement.style.transition = 'margin-top 0.3s ease';\n targetElement.style.marginTop = `${height}px`;\n }\n\n /**\n * Remove pushDown margin from target element\n */\n function removePushDown(): void {\n const pushDownSelector = config.get('banner.pushDown');\n\n if (!pushDownSelector) {\n return;\n }\n\n const targetElement = document.querySelector(pushDownSelector);\n if (!targetElement || !(targetElement instanceof HTMLElement)) {\n return;\n }\n\n // Remove margin-top with transition\n targetElement.style.transition = 'margin-top 0.3s ease';\n targetElement.style.marginTop = '0';\n }\n\n /**\n * Show a banner experience\n */\n function show(experience: Experience): void {\n // If banner already showing for this experience, skip\n if (activeBanners.has(experience.id)) {\n return;\n }\n\n // Only show if we're in a browser environment\n if (typeof document === 'undefined') {\n return;\n }\n\n const banner = createBannerElement(experience);\n document.body.appendChild(banner);\n activeBanners.set(experience.id, banner);\n\n // Apply pushDown to target element if configured\n const content = experience.content as BannerContent;\n const position = content.position ?? config.get('banner.position') ?? 'top';\n applyPushDown(banner, position);\n\n instance.emit('experiences:shown', {\n experienceId: experience.id,\n type: 'banner',\n timestamp: Date.now(),\n });\n }\n\n /**\n * Remove a banner by experience ID (or all if no ID provided)\n */\n function remove(experienceId?: string): void {\n if (experienceId) {\n // Remove specific banner\n const banner = activeBanners.get(experienceId);\n if (banner?.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n activeBanners.delete(experienceId);\n\n // Remove pushDown if no more banners\n if (activeBanners.size === 0) {\n removePushDown();\n }\n } else {\n // Remove all banners\n for (const [id, banner] of activeBanners.entries()) {\n if (banner?.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n activeBanners.delete(id);\n }\n\n // Remove pushDown\n removePushDown();\n }\n }\n\n /**\n * Check if any banner is currently showing\n */\n function isShowing(): boolean {\n return activeBanners.size > 0;\n }\n\n // Expose banner API\n plugin.expose({\n banner: {\n show,\n remove,\n isShowing,\n },\n });\n\n // Auto-show banner on experiences:evaluated event\n instance.on('experiences:evaluated', (payload: unknown) => {\n // Handle both single decision and array of decisions\n // evaluate() emits: { decision, experience }\n // evaluateAll() emits: [{ decision, experience }, ...]\n const items = Array.isArray(payload) ? payload : [payload];\n\n for (const item of items) {\n // Item is { decision, experience }\n const typedItem = item as { decision?: Decision; experience?: Experience };\n const decision = typedItem.decision;\n const experience = typedItem.experience;\n\n // Only handle banner-type experiences\n if (experience?.type === 'banner') {\n if (decision?.show) {\n show(experience);\n } else if (experience.id && activeBanners.has(experience.id)) {\n // Hide specific banner if decision says don't show\n remove(experience.id);\n }\n }\n }\n });\n\n // Cleanup on destroy\n instance.on('sdk:destroy', () => {\n remove();\n });\n};\n","/**\n * Debug Plugin\n *\n * Emits structured debug events to window and optionally logs to console.\n * Useful for debugging and Chrome extension integration.\n */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\n\nexport interface DebugPluginConfig {\n debug?: {\n enabled?: boolean;\n console?: boolean;\n window?: boolean;\n };\n}\n\nexport interface DebugPlugin {\n log(message: string, data?: unknown): void;\n isEnabled(): boolean;\n}\n\n/**\n * Debug Plugin\n *\n * Listens to all SDK events and emits them as window events for debugging.\n * Also optionally logs to console.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n * import { debugPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * const sdk = createInstance({ debug: { enabled: true, console: true } });\n * sdk.use(debugPlugin);\n * ```\n */\nexport const debugPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('debug');\n\n // Set defaults\n plugin.defaults({\n debug: {\n enabled: false,\n console: false,\n window: true,\n },\n });\n\n // Helper to check if debug is enabled\n const isEnabled = (): boolean => config.get('debug.enabled') ?? false;\n const shouldLogConsole = (): boolean => config.get('debug.console') ?? false;\n const shouldEmitWindow = (): boolean => config.get('debug.window') ?? true;\n\n // Log function\n const log = (message: string, data?: unknown): void => {\n if (!isEnabled()) return;\n\n const timestamp = new Date().toISOString();\n const logData = {\n timestamp,\n message,\n data,\n };\n\n // Console logging\n if (shouldLogConsole()) {\n console.log(`[experiences] ${message}`, data || '');\n }\n\n // Window event emission\n if (shouldEmitWindow() && typeof window !== 'undefined') {\n const event = new CustomEvent('experience-sdk:debug', {\n detail: logData,\n });\n window.dispatchEvent(event);\n }\n };\n\n // Expose debug API\n plugin.expose({\n debug: {\n log,\n isEnabled,\n },\n });\n\n // If debug is enabled, listen to all events\n if (isEnabled()) {\n // Listen to experiences:* events\n instance.on('experiences:ready', () => {\n if (!isEnabled()) return;\n log('SDK initialized and ready');\n });\n\n instance.on('experiences:registered', (payload) => {\n if (!isEnabled()) return;\n log('Experience registered', payload);\n });\n\n instance.on('experiences:evaluated', (payload) => {\n if (!isEnabled()) return;\n log('Experience evaluated', payload);\n });\n }\n};\n","// packages/plugins/src/exit-intent/exit-intent.ts\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport type { ExitIntentEvent, ExitIntentPlugin, ExitIntentPluginConfig } from './types';\n\n/**\n * Position in history\n */\ninterface Position {\n x: number;\n y: number;\n}\n\n/**\n * Pure function: Check if device is mobile\n */\nexport function isMobileDevice(userAgent: string): boolean {\n return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);\n}\n\n/**\n * Pure function: Check if minimum time has elapsed\n */\nexport function hasMinTimeElapsed(\n pageLoadTime: number,\n minTime: number,\n currentTime: number\n): boolean {\n return currentTime - pageLoadTime >= minTime;\n}\n\n/**\n * Pure function: Add position to history (immutable)\n */\nexport function addPositionToHistory(\n positions: Position[],\n newPosition: Position,\n maxSize: number\n): Position[] {\n const updated = [...positions, newPosition];\n if (updated.length > maxSize) {\n return updated.slice(1); // Remove oldest\n }\n return updated;\n}\n\n/**\n * Pure function: Calculate velocity from two Y positions\n */\nexport function calculateVelocity(lastY: number, previousY: number): number {\n return Math.abs(lastY - previousY);\n}\n\n/**\n * Pure function: Check if exit intent should trigger based on Pathfora algorithm\n */\nexport function shouldTriggerExitIntent(\n positions: Position[],\n sensitivity: number,\n relatedTarget: EventTarget | null\n): { shouldTrigger: boolean; lastY: number; previousY: number; velocity: number } {\n // Must have movement history\n if (positions.length < 2) {\n return { shouldTrigger: false, lastY: 0, previousY: 0, velocity: 0 };\n }\n\n // Check if leaving the document (mouse entering browser chrome)\n // relatedTarget is null when leaving to browser UI\n if (relatedTarget && (relatedTarget as any).nodeName !== 'HTML') {\n return { shouldTrigger: false, lastY: 0, previousY: 0, velocity: 0 };\n }\n\n // Get last two positions\n const lastY = positions[positions.length - 1].y;\n const previousY = positions[positions.length - 2].y;\n\n // Calculate velocity (speed of upward movement)\n const velocity = calculateVelocity(lastY, previousY);\n\n // Check if moving up and near top (Pathfora algorithm)\n const isMovingUp = lastY < previousY;\n const isNearTop = lastY - velocity <= sensitivity;\n\n return {\n shouldTrigger: isMovingUp && isNearTop,\n lastY,\n previousY,\n velocity,\n };\n}\n\n/**\n * Pure function: Create exit intent event payload\n */\nexport function createExitIntentEvent(\n lastY: number,\n previousY: number,\n velocity: number,\n pageLoadTime: number,\n timestamp: number\n): ExitIntentEvent {\n return {\n timestamp,\n lastY,\n previousY,\n velocity,\n timeOnPage: timestamp - pageLoadTime,\n };\n}\n\n/**\n * Exit Intent Plugin\n *\n * Detects when users are about to leave the page by tracking upward mouse movement\n * near the top of the viewport. Inspired by Pathfora's showOnExitIntent.\n *\n * **Event-Driven Architecture:**\n * This plugin emits `trigger:exitIntent` events when exit intent is detected.\n * The core runtime listens for these events and automatically re-evaluates experiences.\n *\n * **Usage Pattern:**\n * Use `targeting.custom` to check if exit intent has triggered:\n *\n * @example Basic usage\n * ```typescript\n * import { init, register } from '@prosdevlab/experience-sdk';\n * import { exitIntentPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * init({\n * plugins: [exitIntentPlugin],\n * exitIntent: {\n * sensitivity: 20, // Trigger within 20px of top (default: 50)\n * minTimeOnPage: 2000, // Wait 2s before enabling (default: 2000)\n * delay: 0, // Delay after trigger (default: 0)\n * disableOnMobile: true // Disable on mobile (default: true)\n * }\n * });\n *\n * // Show banner only when exit intent is detected\n * register('exit-offer', {\n * type: 'banner',\n * content: {\n * title: 'Wait! Don't leave yet!',\n * message: 'Get 15% off your first order',\n * buttons: [{ text: 'Claim Offer', variant: 'primary' }]\n * },\n * targeting: {\n * custom: (context) => context.triggers?.exitIntent?.triggered === true\n * },\n * frequency: { max: 1, per: 'session' } // Only show once per session\n * });\n * ```\n *\n * @example Combining with other conditions\n * ```typescript\n * // Show exit offer only on shop pages with items in cart\n * register('cart-recovery', {\n * type: 'banner',\n * content: { message: 'Complete your purchase and save!' },\n * targeting: {\n * url: { contains: '/shop' },\n * custom: (context) => {\n * return (\n * context.triggers?.exitIntent?.triggered === true &&\n * getCart().items.length > 0\n * );\n * }\n * }\n * });\n * ```\n *\n * @example Combining multiple triggers (exit intent + scroll depth)\n * ```typescript\n * // Show offer on exit intent OR after 70% scroll\n * register('engaged-exit', {\n * type: 'banner',\n * content: { message: 'You're almost there!' },\n * targeting: {\n * custom: (context) => {\n * const exitIntent = context.triggers?.exitIntent?.triggered;\n * const scrolled = (context.triggers?.scrollDepth?.percent || 0) >= 70;\n * return exitIntent || scrolled;\n * }\n * }\n * });\n * ```\n */\nexport const exitIntentPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('experiences.exitIntent');\n\n // Default configuration\n plugin.defaults({\n exitIntent: {\n sensitivity: 50,\n minTimeOnPage: 2000,\n delay: 0,\n positionHistorySize: 30,\n disableOnMobile: true,\n },\n });\n\n const exitIntentConfig = config.get<ExitIntentPluginConfig['exitIntent']>('exitIntent');\n\n if (!exitIntentConfig) {\n return;\n }\n\n // State\n let positions: Position[] = [];\n let triggered = false;\n const pageLoadTime = Date.now();\n let mouseMoveListener: ((e: MouseEvent) => void) | null = null;\n let mouseOutListener: ((e: MouseEvent) => void) | null = null;\n\n /**\n * Check if exit intent should be disabled (uses pure function)\n */\n function shouldDisable(): boolean {\n if (!exitIntentConfig?.disableOnMobile) {\n return false;\n }\n return isMobileDevice(navigator.userAgent);\n }\n\n /**\n * Track mouse position (updates state immutably using pure function)\n */\n function trackPosition(e: MouseEvent): void {\n const newPosition = { x: e.clientX, y: e.clientY };\n const maxSize = exitIntentConfig?.positionHistorySize ?? 30;\n positions = addPositionToHistory(positions, newPosition, maxSize);\n }\n\n /**\n * Handle exit intent trigger\n */\n function handleExitIntent(e: MouseEvent): void {\n // Check if already triggered\n if (triggered) {\n return;\n }\n\n // Check minimum time on page using pure function\n const minTime = exitIntentConfig?.minTimeOnPage ?? 2000;\n if (!hasMinTimeElapsed(pageLoadTime, minTime, Date.now())) {\n return;\n }\n\n // Check if should trigger using pure function\n const sensitivity = exitIntentConfig?.sensitivity ?? 50;\n const relatedTarget = (e as any).relatedTarget || (e as any).toElement;\n const result = shouldTriggerExitIntent(positions, sensitivity, relatedTarget);\n\n if (result.shouldTrigger) {\n triggered = true;\n\n // Create event payload using pure function\n const eventPayload = createExitIntentEvent(\n result.lastY,\n result.previousY,\n result.velocity,\n pageLoadTime,\n Date.now()\n );\n\n // Apply delay if configured\n const delay = exitIntentConfig?.delay ?? 0;\n\n if (delay > 0) {\n setTimeout(() => {\n // Emit trigger event (Core will handle evaluation)\n instance.emit('trigger:exitIntent', eventPayload);\n }, delay);\n } else {\n // Emit trigger event (Core will handle evaluation)\n instance.emit('trigger:exitIntent', eventPayload);\n }\n\n // Store in sessionStorage to prevent re-triggering\n try {\n sessionStorage.setItem('xp:exitIntent:triggered', Date.now().toString());\n } catch (_e) {\n // Ignore sessionStorage errors (e.g., in incognito mode)\n }\n\n // Cleanup listeners after trigger (one-time event)\n cleanup();\n }\n }\n\n /**\n * Cleanup event listeners\n */\n function cleanup(): void {\n if (mouseMoveListener) {\n document.removeEventListener('mousemove', mouseMoveListener);\n mouseMoveListener = null;\n }\n if (mouseOutListener) {\n document.removeEventListener('mouseout', mouseOutListener);\n mouseOutListener = null;\n }\n }\n\n /**\n * Initialize exit intent detection\n */\n function initialize(): void {\n if (shouldDisable()) {\n return;\n }\n\n // Check if exit intent was already triggered this session\n try {\n const storedTrigger = sessionStorage.getItem('xp:exitIntent:triggered');\n if (storedTrigger) {\n triggered = true;\n return; // Don't set up listeners if already triggered\n }\n } catch (_e) {\n // Ignore sessionStorage errors (e.g., in incognito mode)\n }\n\n mouseMoveListener = trackPosition;\n mouseOutListener = handleExitIntent;\n\n document.addEventListener('mousemove', mouseMoveListener);\n document.addEventListener('mouseout', mouseOutListener);\n }\n\n // Expose API\n plugin.expose({\n exitIntent: {\n /**\n * Check if exit intent has been triggered\n */\n isTriggered: () => triggered,\n\n /**\n * Reset exit intent state (useful for testing)\n */\n reset: () => {\n triggered = false;\n positions = [];\n\n // Clear sessionStorage\n try {\n sessionStorage.removeItem('xp:exitIntent:triggered');\n } catch (_e) {\n // Ignore sessionStorage errors\n }\n\n cleanup();\n initialize();\n },\n\n /**\n * Get current position history\n */\n getPositions: () => [...positions],\n } satisfies ExitIntentPlugin,\n });\n\n // Initialize on plugin load\n initialize();\n\n // Cleanup on instance destroy\n const destroyHandler = () => {\n cleanup();\n };\n instance.on('destroy', destroyHandler);\n};\n","/**\n * Frequency Capping Plugin\n *\n * Tracks experience impressions and enforces frequency caps.\n * Uses sdk-kit's storage plugin for persistence.\n */\n\nimport type { PluginFunction, SDK } from '@lytics/sdk-kit';\nimport { type StoragePlugin, storagePlugin } from '@lytics/sdk-kit-plugins';\nimport type { Decision, TraceStep } from '../types';\n\nexport interface FrequencyPluginConfig {\n frequency?: {\n enabled?: boolean;\n namespace?: string;\n };\n}\n\nexport interface FrequencyPlugin {\n getImpressionCount(experienceId: string): number;\n hasReachedCap(experienceId: string, max: number, per: 'session' | 'day' | 'week'): boolean;\n recordImpression(experienceId: string): void;\n}\n\ninterface ImpressionData {\n count: number;\n lastImpression: number;\n impressions: number[];\n per?: 'session' | 'day' | 'week'; // Track which storage type this uses\n}\n\n/**\n * Frequency Capping Plugin\n *\n * Automatically tracks impressions and enforces frequency caps.\n * Requires storage plugin for persistence.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n * import { frequencyPlugin } from '@prosdevlab/experience-sdk-plugins';\n *\n * const sdk = createInstance({ frequency: { enabled: true } });\n * sdk.use(frequencyPlugin);\n * ```\n */\nexport const frequencyPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('frequency');\n\n // Set defaults\n plugin.defaults({\n frequency: {\n enabled: true,\n namespace: 'experiences:frequency',\n },\n });\n\n // Track experience frequency configs\n const experienceFrequencyMap = new Map<string, 'session' | 'day' | 'week'>();\n\n // Auto-load storage plugin if not already loaded\n if (!(instance as SDK & { storage?: StoragePlugin }).storage) {\n instance.use(storagePlugin);\n }\n\n const isEnabled = (): boolean => config.get('frequency.enabled') ?? true;\n const getNamespace = (): string => config.get('frequency.namespace') ?? 'experiences:frequency';\n\n // Helper to get the right storage backend based on frequency type\n const getStorageBackend = (per: 'session' | 'day' | 'week'): Storage => {\n return per === 'session' ? sessionStorage : localStorage;\n };\n\n // Helper to get storage key\n const getStorageKey = (experienceId: string): string => {\n return `${getNamespace()}:${experienceId}`;\n };\n\n // Helper to get impression data\n const getImpressionData = (\n experienceId: string,\n per: 'session' | 'day' | 'week'\n ): ImpressionData => {\n const storage = getStorageBackend(per);\n const key = getStorageKey(experienceId);\n const raw = storage.getItem(key);\n\n if (!raw) {\n return {\n count: 0,\n lastImpression: 0,\n impressions: [],\n per,\n };\n }\n\n try {\n return JSON.parse(raw) as ImpressionData;\n } catch {\n return {\n count: 0,\n lastImpression: 0,\n impressions: [],\n per,\n };\n }\n };\n\n // Helper to save impression data\n const saveImpressionData = (experienceId: string, data: ImpressionData): void => {\n const per = data.per || 'session'; // Default to session if not specified\n const storage = getStorageBackend(per);\n const key = getStorageKey(experienceId);\n storage.setItem(key, JSON.stringify(data));\n };\n\n // Get time window in milliseconds\n const getTimeWindow = (per: 'session' | 'day' | 'week'): number => {\n switch (per) {\n case 'session':\n return Number.POSITIVE_INFINITY; // Session storage handles this\n case 'day':\n return 24 * 60 * 60 * 1000; // 24 hours\n case 'week':\n return 7 * 24 * 60 * 60 * 1000; // 7 days\n }\n };\n\n /**\n * Get impression count for an experience\n */\n const getImpressionCount = (\n experienceId: string,\n per: 'session' | 'day' | 'week' = 'session'\n ): number => {\n if (!isEnabled()) return 0;\n const data = getImpressionData(experienceId, per);\n return data.count;\n };\n\n /**\n * Check if an experience has reached its frequency cap\n */\n const hasReachedCap = (\n experienceId: string,\n max: number,\n per: 'session' | 'day' | 'week'\n ): boolean => {\n if (!isEnabled()) return false;\n\n const data = getImpressionData(experienceId, per);\n const timeWindow = getTimeWindow(per);\n const now = Date.now();\n\n // For session caps, just check total count\n if (per === 'session') {\n return data.count >= max;\n }\n\n // For time-based caps, count impressions within the window\n const recentImpressions = data.impressions.filter((timestamp) => now - timestamp < timeWindow);\n\n return recentImpressions.length >= max;\n };\n\n /**\n * Record an impression for an experience\n */\n const recordImpression = (\n experienceId: string,\n per: 'session' | 'day' | 'week' = 'session'\n ): void => {\n if (!isEnabled()) return;\n\n const data = getImpressionData(experienceId, per);\n const now = Date.now();\n\n // Update count and add timestamp\n data.count += 1;\n data.lastImpression = now;\n data.impressions.push(now);\n data.per = per; // Store the frequency type\n\n // Keep only recent impressions (last 7 days)\n const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;\n data.impressions = data.impressions.filter((ts) => ts > sevenDaysAgo);\n\n // Save updated data\n saveImpressionData(experienceId, data);\n\n // Emit event\n instance.emit('experiences:impression-recorded', {\n experienceId,\n count: data.count,\n timestamp: now,\n });\n };\n\n // Expose frequency API\n plugin.expose({\n frequency: {\n getImpressionCount,\n hasReachedCap,\n recordImpression,\n // Internal method to register experience frequency config\n _registerExperience: (experienceId: string, per: 'session' | 'day' | 'week') => {\n experienceFrequencyMap.set(experienceId, per);\n },\n },\n });\n\n // Listen to evaluation events and record impressions\n if (isEnabled()) {\n instance.on('experiences:evaluated', (payload: unknown) => {\n // Handle both single decision and array of decisions\n // evaluate() emits: { decision, experience }\n // evaluateAll() emits: [{ decision, experience }, ...]\n const items = Array.isArray(payload) ? payload : [payload];\n\n for (const item of items) {\n // Item is { decision, experience }\n const decision = (item as { decision?: Decision }).decision;\n\n // Only record if experience was shown\n if (decision?.show && decision.experienceId) {\n // Try to get the 'per' value from our map, fall back to checking the input in trace\n let per: 'session' | 'day' | 'week' =\n experienceFrequencyMap.get(decision.experienceId) || 'session';\n\n // If not in map, try to infer from the decision trace\n if (!experienceFrequencyMap.has(decision.experienceId)) {\n const freqStep = decision.trace.find(\n (t: TraceStep) => t.step === 'check-frequency-cap'\n );\n if (freqStep?.input && typeof freqStep.input === 'object' && 'per' in freqStep.input) {\n per = (freqStep.input as { per: 'session' | 'day' | 'week' }).per;\n // Cache it for next time\n experienceFrequencyMap.set(decision.experienceId, per);\n }\n }\n\n recordImpression(decision.experienceId, per);\n }\n }\n });\n }\n};\n","/**\n * Page Visits Plugin\n *\n * Generic page visit tracking for any SDK built on sdk-kit.\n *\n * Features:\n * - Session-scoped counter (sessionStorage)\n * - Lifetime counter with timestamps (localStorage)\n * - First-visit detection\n * - DNT (Do Not Track) support\n * - GDPR-compliant expiration\n * - Auto-loads storage plugin if missing\n *\n * Events emitted:\n * - 'pageVisits:incremented' with PageVisitsEvent\n * - 'pageVisits:reset'\n * - 'pageVisits:disabled' with { reason: 'dnt' | 'config' }\n *\n * @example\n * ```typescript\n * import { SDK } from '@lytics/sdk-kit';\n * import { storagePlugin, pageVisitsPlugin } from '@lytics/sdk-kit-plugins';\n *\n * const sdk = new SDK({\n * pageVisits: {\n * enabled: true,\n * respectDNT: true,\n * ttl: 31536000 // 1 year\n * }\n * });\n *\n * sdk.use(storagePlugin);\n * sdk.use(pageVisitsPlugin);\n *\n * // Listen to visit events\n * sdk.on('pageVisits:incremented', (event) => {\n * console.log('Visit count:', event.totalVisits);\n * if (event.isFirstVisit) {\n * console.log('Welcome, first-time visitor!');\n * }\n * });\n *\n * // API methods\n * console.log(sdk.pageVisits.getTotalCount()); // 5\n * console.log(sdk.pageVisits.getSessionCount()); // 2\n * console.log(sdk.pageVisits.isFirstVisit()); // false\n * ```\n */\n\nimport type { PluginFunction, SDK } from '@lytics/sdk-kit';\nimport { type StoragePlugin, storagePlugin } from '@lytics/sdk-kit-plugins';\nimport type { PageVisitsEvent, PageVisitsPlugin } from './types';\n\n/**\n * Storage data format for lifetime visits\n */\ninterface TotalData {\n count: number;\n first: number; // Timestamp\n last: number; // Timestamp\n}\n\n/**\n * Pure function: Check if Do Not Track is enabled\n */\nexport function respectsDNT(): boolean {\n if (typeof navigator === 'undefined') return false;\n return (\n navigator.doNotTrack === '1' ||\n (navigator as any).msDoNotTrack === '1' ||\n (window as any).doNotTrack === '1'\n );\n}\n\n/**\n * Pure function: Build storage key with optional prefix\n */\nexport function buildStorageKey(key: string, prefix?: string): string {\n return prefix ? `${prefix}${key}` : key;\n}\n\n/**\n * Pure function: Create PageVisitsEvent payload\n */\nexport function createVisitsEvent(\n isFirstVisit: boolean,\n totalVisits: number,\n sessionVisits: number,\n firstVisitTime: number | undefined,\n lastVisitTime: number | undefined,\n timestamp: number\n): PageVisitsEvent {\n return {\n isFirstVisit,\n totalVisits,\n sessionVisits,\n firstVisitTime,\n lastVisitTime,\n timestamp,\n };\n}\n\nexport const pageVisitsPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('pageVisits');\n\n // Set defaults\n plugin.defaults({\n pageVisits: {\n enabled: true,\n respectDNT: true,\n sessionKey: 'pageVisits:session',\n totalKey: 'pageVisits:total',\n ttl: undefined,\n autoIncrement: true,\n },\n });\n\n // Auto-load storage plugin if not present\n if (!(instance as SDK & { storage?: StoragePlugin }).storage) {\n console.warn('[PageVisits] Storage plugin not found, auto-loading...');\n instance.use(storagePlugin);\n }\n\n // Cast instance to include storage\n const sdkInstance = instance as SDK & { storage: StoragePlugin };\n\n // Internal state\n let sessionCount = 0;\n let totalCount = 0;\n let firstVisitTime: number | undefined;\n let lastVisitTime: number | undefined;\n let isFirstVisitFlag = false;\n let initialized = false;\n\n /**\n * Load existing visit data from storage\n */\n function loadData(): void {\n const sessionKey = config.get('pageVisits.sessionKey') ?? 'pageVisits:session';\n const totalKey = config.get('pageVisits.totalKey') ?? 'pageVisits:total';\n\n // Load session count\n const storedSession = sdkInstance.storage.get<number>(sessionKey, {\n backend: 'sessionStorage',\n });\n sessionCount = storedSession ?? 0;\n\n // Load total data\n const storedTotal = sdkInstance.storage.get<TotalData>(totalKey, {\n backend: 'localStorage',\n });\n\n if (storedTotal) {\n totalCount = storedTotal.count ?? 0;\n firstVisitTime = storedTotal.first;\n lastVisitTime = storedTotal.last;\n isFirstVisitFlag = false;\n } else {\n totalCount = 0;\n firstVisitTime = undefined;\n lastVisitTime = undefined;\n isFirstVisitFlag = true;\n }\n }\n\n /**\n * Save visit data to storage\n */\n function saveData(): void {\n const sessionKey = config.get('pageVisits.sessionKey') ?? 'pageVisits:session';\n const totalKey = config.get('pageVisits.totalKey') ?? 'pageVisits:total';\n const ttl = config.get('pageVisits.ttl');\n\n // Save session count\n sdkInstance.storage.set(sessionKey, sessionCount, {\n backend: 'sessionStorage',\n });\n\n // Save total data\n const totalData: TotalData = {\n count: totalCount,\n first: firstVisitTime ?? Date.now(),\n last: lastVisitTime ?? Date.now(),\n };\n\n sdkInstance.storage.set(totalKey, totalData, {\n backend: 'localStorage',\n ...(ttl && { ttl }),\n });\n }\n\n /**\n * Increment visit counters\n */\n function increment(): void {\n if (!initialized) {\n loadData();\n initialized = true;\n }\n\n // Increment counters\n sessionCount += 1;\n totalCount += 1;\n const now = Date.now();\n\n // Set first visit time if needed\n if (isFirstVisitFlag) {\n firstVisitTime = now;\n }\n\n // Update last visit time\n lastVisitTime = now;\n\n // Save to storage\n saveData();\n\n // Emit event using pure function\n const event = createVisitsEvent(\n isFirstVisitFlag,\n totalCount,\n sessionCount,\n firstVisitTime,\n lastVisitTime,\n now\n );\n\n plugin.emit('pageVisits:incremented', event);\n\n // After first increment, no longer first visit\n if (isFirstVisitFlag) {\n isFirstVisitFlag = false;\n }\n }\n\n /**\n * Reset all data\n */\n function reset(): void {\n const sessionKey = config.get('pageVisits.sessionKey') ?? 'pageVisits:session';\n const totalKey = config.get('pageVisits.totalKey') ?? 'pageVisits:total';\n\n // Clear storage\n sdkInstance.storage.remove(sessionKey, { backend: 'sessionStorage' });\n sdkInstance.storage.remove(totalKey, { backend: 'localStorage' });\n\n // Reset state\n sessionCount = 0;\n totalCount = 0;\n firstVisitTime = undefined;\n lastVisitTime = undefined;\n isFirstVisitFlag = false;\n initialized = false;\n\n // Emit event\n plugin.emit('pageVisits:reset');\n }\n\n /**\n * Get current state\n */\n function getState(): PageVisitsEvent {\n return createVisitsEvent(\n isFirstVisitFlag,\n totalCount,\n sessionCount,\n firstVisitTime,\n lastVisitTime,\n Date.now()\n );\n }\n\n /**\n * Initialize plugin\n */\n function initialize(): void {\n const enabled = config.get('pageVisits.enabled') ?? true;\n const respectDNTConfig = config.get('pageVisits.respectDNT') ?? true;\n const autoIncrement = config.get('pageVisits.autoIncrement') ?? true;\n\n // Check DNT using pure function\n if (respectDNTConfig && respectsDNT()) {\n plugin.emit('pageVisits:disabled', { reason: 'dnt' });\n return;\n }\n\n // Check enabled\n if (!enabled) {\n plugin.emit('pageVisits:disabled', { reason: 'config' });\n return;\n }\n\n // Auto-increment on load\n if (autoIncrement) {\n increment();\n }\n }\n\n // Initialize on SDK ready\n instance.on('sdk:ready', initialize);\n\n // Expose public API\n plugin.expose({\n pageVisits: {\n getTotalCount: () => totalCount,\n getSessionCount: () => sessionCount,\n isFirstVisit: () => isFirstVisitFlag,\n getFirstVisitTime: () => firstVisitTime,\n getLastVisitTime: () => lastVisitTime,\n increment,\n reset,\n getState,\n } satisfies PageVisitsPlugin,\n });\n};\n","/** @module scrollDepthPlugin */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport type { ScrollDepthEvent, ScrollDepthPluginConfig } from './types';\n\n/**\n * Pure function: Detect device type based on user agent and screen size\n */\nexport function detectDevice(): 'mobile' | 'tablet' | 'desktop' {\n if (typeof window === 'undefined') return 'desktop';\n\n const ua = navigator.userAgent;\n const isMobile = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);\n const isTablet = /iPad|Android(?!.*Mobile)/i.test(ua);\n\n // Also check screen size as fallback\n const width = window.innerWidth;\n if (width < 768) return 'mobile';\n if (width < 1024) return 'tablet';\n if (isMobile) return 'mobile';\n if (isTablet) return 'tablet';\n\n return 'desktop';\n}\n\n/**\n * Pure function: Throttle helper\n * @param func Function to throttle\n * @param wait Wait time in milliseconds\n * @returns Throttled function\n */\nexport function throttle<T extends (...args: any[]) => void>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let previous = 0;\n\n return function throttled(...args: Parameters<T>) {\n const now = Date.now();\n const remaining = wait - (now - previous);\n\n if (remaining <= 0 || remaining > wait) {\n if (timeout) {\n clearTimeout(timeout);\n timeout = null;\n }\n previous = now;\n func(...args);\n } else if (!timeout) {\n timeout = setTimeout(() => {\n previous = Date.now();\n timeout = null;\n func(...args);\n }, remaining);\n }\n };\n}\n\n/**\n * Pure function: Calculate scroll percentage\n */\nexport function calculateScrollPercent(includeViewportHeight: boolean): number {\n if (typeof document === 'undefined') return 0;\n\n // Browser compatibility: Use scrollingElement or fallback\n const scrollingElement = document.scrollingElement || document.documentElement;\n\n const scrollTop = scrollingElement.scrollTop;\n const scrollHeight = scrollingElement.scrollHeight;\n const clientHeight = scrollingElement.clientHeight;\n\n // Handle edge case: content shorter than viewport\n if (scrollHeight <= clientHeight) {\n return 100; // Treat as fully scrolled\n }\n\n if (includeViewportHeight) {\n // Include viewport: more intuitive for users\n // 100% when bottom of viewport reaches end of content\n return Math.min(((scrollTop + clientHeight) / scrollHeight) * 100, 100);\n }\n\n // Exclude viewport: traditional method\n // 100% when top of viewport reaches scrollable end\n return Math.min((scrollTop / (scrollHeight - clientHeight)) * 100, 100);\n}\n\n/**\n * Pure function: Calculate engagement score from metrics\n */\nexport function calculateEngagementScore(\n velocity: number,\n fastScrollThreshold: number,\n directionChanges: number,\n timeScrollingUp: number,\n totalTime: number\n): number {\n // Lower score = more engaged (slower scrolling, fewer direction changes)\n // Higher score = skimming (fast scrolling, lots of seeking)\n const velocityScore = Math.min((velocity / fastScrollThreshold) * 50, 50);\n const directionScore = Math.min((directionChanges / 5) * 30, 30);\n const seekingScore = Math.min((timeScrollingUp / totalTime) * 20, 20);\n\n return Math.max(0, 100 - (velocityScore + directionScore + seekingScore));\n}\n\n/**\n * Scroll Depth Plugin\n *\n * Tracks scroll depth and emits `trigger:scrollDepth` events when thresholds are crossed.\n *\n * ## How It Works\n *\n * 1. **Detection**: Listens to `scroll` events (throttled)\n * 2. **Calculation**: Calculates current scroll percentage\n * 3. **Tracking**: Tracks maximum scroll depth and threshold crossings\n * 4. **Emission**: Emits `trigger:scrollDepth` events when thresholds are crossed\n *\n * ## Configuration\n *\n * ```typescript\n * init({\n * scrollDepth: {\n * thresholds: [25, 50, 75, 100], // Percentages to track\n * throttle: 100, // Throttle interval (ms)\n * includeViewportHeight: true, // Calculation method\n * recalculateOnResize: true // Recalculate on resize\n * }\n * });\n * ```\n *\n * ## Experience Targeting\n *\n * ```typescript\n * register('mid-article-cta', {\n * type: 'banner',\n * content: { message: 'Enjoying the article?' },\n * targeting: {\n * custom: (ctx) => (ctx.triggers?.scrollDepth?.percent || 0) >= 50\n * }\n * });\n * ```\n *\n * ## API Methods\n *\n * ```typescript\n * // Get maximum scroll percentage reached\n * instance.scrollDepth.getMaxPercent(); // 73\n *\n * // Get current scroll percentage\n * instance.scrollDepth.getCurrentPercent(); // 50\n *\n * // Get all crossed thresholds\n * instance.scrollDepth.getThresholdsCrossed(); // [25, 50]\n *\n * // Reset tracking (useful for testing)\n * instance.scrollDepth.reset();\n * ```\n *\n * @param plugin Plugin interface from sdk-kit\n * @param instance SDK instance\n * @param config SDK configuration\n */\nexport const scrollDepthPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('experiences.scrollDepth');\n\n // Set defaults\n plugin.defaults({\n scrollDepth: {\n thresholds: [25, 50, 75, 100],\n throttle: 100,\n includeViewportHeight: true,\n recalculateOnResize: true,\n trackAdvancedMetrics: false,\n fastScrollVelocityThreshold: 3,\n disableOnMobile: false,\n },\n });\n\n // Get config\n const scrollConfig = config.get('scrollDepth') as ScrollDepthPluginConfig['scrollDepth'];\n if (!scrollConfig) return;\n\n // TypeScript guard: scrollConfig is now guaranteed to be defined\n const cfg = scrollConfig;\n\n // Check device and disable if needed (using pure function)\n const device = detectDevice();\n if (cfg.disableOnMobile && device === 'mobile') {\n return; // Skip initialization on mobile\n }\n\n // State\n let maxScrollPercent = 0;\n const triggeredThresholds = new Set<number>();\n\n // Advanced metrics state\n const pageLoadTime = Date.now();\n let lastScrollPosition = 0;\n let lastScrollTime = Date.now();\n let lastScrollDirection: 'up' | 'down' | null = null;\n let directionChangesSinceLastThreshold = 0;\n let timeScrollingUp = 0;\n const thresholdTimes = new Map<number, number>(); // threshold -> time reached\n\n /**\n * Handle scroll event\n */\n function handleScroll() {\n // Use pure function for calculation\n const currentPercent = calculateScrollPercent(cfg.includeViewportHeight ?? true);\n const now = Date.now();\n const scrollingElement = document.scrollingElement || document.documentElement;\n const currentPosition = scrollingElement.scrollTop;\n\n // Track advanced metrics if enabled\n let velocity = 0;\n let _directionChange = false;\n\n if (cfg.trackAdvancedMetrics) {\n // Calculate velocity (pixels per millisecond)\n const timeDelta = now - lastScrollTime;\n const positionDelta = currentPosition - lastScrollPosition;\n velocity = timeDelta > 0 ? Math.abs(positionDelta) / timeDelta : 0;\n\n // Detect direction changes\n const currentDirection =\n positionDelta > 0 ? 'down' : positionDelta < 0 ? 'up' : lastScrollDirection;\n if (currentDirection && lastScrollDirection && currentDirection !== lastScrollDirection) {\n directionChangesSinceLastThreshold++;\n _directionChange = true;\n }\n\n // Track time spent scrolling up (seeking behavior)\n if (currentDirection === 'up' && timeDelta > 0) {\n timeScrollingUp += timeDelta;\n }\n\n lastScrollDirection = currentDirection;\n lastScrollPosition = currentPosition;\n lastScrollTime = now;\n }\n\n // Update max scroll\n maxScrollPercent = Math.max(maxScrollPercent, currentPercent);\n\n // Check thresholds\n for (const threshold of cfg.thresholds || []) {\n if (currentPercent >= threshold && !triggeredThresholds.has(threshold)) {\n triggeredThresholds.add(threshold);\n\n // Record time to threshold\n if (cfg.trackAdvancedMetrics) {\n thresholdTimes.set(threshold, now - pageLoadTime);\n }\n\n // Build event payload\n const eventPayload: ScrollDepthEvent = {\n triggered: true,\n timestamp: now,\n percent: Math.round(currentPercent * 100) / 100,\n maxPercent: Math.round(maxScrollPercent * 100) / 100,\n threshold,\n thresholdsCrossed: Array.from(triggeredThresholds).sort((a, b) => a - b),\n device,\n };\n\n // Add advanced metrics if enabled\n if (cfg.trackAdvancedMetrics) {\n const fastScrollThreshold = cfg.fastScrollVelocityThreshold || 3;\n const isFastScrolling = velocity > fastScrollThreshold;\n\n // Calculate engagement score using pure function\n const engagementScore = calculateEngagementScore(\n velocity,\n fastScrollThreshold,\n directionChangesSinceLastThreshold,\n timeScrollingUp,\n now - pageLoadTime\n );\n\n eventPayload.advanced = {\n timeToThreshold: now - pageLoadTime,\n velocity: Math.round(velocity * 1000) / 1000, // Round to 3 decimals\n isFastScrolling,\n directionChanges: directionChangesSinceLastThreshold,\n timeScrollingUp,\n engagementScore: Math.round(engagementScore),\n };\n\n // Reset direction changes counter after threshold\n directionChangesSinceLastThreshold = 0;\n }\n\n instance.emit('trigger:scrollDepth', eventPayload);\n }\n }\n }\n\n // Throttle scroll handler (using pure function)\n const throttledScrollHandler = throttle(handleScroll, cfg.throttle || 100);\n\n // Throttle resize handler (using pure function)\n const throttledResizeHandler = throttle(handleScroll, cfg.throttle || 100);\n\n // Initialize\n function initialize() {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return; // Not in browser environment\n }\n\n // Add scroll listener\n window.addEventListener('scroll', throttledScrollHandler, { passive: true });\n\n // Add resize listener (optional)\n if (cfg.recalculateOnResize) {\n window.addEventListener('resize', throttledResizeHandler, { passive: true });\n }\n\n // Don't check initial scroll position - wait for first user interaction\n // This avoids triggering all thresholds immediately on pages that start scrolled\n }\n\n // Cleanup function\n function cleanup() {\n window.removeEventListener('scroll', throttledScrollHandler);\n window.removeEventListener('resize', throttledResizeHandler);\n }\n\n // Setup destroy handler\n const destroyHandler = () => {\n cleanup();\n };\n instance.on('destroy', destroyHandler);\n\n // Expose API\n plugin.expose({\n scrollDepth: {\n /**\n * Get the maximum scroll percentage reached during the session\n */\n getMaxPercent: () => maxScrollPercent,\n\n /**\n * Get the current scroll percentage\n */\n getCurrentPercent: () => calculateScrollPercent(cfg.includeViewportHeight ?? true),\n\n /**\n * Get all thresholds that have been crossed\n */\n getThresholdsCrossed: () => Array.from(triggeredThresholds).sort((a, b) => a - b),\n\n /**\n * Get the detected device type\n */\n getDevice: () => device,\n\n /**\n * Get advanced metrics (only available when trackAdvancedMetrics is enabled)\n */\n getAdvancedMetrics: () => {\n if (!cfg.trackAdvancedMetrics) return null;\n\n const now = Date.now();\n return {\n timeOnPage: now - pageLoadTime,\n directionChanges: directionChangesSinceLastThreshold,\n timeScrollingUp,\n thresholdTimes: Object.fromEntries(thresholdTimes),\n };\n },\n\n /**\n * Reset scroll depth tracking\n * Clears all triggered thresholds, max scroll, and advanced metrics\n */\n reset: () => {\n maxScrollPercent = 0;\n triggeredThresholds.clear();\n directionChangesSinceLastThreshold = 0;\n timeScrollingUp = 0;\n thresholdTimes.clear();\n lastScrollDirection = null;\n },\n },\n });\n\n // Initialize on next tick to ensure DOM is ready\n if (typeof window !== 'undefined') {\n setTimeout(initialize, 0);\n }\n\n // Return cleanup function\n return () => {\n cleanup();\n instance.off('destroy', destroyHandler);\n };\n};\n","/** @module timeDelayPlugin */\n\nimport type { PluginFunction } from '@lytics/sdk-kit';\nimport type { TimeDelayEvent, TimeDelayPlugin, TimeDelayPluginConfig } from './types';\n\n/**\n * Pure function: Calculate elapsed time from start\n */\nexport function calculateElapsed(startTime: number, pausedDuration: number): number {\n return Date.now() - startTime - pausedDuration;\n}\n\n/**\n * Pure function: Check if document is hidden (Page Visibility API)\n */\nexport function isDocumentHidden(): boolean {\n if (typeof document === 'undefined') return false;\n return document.hidden || false;\n}\n\n/**\n * Pure function: Create time delay event payload\n */\nexport function createTimeDelayEvent(\n startTime: number,\n pausedDuration: number,\n wasPaused: boolean,\n visibilityChanges: number\n): TimeDelayEvent {\n const timestamp = Date.now();\n const elapsed = timestamp - startTime;\n const activeElapsed = elapsed - pausedDuration;\n\n return {\n timestamp,\n elapsed,\n activeElapsed,\n wasPaused,\n visibilityChanges,\n };\n}\n\n/**\n * Time Delay Plugin\n *\n * Tracks time elapsed since SDK initialization and emits trigger:timeDelay events\n * when the configured delay is reached.\n *\n * **Features:**\n * - Millisecond precision timing\n * - Pause/resume on tab visibility change (optional)\n * - Tracks active vs total elapsed time\n * - Full timer lifecycle management\n *\n * **Event-Driven Architecture:**\n * This plugin emits `trigger:timeDelay` events when the delay threshold is reached.\n * The core runtime listens for these events and automatically re-evaluates experiences.\n *\n * **Usage Pattern:**\n * Use `targeting.custom` to check if time delay has triggered:\n *\n * @example Basic usage\n * ```typescript\n * import { init, register } from '@prosdevlab/experience-sdk';\n *\n * init({\n * timeDelay: {\n * delay: 5000, // 5 seconds\n * pauseWhenHidden: true // Pause when tab hidden (default)\n * }\n * });\n *\n * // Show banner after 5 seconds of active viewing time\n * register('timed-offer', {\n * type: 'banner',\n * content: {\n * message: 'Limited time offer!',\n * buttons: [{ text: 'Claim Now', variant: 'primary' }]\n * },\n * targeting: {\n * custom: (context) => {\n * const active = context.triggers?.timeDelay?.activeElapsed || 0;\n * return active >= 5000;\n * }\n * }\n * });\n * ```\n *\n * @example Combining with other triggers\n * ```typescript\n * // Show after 10s OR on exit intent (whichever comes first)\n * register('engaged-offer', {\n * type: 'banner',\n * content: { message: 'Special offer for engaged users!' },\n * targeting: {\n * custom: (context) => {\n * const timeElapsed = (context.triggers?.timeDelay?.activeElapsed || 0) >= 10000;\n * const exitIntent = context.triggers?.exitIntent?.triggered;\n * return timeElapsed || exitIntent;\n * }\n * }\n * });\n * ```\n *\n * @param plugin Plugin interface from sdk-kit\n * @param instance SDK instance\n * @param config SDK configuration\n */\nexport const timeDelayPlugin: PluginFunction = (plugin, instance, config) => {\n plugin.ns('experiences.timeDelay');\n\n // Set defaults\n plugin.defaults({\n timeDelay: {\n delay: 0,\n pauseWhenHidden: true,\n },\n });\n\n // Get config\n const timeDelayConfig = config.get('timeDelay') as TimeDelayPluginConfig['timeDelay'];\n if (!timeDelayConfig) return;\n\n const delay = timeDelayConfig.delay ?? 0;\n const pauseWhenHidden = timeDelayConfig.pauseWhenHidden ?? true;\n\n // Skip if delay is 0 (disabled)\n if (delay <= 0) return;\n\n // State\n const startTime = Date.now();\n let triggered = false;\n let paused = false;\n let pausedDuration = 0;\n let lastPauseTime = 0;\n let visibilityChanges = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n let visibilityListener: (() => void) | null = null;\n\n /**\n * Trigger the time delay event\n */\n function trigger(): void {\n if (triggered) return;\n\n triggered = true;\n\n // Create event payload using pure function\n const eventPayload = createTimeDelayEvent(\n startTime,\n pausedDuration,\n visibilityChanges > 0,\n visibilityChanges\n );\n\n // Emit trigger event\n instance.emit('trigger:timeDelay', eventPayload);\n\n // Cleanup\n cleanup();\n }\n\n /**\n * Schedule timer with remaining delay\n */\n function scheduleTimer(remainingDelay: number): void {\n if (timer) {\n clearTimeout(timer);\n }\n\n timer = setTimeout(() => {\n trigger();\n }, remainingDelay);\n }\n\n /**\n * Handle visibility change\n */\n function handleVisibilityChange(): void {\n const hidden = isDocumentHidden();\n\n if (hidden && !paused) {\n // Tab just became hidden - pause timer\n paused = true;\n lastPauseTime = Date.now();\n visibilityChanges++;\n\n // Clear existing timer\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n } else if (!hidden && paused) {\n // Tab just became visible - resume timer\n paused = false;\n const pauseDuration = Date.now() - lastPauseTime;\n pausedDuration += pauseDuration;\n visibilityChanges++;\n\n // Calculate remaining delay\n const elapsed = calculateElapsed(startTime, pausedDuration);\n const remaining = delay - elapsed;\n\n if (remaining > 0) {\n scheduleTimer(remaining);\n } else {\n // Delay already elapsed during pause\n trigger();\n }\n }\n }\n\n /**\n * Cleanup listeners and timers\n */\n function cleanup(): void {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n if (visibilityListener && typeof document !== 'undefined') {\n document.removeEventListener('visibilitychange', visibilityListener);\n visibilityListener = null;\n }\n }\n\n /**\n * Initialize timer and visibility listener\n */\n function initialize(): void {\n // Check if already hidden on init\n if (pauseWhenHidden && isDocumentHidden()) {\n paused = true;\n lastPauseTime = Date.now();\n visibilityChanges++;\n } else {\n // Start timer\n scheduleTimer(delay);\n }\n\n // Setup visibility listener if pause is enabled\n if (pauseWhenHidden && typeof document !== 'undefined') {\n visibilityListener = handleVisibilityChange;\n document.addEventListener('visibilitychange', visibilityListener);\n }\n }\n\n // Expose API\n plugin.expose({\n timeDelay: {\n getElapsed: () => {\n return Date.now() - startTime;\n },\n\n getActiveElapsed: () => {\n let currentPausedDuration = pausedDuration;\n if (paused) {\n // Add current pause duration\n currentPausedDuration += Date.now() - lastPauseTime;\n }\n return calculateElapsed(startTime, currentPausedDuration);\n },\n\n getRemaining: () => {\n if (triggered) return 0;\n\n const elapsed = calculateElapsed(startTime, pausedDuration);\n const remaining = delay - elapsed;\n return Math.max(0, remaining);\n },\n\n isPaused: () => paused,\n\n isTriggered: () => triggered,\n\n reset: () => {\n triggered = false;\n paused = false;\n pausedDuration = 0;\n lastPauseTime = 0;\n visibilityChanges = 0;\n\n cleanup();\n initialize();\n },\n } satisfies TimeDelayPlugin,\n });\n\n // Initialize on plugin load\n initialize();\n\n // Cleanup on instance destroy\n const destroyHandler = () => {\n cleanup();\n };\n instance.on('destroy', destroyHandler);\n};\n","/**\n * Deep merge utility with \"underwrite\" pattern.\n *\n * Recursively merges source into target, where **target values take precedence**.\n * This is the opposite of typical deep merge where source wins.\n *\n * This \"underwrite\" pattern is used for config defaults:\n * - User config (target) always wins\n * - Defaults (source) only fill in missing values\n *\n * @param target - Target object (takes precedence)\n * @param source - Source object (provides defaults)\n * @returns New merged object (does not mutate inputs)\n *\n * @example\n * ```typescript\n * const userConfig = { api: { timeout: 5000 } };\n * const defaults = { api: { timeout: 3000, retries: 3 }, debug: false };\n *\n * const result = deepMerge(target, source);\n * // Result: {\n * // api: { timeout: 5000, retries: 3 }, // timeout from user, retries from defaults\n * // debug: false // debug from defaults\n * // }\n * ```\n */\nexport function deepMerge(\n target: Record<string, any>,\n source: Record<string, any>\n): Record<string, any> {\n // Create a new object to avoid mutation\n const result: Record<string, any> = { ...target };\n\n for (const key in source) {\n if (!Object.hasOwn(source, key)) {\n continue;\n }\n\n const sourceValue = source[key];\n const targetValue = result[key];\n\n // If target has no value for this key, use source value\n if (targetValue === undefined) {\n result[key] = sourceValue;\n continue;\n }\n\n // If both are plain objects, recursively merge\n if (isPlainObject(targetValue) && isPlainObject(sourceValue)) {\n result[key] = deepMerge(targetValue, sourceValue);\n }\n\n // Otherwise, target value wins (underwrite pattern)\n // result[key] is already set from { ...target }\n }\n\n return result;\n}\n\n/**\n * Check if value is a plain object (not array, null, Date, etc.)\n *\n * @param value - Value to check\n * @returns True if value is a plain object\n * @private\n */\nfunction isPlainObject(value: any): value is Record<string, any> {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n\n // Check if it's a plain object (not Array, Date, RegExp, etc.)\n return Object.prototype.toString.call(value) === '[object Object]';\n}\n","/**\n * Configuration management with dot-notation paths and underwrite pattern.\n *\n * The Config class manages SDK configuration with support for:\n * - Dot-notation paths for nested access (`'my.plugin.setting'`)\n * - Underwrite pattern for defaults (existing values take precedence)\n * - Required plugin tracking\n * - Immutable getAll() for safe access\n *\n * @example\n * ```typescript\n * const config = new Config({ api: { timeout: 5000 } });\n *\n * // Set defaults (won't overwrite existing timeout)\n * config.defaults({ api: { timeout: 3000, retries: 3 } });\n *\n * // Get with dot-notation\n * config.get('api.timeout'); // 5000\n * config.get('api.retries'); // 3\n *\n * // Set values\n * config.set('api.baseUrl', 'https://api.example.com');\n * ```\n */\n\nimport { deepMerge } from '../util/deep-merge';\n\nexport class Config {\n private data: Record<string, any> = {};\n private required = new Set<string>();\n\n /**\n * Create a new Config instance.\n *\n * @param initialConfig - Initial configuration object\n */\n constructor(initialConfig: Record<string, any> = {}) {\n // Deep copy to prevent external mutation\n this.data = JSON.parse(JSON.stringify(initialConfig));\n }\n\n /**\n * Set default configuration values using underwrite pattern.\n *\n * Existing configuration values always take precedence over defaults.\n * This allows plugins to provide defaults without overwriting user config.\n *\n * @param config - Default configuration object\n *\n * @example\n * ```typescript\n * // User provided config\n * const config = new Config({ api: { timeout: 10000 } });\n *\n * // Plugin sets defaults\n * config.defaults({\n * api: { timeout: 3000, retries: 3 },\n * debug: false\n * });\n *\n * // Result: { api: { timeout: 10000, retries: 3 }, debug: false }\n * // User's timeout (10000) wins over default (3000)\n * ```\n */\n defaults(config: Record<string, any>): void {\n // Use underwrite pattern: existing config (target) wins over defaults (source)\n this.data = deepMerge(this.data, config);\n }\n\n /**\n * Merge configuration values (new values override existing).\n *\n * New configuration values take precedence over existing values.\n * This is the opposite of defaults() and is used when user provides new config.\n *\n * @param config - Configuration object to merge\n *\n * @example\n * ```typescript\n * const config = new Config({ api: { timeout: 3000 } });\n *\n * // User provides new config\n * config.merge({ api: { timeout: 10000, retries: 3 } });\n *\n * // Result: { api: { timeout: 10000, retries: 3 } }\n * // New timeout (10000) wins over existing (3000)\n * ```\n */\n merge(config: Record<string, any>): void {\n // Regular merge: new config (source) wins over existing (target)\n this.data = deepMerge(config, this.data);\n }\n\n /**\n * Get a configuration value by dot-notation path.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @returns Configuration value or undefined if not found\n *\n * @example\n * ```typescript\n * config.get('api.timeout'); // 5000\n * config.get('api.retries'); // 3\n * config.get('nonexistent.path'); // undefined\n * config.get<number>('api.timeout'); // Type-safe access\n * ```\n */\n get<T = any>(path: string): T {\n const keys = path.split('.');\n let current: any = this.data;\n\n for (const key of keys) {\n if (current == null) {\n return undefined as T;\n }\n current = current[key];\n }\n\n return current as T;\n }\n\n /**\n * Set a configuration value by dot-notation path.\n *\n * Creates intermediate objects as needed.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @param value - Value to set\n *\n * @example\n * ```typescript\n * config.set('api.timeout', 5000);\n * config.set('api.headers.authorization', 'Bearer token');\n * config.set('deeply.nested.value', 42);\n * ```\n */\n set(path: string, value: any): void {\n const keys = path.split('.');\n const lastKey = keys.pop();\n if (!lastKey) return; // Guard against empty path\n\n let current: any = this.data;\n\n // Create intermediate objects\n for (const key of keys) {\n if (current[key] == null || typeof current[key] !== 'object') {\n current[key] = {};\n }\n current = current[key];\n }\n\n current[lastKey] = value;\n }\n\n /**\n * Mark a plugin namespace as required.\n *\n * Required plugins must be properly enabled for the SDK to function.\n *\n * @param namespace - Plugin namespace to mark as required\n *\n * @example\n * ```typescript\n * config.markRequired('analytics');\n * config.isRequired('analytics'); // true\n * ```\n */\n markRequired(namespace?: string): void {\n if (namespace !== undefined && namespace !== null) {\n this.required.add(namespace);\n }\n }\n\n /**\n * Check if a plugin namespace is marked as required.\n *\n * @param namespace - Plugin namespace to check\n * @returns True if the namespace is required\n */\n isRequired(namespace: string): boolean {\n return this.required.has(namespace);\n }\n\n /**\n * Get all configuration as an immutable copy.\n *\n * Returns a deep copy to prevent external mutation of config.\n *\n * @returns Copy of the entire configuration object\n *\n * @example\n * ```typescript\n * const allConfig = config.getAll();\n * allConfig.api = {}; // Does not affect internal config\n * ```\n */\n getAll(): Record<string, any> {\n // Deep copy to prevent nested mutation\n return JSON.parse(JSON.stringify(this.data));\n }\n}\n","/**\n * Event Emitter with wildcard pattern matching support.\n *\n * Supports wildcard patterns for event subscriptions:\n * - Exact match: `'widget.show'` matches `'widget.show'`\n * - Wildcard suffix: `'widget.*'` matches `'widget.show'`, `'widget.hide'`, etc.\n * - Wildcard prefix: `'*.show'` matches `'widget.show'`, `'modal.show'`, etc.\n * - Match all: `'*'` matches all events\n *\n * Uses regex compilation for efficient pattern matching.\n */\n\n/**\n * Subscription entry storing the pattern and handler.\n */\ninterface Subscription {\n pattern: string;\n compiledPattern: RegExp;\n handler: (...args: any[]) => void;\n}\n\nexport class Emitter {\n private subscriptions: Subscription[] = [];\n\n /**\n * Subscribe to an event or pattern.\n *\n * @param event - Event name or wildcard pattern\n * @param handler - Event handler function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const emitter = new Emitter();\n *\n * // Exact match\n * emitter.on('user.login', (data) => console.log('Login:', data));\n *\n * // Wildcard patterns\n * emitter.on('user.*', (data) => console.log('User event:', data));\n * emitter.on('*.error', (data) => console.log('Error:', data));\n * emitter.on('*', (data) => console.log('Any event:', data));\n *\n * // Unsubscribe\n * const unsub = emitter.on('test', handler);\n * unsub();\n * ```\n */\n on(event: string, handler: (...args: any[]) => void): () => void {\n if (typeof handler !== 'function') {\n throw new TypeError('handler must be a function');\n }\n\n const subscription: Subscription = {\n pattern: event,\n compiledPattern: this.compilePattern(event),\n handler,\n };\n\n this.subscriptions.push(subscription);\n\n // Return unsubscribe function\n return () => this.off(event, handler);\n }\n\n /**\n * Unsubscribe from an event or pattern.\n *\n * @param event - Event name or pattern to unsubscribe from\n * @param handler - Handler function to remove\n */\n off(event: string, handler: (...args: any[]) => void): void {\n this.subscriptions = this.subscriptions.filter(\n (sub) => !(sub.pattern === event && sub.handler === handler)\n );\n }\n\n /**\n * Emit an event to all matching subscribers.\n *\n * Notifies all handlers whose patterns match the event name.\n * Handlers are called with the provided arguments.\n * Errors in handlers are caught and logged to prevent breaking the event flow.\n *\n * @param event - Event name to emit\n * @param args - Arguments to pass to handlers\n *\n * @example\n * ```typescript\n * emitter.emit('user.login', { userId: '123' });\n * emitter.emit('widget.show', { id: 'modal-1' }, { animate: true });\n * ```\n */\n emit(event: string, ...args: any[]): void {\n for (const subscription of this.subscriptions) {\n if (subscription.compiledPattern.test(event)) {\n try {\n subscription.handler(...args);\n } catch (err) {\n console.error(`Error in event handler for \"${event}\":`, err);\n }\n }\n }\n }\n\n /**\n * Remove all event listeners.\n *\n * Useful for cleanup when destroying the SDK instance.\n */\n removeAllListeners(): void {\n this.subscriptions = [];\n }\n\n /**\n * Compile a pattern string into a RegExp for matching.\n *\n * Converts wildcard patterns into regular expressions:\n * - `widget.*` → `/^widget\\.(.*?)$/`\n * - `*.show` → `/^(.*?)\\.show$/`\n * - `*` → `/^(.*?)$/`\n *\n * @param pattern - Pattern string with optional wildcards\n * @returns Compiled RegExp for matching event names\n * @private\n */\n private compilePattern(pattern: string): RegExp {\n // Escape all regex special characters\n const escaped = this.escapeRegExp(pattern);\n // Replace escaped asterisks with wildcard capture groups\n const withWildcards = escaped.replace(/\\\\\\*/g, '(.*?)');\n // Anchor to start and end\n return new RegExp(`^${withWildcards}$`);\n }\n\n /**\n * Escape special regex characters in a string.\n *\n * @param str - String to escape\n * @returns Escaped string safe for use in RegExp\n * @private\n */\n private escapeRegExp(str: string): string {\n return str.replace(/[\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n }\n}\n","/**\n * Expose capability for adding plugin methods to SDK instance.\n *\n * Plugins use this to add public API methods that users can call.\n * Methods are merged directly onto the SDK instance.\n *\n * @example\n * ```typescript\n * const expose = new Expose(sdkInstance);\n * expose.expose({\n * track(event: string) {\n * console.log('Tracking:', event);\n * }\n * });\n * // Now: sdk.track('page_view');\n * ```\n */\n\nexport class Expose {\n private sdk: any;\n\n /**\n * Create an Expose instance.\n *\n * @param sdk - SDK instance to expose methods on\n */\n constructor(sdk: any) {\n this.sdk = sdk;\n }\n\n /**\n * Expose methods on the SDK instance.\n *\n * Merges the provided methods object onto the SDK instance,\n * making them available as `sdk.methodName()`.\n *\n * @param api - Object containing methods to expose\n *\n * @example\n * ```typescript\n * expose.expose({\n * track(event: string, properties?: any) {\n * // Track event\n * },\n * identify(userId: string) {\n * // Identify user\n * }\n * });\n *\n * // Available as:\n * // sdk.track('page_view', { path: '/home' });\n * // sdk.identify('user-123');\n * ```\n */\n expose(api: Record<string, any>): void {\n Object.assign(this.sdk, api);\n }\n}\n","/**\n * Namespace capability for plugin identification.\n *\n * Each plugin must claim a unique namespace to identify itself.\n * Namespaces are used for config scoping and event namespacing.\n *\n * @example\n * ```typescript\n * const namespace = new Namespace();\n * namespace.ns('analytics.tracking');\n * console.log(namespace.name); // 'analytics.tracking'\n * ```\n */\n\nexport class Namespace {\n /**\n * The plugin's namespace.\n * Set via the `ns()` method, can only be set once.\n */\n name: string = '';\n\n /**\n * Set the plugin's namespace.\n *\n * Can only be called once per instance. Attempting to set it again throws an error.\n *\n * @param namespace - Dot-notation namespace (e.g., 'analytics.tracking')\n * @throws Error if namespace is already set\n *\n * @example\n * ```typescript\n * const ns = new Namespace();\n * ns.ns('my.plugin'); // Success\n * ns.ns('other.plugin'); // Throws Error\n * ```\n */\n ns(namespace: string): void {\n if (this.name) {\n throw new Error(\n `Namespace already set to \"${this.name}\". Cannot reassign to \"${namespace}\".`\n );\n }\n this.name = namespace;\n }\n}\n","/**\n * Main SDK class\n *\n * Composes all capabilities and provides the plugin registration system.\n * Follows functional plugin architecture with capability injection.\n */\n\nimport { Config } from './capabilities/config';\nimport { Emitter } from './capabilities/emitter';\nimport { Expose } from './capabilities/expose';\nimport { Namespace } from './capabilities/namespace';\nimport type { Plugin, PluginFunction, SDKConfig } from './types';\n\n/**\n * SDK - Main SDK class for building JavaScript SDKs\n *\n * @example\n * ```typescript\n * const sdk = new SDK({ name: 'my-sdk' });\n *\n * sdk.use((plugin, instance, config) => {\n * plugin.ns('my.plugin');\n * plugin.defaults({ my: { setting: 'value' } });\n * plugin.expose({\n * track(event: string) {\n * console.log('Tracking:', event);\n * }\n * });\n * });\n *\n * await sdk.init();\n * sdk.track('page_view');\n * ```\n */\nexport class SDK {\n /**\n * Configuration instance\n * @private\n */\n private configInstance: Config;\n\n /**\n * Event emitter instance\n * @private\n */\n private emitter: Emitter;\n\n /**\n * Registered plugins by namespace\n * @private\n */\n private plugins: Map<string, { namespace: Namespace; plugin: Plugin }> = new Map();\n\n /**\n * Initialization state\n * @private\n */\n private isInitialized = false;\n\n /**\n * Create a new SDK instance.\n *\n * @param config - Initial SDK configuration\n *\n * @example\n * ```typescript\n * const sdk = new SDK({\n * name: 'my-sdk',\n * api: {\n * endpoint: 'https://api.example.com',\n * timeout: 5000\n * }\n * });\n * ```\n */\n constructor(config: SDKConfig = {}) {\n this.emitter = new Emitter();\n this.configInstance = new Config(config);\n }\n\n /**\n * Register a plugin.\n *\n * Plugins receive capability-injected objects and can extend the SDK.\n * Supports method chaining.\n *\n * @param pluginFn - Plugin function\n * @returns this - For method chaining\n *\n * @example\n * ```typescript\n * sdk\n * .use(analyticsPlugin)\n * .use(trackingPlugin)\n * .init();\n * ```\n */\n use(pluginFn: PluginFunction): this {\n // Create capability instances for this plugin\n const namespace = new Namespace();\n const expose = new Expose(this);\n\n // Compose Plugin object with all capabilities\n const plugin: Plugin = {\n // Namespace capability\n ns: namespace.ns.bind(namespace),\n\n // Config capabilities\n defaults: this.configInstance.defaults.bind(this.configInstance),\n\n // Emitter capabilities\n on: this.emitter.on.bind(this.emitter),\n off: this.emitter.off.bind(this.emitter),\n emit: this.emitter.emit.bind(this.emitter),\n\n // Expose capability\n expose: expose.expose.bind(expose),\n\n // Requirer capability\n mustEnable: () => {\n if (namespace.name) {\n this.configInstance.markRequired(namespace.name);\n }\n },\n };\n\n // Execute plugin function with capability injection\n pluginFn(plugin, this, this.configInstance);\n\n // Store plugin by namespace\n if (namespace.name) {\n this.plugins.set(namespace.name, { namespace, plugin });\n }\n\n return this;\n }\n\n /**\n * Initialize the SDK.\n *\n * Emits `sdk:init` event before initialization and `sdk:ready` after.\n * Idempotent - can be called multiple times safely.\n *\n * @param config - Optional configuration to merge with existing config\n * @returns Promise that resolves when initialization is complete\n *\n * @example\n * ```typescript\n * sdk.on('sdk:ready', () => {\n * console.log('SDK is ready!');\n * });\n *\n * await sdk.init({ api: { timeout: 5000 } });\n * ```\n */\n async init(config?: SDKConfig): Promise<void> {\n if (this.isInitialized) {\n console.warn('SDK already initialized');\n return;\n }\n\n // Merge config if provided (new values override defaults)\n if (config) {\n this.configInstance.merge(config);\n }\n\n // Emit init event (plugins can listen for this)\n this.emitter.emit('sdk:init');\n\n // Check for required plugins\n const requiredNamespaces = Array.from(this.plugins.keys()).filter((ns) =>\n this.configInstance.isRequired(ns)\n );\n\n for (const ns of requiredNamespaces) {\n if (!this.plugins.has(ns)) {\n throw new Error(`Required plugin \"${ns}\" is not registered`);\n }\n }\n\n this.isInitialized = true;\n\n // Emit ready event\n this.emitter.emit('sdk:ready');\n }\n\n /**\n * Destroy the SDK.\n *\n * Emits `sdk:destroy` event, clears all plugins, and removes all event listeners.\n *\n * @returns Promise that resolves when destruction is complete\n *\n * @example\n * ```typescript\n * await sdk.destroy();\n * ```\n */\n async destroy(): Promise<void> {\n // Emit destroy event (plugins can listen for cleanup)\n this.emitter.emit('sdk:destroy');\n\n // Clear plugins\n this.plugins.clear();\n\n // Remove all event listeners\n this.emitter.removeAllListeners();\n\n this.isInitialized = false;\n }\n\n /**\n * Get a configuration value by path.\n *\n * Delegates to the Config capability.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @returns Configuration value\n *\n * @example\n * ```typescript\n * const timeout = sdk.get('api.timeout');\n * const nested = sdk.get('my.plugin.setting');\n * ```\n */\n get<T = any>(path: string): T {\n return this.configInstance.get(path);\n }\n\n /**\n * Set a configuration value by path.\n *\n * Delegates to the Config capability.\n *\n * @param path - Dot-notation path (e.g., 'api.timeout')\n * @param value - Value to set\n *\n * @example\n * ```typescript\n * sdk.set('api.timeout', 10000);\n * sdk.set('my.plugin.enabled', true);\n * ```\n */\n set(path: string, value: any): void {\n this.configInstance.set(path, value);\n }\n\n /**\n * Subscribe to an event.\n *\n * Delegates to the Emitter capability. Supports wildcard patterns.\n *\n * @param event - Event name or pattern (e.g., 'track:*')\n * @param handler - Event handler function\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = sdk.on('track:page', (data) => {\n * console.log('Page tracked:', data);\n * });\n *\n * // Later...\n * unsubscribe();\n * ```\n */\n on(event: string, handler: (...args: any[]) => void): () => void {\n return this.emitter.on(event, handler);\n }\n\n /**\n * Unsubscribe from an event.\n *\n * Delegates to the Emitter capability.\n *\n * @param event - Event name\n * @param handler - Event handler function to remove\n *\n * @example\n * ```typescript\n * const handler = () => console.log('ready');\n * sdk.on('sdk:ready', handler);\n * sdk.off('sdk:ready', handler);\n * ```\n */\n off(event: string, handler: (...args: any[]) => void): void {\n this.emitter.off(event, handler);\n }\n\n /**\n * Emit an event.\n *\n * Delegates to the Emitter capability.\n *\n * @param event - Event name\n * @param args - Event arguments\n *\n * @example\n * ```typescript\n * sdk.emit('custom:event', { data: 'value' });\n * ```\n */\n emit(event: string, ...args: any[]): void {\n this.emitter.emit(event, ...args);\n }\n\n /**\n * Get all configuration as an immutable copy.\n *\n * Delegates to the Config capability.\n *\n * @returns Copy of the entire configuration object\n *\n * @example\n * ```typescript\n * const allConfig = sdk.getAll();\n * console.log(allConfig);\n * ```\n */\n getAll(): Record<string, any> {\n return this.configInstance.getAll();\n }\n\n /**\n * Check if SDK is initialized.\n *\n * @returns true if initialized, false otherwise\n *\n * @example\n * ```typescript\n * if (sdk.isReady()) {\n * sdk.track('event');\n * }\n * ```\n */\n isReady(): boolean {\n return this.isInitialized;\n }\n}\n","import { SDK } from '@lytics/sdk-kit';\nimport { storagePlugin } from '@lytics/sdk-kit-plugins';\nimport {\n bannerPlugin,\n debugPlugin,\n exitIntentPlugin,\n frequencyPlugin,\n pageVisitsPlugin,\n scrollDepthPlugin,\n timeDelayPlugin,\n} from '@prosdevlab/experience-sdk-plugins';\nimport type {\n Context,\n Decision,\n Experience,\n ExperienceConfig,\n RuntimeState,\n TraceStep,\n UrlRule,\n} from './types';\n\n/**\n * Experience Runtime\n *\n * Core class that manages experience registration and evaluation.\n * Built on @lytics/sdk-kit for plugin system and lifecycle management.\n *\n * Design principles:\n * - Pure functions for evaluation logic (easy to test)\n * - Event-driven architecture (extensible via plugins)\n * - Explainability-first (every decision has reasons)\n */\nexport class ExperienceRuntime {\n private sdk: SDK;\n private experiences: Map<string, Experience> = new Map();\n private decisions: Decision[] = [];\n private initialized = false;\n private destroyed = false;\n private triggerContext: Context = { triggers: {} };\n\n constructor(config: ExperienceConfig = {}) {\n // Create SDK instance\n this.sdk = new SDK({\n name: 'experience-sdk',\n ...config,\n });\n\n // Auto-register plugins\n this.sdk.use(storagePlugin);\n this.sdk.use(debugPlugin);\n this.sdk.use(frequencyPlugin);\n this.sdk.use(exitIntentPlugin);\n this.sdk.use(scrollDepthPlugin);\n this.sdk.use(pageVisitsPlugin);\n this.sdk.use(timeDelayPlugin);\n this.sdk.use(bannerPlugin);\n\n // Listen for trigger events from display condition plugins\n this.setupTriggerListeners();\n }\n\n /**\n * Setup listeners for trigger:* events\n * This enables event-driven display conditions\n */\n private setupTriggerListeners(): void {\n // Listen for all trigger:* events using wildcard\n // When a trigger fires, update context and re-evaluate\n this.sdk.on('trigger:*', (eventName: string, data: any) => {\n const triggerName = eventName.replace('trigger:', '');\n\n // Update trigger context\n this.triggerContext.triggers = this.triggerContext.triggers || {};\n this.triggerContext.triggers[triggerName] = {\n triggered: true,\n timestamp: Date.now(),\n ...data, // Merge trigger-specific data\n };\n\n // Re-evaluate all experiences with updated context\n this.evaluate(this.triggerContext);\n });\n }\n\n /**\n * Initialize the runtime\n */\n async init(config?: ExperienceConfig): Promise<void> {\n if (this.initialized) {\n console.warn('[experiences] Already initialized');\n return;\n }\n\n // Recreate SDK if it was destroyed\n if (this.destroyed) {\n this.sdk = new SDK({\n name: 'experience-sdk',\n ...config,\n });\n\n // Re-register core plugins\n this.sdk.use(storagePlugin);\n this.sdk.use(debugPlugin);\n this.sdk.use(frequencyPlugin);\n this.sdk.use(exitIntentPlugin);\n this.sdk.use(scrollDepthPlugin);\n this.sdk.use(pageVisitsPlugin);\n this.sdk.use(timeDelayPlugin);\n this.sdk.use(bannerPlugin);\n\n this.destroyed = false;\n }\n\n if (config) {\n // Merge config if provided\n Object.entries(config).forEach(([key, value]) => {\n this.sdk.set(key, value);\n });\n }\n\n // Initialize SDK (will init all plugins)\n await this.sdk.init();\n\n this.initialized = true;\n\n // Emit ready event\n this.sdk.emit('experiences:ready');\n }\n\n /**\n * Register an experience\n */\n register(id: string, experience: Omit<Experience, 'id'>): void {\n const exp: Experience = { id, ...experience };\n this.experiences.set(id, exp);\n\n // Register frequency config with frequency plugin if it exists\n if (exp.frequency && (this.sdk as any).frequency?._registerExperience) {\n (this.sdk as any).frequency._registerExperience(id, exp.frequency.per);\n }\n\n this.sdk.emit('experiences:registered', { id, experience: exp });\n }\n\n /**\n * Evaluate experiences against context\n * Returns decision with explainability\n * First match wins (use evaluateAll() for multiple experiences)\n */\n evaluate(context?: Partial<Context>): Decision {\n const startTime = Date.now();\n const evalContext = buildContext(context);\n\n // Find matching experience\n let matchedExperience: Experience | undefined;\n const allReasons: string[] = [];\n const allTrace: TraceStep[] = [];\n\n for (const [, experience] of this.experiences) {\n const result = evaluateExperience(experience, evalContext);\n\n allReasons.push(...result.reasons);\n allTrace.push(...result.trace);\n\n if (result.matched) {\n // Check frequency cap if experience has frequency rules\n if (experience.frequency && (this.sdk as any).frequency) {\n const freqStart = Date.now();\n const hasReached = (this.sdk as any).frequency.hasReachedCap(\n experience.id,\n experience.frequency.max,\n experience.frequency.per\n );\n\n allTrace.push({\n step: 'check-frequency-cap',\n timestamp: freqStart,\n duration: Date.now() - freqStart,\n input: experience.frequency,\n output: hasReached,\n passed: !hasReached,\n });\n\n if (hasReached) {\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n allReasons.push(\n `Frequency cap reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n continue; // Skip this experience, check next\n }\n\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n allReasons.push(\n `Frequency cap not reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n }\n\n matchedExperience = experience;\n break; // First match wins\n }\n }\n\n const decision: Decision = {\n show: !!matchedExperience,\n experienceId: matchedExperience?.id,\n reasons: allReasons,\n trace: allTrace,\n context: evalContext,\n metadata: {\n evaluatedAt: Date.now(),\n totalDuration: Date.now() - startTime,\n experiencesEvaluated: this.experiences.size,\n },\n };\n\n // Store decision for inspection\n this.decisions.push(decision);\n\n // Emit for plugins to react (include matched experience for rendering)\n this.sdk.emit('experiences:evaluated', {\n decision,\n experience: matchedExperience,\n });\n\n return decision;\n }\n\n /**\n * Evaluate all experiences against context\n * Returns multiple decisions (sorted by priority)\n * All matching experiences will be shown\n */\n evaluateAll(context?: Partial<Context>): Decision[] {\n const evalContext = buildContext(context);\n\n // Sort experiences by priority (higher = more important)\n // Ties maintain registration order (Map preserves insertion order)\n const sortedExperiences = Array.from(this.experiences.values()).sort((a, b) => {\n const priorityA = a.priority ?? 0;\n const priorityB = b.priority ?? 0;\n return priorityB - priorityA; // Descending order\n });\n\n const decisions: Decision[] = [];\n\n // Evaluate each experience\n for (const experience of sortedExperiences) {\n const expStartTime = Date.now();\n const result = evaluateExperience(experience, evalContext);\n\n let show = result.matched;\n const reasons = [...result.reasons];\n const trace = [...result.trace];\n\n // Check frequency cap if experience has frequency rules\n if (show && experience.frequency && (this.sdk as any).frequency) {\n const freqStart = Date.now();\n const hasReached = (this.sdk as any).frequency.hasReachedCap(\n experience.id,\n experience.frequency.max,\n experience.frequency.per\n );\n\n trace.push({\n step: 'check-frequency-cap',\n timestamp: freqStart,\n duration: Date.now() - freqStart,\n input: experience.frequency,\n output: hasReached,\n passed: !hasReached,\n });\n\n if (hasReached) {\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n reasons.push(\n `Frequency cap reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n show = false;\n } else {\n const count = (this.sdk as any).frequency.getImpressionCount(\n experience.id,\n experience.frequency.per\n );\n reasons.push(\n `Frequency cap not reached (${count}/${experience.frequency.max} this ${experience.frequency.per})`\n );\n }\n }\n\n const decision: Decision = {\n show,\n experienceId: experience.id,\n reasons,\n trace,\n context: evalContext,\n metadata: {\n evaluatedAt: Date.now(),\n totalDuration: Date.now() - expStartTime,\n experiencesEvaluated: 1,\n },\n };\n\n decisions.push(decision);\n this.decisions.push(decision);\n }\n\n // Emit single event with all decisions (array)\n // Plugins can filter to their relevant experiences\n const matchedDecisions = decisions.filter((d) => d.show);\n const matchedExperiences = matchedDecisions\n .map((d) => d.experienceId && this.experiences.get(d.experienceId))\n .filter((exp): exp is Experience => exp !== undefined);\n\n this.sdk.emit(\n 'experiences:evaluated',\n matchedDecisions.map((decision, index) => ({\n decision,\n experience: matchedExperiences[index],\n }))\n );\n\n return decisions;\n }\n\n /**\n * Explain a specific experience\n */\n explain(experienceId: string): Decision | null {\n const experience = this.experiences.get(experienceId);\n if (!experience) {\n return null;\n }\n\n const context = buildContext();\n const result = evaluateExperience(experience, context);\n\n return {\n show: result.matched,\n experienceId,\n reasons: result.reasons,\n trace: result.trace,\n context,\n metadata: {\n evaluatedAt: Date.now(),\n totalDuration: 0,\n experiencesEvaluated: 1,\n },\n };\n }\n\n /**\n * Get runtime state (for inspection)\n */\n getState(): RuntimeState {\n return {\n initialized: this.initialized,\n experiences: new Map(this.experiences),\n decisions: [...this.decisions],\n config: this.sdk ? this.sdk.getAll() : {},\n };\n }\n\n /**\n * Event subscription (proxy to SDK)\n */\n on(event: string, handler: (...args: any[]) => void): () => void {\n return this.sdk.on(event, handler);\n }\n\n /**\n * Destroy runtime\n */\n async destroy(): Promise<void> {\n if (this.sdk) {\n await this.sdk.destroy();\n }\n this.destroyed = true;\n this.experiences.clear();\n this.decisions = [];\n this.initialized = false;\n }\n}\n\n// Pure functions for evaluation logic (easy to test, no dependencies)\n\n/**\n * Build evaluation context from partial input\n * Pure function - no side effects\n */\nexport function buildContext(partial?: Partial<Context>): Context {\n return {\n url: partial?.url ?? (typeof window !== 'undefined' ? window.location.href : ''),\n timestamp: Date.now(),\n user: partial?.user,\n custom: partial?.custom,\n triggers: partial?.triggers ?? {}, // Include triggers\n };\n}\n\n/**\n * Evaluate an experience against context\n * Pure function - returns reasons and trace\n */\nexport function evaluateExperience(\n experience: Experience,\n context: Context\n): { matched: boolean; reasons: string[]; trace: TraceStep[] } {\n const reasons: string[] = [];\n const trace: TraceStep[] = [];\n let matched = true;\n\n // Evaluate URL rule\n if (experience.targeting.url) {\n const urlStart = Date.now();\n const urlMatch = evaluateUrlRule(experience.targeting.url, context.url);\n\n trace.push({\n step: 'evaluate-url-rule',\n timestamp: urlStart,\n duration: Date.now() - urlStart,\n input: { rule: experience.targeting.url, url: context.url },\n output: urlMatch,\n passed: urlMatch,\n });\n\n if (urlMatch) {\n reasons.push('URL matches targeting rule');\n } else {\n reasons.push('URL does not match targeting rule');\n matched = false;\n }\n }\n\n return { matched, reasons, trace };\n}\n\n/**\n * Evaluate URL targeting rule\n * Pure function - deterministic output\n */\nexport function evaluateUrlRule(rule: UrlRule, url: string = ''): boolean {\n // Check equals (exact match)\n if (rule.equals !== undefined) {\n return url === rule.equals;\n }\n\n // Check contains (substring match)\n if (rule.contains !== undefined) {\n return url.includes(rule.contains);\n }\n\n // Check matches (regex match)\n if (rule.matches !== undefined) {\n return rule.matches.test(url);\n }\n\n // No rules specified = match all\n return true;\n}\n","/**\n * Singleton Pattern Implementation\n *\n * Provides a default singleton instance with convenient wrapper functions\n * for simple use cases, plus createInstance() for advanced scenarios.\n */\n\nimport { ExperienceRuntime } from './runtime';\nimport type { Context, Decision, Experience, ExperienceConfig, RuntimeState } from './types';\n\n/**\n * Create a new Experience SDK instance\n *\n * Use this for advanced scenarios where you need multiple isolated runtimes.\n *\n * @example\n * ```typescript\n * import { createInstance } from '@prosdevlab/experience-sdk';\n *\n * const exp = createInstance({ debug: true });\n * await exp.init();\n * exp.register('welcome', { ... });\n * ```\n */\nexport function createInstance(config?: ExperienceConfig): ExperienceRuntime {\n return new ExperienceRuntime(config);\n}\n\n/**\n * Default singleton instance\n *\n * Provides a convenient global instance for simple use cases.\n * For script tag users, this is exposed as the global `experiences` object.\n */\nconst defaultInstance = createInstance();\n\n/**\n * Initialize the Experience SDK\n *\n * @example\n * ```typescript\n * import { init } from '@prosdevlab/experience-sdk';\n * await init({ debug: true });\n * ```\n */\nexport async function init(config?: ExperienceConfig): Promise<void> {\n return defaultInstance.init(config);\n}\n\n/**\n * Register an experience\n *\n * @example\n * ```typescript\n * import { register } from '@prosdevlab/experience-sdk';\n *\n * register('welcome-banner', {\n * type: 'banner',\n * targeting: { url: { contains: '/' } },\n * content: { title: 'Welcome!', message: 'Thanks for visiting' }\n * });\n * ```\n */\nexport function register(id: string, experience: Omit<Experience, 'id'>): void {\n defaultInstance.register(id, experience);\n}\n\n/**\n * Evaluate experiences against current context\n * First match wins (use evaluateAll() for multiple experiences)\n *\n * @example\n * ```typescript\n * import { evaluate } from '@prosdevlab/experience-sdk';\n *\n * const decision = evaluate({ url: window.location.href });\n * if (decision.show) {\n * console.log('Show experience:', decision.experienceId);\n * console.log('Reasons:', decision.reasons);\n * }\n * ```\n */\nexport function evaluate(context?: Partial<Context>): Decision {\n return defaultInstance.evaluate(context);\n}\n\n/**\n * Evaluate all experiences against current context\n * Returns array of decisions sorted by priority (higher = more important)\n * All matching experiences will be shown\n *\n * @example\n * ```typescript\n * import { evaluateAll } from '@prosdevlab/experience-sdk';\n *\n * const decisions = evaluateAll({ url: window.location.href });\n * decisions.forEach(decision => {\n * if (decision.show) {\n * console.log('Show:', decision.experienceId);\n * console.log('Reasons:', decision.reasons);\n * }\n * });\n * ```\n */\nexport function evaluateAll(context?: Partial<Context>): Decision[] {\n return defaultInstance.evaluateAll(context);\n}\n\n/**\n * Explain why a specific experience would/wouldn't show\n *\n * @example\n * ```typescript\n * import { explain } from '@prosdevlab/experience-sdk';\n *\n * const explanation = explain('welcome-banner');\n * console.log('Would show?', explanation?.show);\n * console.log('Reasons:', explanation?.reasons);\n * ```\n */\nexport function explain(experienceId: string): Decision | null {\n return defaultInstance.explain(experienceId);\n}\n\n/**\n * Get current runtime state\n *\n * @example\n * ```typescript\n * import { getState } from '@prosdevlab/experience-sdk';\n *\n * const state = getState();\n * console.log('Initialized?', state.initialized);\n * console.log('Experiences:', Array.from(state.experiences.keys()));\n * ```\n */\nexport function getState(): RuntimeState {\n return defaultInstance.getState();\n}\n\n/**\n * Subscribe to SDK events\n *\n * @example\n * ```typescript\n * import { on } from '@prosdevlab/experience-sdk';\n *\n * const unsubscribe = on('experiences:evaluated', (decision) => {\n * console.log('Evaluation:', decision);\n * });\n *\n * // Later: unsubscribe()\n * ```\n */\nexport function on(event: string, handler: (...args: unknown[]) => void): () => void {\n return defaultInstance.on(event, handler);\n}\n\n/**\n * Destroy the SDK instance\n *\n * @example\n * ```typescript\n * import { destroy } from '@prosdevlab/experience-sdk';\n * await destroy();\n * ```\n */\nexport async function destroy(): Promise<void> {\n return defaultInstance.destroy();\n}\n\n/**\n * Default export for convenient importing\n *\n * @example\n * ```typescript\n * import experiences from '@prosdevlab/experience-sdk';\n * await experiences.init();\n * ```\n */\nexport const experiences = {\n createInstance,\n init,\n register,\n evaluate,\n evaluateAll,\n explain,\n getState,\n on,\n destroy,\n};\n\n/**\n * Global singleton instance for IIFE builds\n *\n * When loaded via script tag, this object is available as `window.experiences`\n */\nif (typeof window !== 'undefined') {\n (window as unknown as Record<string, unknown>).experiences = experiences;\n}\n"]}
|