@prosdevlab/experience-sdk-plugins 0.1.4 → 0.3.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.
Files changed (43) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +150 -0
  3. package/README.md +141 -79
  4. package/dist/index.d.ts +813 -35
  5. package/dist/index.js +1910 -66
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/src/banner/banner.ts +63 -62
  9. package/src/exit-intent/exit-intent.test.ts +423 -0
  10. package/src/exit-intent/exit-intent.ts +371 -0
  11. package/src/exit-intent/index.ts +6 -0
  12. package/src/exit-intent/types.ts +59 -0
  13. package/src/index.ts +7 -0
  14. package/src/inline/index.ts +3 -0
  15. package/src/inline/inline.test.ts +620 -0
  16. package/src/inline/inline.ts +269 -0
  17. package/src/inline/insertion.ts +66 -0
  18. package/src/inline/types.ts +52 -0
  19. package/src/integration.test.ts +421 -0
  20. package/src/modal/form-rendering.ts +262 -0
  21. package/src/modal/form-styles.ts +212 -0
  22. package/src/modal/form-validation.test.ts +413 -0
  23. package/src/modal/form-validation.ts +126 -0
  24. package/src/modal/index.ts +3 -0
  25. package/src/modal/modal-styles.ts +204 -0
  26. package/src/modal/modal.browser.test.ts +164 -0
  27. package/src/modal/modal.test.ts +1294 -0
  28. package/src/modal/modal.ts +685 -0
  29. package/src/modal/types.ts +114 -0
  30. package/src/page-visits/index.ts +6 -0
  31. package/src/page-visits/page-visits.test.ts +562 -0
  32. package/src/page-visits/page-visits.ts +314 -0
  33. package/src/page-visits/types.ts +119 -0
  34. package/src/scroll-depth/index.ts +6 -0
  35. package/src/scroll-depth/scroll-depth.test.ts +580 -0
  36. package/src/scroll-depth/scroll-depth.ts +398 -0
  37. package/src/scroll-depth/types.ts +122 -0
  38. package/src/time-delay/index.ts +6 -0
  39. package/src/time-delay/time-delay.test.ts +477 -0
  40. package/src/time-delay/time-delay.ts +296 -0
  41. package/src/time-delay/types.ts +89 -0
  42. package/src/types.ts +20 -36
  43. package/src/utils/sanitize.ts +5 -2
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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\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 = '&times;';\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","/**\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/inline/insertion.ts","../src/inline/inline.ts","../src/modal/form-styles.ts","../src/modal/form-rendering.ts","../src/modal/form-validation.ts","../src/modal/modal-styles.ts","../src/modal/modal.ts","../src/page-visits/page-visits.ts","../src/scroll-depth/scroll-depth.ts","../src/time-delay/time-delay.ts"],"names":["storagePlugin"],"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', 'div', 'ul', 'li'] 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 div: ['class', 'style'],\n ul: ['class', 'style'],\n li: ['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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\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: var(--xp-banner-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);\n font-size: var(--xp-banner-font-size, 14px);\n line-height: var(--xp-banner-line-height, 1.5);\n box-sizing: border-box;\n z-index: var(--xp-banner-z-index, 10000);\n background: var(--xp-banner-bg, #ffffff);\n color: var(--xp-banner-color, #111827);\n border-bottom: var(--xp-banner-border-width, 1px) solid var(--xp-banner-border-color, #e5e7eb);\n box-shadow: var(--xp-banner-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: var(--xp-banner-border-width, 1px) solid var(--xp-banner-border-color, #e5e7eb);\n box-shadow: var(--xp-banner-shadow-bottom, 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: var(--xp-banner-gap, 16px);\n max-width: var(--xp-banner-max-width, 1280px);\n margin: 0 auto;\n padding: var(--xp-banner-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: var(--xp-banner-content-gap, 4px);\n }\n \n .xp-banner__title {\n font-weight: var(--xp-banner-title-weight, 600);\n margin: 0;\n font-size: var(--xp-banner-title-size, 15px);\n line-height: var(--xp-banner-title-line-height, 1.4);\n color: var(--xp-banner-title-color, inherit);\n }\n \n .xp-banner__message {\n margin: 0;\n font-size: var(--xp-banner-message-size, 14px);\n line-height: var(--xp-banner-message-line-height, 1.5);\n color: var(--xp-banner-message-color, #6b7280);\n }\n \n .xp-banner__buttons {\n display: flex;\n align-items: center;\n gap: var(--xp-banner-buttons-gap, 8px);\n flex-shrink: 0;\n }\n \n .xp-banner__button {\n padding: var(--xp-banner-button-padding, 8px 16px);\n border: none;\n border-radius: var(--xp-banner-button-radius, 6px);\n font-size: var(--xp-banner-button-font-size, 14px);\n font-weight: var(--xp-banner-button-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: var(--xp-banner-button-primary-bg, #2563eb);\n color: var(--xp-banner-button-primary-color, #ffffff);\n }\n \n .xp-banner__button--primary:hover {\n background: var(--xp-banner-button-primary-bg-hover, #1d4ed8);\n }\n \n .xp-banner__button--secondary {\n background: var(--xp-banner-button-secondary-bg, #f3f4f6);\n color: var(--xp-banner-button-secondary-color, #374151);\n border: var(--xp-banner-border-width, 1px) solid var(--xp-banner-button-secondary-border, #e5e7eb);\n }\n \n .xp-banner__button--secondary:hover {\n background: var(--xp-banner-button-secondary-bg-hover, #e5e7eb);\n }\n \n .xp-banner__button--link {\n background: transparent;\n color: var(--xp-banner-button-link-color, #2563eb);\n padding: var(--xp-banner-button-link-padding, 6px 12px);\n font-weight: var(--xp-banner-button-link-font-weight, 400);\n }\n \n .xp-banner__button--link:hover {\n background: var(--xp-banner-button-link-bg-hover, #f3f4f6);\n text-decoration: underline;\n }\n \n .xp-banner__close {\n background: transparent;\n border: none;\n color: var(--xp-banner-close-color, #9ca3af);\n font-size: var(--xp-banner-close-size, 20px);\n line-height: 1;\n cursor: pointer;\n padding: var(--xp-banner-close-padding, 4px);\n margin: 0;\n transition: color 0.2s;\n flex-shrink: 0;\n width: var(--xp-banner-close-width, 28px);\n height: var(--xp-banner-close-height, 28px);\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--xp-banner-close-radius, 4px);\n }\n \n .xp-banner__close:hover {\n color: var(--xp-banner-close-color-hover, #111827);\n background: var(--xp-banner-close-bg-hover, #f3f4f6);\n }\n \n @media (max-width: 640px) {\n .xp-banner__container {\n flex-wrap: wrap;\n padding: var(--xp-banner-padding-mobile, 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 - override CSS variables */\n @media (prefers-color-scheme: dark) {\n .xp-banner {\n background: var(--xp-banner-bg-dark, #111827);\n color: var(--xp-banner-color-dark, #f9fafb);\n border-bottom-color: var(--xp-banner-border-color-dark, #1f2937);\n }\n \n .xp-banner--bottom {\n border-top-color: var(--xp-banner-border-color-dark, #1f2937);\n }\n \n .xp-banner__message {\n color: var(--xp-banner-message-color-dark, #9ca3af);\n }\n \n .xp-banner__button--primary {\n background: var(--xp-banner-button-primary-bg-dark, #3b82f6);\n }\n \n .xp-banner__button--primary:hover {\n background: var(--xp-banner-button-primary-bg-hover-dark, #2563eb);\n }\n \n .xp-banner__button--secondary {\n background: var(--xp-banner-button-secondary-bg-dark, #1f2937);\n color: var(--xp-banner-button-secondary-color-dark, #f9fafb);\n border-color: var(--xp-banner-button-secondary-border-dark, #374151);\n }\n \n .xp-banner__button--secondary:hover {\n background: var(--xp-banner-button-secondary-bg-hover-dark, #374151);\n }\n \n .xp-banner__button--link {\n color: var(--xp-banner-button-link-color-dark, #60a5fa);\n }\n \n .xp-banner__button--link:hover {\n background: var(--xp-banner-button-link-bg-hover-dark, #1f2937);\n }\n \n .xp-banner__close {\n color: var(--xp-banner-close-color-dark, #6b7280);\n }\n \n .xp-banner__close:hover {\n color: var(--xp-banner-close-color-hover-dark, #f9fafb);\n background: var(--xp-banner-close-bg-hover-dark, #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 = '&times;';\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 instance.on('sdk:destroy', () => {\n cleanup();\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","import type { InsertionPosition } from './types';\n\n/**\n * Insert content into a target element using specified position\n *\n * @param selector - CSS selector for target element\n * @param content - HTML content to insert\n * @param position - Where to insert the content\n * @param experienceId - Unique identifier for the experience\n * @returns The created wrapper element, or null if target not found\n */\nexport function insertContent(\n selector: string,\n content: string,\n position: InsertionPosition,\n experienceId: string\n): HTMLElement | null {\n const target = document.querySelector(selector);\n\n if (!target) {\n return null;\n }\n\n const wrapper = document.createElement('div');\n wrapper.className = 'xp-inline';\n wrapper.setAttribute('data-xp-id', experienceId);\n wrapper.innerHTML = content;\n\n switch (position) {\n case 'replace':\n target.innerHTML = '';\n target.appendChild(wrapper);\n break;\n case 'append':\n target.appendChild(wrapper);\n break;\n case 'prepend':\n target.insertBefore(wrapper, target.firstChild);\n break;\n case 'before':\n target.parentElement?.insertBefore(wrapper, target);\n break;\n case 'after':\n target.parentElement?.insertBefore(wrapper, target.nextSibling);\n break;\n }\n\n return wrapper;\n}\n\n/**\n * Remove inline content by experience ID\n *\n * @param experienceId - Unique identifier for the experience\n * @returns True if element was found and removed, false otherwise\n */\nexport function removeContent(experienceId: string): boolean {\n const element = document.querySelector(`[data-xp-id=\"${experienceId}\"]`);\n\n if (!element) {\n return false;\n }\n\n element.remove();\n return true;\n}\n","import type { SDK } from '@lytics/sdk-kit';\nimport type { StoragePlugin } from '@lytics/sdk-kit-plugins';\nimport { storagePlugin } from '@lytics/sdk-kit-plugins';\nimport { sanitizeHTML } from '../utils/sanitize';\nimport { insertContent, removeContent } from './insertion';\nimport type { InlinePlugin } from './types';\n\n/**\n * SDK instance with storage plugin\n */\ntype SDKWithStorage = SDK & { storage?: StoragePlugin };\n\n/**\n * Inline Plugin\n *\n * Embeds experiences directly within page content using DOM selectors.\n * Supports multiple insertion positions and dismissal with persistence.\n */\nexport const inlinePlugin = (plugin: any, instance: SDK, config: any): void => {\n plugin.ns('experiences.inline');\n\n plugin.defaults({\n inline: {\n retry: false,\n retryTimeout: 5000,\n },\n });\n\n // Auto-load storage plugin if not present\n if (!(instance as SDKWithStorage).storage) {\n instance.use(storagePlugin);\n }\n\n // Cast instance to include storage\n const sdkInstance = instance as SDKWithStorage;\n\n // Inject CSS variables\n if (typeof document !== 'undefined') {\n const styleId = 'xp-inline-styles';\n if (!document.getElementById(styleId)) {\n const style = document.createElement('style');\n style.id = styleId;\n style.textContent = getInlineStyles();\n document.head.appendChild(style);\n }\n }\n\n const activeInlines = new Map<string, HTMLElement>();\n\n /**\n * Show an inline experience\n */\n const show = (experience: any): void => {\n const { id, content } = experience;\n\n // Check if already showing (prevent duplicates)\n if (activeInlines.has(id)) {\n return;\n }\n\n // Check if dismissed and persisted\n if (content.persist && content.dismissable && sdkInstance.storage) {\n const dismissed = sdkInstance.storage.get(`xp-inline-dismissed-${id}`);\n if (dismissed) {\n instance.emit('experiences:inline:dismissed', {\n experienceId: id,\n reason: 'previously-dismissed',\n timestamp: Date.now(),\n });\n return;\n }\n }\n\n // Try to insert content\n const element = insertContent(\n content.selector,\n sanitizeHTML(content.message),\n content.position || 'replace',\n id\n );\n\n if (!element) {\n // Selector not found\n instance.emit('experiences:inline:error', {\n experienceId: id,\n error: 'selector-not-found',\n selector: content.selector,\n timestamp: Date.now(),\n });\n\n // Retry logic (if enabled)\n const retryEnabled = config.get('inline.retry') ?? false;\n const retryTimeout = config.get('inline.retryTimeout') ?? 5000;\n\n if (retryEnabled) {\n setTimeout(() => {\n show(experience);\n }, retryTimeout);\n }\n\n return;\n }\n\n // Store reference\n activeInlines.set(id, element);\n\n // Apply custom styles\n if (content.className) {\n element.classList.add(content.className);\n }\n if (content.style) {\n Object.assign(element.style, content.style);\n }\n\n // Add dismissal button if needed\n if (content.dismissable) {\n const closeBtn = document.createElement('button');\n closeBtn.className = 'xp-inline__close';\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.textContent = '×';\n closeBtn.onclick = () => {\n remove(id);\n if (content.persist && sdkInstance.storage) {\n sdkInstance.storage.set(`xp-inline-dismissed-${id}`, true);\n }\n instance.emit('experiences:dismissed', {\n experienceId: id,\n timestamp: Date.now(),\n });\n };\n element.prepend(closeBtn);\n }\n\n // Emit shown event\n instance.emit('experiences:shown', {\n experienceId: id,\n type: 'inline',\n selector: content.selector,\n position: content.position || 'replace',\n timestamp: Date.now(),\n });\n };\n\n /**\n * Remove an inline experience\n */\n const remove = (experienceId: string): void => {\n const element = activeInlines.get(experienceId);\n if (!element) return;\n\n removeContent(experienceId);\n activeInlines.delete(experienceId);\n };\n\n /**\n * Check if an inline experience is showing\n */\n const isShowing = (experienceId?: string): boolean => {\n if (experienceId) {\n return activeInlines.has(experienceId);\n }\n return activeInlines.size > 0;\n };\n\n // Expose public API\n plugin.expose({\n inline: {\n show,\n remove,\n isShowing,\n } as InlinePlugin,\n });\n\n // Auto-show on evaluation\n instance.on('experiences:evaluated', (data: any) => {\n if (data.decision?.show && data.experience?.type === 'inline') {\n show(data.experience);\n }\n });\n\n // Cleanup on destroy\n instance.on('sdk:destroy', () => {\n for (const id of Array.from(activeInlines.keys())) {\n remove(id);\n }\n });\n};\n\n/**\n * Get CSS styles for inline experiences\n */\nfunction getInlineStyles(): string {\n return `\n :root {\n --xp-inline-close-size: 24px;\n --xp-inline-close-color: #666;\n --xp-inline-close-hover-color: #111;\n --xp-inline-close-bg: transparent;\n --xp-inline-close-hover-bg: rgba(0, 0, 0, 0.05);\n --xp-inline-close-border-radius: 4px;\n }\n\n @media (prefers-color-scheme: dark) {\n :root {\n --xp-inline-close-color: #9ca3af;\n --xp-inline-close-hover-color: #f9fafb;\n --xp-inline-close-hover-bg: rgba(255, 255, 255, 0.1);\n }\n }\n\n .xp-inline {\n position: relative;\n animation: xp-inline-enter 0.4s ease-out forwards;\n }\n\n @keyframes xp-inline-enter {\n from {\n opacity: 0;\n transform: translateY(-8px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n }\n\n /* Respect user's motion preferences */\n @media (prefers-reduced-motion: reduce) {\n .xp-inline {\n animation: xp-inline-enter-reduced 0.2s ease-out forwards;\n }\n \n @keyframes xp-inline-enter-reduced {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n }\n }\n\n .xp-inline__close {\n position: absolute;\n top: 8px;\n right: 8px;\n width: var(--xp-inline-close-size);\n height: var(--xp-inline-close-size);\n padding: 0;\n border: none;\n background: var(--xp-inline-close-bg);\n color: var(--xp-inline-close-color);\n font-size: 20px;\n line-height: 1;\n cursor: pointer;\n border-radius: var(--xp-inline-close-border-radius);\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1;\n }\n\n .xp-inline__close:hover {\n background: var(--xp-inline-close-hover-bg);\n color: var(--xp-inline-close-hover-color);\n }\n `;\n}\n","/**\n * Form styling with CSS variables for theming\n *\n * Design tokens inspired by Tailwind CSS, fully customizable via CSS variables.\n * Users can override by setting CSS variables in their stylesheet.\n *\n * @example\n * ```css\n * :root {\n * --xp-form-input-border: #3b82f6;\n * --xp-form-input-focus-ring: rgba(59, 130, 246, 0.2);\n * }\n * ```\n */\n\n/**\n * Get CSS for form container\n */\nexport function getFormStyles(): string {\n return `\n margin-top: var(--xp-form-spacing, 16px);\n display: flex;\n flex-direction: column;\n gap: var(--xp-form-gap, 16px);\n `.trim();\n}\n\n/**\n * Get CSS for form field wrapper\n */\nexport function getFieldStyles(): string {\n return `\n display: flex;\n flex-direction: column;\n gap: var(--xp-field-gap, 6px);\n `.trim();\n}\n\n/**\n * Get CSS for form label\n */\nexport function getLabelStyles(): string {\n return `\n font-size: var(--xp-label-font-size, 14px);\n font-weight: var(--xp-label-font-weight, 500);\n color: var(--xp-label-color, #374151);\n line-height: 1.5;\n `.trim();\n}\n\n/**\n * Get CSS for required indicator\n */\nexport function getRequiredStyles(): string {\n return `\n color: var(--xp-required-color, #ef4444);\n `.trim();\n}\n\n/**\n * Get CSS for form input/textarea\n */\nexport function getInputStyles(): string {\n return `\n padding: var(--xp-input-padding, 8px 12px);\n font-size: var(--xp-input-font-size, 14px);\n line-height: 1.5;\n color: var(--xp-input-color, #111827);\n background-color: var(--xp-input-bg, white);\n border: var(--xp-input-border-width, 1px) solid var(--xp-input-border-color, #d1d5db);\n border-radius: var(--xp-input-radius, 6px);\n transition: all 0.15s ease-in-out;\n outline: none;\n width: 100%;\n box-sizing: border-box;\n `.trim();\n}\n\n/**\n * Get CSS for input focus state (applies via :focus)\n */\nexport function getInputFocusStyles(): string {\n return `\n border-color: var(--xp-input-focus-border, #3b82f6);\n box-shadow: 0 0 0 var(--xp-input-focus-ring-width, 3px) var(--xp-input-focus-ring, rgba(59, 130, 246, 0.1));\n `.trim();\n}\n\n/**\n * Get CSS for input error state\n */\nexport function getInputErrorStyles(): string {\n return `\n border-color: var(--xp-input-error-border, #ef4444);\n `.trim();\n}\n\n/**\n * Get CSS for error message\n */\nexport function getErrorMessageStyles(): string {\n return `\n font-size: var(--xp-error-font-size, 13px);\n color: var(--xp-error-color, #ef4444);\n line-height: 1.4;\n min-height: 18px;\n `.trim();\n}\n\n/**\n * Get CSS for submit button\n */\nexport function getSubmitButtonStyles(): string {\n return `\n margin-top: var(--xp-submit-margin-top, 8px);\n padding: var(--xp-submit-padding, 10px 20px);\n font-size: var(--xp-submit-font-size, 14px);\n font-weight: var(--xp-submit-font-weight, 500);\n color: var(--xp-submit-color, white);\n background-color: var(--xp-submit-bg, #2563eb);\n border: none;\n border-radius: var(--xp-submit-radius, 6px);\n cursor: pointer;\n transition: all 0.2s;\n width: 100%;\n `.trim();\n}\n\n/**\n * Get CSS for submit button hover background\n */\nexport function getSubmitButtonHoverBg(): string {\n return 'var(--xp-submit-bg-hover, #1d4ed8)';\n}\n\n/**\n * Get CSS for submit button disabled state\n */\nexport function getSubmitButtonDisabledStyles(): string {\n return `\n opacity: var(--xp-submit-disabled-opacity, 0.6);\n cursor: not-allowed;\n `.trim();\n}\n\n/**\n * Get CSS for form state container (success/error)\n */\nexport function getFormStateStyles(): string {\n return `\n padding: var(--xp-state-padding, 16px);\n border-radius: var(--xp-state-radius, 8px);\n text-align: center;\n `.trim();\n}\n\n/**\n * Get CSS for success state\n */\nexport function getSuccessStateStyles(): string {\n return `\n background-color: var(--xp-success-bg, #f0fdf4);\n border: var(--xp-state-border-width, 1px) solid var(--xp-success-border, #86efac);\n `.trim();\n}\n\n/**\n * Get CSS for error state\n */\nexport function getErrorStateStyles(): string {\n return `\n background-color: var(--xp-error-bg, #fef2f2);\n border: var(--xp-state-border-width, 1px) solid var(--xp-error-border, #fca5a5);\n `.trim();\n}\n\n/**\n * Get CSS for state title\n */\nexport function getStateTitleStyles(): string {\n return `\n font-size: var(--xp-state-title-font-size, 16px);\n font-weight: var(--xp-state-title-font-weight, 600);\n margin: 0 0 var(--xp-state-title-margin-bottom, 8px) 0;\n color: var(--xp-state-title-color, #111827);\n `.trim();\n}\n\n/**\n * Get CSS for state message\n */\nexport function getStateMessageStyles(): string {\n return `\n font-size: var(--xp-state-message-font-size, 14px);\n line-height: 1.5;\n color: var(--xp-state-message-color, #374151);\n margin: 0;\n `.trim();\n}\n\n/**\n * Get CSS for state buttons container\n */\nexport function getStateButtonsStyles(): string {\n return `\n margin-top: var(--xp-state-buttons-margin-top, 16px);\n display: flex;\n gap: var(--xp-state-buttons-gap, 8px);\n justify-content: center;\n flex-wrap: wrap;\n `.trim();\n}\n","/**\n * Pure form rendering functions\n *\n * These functions are intentionally pure (return DOM elements, no side effects) to make them:\n * - Easy to test\n * - Easy to extract into a separate form plugin later\n * - Reusable across different contexts\n */\n\nimport type { ExperienceButton } from '../types';\nimport {\n getErrorMessageStyles,\n getErrorStateStyles,\n getFieldStyles,\n getFormStateStyles,\n getFormStyles,\n getInputStyles,\n getLabelStyles,\n getRequiredStyles,\n getStateButtonsStyles,\n getStateMessageStyles,\n getStateTitleStyles,\n getSubmitButtonHoverBg,\n getSubmitButtonStyles,\n getSuccessStateStyles,\n} from './form-styles';\nimport type { FormConfig, FormField } from './types';\n\n/**\n * Render complete form element\n *\n * @param experienceId - Experience ID for namespacing field IDs\n * @param config - Form configuration\n * @returns Form HTML element\n */\nexport function renderForm(experienceId: string, config: FormConfig): HTMLFormElement {\n const form = document.createElement('form');\n form.className = 'xp-modal__form';\n form.style.cssText = getFormStyles();\n form.dataset.xpExperienceId = experienceId;\n form.setAttribute('novalidate', ''); // Use custom validation instead of browser default\n\n // Render each field\n config.fields.forEach((field) => {\n const fieldElement = renderFormField(experienceId, field);\n form.appendChild(fieldElement);\n });\n\n // Render submit button\n const submitButton = renderSubmitButton(config.submitButton);\n form.appendChild(submitButton);\n\n return form;\n}\n\n/**\n * Render a single form field with label and error container\n *\n * @param experienceId - Experience ID for namespacing field ID\n * @param field - Field configuration\n * @returns Field wrapper HTML element\n */\nexport function renderFormField(experienceId: string, field: FormField): HTMLElement {\n const wrapper = document.createElement('div');\n wrapper.className = 'xp-form__field';\n wrapper.style.cssText = getFieldStyles();\n\n // Label (optional)\n if (field.label) {\n const label = document.createElement('label');\n label.className = 'xp-form__label';\n label.style.cssText = getLabelStyles();\n label.htmlFor = `${experienceId}-${field.name}`;\n label.textContent = field.label;\n\n // Required indicator\n if (field.required) {\n const required = document.createElement('span');\n required.className = 'xp-form__required';\n required.style.cssText = getRequiredStyles();\n required.textContent = ' *';\n required.setAttribute('aria-label', 'required');\n label.appendChild(required);\n }\n\n wrapper.appendChild(label);\n }\n\n // Input element (input or textarea)\n const input =\n field.type === 'textarea'\n ? document.createElement('textarea')\n : document.createElement('input');\n\n input.className = 'xp-form__input';\n input.style.cssText = getInputStyles();\n input.id = `${experienceId}-${field.name}`;\n input.name = field.name;\n\n // Set type for input elements\n if (input instanceof HTMLInputElement) {\n input.type = field.type;\n }\n\n // Placeholder\n if (field.placeholder) {\n input.placeholder = field.placeholder;\n }\n\n // Required attribute (for screen readers)\n if (field.required) {\n input.required = true;\n }\n\n // Pattern attribute (for HTML5 validation as fallback)\n if (field.pattern && input instanceof HTMLInputElement) {\n input.setAttribute('pattern', field.pattern);\n }\n\n // Accessibility attributes\n input.setAttribute('aria-invalid', 'false');\n input.setAttribute('aria-describedby', `${experienceId}-${field.name}-error`);\n\n // Custom styling\n if (field.className) {\n input.className += ` ${field.className}`;\n }\n if (field.style) {\n Object.assign(input.style, field.style);\n }\n\n wrapper.appendChild(input);\n\n // Error message container\n const error = document.createElement('div');\n error.className = 'xp-form__error';\n error.style.cssText = getErrorMessageStyles();\n error.id = `${experienceId}-${field.name}-error`;\n error.setAttribute('role', 'alert');\n error.setAttribute('aria-live', 'polite');\n wrapper.appendChild(error);\n\n return wrapper;\n}\n\n/**\n * Render submit button\n *\n * @param buttonConfig - Button configuration\n * @returns Button HTML element\n */\nexport function renderSubmitButton(buttonConfig: ExperienceButton): HTMLButtonElement {\n const button = document.createElement('button');\n button.type = 'submit';\n button.className = 'xp-form__submit xp-modal__button';\n button.style.cssText = getSubmitButtonStyles();\n\n // Variant styling\n if (buttonConfig.variant) {\n button.className += ` xp-modal__button--${buttonConfig.variant}`;\n }\n\n // Custom class\n if (buttonConfig.className) {\n button.className += ` ${buttonConfig.className}`;\n }\n\n // Button text\n button.textContent = buttonConfig.text;\n\n // Hover effect using CSS variable\n const hoverBg = getSubmitButtonHoverBg();\n button.onmouseover = () => {\n button.style.backgroundColor = hoverBg;\n };\n button.onmouseout = () => {\n button.style.backgroundColor = ''; // Reset to CSS variable default\n };\n\n // Custom styling (applied last to allow overrides)\n if (buttonConfig.style) {\n Object.assign(button.style, buttonConfig.style);\n }\n\n return button;\n}\n\n/**\n * Render form state (success or error)\n *\n * @param state - State configuration ('success' or 'error')\n * @param stateConfig - State content configuration\n * @returns State HTML element\n */\nexport function renderFormState(\n state: 'success' | 'error',\n stateConfig: { title?: string; message: string; buttons?: ExperienceButton[] }\n): HTMLElement {\n const stateEl = document.createElement('div');\n stateEl.className = `xp-form__state xp-form__state--${state}`;\n\n // Base styles + state-specific styles\n const baseStyles = getFormStateStyles();\n const stateStyles = state === 'success' ? getSuccessStateStyles() : getErrorStateStyles();\n stateEl.style.cssText = `${baseStyles}; ${stateStyles}`;\n\n // Title (optional)\n if (stateConfig.title) {\n const title = document.createElement('h3');\n title.className = 'xp-form__state-title';\n title.style.cssText = getStateTitleStyles();\n title.textContent = stateConfig.title;\n stateEl.appendChild(title);\n }\n\n // Message\n const message = document.createElement('div');\n message.className = 'xp-form__state-message';\n message.style.cssText = getStateMessageStyles();\n message.textContent = stateConfig.message;\n stateEl.appendChild(message);\n\n // Buttons (optional)\n if (stateConfig.buttons && stateConfig.buttons.length > 0) {\n const buttonContainer = document.createElement('div');\n buttonContainer.className = 'xp-form__state-buttons';\n buttonContainer.style.cssText = getStateButtonsStyles();\n\n stateConfig.buttons.forEach((btnConfig) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'xp-modal__button';\n\n if (btnConfig.variant) {\n btn.className += ` xp-modal__button--${btnConfig.variant}`;\n }\n if (btnConfig.className) {\n btn.className += ` ${btnConfig.className}`;\n }\n\n btn.textContent = btnConfig.text;\n\n if (btnConfig.style) {\n Object.assign(btn.style, btnConfig.style);\n }\n\n // Store action/dismiss metadata for event handlers\n if (btnConfig.action) {\n btn.dataset.action = btnConfig.action;\n }\n if (btnConfig.dismiss) {\n btn.dataset.dismiss = 'true';\n }\n\n buttonContainer.appendChild(btn);\n });\n\n stateEl.appendChild(buttonContainer);\n }\n\n return stateEl;\n}\n","/**\n * Pure validation functions for form fields\n *\n * These functions are intentionally pure (no side effects) to make them:\n * - Easy to test\n * - Easy to extract into a separate form plugin later\n * - Reusable across different contexts\n */\n\nimport type { FormConfig, FormField, ValidationResult } from './types';\n\n/**\n * Validate a single form field\n *\n * @param field - Field configuration\n * @param value - Current field value\n * @returns Validation result with errors if invalid\n */\nexport function validateField(field: FormField, value: string): ValidationResult {\n const errors: Record<string, string> = {};\n\n // Required field validation\n if (field.required && (!value || value.trim() === '')) {\n errors[field.name] = field.errorMessage || `${field.label || field.name} is required`;\n return { valid: false, errors };\n }\n\n // Skip further validation if field is empty and not required\n if (!value || value.trim() === '') {\n return { valid: true };\n }\n\n // Type-specific validation\n switch (field.type) {\n case 'email': {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (!emailRegex.test(value)) {\n errors[field.name] = field.errorMessage || 'Please enter a valid email address';\n }\n break;\n }\n\n case 'url': {\n try {\n new URL(value);\n } catch {\n errors[field.name] = field.errorMessage || 'Please enter a valid URL';\n }\n break;\n }\n\n case 'tel': {\n // Basic phone validation (allows digits, spaces, dashes, parentheses, plus)\n const phoneRegex = /^[\\d\\s\\-()+]+$/;\n if (!phoneRegex.test(value)) {\n errors[field.name] = field.errorMessage || 'Please enter a valid phone number';\n }\n break;\n }\n\n case 'number': {\n if (Number.isNaN(Number(value))) {\n errors[field.name] = field.errorMessage || 'Please enter a valid number';\n }\n break;\n }\n }\n\n // Custom pattern validation (regex)\n if (field.pattern && value) {\n try {\n const regex = new RegExp(field.pattern);\n if (!regex.test(value)) {\n errors[field.name] =\n field.errorMessage || `Invalid format for ${field.label || field.name}`;\n }\n } catch (_error) {\n // Invalid regex pattern - log warning but don't break validation\n console.warn(`Invalid regex pattern for field ${field.name}:`, field.pattern);\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors: Object.keys(errors).length > 0 ? errors : undefined,\n };\n}\n\n/**\n * Validate entire form\n *\n * @param config - Form configuration\n * @param data - Current form data\n * @returns Validation result with all field errors if invalid\n */\nexport function validateForm(config: FormConfig, data: Record<string, string>): ValidationResult {\n const errors: Record<string, string> = {};\n\n // Validate each field\n config.fields.forEach((field) => {\n const value = data[field.name] || '';\n const result = validateField(field, value);\n\n if (!result.valid && result.errors) {\n Object.assign(errors, result.errors);\n }\n });\n\n // Custom validation function\n if (config.validate) {\n try {\n const customResult = config.validate(data);\n if (!customResult.valid && customResult.errors) {\n Object.assign(errors, customResult.errors);\n }\n } catch (error) {\n console.error('Custom validation function threw an error:', error);\n // Don't prevent submission if custom validation has a bug\n }\n }\n\n return {\n valid: Object.keys(errors).length === 0,\n errors: Object.keys(errors).length > 0 ? errors : undefined,\n };\n}\n","/**\n * Modal styling with CSS variables for theming\n *\n * Design tokens for modal dialogs, fully customizable via CSS variables.\n * Users can override by setting CSS variables in their stylesheet.\n *\n * @example\n * ```css\n * :root {\n * --xp-modal-backdrop-bg: rgba(0, 0, 0, 0.7);\n * --xp-modal-dialog-bg: #ffffff;\n * --xp-modal-dialog-radius: 12px;\n * }\n * ```\n */\n\n/**\n * Get CSS for modal backdrop\n */\nexport function getBackdropStyles(): string {\n return `\n position: absolute;\n inset: 0;\n background-color: var(--xp-modal-backdrop-bg, rgba(0, 0, 0, 0.5));\n `.trim();\n}\n\n/**\n * Get CSS for modal dialog\n */\nexport function getDialogStyles(params: {\n width: string;\n maxWidth: string;\n height: string;\n maxHeight: string;\n borderRadius: string;\n padding: string;\n}): string {\n return `\n position: relative;\n background: var(--xp-modal-dialog-bg, white);\n border-radius: var(--xp-modal-dialog-radius, ${params.borderRadius});\n box-shadow: var(--xp-modal-dialog-shadow, 0 4px 6px rgba(0, 0, 0, 0.1));\n max-width: ${params.width};\n width: ${params.maxWidth};\n height: ${params.height};\n max-height: ${params.maxHeight};\n overflow-y: auto;\n padding: ${params.padding};\n `.trim();\n}\n\n/**\n * Get CSS for hero image\n */\nexport function getHeroImageStyles(params: { maxHeight: number; borderRadius: string }): string {\n return `\n width: 100%;\n height: auto;\n max-height: ${params.maxHeight}px;\n object-fit: cover;\n border-radius: ${params.borderRadius};\n display: block;\n margin: 0;\n `.trim();\n}\n\n/**\n * Get CSS for close button\n */\nexport function getCloseButtonStyles(): string {\n return `\n position: absolute;\n top: var(--xp-modal-close-top, 16px);\n right: var(--xp-modal-close-right, 16px);\n background: none;\n border: none;\n font-size: var(--xp-modal-close-size, 24px);\n line-height: 1;\n cursor: pointer;\n padding: var(--xp-modal-close-padding, 4px 8px);\n color: var(--xp-modal-close-color, #666);\n opacity: var(--xp-modal-close-opacity, 0.7);\n transition: opacity 0.2s;\n `.trim();\n}\n\n/**\n * Get close button hover opacity\n */\nexport function getCloseButtonHoverOpacity(): string {\n return 'var(--xp-modal-close-hover-opacity, 1)';\n}\n\n/**\n * Get close button default opacity\n */\nexport function getCloseButtonDefaultOpacity(): string {\n return 'var(--xp-modal-close-opacity, 0.7)';\n}\n\n/**\n * Get CSS for content wrapper\n */\nexport function getContentWrapperStyles(padding: string): string {\n return `padding: ${padding};`;\n}\n\n/**\n * Get CSS for modal title\n */\nexport function getTitleStyles(): string {\n return `\n margin: 0 0 var(--xp-modal-title-margin-bottom, 16px) 0;\n font-size: var(--xp-modal-title-size, 20px);\n font-weight: var(--xp-modal-title-weight, 600);\n color: var(--xp-modal-title-color, #111);\n `.trim();\n}\n\n/**\n * Get CSS for modal message\n */\nexport function getMessageStyles(): string {\n return `\n margin: 0 0 var(--xp-modal-message-margin-bottom, 20px) 0;\n font-size: var(--xp-modal-message-size, 14px);\n line-height: var(--xp-modal-message-line-height, 1.5);\n color: var(--xp-modal-message-color, #444);\n `.trim();\n}\n\n/**\n * Get CSS for button container\n */\nexport function getButtonContainerStyles(): string {\n return `\n display: flex;\n gap: var(--xp-modal-buttons-gap, 8px);\n flex-wrap: wrap;\n `.trim();\n}\n\n/**\n * Get CSS for primary button\n */\nexport function getPrimaryButtonStyles(): string {\n return `\n padding: var(--xp-button-padding, 10px 20px);\n font-size: var(--xp-button-font-size, 14px);\n font-weight: var(--xp-button-font-weight, 500);\n border-radius: var(--xp-button-radius, 6px);\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n background: var(--xp-button-primary-bg, #2563eb);\n color: var(--xp-button-primary-color, white);\n `.trim();\n}\n\n/**\n * Get primary button hover background\n */\nexport function getPrimaryButtonHoverBg(): string {\n return 'var(--xp-button-primary-bg-hover, #1d4ed8)';\n}\n\n/**\n * Get primary button default background\n */\nexport function getPrimaryButtonDefaultBg(): string {\n return 'var(--xp-button-primary-bg, #2563eb)';\n}\n\n/**\n * Get CSS for secondary button\n */\nexport function getSecondaryButtonStyles(): string {\n return `\n padding: var(--xp-button-padding, 10px 20px);\n font-size: var(--xp-button-font-size, 14px);\n font-weight: var(--xp-button-font-weight, 500);\n border-radius: var(--xp-button-radius, 6px);\n cursor: pointer;\n transition: all 0.2s;\n border: none;\n background: var(--xp-button-secondary-bg, #f3f4f6);\n color: var(--xp-button-secondary-color, #374151);\n `.trim();\n}\n\n/**\n * Get secondary button hover background\n */\nexport function getSecondaryButtonHoverBg(): string {\n return 'var(--xp-button-secondary-bg-hover, #e5e7eb)';\n}\n\n/**\n * Get secondary button default background\n */\nexport function getSecondaryButtonDefaultBg(): string {\n return 'var(--xp-button-secondary-bg, #f3f4f6)';\n}\n","import type { SDK } from '@lytics/sdk-kit';\nimport type { ExperienceButton } from '../types';\nimport { sanitizeHTML } from '../utils/sanitize';\nimport { renderForm, renderFormState } from './form-rendering';\nimport { getInputErrorStyles } from './form-styles';\nimport { validateField, validateForm } from './form-validation';\nimport {\n getBackdropStyles,\n getButtonContainerStyles,\n getCloseButtonDefaultOpacity,\n getCloseButtonHoverOpacity,\n getCloseButtonStyles,\n getContentWrapperStyles,\n getDialogStyles,\n getHeroImageStyles,\n getMessageStyles,\n getPrimaryButtonDefaultBg,\n getPrimaryButtonHoverBg,\n getPrimaryButtonStyles,\n getSecondaryButtonDefaultBg,\n getSecondaryButtonHoverBg,\n getSecondaryButtonStyles,\n getTitleStyles,\n} from './modal-styles';\nimport type { ModalContent, ModalPlugin } from './types';\n\n/**\n * Modal Plugin for @prosdevlab/experience-sdk\n *\n * Renders experiences as accessible modal dialogs with:\n * - Focus trap and keyboard handling\n * - ARIA attributes for screen readers\n * - Backdrop and close button\n * - Responsive design\n */\nexport const modalPlugin = (plugin: any, instance: SDK): void => {\n plugin.ns('experiences.modal');\n plugin.defaults({\n modal: {\n dismissable: true,\n backdropDismiss: true,\n zIndex: 10001,\n size: 'md',\n mobileFullscreen: false,\n position: 'center',\n animation: 'fade',\n animationDuration: 200,\n },\n });\n\n // Track active modals\n const activeModals = new Map<string, HTMLElement>();\n // Track focus before modal opened\n const previouslyFocusedElement = new Map<string, HTMLElement | null>();\n // Track form data by experience ID\n const formData = new Map<string, Record<string, string>>();\n\n /**\n * Get focusable elements within a container\n */\n const getFocusableElements = (container: HTMLElement): HTMLElement[] => {\n const selector =\n 'a[href], button, textarea, input, select, details, [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(container.querySelectorAll(selector)).filter(\n (el) => !(el as HTMLElement).hasAttribute('disabled')\n ) as HTMLElement[];\n };\n\n /**\n * Create focus trap for modal\n */\n const createFocusTrap = (container: HTMLElement): (() => void) => {\n const focusable = getFocusableElements(container);\n if (focusable.length === 0) return () => {};\n\n const firstFocusable = focusable[0];\n const lastFocusable = focusable[focusable.length - 1];\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key !== 'Tab') return;\n\n if (e.shiftKey) {\n // Shift + Tab\n if (document.activeElement === firstFocusable) {\n e.preventDefault();\n lastFocusable.focus();\n }\n } else {\n // Tab\n if (document.activeElement === lastFocusable) {\n e.preventDefault();\n firstFocusable.focus();\n }\n }\n };\n\n container.addEventListener('keydown', handleKeyDown);\n\n // Set initial focus\n firstFocusable.focus();\n\n // Return cleanup function\n return () => {\n container.removeEventListener('keydown', handleKeyDown);\n };\n };\n\n /**\n * Get modal size width\n */\n const getSizeWidth = (size: string): string => {\n switch (size) {\n case 'sm':\n return '400px';\n case 'md':\n return '600px';\n case 'lg':\n return '800px';\n case 'fullscreen':\n return '100vw';\n case 'auto':\n return 'auto';\n default:\n return '600px'; // md default\n }\n };\n\n /**\n * Check if mobile viewport\n */\n const isMobile = (): boolean => {\n return typeof window !== 'undefined' && window.innerWidth < 640;\n };\n\n /**\n * Render modal DOM structure\n */\n const renderModal = (experienceId: string, content: ModalContent): HTMLElement => {\n const modalConfig = instance.get('modal') || {};\n const zIndex = modalConfig.zIndex || 10001;\n const size = modalConfig.size || 'md';\n const position = modalConfig.position || 'center';\n const animation = modalConfig.animation || 'fade';\n const animationDuration = modalConfig.animationDuration || 200;\n\n // Determine if should be fullscreen\n const mobileFullscreen =\n modalConfig.mobileFullscreen !== undefined ? modalConfig.mobileFullscreen : size === 'lg'; // Auto-enable for lg size\n const shouldBeFullscreen = size === 'fullscreen' || (mobileFullscreen && isMobile());\n\n // Create modal container\n const container = document.createElement('div');\n const sizeClass = shouldBeFullscreen ? 'fullscreen' : size;\n const positionClass = position === 'bottom' ? 'xp-modal--bottom' : 'xp-modal--center';\n const animationClass = animation !== 'none' ? `xp-modal--${animation}` : '';\n container.className =\n `xp-modal xp-modal--${sizeClass} ${positionClass} ${animationClass} ${content.className || ''}`.trim();\n container.setAttribute('data-xp-id', experienceId);\n container.setAttribute('role', 'dialog');\n container.setAttribute('aria-modal', 'true');\n if (content.title) {\n container.setAttribute('aria-labelledby', `xp-modal-title-${experienceId}`);\n }\n\n // Container styles\n const alignItems = position === 'bottom' ? 'flex-end' : 'center';\n container.style.cssText = `position: fixed; inset: 0; z-index: ${zIndex}; display: flex; align-items: ${alignItems}; justify-content: center;`;\n\n // Apply animation\n if (animation !== 'none') {\n container.style.opacity = '0';\n container.style.transition = `opacity ${animationDuration}ms ease-in-out`;\n\n if (animation === 'slide-up') {\n container.style.transform = 'translateY(100%)';\n container.style.transition += `, transform ${animationDuration}ms ease-out`;\n }\n }\n\n // Apply custom styles\n if (content.style) {\n Object.entries(content.style).forEach(([key, value]) => {\n container.style.setProperty(key, String(value));\n });\n }\n\n // Backdrop\n const backdrop = document.createElement('div');\n backdrop.className = 'xp-modal__backdrop';\n backdrop.style.cssText = getBackdropStyles();\n container.appendChild(backdrop);\n\n // Dialog\n const dialog = document.createElement('div');\n const dialogWidth = shouldBeFullscreen ? '100%' : size === 'auto' ? 'none' : getSizeWidth(size);\n const dialogHeight = shouldBeFullscreen ? '100%' : 'auto';\n const dialogMaxWidth = shouldBeFullscreen ? '100%' : size === 'auto' ? 'none' : '90%';\n const dialogBorderRadius = shouldBeFullscreen ? '0' : '8px';\n const dialogPadding = content.image ? '0' : '24px';\n\n dialog.className = `xp-modal__dialog${content.image ? ' xp-modal__dialog--has-image' : ''}`;\n dialog.style.cssText = getDialogStyles({\n width: dialogWidth,\n maxWidth: dialogMaxWidth,\n height: dialogHeight,\n maxHeight: shouldBeFullscreen ? '100%' : '90vh',\n borderRadius: dialogBorderRadius,\n padding: dialogPadding,\n });\n container.appendChild(dialog);\n\n // Hero image (if provided)\n if (content.image) {\n const img = document.createElement('img');\n img.className = 'xp-modal__hero-image';\n img.src = content.image.src;\n img.alt = content.image.alt;\n img.loading = 'lazy';\n\n // Use custom maxHeight if provided, otherwise default based on viewport\n const maxHeight = content.image.maxHeight || (isMobile() ? 200 : 300);\n img.style.cssText = getHeroImageStyles({\n maxHeight,\n borderRadius: shouldBeFullscreen ? '0' : '8px 8px 0 0',\n });\n\n dialog.appendChild(img);\n }\n\n // Close button\n if (modalConfig.dismissable !== false) {\n const closeButton = document.createElement('button');\n closeButton.className = 'xp-modal__close';\n closeButton.setAttribute('aria-label', 'Close dialog');\n closeButton.innerHTML = '&times;';\n closeButton.style.cssText = getCloseButtonStyles();\n closeButton.onmouseover = () => {\n closeButton.style.opacity = getCloseButtonHoverOpacity();\n };\n closeButton.onmouseout = () => {\n closeButton.style.opacity = getCloseButtonDefaultOpacity();\n };\n closeButton.onclick = () => removeModal(experienceId);\n dialog.appendChild(closeButton);\n }\n\n // Content wrapper\n const contentWrapper = document.createElement('div');\n contentWrapper.className = 'xp-modal__content';\n const contentPadding = content.image ? '24px' : '24px 24px 0 24px';\n contentWrapper.style.cssText = getContentWrapperStyles(contentPadding);\n\n // Title\n if (content.title) {\n const title = document.createElement('h2');\n title.id = `xp-modal-title-${experienceId}`;\n title.className = 'xp-modal__title';\n title.textContent = content.title;\n title.style.cssText = getTitleStyles();\n contentWrapper.appendChild(title);\n }\n\n // Message\n const message = document.createElement('div');\n message.className = 'xp-modal__message';\n message.innerHTML = sanitizeHTML(content.message);\n message.style.cssText = getMessageStyles();\n contentWrapper.appendChild(message);\n\n // Form or Buttons\n if (content.form) {\n // Render form\n const form = renderForm(experienceId, content.form);\n contentWrapper.appendChild(form);\n\n // Store form config for later use (state rendering)\n (container as any).__formConfig = content.form;\n\n // Initialize form data\n const data: Record<string, string> = {};\n content.form.fields.forEach((field) => {\n data[field.name] = '';\n });\n formData.set(experienceId, data);\n\n // Add form event listeners\n content.form.fields.forEach((field) => {\n const input = form.querySelector(`#${experienceId}-${field.name}`) as\n | HTMLInputElement\n | HTMLTextAreaElement;\n const errorEl = form.querySelector(`#${experienceId}-${field.name}-error`) as HTMLElement;\n\n if (!input) return;\n\n // Update form data on input change\n input.addEventListener('input', () => {\n const currentData = formData.get(experienceId) || {};\n currentData[field.name] = input.value;\n formData.set(experienceId, currentData);\n\n // Emit change event\n instance.emit('experiences:modal:form:change', {\n experienceId,\n field: field.name,\n value: input.value,\n formData: { ...currentData },\n timestamp: Date.now(),\n });\n });\n\n // Validate on blur\n input.addEventListener('blur', () => {\n const currentData = formData.get(experienceId) || {};\n const result = validateField(field, currentData[field.name] || '');\n\n if (!result.valid && result.errors) {\n // Show error\n input.style.cssText += `; ${getInputErrorStyles()}`;\n input.setAttribute('aria-invalid', 'true');\n errorEl.textContent = result.errors[field.name] || '';\n\n // Emit validation event\n instance.emit('experiences:modal:form:validation', {\n experienceId,\n field: field.name,\n valid: false,\n errors: result.errors,\n timestamp: Date.now(),\n });\n } else {\n // Clear error\n input.style.cssText = input.style.cssText.replace(getInputErrorStyles(), '');\n input.setAttribute('aria-invalid', 'false');\n errorEl.textContent = '';\n\n // Emit validation event\n instance.emit('experiences:modal:form:validation', {\n experienceId,\n field: field.name,\n valid: true,\n timestamp: Date.now(),\n });\n }\n });\n });\n\n // Handle form submission\n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n\n if (!content.form) return;\n\n const currentData = formData.get(experienceId) || {};\n const result = validateForm(content.form, currentData);\n\n if (!result.valid && result.errors) {\n // Show all errors\n content.form.fields.forEach((field) => {\n if (result.errors?.[field.name]) {\n const input = form.querySelector(\n `#${experienceId}-${field.name}`\n ) as HTMLInputElement;\n const errorEl = form.querySelector(\n `#${experienceId}-${field.name}-error`\n ) as HTMLElement;\n\n if (input) {\n input.style.cssText += `; ${getInputErrorStyles()}`;\n input.setAttribute('aria-invalid', 'true');\n }\n if (errorEl) {\n errorEl.textContent = result.errors[field.name] || '';\n }\n }\n });\n\n // Emit validation failure\n instance.emit('experiences:modal:form:validation', {\n experienceId,\n valid: false,\n errors: result.errors,\n timestamp: Date.now(),\n });\n\n return;\n }\n\n // Disable submit button\n const submitButton = form.querySelector('button[type=\"submit\"]') as HTMLButtonElement;\n if (submitButton) {\n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n }\n\n // Emit submit event\n instance.emit('experiences:modal:form:submit', {\n experienceId,\n formData: { ...currentData },\n timestamp: Date.now(),\n });\n });\n } else if (content.buttons && content.buttons.length > 0) {\n // Render buttons\n const buttonContainer = document.createElement('div');\n buttonContainer.className = 'xp-modal__buttons';\n buttonContainer.style.cssText = getButtonContainerStyles();\n\n content.buttons.forEach((button: ExperienceButton) => {\n const btn = document.createElement('button');\n btn.className = `xp-modal__button xp-modal__button--${button.variant || 'secondary'}`;\n btn.textContent = button.text;\n\n // Apply button styles based on variant\n if (button.variant === 'primary') {\n btn.style.cssText = getPrimaryButtonStyles();\n btn.onmouseover = () => {\n btn.style.background = getPrimaryButtonHoverBg();\n };\n btn.onmouseout = () => {\n btn.style.background = getPrimaryButtonDefaultBg();\n };\n } else {\n btn.style.cssText = getSecondaryButtonStyles();\n btn.onmouseover = () => {\n btn.style.background = getSecondaryButtonHoverBg();\n };\n btn.onmouseout = () => {\n btn.style.background = getSecondaryButtonDefaultBg();\n };\n }\n\n btn.onclick = () => {\n instance.emit('experiences:action', {\n experienceId,\n action: button.action,\n button,\n timestamp: Date.now(),\n });\n\n if (button.dismiss) {\n removeModal(experienceId);\n }\n\n if (button.url) {\n window.location.href = button.url;\n }\n };\n\n buttonContainer.appendChild(btn);\n });\n\n contentWrapper.appendChild(buttonContainer);\n }\n\n dialog.appendChild(contentWrapper);\n\n // Backdrop dismiss\n if (modalConfig.backdropDismiss !== false) {\n backdrop.onclick = () => removeModal(experienceId);\n }\n\n // Escape key handler\n const handleEscape = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && modalConfig.dismissable !== false) {\n removeModal(experienceId);\n }\n };\n document.addEventListener('keydown', handleEscape);\n\n // Store cleanup for escape listener\n (container as any).__cleanupEscape = () => {\n document.removeEventListener('keydown', handleEscape);\n };\n\n return container;\n };\n\n /**\n * Show a modal experience\n */\n const showModal = (experience: any): void => {\n const experienceId = experience.id;\n\n // Don't show if already showing\n if (activeModals.has(experienceId)) return;\n\n // Hide any existing modals (prevent stacking for better UX)\n if (activeModals.size > 0) {\n const existingIds = Array.from(activeModals.keys());\n for (const id of existingIds) {\n removeModal(id);\n }\n }\n\n // Store currently focused element\n previouslyFocusedElement.set(experienceId, document.activeElement as HTMLElement);\n\n // Render modal\n const modal = renderModal(experienceId, experience.content);\n document.body.appendChild(modal);\n activeModals.set(experienceId, modal);\n\n // Trigger animation after adding to DOM\n const modalConfig = instance.get('modal') || {};\n const animation = modalConfig.animation || 'fade';\n\n if (animation !== 'none') {\n requestAnimationFrame(() => {\n modal.style.opacity = '1';\n\n if (animation === 'slide-up') {\n modal.style.transform = 'translateY(0)';\n }\n });\n }\n\n // Setup focus trap\n const cleanupFocusTrap = createFocusTrap(modal);\n (modal as any).__cleanupFocusTrap = cleanupFocusTrap;\n\n // Emit shown event\n instance.emit('experiences:shown', {\n experienceId,\n timestamp: Date.now(),\n });\n\n // Emit trigger event for context\n instance.emit('trigger:modal', {\n experienceId,\n timestamp: Date.now(),\n shown: true,\n });\n };\n\n /**\n * Remove a modal\n */\n const removeModal = (experienceId: string): void => {\n const modal = activeModals.get(experienceId);\n if (!modal) return;\n\n // Cleanup focus trap\n if ((modal as any).__cleanupFocusTrap) {\n (modal as any).__cleanupFocusTrap();\n }\n\n // Cleanup escape listener\n if ((modal as any).__cleanupEscape) {\n (modal as any).__cleanupEscape();\n }\n\n // Return focus\n const previousElement = previouslyFocusedElement.get(experienceId);\n if (previousElement && document.body.contains(previousElement)) {\n previousElement.focus();\n }\n previouslyFocusedElement.delete(experienceId);\n\n // Remove from DOM\n modal.remove();\n activeModals.delete(experienceId);\n\n // Emit dismissed event\n instance.emit('experiences:dismissed', {\n experienceId,\n timestamp: Date.now(),\n });\n };\n\n /**\n * Check if a modal is showing\n */\n const isShowing = (experienceId?: string): boolean => {\n if (experienceId) {\n return activeModals.has(experienceId);\n }\n return activeModals.size > 0;\n };\n\n /**\n * Show form success or error state\n */\n const showFormState = (experienceId: string, state: 'success' | 'error'): void => {\n const modal = activeModals.get(experienceId);\n if (!modal) return;\n\n const form = modal.querySelector('.xp-modal__form') as HTMLFormElement;\n if (!form) return;\n\n // Get the form config from the experience\n // Note: We need to store this when the modal is created\n const formConfig = (modal as any).__formConfig;\n if (!formConfig) return;\n\n const stateConfig = state === 'success' ? formConfig.successState : formConfig.errorState;\n if (!stateConfig) return;\n\n // Render state element\n const stateEl = renderFormState(state, stateConfig);\n\n // Replace form with state\n form.replaceWith(stateEl);\n\n // Emit state event\n instance.emit('experiences:modal:form:state', {\n experienceId,\n state,\n timestamp: Date.now(),\n });\n };\n\n /**\n * Reset form to initial state\n */\n const resetForm = (experienceId: string): void => {\n const modal = activeModals.get(experienceId);\n if (!modal) return;\n\n const form = modal.querySelector('.xp-modal__form') as HTMLFormElement;\n if (!form) return;\n\n // Reset form element\n form.reset();\n\n // Clear form data\n const data = formData.get(experienceId);\n if (data) {\n Object.keys(data).forEach((key) => {\n data[key] = '';\n });\n }\n\n // Clear all error messages\n const errors = form.querySelectorAll('.xp-form__error');\n errors.forEach((error) => {\n (error as HTMLElement).textContent = '';\n });\n\n // Reset all inputs\n const inputs = form.querySelectorAll('.xp-form__input') as NodeListOf<\n HTMLInputElement | HTMLTextAreaElement\n >;\n inputs.forEach((input) => {\n input.setAttribute('aria-invalid', 'false');\n input.style.cssText = input.style.cssText.replace(getInputErrorStyles(), '');\n });\n };\n\n /**\n * Get current form data\n */\n const getFormData = (experienceId: string): Record<string, string> | null => {\n return formData.get(experienceId) || null;\n };\n\n // Expose public API\n plugin.expose({\n modal: {\n show: showModal,\n remove: removeModal,\n isShowing,\n showFormState,\n resetForm,\n getFormData,\n } as ModalPlugin,\n });\n\n // Auto-show modal experiences when evaluated\n instance.on('experiences:evaluated', (data: any) => {\n const { decision, experience } = data;\n if (decision.show && decision.experienceId && experience) {\n // Check if this is a modal experience (using 'type' property)\n if (experience.type === 'modal') {\n showModal(experience);\n }\n }\n });\n\n // Cleanup on destroy\n instance.on('sdk:destroy', () => {\n activeModals.forEach((_, id) => {\n removeModal(id);\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 instance.on('sdk:destroy', () => {\n cleanup();\n });\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 };\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 instance.on('sdk:destroy', () => {\n cleanup();\n });\n};\n"]}