@prosdevlab/experience-sdk-plugins 0.1.3 → 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.
- package/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +56 -0
- package/dist/index.d.ts +626 -2
- package/dist/index.js +799 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/banner/banner.ts +149 -51
- package/src/exit-intent/exit-intent.test.ts +423 -0
- package/src/exit-intent/exit-intent.ts +372 -0
- package/src/exit-intent/index.ts +6 -0
- package/src/exit-intent/types.ts +59 -0
- package/src/index.ts +5 -0
- package/src/integration.test.ts +362 -0
- package/src/page-visits/index.ts +6 -0
- package/src/page-visits/page-visits.test.ts +562 -0
- package/src/page-visits/page-visits.ts +314 -0
- package/src/page-visits/types.ts +119 -0
- package/src/scroll-depth/index.ts +6 -0
- package/src/scroll-depth/scroll-depth.test.ts +545 -0
- package/src/scroll-depth/scroll-depth.ts +400 -0
- package/src/scroll-depth/types.ts +122 -0
- package/src/time-delay/index.ts +6 -0
- package/src/time-delay/time-delay.test.ts +477 -0
- package/src/time-delay/time-delay.ts +297 -0
- package/src/time-delay/types.ts +89 -0
- package/src/utils/sanitize.ts +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils/sanitize.ts","../src/banner/banner.ts","../src/debug/debug.ts","../src/frequency/frequency.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/utils/sanitize.ts","../src/banner/banner.ts","../src/debug/debug.ts","../src/exit-intent/exit-intent.ts","../src/frequency/frequency.ts","../src/page-visits/page-visits.ts","../src/scroll-depth/scroll-depth.ts","../src/time-delay/time-delay.ts"],"names":["storagePlugin"],"mappings":";;;AAaA,IAAM,YAAA,GAAe,CAAC,QAAA,EAAU,IAAA,EAAM,KAAK,IAAA,EAAM,MAAA,EAAQ,GAAA,EAAK,GAAA,EAAK,GAAG,CAAA;AAKtE,IAAM,kBAAA,GAA+C;AAAA,EACnD,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,SAAS,OAAO,CAAA;AAAA,EACrC,IAAA,EAAM,CAAC,OAAA,EAAS,OAAO,CAAA;AAAA,EACvB,CAAA,EAAG,CAAC,OAAA,EAAS,OAAO;AAAA;AAEtB,CAAA;AAcO,SAAS,aAAa,IAAA,EAAsB;AACjD,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AACrC,IAAA,OAAO,EAAA;AAAA,EACT;AAGA,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACzC,EAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAKjB,EAAA,SAAS,aAAa,IAAA,EAAoB;AAExC,IAAA,IAAI,IAAA,CAAK,QAAA,KAAa,IAAA,CAAK,SAAA,EAAW;AACpC,MAAA,OAAO,UAAA,CAAW,IAAA,CAAK,WAAA,IAAe,EAAE,CAAA;AAAA,IAC1C;AAGA,IAAA,IAAI,IAAA,CAAK,QAAA,KAAa,IAAA,CAAK,YAAA,EAAc;AACvC,MAAA,MAAM,OAAA,GAAU,IAAA;AAChB,MAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,WAAA,EAAY;AAK5C,MAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AACrC,QAAA,OAAO,EAAA;AAAA,MACT;AAGA,MAAA,IAAI,CAAC,YAAA,CAAa,QAAA,CAAS,OAAc,CAAA,EAAG;AAC1C,QAAA,OAAO,EAAA;AAAA,MACT;AAGA,MAAA,MAAM,YAAA,GAAe,kBAAA,CAAmB,OAAO,CAAA,IAAK,EAAC;AAGrD,MAAA,MAAM,QAAkB,EAAC;AACzB,MAAA,KAAA,MAAW,QAAQ,YAAA,EAAc;AAC/B,QAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,YAAA,CAAa,IAAI,CAAA;AACvC,QAAA,IAAI,UAAU,IAAA,EAAM;AAElB,UAAA,IAAI,SAAS,MAAA,EAAQ;AAEnB,YAAA,MAAM,aAAA,GAAgB,YAAY,KAAK,CAAA;AACvC,YAAA,IAAI,aAAA,EAAe;AACjB,cAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,eAAA,CAAgB,aAAa,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,YACvD;AAAA,UACF,CAAA,MAAO;AAEL,YAAA,KAAA,CAAM,KAAK,CAAA,EAAG,IAAI,KAAK,eAAA,CAAgB,KAAK,CAAC,CAAA,CAAA,CAAG,CAAA;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAEA,MAAA,MAAM,UAAA,GAAa,MAAM,MAAA,GAAS,CAAA,GAAI,IAAI,KAAA,CAAM,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,GAAK,EAAA;AAG9D,MAAA,IAAI,SAAA,GAAY,EAAA;AAChB,MAAA,KAAA,MAAW,KAAA,IAAS,KAAA,CAAM,IAAA,CAAK,OAAA,CAAQ,UAAU,CAAA,EAAG;AAClD,QAAA,SAAA,IAAa,aAAa,KAAK,CAAA;AAAA,MACjC;AAGA,MAAA,IAAI,YAAY,IAAA,EAAM;AACpB,QAAA,OAAO,MAAM,UAAU,CAAA,GAAA,CAAA;AAAA,MACzB;AAEA,MAAA,OAAO,IAAI,OAAO,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,SAAS,KAAK,OAAO,CAAA,CAAA,CAAA;AAAA,IAC1D;AAEA,IAAA,OAAO,EAAA;AAAA,EACT;AAGA,EAAA,IAAI,SAAA,GAAY,EAAA;AAChB,EAAA,KAAA,MAAW,KAAA,IAAS,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,UAAU,CAAA,EAAG;AAC/C,IAAA,SAAA,IAAa,aAAa,KAAK,CAAA;AAAA,EACjC;AAEA,EAAA,OAAO,SAAA;AACT;AAKA,SAAS,WAAW,IAAA,EAAsB;AACxC,EAAA,MAAM,GAAA,GAAM,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AACxC,EAAA,GAAA,CAAI,WAAA,GAAc,IAAA;AAClB,EAAA,OAAO,GAAA,CAAI,SAAA;AACb;AAKA,SAAS,gBAAgB,KAAA,EAAuB;AAC9C,EAAA,OAAO,MACJ,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAA,CACrB,OAAA,CAAQ,MAAM,MAAM,CAAA,CACpB,QAAQ,IAAA,EAAM,MAAM,EACpB,OAAA,CAAQ,IAAA,EAAM,QAAQ,CAAA,CACtB,OAAA,CAAQ,MAAM,OAAO,CAAA;AAC1B;AAQA,SAAS,YAAY,GAAA,EAAqB;AACxC,EAAA,IAAI,CAAC,GAAA,IAAO,OAAO,GAAA,KAAQ,QAAA,EAAU;AACnC,IAAA,OAAO,EAAA;AAAA,EACT;AAGA,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI;AACF,IAAA,OAAA,GAAU,mBAAmB,GAAG,CAAA;AAAA,EAClC,CAAA,CAAA,MAAQ;AAEN,IAAA,OAAA,GAAU,GAAA;AAAA,EACZ;AAEA,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,IAAA,EAAK,CAAE,WAAA,EAAY;AAG3C,EAAA,IACE,OAAA,CAAQ,WAAW,aAAa,CAAA,IAChC,QAAQ,UAAA,CAAW,OAAO,CAAA,IAC1B,GAAA,CAAI,WAAA,EAAY,CAAE,MAAK,CAAE,UAAA,CAAW,aAAa,CAAA,IACjD,GAAA,CAAI,WAAA,GAAc,IAAA,EAAK,CAAE,UAAA,CAAW,OAAO,CAAA,EAC3C;AACA,IAAA,OAAO,EAAA;AAAA,EACT;AAGA,EAAA,IACE,OAAA,CAAQ,UAAA,CAAW,SAAS,CAAA,IAC5B,OAAA,CAAQ,UAAA,CAAW,UAAU,CAAA,IAC7B,OAAA,CAAQ,UAAA,CAAW,SAAS,CAAA,IAC5B,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,IACzB,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IACtB,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,IACtB,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA,EACtB;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AAGA,EAAA,IAAI,CAAC,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG;AAC1B,IAAA,OAAO,GAAA;AAAA,EACT;AAGA,EAAA,OAAO,EAAA;AACT;;;AC3IO,IAAM,YAAA,GAA+B,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AACxE,EAAA,MAAA,CAAO,GAAG,QAAQ,CAAA;AAGlB,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,MAAA,EAAQ;AAAA,MACN,QAAA,EAAU,KAAA;AAAA,MACV,WAAA,EAAa,IAAA;AAAA,MACb,MAAA,EAAQ;AAAA;AACV,GACD,CAAA;AAGD,EAAA,MAAM,aAAA,uBAAoB,GAAA,EAAyB;AAKnD,EAAA,SAAS,mBAAA,GAA4B;AACnC,IAAA,MAAM,OAAA,GAAU,kBAAA;AAChB,IAAA,IAAI,QAAA,CAAS,cAAA,CAAe,OAAO,CAAA,EAAG;AACpC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,IAAA,KAAA,CAAM,EAAA,GAAK,OAAA;AACX,IAAA,KAAA,CAAM,WAAA,GAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAA,CAAA;AAwNpB,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,KAAK,CAAA;AAAA,EACjC;AAKA,EAAA,SAAS,oBAAoB,UAAA,EAAqC;AAChE,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAE3B,IAAA,MAAM,WAAW,OAAA,CAAQ,QAAA,IAAY,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA,IAAK,KAAA;AACtE,IAAA,MAAM,cAAc,OAAA,CAAQ,WAAA,IAAe,MAAA,CAAO,GAAA,CAAI,oBAAoB,CAAA,IAAK,IAAA;AAC/E,IAAA,MAAM,MAAA,GAAS,MAAA,CAAO,GAAA,CAAI,eAAe,CAAA,IAAK,GAAA;AAG9C,IAAA,mBAAA,EAAoB;AAGpB,IAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC3C,IAAA,MAAA,CAAO,YAAA,CAAa,oBAAA,EAAsB,UAAA,CAAW,EAAE,CAAA;AAGvD,IAAA,MAAM,WAAA,GAAc,CAAC,WAAA,EAAa,CAAA,WAAA,EAAc,QAAQ,CAAA,CAAE,CAAA;AAC1D,IAAA,IAAI,QAAQ,SAAA,EAAW;AACrB,MAAA,WAAA,CAAY,IAAA,CAAK,QAAQ,SAAS,CAAA;AAAA,IACpC;AACA,IAAA,MAAA,CAAO,SAAA,GAAY,WAAA,CAAY,IAAA,CAAK,GAAG,CAAA;AAGvC,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,OAAA,CAAQ,KAAK,CAAA;AAAA,IAC3C;AAGA,IAAA,IAAI,WAAW,GAAA,EAAO;AACpB,MAAA,MAAA,CAAO,KAAA,CAAM,MAAA,GAAS,MAAA,CAAO,MAAM,CAAA;AAAA,IACrC;AAGA,IAAA,MAAM,SAAA,GAAY,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC9C,IAAA,SAAA,CAAU,SAAA,GAAY,sBAAA;AACtB,IAAA,MAAA,CAAO,YAAY,SAAS,CAAA;AAG5B,IAAA,MAAM,UAAA,GAAa,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC/C,IAAA,UAAA,CAAW,SAAA,GAAY,oBAAA;AAGvB,IAAA,IAAI,QAAQ,KAAA,EAAO;AACjB,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,IAAI,CAAA;AACzC,MAAA,KAAA,CAAM,SAAA,GAAY,kBAAA;AAElB,MAAA,KAAA,CAAM,SAAA,GAAY,YAAA,CAAa,OAAA,CAAQ,KAAK,CAAA;AAC5C,MAAA,UAAA,CAAW,YAAY,KAAK,CAAA;AAAA,IAC9B;AAGA,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,aAAA,CAAc,GAAG,CAAA;AAC1C,IAAA,OAAA,CAAQ,SAAA,GAAY,oBAAA;AAEpB,IAAA,OAAA,CAAQ,SAAA,GAAY,YAAA,CAAa,OAAA,CAAQ,OAAO,CAAA;AAChD,IAAA,UAAA,CAAW,YAAY,OAAO,CAAA;AAE9B,IAAA,SAAA,CAAU,YAAY,UAAU,CAAA;AAGhC,IAAA,MAAM,UAAA,GAAa,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC/C,IAAA,UAAA,CAAW,SAAA,GAAY,oBAAA;AAGvB,IAAA,SAAS,aAAa,YAAA,EAQA;AACpB,MAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,MAAA,MAAA,CAAO,cAAc,YAAA,CAAa,IAAA;AAElC,MAAA,MAAM,OAAA,GAAU,aAAa,OAAA,IAAW,SAAA;AAGxC,MAAA,MAAM,aAAA,GAAgB,CAAC,mBAAA,EAAqB,CAAA,mBAAA,EAAsB,OAAO,CAAA,CAAE,CAAA;AAC3E,MAAA,IAAI,aAAa,SAAA,EAAW;AAC1B,QAAA,aAAA,CAAc,IAAA,CAAK,aAAa,SAAS,CAAA;AAAA,MAC3C;AACA,MAAA,MAAA,CAAO,SAAA,GAAY,aAAA,CAAc,IAAA,CAAK,GAAG,CAAA;AAGzC,MAAA,IAAI,aAAa,KAAA,EAAO;AACtB,QAAA,MAAA,CAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,YAAA,CAAa,KAAK,CAAA;AAAA,MAChD;AAEA,MAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AAErC,QAAA,QAAA,CAAS,KAAK,oBAAA,EAAsB;AAAA,UAClC,cAAc,UAAA,CAAW,EAAA;AAAA,UACzB,IAAA,EAAM,QAAA;AAAA,UACN,QAAQ,YAAA,CAAa,MAAA;AAAA,UACrB,KAAK,YAAA,CAAa,GAAA;AAAA,UAClB,UAAU,YAAA,CAAa,QAAA;AAAA,UACvB,OAAA;AAAA,UACA,SAAA,EAAW,KAAK,GAAA;AAAI,SACrB,CAAA;AAGD,QAAA,IAAI,aAAa,GAAA,EAAK;AACpB,UAAA,MAAA,CAAO,QAAA,CAAS,OAAO,YAAA,CAAa,GAAA;AAAA,QACtC;AAAA,MACF,CAAC,CAAA;AAED,MAAA,OAAO,MAAA;AAAA,IACT;AAGA,IAAA,IAAI,OAAA,CAAQ,OAAA,IAAW,OAAA,CAAQ,OAAA,CAAQ,SAAS,CAAA,EAAG;AACjD,MAAA,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,CAAC,YAAA,KAAiB;AACxC,QAAA,MAAM,MAAA,GAAS,aAAa,YAAY,CAAA;AACxC,QAAA,UAAA,CAAW,YAAY,MAAM,CAAA;AAAA,MAC/B,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,MAAM,WAAA,GAAc,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AACnD,MAAA,WAAA,CAAY,SAAA,GAAY,kBAAA;AACxB,MAAA,WAAA,CAAY,SAAA,GAAY,SAAA;AACxB,MAAA,WAAA,CAAY,YAAA,CAAa,cAAc,cAAc,CAAA;AAErD,MAAA,WAAA,CAAY,gBAAA,CAAiB,SAAS,MAAM;AAC1C,QAAA,MAAA,CAAO,WAAW,EAAE,CAAA;AACpB,QAAA,QAAA,CAAS,KAAK,uBAAA,EAAyB;AAAA,UACrC,cAAc,UAAA,CAAW,EAAA;AAAA,UACzB,IAAA,EAAM;AAAA,SACP,CAAA;AAAA,MACH,CAAC,CAAA;AAED,MAAA,UAAA,CAAW,YAAY,WAAW,CAAA;AAAA,IACpC;AAEA,IAAA,SAAA,CAAU,YAAY,UAAU,CAAA;AAEhC,IAAA,OAAO,MAAA;AAAA,EACT;AAKA,EAAA,SAAS,aAAA,CAAc,QAAqB,QAAA,EAAkC;AAC5E,IAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA;AAErD,IAAA,IAAI,CAAC,gBAAA,IAAoB,QAAA,KAAa,KAAA,EAAO;AAC3C,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,aAAA,CAAc,gBAAgB,CAAA;AAC7D,IAAA,IAAI,CAAC,aAAA,IAAiB,EAAE,aAAA,YAAyB,WAAA,CAAA,EAAc;AAC7D,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,SAAS,MAAA,CAAO,YAAA;AAGtB,IAAA,aAAA,CAAc,MAAM,UAAA,GAAa,sBAAA;AACjC,IAAA,aAAA,CAAc,KAAA,CAAM,SAAA,GAAY,CAAA,EAAG,MAAM,CAAA,EAAA,CAAA;AAAA,EAC3C;AAKA,EAAA,SAAS,cAAA,GAAuB;AAC9B,IAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA;AAErD,IAAA,IAAI,CAAC,gBAAA,EAAkB;AACrB,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,aAAA,CAAc,gBAAgB,CAAA;AAC7D,IAAA,IAAI,CAAC,aAAA,IAAiB,EAAE,aAAA,YAAyB,WAAA,CAAA,EAAc;AAC7D,MAAA;AAAA,IACF;AAGA,IAAA,aAAA,CAAc,MAAM,UAAA,GAAa,sBAAA;AACjC,IAAA,aAAA,CAAc,MAAM,SAAA,GAAY,GAAA;AAAA,EAClC;AAKA,EAAA,SAAS,KAAK,UAAA,EAA8B;AAE1C,IAAA,IAAI,aAAA,CAAc,GAAA,CAAI,UAAA,CAAW,EAAE,CAAA,EAAG;AACpC,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACnC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GAAS,oBAAoB,UAAU,CAAA;AAC7C,IAAA,QAAA,CAAS,IAAA,CAAK,YAAY,MAAM,CAAA;AAChC,IAAA,aAAA,CAAc,GAAA,CAAI,UAAA,CAAW,EAAA,EAAI,MAAM,CAAA;AAGvC,IAAA,MAAM,UAAU,UAAA,CAAW,OAAA;AAC3B,IAAA,MAAM,WAAW,OAAA,CAAQ,QAAA,IAAY,MAAA,CAAO,GAAA,CAAI,iBAAiB,CAAA,IAAK,KAAA;AACtE,IAAA,aAAA,CAAc,QAAQ,QAAQ,CAAA;AAE9B,IAAA,QAAA,CAAS,KAAK,mBAAA,EAAqB;AAAA,MACjC,cAAc,UAAA,CAAW,EAAA;AAAA,MACzB,IAAA,EAAM,QAAA;AAAA,MACN,SAAA,EAAW,KAAK,GAAA;AAAI,KACrB,CAAA;AAAA,EACH;AAKA,EAAA,SAAS,OAAO,YAAA,EAA6B;AAC3C,IAAA,IAAI,YAAA,EAAc;AAEhB,MAAA,MAAM,MAAA,GAAS,aAAA,CAAc,GAAA,CAAI,YAAY,CAAA;AAC7C,MAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,QAAA,MAAA,CAAO,UAAA,CAAW,YAAY,MAAM,CAAA;AAAA,MACtC;AACA,MAAA,aAAA,CAAc,OAAO,YAAY,CAAA;AAGjC,MAAA,IAAI,aAAA,CAAc,SAAS,CAAA,EAAG;AAC5B,QAAA,cAAA,EAAe;AAAA,MACjB;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,KAAA,MAAW,CAAC,EAAA,EAAI,MAAM,CAAA,IAAK,aAAA,CAAc,SAAQ,EAAG;AAClD,QAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,UAAA,MAAA,CAAO,UAAA,CAAW,YAAY,MAAM,CAAA;AAAA,QACtC;AACA,QAAA,aAAA,CAAc,OAAO,EAAE,CAAA;AAAA,MACzB;AAGA,MAAA,cAAA,EAAe;AAAA,IACjB;AAAA,EACF;AAKA,EAAA,SAAS,SAAA,GAAqB;AAC5B,IAAA,OAAO,cAAc,IAAA,GAAO,CAAA;AAAA,EAC9B;AAGA,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,MAAA,EAAQ;AAAA,MACN,IAAA;AAAA,MACA,MAAA;AAAA,MACA;AAAA;AACF,GACD,CAAA;AAGD,EAAA,QAAA,CAAS,EAAA,CAAG,uBAAA,EAAyB,CAAC,OAAA,KAAqB;AAIzD,IAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,OAAA,GAAU,CAAC,OAAO,CAAA;AAEzD,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AAExB,MAAA,MAAM,SAAA,GAAY,IAAA;AAClB,MAAA,MAAM,WAAW,SAAA,CAAU,QAAA;AAC3B,MAAA,MAAM,aAAa,SAAA,CAAU,UAAA;AAG7B,MAAA,IAAI,UAAA,EAAY,SAAS,QAAA,EAAU;AACjC,QAAA,IAAI,UAAU,IAAA,EAAM;AAClB,UAAA,IAAA,CAAK,UAAU,CAAA;AAAA,QACjB,WAAW,UAAA,CAAW,EAAA,IAAM,cAAc,GAAA,CAAI,UAAA,CAAW,EAAE,CAAA,EAAG;AAE5D,UAAA,MAAA,CAAO,WAAW,EAAE,CAAA;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC,CAAA;AAGD,EAAA,QAAA,CAAS,EAAA,CAAG,eAAe,MAAM;AAC/B,IAAA,MAAA,EAAO;AAAA,EACT,CAAC,CAAA;AACH;;;AC5iBO,IAAM,WAAA,GAA8B,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AACvE,EAAA,MAAA,CAAO,GAAG,OAAO,CAAA;AAGjB,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,KAAA,EAAO;AAAA,MACL,OAAA,EAAS,KAAA;AAAA,MACT,OAAA,EAAS,KAAA;AAAA,MACT,MAAA,EAAQ;AAAA;AACV,GACD,CAAA;AAGD,EAAA,MAAM,SAAA,GAAY,MAAe,MAAA,CAAO,GAAA,CAAI,eAAe,CAAA,IAAK,KAAA;AAChE,EAAA,MAAM,gBAAA,GAAmB,MAAe,MAAA,CAAO,GAAA,CAAI,eAAe,CAAA,IAAK,KAAA;AACvE,EAAA,MAAM,gBAAA,GAAmB,MAAe,MAAA,CAAO,GAAA,CAAI,cAAc,CAAA,IAAK,IAAA;AAGtE,EAAA,MAAM,GAAA,GAAM,CAAC,OAAA,EAAiB,IAAA,KAAyB;AACrD,IAAA,IAAI,CAAC,WAAU,EAAG;AAElB,IAAA,MAAM,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AACzC,IAAA,MAAM,OAAA,GAAU;AAAA,MACd,SAAA;AAAA,MACA,OAAA;AAAA,MACA;AAAA,KACF;AAGA,IAAA,IAAI,kBAAiB,EAAG;AACtB,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,cAAA,EAAiB,OAAO,CAAA,CAAA,EAAI,QAAQ,EAAE,CAAA;AAAA,IACpD;AAGA,IAAA,IAAI,gBAAA,EAAiB,IAAK,OAAO,MAAA,KAAW,WAAA,EAAa;AACvD,MAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,CAAY,sBAAA,EAAwB;AAAA,QACpD,MAAA,EAAQ;AAAA,OACT,CAAA;AACD,MAAA,MAAA,CAAO,cAAc,KAAK,CAAA;AAAA,IAC5B;AAAA,EACF,CAAA;AAGA,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,KAAA,EAAO;AAAA,MACL,GAAA;AAAA,MACA;AAAA;AACF,GACD,CAAA;AAGD,EAAA,IAAI,WAAU,EAAG;AAEf,IAAA,QAAA,CAAS,EAAA,CAAG,qBAAqB,MAAM;AACrC,MAAA,IAAI,CAAC,WAAU,EAAG;AAClB,MAAA,GAAA,CAAI,2BAA2B,CAAA;AAAA,IACjC,CAAC,CAAA;AAED,IAAA,QAAA,CAAS,EAAA,CAAG,wBAAA,EAA0B,CAAC,OAAA,KAAY;AACjD,MAAA,IAAI,CAAC,WAAU,EAAG;AAClB,MAAA,GAAA,CAAI,yBAAyB,OAAO,CAAA;AAAA,IACtC,CAAC,CAAA;AAED,IAAA,QAAA,CAAS,EAAA,CAAG,uBAAA,EAAyB,CAAC,OAAA,KAAY;AAChD,MAAA,IAAI,CAAC,WAAU,EAAG;AAClB,MAAA,GAAA,CAAI,wBAAwB,OAAO,CAAA;AAAA,IACrC,CAAC,CAAA;AAAA,EACH;AACF;;;ACzFO,SAAS,eAAe,SAAA,EAA4B;AACzD,EAAA,OAAO,gEAAA,CAAiE,KAAK,SAAS,CAAA;AACxF;AAKO,SAAS,iBAAA,CACd,YAAA,EACA,OAAA,EACA,WAAA,EACS;AACT,EAAA,OAAO,cAAc,YAAA,IAAgB,OAAA;AACvC;AAKO,SAAS,oBAAA,CACd,SAAA,EACA,WAAA,EACA,OAAA,EACY;AACZ,EAAA,MAAM,OAAA,GAAU,CAAC,GAAG,SAAA,EAAW,WAAW,CAAA;AAC1C,EAAA,IAAI,OAAA,CAAQ,SAAS,OAAA,EAAS;AAC5B,IAAA,OAAO,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,EACxB;AACA,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,iBAAA,CAAkB,OAAe,SAAA,EAA2B;AAC1E,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,KAAA,GAAQ,SAAS,CAAA;AACnC;AAKO,SAAS,uBAAA,CACd,SAAA,EACA,WAAA,EACA,aAAA,EACgF;AAEhF,EAAA,IAAI,SAAA,CAAU,SAAS,CAAA,EAAG;AACxB,IAAA,OAAO,EAAE,eAAe,KAAA,EAAO,KAAA,EAAO,GAAG,SAAA,EAAW,CAAA,EAAG,UAAU,CAAA,EAAE;AAAA,EACrE;AAIA,EAAA,IAAI,aAAA,IAAkB,aAAA,CAAsB,QAAA,KAAa,MAAA,EAAQ;AAC/D,IAAA,OAAO,EAAE,eAAe,KAAA,EAAO,KAAA,EAAO,GAAG,SAAA,EAAW,CAAA,EAAG,UAAU,CAAA,EAAE;AAAA,EACrE;AAGA,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA,CAAE,CAAA;AAC9C,EAAA,MAAM,SAAA,GAAY,SAAA,CAAU,SAAA,CAAU,MAAA,GAAS,CAAC,CAAA,CAAE,CAAA;AAGlD,EAAA,MAAM,QAAA,GAAW,iBAAA,CAAkB,KAAA,EAAO,SAAS,CAAA;AAGnD,EAAA,MAAM,aAAa,KAAA,GAAQ,SAAA;AAC3B,EAAA,MAAM,SAAA,GAAY,QAAQ,QAAA,IAAY,WAAA;AAEtC,EAAA,OAAO;AAAA,IACL,eAAe,UAAA,IAAc,SAAA;AAAA,IAC7B,KAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAKO,SAAS,qBAAA,CACd,KAAA,EACA,SAAA,EACA,QAAA,EACA,cACA,SAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAA;AAAA,IACA,YAAY,SAAA,GAAY;AAAA,GAC1B;AACF;AA+EO,IAAM,gBAAA,GAAmC,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AAC5E,EAAA,MAAA,CAAO,GAAG,wBAAwB,CAAA;AAGlC,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,UAAA,EAAY;AAAA,MACV,WAAA,EAAa,EAAA;AAAA,MACb,aAAA,EAAe,GAAA;AAAA,MACf,KAAA,EAAO,CAAA;AAAA,MACP,mBAAA,EAAqB,EAAA;AAAA,MACrB,eAAA,EAAiB;AAAA;AACnB,GACD,CAAA;AAED,EAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,GAAA,CAA0C,YAAY,CAAA;AAEtF,EAAA,IAAI,CAAC,gBAAA,EAAkB;AACrB,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,YAAwB,EAAC;AAC7B,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,MAAM,YAAA,GAAe,KAAK,GAAA,EAAI;AAC9B,EAAA,IAAI,iBAAA,GAAsD,IAAA;AAC1D,EAAA,IAAI,gBAAA,GAAqD,IAAA;AAKzD,EAAA,SAAS,aAAA,GAAyB;AAChC,IAAA,IAAI,CAAC,kBAAkB,eAAA,EAAiB;AACtC,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,OAAO,cAAA,CAAe,UAAU,SAAS,CAAA;AAAA,EAC3C;AAKA,EAAA,SAAS,cAAc,CAAA,EAAqB;AAC1C,IAAA,MAAM,cAAc,EAAE,CAAA,EAAG,EAAE,OAAA,EAAS,CAAA,EAAG,EAAE,OAAA,EAAQ;AACjD,IAAA,MAAM,OAAA,GAAU,kBAAkB,mBAAA,IAAuB,EAAA;AACzD,IAAA,SAAA,GAAY,oBAAA,CAAqB,SAAA,EAAW,WAAA,EAAa,OAAO,CAAA;AAAA,EAClE;AAKA,EAAA,SAAS,iBAAiB,CAAA,EAAqB;AAE7C,IAAA,IAAI,SAAA,EAAW;AACb,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,OAAA,GAAU,kBAAkB,aAAA,IAAiB,GAAA;AACnD,IAAA,IAAI,CAAC,iBAAA,CAAkB,YAAA,EAAc,SAAS,IAAA,CAAK,GAAA,EAAK,CAAA,EAAG;AACzD,MAAA;AAAA,IACF;AAGA,IAAA,MAAM,WAAA,GAAc,kBAAkB,WAAA,IAAe,EAAA;AACrD,IAAA,MAAM,aAAA,GAAiB,CAAA,CAAU,aAAA,IAAkB,CAAA,CAAU,SAAA;AAC7D,IAAA,MAAM,MAAA,GAAS,uBAAA,CAAwB,SAAA,EAAW,WAAA,EAAa,aAAa,CAAA;AAE5E,IAAA,IAAI,OAAO,aAAA,EAAe;AACxB,MAAA,SAAA,GAAY,IAAA;AAGZ,MAAA,MAAM,YAAA,GAAe,qBAAA;AAAA,QACnB,MAAA,CAAO,KAAA;AAAA,QACP,MAAA,CAAO,SAAA;AAAA,QACP,MAAA,CAAO,QAAA;AAAA,QACP,YAAA;AAAA,QACA,KAAK,GAAA;AAAI,OACX;AAGA,MAAA,MAAM,KAAA,GAAQ,kBAAkB,KAAA,IAAS,CAAA;AAEzC,MAAA,IAAI,QAAQ,CAAA,EAAG;AACb,QAAA,UAAA,CAAW,MAAM;AAEf,UAAA,QAAA,CAAS,IAAA,CAAK,sBAAsB,YAAY,CAAA;AAAA,QAClD,GAAG,KAAK,CAAA;AAAA,MACV,CAAA,MAAO;AAEL,QAAA,QAAA,CAAS,IAAA,CAAK,sBAAsB,YAAY,CAAA;AAAA,MAClD;AAGA,MAAA,IAAI;AACF,QAAA,cAAA,CAAe,QAAQ,yBAAA,EAA2B,IAAA,CAAK,GAAA,EAAI,CAAE,UAAU,CAAA;AAAA,MACzE,SAAS,EAAA,EAAI;AAAA,MAEb;AAGA,MAAA,OAAA,EAAQ;AAAA,IACV;AAAA,EACF;AAKA,EAAA,SAAS,OAAA,GAAgB;AACvB,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,QAAA,CAAS,mBAAA,CAAoB,aAAa,iBAAiB,CAAA;AAC3D,MAAA,iBAAA,GAAoB,IAAA;AAAA,IACtB;AACA,IAAA,IAAI,gBAAA,EAAkB;AACpB,MAAA,QAAA,CAAS,mBAAA,CAAoB,YAAY,gBAAgB,CAAA;AACzD,MAAA,gBAAA,GAAmB,IAAA;AAAA,IACrB;AAAA,EACF;AAKA,EAAA,SAAS,UAAA,GAAmB;AAC1B,IAAA,IAAI,eAAc,EAAG;AACnB,MAAA;AAAA,IACF;AAGA,IAAA,IAAI;AACF,MAAA,MAAM,aAAA,GAAgB,cAAA,CAAe,OAAA,CAAQ,yBAAyB,CAAA;AACtE,MAAA,IAAI,aAAA,EAAe;AACjB,QAAA,SAAA,GAAY,IAAA;AACZ,QAAA;AAAA,MACF;AAAA,IACF,SAAS,EAAA,EAAI;AAAA,IAEb;AAEA,IAAA,iBAAA,GAAoB,aAAA;AACpB,IAAA,gBAAA,GAAmB,gBAAA;AAEnB,IAAA,QAAA,CAAS,gBAAA,CAAiB,aAAa,iBAAiB,CAAA;AACxD,IAAA,QAAA,CAAS,gBAAA,CAAiB,YAAY,gBAAgB,CAAA;AAAA,EACxD;AAGA,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,UAAA,EAAY;AAAA;AAAA;AAAA;AAAA,MAIV,aAAa,MAAM,SAAA;AAAA;AAAA;AAAA;AAAA,MAKnB,OAAO,MAAM;AACX,QAAA,SAAA,GAAY,KAAA;AACZ,QAAA,SAAA,GAAY,EAAC;AAGb,QAAA,IAAI;AACF,UAAA,cAAA,CAAe,WAAW,yBAAyB,CAAA;AAAA,QACrD,SAAS,EAAA,EAAI;AAAA,QAEb;AAEA,QAAA,OAAA,EAAQ;AACR,QAAA,UAAA,EAAW;AAAA,MACb,CAAA;AAAA;AAAA;AAAA;AAAA,MAKA,YAAA,EAAc,MAAM,CAAC,GAAG,SAAS;AAAA;AACnC,GACD,CAAA;AAGD,EAAA,UAAA,EAAW;AAGX,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,OAAA,EAAQ;AAAA,EACV,CAAA;AACA,EAAA,QAAA,CAAS,EAAA,CAAG,WAAW,cAAc,CAAA;AACvC;ACrUO,IAAM,eAAA,GAAkC,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AAC3E,EAAA,MAAA,CAAO,GAAG,WAAW,CAAA;AAGrB,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,SAAA,EAAW;AAAA,MACT,OAAA,EAAS,IAAA;AAAA,MACT,SAAA,EAAW;AAAA;AACb,GACD,CAAA;AAGD,EAAA,MAAM,sBAAA,uBAA6B,GAAA,EAAwC;AAG3E,EAAA,IAAI,CAAE,SAA+C,OAAA,EAAS;AAC5D,IAAA,QAAA,CAAS,IAAI,aAAa,CAAA;AAAA,EAC5B;AAEA,EAAA,MAAM,SAAA,GAAY,MAAe,MAAA,CAAO,GAAA,CAAI,mBAAmB,CAAA,IAAK,IAAA;AACpE,EAAA,MAAM,YAAA,GAAe,MAAc,MAAA,CAAO,GAAA,CAAI,qBAAqB,CAAA,IAAK,uBAAA;AAGxE,EAAA,MAAM,iBAAA,GAAoB,CAAC,GAAA,KAA6C;AACtE,IAAA,OAAO,GAAA,KAAQ,YAAY,cAAA,GAAiB,YAAA;AAAA,EAC9C,CAAA;AAGA,EAAA,MAAM,aAAA,GAAgB,CAAC,YAAA,KAAiC;AACtD,IAAA,OAAO,CAAA,EAAG,YAAA,EAAc,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA;AAAA,EAC1C,CAAA;AAGA,EAAA,MAAM,iBAAA,GAAoB,CACxB,YAAA,EACA,GAAA,KACmB;AACnB,IAAA,MAAM,OAAA,GAAU,kBAAkB,GAAG,CAAA;AACrC,IAAA,MAAM,GAAA,GAAM,cAAc,YAAY,CAAA;AACtC,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,OAAA,CAAQ,GAAG,CAAA;AAE/B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,CAAA;AAAA,QACP,cAAA,EAAgB,CAAA;AAAA,QAChB,aAAa,EAAC;AAAA,QACd;AAAA,OACF;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,OAAO,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,IACvB,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,CAAA;AAAA,QACP,cAAA,EAAgB,CAAA;AAAA,QAChB,aAAa,EAAC;AAAA,QACd;AAAA,OACF;AAAA,IACF;AAAA,EACF,CAAA;AAGA,EAAA,MAAM,kBAAA,GAAqB,CAAC,YAAA,EAAsB,IAAA,KAA+B;AAC/E,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,IAAO,SAAA;AACxB,IAAA,MAAM,OAAA,GAAU,kBAAkB,GAAG,CAAA;AACrC,IAAA,MAAM,GAAA,GAAM,cAAc,YAAY,CAAA;AACtC,IAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAK,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,EAC3C,CAAA;AAGA,EAAA,MAAM,aAAA,GAAgB,CAAC,GAAA,KAA4C;AACjE,IAAA,QAAQ,GAAA;AAAK,MACX,KAAK,SAAA;AACH,QAAA,OAAO,MAAA,CAAO,iBAAA;AAAA;AAAA,MAChB,KAAK,KAAA;AACH,QAAA,OAAO,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAAA;AAAA,MACxB,KAAK,MAAA;AACH,QAAA,OAAO,CAAA,GAAI,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,GAAA;AAAA;AAC9B,EACF,CAAA;AAKA,EAAA,MAAM,kBAAA,GAAqB,CACzB,YAAA,EACA,GAAA,GAAkC,SAAA,KACvB;AACX,IAAA,IAAI,CAAC,SAAA,EAAU,EAAG,OAAO,CAAA;AACzB,IAAA,MAAM,IAAA,GAAO,iBAAA,CAAkB,YAAA,EAAc,GAAG,CAAA;AAChD,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd,CAAA;AAKA,EAAA,MAAM,aAAA,GAAgB,CACpB,YAAA,EACA,GAAA,EACA,GAAA,KACY;AACZ,IAAA,IAAI,CAAC,SAAA,EAAU,EAAG,OAAO,KAAA;AAEzB,IAAA,MAAM,IAAA,GAAO,iBAAA,CAAkB,YAAA,EAAc,GAAG,CAAA;AAChD,IAAA,MAAM,UAAA,GAAa,cAAc,GAAG,CAAA;AACpC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,IAAA,IAAI,QAAQ,SAAA,EAAW;AACrB,MAAA,OAAO,KAAK,KAAA,IAAS,GAAA;AAAA,IACvB;AAGA,IAAA,MAAM,iBAAA,GAAoB,KAAK,WAAA,CAAY,MAAA,CAAO,CAAC,SAAA,KAAc,GAAA,GAAM,YAAY,UAAU,CAAA;AAE7F,IAAA,OAAO,kBAAkB,MAAA,IAAU,GAAA;AAAA,EACrC,CAAA;AAKA,EAAA,MAAM,gBAAA,GAAmB,CACvB,YAAA,EACA,GAAA,GAAkC,SAAA,KACzB;AACT,IAAA,IAAI,CAAC,WAAU,EAAG;AAElB,IAAA,MAAM,IAAA,GAAO,iBAAA,CAAkB,YAAA,EAAc,GAAG,CAAA;AAChD,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,IAAA,IAAA,CAAK,KAAA,IAAS,CAAA;AACd,IAAA,IAAA,CAAK,cAAA,GAAiB,GAAA;AACtB,IAAA,IAAA,CAAK,WAAA,CAAY,KAAK,GAAG,CAAA;AACzB,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AAGX,IAAA,MAAM,YAAA,GAAe,GAAA,GAAM,CAAA,GAAI,EAAA,GAAK,KAAK,EAAA,GAAK,GAAA;AAC9C,IAAA,IAAA,CAAK,cAAc,IAAA,CAAK,WAAA,CAAY,OAAO,CAAC,EAAA,KAAO,KAAK,YAAY,CAAA;AAGpE,IAAA,kBAAA,CAAmB,cAAc,IAAI,CAAA;AAGrC,IAAA,QAAA,CAAS,KAAK,iCAAA,EAAmC;AAAA,MAC/C,YAAA;AAAA,MACA,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,SAAA,EAAW;AAAA,KACZ,CAAA;AAAA,EACH,CAAA;AAGA,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,SAAA,EAAW;AAAA,MACT,kBAAA;AAAA,MACA,aAAA;AAAA,MACA,gBAAA;AAAA;AAAA,MAEA,mBAAA,EAAqB,CAAC,YAAA,EAAsB,GAAA,KAAoC;AAC9E,QAAA,sBAAA,CAAuB,GAAA,CAAI,cAAc,GAAG,CAAA;AAAA,MAC9C;AAAA;AACF,GACD,CAAA;AAGD,EAAA,IAAI,WAAU,EAAG;AACf,IAAA,QAAA,CAAS,EAAA,CAAG,uBAAA,EAAyB,CAAC,OAAA,KAAqB;AAIzD,MAAA,MAAM,QAAQ,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,OAAA,GAAU,CAAC,OAAO,CAAA;AAEzD,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AAExB,QAAA,MAAM,WAAY,IAAA,CAAiC,QAAA;AAGnD,QAAA,IAAI,QAAA,EAAU,IAAA,IAAQ,QAAA,CAAS,YAAA,EAAc;AAE3C,UAAA,IAAI,GAAA,GACF,sBAAA,CAAuB,GAAA,CAAI,QAAA,CAAS,YAAY,CAAA,IAAK,SAAA;AAGvD,UAAA,IAAI,CAAC,sBAAA,CAAuB,GAAA,CAAI,QAAA,CAAS,YAAY,CAAA,EAAG;AACtD,YAAA,MAAM,QAAA,GAAW,SAAS,KAAA,CAAM,IAAA;AAAA,cAC9B,CAAC,CAAA,KAAiB,CAAA,CAAE,IAAA,KAAS;AAAA,aAC/B;AACA,YAAA,IAAI,QAAA,EAAU,SAAS,OAAO,QAAA,CAAS,UAAU,QAAA,IAAY,KAAA,IAAS,SAAS,KAAA,EAAO;AACpF,cAAA,GAAA,GAAO,SAAS,KAAA,CAA8C,GAAA;AAE9D,cAAA,sBAAA,CAAuB,GAAA,CAAI,QAAA,CAAS,YAAA,EAAc,GAAG,CAAA;AAAA,YACvD;AAAA,UACF;AAEA,UAAA,gBAAA,CAAiB,QAAA,CAAS,cAAc,GAAG,CAAA;AAAA,QAC7C;AAAA,MACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AACF;ACrLO,SAAS,WAAA,GAAuB;AACrC,EAAA,IAAI,OAAO,SAAA,KAAc,WAAA,EAAa,OAAO,KAAA;AAC7C,EAAA,OACE,UAAU,UAAA,KAAe,GAAA,IACxB,UAAkB,YAAA,KAAiB,GAAA,IACnC,OAAe,UAAA,KAAe,GAAA;AAEnC;AAYO,SAAS,kBACd,YAAA,EACA,WAAA,EACA,aAAA,EACA,cAAA,EACA,eACA,SAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACL,YAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAA;AAAA,IACA,cAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AACF;AAEO,IAAM,gBAAA,GAAmC,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AAC5E,EAAA,MAAA,CAAO,GAAG,YAAY,CAAA;AAGtB,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,UAAA,EAAY;AAAA,MACV,OAAA,EAAS,IAAA;AAAA,MACT,UAAA,EAAY,IAAA;AAAA,MACZ,UAAA,EAAY,oBAAA;AAAA,MACZ,QAAA,EAAU,kBAAA;AAAA,MACV,GAAA,EAAK,MAAA;AAAA,MACL,aAAA,EAAe;AAAA;AACjB,GACD,CAAA;AAGD,EAAA,IAAI,CAAE,SAA+C,OAAA,EAAS;AAC5D,IAAA,OAAA,CAAQ,KAAK,wDAAwD,CAAA;AACrE,IAAA,QAAA,CAAS,IAAIA,aAAa,CAAA;AAAA,EAC5B;AAGA,EAAA,MAAM,WAAA,GAAc,QAAA;AAGpB,EAAA,IAAI,YAAA,GAAe,CAAA;AACnB,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,cAAA;AACJ,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI,gBAAA,GAAmB,KAAA;AACvB,EAAA,IAAI,WAAA,GAAc,KAAA;AAKlB,EAAA,SAAS,QAAA,GAAiB;AACxB,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,GAAA,CAAI,uBAAuB,CAAA,IAAK,oBAAA;AAC1D,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,qBAAqB,CAAA,IAAK,kBAAA;AAGtD,IAAA,MAAM,aAAA,GAAgB,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAY,UAAA,EAAY;AAAA,MAChE,OAAA,EAAS;AAAA,KACV,CAAA;AACD,IAAA,YAAA,GAAe,aAAA,IAAiB,CAAA;AAGhC,IAAA,MAAM,WAAA,GAAc,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAe,QAAA,EAAU;AAAA,MAC/D,OAAA,EAAS;AAAA,KACV,CAAA;AAED,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,UAAA,GAAa,YAAY,KAAA,IAAS,CAAA;AAClC,MAAA,cAAA,GAAiB,WAAA,CAAY,KAAA;AAC7B,MAAA,aAAA,GAAgB,WAAA,CAAY,IAAA;AAC5B,MAAA,gBAAA,GAAmB,KAAA;AAAA,IACrB,CAAA,MAAO;AACL,MAAA,UAAA,GAAa,CAAA;AACb,MAAA,cAAA,GAAiB,MAAA;AACjB,MAAA,aAAA,GAAgB,MAAA;AAChB,MAAA,gBAAA,GAAmB,IAAA;AAAA,IACrB;AAAA,EACF;AAKA,EAAA,SAAS,QAAA,GAAiB;AACxB,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,GAAA,CAAI,uBAAuB,CAAA,IAAK,oBAAA;AAC1D,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,qBAAqB,CAAA,IAAK,kBAAA;AACtD,IAAA,MAAM,GAAA,GAAM,MAAA,CAAO,GAAA,CAAI,gBAAgB,CAAA;AAGvC,IAAA,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,UAAA,EAAY,YAAA,EAAc;AAAA,MAChD,OAAA,EAAS;AAAA,KACV,CAAA;AAGD,IAAA,MAAM,SAAA,GAAuB;AAAA,MAC3B,KAAA,EAAO,UAAA;AAAA,MACP,KAAA,EAAO,cAAA,IAAkB,IAAA,CAAK,GAAA,EAAI;AAAA,MAClC,IAAA,EAAM,aAAA,IAAiB,IAAA,CAAK,GAAA;AAAI,KAClC;AAEA,IAAA,WAAA,CAAY,OAAA,CAAQ,GAAA,CAAI,QAAA,EAAU,SAAA,EAAW;AAAA,MAC3C,OAAA,EAAS,cAAA;AAAA,MACT,GAAI,GAAA,IAAO,EAAE,GAAA;AAAI,KAClB,CAAA;AAAA,EACH;AAKA,EAAA,SAAS,SAAA,GAAkB;AACzB,IAAA,IAAI,CAAC,WAAA,EAAa;AAChB,MAAA,QAAA,EAAS;AACT,MAAA,WAAA,GAAc,IAAA;AAAA,IAChB;AAGA,IAAA,YAAA,IAAgB,CAAA;AAChB,IAAA,UAAA,IAAc,CAAA;AACd,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AAGrB,IAAA,IAAI,gBAAA,EAAkB;AACpB,MAAA,cAAA,GAAiB,GAAA;AAAA,IACnB;AAGA,IAAA,aAAA,GAAgB,GAAA;AAGhB,IAAA,QAAA,EAAS;AAGT,IAAA,MAAM,KAAA,GAAQ,iBAAA;AAAA,MACZ,gBAAA;AAAA,MACA,UAAA;AAAA,MACA,YAAA;AAAA,MACA,cAAA;AAAA,MACA,aAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,MAAA,CAAO,IAAA,CAAK,0BAA0B,KAAK,CAAA;AAG3C,IAAA,IAAI,gBAAA,EAAkB;AACpB,MAAA,gBAAA,GAAmB,KAAA;AAAA,IACrB;AAAA,EACF;AAKA,EAAA,SAAS,KAAA,GAAc;AACrB,IAAA,MAAM,UAAA,GAAa,MAAA,CAAO,GAAA,CAAI,uBAAuB,CAAA,IAAK,oBAAA;AAC1D,IAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,qBAAqB,CAAA,IAAK,kBAAA;AAGtD,IAAA,WAAA,CAAY,QAAQ,MAAA,CAAO,UAAA,EAAY,EAAE,OAAA,EAAS,kBAAkB,CAAA;AACpE,IAAA,WAAA,CAAY,QAAQ,MAAA,CAAO,QAAA,EAAU,EAAE,OAAA,EAAS,gBAAgB,CAAA;AAGhE,IAAA,YAAA,GAAe,CAAA;AACf,IAAA,UAAA,GAAa,CAAA;AACb,IAAA,cAAA,GAAiB,MAAA;AACjB,IAAA,aAAA,GAAgB,MAAA;AAChB,IAAA,gBAAA,GAAmB,KAAA;AACnB,IAAA,WAAA,GAAc,KAAA;AAGd,IAAA,MAAA,CAAO,KAAK,kBAAkB,CAAA;AAAA,EAChC;AAKA,EAAA,SAAS,QAAA,GAA4B;AACnC,IAAA,OAAO,iBAAA;AAAA,MACL,gBAAA;AAAA,MACA,UAAA;AAAA,MACA,YAAA;AAAA,MACA,cAAA;AAAA,MACA,aAAA;AAAA,MACA,KAAK,GAAA;AAAI,KACX;AAAA,EACF;AAKA,EAAA,SAAS,UAAA,GAAmB;AAC1B,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,GAAA,CAAI,oBAAoB,CAAA,IAAK,IAAA;AACpD,IAAA,MAAM,gBAAA,GAAmB,MAAA,CAAO,GAAA,CAAI,uBAAuB,CAAA,IAAK,IAAA;AAChE,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,GAAA,CAAI,0BAA0B,CAAA,IAAK,IAAA;AAGhE,IAAA,IAAI,gBAAA,IAAoB,aAAY,EAAG;AACrC,MAAA,MAAA,CAAO,IAAA,CAAK,qBAAA,EAAuB,EAAE,MAAA,EAAQ,OAAO,CAAA;AACpD,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,MAAA,CAAO,IAAA,CAAK,qBAAA,EAAuB,EAAE,MAAA,EAAQ,UAAU,CAAA;AACvD,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,aAAA,EAAe;AACjB,MAAA,SAAA,EAAU;AAAA,IACZ;AAAA,EACF;AAGA,EAAA,QAAA,CAAS,EAAA,CAAG,aAAa,UAAU,CAAA;AAGnC,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,UAAA,EAAY;AAAA,MACV,eAAe,MAAM,UAAA;AAAA,MACrB,iBAAiB,MAAM,YAAA;AAAA,MACvB,cAAc,MAAM,gBAAA;AAAA,MACpB,mBAAmB,MAAM,cAAA;AAAA,MACzB,kBAAkB,MAAM,aAAA;AAAA,MACxB,SAAA;AAAA,MACA,KAAA;AAAA,MACA;AAAA;AACF,GACD,CAAA;AACH;;;ACjTO,SAAS,YAAA,GAAgD;AAC9D,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,SAAA;AAE1C,EAAA,MAAM,KAAK,SAAA,CAAU,SAAA;AACrB,EAAA,MAAM,QAAA,GAAW,2DAAA,CAA4D,IAAA,CAAK,EAAE,CAAA;AACpF,EAAA,MAAM,QAAA,GAAW,2BAAA,CAA4B,IAAA,CAAK,EAAE,CAAA;AAGpD,EAAA,MAAM,QAAQ,MAAA,CAAO,UAAA;AACrB,EAAA,IAAI,KAAA,GAAQ,KAAK,OAAO,QAAA;AACxB,EAAA,IAAI,KAAA,GAAQ,MAAM,OAAO,QAAA;AACzB,EAAA,IAAI,UAAU,OAAO,QAAA;AACrB,EAAA,IAAI,UAAU,OAAO,QAAA;AAErB,EAAA,OAAO,SAAA;AACT;AAQO,SAAS,QAAA,CACd,MACA,IAAA,EACkC;AAClC,EAAA,IAAI,OAAA,GAAgD,IAAA;AACpD,EAAA,IAAI,QAAA,GAAW,CAAA;AAEf,EAAA,OAAO,SAAS,aAAa,IAAA,EAAqB;AAChD,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,SAAA,GAAY,QAAQ,GAAA,GAAM,QAAA,CAAA;AAEhC,IAAA,IAAI,SAAA,IAAa,CAAA,IAAK,SAAA,GAAY,IAAA,EAAM;AACtC,MAAA,IAAI,OAAA,EAAS;AACX,QAAA,YAAA,CAAa,OAAO,CAAA;AACpB,QAAA,OAAA,GAAU,IAAA;AAAA,MACZ;AACA,MAAA,QAAA,GAAW,GAAA;AACX,MAAA,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,IACd,CAAA,MAAA,IAAW,CAAC,OAAA,EAAS;AACnB,MAAA,OAAA,GAAU,WAAW,MAAM;AACzB,QAAA,QAAA,GAAW,KAAK,GAAA,EAAI;AACpB,QAAA,OAAA,GAAU,IAAA;AACV,QAAA,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,MACd,GAAG,SAAS,CAAA;AAAA,IACd;AAAA,EACF,CAAA;AACF;AAKO,SAAS,uBAAuB,qBAAA,EAAwC;AAC7E,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,CAAA;AAG5C,EAAA,MAAM,gBAAA,GAAmB,QAAA,CAAS,gBAAA,IAAoB,QAAA,CAAS,eAAA;AAE/D,EAAA,MAAM,YAAY,gBAAA,CAAiB,SAAA;AACnC,EAAA,MAAM,eAAe,gBAAA,CAAiB,YAAA;AACtC,EAAA,MAAM,eAAe,gBAAA,CAAiB,YAAA;AAGtC,EAAA,IAAI,gBAAgB,YAAA,EAAc;AAChC,IAAA,OAAO,GAAA;AAAA,EACT;AAEA,EAAA,IAAI,qBAAA,EAAuB;AAGzB,IAAA,OAAO,KAAK,GAAA,CAAA,CAAM,SAAA,GAAY,YAAA,IAAgB,YAAA,GAAgB,KAAK,GAAG,CAAA;AAAA,EACxE;AAIA,EAAA,OAAO,KAAK,GAAA,CAAK,SAAA,IAAa,YAAA,GAAe,YAAA,CAAA,GAAiB,KAAK,GAAG,CAAA;AACxE;AAKO,SAAS,wBAAA,CACd,QAAA,EACA,mBAAA,EACA,gBAAA,EACA,iBACA,SAAA,EACQ;AAGR,EAAA,MAAM,gBAAgB,IAAA,CAAK,GAAA,CAAK,QAAA,GAAW,mBAAA,GAAuB,IAAI,EAAE,CAAA;AACxE,EAAA,MAAM,iBAAiB,IAAA,CAAK,GAAA,CAAK,gBAAA,GAAmB,CAAA,GAAK,IAAI,EAAE,CAAA;AAC/D,EAAA,MAAM,eAAe,IAAA,CAAK,GAAA,CAAK,eAAA,GAAkB,SAAA,GAAa,IAAI,EAAE,CAAA;AAEpE,EAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,GAAA,IAAO,aAAA,GAAgB,iBAAiB,YAAA,CAAa,CAAA;AAC1E;AA2DO,IAAM,iBAAA,GAAoC,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AAC7E,EAAA,MAAA,CAAO,GAAG,yBAAyB,CAAA;AAGnC,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,WAAA,EAAa;AAAA,MACX,UAAA,EAAY,CAAC,EAAA,EAAI,EAAA,EAAI,IAAI,GAAG,CAAA;AAAA,MAC5B,QAAA,EAAU,GAAA;AAAA,MACV,qBAAA,EAAuB,IAAA;AAAA,MACvB,mBAAA,EAAqB,IAAA;AAAA,MACrB,oBAAA,EAAsB,KAAA;AAAA,MACtB,2BAAA,EAA6B,CAAA;AAAA,MAC7B,eAAA,EAAiB;AAAA;AACnB,GACD,CAAA;AAGD,EAAA,MAAM,YAAA,GAAe,MAAA,CAAO,GAAA,CAAI,aAAa,CAAA;AAC7C,EAAA,IAAI,CAAC,YAAA,EAAc;AAGnB,EAAA,MAAM,GAAA,GAAM,YAAA;AAGZ,EAAA,MAAM,SAAS,YAAA,EAAa;AAC5B,EAAA,IAAI,GAAA,CAAI,eAAA,IAAmB,MAAA,KAAW,QAAA,EAAU;AAC9C,IAAA;AAAA,EACF;AAGA,EAAA,IAAI,gBAAA,GAAmB,CAAA;AACvB,EAAA,MAAM,mBAAA,uBAA0B,GAAA,EAAY;AAG5C,EAAA,MAAM,YAAA,GAAe,KAAK,GAAA,EAAI;AAC9B,EAAA,IAAI,kBAAA,GAAqB,CAAA;AACzB,EAAA,IAAI,cAAA,GAAiB,KAAK,GAAA,EAAI;AAC9B,EAAA,IAAI,mBAAA,GAA4C,IAAA;AAChD,EAAA,IAAI,kCAAA,GAAqC,CAAA;AACzC,EAAA,IAAI,eAAA,GAAkB,CAAA;AACtB,EAAA,MAAM,cAAA,uBAAqB,GAAA,EAAoB;AAK/C,EAAA,SAAS,YAAA,GAAe;AAEtB,IAAA,MAAM,cAAA,GAAiB,sBAAA,CAAuB,GAAA,CAAI,qBAAA,IAAyB,IAAI,CAAA;AAC/E,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,gBAAA,GAAmB,QAAA,CAAS,gBAAA,IAAoB,QAAA,CAAS,eAAA;AAC/D,IAAA,MAAM,kBAAkB,gBAAA,CAAiB,SAAA;AAGzC,IAAA,IAAI,QAAA,GAAW,CAAA;AAGf,IAAA,IAAI,IAAI,oBAAA,EAAsB;AAE5B,MAAA,MAAM,YAAY,GAAA,GAAM,cAAA;AACxB,MAAA,MAAM,gBAAgB,eAAA,GAAkB,kBAAA;AACxC,MAAA,QAAA,GAAW,YAAY,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,aAAa,IAAI,SAAA,GAAY,CAAA;AAGjE,MAAA,MAAM,mBACJ,aAAA,GAAgB,CAAA,GAAI,MAAA,GAAS,aAAA,GAAgB,IAAI,IAAA,GAAO,mBAAA;AAC1D,MAAA,IAAI,gBAAA,IAAoB,mBAAA,IAAuB,gBAAA,KAAqB,mBAAA,EAAqB;AACvF,QAAA,kCAAA,EAAA;AACmB,MACrB;AAGA,MAAA,IAAI,gBAAA,KAAqB,IAAA,IAAQ,SAAA,GAAY,CAAA,EAAG;AAC9C,QAAA,eAAA,IAAmB,SAAA;AAAA,MACrB;AAEA,MAAA,mBAAA,GAAsB,gBAAA;AACtB,MAAA,kBAAA,GAAqB,eAAA;AACrB,MAAA,cAAA,GAAiB,GAAA;AAAA,IACnB;AAGA,IAAA,gBAAA,GAAmB,IAAA,CAAK,GAAA,CAAI,gBAAA,EAAkB,cAAc,CAAA;AAG5D,IAAA,KAAA,MAAW,SAAA,IAAa,GAAA,CAAI,UAAA,IAAc,EAAC,EAAG;AAC5C,MAAA,IAAI,kBAAkB,SAAA,IAAa,CAAC,mBAAA,CAAoB,GAAA,CAAI,SAAS,CAAA,EAAG;AACtE,QAAA,mBAAA,CAAoB,IAAI,SAAS,CAAA;AAGjC,QAAA,IAAI,IAAI,oBAAA,EAAsB;AAC5B,UAAA,cAAA,CAAe,GAAA,CAAI,SAAA,EAAW,GAAA,GAAM,YAAY,CAAA;AAAA,QAClD;AAGA,QAAA,MAAM,YAAA,GAAiC;AAAA,UACrC,SAAA,EAAW,IAAA;AAAA,UACX,SAAA,EAAW,GAAA;AAAA,UACX,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,cAAA,GAAiB,GAAG,CAAA,GAAI,GAAA;AAAA,UAC5C,UAAA,EAAY,IAAA,CAAK,KAAA,CAAM,gBAAA,GAAmB,GAAG,CAAA,GAAI,GAAA;AAAA,UACjD,SAAA;AAAA,UACA,iBAAA,EAAmB,KAAA,CAAM,IAAA,CAAK,mBAAmB,CAAA,CAAE,KAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAAA,UACvE;AAAA,SACF;AAGA,QAAA,IAAI,IAAI,oBAAA,EAAsB;AAC5B,UAAA,MAAM,mBAAA,GAAsB,IAAI,2BAAA,IAA+B,CAAA;AAC/D,UAAA,MAAM,kBAAkB,QAAA,GAAW,mBAAA;AAGnC,UAAA,MAAM,eAAA,GAAkB,wBAAA;AAAA,YACtB,QAAA;AAAA,YACA,mBAAA;AAAA,YACA,kCAAA;AAAA,YACA,eAAA;AAAA,YACA,GAAA,GAAM;AAAA,WACR;AAEA,UAAA,YAAA,CAAa,QAAA,GAAW;AAAA,YACtB,iBAAiB,GAAA,GAAM,YAAA;AAAA,YACvB,QAAA,EAAU,IAAA,CAAK,KAAA,CAAM,QAAA,GAAW,GAAI,CAAA,GAAI,GAAA;AAAA;AAAA,YACxC,eAAA;AAAA,YACA,gBAAA,EAAkB,kCAAA;AAAA,YAClB,eAAA;AAAA,YACA,eAAA,EAAiB,IAAA,CAAK,KAAA,CAAM,eAAe;AAAA,WAC7C;AAGA,UAAA,kCAAA,GAAqC,CAAA;AAAA,QACvC;AAEA,QAAA,QAAA,CAAS,IAAA,CAAK,uBAAuB,YAAY,CAAA;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,sBAAA,GAAyB,QAAA,CAAS,YAAA,EAAc,GAAA,CAAI,YAAY,GAAG,CAAA;AAGzE,EAAA,MAAM,sBAAA,GAAyB,QAAA,CAAS,YAAA,EAAc,GAAA,CAAI,YAAY,GAAG,CAAA;AAGzE,EAAA,SAAS,UAAA,GAAa;AACpB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,aAAa,WAAA,EAAa;AACpE,MAAA;AAAA,IACF;AAGA,IAAA,MAAA,CAAO,iBAAiB,QAAA,EAAU,sBAAA,EAAwB,EAAE,OAAA,EAAS,MAAM,CAAA;AAG3E,IAAA,IAAI,IAAI,mBAAA,EAAqB;AAC3B,MAAA,MAAA,CAAO,iBAAiB,QAAA,EAAU,sBAAA,EAAwB,EAAE,OAAA,EAAS,MAAM,CAAA;AAAA,IAC7E;AAAA,EAIF;AAGA,EAAA,SAAS,OAAA,GAAU;AACjB,IAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,sBAAsB,CAAA;AAC3D,IAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,sBAAsB,CAAA;AAAA,EAC7D;AAGA,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,OAAA,EAAQ;AAAA,EACV,CAAA;AACA,EAAA,QAAA,CAAS,EAAA,CAAG,WAAW,cAAc,CAAA;AAGrC,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,WAAA,EAAa;AAAA;AAAA;AAAA;AAAA,MAIX,eAAe,MAAM,gBAAA;AAAA;AAAA;AAAA;AAAA,MAKrB,iBAAA,EAAmB,MAAM,sBAAA,CAAuB,GAAA,CAAI,yBAAyB,IAAI,CAAA;AAAA;AAAA;AAAA;AAAA,MAKjF,oBAAA,EAAsB,MAAM,KAAA,CAAM,IAAA,CAAK,mBAAmB,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAC,CAAA;AAAA;AAAA;AAAA;AAAA,MAKhF,WAAW,MAAM,MAAA;AAAA;AAAA;AAAA;AAAA,MAKjB,oBAAoB,MAAM;AACxB,QAAA,IAAI,CAAC,GAAA,CAAI,oBAAA,EAAsB,OAAO,IAAA;AAEtC,QAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,QAAA,OAAO;AAAA,UACL,YAAY,GAAA,GAAM,YAAA;AAAA,UAClB,gBAAA,EAAkB,kCAAA;AAAA,UAClB,eAAA;AAAA,UACA,cAAA,EAAgB,MAAA,CAAO,WAAA,CAAY,cAAc;AAAA,SACnD;AAAA,MACF,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,OAAO,MAAM;AACX,QAAA,gBAAA,GAAmB,CAAA;AACnB,QAAA,mBAAA,CAAoB,KAAA,EAAM;AAC1B,QAAA,kCAAA,GAAqC,CAAA;AACrC,QAAA,eAAA,GAAkB,CAAA;AAClB,QAAA,cAAA,CAAe,KAAA,EAAM;AACrB,QAAA,mBAAA,GAAsB,IAAA;AAAA,MACxB;AAAA;AACF,GACD,CAAA;AAGD,EAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,IAAA,UAAA,CAAW,YAAY,CAAC,CAAA;AAAA,EAC1B;AAGA,EAAA,OAAO,MAAM;AACX,IAAA,OAAA,EAAQ;AACR,IAAA,QAAA,CAAS,GAAA,CAAI,WAAW,cAAc,CAAA;AAAA,EACxC,CAAA;AACF;;;ACvYO,SAAS,gBAAA,CAAiB,WAAmB,cAAA,EAAgC;AAClF,EAAA,OAAO,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA,GAAY,cAAA;AAClC;AAKO,SAAS,gBAAA,GAA4B;AAC1C,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,KAAA;AAC5C,EAAA,OAAO,SAAS,MAAA,IAAU,KAAA;AAC5B;AAKO,SAAS,oBAAA,CACd,SAAA,EACA,cAAA,EACA,SAAA,EACA,iBAAA,EACgB;AAChB,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,EAAA,MAAM,UAAU,SAAA,GAAY,SAAA;AAC5B,EAAA,MAAM,gBAAgB,OAAA,GAAU,cAAA;AAEhC,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,OAAA;AAAA,IACA,aAAA;AAAA,IACA,SAAA;AAAA,IACA;AAAA,GACF;AACF;AAoEO,IAAM,eAAA,GAAkC,CAAC,MAAA,EAAQ,QAAA,EAAU,MAAA,KAAW;AAC3E,EAAA,MAAA,CAAO,GAAG,uBAAuB,CAAA;AAGjC,EAAA,MAAA,CAAO,QAAA,CAAS;AAAA,IACd,SAAA,EAAW;AAAA,MACT,KAAA,EAAO,CAAA;AAAA,MACP,eAAA,EAAiB;AAAA;AACnB,GACD,CAAA;AAGD,EAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,GAAA,CAAI,WAAW,CAAA;AAC9C,EAAA,IAAI,CAAC,eAAA,EAAiB;AAEtB,EAAA,MAAM,KAAA,GAAQ,gBAAgB,KAAA,IAAS,CAAA;AACvC,EAAA,MAAM,eAAA,GAAkB,gBAAgB,eAAA,IAAmB,IAAA;AAG3D,EAAA,IAAI,SAAS,CAAA,EAAG;AAGhB,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,MAAA,GAAS,KAAA;AACb,EAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,EAAA,IAAI,aAAA,GAAgB,CAAA;AACpB,EAAA,IAAI,iBAAA,GAAoB,CAAA;AACxB,EAAA,IAAI,KAAA,GAA8C,IAAA;AAClD,EAAA,IAAI,kBAAA,GAA0C,IAAA;AAK9C,EAAA,SAAS,OAAA,GAAgB;AACvB,IAAA,IAAI,SAAA,EAAW;AAEf,IAAA,SAAA,GAAY,IAAA;AAGZ,IAAA,MAAM,YAAA,GAAe,oBAAA;AAAA,MACnB,SAAA;AAAA,MACA,cAAA;AAAA,MACA,iBAAA,GAAoB,CAAA;AAAA,MACpB;AAAA,KACF;AAGA,IAAA,QAAA,CAAS,IAAA,CAAK,qBAAqB,YAAY,CAAA;AAG/C,IAAA,OAAA,EAAQ;AAAA,EACV;AAKA,EAAA,SAAS,cAAc,cAAA,EAA8B;AACnD,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,YAAA,CAAa,KAAK,CAAA;AAAA,IACpB;AAEA,IAAA,KAAA,GAAQ,WAAW,MAAM;AACvB,MAAA,OAAA,EAAQ;AAAA,IACV,GAAG,cAAc,CAAA;AAAA,EACnB;AAKA,EAAA,SAAS,sBAAA,GAA+B;AACtC,IAAA,MAAM,SAAS,gBAAA,EAAiB;AAEhC,IAAA,IAAI,MAAA,IAAU,CAAC,MAAA,EAAQ;AAErB,MAAA,MAAA,GAAS,IAAA;AACT,MAAA,aAAA,GAAgB,KAAK,GAAA,EAAI;AACzB,MAAA,iBAAA,EAAA;AAGA,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,KAAA,GAAQ,IAAA;AAAA,MACV;AAAA,IACF,CAAA,MAAA,IAAW,CAAC,MAAA,IAAU,MAAA,EAAQ;AAE5B,MAAA,MAAA,GAAS,KAAA;AACT,MAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,GAAA,EAAI,GAAI,aAAA;AACnC,MAAA,cAAA,IAAkB,aAAA;AAClB,MAAA,iBAAA,EAAA;AAGA,MAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,SAAA,EAAW,cAAc,CAAA;AAC1D,MAAA,MAAM,YAAY,KAAA,GAAQ,OAAA;AAE1B,MAAA,IAAI,YAAY,CAAA,EAAG;AACjB,QAAA,aAAA,CAAc,SAAS,CAAA;AAAA,MACzB,CAAA,MAAO;AAEL,QAAA,OAAA,EAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAKA,EAAA,SAAS,OAAA,GAAgB;AACvB,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,KAAA,GAAQ,IAAA;AAAA,IACV;AACA,IAAA,IAAI,kBAAA,IAAsB,OAAO,QAAA,KAAa,WAAA,EAAa;AACzD,MAAA,QAAA,CAAS,mBAAA,CAAoB,oBAAoB,kBAAkB,CAAA;AACnE,MAAA,kBAAA,GAAqB,IAAA;AAAA,IACvB;AAAA,EACF;AAKA,EAAA,SAAS,UAAA,GAAmB;AAE1B,IAAA,IAAI,eAAA,IAAmB,kBAAiB,EAAG;AACzC,MAAA,MAAA,GAAS,IAAA;AACT,MAAA,aAAA,GAAgB,KAAK,GAAA,EAAI;AACzB,MAAA,iBAAA,EAAA;AAAA,IACF,CAAA,MAAO;AAEL,MAAA,aAAA,CAAc,KAAK,CAAA;AAAA,IACrB;AAGA,IAAA,IAAI,eAAA,IAAmB,OAAO,QAAA,KAAa,WAAA,EAAa;AACtD,MAAA,kBAAA,GAAqB,sBAAA;AACrB,MAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,kBAAkB,CAAA;AAAA,IAClE;AAAA,EACF;AAGA,EAAA,MAAA,CAAO,MAAA,CAAO;AAAA,IACZ,SAAA,EAAW;AAAA,MACT,YAAY,MAAM;AAChB,QAAA,OAAO,IAAA,CAAK,KAAI,GAAI,SAAA;AAAA,MACtB,CAAA;AAAA,MAEA,kBAAkB,MAAM;AACtB,QAAA,IAAI,qBAAA,GAAwB,cAAA;AAC5B,QAAA,IAAI,MAAA,EAAQ;AAEV,UAAA,qBAAA,IAAyB,IAAA,CAAK,KAAI,GAAI,aAAA;AAAA,QACxC;AACA,QAAA,OAAO,gBAAA,CAAiB,WAAW,qBAAqB,CAAA;AAAA,MAC1D,CAAA;AAAA,MAEA,cAAc,MAAM;AAClB,QAAA,IAAI,WAAW,OAAO,CAAA;AAEtB,QAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,SAAA,EAAW,cAAc,CAAA;AAC1D,QAAA,MAAM,YAAY,KAAA,GAAQ,OAAA;AAC1B,QAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAA;AAAA,MAC9B,CAAA;AAAA,MAEA,UAAU,MAAM,MAAA;AAAA,MAEhB,aAAa,MAAM,SAAA;AAAA,MAEnB,OAAO,MAAM;AACX,QAAA,SAAA,GAAY,KAAA;AACZ,QAAA,MAAA,GAAS,KAAA;AACT,QAAA,cAAA,GAAiB,CAAA;AACjB,QAAA,aAAA,GAAgB,CAAA;AAChB,QAAA,iBAAA,GAAoB,CAAA;AAEpB,QAAA,OAAA,EAAQ;AACR,QAAA,UAAA,EAAW;AAAA,MACb;AAAA;AACF,GACD,CAAA;AAGD,EAAA,UAAA,EAAW;AAGX,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAA,OAAA,EAAQ;AAAA,EACV,CAAA;AACA,EAAA,QAAA,CAAS,EAAA,CAAG,WAAW,cAAc,CAAA;AACvC","file":"index.js","sourcesContent":["/**\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"]}
|