@active-reach/web-sdk 1.14.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aegis.min.js +1 -1
- package/dist/aegis.min.js.map +1 -1
- package/dist/{analytics-DGt-CSgi.mjs → analytics-CjLItVo2.mjs} +130 -17
- package/dist/analytics-CjLItVo2.mjs.map +1 -0
- package/dist/core/analytics.d.ts +56 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/bootstrap.d.ts +22 -6
- package/dist/core/bootstrap.d.ts.map +1 -1
- package/dist/core/session.d.ts.map +1 -1
- package/dist/ecommerce/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/push/AegisWebPush.js +7 -7
- package/dist/push/AegisWebPush.js.map +1 -1
- package/dist/react.js +2 -2
- package/dist/react.js.map +1 -1
- package/dist/snippet.min.js +1 -1
- package/dist/types/events.d.ts +20 -0
- package/dist/types/events.d.ts.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/analytics-DGt-CSgi.mjs.map +0 -1
package/dist/aegis.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aegis.min.js","sources":["../src/types/config.ts","../src/utils/uuid.ts","../src/utils/logger.ts","../src/utils/identity.ts","../src/core/session.ts","../src/core/queue.ts","../src/core/transport.ts","../src/utils/storage.ts","../src/utils/device.ts","../src/utils/url-parser.ts","../src/plugins/registry.ts","../src/utils/consent.ts","../src/utils/meta-cookies.ts","../src/ecommerce/index.ts","../src/core/rate-limiter.ts","../src/governance/murmur3.ts","../src/governance/bloom-filter.ts","../src/governance/name-governor.ts","../src/governance/trait-governor.ts","../src/core/user-namespace.ts","../src/core/analytics.ts","../src/inapp/renderers/carousel-cards.ts","../src/inapp/renderers/sticky-bar.ts","../src/inapp/renderers/progress-bar.ts","../src/inapp/renderers/coachmark-tour.ts","../src/inapp/AegisInAppManager.ts","../src/inapp/renderers/product-recommendation.ts","../src/widgets/AegisWidgetManager.ts","../src/triggers/TriggerEngine.ts","../src/core/bootstrap.ts","../src/cdn.ts","../src/runtime/AegisMessageRuntime.ts","../src/placements/AegisPlacementManager.ts"],"sourcesContent":["export type CellRegion = 'us-east' | 'us-west' | 'eu-central' | 'ap-south' | 'ap-southeast';\n\nexport interface CellEndpoint {\n region: CellRegion;\n url: string;\n priority: number;\n healthy: boolean;\n}\n\nexport interface AegisConfig {\n write_key: string;\n\n workspace_id?: string;\n \n api_host?: string;\n \n cell_endpoints?: CellEndpoint[];\n \n preferred_region?: CellRegion;\n \n auto_region_detection?: boolean;\n \n batch_size?: number;\n \n batch_interval?: number;\n \n capture_utm?: boolean;\n \n capture_referrer?: boolean;\n \n auto_page_view?: boolean;\n \n session_timeout?: number;\n \n debug?: boolean;\n \n respect_dnt?: boolean;\n \n cross_domain_tracking?: boolean;\n \n cookie_domain?: string;\n \n secure_cookie?: boolean;\n \n enable_offline_mode?: boolean;\n \n max_offline_events?: number;\n \n retry_failed_requests?: boolean;\n \n max_retries?: number;\n \n retry_backoff_multiplier?: number;\n \n request_timeout?: number;\n\n /** Client-side rate limiter — burst (token bucket capacity). Default 100. */\n rate_limit_burst?: number;\n\n /** Client-side rate limiter — sustained tokens per second. Default 20. */\n rate_limit_per_second?: number;\n\n plugins?: string[];\n\n wait_for_consent?: boolean;\n\n default_consent?: {\n analytics?: boolean;\n marketing?: boolean;\n functional?: boolean;\n };\n\n enable_consent_mode?: boolean;\n\n integrate_onetrust?: boolean;\n\n integrate_cookiebot?: boolean;\n\n integrate_google_consent_mode?: boolean;\n\n /** Push notification configuration. When provided, AegisWebPush is initialized. */\n push?: {\n /** VAPID public key for Web Push subscription. */\n vapidPublicKey: string;\n /** Automatically prompt for push permission after SDK init. Default: false. */\n autoPrompt?: boolean;\n /** Delay in ms before showing auto-prompt. Default: 5000. */\n promptDelay?: number;\n /** Path to the service worker file. Default: '/aegis-sw.js'. */\n serviceWorkerPath?: string;\n /** Scope for the service worker registration. Default: '/'. Critical for e-commerce platforms. */\n serviceWorkerScope?: string;\n };\n}\n\nexport interface AegisConfigInternal extends Required<Omit<AegisConfig, 'api_host' | 'cell_endpoints' | 'preferred_region' | 'cookie_domain' | 'plugins' | 'default_consent' | 'workspace_id'>> {\n initialized: boolean;\n workspace_id: string | null;\n api_host: string | null;\n cell_endpoints: CellEndpoint[];\n preferred_region: CellRegion | null;\n cookie_domain: string | null;\n plugins: string[];\n active_cell: CellEndpoint | null;\n default_consent: {\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n };\n}\n\nexport const DEFAULT_CONFIG: Partial<AegisConfigInternal> = {\n workspace_id: null,\n batch_size: 10,\n batch_interval: 1000,\n capture_utm: true,\n capture_referrer: true,\n auto_page_view: false,\n session_timeout: 30,\n debug: false,\n respect_dnt: true,\n cross_domain_tracking: false,\n secure_cookie: true,\n enable_offline_mode: true,\n max_offline_events: 100,\n retry_failed_requests: true,\n max_retries: 3,\n retry_backoff_multiplier: 2,\n request_timeout: 5000,\n rate_limit_burst: 100,\n rate_limit_per_second: 20,\n auto_region_detection: true,\n wait_for_consent: false,\n enable_consent_mode: false,\n integrate_onetrust: false,\n integrate_cookiebot: false,\n integrate_google_consent_mode: false,\n default_consent: {\n analytics: false,\n marketing: false,\n functional: true,\n },\n initialized: false,\n api_host: null,\n cell_endpoints: [],\n preferred_region: null,\n cookie_domain: null,\n plugins: [],\n active_cell: null,\n};\n","export function generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport function generateSessionId(): string {\n return `sess_${Date.now()}_${generateUUID().slice(0, 8)}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${Date.now()}_${generateUUID().slice(0, 8)}`;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nclass Logger {\n private enabled: boolean = false;\n private prefix: string = '[Aegis SDK]';\n\n enable(): void {\n this.enabled = true;\n }\n\n disable(): void {\n this.enabled = false;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n debug(message: string, ...args: any[]): void {\n if (!this.enabled) return;\n console.debug(`${this.prefix} ${message}`, ...args);\n }\n\n info(message: string, ...args: any[]): void {\n if (!this.enabled) return;\n console.info(`${this.prefix} ${message}`, ...args);\n }\n\n warn(message: string, ...args: any[]): void {\n console.warn(`${this.prefix} ${message}`, ...args);\n }\n\n error(message: string, ...args: any[]): void {\n console.error(`${this.prefix} ${message}`, ...args);\n }\n}\n\nexport const logger = new Logger();\n","import { generateUUID } from './uuid';\nimport { Storage } from './storage';\nimport { logger } from './logger';\n\nexport interface IdentityState {\n anonymousId: string;\n userId: string | null;\n traits: Record<string, any>;\n}\n\nexport class Identity {\n private storage: Storage;\n private anonymousId: string;\n private userId: string | null = null;\n private traits: Record<string, any> = {};\n\n constructor(storage: Storage) {\n this.storage = storage;\n this.anonymousId = this.getOrCreateAnonymousId();\n this.loadUserIdentity();\n }\n\n private getOrCreateAnonymousId(): string {\n let id = this.storage.get('anon_id');\n\n if (!id) {\n id = generateUUID();\n this.storage.set('anon_id', id, 365);\n logger.debug('Created new anonymous ID:', id);\n } else {\n logger.debug('Loaded existing anonymous ID:', id);\n }\n\n return id;\n }\n\n private loadUserIdentity(): void {\n const storedUserId = this.storage.get('user_id');\n const storedTraits = this.storage.get('user_traits');\n\n if (storedUserId) {\n this.userId = storedUserId;\n logger.debug('Loaded user ID:', storedUserId);\n }\n\n if (storedTraits) {\n try {\n this.traits = JSON.parse(storedTraits);\n logger.debug('Loaded user traits:', this.traits);\n } catch (error) {\n logger.warn('Failed to parse stored traits:', error);\n this.traits = {};\n }\n }\n }\n\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n setUserId(userId: string, traits?: Record<string, any>): void {\n this.userId = userId;\n this.storage.set('user_id', userId, 365);\n logger.info('User identified:', userId);\n\n if (traits) {\n this.setTraits(traits);\n }\n }\n\n getUserId(): string | null {\n return this.userId;\n }\n\n setTraits(traits: Record<string, any>): void {\n this.traits = { ...this.traits, ...traits };\n this.storage.set('user_traits', JSON.stringify(this.traits), 365);\n logger.debug('User traits updated:', this.traits);\n }\n\n getTraits(): Record<string, any> {\n return { ...this.traits };\n }\n\n reset(): void {\n this.userId = null;\n this.traits = {};\n this.anonymousId = generateUUID();\n\n this.storage.remove('user_id');\n this.storage.remove('user_traits');\n this.storage.set('anon_id', this.anonymousId, 365);\n\n logger.info('Identity reset, new anonymous ID:', this.anonymousId);\n }\n\n alias(newUserId: string): { previousId: string; newUserId: string } {\n const previousId = this.userId || this.anonymousId;\n\n this.setUserId(newUserId);\n\n logger.info('User aliased:', { previousId, newUserId });\n\n return { previousId, newUserId };\n }\n\n getState(): IdentityState {\n return {\n anonymousId: this.anonymousId,\n userId: this.userId,\n traits: this.getTraits(),\n };\n }\n}\n","import { Storage } from '../utils/storage';\nimport { generateSessionId } from '../utils/uuid';\nimport { logger } from '../utils/logger';\nimport { AdClickIDs } from '../utils/url-parser';\n\nexport interface SessionState {\n sessionId: string;\n startTime: number;\n lastActivityTime: number;\n eventCount: number;\n adClickIDs?: AdClickIDs;\n landingPage?: string;\n}\n\nexport class SessionManager {\n private storage: Storage;\n private sessionId: string;\n private sessionTimeout: number;\n private lastActivityTime: number;\n private sessionStartTime: number;\n private eventCount: number = 0;\n private checkInterval: number | null = null;\n private activityThrottle: number = 1000;\n private lastActivityUpdate: number = 0;\n private adClickIDs: AdClickIDs = {};\n private landingPage?: string;\n\n constructor(storage: Storage, sessionTimeoutMinutes: number = 30) {\n this.storage = storage;\n this.sessionTimeout = sessionTimeoutMinutes * 60 * 1000;\n this.lastActivityTime = Date.now();\n this.sessionStartTime = Date.now();\n\n const session = this.loadSession();\n if (session) {\n this.sessionId = session.sessionId;\n this.sessionStartTime = session.startTime;\n this.lastActivityTime = session.lastActivityTime;\n this.eventCount = session.eventCount;\n this.adClickIDs = session.adClickIDs || {};\n this.landingPage = session.landingPage;\n logger.debug('Session loaded:', session);\n } else {\n this.sessionId = this.createNewSession();\n }\n\n this.startActivityTracking();\n this.startExpiryCheck();\n }\n\n private loadSession(): SessionState | null {\n const sessionData = this.storage.get('session');\n\n if (!sessionData) {\n return null;\n }\n\n try {\n const session: SessionState = JSON.parse(sessionData);\n const elapsed = Date.now() - session.lastActivityTime;\n\n if (elapsed < this.sessionTimeout) {\n return session;\n } else {\n logger.debug('Session expired, creating new session');\n this.storage.remove('session');\n return null;\n }\n } catch (error) {\n logger.warn('Failed to parse session data:', error);\n this.storage.remove('session');\n return null;\n }\n }\n\n private createNewSession(): string {\n const newSessionId = generateSessionId();\n this.sessionStartTime = Date.now();\n this.lastActivityTime = Date.now();\n this.eventCount = 0;\n\n this.persistSession();\n logger.info('New session created:', newSessionId);\n\n return newSessionId;\n }\n\n private persistSession(): void {\n const sessionData: SessionState = {\n sessionId: this.sessionId,\n startTime: this.sessionStartTime,\n lastActivityTime: this.lastActivityTime,\n eventCount: this.eventCount,\n adClickIDs: this.adClickIDs,\n landingPage: this.landingPage,\n };\n\n this.storage.set('session', JSON.stringify(sessionData));\n }\n\n private startActivityTracking(): void {\n const events = ['click', 'scroll', 'keypress', 'mousemove', 'touchstart'];\n\n const activityHandler = () => this.onActivity();\n\n events.forEach((event) => {\n window.addEventListener(event, activityHandler, { passive: true });\n });\n\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.onActivity();\n }\n });\n }\n\n private startExpiryCheck(): void {\n this.checkInterval = window.setInterval(() => {\n this.checkSessionExpiry();\n }, 60000);\n }\n\n private onActivity(): void {\n const now = Date.now();\n const timeSinceLastUpdate = now - this.lastActivityUpdate;\n\n if (timeSinceLastUpdate < this.activityThrottle) {\n return;\n }\n\n this.lastActivityUpdate = now;\n const timeSinceLastActivity = now - this.lastActivityTime;\n\n if (timeSinceLastActivity > this.sessionTimeout) {\n logger.info('Session expired due to inactivity, creating new session');\n this.sessionId = this.createNewSession();\n } else {\n this.lastActivityTime = now;\n this.persistSession();\n }\n }\n\n private checkSessionExpiry(): void {\n const elapsed = Date.now() - this.lastActivityTime;\n\n if (elapsed > this.sessionTimeout) {\n logger.info('Session expired during check, creating new session');\n this.sessionId = this.createNewSession();\n }\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n incrementEventCount(): void {\n this.eventCount++;\n this.lastActivityTime = Date.now();\n this.persistSession();\n }\n\n getSessionDuration(): number {\n return Date.now() - this.sessionStartTime;\n }\n\n getEventCount(): number {\n return this.eventCount;\n }\n\n getState(): SessionState {\n return {\n sessionId: this.sessionId,\n startTime: this.sessionStartTime,\n lastActivityTime: this.lastActivityTime,\n eventCount: this.eventCount,\n adClickIDs: this.adClickIDs,\n landingPage: this.landingPage,\n };\n }\n\n setAdClickIDs(adClickIDs: AdClickIDs, landingPage?: string): void {\n if (Object.keys(adClickIDs).length > 0) {\n this.adClickIDs = { ...this.adClickIDs, ...adClickIDs };\n if (landingPage) {\n this.landingPage = landingPage;\n }\n this.persistSession();\n logger.info('Ad click IDs stored in session:', this.adClickIDs);\n }\n }\n\n getAdClickIDs(): AdClickIDs {\n return this.adClickIDs;\n }\n\n getLandingPage(): string | undefined {\n return this.landingPage;\n }\n\n destroy(): void {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n }\n}\n","import { AegisEvent } from '../types/events';\nimport { Transport } from './transport';\nimport { Storage } from '../utils/storage';\nimport { logger } from '../utils/logger';\n\nexport interface QueueConfig {\n batchSize: number;\n batchInterval: number;\n enableOfflineMode: boolean;\n maxOfflineEvents: number;\n}\n\nexport class EventQueue {\n private buffer: AegisEvent[] = [];\n private config: QueueConfig;\n private transport: Transport;\n private storage: Storage;\n private flushInterval: number | null = null;\n private isFlushing: boolean = false;\n private offlineQueue: AegisEvent[] = [];\n private isOnline: boolean = navigator.onLine;\n\n constructor(config: QueueConfig, transport: Transport, storage: Storage) {\n this.config = config;\n this.transport = transport;\n this.storage = storage;\n\n if (config.enableOfflineMode) {\n this.loadOfflineQueue();\n this.setupOnlineListener();\n }\n\n this.startFlushTimer();\n this.setupUnloadHandlers();\n }\n\n private loadOfflineQueue(): void {\n const offlineData = this.storage.get('offline_queue');\n\n if (!offlineData) {\n return;\n }\n\n try {\n const events = JSON.parse(offlineData) as AegisEvent[];\n\n if (Array.isArray(events) && events.length > 0) {\n this.offlineQueue = events.slice(0, this.config.maxOfflineEvents);\n logger.info(`Loaded ${this.offlineQueue.length} offline events`);\n\n if (this.isOnline) {\n this.flushOfflineQueue();\n }\n }\n } catch (error) {\n logger.warn('Failed to load offline queue:', error);\n this.storage.remove('offline_queue');\n }\n }\n\n private saveOfflineQueue(): void {\n if (this.offlineQueue.length === 0) {\n this.storage.remove('offline_queue');\n return;\n }\n\n try {\n const data = JSON.stringify(this.offlineQueue);\n this.storage.set('offline_queue', data);\n logger.debug(`Saved ${this.offlineQueue.length} events to offline queue`);\n } catch (error) {\n logger.warn('Failed to save offline queue:', error);\n }\n }\n\n private setupOnlineListener(): void {\n window.addEventListener('online', () => {\n logger.info('Connection restored');\n this.isOnline = true;\n this.flushOfflineQueue();\n this.flush();\n });\n\n window.addEventListener('offline', () => {\n logger.warn('Connection lost, entering offline mode');\n this.isOnline = false;\n });\n }\n\n private async flushOfflineQueue(): Promise<void> {\n if (this.offlineQueue.length === 0) {\n return;\n }\n\n logger.info(`Flushing ${this.offlineQueue.length} offline events`);\n\n const events = [...this.offlineQueue];\n this.offlineQueue = [];\n this.storage.remove('offline_queue');\n\n const result = await this.transport.send(events);\n\n if (!result.success) {\n logger.warn('Failed to send offline events, re-queueing');\n this.offlineQueue = [...events, ...this.offlineQueue].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n } else {\n logger.info('Offline events sent successfully');\n }\n }\n\n push(event: AegisEvent): void {\n if (!this.isOnline && this.config.enableOfflineMode) {\n this.addToOfflineQueue(event);\n return;\n }\n\n this.buffer.push(event);\n logger.debug('Event queued:', event.type, event);\n\n if (this.buffer.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private addToOfflineQueue(event: AegisEvent): void {\n if (this.offlineQueue.length >= this.config.maxOfflineEvents) {\n logger.warn('Offline queue full, dropping oldest event');\n this.offlineQueue.shift();\n }\n\n this.offlineQueue.push(event);\n this.saveOfflineQueue();\n logger.debug('Event added to offline queue');\n }\n\n async flush(): Promise<void> {\n if (this.isFlushing) {\n logger.debug('Flush already in progress, skipping');\n return;\n }\n\n if (this.buffer.length === 0) {\n return;\n }\n\n this.isFlushing = true;\n\n const events = [...this.buffer];\n this.buffer = [];\n\n logger.info(`Flushing ${events.length} events`);\n\n try {\n const result = await this.transport.send(events);\n\n if (!result.success) {\n logger.error('Failed to send events:', result.error);\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...events].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n logger.info('Events saved to offline queue');\n } else {\n this.buffer.unshift(...events);\n logger.warn('Events re-queued for retry');\n }\n } else {\n logger.info('Events sent successfully');\n }\n } catch (error) {\n logger.error('Flush error:', error);\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...events].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n }\n\n this.flushInterval = window.setInterval(() => {\n this.flush();\n }, this.config.batchInterval);\n\n logger.debug(\n `Flush timer started with interval: ${this.config.batchInterval}ms`\n );\n }\n\n private setupUnloadHandlers(): void {\n const flushBeforeUnload = () => {\n if (this.buffer.length > 0 || this.offlineQueue.length > 0) {\n this.persistBufferToOfflineQueue();\n this.flush();\n }\n };\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n flushBeforeUnload();\n }\n });\n\n window.addEventListener('beforeunload', flushBeforeUnload);\n\n window.addEventListener('pagehide', flushBeforeUnload);\n }\n\n private persistBufferToOfflineQueue(): void {\n if (this.buffer.length === 0) {\n return;\n }\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...this.buffer].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n logger.debug('Buffer persisted to offline queue before unload');\n }\n }\n\n getQueueSize(): number {\n return this.buffer.length;\n }\n\n getOfflineQueueSize(): number {\n return this.offlineQueue.length;\n }\n\n clear(): void {\n this.buffer = [];\n this.offlineQueue = [];\n this.storage.remove('offline_queue');\n logger.info('Queue cleared');\n }\n\n destroy(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n this.flush();\n }\n}\n","import { AegisEvent, BatchPayload, BatchResponse } from '../types/events';\nimport { CellEndpoint, CellRegion } from '../types/config';\nimport { logger } from '../utils/logger';\nimport { Storage } from '../utils/storage';\n\nexport interface TransportConfig {\n writeKey: string;\n apiHost: string | null;\n cellEndpoints: CellEndpoint[];\n preferredRegion: CellRegion | null;\n autoRegionDetection: boolean;\n requestTimeout: number;\n retryFailedRequests: boolean;\n maxRetries: number;\n retryBackoffMultiplier: number;\n}\n\nexport interface SendResult {\n success: boolean;\n response?: BatchResponse;\n error?: Error;\n endpoint?: string;\n}\n\nexport class Transport {\n private config: TransportConfig;\n private storage: Storage;\n private activeCell: CellEndpoint | null = null;\n private healthCheckInterval: number | null = null;\n private regionLatencyMap: Map<string, number> = new Map();\n\n constructor(config: TransportConfig, storage: Storage) {\n this.config = config;\n this.storage = storage;\n\n if (config.cellEndpoints.length > 0) {\n this.initializeCellularArchitecture();\n }\n }\n\n private async initializeCellularArchitecture(): Promise<void> {\n const cachedCell = this.loadCachedCell();\n\n if (cachedCell && this.isCellHealthy(cachedCell)) {\n this.activeCell = cachedCell;\n logger.info('Using cached cell:', cachedCell);\n } else {\n await this.selectOptimalCell();\n }\n\n this.startHealthChecks();\n }\n\n private loadCachedCell(): CellEndpoint | null {\n const cached = this.storage.get('active_cell');\n\n if (!cached) {\n return null;\n }\n\n try {\n return JSON.parse(cached) as CellEndpoint;\n } catch (error) {\n logger.warn('Failed to parse cached cell:', error);\n return null;\n }\n }\n\n private saveCachedCell(cell: CellEndpoint): void {\n this.storage.set('active_cell', JSON.stringify(cell), 7);\n }\n\n private isCellHealthy(cell: CellEndpoint): boolean {\n return (\n cell.healthy &&\n this.config.cellEndpoints.some(\n (c) => c.region === cell.region && c.url === cell.url\n )\n );\n }\n\n private async selectOptimalCell(): Promise<void> {\n logger.info('Selecting optimal cell...');\n\n if (this.config.preferredRegion) {\n const preferredCell = this.config.cellEndpoints.find(\n (cell) => cell.region === this.config.preferredRegion && cell.healthy\n );\n\n if (preferredCell) {\n this.activeCell = preferredCell;\n this.saveCachedCell(preferredCell);\n logger.info('Using preferred region cell:', preferredCell);\n return;\n }\n }\n\n if (this.config.autoRegionDetection) {\n await this.detectOptimalRegion();\n } else {\n this.selectCellByPriority();\n }\n }\n\n private async detectOptimalRegion(): Promise<void> {\n const healthyCells = this.config.cellEndpoints.filter((cell) => cell.healthy);\n\n if (healthyCells.length === 0) {\n logger.error('No healthy cells available');\n return;\n }\n\n const latencyPromises = healthyCells.map(async (cell) => {\n const latency = await this.measureLatency(cell);\n this.regionLatencyMap.set(cell.region, latency);\n return { cell, latency };\n });\n\n const results = await Promise.all(latencyPromises);\n results.sort((a, b) => a.latency - b.latency);\n\n const optimalCell = results[0].cell;\n this.activeCell = optimalCell;\n this.saveCachedCell(optimalCell);\n\n logger.info('Optimal cell selected:', {\n region: optimalCell.region,\n latency: results[0].latency,\n });\n }\n\n private async measureLatency(cell: CellEndpoint): Promise<number> {\n const startTime = performance.now();\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 2000);\n\n const response = await fetch(`${cell.url}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return performance.now() - startTime;\n } else {\n return Infinity;\n }\n } catch (error) {\n logger.warn(`Health check failed for ${cell.region}:`, error);\n return Infinity;\n }\n }\n\n private selectCellByPriority(): void {\n const sortedCells = [...this.config.cellEndpoints]\n .filter((cell) => cell.healthy)\n .sort((a, b) => a.priority - b.priority);\n\n if (sortedCells.length > 0) {\n this.activeCell = sortedCells[0];\n this.saveCachedCell(sortedCells[0]);\n logger.info('Cell selected by priority:', sortedCells[0]);\n } else {\n logger.error('No healthy cells available');\n }\n }\n\n private startHealthChecks(): void {\n this.healthCheckInterval = window.setInterval(() => {\n this.performHealthChecks();\n }, 60000);\n }\n\n private async performHealthChecks(): Promise<void> {\n const healthPromises = this.config.cellEndpoints.map(async (cell) => {\n const isHealthy = await this.checkCellHealth(cell);\n cell.healthy = isHealthy;\n return { cell, isHealthy };\n });\n\n const results = await Promise.all(healthPromises);\n\n if (this.activeCell && !this.activeCell.healthy) {\n logger.warn('Active cell unhealthy, selecting new cell');\n await this.selectOptimalCell();\n }\n\n logger.debug('Health check results:', results);\n }\n\n private async checkCellHealth(cell: CellEndpoint): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 3000);\n\n const response = await fetch(`${cell.url}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.ok;\n } catch (error) {\n return false;\n }\n }\n\n async send(events: AegisEvent[]): Promise<SendResult> {\n if (events.length === 0) {\n return { success: true };\n }\n\n const endpoint = this.getActiveEndpoint();\n const payload = this.buildPayload(events);\n\n logger.debug('Sending batch:', { endpoint, eventCount: events.length });\n\n if (document.visibilityState === 'hidden' && typeof navigator.sendBeacon === 'function') {\n const result = this.sendViaBeacon(endpoint, payload);\n return Promise.resolve(result);\n } else {\n return this.sendViaFetch(endpoint, payload);\n }\n }\n\n private getActiveEndpoint(): string {\n if (this.activeCell) {\n return `${this.activeCell.url}/v1/batch`;\n }\n\n if (this.config.apiHost) {\n return `${this.config.apiHost}/v1/batch`;\n }\n\n throw new Error('No active endpoint available');\n }\n\n private buildPayload(events: AegisEvent[]): BatchPayload {\n return {\n batch: events,\n sentAt: new Date().toISOString(),\n writeKey: this.config.writeKey,\n context: this.activeCell\n ? {\n cell: {\n region: this.activeCell.region,\n endpoint: this.activeCell.url,\n },\n }\n : undefined,\n };\n }\n\n private sendViaBeacon(endpoint: string, payload: BatchPayload): SendResult | Promise<SendResult> {\n try {\n const blob = new Blob([JSON.stringify(payload)], {\n type: 'application/json',\n });\n\n const sent = navigator.sendBeacon(endpoint, blob);\n\n if (sent) {\n logger.debug('Batch sent via Beacon');\n return { success: true, endpoint };\n } else {\n logger.warn('Beacon send failed, falling back to fetch');\n return this.sendViaFetch(endpoint, payload);\n }\n } catch (error) {\n logger.error('Beacon send error:', error);\n return {\n success: false,\n error: error as Error,\n endpoint,\n };\n }\n }\n\n private async sendViaFetch(\n endpoint: string,\n payload: BatchPayload,\n attempt: number = 1\n ): Promise<SendResult> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n this.config.requestTimeout\n );\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body: JSON.stringify(payload),\n keepalive: true,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const result: BatchResponse = await response.json();\n logger.debug('Batch sent successfully:', result);\n return { success: true, response: result, endpoint };\n } else {\n const errorText = await response.text();\n const error = new Error(\n `HTTP ${response.status}: ${errorText || response.statusText}`\n );\n\n logger.error('Batch send failed:', error);\n\n if (this.shouldRetry(response.status, attempt)) {\n return this.retryRequest(endpoint, payload, attempt);\n }\n\n return { success: false, error, endpoint };\n }\n } catch (error) {\n logger.error('Fetch error:', error);\n\n if (this.shouldRetry(0, attempt)) {\n return this.retryRequest(endpoint, payload, attempt);\n }\n\n return {\n success: false,\n error: error as Error,\n endpoint,\n };\n }\n }\n\n private shouldRetry(statusCode: number, attempt: number): boolean {\n if (!this.config.retryFailedRequests) {\n return false;\n }\n\n if (attempt >= this.config.maxRetries) {\n return false;\n }\n\n if (statusCode === 0) {\n return true;\n }\n\n return statusCode >= 500 || statusCode === 429;\n }\n\n private async retryRequest(\n endpoint: string,\n payload: BatchPayload,\n attempt: number\n ): Promise<SendResult> {\n const delay =\n Math.pow(this.config.retryBackoffMultiplier, attempt) * 1000;\n\n logger.info(`Retrying request in ${delay}ms (attempt ${attempt + 1})`);\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n return this.sendViaFetch(endpoint, payload, attempt + 1);\n }\n\n getActiveCell(): CellEndpoint | null {\n return this.activeCell;\n }\n\n getRegionLatencies(): Map<string, number> {\n return new Map(this.regionLatencyMap);\n }\n\n destroy(): void {\n if (this.healthCheckInterval) {\n clearInterval(this.healthCheckInterval);\n this.healthCheckInterval = null;\n }\n }\n}\n","const STORAGE_PREFIX = 'aegis_';\n\nexport interface StorageOptions {\n cookieDomain?: string | null;\n secureCookie?: boolean;\n crossDomain?: boolean;\n}\n\nexport class Storage {\n private useLocalStorage: boolean;\n private options: StorageOptions;\n\n constructor(options: StorageOptions = {}) {\n this.options = options;\n this.useLocalStorage = this.isLocalStorageAvailable();\n }\n\n private isLocalStorageAvailable(): boolean {\n try {\n const test = '__aegis_test__';\n localStorage.setItem(test, test);\n localStorage.removeItem(test);\n return true;\n } catch {\n return false;\n }\n }\n\n set(key: string, value: string, expiryDays: number = 365): void {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n const item = {\n value,\n expiry: Date.now() + expiryDays * 24 * 60 * 60 * 1000,\n };\n localStorage.setItem(fullKey, JSON.stringify(item));\n } catch (error) {\n console.warn('[Aegis Storage] localStorage.setItem failed:', error);\n }\n }\n\n this.setCookie(fullKey, value, expiryDays);\n }\n\n get(key: string): string | null {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n const itemStr = localStorage.getItem(fullKey);\n if (itemStr) {\n const item = JSON.parse(itemStr);\n if (Date.now() < item.expiry) {\n return item.value;\n } else {\n localStorage.removeItem(fullKey);\n }\n }\n } catch (error) {\n console.warn('[Aegis Storage] localStorage.getItem failed:', error);\n }\n }\n\n return this.getCookie(fullKey);\n }\n\n remove(key: string): void {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n localStorage.removeItem(fullKey);\n } catch (error) {\n console.warn('[Aegis Storage] localStorage.removeItem failed:', error);\n }\n }\n\n this.deleteCookie(fullKey);\n }\n\n clear(): void {\n if (this.useLocalStorage) {\n try {\n const keys = Object.keys(localStorage);\n keys.forEach((key) => {\n if (key.startsWith(STORAGE_PREFIX)) {\n localStorage.removeItem(key);\n }\n });\n } catch (error) {\n console.warn('[Aegis Storage] localStorage.clear failed:', error);\n }\n }\n\n const cookies = document.cookie.split(';');\n cookies.forEach((cookie) => {\n const key = cookie.split('=')[0].trim();\n if (key.startsWith(STORAGE_PREFIX)) {\n this.deleteCookie(key);\n }\n });\n }\n\n private setCookie(name: string, value: string, days: number): void {\n try {\n const date = new Date();\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\n const expires = `expires=${date.toUTCString()}`;\n \n let cookieStr = `${name}=${value};${expires};path=/`;\n \n if (this.options.secureCookie && window.location.protocol === 'https:') {\n cookieStr += ';Secure';\n }\n \n cookieStr += ';SameSite=Lax';\n \n if (this.options.cookieDomain) {\n cookieStr += `;domain=${this.options.cookieDomain}`;\n } else if (this.options.crossDomain) {\n const domain = this.getRootDomain();\n if (domain) {\n cookieStr += `;domain=${domain}`;\n }\n }\n \n document.cookie = cookieStr;\n } catch (error) {\n console.warn('[Aegis Storage] setCookie failed:', error);\n }\n }\n\n private getCookie(name: string): string | null {\n try {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n \n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) === ' ') {\n c = c.substring(1, c.length);\n }\n if (c.indexOf(nameEQ) === 0) {\n return c.substring(nameEQ.length, c.length);\n }\n }\n } catch (error) {\n console.warn('[Aegis Storage] getCookie failed:', error);\n }\n \n return null;\n }\n\n private deleteCookie(name: string): void {\n try {\n let cookieStr = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`;\n \n if (this.options.cookieDomain) {\n cookieStr += `;domain=${this.options.cookieDomain}`;\n } else if (this.options.crossDomain) {\n const domain = this.getRootDomain();\n if (domain) {\n cookieStr += `;domain=${domain}`;\n }\n }\n \n document.cookie = cookieStr;\n } catch (error) {\n console.warn('[Aegis Storage] deleteCookie failed:', error);\n }\n }\n\n private getRootDomain(): string | null {\n try {\n const hostname = window.location.hostname;\n \n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(hostname)) {\n return null;\n }\n \n if (hostname === 'localhost') {\n return null;\n }\n \n const parts = hostname.split('.');\n \n if (parts.length <= 2) {\n return `.${hostname}`;\n }\n \n return `.${parts.slice(-2).join('.')}`;\n } catch (error) {\n console.warn('[Aegis Storage] getRootDomain failed:', error);\n return null;\n }\n }\n}\n","import { EventContext } from '../types/events';\nimport { parseUTMParameters } from './url-parser';\nimport { AegisConfigInternal } from '../types/config';\nimport type { SessionManager } from '../core/session';\n\nexport function buildContext(config: AegisConfigInternal, session?: SessionManager): EventContext {\n const utm = config.capture_utm ? parseUTMParameters() : undefined;\n const device = detectDevice();\n const os = detectOS();\n const browser = detectBrowser();\n const network = detectNetworkInfo();\n\n const adClickIDs = session?.getAdClickIDs();\n const landingPage = session?.getLandingPage();\n\n return {\n library: {\n name: '@active-reach/web-sdk',\n version: '1.0.0',\n },\n page: {\n path: window.location.pathname,\n referrer: config.capture_referrer ? document.referrer : '',\n search: window.location.search,\n title: document.title,\n url: window.location.href,\n hash: window.location.hash,\n },\n userAgent: navigator.userAgent,\n locale: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n screen: {\n width: window.screen.width,\n height: window.screen.height,\n density: window.devicePixelRatio || 1,\n },\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n ...(utm && Object.keys(utm).length > 0 && { campaign: utm }),\n ...(adClickIDs && Object.keys(adClickIDs).length > 0 && { adClickIDs }),\n ...(landingPage && { landingPage }),\n ...(network && { network }),\n ...(device && { device }),\n ...(os && { os }),\n ...(browser && { browser }),\n ...(config.active_cell && {\n cell: {\n region: config.active_cell.region,\n endpoint: config.active_cell.url,\n },\n }),\n };\n}\n\nfunction detectDevice(): EventContext['device'] {\n const ua = navigator.userAgent;\n\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\n return { type: 'tablet' };\n }\n\n if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\n return { type: 'mobile' };\n }\n\n return { type: 'desktop' };\n}\n\nfunction detectOS(): EventContext['os'] {\n const ua = navigator.userAgent;\n\n if (/Windows NT 10/i.test(ua)) return { name: 'Windows', version: '10' };\n if (/Windows NT 6.3/i.test(ua)) return { name: 'Windows', version: '8.1' };\n if (/Windows NT 6.2/i.test(ua)) return { name: 'Windows', version: '8' };\n if (/Windows NT 6.1/i.test(ua)) return { name: 'Windows', version: '7' };\n if (/Windows/i.test(ua)) return { name: 'Windows', version: 'Unknown' };\n\n if (/Mac OS X (\\d+)[._](\\d+)/.test(ua)) {\n const match = ua.match(/Mac OS X (\\d+)[._](\\d+)/);\n return { name: 'macOS', version: `${match![1]}.${match![2]}` };\n }\n if (/Mac/i.test(ua)) return { name: 'macOS', version: 'Unknown' };\n\n if (/Android (\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Android (\\d+\\.\\d+)/);\n return { name: 'Android', version: match![1] };\n }\n if (/Android/i.test(ua)) return { name: 'Android', version: 'Unknown' };\n\n if (/iPhone OS (\\d+)_(\\d+)/.test(ua)) {\n const match = ua.match(/iPhone OS (\\d+)_(\\d+)/);\n return { name: 'iOS', version: `${match![1]}.${match![2]}` };\n }\n if (/iPad.*OS (\\d+)_(\\d+)/.test(ua)) {\n const match = ua.match(/iPad.*OS (\\d+)_(\\d+)/);\n return { name: 'iOS', version: `${match![1]}.${match![2]}` };\n }\n if (/iPhone|iPad/i.test(ua)) return { name: 'iOS', version: 'Unknown' };\n\n if (/Linux/i.test(ua)) return { name: 'Linux', version: 'Unknown' };\n\n return { name: 'Unknown', version: 'Unknown' };\n}\n\nfunction detectBrowser(): EventContext['browser'] {\n const ua = navigator.userAgent;\n\n if (/Edg\\/(\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Edg\\/(\\d+\\.\\d+)/);\n return { name: 'Edge', version: match![1] };\n }\n\n if (/Chrome\\/(\\d+\\.\\d+)/.test(ua) && !/Edg/.test(ua)) {\n const match = ua.match(/Chrome\\/(\\d+\\.\\d+)/);\n return { name: 'Chrome', version: match![1] };\n }\n\n if (/Firefox\\/(\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Firefox\\/(\\d+\\.\\d+)/);\n return { name: 'Firefox', version: match![1] };\n }\n\n if (/Safari\\/(\\d+\\.\\d+)/.test(ua) && !/Chrome/.test(ua)) {\n const match = ua.match(/Version\\/(\\d+\\.\\d+)/);\n return { name: 'Safari', version: match ? match[1] : 'Unknown' };\n }\n\n if (/MSIE (\\d+\\.\\d+)/.test(ua) || /Trident\\//.test(ua)) {\n const match = ua.match(/MSIE (\\d+\\.\\d+)/) || ua.match(/rv:(\\d+\\.\\d+)/);\n return { name: 'Internet Explorer', version: match ? match[1] : 'Unknown' };\n }\n\n return { name: 'Unknown', version: 'Unknown' };\n}\n\nfunction detectNetworkInfo(): EventContext['network'] | undefined {\n try {\n const connection =\n (navigator as any).connection ||\n (navigator as any).mozConnection ||\n (navigator as any).webkitConnection;\n\n if (connection) {\n return {\n effectiveType: connection.effectiveType,\n downlink: connection.downlink,\n rtt: connection.rtt,\n };\n }\n } catch (error) {\n console.warn('[Aegis Device] detectNetworkInfo failed:', error);\n }\n\n return undefined;\n}\n\nexport function isBot(): boolean {\n const ua = navigator.userAgent.toLowerCase();\n const botPatterns = [\n 'bot',\n 'crawl',\n 'spider',\n 'slurp',\n 'mediapartners',\n 'googlebot',\n 'bingbot',\n 'yahoo',\n 'duckduckbot',\n 'baiduspider',\n 'yandex',\n 'facebookexternalhit',\n 'linkedinbot',\n 'twitterbot',\n 'slackbot',\n 'telegrambot',\n 'whatsapp',\n 'pingdom',\n 'uptimerobot',\n ];\n\n return botPatterns.some((pattern) => ua.includes(pattern));\n}\n","export interface UTMParams {\n source?: string;\n medium?: string;\n campaign?: string;\n term?: string;\n content?: string;\n name?: string;\n}\n\nexport function parseUTMParameters(url?: string): UTMParams {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const utm: UTMParams = {};\n\n const utmKeys: Array<keyof UTMParams> = [\n 'source',\n 'medium',\n 'campaign',\n 'term',\n 'content',\n 'name',\n ];\n\n utmKeys.forEach((key) => {\n const value = searchParams.get(`utm_${key}`);\n if (value) {\n utm[key] = value;\n }\n });\n\n return utm;\n } catch (error) {\n console.warn('[Aegis URL Parser] parseUTMParameters failed:', error);\n return {};\n }\n}\n\nexport function parseQueryParameters(url?: string): Record<string, string> {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const params: Record<string, string> = {};\n\n searchParams.forEach((value, key) => {\n params[key] = value;\n });\n\n return params;\n } catch (error) {\n console.warn('[Aegis URL Parser] parseQueryParameters failed:', error);\n return {};\n }\n}\n\nexport function getCanonicalURL(): string {\n try {\n const canonical = document.querySelector('link[rel=\"canonical\"]');\n if (canonical && canonical.getAttribute('href')) {\n return canonical.getAttribute('href')!;\n }\n \n return window.location.href;\n } catch (error) {\n console.warn('[Aegis URL Parser] getCanonicalURL failed:', error);\n return window.location.href;\n }\n}\n\nexport function stripQueryParameters(url: string): string {\n try {\n const urlObj = new URL(url);\n return `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;\n } catch (error) {\n console.warn('[Aegis URL Parser] stripQueryParameters failed:', error);\n return url;\n }\n}\n\nexport interface AdClickIDs {\n gclid?: string;\n fbclid?: string;\n msclkid?: string;\n ctwa_clid?: string;\n ttclid?: string;\n li_fat_id?: string;\n}\n\nexport function parseAdClickIDs(url?: string): AdClickIDs {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const adClickIDs: AdClickIDs = {};\n\n const clickIDKeys: Array<keyof AdClickIDs> = [\n 'gclid',\n 'fbclid',\n 'msclkid',\n 'ctwa_clid',\n 'ttclid',\n 'li_fat_id',\n ];\n\n clickIDKeys.forEach((key) => {\n const value = searchParams.get(key);\n if (value) {\n adClickIDs[key] = value;\n }\n });\n\n return adClickIDs;\n } catch (error) {\n console.warn('[Aegis URL Parser] parseAdClickIDs failed:', error);\n return {};\n }\n}\n\nexport function hasAdClickIDs(adClickIDs: AdClickIDs): boolean {\n return Object.keys(adClickIDs).length > 0;\n}\n","import { Plugin } from '../types/plugin';\nimport { logger } from '../utils/logger';\n\nexport class PluginRegistry {\n private plugins: Map<string, Plugin> = new Map();\n\n register(plugin: Plugin): void {\n if (this.plugins.has(plugin.name)) {\n logger.warn(`Plugin \"${plugin.name}\" already registered, skipping`);\n return;\n }\n\n this.plugins.set(plugin.name, plugin);\n logger.info(`Plugin registered: ${plugin.name} v${plugin.version}`);\n }\n\n unregister(pluginName: string): void {\n const plugin = this.plugins.get(pluginName);\n\n if (!plugin) {\n logger.warn(`Plugin \"${pluginName}\" not found`);\n return;\n }\n\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch (error) {\n logger.error(`Error destroying plugin \"${pluginName}\":`, error);\n }\n }\n\n this.plugins.delete(pluginName);\n logger.info(`Plugin unregistered: ${pluginName}`);\n }\n\n get(pluginName: string): Plugin | undefined {\n return this.plugins.get(pluginName);\n }\n\n getAll(): Plugin[] {\n return Array.from(this.plugins.values());\n }\n\n async executeHook<T = any>(\n hookName: keyof Plugin,\n ...args: any[]\n ): Promise<T | undefined> {\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n try {\n const result = await (hook as Function).apply(plugin, args);\n\n if (result !== undefined) {\n return result as T;\n }\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, args });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n }\n }\n }\n\n return undefined;\n }\n\n async executeHookChain<T = any>(\n hookName: keyof Plugin,\n initialValue: T,\n ...additionalArgs: any[]\n ): Promise<T> {\n let value = initialValue;\n\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n try {\n const result = await (hook as Function).apply(plugin, [\n value,\n ...additionalArgs,\n ]);\n\n if (result !== undefined && result !== null) {\n value = result;\n }\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, value, additionalArgs });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n }\n }\n }\n\n return value;\n }\n\n async executeHookParallel<T = any>(\n hookName: keyof Plugin,\n ...args: any[]\n ): Promise<T[]> {\n const promises: Promise<T>[] = [];\n\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n const promise = (async () => {\n try {\n return await (hook as Function).apply(plugin, args);\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, args });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n\n return undefined;\n }\n })();\n\n promises.push(promise);\n }\n }\n\n const results = await Promise.all(promises);\n return results.filter((r) => r !== undefined) as T[];\n }\n\n clear(): void {\n for (const plugin of this.plugins.values()) {\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch (error) {\n logger.error(`Error destroying plugin \"${plugin.name}\":`, error);\n }\n }\n }\n\n this.plugins.clear();\n logger.info('All plugins cleared');\n }\n}\n","import { Storage } from './storage';\nimport { logger } from './logger';\n\nexport type ConsentCategory =\n | 'analytics'\n | 'marketing'\n | 'functional'\n | 'necessary';\n\nexport interface ConsentPreferences {\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n necessary: boolean;\n}\n\nexport type ConsentStatus = 'granted' | 'denied' | 'pending';\n\nexport interface ConsentManagerConfig {\n defaultConsent?: Partial<ConsentPreferences>;\n waitForConsent?: boolean;\n consentStorageKey?: string;\n}\n\nexport class ConsentManager {\n private storage: Storage;\n private config: Required<ConsentManagerConfig>;\n private preferences: ConsentPreferences;\n private listeners: Array<(preferences: ConsentPreferences) => void> = [];\n\n constructor(storage: Storage, config: ConsentManagerConfig = {}) {\n this.storage = storage;\n this.config = {\n defaultConsent: config.defaultConsent || {},\n waitForConsent: config.waitForConsent ?? false,\n consentStorageKey: config.consentStorageKey || 'aegis_consent',\n };\n\n this.preferences = this.loadPreferences();\n }\n\n private loadPreferences(): ConsentPreferences {\n const stored = this.storage.get(this.config.consentStorageKey);\n\n if (stored) {\n try {\n const parsed = JSON.parse(stored);\n logger.info('Loaded consent preferences:', parsed);\n return this.mergeWithDefaults(parsed);\n } catch (error) {\n logger.warn('Failed to parse consent preferences:', error);\n }\n }\n\n return this.getDefaultPreferences();\n }\n\n private getDefaultPreferences(): ConsentPreferences {\n return {\n analytics: this.config.defaultConsent.analytics ?? false,\n marketing: this.config.defaultConsent.marketing ?? false,\n functional: this.config.defaultConsent.functional ?? true,\n necessary: true,\n };\n }\n\n private mergeWithDefaults(\n preferences: Partial<ConsentPreferences>\n ): ConsentPreferences {\n return {\n necessary: true,\n analytics: preferences.analytics ?? this.config.defaultConsent.analytics ?? false,\n marketing: preferences.marketing ?? this.config.defaultConsent.marketing ?? false,\n functional: preferences.functional ?? this.config.defaultConsent.functional ?? true,\n };\n }\n\n private savePreferences(): void {\n try {\n this.storage.set(\n this.config.consentStorageKey,\n JSON.stringify(this.preferences),\n 365\n );\n logger.info('Saved consent preferences:', this.preferences);\n } catch (error) {\n logger.error('Failed to save consent preferences:', error);\n }\n }\n\n setConsent(preferences: Partial<ConsentPreferences>): void {\n const updated = { ...this.preferences, ...preferences };\n updated.necessary = true;\n\n this.preferences = updated;\n this.savePreferences();\n this.notifyListeners();\n\n logger.info('Consent updated:', this.preferences);\n }\n\n grantAll(): void {\n this.setConsent({\n analytics: true,\n marketing: true,\n functional: true,\n necessary: true,\n });\n }\n\n denyAll(): void {\n this.setConsent({\n analytics: false,\n marketing: false,\n functional: false,\n necessary: true,\n });\n }\n\n hasConsent(category: ConsentCategory): boolean {\n return this.preferences[category];\n }\n\n getPreferences(): ConsentPreferences {\n return { ...this.preferences };\n }\n\n getStatus(category: ConsentCategory): ConsentStatus {\n if (this.hasConsent(category)) {\n return 'granted';\n }\n\n const stored = this.storage.get(this.config.consentStorageKey);\n if (!stored && this.config.waitForConsent) {\n return 'pending';\n }\n\n return 'denied';\n }\n\n isAnalyticsEnabled(): boolean {\n return this.hasConsent('analytics');\n }\n\n isMarketingEnabled(): boolean {\n return this.hasConsent('marketing');\n }\n\n onChange(callback: (preferences: ConsentPreferences) => void): () => void {\n this.listeners.push(callback);\n\n return () => {\n const index = this.listeners.indexOf(callback);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n };\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((listener) => {\n try {\n listener(this.getPreferences());\n } catch (error) {\n logger.error('Consent listener error:', error);\n }\n });\n }\n\n reset(): void {\n this.storage.remove(this.config.consentStorageKey);\n this.preferences = this.getDefaultPreferences();\n this.notifyListeners();\n logger.info('Consent preferences reset');\n }\n\n integrateOneTrust(): void {\n if (typeof window === 'undefined') return;\n\n const oneTrust = (window as any).OneTrust;\n if (!oneTrust) {\n logger.warn('OneTrust not detected');\n return;\n }\n\n const syncWithOneTrust = () => {\n const activeGroups = (window as any).OnetrustActiveGroups || '';\n\n this.setConsent({\n necessary: true,\n functional: activeGroups.includes('C0003'),\n analytics: activeGroups.includes('C0002'),\n marketing: activeGroups.includes('C0004'),\n });\n\n logger.info('Synced with OneTrust consent');\n };\n\n if ((window as any).OptanonWrapper) {\n const originalWrapper = (window as any).OptanonWrapper;\n (window as any).OptanonWrapper = function () {\n originalWrapper();\n syncWithOneTrust();\n };\n } else {\n (window as any).OptanonWrapper = syncWithOneTrust;\n }\n\n syncWithOneTrust();\n }\n\n integrateCookiebot(): void {\n if (typeof window === 'undefined') return;\n\n const cookiebot = (window as any).Cookiebot;\n if (!cookiebot) {\n logger.warn('Cookiebot not detected');\n return;\n }\n\n const syncWithCookiebot = () => {\n const consent = cookiebot.consent || {};\n\n this.setConsent({\n necessary: true,\n functional: consent.preferences || false,\n analytics: consent.statistics || false,\n marketing: consent.marketing || false,\n });\n\n logger.info('Synced with Cookiebot consent');\n };\n\n window.addEventListener('CookiebotOnAccept', syncWithCookiebot);\n window.addEventListener('CookiebotOnDecline', syncWithCookiebot);\n\n if (cookiebot.consent) {\n syncWithCookiebot();\n }\n }\n\n integrateGoogleConsentMode(): void {\n if (typeof window === 'undefined') return;\n\n const gtag = (window as any).gtag;\n if (!gtag) {\n logger.warn('Google Tag Manager not detected');\n return;\n }\n\n const updateGoogleConsent = () => {\n gtag('consent', 'update', {\n ad_storage: this.preferences.marketing ? 'granted' : 'denied',\n analytics_storage: this.preferences.analytics ? 'granted' : 'denied',\n ad_user_data: this.preferences.marketing ? 'granted' : 'denied',\n ad_personalization: this.preferences.marketing ? 'granted' : 'denied',\n functionality_storage: this.preferences.functional ? 'granted' : 'denied',\n personalization_storage: this.preferences.functional ? 'granted' : 'denied',\n security_storage: 'granted',\n });\n\n logger.info('Updated Google Consent Mode');\n };\n\n this.onChange(updateGoogleConsent);\n updateGoogleConsent();\n }\n}\n","/**\n * Meta Pixel companion cookies (_fbp, _fbc) — B1.2 of META_CAPI_PHASE1_TRACKER.\n *\n * We do NOT fire `fbq()` browser-side. We write the cookies Meta uses to\n * identify the same browser so server-side CAPI events (forwarded from\n * `meta_conversions_api_service.py`) carry the high-EMQ match keys\n * (fbp + fbc) without the cost of running a full Meta Pixel.\n *\n * Cookie spec (Meta docs, verified May 2026):\n * _fbp = fb.<subdomain_index>.<creation_ms>.<random>\n * _fbc = fb.<subdomain_index>.<click_ms>.<fbclid>\n * subdomain_index = number of dots in cookie domain\n * (example.com → 1, sub.example.com → 2)\n * random = 10-digit random integer\n * Lifetime: 90 days (Meta's default; longer doesn't increase EMQ)\n *\n * Reasoning for raw document.cookie writes (instead of utils/storage.ts):\n * The Storage helper prefixes every key with `aegis_` so writes go to\n * `aegis__fbp` / `aegis__fbc`. Meta only honors the exact names\n * `_fbp` and `_fbc`. We bypass the helper and write directly.\n *\n * Domain scope: written to the eTLD+1 (root domain) so a tenant who\n * later wires their own Meta Pixel via GTM sees the same cookies a\n * subdomain-rooted set would not.\n *\n * SSR-safe: every helper is no-op when `document` / `window` are\n * undefined. Callers don't need to guard.\n */\n\nconst FBP_COOKIE = '_fbp';\nconst FBC_COOKIE = '_fbc';\nconst COOKIE_LIFETIME_DAYS = 90;\n\nexport interface MetaCookies {\n fbp: string | null;\n fbc: string | null;\n fbclid: string | null;\n}\n\n/** Read a cookie value or null. SSR-safe. */\nfunction readCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n try {\n const target = `${name}=`;\n const parts = document.cookie.split(';');\n for (let i = 0; i < parts.length; i++) {\n let c = parts[i];\n while (c.charAt(0) === ' ') c = c.substring(1);\n if (c.indexOf(target) === 0) {\n return decodeURIComponent(c.substring(target.length));\n }\n }\n } catch {\n /* swallow — cookie access can throw in cross-origin / sandboxed iframes */\n }\n return null;\n}\n\n/** Write a cookie at root-domain scope so cross-subdomain reads work. SSR-safe. */\nfunction writeCookie(name: string, value: string, days: number): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n try {\n const expiry = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();\n const domain = getRootDomain();\n const isHttps = window.location.protocol === 'https:';\n let cookieStr = `${name}=${encodeURIComponent(value)};expires=${expiry};path=/;SameSite=Lax`;\n if (isHttps) cookieStr += ';Secure';\n if (domain) cookieStr += `;domain=${domain}`;\n document.cookie = cookieStr;\n } catch {\n /* ignore — best effort */\n }\n}\n\n/**\n * Get eTLD+1 of current host. Returns null on localhost / IP / single-label\n * hosts so we fall through to host-only cookie scope (browser default).\n *\n * Heuristic, not PSL-perfect: produces `example.com` for `a.b.example.com`.\n * Edge cases (`co.uk` etc.) get the wrong answer, but Meta itself uses\n * the same heuristic when their own pixel writes _fbp, so we stay aligned.\n */\nfunction getRootDomain(): string | null {\n if (typeof window === 'undefined') return null;\n const host = window.location.hostname;\n if (!host || host === 'localhost') return null;\n // IP address — host-only cookie scope.\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return null;\n const parts = host.split('.');\n if (parts.length < 2) return null;\n return '.' + parts.slice(-2).join('.');\n}\n\n/**\n * Subdomain index per Meta spec — count of dots in the cookie domain.\n * Counts components, not literal dots:\n * example.com → 1\n * sub.example.com → 2\n * sub.sub.example.com → 3\n */\nfunction subdomainIndex(): number {\n if (typeof window === 'undefined') return 1;\n const host = window.location.hostname;\n if (!host || host === 'localhost' || /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return 1;\n const parts = host.split('.');\n return Math.max(1, parts.length - 1);\n}\n\n/** 10-digit random integer (Meta's spec format). */\nfunction randomToken(): string {\n // Math.random() * 1e10 produces a 10-digit-ish integer; pad-left to be safe.\n const n = Math.floor(Math.random() * 1e10);\n return n.toString().padStart(10, '0');\n}\n\n/**\n * Read `_fbp` from cookies, write a fresh one if absent.\n * Returns the resulting `_fbp` value or null if cookies unavailable.\n */\nexport function ensureFbp(): string | null {\n const existing = readCookie(FBP_COOKIE);\n if (existing) return existing;\n const fbp = `fb.${subdomainIndex()}.${Date.now()}.${randomToken()}`;\n writeCookie(FBP_COOKIE, fbp, COOKIE_LIFETIME_DAYS);\n return fbp;\n}\n\n/**\n * Read `_fbc` from cookies. If absent and the URL carries `fbclid`, write\n * a fresh `_fbc` per Meta's spec. Returns the resulting value or null.\n *\n * Note: `_fbc` has no value to write without an `fbclid` source — unlike\n * `_fbp` which is browser-identity-only and can be self-generated.\n */\nexport function ensureFbc(): string | null {\n const existing = readCookie(FBC_COOKIE);\n if (existing) return existing;\n const fbclid = readFbclidFromUrl();\n if (!fbclid) return null;\n const fbc = `fb.${subdomainIndex()}.${Date.now()}.${fbclid}`;\n writeCookie(FBC_COOKIE, fbc, COOKIE_LIFETIME_DAYS);\n return fbc;\n}\n\n/** Pull `fbclid` from `?fbclid=...` query string. Null if absent or SSR. */\nexport function readFbclidFromUrl(): string | null {\n if (typeof window === 'undefined') return null;\n try {\n return new URLSearchParams(window.location.search).get('fbclid');\n } catch {\n return null;\n }\n}\n\n/**\n * Single call that returns `{fbp, fbc, fbclid}` for injection into every\n * event's `properties` (which lands in ClickHouse `in_app_events.metadata`,\n * which the cell-side CAPI service reads directly).\n *\n * Side effects: ensures both cookies are present before returning. Idempotent\n * — safe to call on every event capture.\n */\nexport function captureMetaCookies(): MetaCookies {\n return {\n fbp: ensureFbp(),\n fbc: ensureFbc(),\n fbclid: readFbclidFromUrl(),\n };\n}\n","/**\n * E-Commerce helper methods for the Aegis SDK.\n *\n * Provides standardized e-commerce event tracking that maps to the\n * Active Reach Intelligence data governance pipeline.\n *\n * Event names follow the canonical mapper:\n * product_viewed → engagement.product_view\n * product_list_viewed → engagement.product_list_view\n * cart_item_added → cart_item_added\n * cart_item_removed → cart_item_removed\n * cart_viewed → cart_viewed\n * checkout_started → checkout_started\n * checkout_step → checkout_step\n * order_completed → transaction.purchase\n * order_refunded → transaction.refund\n * coupon_applied → coupon_applied\n * coupon_removed → coupon_removed\n * search_performed → search_performed\n * wishlist_item_added → wishlist_item_added\n * product_waitlisted → product_waitlisted (back-in-stock waitlist opt-in;\n * consumed by product_event_trigger_service\n * _get_waitlisted_contacts; fires catalog.back_in_stock\n * journey trigger on the next ON_STOCK flip)\n * promotion_viewed → promotion_viewed\n * promotion_clicked → promotion_clicked\n */\n\nimport type { Aegis } from '../core/analytics';\nimport type {\n EcommerceProduct,\n EcommerceCart,\n EcommerceCheckout,\n EcommerceOrder,\n EcommerceCoupon,\n EcommerceSearch,\n EcommerceProductList,\n EcommerceWishlist,\n EcommerceWaitlist,\n EcommercePromotion,\n} from './types';\n\nfunction sumItems(products: EcommerceProduct[]): number {\n return products.reduce((sum, p) => sum + (p.quantity ?? 1), 0);\n}\n\n// Cart abandonment detection is SERVER-SIDE. The SDK fires the canonical\n// e-commerce events (`cart_item_added`, `cart_item_removed`, `cart_viewed`,\n// `transaction.purchase`); a Celery beat task on cell-plane scans\n// `aegis.events_with_ttl` every 5 minutes for shoppers with a\n// `cart_item_added` >30 min old and no subsequent purchase, emitting\n// `cart_abandoned` to the `behavioral-events_{cell_id}` Kafka topic.\n//\n// The SPA storefront has no persistent server-side cart object, so cart\n// identity is keyed off the shopper themselves: `COALESCE(user_id,\n// anonymous_id)`. Provider-issued cart tokens (Shopify/Woo/Magento pixel\n// bridge via Widget Manager) are surfaced on the emitted event when\n// present but are not required for detection.\n//\n// We deliberately do NOT run a JS timer on the client — mobile browsers\n// throttle background timers to ~1Hz and they're unreliable for any\n// abandonment threshold beyond a few seconds. Industry alignment: WebEngage,\n// MoEngage, CleverTap, Bitespeed, Klaviyo all detect server-side.\n//\n// See: `apps/cell-plane/app/workers/storefront_cart_abandonment_scanner.py`\n\nfunction mapProducts(products: EcommerceProduct[]) {\n return products.map((p) => ({\n product_id: p.product_id,\n sku: p.sku ?? p.product_id,\n name: p.name,\n price: p.price,\n quantity: p.quantity ?? 1,\n currency: p.currency,\n category: p.category,\n brand: p.brand,\n variant_id: p.variant_id,\n variant_label: p.variant_label,\n position: p.position,\n }));\n}\n\nexport class EcommerceTracker {\n constructor(private aegis: Aegis) {}\n\n // -- Product Discovery --\n\n productViewed(product: EcommerceProduct): void {\n this.aegis.track('product_viewed', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n variant_label: product.variant_label,\n image_url: product.image_url,\n url: product.url,\n });\n }\n\n productListViewed(list: EcommerceProductList): void {\n this.aegis.track('product_list_viewed', {\n list_id: list.list_id,\n list_name: list.list_name,\n category: list.category,\n products: mapProducts(list.products),\n });\n }\n\n productClicked(product: EcommerceProduct, source?: { list_id?: string; position?: number; section?: string }): void {\n this.aegis.track('product_clicked', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n list_id: source?.list_id,\n position: source?.position ?? product.position,\n section: source?.section,\n });\n }\n\n productImpressed(product: EcommerceProduct, source?: { list_id?: string; position?: number; section?: string }): void {\n this.aegis.track('product_impression', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n list_id: source?.list_id,\n position: source?.position ?? product.position,\n section: source?.section,\n });\n }\n\n categoryFiltered(category: string, options?: { previous_category?: string; result_count?: number }): void {\n this.aegis.track('category_filtered', {\n category,\n previous_category: options?.previous_category,\n result_count: options?.result_count,\n });\n }\n\n searchPerformed(search: EcommerceSearch): void {\n this.aegis.track('search_performed', {\n query: search.query,\n results_count: search.results_count,\n filters: search.filters,\n });\n }\n\n // -- Cart --\n\n addToCart(product: EcommerceProduct): void {\n this.aegis.track('cart_item_added', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n quantity: product.quantity ?? 1,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n variant_label: product.variant_label,\n });\n }\n\n removeFromCart(product: EcommerceProduct): void {\n this.aegis.track('cart_item_removed', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n quantity: product.quantity ?? 1,\n currency: product.currency ?? 'INR',\n variant_id: product.variant_id,\n });\n }\n\n cartViewed(cart: EcommerceCart): void {\n this.aegis.track('cart_viewed', {\n cart_id: cart.cart_id,\n value: cart.value,\n currency: cart.currency ?? 'INR',\n num_items: sumItems(cart.products),\n products: mapProducts(cart.products),\n });\n }\n\n // -- Checkout --\n\n checkoutStarted(checkout: EcommerceCheckout): void {\n this.aegis.track('checkout_started', {\n checkout_id: checkout.checkout_id,\n value: checkout.value,\n currency: checkout.currency ?? 'INR',\n num_items: sumItems(checkout.products),\n coupon: checkout.coupon,\n shipping: checkout.shipping,\n tax: checkout.tax,\n products: mapProducts(checkout.products),\n });\n }\n\n checkoutStep(step: number, options?: Record<string, unknown>): void {\n this.aegis.track('checkout_step', {\n step,\n ...options,\n });\n }\n\n // -- Order --\n\n orderCompleted(order: EcommerceOrder): void {\n this.aegis.track('order_completed', {\n order_id: order.order_id,\n value: order.value,\n revenue: order.revenue ?? order.value,\n currency: order.currency ?? 'INR',\n num_items: sumItems(order.products),\n coupon: order.coupon,\n shipping: order.shipping,\n tax: order.tax,\n discount: order.discount,\n payment_method: order.payment_method,\n products: mapProducts(order.products),\n });\n }\n\n orderRefunded(orderId: string, value?: number, products?: EcommerceProduct[]): void {\n this.aegis.track('order_refunded', {\n order_id: orderId,\n value,\n products: products ? mapProducts(products) : undefined,\n });\n }\n\n // -- Coupons --\n\n couponApplied(coupon: EcommerceCoupon): void {\n this.aegis.track('coupon_applied', { ...coupon });\n }\n\n couponRemoved(coupon: EcommerceCoupon): void {\n this.aegis.track('coupon_removed', { ...coupon });\n }\n\n // -- Wishlist --\n\n wishlistItemAdded(wishlist: EcommerceWishlist): void {\n this.aegis.track('wishlist_item_added', {\n wishlist_id: wishlist.wishlist_id,\n wishlist_name: wishlist.wishlist_name,\n product_id: wishlist.product.product_id,\n sku: wishlist.product.sku ?? wishlist.product.product_id,\n name: wishlist.product.name,\n price: wishlist.product.price,\n variant_id: wishlist.product.variant_id,\n });\n }\n\n // -- Back-in-stock waitlist --\n //\n // Server-side substrate: contact_events row keyed on\n // (organization_id, contact_id, event_name='product_waitlisted',\n // event_properties['product_id']). Resolved by\n // product_event_trigger_service._get_waitlisted_contacts and fanned out via\n // catalog.back_in_stock journey trigger when stock_event_handler_worker\n // detects the SKU flipping back into stock.\n productWaitlisted(waitlist: EcommerceWaitlist): void {\n this.aegis.track('product_waitlisted', {\n product_id: waitlist.product.product_id,\n sku: waitlist.product.sku ?? waitlist.product.product_id,\n variant_id: waitlist.product.variant_id,\n name: waitlist.product.name,\n price: waitlist.product.price,\n channels: waitlist.channels,\n });\n }\n\n // -- Promotions --\n\n promotionViewed(promo: EcommercePromotion): void {\n this.aegis.track('promotion_viewed', { ...promo });\n }\n\n promotionClicked(promo: EcommercePromotion): void {\n this.aegis.track('promotion_clicked', { ...promo });\n }\n}\n\nexport type {\n EcommerceProduct,\n EcommerceCart,\n EcommerceCheckout,\n EcommerceOrder,\n EcommerceCoupon,\n EcommerceSearch,\n EcommerceProductList,\n EcommerceWishlist,\n EcommerceWaitlist,\n EcommercePromotion,\n} from './types';\n","/**\n * Token bucket rate limiter for client-side event throttling.\n *\n * Limits per session: burst (default 100) + sustained (default 20/sec).\n * Drops events with a console warning once a burst sustains. Drops are\n * emitted as a single coalesced `aegis.client.rate_limited` meta-event\n * per second so we don't flood ingress with drop notifications.\n *\n * Defaults are deliberately generous — the goal is to prevent runaway\n * loops (a buggy app firing 10k events/sec) without affecting normal use.\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface RateLimiterOptions {\n /** Max tokens (burst size). Default 100. */\n burstCapacity?: number;\n /** Sustained rate per second. Default 20. */\n refillPerSecond?: number;\n /** Window in ms over which dropped events are coalesced. Default 1000. */\n dropCoalesceWindowMs?: number;\n /** Optional callback fired with the count of dropped events per coalesce window. */\n onDropBatch?: (droppedCount: number, sampleEventName: string | null) => void;\n}\n\nexport class RateLimiter {\n private tokens: number;\n private readonly capacity: number;\n private readonly refillPerSecond: number;\n private lastRefillMs: number;\n\n private droppedThisWindow = 0;\n private firstDroppedName: string | null = null;\n private windowFlushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly windowMs: number;\n private readonly onDropBatch?: (droppedCount: number, sampleEventName: string | null) => void;\n\n constructor(options: RateLimiterOptions = {}) {\n this.capacity = Math.max(1, options.burstCapacity ?? 100);\n this.refillPerSecond = Math.max(1, options.refillPerSecond ?? 20);\n this.windowMs = options.dropCoalesceWindowMs ?? 1000;\n this.onDropBatch = options.onDropBatch;\n this.tokens = this.capacity;\n this.lastRefillMs = Date.now();\n }\n\n /**\n * Try to consume 1 token. Returns true if allowed, false if throttled.\n * Throttled events are coalesced into a single onDropBatch callback per window.\n */\n tryConsume(eventName: string): boolean {\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n this.droppedThisWindow += 1;\n if (this.firstDroppedName === null) {\n this.firstDroppedName = eventName;\n logger.warn(\n `[Aegis] Rate limit reached — dropping event \"${eventName}\" and any further events for ${this.windowMs}ms. Burst=${this.capacity}, refill=${this.refillPerSecond}/s.`,\n );\n }\n\n if (!this.windowFlushTimer) {\n this.windowFlushTimer = setTimeout(() => this.flushWindow(), this.windowMs);\n }\n\n return false;\n }\n\n /** Diagnostics — current token count (rounded down). */\n getAvailableTokens(): number {\n this.refill();\n return Math.floor(this.tokens);\n }\n\n /** Cleanup. Idempotent. */\n destroy(): void {\n if (this.windowFlushTimer) {\n clearTimeout(this.windowFlushTimer);\n this.windowFlushTimer = null;\n }\n this.flushWindow();\n }\n\n // ---------------------------------------------------------------------\n\n private refill(): void {\n const now = Date.now();\n const elapsedSec = (now - this.lastRefillMs) / 1000;\n if (elapsedSec <= 0) return;\n\n const newTokens = elapsedSec * this.refillPerSecond;\n this.tokens = Math.min(this.capacity, this.tokens + newTokens);\n this.lastRefillMs = now;\n }\n\n private flushWindow(): void {\n if (this.windowFlushTimer) {\n clearTimeout(this.windowFlushTimer);\n this.windowFlushTimer = null;\n }\n if (this.droppedThisWindow > 0 && this.onDropBatch) {\n try {\n this.onDropBatch(this.droppedThisWindow, this.firstDroppedName);\n } catch (err) {\n logger.warn('[Aegis] RateLimiter.onDropBatch threw:', err);\n }\n }\n this.droppedThisWindow = 0;\n this.firstDroppedName = null;\n }\n}\n","/**\n * MurmurHash3 x86-32 — deterministic, byte-identical with Python `mmh3.hash`.\n *\n * We hand-roll rather than pull a dep because (a) our bundle-budget is\n * aggressive for storefront first-paint, and (b) this is the authoritative\n * cross-language hash for the event-governance bloom filter — any behavior\n * drift between Python and JS produces silent FP-rate spikes in prod.\n *\n * Contract:\n * • Input is a JS string. It is UTF-8 encoded via TextEncoder before hashing.\n * • Output is an unsigned 32-bit integer (0 .. 2^32-1).\n * • Matches `mmh3.hash(s.encode('utf-8'), seed=seed, signed=False)` in\n * Python exactly — verified by libs/shared-types/bloom-test-vectors.json\n * in CI.\n *\n * Reference: Austin Appleby, MurmurHash3 (public domain).\n * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\n *\n * Implementation notes:\n * • All arithmetic on 32-bit ints. JS does bitwise ops on 32-bit ints natively;\n * `Math.imul` gives us correct 32-bit multiplication without overflow.\n * • We use `>>> 0` to coerce to unsigned after every step that could produce\n * a negative or >32-bit value.\n * • UTF-8 encoding matters — a naive `charCodeAt` loop would mis-hash any\n * string with non-ASCII characters (e.g. emoji, accented chars) and the\n * Python side would disagree. We always encode via TextEncoder first.\n */\n\nconst C1 = 0xcc9e2d51;\nconst C2 = 0x1b873593;\n\n/**\n * Compute MurmurHash3 x86-32 of a UTF-8 encoded string.\n *\n * @param input The string to hash.\n * @param seed 32-bit unsigned seed.\n * @returns Unsigned 32-bit integer hash.\n */\nexport function murmurhash3_x86_32(input: string, seed: number = 0): number {\n // Encode to UTF-8 bytes — matches Python's `s.encode('utf-8')`.\n const bytes = new TextEncoder().encode(input);\n return murmurhash3_bytes(bytes, seed);\n}\n\n/**\n * Compute MurmurHash3 x86-32 over a byte buffer directly.\n *\n * Exposed for callers that already have UTF-8 bytes and want to skip the\n * encode step (e.g., internal bloom-hash paths).\n */\nexport function murmurhash3_bytes(bytes: Uint8Array, seed: number = 0): number {\n const len = bytes.length;\n const nBlocks = Math.floor(len / 4);\n\n let h1 = seed >>> 0;\n\n // Body — consume 4-byte blocks.\n for (let i = 0; i < nBlocks; i++) {\n const offset = i * 4;\n let k1 =\n bytes[offset] |\n (bytes[offset + 1] << 8) |\n (bytes[offset + 2] << 16) |\n (bytes[offset + 3] << 24);\n\n k1 = Math.imul(k1, C1);\n k1 = (k1 << 15) | (k1 >>> 17);\n k1 = Math.imul(k1, C2);\n\n h1 ^= k1;\n h1 = (h1 << 13) | (h1 >>> 19);\n h1 = (Math.imul(h1, 5) + 0xe6546b64) >>> 0;\n }\n\n // Tail — up to 3 trailing bytes.\n const tailStart = nBlocks * 4;\n let k1 = 0;\n const tailLen = len - tailStart;\n if (tailLen === 3) k1 ^= bytes[tailStart + 2] << 16;\n if (tailLen >= 2) k1 ^= bytes[tailStart + 1] << 8;\n if (tailLen >= 1) {\n k1 ^= bytes[tailStart];\n k1 = Math.imul(k1, C1);\n k1 = (k1 << 15) | (k1 >>> 17);\n k1 = Math.imul(k1, C2);\n h1 ^= k1;\n }\n\n // Finalization — avalanche (fmix32).\n h1 ^= len;\n h1 ^= h1 >>> 16;\n h1 = Math.imul(h1, 0x85ebca6b);\n h1 ^= h1 >>> 13;\n h1 = Math.imul(h1, 0xc2b2ae35);\n h1 ^= h1 >>> 16;\n\n return h1 >>> 0;\n}\n","/**\n * Bloom filter — wire-compatible with the Python builder in\n * apps/control-plane/app/services/event_governance_bloom.py\n *\n * This SDK-side variant is QUERY-ONLY. The filter is built server-side\n * from the org's registered event-name set, base64-encoded, and shipped\n * to the browser on `/v1/sdk/bootstrap`. The SDK reads it once, uses it\n * to gate `track()` calls, and discards it on the next bootstrap.\n *\n * Wire format:\n * • `m` bits of storage, represented as m/8 bytes, base64-encoded.\n * • Bit `i` is at byte `i >> 3`, bitmask `1 << (i & 7)` — LSB-first.\n * • `m` MUST be a power of two — allows `idx & (m-1)` modulo.\n * • `k` hash functions synthesized via Kirsch-Mitzenmacher from two\n * MurmurHash3 x86-32 hashes with seeds (seedA, seedB):\n * h_i(x) = (h1(x) + i * h2(x)) mod m\n * • Input strings are UTF-8 encoded (handled inside murmurhash3_x86_32).\n */\n\nimport { murmurhash3_x86_32 } from './murmur3';\n\n/** Decode a base64 string to a Uint8Array. Accepts web (atob) and node (Buffer). */\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== 'undefined') {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node.js fallback — used by tests and SSR integrations.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const B = (globalThis as any).Buffer;\n if (B && typeof B.from === 'function') {\n const buf: Uint8Array = B.from(b64, 'base64');\n // Copy into a plain Uint8Array so callers don't observe Buffer semantics.\n return new Uint8Array(buf);\n }\n throw new Error('No base64 decoder available (neither atob nor Buffer)');\n}\n\nexport interface BloomFilterParams {\n /** Bits in the filter (must be power of 2). */\n m: number;\n /** Number of hash functions (Kirsch-Mitzenmacher synthesized). */\n k: number;\n /** Seed for the first MurmurHash3 call. */\n seedA: number;\n /** Seed for the second MurmurHash3 call. */\n seedB: number;\n}\n\nexport class BloomFilter {\n private readonly buf: Uint8Array;\n private readonly mask: number;\n\n constructor(buf: Uint8Array, private readonly params: BloomFilterParams) {\n if ((params.m & (params.m - 1)) !== 0) {\n throw new Error(`Bloom filter m must be a power of 2, got ${params.m}`);\n }\n if (buf.length !== params.m >> 3) {\n throw new Error(\n `Bloom filter buffer size mismatch: expected ${params.m >> 3} bytes, got ${buf.length}`\n );\n }\n this.buf = buf;\n this.mask = params.m - 1;\n }\n\n /** Build from the wire format (base64 bytes + explicit params). */\n static fromBase64(bloomB64: string, params: BloomFilterParams): BloomFilter {\n const bytes = base64ToBytes(bloomB64);\n return new BloomFilter(bytes, params);\n }\n\n /**\n * Returns true if `name` is probably in the set — possibly with the\n * filter's configured false-positive rate. FALSE is always authoritative.\n *\n * FP here means: SDK thinks a name is already registered when it isn't.\n * That costs one wasted server round-trip (gateway does the exact check\n * and catches it) — strictly safer than a false-negative, which could\n * leak a novel name past the SDK.\n */\n has(name: string): boolean {\n const h1 = murmurhash3_x86_32(name, this.params.seedA);\n const h2 = murmurhash3_x86_32(name, this.params.seedB);\n\n for (let i = 0; i < this.params.k; i++) {\n // >>> 0 at each step to keep arithmetic in unsigned 32-bit land;\n // we can't just take & mask at the end because h1 + i*h2 could\n // overflow past 2^32 when i*h2 is large.\n const combined = (h1 + Math.imul(i, h2)) >>> 0;\n const idx = combined & this.mask;\n const bit = this.buf[idx >> 3] & (1 << (idx & 7));\n if (bit === 0) return false;\n }\n return true;\n }\n}\n","/**\n * NameGovernor — client-side event-name cap enforcement.\n *\n * The governor consumes an `EventGovernanceHint` from the bootstrap response\n * and decides whether to let a `track()` call proceed to the network or drop\n * it locally.\n *\n * Why this exists:\n * Without it, a page that calls `track('new_btn_' + Date.now())` in a\n * render loop would (a) pass the local rate-limiter if within burst, (b)\n * force the gateway to ask control-plane for a verdict on every unique\n * name, and (c) amplify the CP `/event-governance/check` load quadratically\n * in the number of tabs firing the same bug. The bloom filter gives the\n * SDK enough information to drop novel names locally once the org hits\n * its cap, collapsing the amplification to zero.\n *\n * Design constraints:\n * • FAIL-OPEN — missing hint (Enterprise org, Redis outage) lets every\n * name through. The gateway is still the authoritative cap.\n * • FALSE-POSITIVE-SAFE — if the bloom says \"known\" for a name that isn't\n * actually registered, we send to gateway and the gateway's exact check\n * catches it. FPs cost one round trip, not a cap bypass.\n * • LOCAL-MEMO — a novel name that has already charged `remainingNewNames`\n * in this tab must NOT charge again on subsequent calls within the same\n * hint TTL. Without this, `track('new_btn_click')` fired 50 times would\n * drain 50 from the counter and block every OTHER legitimate novel name.\n * See `localNovelNames`.\n *\n * See docs/architecture/RFC_2026_04_SDK_GOVERNANCE_HINTS.md §3.3.\n */\n\nimport { BloomFilter } from './bloom-filter';\n\n/** Shape of the hint delivered by `/v1/sdk/bootstrap`. */\nexport interface EventGovernanceHint {\n bloom_algo: string;\n seed_a: number;\n seed_b: number;\n k: number;\n m: number;\n bloom_b64: string;\n remaining_new_names: number | null;\n /**\n * When true, the server is in its 7-day soft-cap grace window: it accepts\n * novel event names past the cap. SDK must NOT drop locally in this mode —\n * doing so would enforce harder than the server. See\n * apps/control-plane/app/schemas/event_governance_hint.py for the canonical\n * contract.\n */\n grace_active?: boolean;\n ttl_seconds: number;\n}\n\nexport interface DropReport {\n /** Map of event_name → count of local drops since last drain. */\n events: Record<string, number>;\n /** Total events dropped across all names (sum of values). */\n total: number;\n /** When the report window started (ms since epoch). */\n since: number;\n}\n\n/** Matches the `bloom_algo` tag shipped by the current control-plane build. */\nconst SUPPORTED_ALGO = 'mmh3_x86_32_km';\n\n/**\n * Best-effort console.warn — coalesced so a runaway render loop doesn't\n * flood the dev console. We only warn on the FIRST distinct dropped name;\n * subsequent names silently increment the telemetry counter.\n */\nfunction warnOncePerSession(message: string): void {\n if (typeof console === 'undefined' || typeof console.warn !== 'function') return;\n console.warn(message);\n}\n\nexport class NameGovernor {\n private bloom: BloomFilter | null = null;\n private remainingNewNames: number = Infinity;\n private graceActive: boolean = false;\n\n /**\n * Names this SDK instance has seen AS NOVEL and already charged against\n * `remainingNewNames`. Reset on every `ingestHint()` so we don't leak\n * accounting across hint refreshes.\n */\n private localNovelNames: Set<string> = new Set();\n\n /** Coalesced telemetry — flushed to the transport via drainDropReport(). */\n private droppedSinceLastReport: Map<string, number> = new Map();\n private reportWindowStart: number = Date.now();\n private hasWarnedThisSession = false;\n\n /**\n * Ingest a freshly-bootstrapped hint. Call on every successful bootstrap.\n * Passing `null` disables governance (fail-open).\n */\n ingestHint(hint: EventGovernanceHint | null): void {\n if (!hint || hint.bloom_algo !== SUPPORTED_ALGO) {\n // Unknown algo — a newer CP ships a version this SDK can't decode.\n // Silently disable governance; gateway remains authoritative.\n this.bloom = null;\n this.remainingNewNames = Infinity;\n this.graceActive = false;\n this.localNovelNames.clear();\n return;\n }\n\n try {\n this.bloom = BloomFilter.fromBase64(hint.bloom_b64, {\n m: hint.m,\n k: hint.k,\n seedA: hint.seed_a,\n seedB: hint.seed_b,\n });\n } catch {\n // Malformed hint — fail-open. Logged by the caller.\n this.bloom = null;\n }\n\n this.remainingNewNames = hint.remaining_new_names ?? Infinity;\n this.graceActive = hint.grace_active === true;\n this.localNovelNames.clear();\n }\n\n /**\n * Decide whether a `track()` call should proceed.\n *\n * Returns true = send to network (rate-limiter still runs after).\n * Returns false = drop locally; caller should return early.\n */\n shouldSend(eventName: string): boolean {\n // No hint = unlimited plan or fail-open. Send everything.\n if (!this.bloom) return true;\n\n // 7-day soft-cap grace window is active — server accepts novel names\n // past the cap, so SDK must not enforce harder. Fail-open to gateway.\n if (this.graceActive) return true;\n\n // Known name — send.\n if (this.bloom.has(eventName)) return true;\n\n // Already charged in this session — send (gateway has it cached too).\n if (this.localNovelNames.has(eventName)) return true;\n\n // Novel name, within headroom — charge once, send.\n if (this.remainingNewNames > 0) {\n this.localNovelNames.add(eventName);\n this.remainingNewNames -= 1;\n return true;\n }\n\n // Novel name, over the cap — drop locally, record for telemetry.\n const prev = this.droppedSinceLastReport.get(eventName) ?? 0;\n this.droppedSinceLastReport.set(eventName, prev + 1);\n\n // Only warn once per session, only on the first distinct dropped name —\n // a runaway loop will fill droppedSinceLastReport silently.\n if (!this.hasWarnedThisSession) {\n this.hasWarnedThisSession = true;\n warnOncePerSession(\n `[aegis] Event-name cap reached — \"${eventName}\" dropped locally. ` +\n `Upgrade your plan or remove dynamically-generated event names.`\n );\n }\n return false;\n }\n\n /**\n * Snapshot + reset the dropped-names counter. Called by the telemetry\n * beacon on batch flush so the gateway gets visibility into client-side\n * drops for ops dashboards.\n */\n drainDropReport(): DropReport | null {\n if (this.droppedSinceLastReport.size === 0) return null;\n\n const events: Record<string, number> = {};\n let total = 0;\n for (const [name, count] of this.droppedSinceLastReport) {\n events[name] = count;\n total += count;\n }\n\n const since = this.reportWindowStart;\n this.droppedSinceLastReport.clear();\n this.reportWindowStart = Date.now();\n\n return { events, total, since };\n }\n\n // Test-only accessors — not part of public API but easier than reflection.\n /** @internal */\n _debugState(): {\n hasBloom: boolean;\n remaining: number;\n localNovel: number;\n graceActive: boolean;\n } {\n return {\n hasBloom: this.bloom !== null,\n remaining: this.remainingNewNames,\n localNovel: this.localNovelNames.size,\n graceActive: this.graceActive,\n };\n }\n}\n","/**\n * TraitGovernor — client-side guards for trait writes (P10.5).\n *\n * Runs the same 5 ingestion guards client-side that the cell-plane backend\n * runs at the trait-write chokepoint (`record_attribute_keys`), so the SDK\n * can warn developers about problematic keys/values BEFORE network send.\n *\n * Why two chokepoints:\n * - **Backend** (`record_attribute_keys` → DLQ): authoritative; rejects\n * payloads that bypass the SDK (direct HTTP, CSV import, integration\n * workers). Operators see rejections via /Validation tab.\n * - **SDK** (this file): developer-facing; warns in console.warn at dev\n * time so the issue is caught BEFORE the bad data hits production. We\n * normalize what we can (key casing, value truncation, date format)\n * and warn-then-drop what we can't (reserved prefixes).\n *\n * \"First-3 per verdict per session\" — we warn at most 3 times for each\n * verdict per `(workspace_id, verdict)` key in any given page load. After\n * that, silent — the developer got the message; further warnings would\n * just spam the console and obscure other issues.\n *\n * Canonical contract: the verdict codes here MUST match\n * `data_governance.ingestion_guards.GuardResult.verdict` codes + the\n * `ingestion_dlq.verdict` DB CHECK + the FE `IngestionDLQVerdict` union.\n * Real-env smoke asserts BE ↔ DB drift; TS narrowing catches FE drift;\n * this file completes the four-surface drift protection.\n */\n\nexport type TraitVerdict =\n | 'bad_key_format'\n | 'value_too_long'\n | 'bad_date_format'\n | 'name_too_long' // not used here (event-name guard); kept in union for symmetry\n | 'reserved_prefix';\n\nexport interface TraitDrop {\n /** Original key as the developer wrote it. */\n originalKey: string;\n /** What the guard decided. */\n verdict: TraitVerdict;\n /** Human-readable reason — same text we'd log to ingestion_dlq.reason. */\n reason: string;\n}\n\nexport interface TraitResult {\n /**\n * Sanitized traits to actually send. Reserved-prefix keys are absent;\n * camelCase keys are rewritten to snake_case; long values truncated;\n * ISO dates → epoch ms.\n */\n sanitized: Record<string, unknown>;\n /** Keys that were dropped or modified (one row per change). */\n drops: TraitDrop[];\n}\n\n// Reserved namespace prefixes — mirrors\n// data_governance.ingestion_guards.DEFAULT_RESERVED_PREFIXES.\nconst RESERVED_PREFIXES: readonly string[] = [\n 'system.',\n 'user.',\n 'loyalty.',\n 'review.',\n 'cart.',\n 'checkout.',\n 'product.',\n 'pos.',\n 'bill.',\n 'feedback.',\n 'chat.',\n 'delivery.',\n 'event.',\n '$',\n '_',\n];\n\n// PII not detected here — that's done server-side by the canonical\n// is_likely_pii helper. SDK side handles only normalization + format\n// rejections that benefit from early warning.\n\nconst KEY_SOFT_VALUE_CAP = 512; // truncate\nconst KEY_HARD_VALUE_CAP = 10_000; // reject (DoS protection)\n\nconst CAMEL_BOUNDARY_1 = /(.)([A-Z][a-z]+)/g;\nconst CAMEL_BOUNDARY_2 = /([a-z0-9])([A-Z])/g;\nconst NON_SNAKE = /[\\s\\-.]+/g;\nconst DUPE_UNDERSCORE = /_+/g;\nconst ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\nconst DATE_KEY_HINTS = new Set([\n 'at', 'on', 'date', 'time', 'timestamp', 'dob', 'birthday',\n 'joined', 'expired', 'expires', 'created', 'updated', 'started', 'ended',\n]);\n\n/**\n * Normalize camelCase / PascalCase / dash-or-space-separated keys to\n * snake_case. Returns `null` if the key reduces to empty (caller treats\n * as `bad_key_format`).\n */\nfunction normalizeKey(key: string): string | null {\n if (!key) return null;\n const stage1 = key.replace(CAMEL_BOUNDARY_1, '$1_$2').replace(CAMEL_BOUNDARY_2, '$1_$2');\n const stage2 = stage1.replace(NON_SNAKE, '_').toLowerCase();\n const result = stage2.replace(DUPE_UNDERSCORE, '_').replace(/^_+|_+$/g, '');\n return result || null;\n}\n\nfunction startsWithReserved(key: string): string | null {\n const lower = key.toLowerCase();\n for (const prefix of RESERVED_PREFIXES) {\n if (lower.startsWith(prefix)) return prefix;\n }\n return null;\n}\n\nfunction looksLikeDateKey(key: string): boolean {\n for (const part of key.toLowerCase().split(/[_\\-.\\s]+/)) {\n if (DATE_KEY_HINTS.has(part)) return true;\n }\n return false;\n}\n\n/**\n * ISO-8601 / epoch string → epoch ms (number). Returns null on parse\n * failure. Numeric inputs assumed epoch (seconds if <1e11, else ms).\n */\nfunction parseDateValue(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value < 1e11 ? Math.floor(value * 1000) : Math.floor(value);\n }\n if (typeof value === 'string' && value) {\n const s = value.trim();\n if (ISO_8601.test(s)) {\n const ms = Date.parse(s);\n if (!Number.isNaN(ms)) return ms;\n }\n const asNum = Number(s);\n if (Number.isFinite(asNum)) {\n return asNum < 1e11 ? Math.floor(asNum * 1000) : Math.floor(asNum);\n }\n }\n return null;\n}\n\nfunction warnOnConsole(message: string): void {\n if (typeof console === 'undefined' || typeof console.warn !== 'function') return;\n console.warn(message);\n}\n\nexport class TraitGovernor {\n /** Cap warnings at 3 per (workspace_id, verdict) per session. */\n private static readonly WARN_CAP = 3;\n /** workspace_id || '__no_workspace__' → verdict → count. */\n private warnCounts: Map<string, Map<TraitVerdict, number>> = new Map();\n\n /**\n * Apply all guards to a traits object. Returns the sanitized payload\n * to send + a list of drops/modifications.\n *\n * Pass `workspace_id` so warning rate-limits scope per workspace; an\n * agency user switching workspaces won't have their warning budget\n * consumed cross-workspace.\n */\n process(\n traits: Record<string, unknown> | null | undefined,\n workspace_id?: string | null,\n ): TraitResult {\n const sanitized: Record<string, unknown> = {};\n const drops: TraitDrop[] = [];\n if (!traits || typeof traits !== 'object') {\n return { sanitized, drops };\n }\n\n for (const rawKey of Object.keys(traits)) {\n // 1. Reserved-prefix check runs on the ORIGINAL key, BEFORE\n // normalization. Otherwise `system.foo` would normalize to\n // `system_foo` (the `.` collapses to `_`) and bypass the\n // reserved check. This is the same order-of-operations bug\n // we caught at smoke time on the backend (2026-05-13).\n const prefix = startsWithReserved(rawKey);\n if (prefix !== null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'reserved_prefix',\n reason: `key uses reserved namespace ${JSON.stringify(prefix)}`,\n });\n continue;\n }\n\n // 2. Normalize key casing.\n const normalizedKey = normalizeKey(rawKey);\n if (normalizedKey === null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'bad_key_format',\n reason: `key reduced to empty after normalization`,\n });\n continue;\n }\n\n // 3. Re-check reserved on normalized form (belt-and-braces).\n if (startsWithReserved(normalizedKey) !== null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'reserved_prefix',\n reason: `normalized key ${JSON.stringify(normalizedKey)} still uses a reserved namespace`,\n });\n continue;\n }\n\n // 4. Value-side guards.\n let value = traits[rawKey];\n\n // 4a. Truncate long strings (soft cap); reject huge strings (hard cap).\n if (typeof value === 'string') {\n if (value.length > KEY_HARD_VALUE_CAP) {\n drops.push({\n originalKey: rawKey,\n verdict: 'value_too_long',\n reason: `value length ${value.length} exceeds hard cap ${KEY_HARD_VALUE_CAP}`,\n });\n continue;\n }\n if (value.length > KEY_SOFT_VALUE_CAP) {\n drops.push({\n originalKey: rawKey,\n verdict: 'value_too_long',\n reason: `value truncated from ${value.length} to ${KEY_SOFT_VALUE_CAP} chars`,\n });\n value = value.slice(0, KEY_SOFT_VALUE_CAP);\n }\n }\n\n // 4b. Date normalization — only when the key hints at date semantics\n // AND the value looks string-shaped. Numeric values are assumed\n // epoch already. Free-form strings on non-date-keyed properties\n // pass through (caller meant a string).\n if (looksLikeDateKey(normalizedKey) && typeof value === 'string') {\n const parsed = parseDateValue(value);\n if (parsed === null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'bad_date_format',\n reason: `value ${JSON.stringify(value)} on date-keyed field ${JSON.stringify(normalizedKey)} didn't parse as ISO-8601 / epoch`,\n });\n continue;\n }\n value = parsed;\n }\n\n sanitized[normalizedKey] = value;\n }\n\n // Emit first-3-per-verdict warnings (silent past the cap).\n for (const drop of drops) {\n this.maybeWarn(workspace_id ?? null, drop);\n }\n\n return { sanitized, drops };\n }\n\n private maybeWarn(workspace_id: string | null, drop: TraitDrop): void {\n const ws = workspace_id ?? '__no_workspace__';\n let perVerdict = this.warnCounts.get(ws);\n if (!perVerdict) {\n perVerdict = new Map();\n this.warnCounts.set(ws, perVerdict);\n }\n const count = perVerdict.get(drop.verdict) ?? 0;\n if (count >= TraitGovernor.WARN_CAP) return;\n perVerdict.set(drop.verdict, count + 1);\n warnOnConsole(\n `[Aegis SDK] trait ${drop.verdict}: ${drop.reason} ` +\n `(original key: ${JSON.stringify(drop.originalKey)}). ` +\n `Backend will reject; fix the SDK call to silence this warning.`,\n );\n }\n}\n","/**\n * UserNamespace — typed PII + per-channel opt-in setters that delegate to\n * `Aegis.identify()`. WebEngage-/CleverTap-parity ergonomics.\n *\n * Pre-login `setX` calls accumulate in `pendingTraits` and flush as one\n * identify() when `login()` fires — avoids polluting the contact graph\n * with anonymous-id-as-userId rows.\n *\n * See docs/architecture/SDK_USER_NAMESPACE_AND_SCREEN.md.\n */\n\nimport type { Aegis } from './analytics';\nimport { logger } from '../utils/logger';\n\nexport type OptInChannel =\n | 'email'\n | 'sms'\n | 'push'\n | 'webpush'\n | 'whatsapp'\n | 'rcs'\n | 'inapp';\n\nconst VALID_CHANNELS: ReadonlySet<OptInChannel> = new Set<OptInChannel>([\n 'email',\n 'sms',\n 'push',\n 'webpush',\n 'whatsapp',\n 'rcs',\n 'inapp',\n]);\n\n// Conservative RFC 5322 subset — same shape every CEP peer uses on the\n// browser side. Server-side validation is authoritative.\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n// E.164: leading '+' then 8–15 digits.\nconst PHONE_RE = /^\\+[1-9]\\d{7,14}$/;\n// SHA-256 hex string: exactly 64 hex chars.\nconst SHA256_HEX_RE = /^[a-f0-9]{64}$/i;\n// ISO date: YYYY-MM-DD (we don't accept full ISO datetime — birth dates\n// don't have a time component).\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport class UserNamespace {\n private readonly aegis: Aegis;\n private pendingTraits: Record<string, unknown> = {};\n\n constructor(aegis: Aegis) {\n this.aegis = aegis;\n }\n\n /**\n * Authoritative identity write. Flushes any pending pre-login traits\n * along with the supplied `traits` argument as a single identify call.\n */\n login(userId: string, traits?: Record<string, unknown>): void {\n if (typeof userId !== 'string' || userId.length === 0) {\n logger.warn('[user.login] userId must be a non-empty string');\n return;\n }\n const merged = { ...this.pendingTraits, ...(traits || {}) };\n this.pendingTraits = {};\n this.aegis.identify(userId, merged);\n }\n\n /**\n * Drops the current userId and any pending pre-login traits. Does NOT\n * fire a server-side logout event — that's a separate explicit `track`.\n */\n logout(): void {\n this.pendingTraits = {};\n this.aegis.reset();\n }\n\n setAttribute(key: string, value: unknown): void {\n if (typeof key !== 'string' || key.length === 0) {\n logger.warn('[user.setAttribute] key must be a non-empty string');\n return;\n }\n this.writeTraits({ [key]: value });\n }\n\n setAttributes(map: Record<string, unknown>): void {\n if (!map || typeof map !== 'object') {\n logger.warn('[user.setAttributes] map must be an object');\n return;\n }\n this.writeTraits(map);\n }\n\n setEmail(email: string): void {\n if (!EMAIL_RE.test(email)) {\n logger.warn('[user.setEmail] invalid email format');\n return;\n }\n this.writeTraits({ email: email.toLowerCase() });\n }\n\n setPhone(phone: string): void {\n if (!PHONE_RE.test(phone)) {\n logger.warn('[user.setPhone] phone must be E.164 (e.g. +15551234567)');\n return;\n }\n this.writeTraits({ phone });\n }\n\n setHashedEmail(sha256Hex: string): void {\n if (!SHA256_HEX_RE.test(sha256Hex)) {\n logger.warn('[user.setHashedEmail] expected 64-char hex SHA-256');\n return;\n }\n this.writeTraits({ email_sha256: sha256Hex.toLowerCase() });\n }\n\n setHashedPhone(sha256Hex: string): void {\n if (!SHA256_HEX_RE.test(sha256Hex)) {\n logger.warn('[user.setHashedPhone] expected 64-char hex SHA-256');\n return;\n }\n this.writeTraits({ phone_sha256: sha256Hex.toLowerCase() });\n }\n\n setBirthDate(iso: string): void {\n if (!ISO_DATE_RE.test(iso)) {\n logger.warn('[user.setBirthDate] expected YYYY-MM-DD');\n return;\n }\n this.writeTraits({ birth_date: iso });\n }\n\n setOptIn(channel: OptInChannel, granted: boolean): void {\n if (!VALID_CHANNELS.has(channel)) {\n logger.warn(`[user.setOptIn] unknown channel: ${channel}`);\n return;\n }\n if (typeof granted !== 'boolean') {\n logger.warn('[user.setOptIn] granted must be a boolean');\n return;\n }\n this.writeTraits({ [`opt_in_${channel}`]: granted });\n }\n\n /**\n * HMAC-signed identity token. Ingress-side verification not yet wired —\n * the trait is forwarded as-is and persisted on the contact record\n * until that lands.\n */\n setSecureToken(token: string): void {\n if (typeof token !== 'string' || token.length === 0) {\n logger.warn('[user.setSecureToken] token must be a non-empty string');\n return;\n }\n this.writeTraits({ _secure_token: token });\n }\n\n /**\n * Test/inspection hook — returns a copy of the pending traits buffer.\n * Used by the e2e smoke test to assert pre-login accumulation.\n */\n _getPendingTraits(): Record<string, unknown> {\n return { ...this.pendingTraits };\n }\n\n // ---------------------------------------------------------------------------\n\n private writeTraits(traits: Record<string, unknown>): void {\n const userId = this.aegis.getUserId();\n if (userId) {\n this.aegis.identify(userId, traits);\n return;\n }\n Object.assign(this.pendingTraits, traits);\n }\n}\n","import {\n AegisConfig,\n AegisConfigInternal,\n DEFAULT_CONFIG,\n CellEndpoint,\n} from '../types/config';\nimport {\n TrackEvent,\n IdentifyEvent,\n PageEvent,\n GroupEvent,\n AliasEvent,\n ScreenEvent,\n AegisEvent,\n} from '../types/events';\nimport { Plugin } from '../types/plugin';\nimport { Identity } from '../utils/identity';\nimport { SessionManager } from './session';\nimport { EventQueue } from './queue';\nimport { Transport } from './transport';\nimport { Storage } from '../utils/storage';\nimport { buildContext, isBot } from '../utils/device';\nimport { generateMessageId } from '../utils/uuid';\nimport { logger } from '../utils/logger';\nimport { PluginRegistry } from '../plugins/registry';\nimport { ConsentManager, ConsentPreferences, ConsentCategory } from '../utils/consent';\nimport { captureMetaCookies } from '../utils/meta-cookies';\nimport { parseAdClickIDs, hasAdClickIDs } from '../utils/url-parser';\nimport { EcommerceTracker } from '../ecommerce';\nimport { RateLimiter } from './rate-limiter';\nimport { NameGovernor, EventGovernanceHint, TraitGovernor } from '../governance';\nimport { UserNamespace } from './user-namespace';\n\nexport class Aegis {\n private config: AegisConfigInternal | null = null;\n private storage!: Storage;\n private identity!: Identity;\n private session: SessionManager | null = null;\n private queue: EventQueue | null = null;\n private transport: Transport | null = null;\n private plugins: PluginRegistry = new PluginRegistry();\n private initPromise: Promise<void> | null = null;\n private consent: ConsentManager | null = null;\n private _ecommerce: EcommerceTracker | null = null;\n private _user: UserNamespace | null = null;\n private rateLimiter: RateLimiter | null = null;\n private nameGovernor: NameGovernor = new NameGovernor();\n // P10.5 — client-side guards for trait writes. Mirrors the BE\n // chokepoint in `record_attribute_keys` so developers see console\n // warnings about reserved-prefix / bad-key / bad-date traits at\n // dev time, BEFORE the data hits production.\n private traitGovernor: TraitGovernor = new TraitGovernor();\n private _lastPageUrl: string | null = null;\n private _popstateHandler: (() => void) | null = null;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n\n // B1.2 — last messageId per event name. Tenants who run their own Meta\n // Pixel via GTM call `aegis.lastEventId('Purchase')` to retrieve the id\n // we generated, then pass it as `eventID` to their `fbq('track', ...)`\n // call. Meta dedups on (pixel_id, event_name, event_id) so the\n // browser-fired Pixel event and our server-side CAPI event collapse to\n // one. See docs/integrations/META_PIXEL_COEXISTENCE.md.\n private _lastEventIds: Map<string, string> = new Map();\n\n // Plugin-handshake P1 — workspace_code allowlist for path-segment\n // detection. Ingested from the bootstrap response via\n // `ingestWorkspaceCodes()`. Empty set = no codes registered for this\n // org (single-outlet tenant); path-detection cascade is skipped.\n private _workspaceCodes: Set<string> = new Set();\n\n // Plugin-handshake P1 (Track E) — runtime workspace selection via\n // aegis.setWorkspace(). Persisted to sessionStorage for the page-load\n // session so SPA navigations + reloads on the same outlet inherit it.\n private _runtimeWorkspace: string | null = null;\n\n async init(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): Promise<void> {\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = this._init(writeKey, config);\n return this.initPromise;\n }\n\n private async _init(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): Promise<void> {\n if (this.config?.initialized) {\n logger.warn('SDK already initialized');\n return;\n }\n\n if (this.shouldRespectDNT(config)) {\n logger.info('Do Not Track is enabled, SDK disabled');\n return;\n }\n\n if (isBot()) {\n logger.info('Bot detected, SDK disabled');\n return;\n }\n\n this.config = this.buildConfig(writeKey, config);\n\n if (this.config.debug) {\n logger.enable();\n }\n\n logger.info('Initializing Aegis SDK...', this.config);\n\n this.storage = new Storage({\n cookieDomain: this.config.cookie_domain,\n secureCookie: this.config.secure_cookie,\n crossDomain: this.config.cross_domain_tracking,\n });\n\n this.identity = new Identity(this.storage);\n\n this.consent = new ConsentManager(this.storage, {\n defaultConsent: this.config.default_consent,\n waitForConsent: this.config.wait_for_consent,\n });\n\n if (this.config.integrate_onetrust) {\n this.consent.integrateOneTrust();\n }\n\n if (this.config.integrate_cookiebot) {\n this.consent.integrateCookiebot();\n }\n\n if (this.config.integrate_google_consent_mode) {\n this.consent.integrateGoogleConsentMode();\n }\n\n this.session = new SessionManager(\n this.storage,\n this.config.session_timeout\n );\n\n this.captureAdClickIDsOnInit();\n\n this.transport = new Transport(\n {\n writeKey: this.config.write_key,\n apiHost: this.config.api_host,\n cellEndpoints: this.config.cell_endpoints,\n preferredRegion: this.config.preferred_region,\n autoRegionDetection: this.config.auto_region_detection,\n requestTimeout: this.config.request_timeout,\n retryFailedRequests: this.config.retry_failed_requests,\n maxRetries: this.config.max_retries,\n retryBackoffMultiplier: this.config.retry_backoff_multiplier,\n },\n this.storage\n );\n\n this.config.active_cell = this.transport.getActiveCell();\n\n this.queue = new EventQueue(\n {\n batchSize: this.config.batch_size,\n batchInterval: this.config.batch_interval,\n enableOfflineMode: this.config.enable_offline_mode,\n maxOfflineEvents: this.config.max_offline_events,\n },\n this.transport,\n this.storage\n );\n\n this.rateLimiter = new RateLimiter({\n burstCapacity: this.config.rate_limit_burst,\n refillPerSecond: this.config.rate_limit_per_second,\n onDropBatch: (count, sampleName) => this.emitRateLimitMeta(count, sampleName),\n });\n\n await this.initializePlugins();\n\n if (this.config.auto_page_view) {\n this.page();\n this.startSPATracking();\n }\n\n logger.info('Aegis SDK initialized successfully');\n }\n\n /**\n * SPA navigation tracking — monkey-patches pushState/replaceState and\n * listens for popstate to fire page() on every client-side route change.\n */\n private startSPATracking(): void {\n this._lastPageUrl = window.location.href;\n\n const onRouteChange = () => {\n // Debounce: only fire if URL actually changed\n const currentUrl = window.location.href;\n if (currentUrl === this._lastPageUrl) return;\n this._lastPageUrl = currentUrl;\n\n // Small delay to let the framework update document.title\n setTimeout(() => {\n this.page();\n }, 50);\n };\n\n // Listen for browser back/forward\n this._popstateHandler = onRouteChange;\n window.addEventListener('popstate', this._popstateHandler);\n\n // Monkey-patch pushState and replaceState\n this._originalPushState = history.pushState.bind(history);\n this._originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this._originalPushState!(...args);\n onRouteChange();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this._originalReplaceState!(...args);\n onRouteChange();\n };\n }\n\n private stopSPATracking(): void {\n if (this._popstateHandler) {\n window.removeEventListener('popstate', this._popstateHandler);\n this._popstateHandler = null;\n }\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n\n private buildConfig(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): AegisConfigInternal {\n return {\n ...DEFAULT_CONFIG,\n ...config,\n write_key: writeKey,\n initialized: true,\n } as AegisConfigInternal;\n }\n\n private shouldRespectDNT(config?: Partial<AegisConfig>): boolean {\n const respectDNT = config?.respect_dnt !== false;\n if (!respectDNT) return false;\n\n const dnt =\n navigator.doNotTrack ||\n (window as any).doNotTrack ||\n (navigator as any).msDoNotTrack;\n\n return dnt === '1' || dnt === 'yes';\n }\n\n private captureAdClickIDsOnInit(): void {\n if (!this.session) return;\n\n const adClickIDs = parseAdClickIDs();\n\n if (hasAdClickIDs(adClickIDs)) {\n this.session.setAdClickIDs(adClickIDs, window.location.href);\n logger.info('Ad click IDs captured on page load:', adClickIDs);\n }\n }\n\n private async initializePlugins(): Promise<void> {\n if (!this.config) return;\n\n for (const pluginName of this.config.plugins) {\n logger.debug(`Initializing plugin: ${pluginName}`);\n }\n\n await this.plugins.executeHook('init', this.config);\n }\n\n use(plugin: Plugin): void {\n this.plugins.register(plugin);\n\n if (this.config?.initialized && plugin.init) {\n Promise.resolve(plugin.init(this.config)).catch((error: any) => {\n logger.error(`Failed to initialize plugin \"${plugin.name}\":`, error);\n });\n }\n }\n\n /**\n * Symmetric counterpart to `use(plugin)`. Calls the plugin's `destroy()`\n * hook (if defined) and removes it from the registry so its hooks no\n * longer fire on subsequent events. Safe to call with a name that\n * isn't registered — logs a warning and returns.\n *\n * Primary use case: React components that register a plugin in\n * `useEffect` and need to unregister in the cleanup function so HMR /\n * provider remount doesn't leak listeners. See cashier-portal's\n * MetaPixelInjector for an example.\n */\n removePlugin(name: string): void {\n this.plugins.unregister(name);\n }\n\n /**\n * Plugin-handshake P1 — ingest the workspace_code allowlist from the\n * bootstrap response. Pass `result.workspaceCodes` from `bootstrap()`\n * here so the SDK can do path-segment workspace detection on\n * customer-facing pages like `/south/rewards`, `/ghatkopar/feedback`.\n *\n * Empty array is the right answer for single-outlet tenants — SDK\n * will skip the path cascade and fall through to query param /\n * setWorkspace() / gateway origin lookup.\n *\n * Safe to call before init() — the set is consulted at event-fire time.\n */\n ingestWorkspaceCodes(codes: string[] | null | undefined): void {\n this._workspaceCodes = new Set(\n (codes ?? []).filter((c) => typeof c === 'string' && c.length > 0),\n );\n logger.debug('Workspace codes ingested', { count: this._workspaceCodes.size });\n }\n\n /**\n * Plugin-handshake P1 (Track E) — explicitly set the workspace for\n * subsequent events. Used by:\n * - SPAs where workspace context changes via in-app routing\n * - Mobile / RN apps that have no URL\n * - Custom integrations that resolve workspace from their own state\n *\n * Pass null to clear (or call clearWorkspace()).\n *\n * Persisted to sessionStorage so SPA reloads / page transitions on the\n * same outlet inherit it without re-calling.\n */\n setWorkspace(codeOrId: string | null): void {\n this._runtimeWorkspace = codeOrId;\n if (typeof window !== 'undefined' && window.sessionStorage) {\n try {\n if (codeOrId) {\n window.sessionStorage.setItem('aegis_runtime_ws', codeOrId);\n } else {\n window.sessionStorage.removeItem('aegis_runtime_ws');\n }\n } catch {\n // Quota / private mode — non-fatal; in-memory value still applies.\n }\n }\n logger.debug('Runtime workspace set', { value: codeOrId });\n }\n\n /** Symmetric helper to clear the runtime workspace. */\n clearWorkspace(): void {\n this.setWorkspace(null);\n }\n\n /**\n * Inspect the URL's first path segment and return it if it's in the\n * org's workspace_code allowlist. Skips well-known non-workspace prefixes\n * (e.g. `b` for bill short codes; `s` is the storefront app slug).\n *\n * Returns undefined when:\n * - window is undefined (SSR / Node)\n * - path is empty / root\n * - first segment isn't in `_workspaceCodes` (e.g. /products, /cart)\n *\n * The returned value is a workspace CODE (slug like \"south\"), not a\n * UUID. The gateway normalizes code → UUID server-side via\n * workspace_subaccounts lookup with Redis cache.\n */\n private getPathWorkspaceCode(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n if (this._workspaceCodes.size === 0) return undefined;\n try {\n const segments = window.location.pathname.split('/').filter(Boolean);\n if (segments.length === 0) return undefined;\n const first = segments[0].toLowerCase();\n return this._workspaceCodes.has(first) ? first : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Resolve the effective `workspace_id` for the next event.\n *\n * Cascade (highest precedence first):\n * 1. `this.config.workspace_id` — explicit operator config (headless\n * SDK usage, server-rendered apps, native shells).\n * 2. `?ws=` query param on the current URL — the P3 storefront URL\n * contract. The outlet picker writes this via `router.replace`.\n * 3. URL path segment (P1 Track A) — `/south/rewards` → \"south\" when\n * \"south\" is in the org's workspace_code allowlist.\n * 4. `aegis.setWorkspace()` runtime override (P1 Track E) — SPAs +\n * mobile + custom integrations.\n * 5. sessionStorage `aegis_runtime_ws` — persists setWorkspace across\n * reloads/SPA route changes.\n * 6. `undefined` — gateway falls back to `resolveByOrigin` lookup\n * against the property's allowed_origins.\n *\n * The returned value can be EITHER a UUID (from config / ?ws=) or a\n * workspace CODE slug (from path / setWorkspace). The gateway\n * normalizes slug → UUID server-side; analytics layer never needs to\n * worry about the difference.\n *\n * See docs/architecture/MULTI_OUTLET_URL_STRATEGY.md §2.3 and\n * docs/architecture/PLUGIN_HANDSHAKE_AUTOMATION.md §2.\n */\n /**\n * Public so peer SDK surfaces (AegisMessageRuntime / AegisInAppManager /\n * AegisPlacementManager / AegisWidgetManager) can plumb the same\n * resolved workspace into their own gateway POSTs. Each manager calls\n * the gateway on its own endpoint (`/v1/in_app/events`,\n * `/v1/placements/track`, `/v1/widgets/track-event`); without this,\n * those events arrive at event-ingress with no workspace_id stamped\n * → impressions/clicks fall back to the org's primary workspace and\n * cross-outlet attribution breaks.\n */\n public getEffectiveWorkspaceId(): string | undefined {\n // 1. Explicit operator config wins\n const configured = this.config?.workspace_id;\n if (configured) return configured;\n if (typeof window === 'undefined') return undefined;\n\n // 2. ?ws= query param\n try {\n const ws = new URLSearchParams(window.location.search).get('ws');\n if (ws && ws.length > 0) return ws;\n } catch {\n /* malformed search — fall through */\n }\n\n // 3. URL path segment matching workspace_code allowlist\n const pathWs = this.getPathWorkspaceCode();\n if (pathWs) return pathWs;\n\n // 4. Runtime aegis.setWorkspace() value (in-memory)\n if (this._runtimeWorkspace) return this._runtimeWorkspace;\n\n // 5. sessionStorage fallback (set by prior setWorkspace() this session)\n try {\n if (window.sessionStorage) {\n const cached = window.sessionStorage.getItem('aegis_runtime_ws');\n if (cached && cached.length > 0) return cached;\n }\n } catch {\n /* private mode — fall through */\n }\n\n // 6. Gateway handles via resolveByOrigin\n return undefined;\n }\n\n track(eventName: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n const messageId = generateMessageId();\n const event: TrackEvent = {\n type: 'track',\n event: eventName,\n properties: properties || {},\n messageId,\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n context: buildContext(this.config!, this.session!),\n };\n\n // Record so tenants who run their own Meta Pixel via GTM can pull the\n // event_id and pass it as `eventID` in fbq('track', ...). See B1.2.\n this._lastEventIds.set(eventName, messageId);\n\n this.captureEvent(event);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n // P10.5 — apply trait governance BEFORE setUserId + send. The BE\n // applies the same guards in `record_attribute_keys`, but doing it\n // client-side lets us warn the developer in console.warn and emit\n // sanitized values so what they see locally matches what gets stored.\n const wsForGovernor = this.getEffectiveWorkspaceId() || null;\n const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);\n\n this.identity.setUserId(userId, governedTraits);\n\n const event: IdentifyEvent = {\n type: 'identify',\n traits: governedTraits,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: userId,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n page(name?: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n const event: PageEvent = {\n type: 'page',\n name: name || document.title,\n properties: properties || {},\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n group(groupId: string, traits?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n // P10.5 — same trait governance as identify(); group traits ride\n // the same `record_attribute_keys` BE chokepoint.\n const wsForGovernor = this.getEffectiveWorkspaceId() || null;\n const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);\n\n const event: GroupEvent = {\n type: 'group',\n groupId,\n traits: governedTraits,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n alias(newUserId: string): void {\n if (!this.assertInitialized()) return;\n\n const { previousId } = this.identity.alias(newUserId);\n\n const event: AliasEvent = {\n type: 'alias',\n previousId,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: newUserId,\n sessionId: this.session!.getSessionId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n /**\n * SPA-logical screen view. Distinct from `page()` (URL-bound) — fires\n * when the user enters a logical surface like cart / checkout / drawer\n * without a URL change. Gateway maps `screen` → `engagement.screen_view`.\n */\n screen(name: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n if (typeof name !== 'string' || name.length === 0) {\n logger.warn('aegis.screen: name must be a non-empty string');\n return;\n }\n\n const event: ScreenEvent = {\n type: 'screen',\n name,\n properties: properties || {},\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n private async captureEvent(event: AegisEvent): Promise<void> {\n if (this.config?.enable_consent_mode && this.consent) {\n if (event.type === 'track' || event.type === 'page') {\n if (!this.consent.hasConsent('analytics')) {\n logger.debug('Event blocked: analytics consent not granted');\n return;\n }\n }\n }\n\n // Event-name governance — drops novel names once the org's unique-event\n // cap is exhausted. Meta events (aegis.client.*) bypass the governor so\n // the telemetry channel stays open even under a cap-exhaustion storm.\n // See governance/name-governor.ts.\n if (event.type === 'track') {\n const trackEvent = event as TrackEvent;\n const isMetaEvent =\n typeof trackEvent.event === 'string' &&\n trackEvent.event.startsWith('aegis.client.');\n if (!isMetaEvent && !this.nameGovernor.shouldSend(trackEvent.event)) {\n return;\n }\n }\n\n if (this.rateLimiter) {\n const eventLabel =\n event.type === 'track' && (event as TrackEvent).event\n ? (event as TrackEvent).event\n : event.type;\n // Meta-events bypass the rate limiter to avoid recursion / lost telemetry.\n const isMetaEvent =\n event.type === 'track' &&\n typeof (event as TrackEvent).event === 'string' &&\n (event as TrackEvent).event.startsWith('aegis.client.');\n if (!isMetaEvent && !this.rateLimiter.tryConsume(eventLabel)) {\n return;\n }\n }\n\n // Auto-inject campaign attribution from UTM context into event properties\n // so downstream consumers (event-ingress, ClickHouse) receive campaign_id\n // without requiring manual mapping by the integrator.\n const ctx = (event as any).context;\n const props = (event as any).properties;\n if (ctx?.campaign && props && !props.campaign_id) {\n const utm = ctx.campaign;\n if (utm.campaign) props.campaign_id = utm.campaign;\n if (utm.source) props.campaign_source = utm.source;\n if (utm.medium) props.campaign_medium = utm.medium;\n if (utm.content) props.campaign_content = utm.content;\n if (utm.term) props.campaign_term = utm.term;\n }\n if (ctx?.adClickIDs && props) {\n const clicks = ctx.adClickIDs;\n const clickId = clicks.gclid || clicks.fbclid || clicks.ctwa_clid || clicks.msclkid || clicks.ttclid;\n if (clickId && !props.campaign_click_id) props.campaign_click_id = clickId;\n }\n\n // B1.2 — Auto-inject Meta companion cookies (_fbp, _fbc, fbclid) into\n // event properties so the cell-side CAPI service can pass them as\n // user_data fields to Meta. Lifts EMQ from ~6-7 (PII-only) to ≥8.0\n // without firing fbq() browser-side. fbp is self-generated if absent;\n // fbc only set when an inbound fbclid landed in the URL.\n // See libs/web-sdk/src/utils/meta-cookies.ts for the spec.\n if (props) {\n const meta = captureMetaCookies();\n if (meta.fbp && !props.fbp) props.fbp = meta.fbp;\n if (meta.fbc && !props.fbc) props.fbc = meta.fbc;\n if (meta.fbclid && !props.fbclid) props.fbclid = meta.fbclid;\n }\n\n logger.debug('Capturing event:', event);\n\n const transformedEvent = await this.plugins.executeHookChain(\n 'beforeEventCapture',\n event\n );\n\n if (!transformedEvent) {\n logger.debug('Event cancelled by plugin');\n return;\n }\n\n this.queue!.push(transformedEvent);\n this.session!.incrementEventCount();\n\n await this.plugins.executeHook('afterEventCapture', transformedEvent);\n }\n\n /**\n * Emit a coalesced meta-event reporting client-side rate-limit drops.\n * Bypasses the limiter (see captureEvent guard).\n */\n private emitRateLimitMeta(count: number, sampleEventName: string | null): void {\n if (!this.config?.initialized || !this.session || !this.identity) return;\n const event: TrackEvent = {\n type: 'track',\n event: 'aegis.client.rate_limited',\n properties: {\n dropped_count: count,\n sample_event_name: sampleEventName,\n burst_capacity: this.config.rate_limit_burst,\n refill_per_second: this.config.rate_limit_per_second,\n },\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session.getSessionId(),\n context: buildContext(this.config, this.session),\n };\n this.queue?.push(event);\n }\n\n reset(): void {\n if (!this.assertInitialized()) return;\n\n this.identity.reset();\n // Drop pending pre-login traits buffered on the user namespace —\n // a reset() either through `aegis.reset()` or `aegis.user.logout()`\n // should leave no residual identity state behind.\n this._user = null;\n logger.info('User identity reset');\n }\n\n /**\n * Ingest the event-governance hint returned from `/v1/sdk/bootstrap`.\n *\n * Callers (Shopify pixel, snippet, react integration) run `bootstrap()`\n * themselves and pass the resulting `eventGovernance` field here so the\n * SDK can self-throttle novel event names before they hit the gateway.\n *\n * Passing null/undefined disables governance (Enterprise plan / outage\n * fail-open). Safe to call before `init()` — the hint is stored and\n * applied as soon as init completes.\n */\n ingestGovernanceHint(hint: EventGovernanceHint | null | undefined): void {\n this.nameGovernor.ingestHint(hint ?? null);\n logger.debug('Governance hint ingested', hint ? {\n k: hint.k,\n m: hint.m,\n remaining: hint.remaining_new_names,\n } : 'disabled');\n }\n\n /**\n * Drain the client-side drop counter and emit a meta-event. Called\n * periodically by the queue flush path so ops dashboards see novel-name\n * amplification patterns in near-real-time.\n */\n private emitGovernanceDropMeta(): void {\n if (!this.config?.initialized || !this.session || !this.identity) return;\n const report = this.nameGovernor.drainDropReport();\n if (!report) return;\n\n const event: TrackEvent = {\n type: 'track',\n event: 'aegis.client.name_governor_dropped',\n properties: {\n dropped_count: report.total,\n distinct_names: Object.keys(report.events).length,\n // Cap the payload — a runaway loop could produce thousands of names;\n // we ship the top 10 for diagnostics and the total for counting.\n sample_names: Object.entries(report.events)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([name, count]) => ({ name, count })),\n window_start: report.since,\n window_end: Date.now(),\n },\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session.getSessionId(),\n context: buildContext(this.config, this.session),\n };\n this.queue?.push(event);\n }\n\n async flush(): Promise<void> {\n if (!this.assertInitialized()) return;\n\n // Emit any coalesced governance-drop telemetry BEFORE flushing so the\n // meta event rides in the same batch as the rest.\n this.emitGovernanceDropMeta();\n await this.queue!.flush();\n }\n\n getAnonymousId(): string | null {\n if (!this.identity) return null;\n return this.identity.getAnonymousId();\n }\n\n getUserId(): string | null {\n if (!this.identity) return null;\n return this.identity.getUserId();\n }\n\n getSessionId(): string | null {\n if (!this.session) return null;\n return this.session.getSessionId();\n }\n\n /**\n * B1.2 — Tenant-Pixel coexistence handoff. Returns the messageId of the\n * most recent `track('<eventName>', ...)` call, or null if no event of\n * that name has been tracked since SDK init.\n *\n * Use this from a tenant's own Meta Pixel script (e.g. via GTM) to pass\n * the same event_id to `fbq('track', '<eventName>', payload, {eventID: id})`.\n * Meta dedups on (pixel_id, event_name, event_id) so the browser-fired\n * Pixel event and our server-side CAPI event collapse to one — preventing\n * double-counting.\n *\n * Event names are case-sensitive (\"Purchase\" ≠ \"purchase\") — Meta's dedup\n * is also case-sensitive, so be exact.\n *\n * See docs/integrations/META_PIXEL_COEXISTENCE.md.\n */\n lastEventId(eventName: string): string | null {\n return this._lastEventIds.get(eventName) ?? null;\n }\n\n debug(enable: boolean = true): void {\n if (enable) {\n logger.enable();\n } else {\n logger.disable();\n }\n\n if (this.config) {\n this.config.debug = enable;\n }\n }\n\n setCell(cellEndpoint: CellEndpoint): void {\n if (!this.config) {\n logger.warn('SDK not initialized');\n return;\n }\n\n const existingCell = this.config.cell_endpoints.find(\n (c) => c.region === cellEndpoint.region\n );\n\n if (existingCell) {\n Object.assign(existingCell, cellEndpoint);\n } else {\n this.config.cell_endpoints.push(cellEndpoint);\n }\n\n logger.info('Cell endpoint configured:', cellEndpoint);\n }\n\n getCellInfo() {\n if (!this.transport) return null;\n\n return {\n activeCell: this.transport.getActiveCell(),\n latencies: this.transport.getRegionLatencies(),\n };\n }\n\n setConsent(preferences: Partial<ConsentPreferences>): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n this.consent.setConsent(preferences);\n logger.info('Consent preferences updated');\n }\n\n grantConsent(category?: ConsentCategory): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n if (category) {\n this.consent.setConsent({ [category]: true });\n } else {\n this.consent.grantAll();\n }\n }\n\n denyConsent(category?: ConsentCategory): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n if (category) {\n this.consent.setConsent({ [category]: false });\n } else {\n this.consent.denyAll();\n }\n }\n\n hasConsent(category: ConsentCategory): boolean {\n if (!this.consent) {\n return true;\n }\n\n return this.consent.hasConsent(category);\n }\n\n getConsentPreferences(): ConsentPreferences | null {\n if (!this.consent) {\n return null;\n }\n\n return this.consent.getPreferences();\n }\n\n onConsentChange(callback: (preferences: ConsentPreferences) => void): () => void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return () => {};\n }\n\n return this.consent.onChange(callback);\n }\n\n private assertInitialized(): boolean {\n if (!this.config?.initialized) {\n logger.warn('SDK not initialized. Call aegis.init() first.');\n return false;\n }\n return true;\n }\n\n get ecommerce(): EcommerceTracker {\n if (!this._ecommerce) {\n this._ecommerce = new EcommerceTracker(this);\n }\n return this._ecommerce;\n }\n\n get user(): UserNamespace {\n if (!this._user) {\n this._user = new UserNamespace(this);\n }\n return this._user;\n }\n\n destroy(): void {\n this.stopSPATracking();\n\n if (this.queue) {\n this.queue.destroy();\n }\n\n if (this.session) {\n this.session.destroy();\n }\n\n if (this.transport) {\n this.transport.destroy();\n }\n\n if (this.rateLimiter) {\n this.rateLimiter.destroy();\n this.rateLimiter = null;\n }\n\n this.plugins.clear();\n\n logger.info('SDK destroyed');\n }\n}\n","/**\n * CarouselCards renderer — a swipeable / autoplaying sequence of product\n * cards. Used for post-purchase upsells, cross-sell, and \"recently viewed\"\n * surfaces. The entire card list is pre-bundled — no network call on\n * trigger; slide advance is pure client state.\n *\n * Data shape (interactive_config, populated at campaign create time and\n * validated server-side):\n * cards: Array<{ image_url?, title, body?, cta_text?, cta_url? }>\n * autoplay_ms?: number // 0 = manual advance only\n * loop?: boolean // wrap to card 0 on last-next\n */\n\nimport type { RenderContext } from './types';\n\ntype Card = {\n image_url?: string;\n title?: string;\n body?: string;\n cta_text?: string;\n cta_url?: string;\n};\n\nconst MAX_CARDS_RENDERED = 10;\n\nexport function renderCarouselCards(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_CARDS_RENDERED);\n\n if (cards.length === 0) {\n log('carousel_cards rendered with zero cards — skipping', 'warn');\n return;\n }\n\n const autoplay = Number(ic.autoplay_ms) || 0;\n const loop = ic.loop !== false; // default true\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#ffffff');\n const fg = sanitizeColor((campaign.text_color as string) || '#0f172a');\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-carousel-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; left: 0; right: 0; bottom: 0;\n z-index: 99999; padding: 12px 12px 20px;\n background: rgba(0,0,0,0.12); backdrop-filter: blur(8px);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `;\n\n const card = document.createElement('div');\n card.style.cssText = `\n background: ${bg}; color: ${fg}; border-radius: 16px;\n box-shadow: 0 8px 20px rgba(0,0,0,0.08); padding: 16px;\n max-width: 520px; margin: 0 auto; position: relative;\n `;\n\n const header = document.createElement('div');\n header.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px;';\n\n const headerText = document.createElement('div');\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 15px; margin-bottom: 2px;';\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.75;';\n headerText.appendChild(title);\n headerText.appendChild(body);\n header.appendChild(headerText);\n\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '✕';\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 16px; cursor: pointer; opacity: 0.6; padding: 2px 6px;\n `;\n closeBtn.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n });\n header.appendChild(closeBtn);\n card.appendChild(header);\n\n const track = document.createElement('div');\n track.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory;\n scrollbar-width: none; -ms-overflow-style: none; padding-bottom: 4px;\n `;\n (track.style as unknown as { msOverflowStyle?: string }).msOverflowStyle = 'none';\n track.addEventListener('wheel', (e) => {\n if (Math.abs(e.deltaX) < Math.abs(e.deltaY)) return;\n track.scrollLeft += e.deltaX;\n });\n\n cards.forEach((c, i) => {\n const tile = document.createElement('div');\n tile.setAttribute('data-card-index', String(i));\n tile.style.cssText = `\n flex: 0 0 auto; width: 140px; scroll-snap-align: start;\n background: ${fg}0a; border-radius: 12px; padding: 10px;\n display: flex; flex-direction: column; gap: 6px;\n cursor: ${c.cta_url ? 'pointer' : 'default'};\n `;\n if (c.image_url) {\n const img = document.createElement('img');\n const safe = sanitizeUrl(c.image_url);\n if (safe) {\n img.src = safe;\n img.alt = '';\n img.loading = 'lazy';\n img.style.cssText = 'width: 100%; height: 96px; border-radius: 8px; object-fit: cover;';\n tile.appendChild(img);\n }\n }\n if (c.title) {\n const t = document.createElement('div');\n t.textContent = c.title;\n t.style.cssText = 'font-weight: 600; font-size: 13px; line-height: 1.3;';\n tile.appendChild(t);\n }\n if (c.body) {\n const b = document.createElement('div');\n b.textContent = c.body;\n b.style.cssText = 'font-size: 11.5px; opacity: 0.72; line-height: 1.3;';\n tile.appendChild(b);\n }\n if (c.cta_text && c.cta_url) {\n const cta = document.createElement('button');\n cta.textContent = c.cta_text;\n cta.style.cssText = `\n margin-top: auto; background: ${fg}; color: ${bg};\n border: none; padding: 6px 10px; border-radius: 999px;\n font-size: 12px; font-weight: 600; cursor: pointer;\n `;\n const goto = (e: Event) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked');\n const safe = sanitizeUrl(c.cta_url!);\n if (safe) window.location.href = safe;\n };\n cta.addEventListener('click', goto);\n tile.appendChild(cta);\n tile.addEventListener('click', goto);\n }\n track.appendChild(tile);\n });\n card.appendChild(track);\n\n // Dots + autoplay\n const dots = document.createElement('div');\n dots.style.cssText = 'display: flex; justify-content: center; gap: 6px; margin-top: 10px;';\n const dotEls: HTMLSpanElement[] = [];\n cards.forEach(() => {\n const dot = document.createElement('span');\n dot.style.cssText = `\n width: 6px; height: 6px; border-radius: 999px; background: ${fg};\n opacity: 0.25; transition: opacity 0.2s;\n `;\n dotEls.push(dot);\n dots.appendChild(dot);\n });\n card.appendChild(dots);\n\n const setActive = (idx: number) => {\n dotEls.forEach((d, i) => (d.style.opacity = i === idx ? '0.9' : '0.25'));\n };\n setActive(0);\n\n let activeIdx = 0;\n const tiles = track.querySelectorAll<HTMLDivElement>('[data-card-index]');\n const goto = (idx: number) => {\n activeIdx = ((idx % cards.length) + cards.length) % cards.length;\n const el = tiles[activeIdx];\n if (el) {\n el.scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });\n setActive(activeIdx);\n }\n };\n\n let autoplayTimer: ReturnType<typeof setInterval> | null = null;\n if (autoplay > 0 && cards.length > 1) {\n autoplayTimer = setInterval(() => {\n const next = activeIdx + 1;\n if (next >= cards.length && !loop) {\n if (autoplayTimer) clearInterval(autoplayTimer);\n return;\n }\n goto(next);\n }, autoplay);\n }\n\n overlay.addEventListener('remove', () => {\n if (autoplayTimer) clearInterval(autoplayTimer);\n });\n\n // Observe scroll to keep dots in sync when the user swipes.\n track.addEventListener('scroll', () => {\n const approx = Math.round(track.scrollLeft / 150);\n if (approx !== activeIdx && approx >= 0 && approx < cards.length) {\n activeIdx = approx;\n setActive(activeIdx);\n }\n });\n\n overlay.appendChild(card);\n document.body.appendChild(overlay);\n}\n","/**\n * StickyBar renderer — a pinned notification strip at the top or bottom\n * of the viewport. Used for free-shipping hints, flash-sale banners,\n * cart-reminder strips. Dismissible, optionally auto-hiding.\n *\n * interactive_config:\n * sticky_position: 'top' | 'bottom' (required)\n * sticky_bg_color?: string\n * sticky_dismissible?: boolean default true\n * sticky_auto_hide_ms?: number 0 = never\n */\n\nimport type { RenderContext } from './types';\n\n// Persist per-campaign dismissal across reloads so we don't re-show a\n// bar the user already closed. Scoped per campaign ID; storage key\n// survives navigation but is cleared when the user explicitly clears site data.\nconst DISMISS_STORAGE_PREFIX = 'aegis_sticky_dismissed:';\n\nexport function renderStickyBar(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const position = ic.sticky_position === 'top' ? 'top' : 'bottom';\n const dismissible = ic.sticky_dismissible !== false;\n const autoHide = Number(ic.sticky_auto_hide_ms) || 0;\n\n // Already dismissed this session? Skip silently — the prefetch bundle\n // keeps re-offering it, but the user's explicit close stands until TTL.\n try {\n if (\n typeof localStorage !== 'undefined' &&\n localStorage.getItem(DISMISS_STORAGE_PREFIX + campaign.id)\n ) {\n log(`sticky_bar ${campaign.id} suppressed — user dismissed earlier`);\n return;\n }\n } catch {\n // localStorage blocked — fall through and render\n }\n\n addAnimationStyles();\n\n const bg = sanitizeColor(\n (ic.sticky_bg_color as string) || (campaign.background_color as string) || '#4169e1',\n );\n const fg = sanitizeColor((campaign.text_color as string) || '#ffffff');\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-sticky-bar';\n bar.setAttribute('data-campaign-id', campaign.id);\n\n const positionCss =\n position === 'top'\n ? 'top: 0; left: 0; right: 0; animation: aegisSlideDown 0.3s ease-out;'\n : 'bottom: 0; left: 0; right: 0; animation: aegisSlideInFromBottom 0.3s ease-out;';\n\n bar.style.cssText = `\n position: fixed; ${positionCss}\n background: ${bg}; color: ${fg};\n padding: 10px 14px; z-index: 999998;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n display: flex; align-items: center; gap: 12px; justify-content: center;\n box-shadow: 0 ${position === 'top' ? '2px' : '-2px'} 8px rgba(0,0,0,0.08);\n `;\n\n const label = document.createElement('div');\n label.style.cssText = 'flex: 0 1 auto; font-size: 13px; font-weight: 500; text-align: center;';\n const strong = document.createElement('strong');\n strong.textContent = campaign.title;\n strong.style.cssText = 'margin-right: 6px; font-weight: 700;';\n label.appendChild(strong);\n label.appendChild(document.createTextNode(campaign.body));\n bar.appendChild(label);\n\n if (campaign.action_url && campaign.button_text) {\n const cta = document.createElement('button');\n cta.textContent = campaign.button_text;\n cta.style.cssText = `\n background: ${fg}; color: ${bg};\n border: none; padding: 6px 14px; border-radius: 999px;\n font-size: 12px; font-weight: 700; cursor: pointer; white-space: nowrap;\n `;\n cta.addEventListener('click', () => {\n trackEvent(campaign.id, 'clicked');\n const safe = sanitizeUrl(campaign.action_url!);\n if (safe) window.location.href = safe;\n });\n bar.appendChild(cta);\n }\n\n let autoHideTimer: ReturnType<typeof setTimeout> | null = null;\n const remove = (persist: boolean) => {\n if (autoHideTimer) clearTimeout(autoHideTimer);\n if (persist) {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(DISMISS_STORAGE_PREFIX + campaign.id, '1');\n }\n } catch {\n // swallow — localStorage may be disabled\n }\n }\n bar.remove();\n };\n\n if (dismissible) {\n const close = document.createElement('button');\n close.textContent = '✕';\n close.setAttribute('aria-label', 'Dismiss');\n close.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 16px; cursor: pointer; padding: 0 4px; opacity: 0.75;\n `;\n close.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n remove(true);\n });\n bar.appendChild(close);\n }\n\n if (autoHide > 0) {\n autoHideTimer = setTimeout(() => remove(false), autoHide);\n }\n\n document.body.appendChild(bar);\n}\n","/**\n * ProgressBar renderer — a persistent bar that shows the user's progress\n * toward a reward threshold (free shipping, loyalty tier, referral bonus).\n *\n * Two sources of the current value:\n * 'client' — cheap: read from window.Shopify / WooCommerce / Magento\n * cart globals. Untrusted; fine for visual-only hints.\n * 'sse' — trusted: cell-plane pushes current_value via SSE to the\n * already-shipped realtime server. For cart-gated rewards\n * (must agree with server-side checkout guard), prefer SSE.\n *\n * For Phase 1 we implement CLIENT mode end-to-end. SSE mode reads\n * `window.__aegisProgressSSE[campaign.id]` if populated by a parent app\n * — higher-level integration (the SSE bridge) can push values there.\n *\n * interactive_config:\n * progress_goal_type: 'cart_total' | 'items_in_cart' | 'loyalty_points' | 'referrals'\n * progress_threshold: number\n * progress_reward_text?: string\n * progress_source?: 'client' | 'sse' default 'client'\n */\n\nimport type { RenderContext } from './types';\n\ntype GoalType = 'cart_total' | 'items_in_cart' | 'loyalty_points' | 'referrals';\n\nexport function renderProgressBar(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const goalType = (ic.progress_goal_type as GoalType) || 'cart_total';\n const threshold = Number(ic.progress_threshold);\n if (!(threshold > 0)) {\n log('progress_bar requires a positive progress_threshold — skipping', 'warn');\n return;\n }\n const rewardText =\n (ic.progress_reward_text as string) || (campaign.body as string) || 'Unlocked!';\n const source = ic.progress_source === 'sse' ? 'sse' : 'client';\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#f8fafc');\n const fill = sanitizeColor((campaign.text_color as string) || '#4169e1');\n const fg = '#0f172a';\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-progress-bar';\n bar.setAttribute('data-campaign-id', campaign.id);\n bar.style.cssText = `\n position: fixed; left: 12px; right: 12px; bottom: 12px;\n max-width: 540px; margin: 0 auto;\n background: ${bg}; color: ${fg}; border-radius: 12px;\n padding: 10px 14px; z-index: 999997;\n box-shadow: 0 8px 20px rgba(0,0,0,0.08);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `;\n\n const label = document.createElement('div');\n label.style.cssText = 'display: flex; justify-content: space-between; font-size: 13px; font-weight: 600; margin-bottom: 6px;';\n\n const leading = document.createElement('span');\n leading.textContent = campaign.title;\n label.appendChild(leading);\n\n const numeric = document.createElement('span');\n numeric.style.cssText = 'opacity: 0.7; font-weight: 500;';\n label.appendChild(numeric);\n bar.appendChild(label);\n\n const shell = document.createElement('div');\n shell.style.cssText = `\n height: 8px; border-radius: 999px; background: ${fill}22;\n overflow: hidden;\n `;\n const fillEl = document.createElement('div');\n fillEl.style.cssText = `\n height: 100%; border-radius: 999px; background: ${fill};\n width: 0%; transition: width 0.4s cubic-bezier(.4,0,.2,1);\n `;\n shell.appendChild(fillEl);\n bar.appendChild(shell);\n\n const footnote = document.createElement('div');\n footnote.style.cssText = 'font-size: 11.5px; opacity: 0.65; margin-top: 6px;';\n bar.appendChild(footnote);\n\n const readCurrentValue = (): number => {\n if (source === 'sse') {\n const hook = (window as unknown as {\n __aegisProgressSSE?: Record<string, number>;\n }).__aegisProgressSSE;\n return (hook && typeof hook[campaign.id] === 'number' ? hook[campaign.id] : 0) as number;\n }\n // client mode — read the platform cart globals the widget manager\n // already normalises (Shopify/Woo/Magento). We peek at the same\n // locations without pulling in AegisWidgetManager's 2k-line module.\n try {\n if (goalType === 'cart_total' || goalType === 'items_in_cart') {\n const win = window as unknown as {\n Shopify?: { checkout?: { total_price?: string | number } };\n aegis_cart?: { cart_total?: number; cart_items?: unknown[] };\n localStorage?: Storage;\n };\n if (win.Shopify?.checkout) {\n const v = parseFloat(String(win.Shopify.checkout.total_price || 0));\n return goalType === 'cart_total' ? v : 0;\n }\n if (win.aegis_cart) {\n if (goalType === 'items_in_cart') {\n return Array.isArray(win.aegis_cart.cart_items) ? win.aegis_cart.cart_items.length : 0;\n }\n return Number(win.aegis_cart.cart_total || 0);\n }\n // Magento cache fallback\n try {\n const cacheStr = window.localStorage?.getItem('mage-cache-storage');\n if (cacheStr) {\n const cache = JSON.parse(cacheStr) as { cart?: { subtotalAmount?: number; items?: unknown[] } };\n const cart = cache.cart;\n if (cart) {\n return goalType === 'cart_total'\n ? Number(cart.subtotalAmount || 0)\n : Array.isArray(cart.items)\n ? cart.items.length\n : 0;\n }\n }\n } catch {\n /* noop */\n }\n }\n // loyalty_points / referrals are expected to flow via SSE/push; in\n // client mode we cannot trust a local counter.\n return 0;\n } catch {\n return 0;\n }\n };\n\n let lastFiredUnlock = false;\n const update = () => {\n const cur = readCurrentValue();\n const pct = Math.max(0, Math.min(100, (cur / threshold) * 100));\n fillEl.style.width = `${pct}%`;\n numeric.textContent = `${cur.toFixed(0)} / ${threshold.toFixed(0)}`;\n if (pct >= 100) {\n footnote.textContent = rewardText;\n if (!lastFiredUnlock) {\n lastFiredUnlock = true;\n trackEvent(campaign.id, 'clicked'); // \"unlocked\" recorded as click\n }\n } else {\n const remaining = Math.max(0, threshold - cur);\n footnote.textContent = `Add ${remaining.toFixed(0)} more to unlock: ${rewardText}`;\n }\n };\n\n update();\n\n // Client-mode cart state changes fire events on the storefront globals\n // (Shopify emits `cart:updated`, Woo emits its own). Poll fallback at\n // 1s — negligible overhead because every tick is synchronous.\n const pollTimer = setInterval(update, 1000);\n const cleanup = () => clearInterval(pollTimer);\n window.addEventListener('beforeunload', cleanup, { once: true });\n bar.addEventListener('remove', cleanup);\n\n document.body.appendChild(bar);\n}\n","/**\n * CoachmarkTour renderer — a guided, multi-step walkthrough anchored to\n * DOM elements via CSS selectors. One tooltip appears at a time with\n * next/back navigation; completion + skip are persisted per contact via\n * localStorage under the `resume_key`, so an abandoned tour resumes on\n * the next visit instead of restarting or re-bothering the user.\n *\n * interactive_config:\n * steps: Array<{\n * anchor_web?: string, // CSS selector for THIS platform\n * anchor_android?: string, // ignored on web\n * anchor_ios?: string, // ignored on web\n * title: string,\n * body: string,\n * placement?: 'top' | 'bottom' | 'left' | 'right',\n * cta_text?: string,\n * }>\n * resume_key: string,\n * allow_skip?: boolean,\n * show_progress_dots?: boolean,\n */\n\nimport type { RenderContext } from './types';\n\ntype Step = {\n anchor_web?: string;\n anchor_android?: string;\n anchor_ios?: string;\n title: string;\n body: string;\n placement?: 'top' | 'bottom' | 'left' | 'right';\n cta_text?: string;\n};\n\nconst RESUME_PREFIX = 'aegis_coachmark_progress:';\n\nfunction readResumeIdx(resumeKey: string): number {\n try {\n if (typeof localStorage === 'undefined') return 0;\n const raw = localStorage.getItem(RESUME_PREFIX + resumeKey);\n const n = raw ? parseInt(raw, 10) : 0;\n return Number.isFinite(n) && n >= 0 ? n : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeResumeIdx(resumeKey: string, idx: number): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(RESUME_PREFIX + resumeKey, String(idx));\n }\n } catch {\n /* noop */\n }\n}\n\nfunction clearResume(resumeKey: string): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(RESUME_PREFIX + resumeKey);\n }\n } catch {\n /* noop */\n }\n}\n\nexport function renderCoachmarkTour(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const resumeKey = ic.resume_key as string | undefined;\n if (!resumeKey) {\n log('coachmark_tour requires interactive_config.resume_key — skipping', 'warn');\n return;\n }\n\n const steps = Array.isArray(ic.steps) ? (ic.steps as Step[]) : [];\n if (steps.length === 0) {\n log('coachmark_tour has no steps — skipping', 'warn');\n return;\n }\n\n const allowSkip = ic.allow_skip !== false;\n const showDots = ic.show_progress_dots !== false;\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#0f172a');\n const fg = sanitizeColor((campaign.text_color as string) || '#ffffff');\n\n // Resume or start at 0.\n let current = readResumeIdx(resumeKey);\n if (current >= steps.length) {\n log(`coachmark_tour ${resumeKey} already complete — not re-showing`);\n return;\n }\n\n // Track the single overall impression once per mount.\n trackEvent(campaign.id, 'impression');\n\n let pointerEl: HTMLElement | null = null;\n let tipEl: HTMLElement | null = null;\n let highlightEl: HTMLElement | null = null;\n\n const cleanupOne = () => {\n pointerEl?.remove();\n tipEl?.remove();\n highlightEl?.remove();\n pointerEl = null;\n tipEl = null;\n highlightEl = null;\n };\n\n const finish = (opts: { skipped: boolean }) => {\n cleanupOne();\n if (opts.skipped) {\n trackEvent(campaign.id, 'dismissed');\n } else {\n trackEvent(campaign.id, 'clicked'); // completion\n }\n // Mark tour as complete so we never restart it for this contact.\n writeResumeIdx(resumeKey, steps.length);\n };\n\n const showStep = (idx: number) => {\n cleanupOne();\n const step = steps[idx];\n if (!step) {\n finish({ skipped: false });\n return;\n }\n writeResumeIdx(resumeKey, idx);\n\n const selector = step.anchor_web;\n let anchor: Element | null = null;\n if (selector) {\n try {\n anchor = document.querySelector(selector);\n } catch {\n anchor = null;\n }\n }\n if (!anchor) {\n log(`coachmark step ${idx} — selector '${selector}' not found; advancing`, 'warn');\n showStep(idx + 1);\n return;\n }\n\n const rect = anchor.getBoundingClientRect();\n // Slight highlight around the anchor so the user's eye snaps to it.\n highlightEl = document.createElement('div');\n highlightEl.style.cssText = `\n position: fixed;\n top: ${rect.top - 6}px; left: ${rect.left - 6}px;\n width: ${rect.width + 12}px; height: ${rect.height + 12}px;\n border-radius: 10px; z-index: 999998;\n box-shadow: 0 0 0 2px ${fg}, 0 0 0 9999px rgba(0,0,0,0.4);\n pointer-events: none;\n transition: all 0.2s ease;\n `;\n document.body.appendChild(highlightEl);\n\n const placement = step.placement || 'bottom';\n tipEl = document.createElement('div');\n tipEl.setAttribute('data-campaign-id', campaign.id);\n tipEl.style.cssText = `\n position: fixed; z-index: 999999;\n background: ${bg}; color: ${fg};\n padding: 12px 14px; border-radius: 12px;\n max-width: 260px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.25);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisFadeIn 0.2s ease;\n `;\n\n const titleEl = document.createElement('div');\n titleEl.textContent = step.title;\n titleEl.style.cssText = 'font-weight: 700; font-size: 13px; margin-bottom: 4px;';\n tipEl.appendChild(titleEl);\n\n const bodyEl = document.createElement('div');\n bodyEl.textContent = step.body;\n bodyEl.style.cssText = 'font-size: 12.5px; line-height: 1.4; opacity: 0.9;';\n tipEl.appendChild(bodyEl);\n\n const controls = document.createElement('div');\n controls.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-top: 10px; gap: 8px;';\n\n // Dots\n if (showDots) {\n const dots = document.createElement('div');\n dots.style.cssText = 'display: flex; gap: 4px;';\n steps.forEach((_, i) => {\n const d = document.createElement('span');\n d.style.cssText = `\n width: 5px; height: 5px; border-radius: 999px;\n background: ${fg}; opacity: ${i === idx ? '0.95' : '0.3'};\n `;\n dots.appendChild(d);\n });\n controls.appendChild(dots);\n } else {\n controls.appendChild(document.createElement('span'));\n }\n\n const buttons = document.createElement('div');\n buttons.style.cssText = 'display: flex; gap: 6px;';\n\n if (allowSkip && idx < steps.length - 1) {\n const skip = document.createElement('button');\n skip.textContent = 'Skip';\n skip.style.cssText = `\n background: transparent; color: inherit; opacity: 0.7;\n border: none; font-size: 12px; cursor: pointer; padding: 6px 8px;\n `;\n skip.addEventListener('click', () => finish({ skipped: true }));\n buttons.appendChild(skip);\n }\n\n const next = document.createElement('button');\n const isLast = idx === steps.length - 1;\n next.textContent = step.cta_text || (isLast ? 'Done' : 'Next');\n next.style.cssText = `\n background: ${fg}; color: ${bg};\n border: none; padding: 6px 12px; border-radius: 999px;\n font-size: 12px; font-weight: 700; cursor: pointer;\n `;\n next.addEventListener('click', () => {\n if (isLast) {\n finish({ skipped: false });\n } else {\n showStep(idx + 1);\n }\n });\n buttons.appendChild(next);\n\n controls.appendChild(buttons);\n tipEl.appendChild(controls);\n document.body.appendChild(tipEl);\n\n // Position after insertion so we know the tip's own dimensions.\n const tipRect = tipEl.getBoundingClientRect();\n const margin = 12;\n let top = rect.bottom + margin;\n let left = rect.left;\n if (placement === 'top') {\n top = rect.top - tipRect.height - margin;\n } else if (placement === 'left') {\n top = rect.top;\n left = rect.left - tipRect.width - margin;\n } else if (placement === 'right') {\n top = rect.top;\n left = rect.right + margin;\n }\n // Clamp to viewport.\n top = Math.max(8, Math.min(window.innerHeight - tipRect.height - 8, top));\n left = Math.max(8, Math.min(window.innerWidth - tipRect.width - 8, left));\n tipEl.style.top = `${top}px`;\n tipEl.style.left = `${left}px`;\n };\n\n showStep(current);\n\n // If the host page unloads mid-tour, leave the current index persisted so\n // the next session resumes from there. Nothing else to do — writeResumeIdx\n // was called in showStep.\n // Expose a cancel hook so app code can reset the tour from Dev Tools etc.\n (window as unknown as { aegisResetTour?: (key: string) => void }).aegisResetTour = (key: string) => clearResume(key);\n}\n","/**\n * AegisInAppManager - Web SDK In-App Messaging Module\n *\n * HTTP polling architecture with client-side evaluation.\n *\n * Security:\n * - XSS Protection: Uses DOM APIs (createElement, textContent) instead of innerHTML\n * - URL Validation: Prevents javascript: protocol injection\n * - CSP Compatible: No inline styles in HTML strings\n *\n * Architecture (RFC 2026-04-16):\n * - Polls /v1/in-app/active on initialize + on identity change\n * - Caches campaigns locally in memory\n * - Evaluates trigger rules client-side\n * - Tracks events (impression, click, dismiss) asynchronously\n *\n * NOTE: Persistent SSE for downstream delivery is disabled by default\n * (enableSSE=false). Tenant dashboards that need realtime (inbox, chat,\n * live page) open SSE directly via Next.js API routes — not via this SDK.\n * End-user storefronts use polling + Web Push for background delivery.\n */\n\nimport type { RenderContext } from './renderers';\nimport {\n renderCarouselCards,\n renderCoachmarkTour,\n renderProductRecommendation,\n renderProgressBar,\n renderStickyBar,\n} from './renderers';\nimport { Storage } from '../utils/storage';\n\n// Read the SDK's persisted anonymous_id from localStorage/cookie if available.\n// Returns undefined when called server-side (no document/window) or when no\n// id has been minted yet — caller falls through to leaving userId unset.\nfunction readAnonIdFromStorage(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const storage = new Storage();\n return storage.get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface InAppCampaign {\n id: string;\n type:\n | 'modal'\n | 'banner'\n | 'tooltip'\n | 'full_screen'\n | 'half_interstitial'\n | 'alert'\n | 'pip'\n // Preload-first display types (2026-04-22 §5.4 of the expansion plan)\n | 'carousel_cards'\n | 'sticky_bar'\n | 'progress_bar'\n | 'coachmark_tour'\n | 'product_recommendation';\n sub_type?: string;\n title: string;\n body: string;\n image_url?: string;\n video_url?: string;\n action_url?: string;\n button_text?: string;\n background_color?: string;\n text_color?: string;\n priority: number;\n expires_at?: string;\n frequency?: {\n max_impressions?: number;\n max_impressions_per_day?: number;\n cooldown_seconds?: number;\n // Conversion-aware suppression — added by Micro-Intent Engine P0a (2026-04-30).\n // Mirrors cell-plane FrequencyCap. When the host app calls\n // `notifyConversion(goalName)`, every armed campaign whose\n // `suppress_after_conversion_seconds` is set gets silenced for that\n // many seconds (session-scoped via sessionStorage). Prevents the\n // \"showing 10% off to a buyer who just paid full price\" failure mode.\n suppress_after_conversion_seconds?: number;\n suppress_after_dismiss_seconds?: number;\n scope?: 'session' | 'user' | 'user_sku';\n };\n interactive_config?: Record<string, unknown>;\n client_trigger?: {\n type: string;\n config?: Record<string, unknown>;\n };\n assigned_variant_id?: string;\n // F2 — campaign-delivery surface selection. When the array contains\n // 'embedded_card' AND the page has a `[data-aegis-slot=\"<widget_category>\"]`\n // anchor, the SDK renders this campaign INTO the slot (in addition to or\n // instead of an overlay; see `renderIntoSlots`). Defaults to\n // `['in_app_overlay']` server-side so legacy campaigns keep auto-overlay\n // behavior unchanged.\n delivery_modes?: Array<'in_app_overlay' | 'embedded_card' | 'standalone_page'>;\n // F3 — wide grouping discriminator used as the slot lookup key. A campaign\n // with `widget_category='feedback'` will fill the first\n // `[data-aegis-slot=\"feedback\"]` element on the page (if `embedded_card` is\n // among its delivery_modes).\n widget_category?: 'feedback' | 'loyalty' | 'promo' | 'commerce' | 'support' | 'custom';\n // F2 — page-key for `standalone_page` delivery mode. Read by the Next.js\n // route handler in `apps/cashier-portal/src/app/s/[slug]/[workspace]/[page_key]`.\n // Matches the existing storefront vocabulary (`rewards`/`members`)\n // in apps/cashier-portal/src/app/s/[slug]/{rewards,members}/page.tsx.\n page_key?: 'bill' | 'rewards' | 'feedback' | 'reviews' | 'members';\n}\n\n/**\n * Payload for `campaign-click` lifecycle event. CANCELLABLE — return `false`\n * (or call `evt.preventDefault()`) from the handler to suppress the SDK's\n * default `window.location.href` navigation, e.g. to route via a SPA router.\n */\nexport interface CampaignClickEvent {\n campaign: InAppCampaign;\n /** Sanitized destination URL — already passed through `sanitizeUrl()`. */\n action_url: string;\n /** Best-effort button identifier — populated when the click came from a\n * named button in a multi-button campaign (modals, alerts). `cta` for the\n * primary CTA on single-button campaigns. */\n button_id?: string;\n /** Mutate via `evt.preventDefault()` to signal the SDK to skip its\n * default hard-navigation. Identical semantics to DOM CustomEvent. */\n preventDefault: () => void;\n defaultPrevented: boolean;\n}\n\n/** Payload for `campaign-dismiss` lifecycle event. */\nexport interface CampaignDismissEvent {\n campaign: InAppCampaign;\n /** Source of dismissal — close button, swipe, ESC key, programmatic. */\n source: 'close_button' | 'overlay' | 'esc' | 'auto_timeout' | 'programmatic';\n}\n\n/** Payload for `error` lifecycle event. Aggregates non-fatal SDK errors so\n * host apps can wire them into Sentry / Datadog RUM / similar. */\nexport interface AegisErrorEvent {\n message: string;\n error: unknown;\n /** Optional structured context — `{ event: 'campaign-click' }` for handler\n * throws, `{ campaign_id, stage }` for render path failures, etc. */\n context: Record<string, unknown>;\n}\n\n/**\n * Map of lifecycle event name → handler signature. Used by the typed\n * `on(event, handler)` overload below so TS correctly narrows the payload\n * type per event without a discriminated union at the call site.\n */\nexport interface InAppLifecycleEventMap {\n 'campaigns-loaded': (campaigns: InAppCampaign[]) => void;\n 'campaign-will-show': (campaign: InAppCampaign) => void | false;\n 'campaign-shown': (campaign: InAppCampaign) => void;\n 'campaign-click': (evt: CampaignClickEvent) => void | false;\n 'campaign-dismiss': (evt: CampaignDismissEvent) => void;\n 'error': (evt: AegisErrorEvent) => void;\n}\n\nexport interface AegisInAppConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n organizationId?: string;\n // Resolved by POST /v1/sdk/bootstrap — required in production. Campaigns\n // with a non-empty `target_properties` list are filtered against this on\n // the server, so passing the wrong id (or omitting it) narrows visible\n // campaigns to the property-agnostic ones only.\n propertyId?: string;\n debugMode?: boolean;\n enableSSE?: boolean;\n /**\n * Callback invoked when the manager receives an in-app campaign with\n * a gamification sub_type (spin_wheel / scratch_card). The facade\n * (AegisMessageRuntime) wires this to the widget-renderer path so\n * interactive campaigns render correctly. When this is undefined,\n * gamification campaigns are logged and skipped.\n */\n onInteractiveCampaign?: (campaign: InAppCampaign) => void;\n /**\n * Workspace cascade reader. Set this from the host SDK as\n * `getWorkspaceId: () => aegis.getEffectiveWorkspaceId()` so in-app\n * impressions/clicks/dismisses carry the same workspace context the\n * main analytics SDK sees (URL path slug, ?ws=, setWorkspace runtime).\n * Without it, in-app engagement events arrive at event-ingress with\n * no workspace_id stamped → cross-outlet attribution silently falls\n * back to the org's primary workspace.\n */\n getWorkspaceId?: () => string | undefined;\n}\n\nexport class AegisInAppManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private organizationId?: string;\n private propertyId?: string;\n private debugMode: boolean;\n private enableSSE: boolean;\n private onInteractiveCampaign?: (campaign: InAppCampaign) => void;\n private getWorkspaceId?: () => string | undefined;\n\n private campaigns: InAppCampaign[] = [];\n private displayedCampaigns = new Set<string>();\n // Conversion-aware suppression — campaign_id -> epoch_ms when suppression expires.\n // Populated by `notifyConversion()` and rehydrated from sessionStorage in\n // `refreshCampaigns()` so a navigation between pages preserves silence.\n private suppressedUntil = new Map<string, number>();\n private eventSource?: EventSource;\n private isInitialized = false;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n // sessionStorage key prefix — one entry per goal so we can introspect what\n // converted and when. Format: { ts: epoch_ms }.\n private static readonly CONVERSION_STORAGE_PREFIX = 'aegis_conv_';\n\n // ── Lifecycle event bus (1.8.0) ────────────────────────────────────────\n // Typed callback registry — host apps register handlers via the public\n // `on*` methods below. Each handler is stored once; calling the\n // returned unsubscribe fn removes it. Multiple subscribers per event are\n // supported. Callbacks that return `false` (or call `evt.preventDefault()`\n // on cancellable events) signal the SDK to skip its default behaviour.\n //\n // Industry parity: matches CleverTap `notificationCallback`, WebEngage\n // `onCallback`, MoEngage `onSelfHandledClick`, Iterable url-delegate, etc.\n // The DOM CustomEvent path (window.dispatchEvent('aegis:campaign-click'))\n // is preserved for backward compat + advanced multi-subscriber + pre-init\n // patterns where typed methods aren't reachable yet.\n private hooks = new Map<string, Set<(...args: unknown[]) => void | false | undefined>>();\n // `aegis.inApp.ready` resolves when the first refreshCampaigns completes\n // — matches CleverTap `onLoad`, WebEngage `onReady`. Lets host apps await\n // a known-good state before reading `campaigns` or registering handlers\n // that need armed campaigns visible.\n private readyResolve!: () => void;\n readonly ready: Promise<void> = new Promise((resolve) => {\n this.readyResolve = resolve;\n });\n\n private emit<T>(eventName: string, payload: T): boolean {\n const handlers = this.hooks.get(eventName);\n if (!handlers || handlers.size === 0) return true;\n let proceed = true;\n for (const handler of handlers) {\n try {\n const result = (handler as (p: T) => void | false | undefined)(payload);\n if (result === false) proceed = false;\n } catch (err) {\n // Don't let a misbehaving host handler crash the SDK render path.\n // Surface via onError but keep going for the remaining handlers.\n this.emitError(err, { event: eventName });\n }\n }\n return proceed;\n }\n\n private emitError(err: unknown, context?: Record<string, unknown>): void {\n const handlers = this.hooks.get('error');\n if (!handlers || handlers.size === 0) {\n // No subscriber — log to console so errors aren't completely silent.\n this.log(`Aegis SDK error: ${err instanceof Error ? err.message : String(err)}`, 'error');\n return;\n }\n for (const handler of handlers) {\n try {\n (handler as (e: AegisErrorEvent) => void)({\n message: err instanceof Error ? err.message : String(err),\n error: err,\n context: context ?? {},\n });\n } catch {\n // ignore — we already tried to surface\n }\n }\n }\n\n private register<T>(\n eventName: string,\n handler: (payload: T) => void | false | undefined,\n ): () => void {\n if (!this.hooks.has(eventName)) this.hooks.set(eventName, new Set());\n this.hooks.get(eventName)!.add(handler as (...args: unknown[]) => void | false | undefined);\n return () => {\n this.hooks.get(eventName)?.delete(handler as (...args: unknown[]) => void | false | undefined);\n };\n }\n\n /**\n * Subscribe to in-app message lifecycle events. Returns an unsubscribe\n * function — call to remove the handler (matches React `useEffect` cleanup\n * convention).\n *\n * Supported events: `campaigns-loaded`, `campaign-will-show` (cancellable),\n * `campaign-shown`, `campaign-click` (cancellable), `campaign-dismiss`,\n * `error`. Cancellable events: handler returns `false` to suppress the\n * SDK's default behaviour (e.g., prevent the hard navigation on click,\n * skip rendering on will-show).\n */\n on<E extends keyof InAppLifecycleEventMap>(\n event: E,\n handler: InAppLifecycleEventMap[E],\n ): () => void {\n return this.register(event, handler as never);\n }\n\n /** Sugar: subscribe to `campaigns-loaded`. Fires after every successful\n * `/v1/in-app/active` poll with the full active-campaign list. */\n onCampaignsLoaded(\n handler: (campaigns: InAppCampaign[]) => void,\n ): () => void {\n return this.register('campaigns-loaded', handler);\n }\n\n /** Sugar: subscribe to `campaign-will-show`. CANCELLABLE — return `false`\n * to suppress rendering this campaign (host-side suppression rules,\n * route-aware blocking, etc.). */\n onCampaignWillShow(\n handler: (campaign: InAppCampaign) => void | false,\n ): () => void {\n return this.register('campaign-will-show', handler);\n }\n\n /** Sugar: subscribe to `campaign-shown`. Fires after the SDK has painted\n * the campaign to the DOM (post-impression). */\n onCampaignShown(\n handler: (campaign: InAppCampaign) => void,\n ): () => void {\n return this.register('campaign-shown', handler);\n }\n\n /** Sugar: subscribe to `campaign-click`. CANCELLABLE — return `false` (or\n * call `evt.preventDefault()`) to suppress the SDK's default\n * `window.location.href` navigation, e.g. to route via a SPA router. */\n onCampaignClick(\n handler: (evt: CampaignClickEvent) => void | false,\n ): () => void {\n return this.register('campaign-click', handler);\n }\n\n /** Sugar: subscribe to `campaign-dismiss`. Fires when user closes/swipes\n * away the campaign before clicking through. */\n onCampaignDismiss(\n handler: (evt: CampaignDismissEvent) => void,\n ): () => void {\n return this.register('campaign-dismiss', handler);\n }\n\n /** Sugar: subscribe to `error`. Aggregates non-fatal SDK errors — fetch\n * failures, render exceptions, host-handler throws. Wire this into your\n * error monitoring (Sentry, Datadog RUM, etc.). */\n onError(\n handler: (evt: AegisErrorEvent) => void,\n ): () => void {\n return this.register('error', handler);\n }\n \n constructor(config: AegisInAppConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n // Fall back to the SDK's persisted anonymous_id (set on first /v1/sdk/bootstrap\n // and reused across the main analytics SDK) so anonymous storefront users\n // get journey-driven in-app messages keyed to the same identity used in\n // /v1/batch payloads. Without this, polling defaults to \"anonymous\" on the\n // server and bucket-misses every per-contact priority queue.\n this.userId = config.userId ?? readAnonIdFromStorage();\n this.contactId = config.contactId;\n this.organizationId = config.organizationId;\n this.propertyId = config.propertyId;\n this.debugMode = config.debugMode || false;\n // SSE is disabled by default. Kept as opt-in capability for future tenant\n // dashboards that embed the Aegis SDK and need realtime updates. End-user\n // storefronts must NOT enable this — persistent-per-tab SSE doesn't scale.\n this.enableSSE = config.enableSSE === true;\n this.onInteractiveCampaign = config.onInteractiveCampaign;\n this.getWorkspaceId = config.getWorkspaceId;\n }\n \n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisInApp already initialized');\n return;\n }\n\n // Mark user as returning for subsequent visits\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem('aegis_returning_user', '1');\n }\n\n await this.refreshCampaigns();\n\n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n \n this.isInitialized = true;\n this.log('AegisInApp initialized successfully');\n }\n \n updateUserId(userId: string): void {\n this.userId = userId;\n this.refreshCampaigns();\n }\n \n updateContactId(contactId: string): void {\n this.contactId = contactId;\n this.disconnectSSE();\n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n this.refreshCampaigns();\n }\n\n /**\n * Conversion-aware suppression. Call this when the host app observes a\n * goal event (purchase, order_placed, checkout_completed, or any custom\n * goal the workspace has configured per-campaign).\n *\n * For every armed campaign whose `frequency.suppress_after_conversion_seconds`\n * is set, this:\n * 1. Persists the conversion in sessionStorage (so subsequent page\n * loads in the same tab retain silence).\n * 2. Computes an expiry epoch_ms and stores it in `suppressedUntil`.\n * 3. The two campaign-evaluation paths (`tryDisplayNextCampaign`\n * and `onClientEvent`) skip campaigns whose `suppressedUntil`\n * has not yet passed.\n *\n * Scope: only `session` is implemented in P0a. `user` and `user_sku`\n * scopes degrade to session-only with a debug log; the schema accepts\n * them so future expansion is non-breaking.\n *\n * Safe to call repeatedly for the same goal — each call refreshes the\n * sessionStorage timestamp and re-applies suppression to any campaigns\n * loaded since the last call.\n */\n notifyConversion(goalName: string): void {\n if (!goalName) {\n this.log('notifyConversion called with empty goalName; ignored', 'warn');\n return;\n }\n const ts = Date.now();\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.setItem(\n `${AegisInAppManager.CONVERSION_STORAGE_PREFIX}${goalName}`,\n JSON.stringify({ ts }),\n );\n } catch {\n // sessionStorage may be disabled (Safari private mode, blocked\n // third-party context). Suppression still applies for the\n // current page lifetime via the in-memory map.\n }\n }\n this.applySuppressionFromCampaigns(ts);\n this.log(`notifyConversion: ${goalName} — suppressing ${this.suppressedUntil.size} armed campaigns`);\n }\n\n /**\n * Compute and store `suppressedUntil` entries for every armed campaign\n * whose `frequency.suppress_after_conversion_seconds` is set. Called by\n * `notifyConversion()` and `refreshCampaigns()` (the latter rehydrates\n * suppression for newly-loaded campaigns when a prior conversion event\n * exists in sessionStorage).\n *\n * `convertedAt` is the epoch_ms of the conversion; pass `Date.now()`\n * when called from `notifyConversion`, or the `ts` recovered from\n * sessionStorage when rehydrating.\n */\n private applySuppressionFromCampaigns(convertedAt: number): void {\n for (const c of this.campaigns) {\n const seconds = c.frequency?.suppress_after_conversion_seconds;\n if (typeof seconds !== 'number' || seconds <= 0) continue;\n const scope = c.frequency?.scope ?? 'session';\n if (scope !== 'session') {\n // P0a only implements session scope. Schema accepts user / user_sku\n // so future work doesn't break wire compatibility, but for now we\n // degrade to session and log.\n this.log(\n `applySuppressionFromCampaigns: scope=${scope} not implemented; degrading to session for campaign ${c.id}`,\n 'warn',\n );\n }\n const expiresAt = convertedAt + seconds * 1000;\n // Take the max so a later/longer suppression wins over an earlier one.\n const existing = this.suppressedUntil.get(c.id);\n if (existing === undefined || existing < expiresAt) {\n this.suppressedUntil.set(c.id, expiresAt);\n }\n }\n }\n\n /**\n * Recover any prior conversion timestamps from sessionStorage and apply\n * suppression to currently-loaded campaigns. Called from\n * `refreshCampaigns()` after `this.campaigns` is populated. This is the\n * mechanism that prevents a converted buyer from seeing a discount popup\n * on the next page load within the same session.\n */\n private rehydrateSuppressionFromStorage(): void {\n if (typeof sessionStorage === 'undefined') return;\n let earliestTs: number | null = null;\n try {\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i);\n if (!key || !key.startsWith(AegisInAppManager.CONVERSION_STORAGE_PREFIX)) continue;\n const raw = sessionStorage.getItem(key);\n if (!raw) continue;\n const parsed = JSON.parse(raw) as { ts?: number };\n if (typeof parsed.ts === 'number') {\n earliestTs = earliestTs === null ? parsed.ts : Math.min(earliestTs, parsed.ts);\n }\n }\n } catch {\n // Malformed entries are silently skipped — suppression simply\n // doesn't apply for them.\n return;\n }\n if (earliestTs !== null) {\n this.applySuppressionFromCampaigns(earliestTs);\n }\n }\n\n /**\n * Returns true if the campaign is currently suppressed (Date.now() is\n * before the stored expiry). Used by both display paths.\n */\n private isSuppressed(campaignId: string): boolean {\n const expiresAt = this.suppressedUntil.get(campaignId);\n if (expiresAt === undefined) return false;\n if (Date.now() >= expiresAt) {\n this.suppressedUntil.delete(campaignId);\n return false;\n }\n return true;\n }\n \n private connectSSE(): void {\n if (this.eventSource) {\n this.disconnectSSE();\n }\n \n if (!this.organizationId) {\n this.log('Cannot connect SSE without organization ID', 'warn');\n return;\n }\n \n const url = new URL('/v1/stream/realtime', this.apiHost);\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n \n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n \n const queryParams = new URLSearchParams();\n Object.entries(headers).forEach(([key, value]) => {\n queryParams.append(key, value);\n });\n \n this.eventSource = new EventSource(`${url}?${queryParams.toString()}`);\n \n this.eventSource.addEventListener('open', () => {\n this.log('SSE connection established');\n this.reconnectAttempts = 0;\n });\n \n this.eventSource.addEventListener('in_app_campaign_updated', (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data);\n this.log(`Received in-app campaign update: ${data.campaign_id}`);\n this.refreshCampaigns();\n } catch (error) {\n this.log(`Error parsing SSE event: ${error}`, 'error');\n }\n });\n \n this.eventSource.addEventListener('heartbeat', () => {\n this.log('SSE heartbeat received');\n });\n\n this.eventSource.addEventListener('error', () => {\n this.log('SSE connection error', 'error');\n\n if (this.eventSource?.readyState === EventSource.CLOSED) {\n this.attemptReconnect();\n }\n });\n }\n \n private disconnectSSE(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = undefined;\n this.log('SSE connection closed');\n }\n }\n \n private attemptReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log('Max reconnect attempts reached, giving up', 'warn');\n return;\n }\n \n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n \n this.log(`Reconnecting SSE in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n setTimeout(() => {\n if (this.isInitialized && this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n }, delay);\n }\n \n private async refreshCampaigns(): Promise<void> {\n try {\n // Build URL with client context for server-side targeting (Critical Fix B)\n const context = new URLSearchParams({\n device_type: this.detectDeviceType(),\n page_url: typeof window !== 'undefined' ? window.location.pathname : '/',\n });\n\n // Geo is resolved server-side from IP — no need to send\n context.set('is_new_user', this.isNewUser() ? 'true' : 'false');\n\n const url = `${this.apiHost}/v1/in-app/active?${context.toString()}`;\n\n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n\n if (this.userId) {\n headers['X-User-ID'] = this.userId;\n }\n\n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n\n if (this.organizationId) {\n headers['X-Organization-ID'] = this.organizationId;\n }\n\n if (this.propertyId) {\n headers['X-Property-Id'] = this.propertyId;\n }\n\n // Send cached A/B assignments so server can skip re-evaluation (Critical Fix C)\n const abAssignments = this.getABAssignments();\n if (Object.keys(abAssignments).length > 0) {\n headers['X-AB-Assignments'] = btoa(JSON.stringify(abAssignments));\n }\n\n const response = await fetch(url, {\n method: 'GET',\n headers,\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch campaigns: ${response.status}`);\n }\n\n const payload = await response.json();\n // Server returns an array; defensively handle error bodies (HTML\n // from misconfigured gateways, {} from maintenance endpoints, etc.)\n // so a bad response doesn't turn `updateContactId` into a thrower.\n this.campaigns = Array.isArray(payload) ? payload : [];\n\n // Process A/B assignments from server response (Critical Fix C)\n this.processABAssignments(this.campaigns);\n\n // Rehydrate conversion suppression from sessionStorage so a buyer\n // who converted on a prior page load in the same tab does not see\n // a discount popup on this load. Must run after this.campaigns is\n // populated and BEFORE tryDisplayNextCampaign so the first display\n // attempt honors suppression.\n this.rehydrateSuppressionFromStorage();\n\n this.log(`Fetched ${this.campaigns.length} campaigns`);\n\n // Lifecycle: `campaigns-loaded` — fires after every successful refresh\n // with the full active-campaign list. Host apps subscribe to drive\n // route-aware allowlists, sticky inboxes, badge counts, etc.\n this.emit('campaigns-loaded', this.campaigns);\n\n // Resolve `aegis.inApp.ready` after the FIRST successful poll so host\n // apps can `await aegis.inApp.ready` before reading `campaigns` or\n // making decisions that depend on a known-good initial state. Idempotent\n // — calling resolve() twice is a no-op for an already-settled promise.\n this.readyResolve();\n\n this.tryDisplayNextCampaign();\n\n // F2/F3 / U8 — fill any `[data-aegis-slot]` anchors with matching\n // `embedded_card`-mode campaigns. Independent of the overlay path:\n // a campaign with delivery_modes=[overlay,card] renders both ways\n // (overlay if conditions match + card if the page has a slot).\n // No-op when no slots are on the page.\n this.renderIntoSlots();\n\n // PR-B U7 — also fill any `[data-aegis-token-data]` anchors with\n // a server-injected campaign payload. Used by the standalone-page\n // route at `/s/[slug]/[workspace]/[page_key]?t=<token>`: the page\n // handler verifies the token server-side (via F16 internal),\n // embeds the resolved campaign data inline, and the SDK uses the\n // SAME renderer dispatch as the slot path. No separate widget\n // code, no public token-resolve endpoint.\n this.renderTokenAnchors();\n\n } catch (error) {\n this.emitError(error, { stage: 'refresh-campaigns' });\n this.log(`Error refreshing campaigns: ${error}`, 'error');\n }\n }\n\n // --- Client Context Helpers (Critical Fix B) ---\n\n private detectDeviceType(): 'mobile' | 'desktop' | 'tablet' {\n if (typeof navigator === 'undefined') return 'desktop';\n const ua = navigator.userAgent;\n if (/Mobi|Android/i.test(ua)) return 'mobile';\n if (/iPad|Tablet/i.test(ua)) return 'tablet';\n return 'desktop';\n }\n\n private isNewUser(): boolean {\n if (typeof localStorage === 'undefined') return true;\n return !localStorage.getItem('aegis_returning_user');\n }\n\n // --- A/B Assignment Persistence (Critical Fix C) ---\n\n private getABAssignments(): Record<string, string> {\n if (typeof localStorage === 'undefined') return {};\n try {\n return JSON.parse(localStorage.getItem('aegis_ab_assignments') || '{}');\n } catch {\n return {};\n }\n }\n\n private processABAssignments(campaigns: InAppCampaign[]): void {\n if (typeof localStorage === 'undefined') return;\n const stored = this.getABAssignments();\n for (const campaign of campaigns) {\n if (campaign.assigned_variant_id) {\n stored[campaign.id] = campaign.assigned_variant_id;\n }\n }\n localStorage.setItem('aegis_ab_assignments', JSON.stringify(stored));\n }\n\n private getVariantId(campaignId: string): string | undefined {\n const assignments = this.getABAssignments();\n return assignments[campaignId] ?? undefined;\n }\n \n private tryDisplayNextCampaign(): void {\n // Only auto-display campaigns that DO NOT have a client_trigger.\n // Trigger-gated campaigns wait for the host app to call\n // `onClientEvent()` or for TriggerEngine to fire the matching\n // behavioural event. This preserves the preload-first contract —\n // the campaign is armed but the render is gated.\n // Conversion-aware suppression: campaigns silenced by a prior\n // notifyConversion() call (or rehydrated from sessionStorage) are\n // skipped until their suppression window expires.\n const campaign = this.campaigns.find((c) =>\n !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id),\n );\n\n if (campaign) {\n this.displayCampaign(campaign);\n }\n }\n\n // --- F2/F3 / U8: embedded-card slot rendering ---------------------------\n //\n // Tracks slot DOM elements we've already filled so a second\n // refresh-cycle (SSE update, polling tick) doesn't re-render the same\n // campaign on top of itself. WeakSet so DOM removals get GC'd.\n private filledSlots = new WeakSet<Element>();\n\n /**\n * Scan the page for `[data-aegis-slot]` anchors and render any\n * campaign whose `widget_category` matches the slot key AND whose\n * `delivery_modes` includes `'embedded_card'`.\n *\n * Runs after every refresh-campaigns success — the WeakSet guard\n * keeps it idempotent across re-fetches. The slot key is the campaign's\n * `widget_category` (the wide grouping discriminator from F3), so a\n * `<div data-aegis-slot=\"feedback\">` will accept the first active\n * campaign with `widget_category='feedback'` + embedded_card mode.\n */\n private renderIntoSlots(): void {\n if (typeof document === 'undefined') return;\n const slots = document.querySelectorAll('[data-aegis-slot]');\n if (slots.length === 0) return;\n\n // First-active-per-category wins. Caller-side ordering of\n // `this.campaigns` is priority-sorted by the cell-plane endpoint.\n const eligibleByCategory = new Map<string, InAppCampaign>();\n for (const c of this.campaigns) {\n const modes = c.delivery_modes;\n const category = c.widget_category;\n if (!modes || !modes.includes('embedded_card')) continue;\n if (!category) continue;\n if (!eligibleByCategory.has(category)) {\n eligibleByCategory.set(category, c);\n }\n }\n if (eligibleByCategory.size === 0) return;\n\n slots.forEach((slot) => {\n if (this.filledSlots.has(slot)) return;\n const key = slot.getAttribute('data-aegis-slot');\n if (!key) return;\n const campaign = eligibleByCategory.get(key);\n if (!campaign) return;\n this.renderCampaignIntoSlot(campaign, slot as HTMLElement);\n this.filledSlots.add(slot);\n });\n }\n\n /**\n * U7 — render server-injected token-bound campaigns.\n *\n * The standalone-page route at `apps/cashier-portal/src/app/s/[slug]/\n * [workspace]/[page_key]/page.tsx` (and the `/c/[token]` Aegis-hosted\n * fallback) verify the URL token server-side via the F16 internal\n * endpoint, then embed the resolved campaign payload as JSON inside\n * a `<div data-aegis-token-data=\"...\">` anchor. This SDK method\n * scans for those anchors, parses the payload, and dispatches to\n * the SAME `renderCampaignIntoSlot` used by the [data-aegis-slot]\n * path — so widget rendering code is single-sourced.\n *\n * Why server-injected (not client-fetched): keeps INTERNAL_API_SECRET\n * server-side, avoids an extra browser round-trip for verify, gives\n * Next.js SSR-friendly initial HTML for SEO/no-JS users (rendered\n * via the page handler's React tree alongside the SDK anchor).\n *\n * Idempotency: filledSlots is the same WeakSet used by\n * renderIntoSlots — re-running on SSE/poll refresh is a no-op for\n * already-filled anchors.\n */\n private renderTokenAnchors(): void {\n if (typeof document === 'undefined') return;\n const anchors = document.querySelectorAll('[data-aegis-token-data]');\n if (anchors.length === 0) return;\n\n anchors.forEach((anchor) => {\n if (this.filledSlots.has(anchor)) return;\n const raw = anchor.getAttribute('data-aegis-token-data');\n if (!raw) return;\n\n let payload:\n | { campaign?: InAppCampaign; submit_url?: string }\n | null = null;\n try {\n payload = JSON.parse(raw);\n } catch (e) {\n this.log(\n `data-aegis-token-data: invalid JSON, skipping anchor (${e})`,\n 'warn',\n );\n this.filledSlots.add(anchor); // don't retry on next refresh\n return;\n }\n if (!payload || !payload.campaign || !payload.campaign.id) {\n this.log(\n 'data-aegis-token-data: missing `campaign` in payload, skipping',\n 'warn',\n );\n this.filledSlots.add(anchor);\n return;\n }\n\n // U7 step 3 — pass the submit_url through to the renderer so\n // click handlers can POST the structured response (rating value,\n // NPS score, etc.) and atomically consume the token.\n this.renderCampaignIntoSlot(\n payload.campaign,\n anchor as HTMLElement,\n { submitUrl: payload.submit_url },\n );\n this.filledSlots.add(anchor);\n });\n }\n\n /**\n * Slot-mode counterpart to `displayCampaign`. Reuses the existing\n * lifecycle hooks (`campaign-will-show`, `displayedCampaigns`,\n * `campaign-shown`, impression tracking) so analytics + frequency\n * caps work the same way as overlay renders.\n *\n * Only the sub_types that make UX sense embedded are handled; the\n * rest (modal, full_screen, half_interstitial, alert, pip) silently\n * skip — those types only make sense as fullscreen overlays.\n */\n private renderCampaignIntoSlot(\n campaign: InAppCampaign,\n target: HTMLElement,\n options?: { submitUrl?: string },\n ): void {\n const proceed = this.emit('campaign-will-show', campaign);\n if (!proceed) {\n this.log(\n `slot campaign ${campaign.id} suppressed by campaign-will-show handler`,\n );\n return;\n }\n\n this.displayedCampaigns.add(campaign.id);\n this.addAnimationStyles();\n\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const bg = this.sanitizeColor(campaign.background_color || '#4169e1');\n const text = this.sanitizeColor(campaign.text_color || '#ffffff');\n const submitUrl = options?.submitUrl;\n\n let rendered = false;\n switch (campaign.sub_type) {\n case 'star_rating':\n rendered = this.renderStarRatingSlot(\n campaign, ic, bg, text, target, submitUrl,\n );\n break;\n case 'nps_survey':\n rendered = this.renderNPSSurveySlot(\n campaign, ic, bg, text, target, submitUrl,\n );\n break;\n // Future sub_types (quick_poll, countdown_offer, quiz, sticky_bar,\n // progress_bar, carousel_cards, product_recommendation) get added\n // here as merchants need them embedded. Until then, fall through\n // to the warn below — campaigns will still render as overlays via\n // the parallel `tryDisplayNextCampaign` path.\n default:\n this.log(\n `slot mode not yet supported for sub_type: ${campaign.sub_type ?? campaign.type}`,\n 'warn',\n );\n // Roll back the displayed marker so the overlay path can still\n // run — slot fallback shouldn't burn the campaign.\n this.displayedCampaigns.delete(campaign.id);\n return;\n }\n\n if (!rendered) return;\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n\n /**\n * Slot-mode wrappers. They build a small card container (no overlay\n * positioning, no close button) and append the SHARED body element\n * the overlay path also uses. Body construction lives in the\n * `_buildXxxBody` helpers below so visual output stays identical\n * between overlay and slot modes — adding a renderer feature in\n * one place updates both.\n */\n private renderStarRatingSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n submitUrl?: string,\n ): boolean {\n const card = this._wrapInSlotCard(\n 'aegis-in-app-rating-card', campaign.id, bg, text,\n );\n card.appendChild(\n this._buildStarRatingBody(campaign, ic, text, 'slot', submitUrl),\n );\n target.appendChild(card);\n return true;\n }\n\n private renderNPSSurveySlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n submitUrl?: string,\n ): boolean {\n const card = this._wrapInSlotCard(\n 'aegis-in-app-nps-card', campaign.id, bg, text,\n );\n card.appendChild(\n this._buildNPSSurveyBody(campaign, ic, text, 'slot', submitUrl),\n );\n target.appendChild(card);\n return true;\n }\n\n /**\n * Token-bound submission helper. POSTs the structured response to\n * the per-token submit URL the page handler embedded. Best-effort\n * — failure is logged but doesn't throw (the customer already\n * clicked; we can't undo their interaction).\n */\n private _submitTokenResponse(\n submitUrl: string,\n payload: Record<string, unknown>,\n ): void {\n if (!submitUrl) return;\n // Fire-and-forget; we don't gate the UI on the response.\n fetch(submitUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ response: payload }),\n keepalive: true,\n }).catch((err) => {\n this.log(`token submit failed: ${err}`, 'warn');\n });\n }\n\n // --- Shared body builders (used by both overlay + slot paths) ----------\n //\n // Sizing: `'overlay'` matches the legacy fullscreen-modal scale (24px\n // padding, 18px title, 32px stars). `'slot'` is the embedded card\n // scale (20px padding, 16px title, 28px stars) — proportional, doesn't\n // dwarf merchant page chrome. Adding a new variant means a new tuple\n // here, not a new renderer.\n\n private _wrapInSlotCard(\n className: string,\n campaignId: string,\n bg: string,\n text: string,\n ): HTMLElement {\n const card = document.createElement('div');\n card.className = className;\n card.setAttribute('data-campaign-id', campaignId);\n card.style.cssText = `\n width: 100%; border-radius: 12px; overflow: hidden;\n background: ${bg}; color: ${text};\n box-shadow: 0 4px 12px rgba(0,0,0,0.06);\n `;\n return card;\n }\n\n private _buildStarRatingBody(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n text: string,\n variant: 'overlay' | 'slot',\n submitUrl?: string,\n ): HTMLElement {\n const isOverlay = variant === 'overlay';\n const padding = isOverlay ? '24px' : '20px';\n const titleSize = isOverlay ? '18px' : '16px';\n const titleWeight = isOverlay ? '700' : '600';\n const titleMargin = isOverlay ? '16px' : '12px';\n const starSize = isOverlay ? '32px' : '28px';\n const starsMarginBottom = isOverlay ? '16px' : '0';\n const hoverScale = isOverlay ? '1.2' : '1.15';\n\n const body = document.createElement('div');\n body.style.cssText = `padding: ${padding}; text-align: center;`;\n\n const title = document.createElement('div');\n title.style.cssText =\n `font-size: ${titleSize}; font-weight: ${titleWeight}; margin-bottom: ${titleMargin};`;\n title.textContent = campaign.title || 'Rate your experience';\n body.appendChild(title);\n\n const stars = document.createElement('div');\n stars.style.cssText =\n `display: flex; gap: 8px; justify-content: center;` +\n (starsMarginBottom !== '0' ? ` margin-bottom: ${starsMarginBottom};` : '');\n const maxStars = (ic.rating_scale as number) || 5;\n for (let i = 1; i <= maxStars; i++) {\n const star = document.createElement('span');\n star.style.cssText =\n `font-size: ${starSize}; cursor: pointer; transition: transform 0.1s; user-select: none;` +\n (text ? ` color: ${text};` : '');\n star.textContent = '☆'; // ☆\n const value = i;\n star.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (submitUrl) {\n this._submitTokenResponse(submitUrl, {\n sub_type: 'star_rating', value,\n });\n }\n });\n star.addEventListener('mouseenter', () => {\n star.style.transform = `scale(${hoverScale})`;\n });\n star.addEventListener('mouseleave', () => {\n star.style.transform = 'scale(1)';\n });\n stars.appendChild(star);\n }\n body.appendChild(stars);\n return body;\n }\n\n private _buildNPSSurveyBody(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n text: string,\n variant: 'overlay' | 'slot',\n submitUrl?: string,\n ): HTMLElement {\n const isOverlay = variant === 'overlay';\n const padding = isOverlay ? '24px' : '20px';\n const titleAlign = isOverlay ? 'center' : 'center';\n\n const body = document.createElement('div');\n body.style.cssText = `padding: ${padding};` +\n (isOverlay ? ' text-align: center;' : '');\n\n const title = document.createElement('div');\n title.style.cssText =\n `font-size: 16px; font-weight: ${isOverlay ? '700' : '600'}; ` +\n `margin-bottom: ${isOverlay ? '16px' : '12px'}; text-align: ${titleAlign};`;\n title.textContent =\n (ic.nps_question as string) || campaign.title ||\n 'How likely are you to recommend us?';\n body.appendChild(title);\n\n if (isOverlay) {\n // Overlay variant uses the round-pip scale + Not/Very labels\n // below — keeps the legacy modal look pixel-identical.\n const scale = document.createElement('div');\n scale.style.cssText =\n 'display: flex; gap: 4px; justify-content: center; flex-wrap: wrap; margin-bottom: 12px;';\n for (let i = 0; i <= 10; i++) {\n const btn = document.createElement('span');\n btn.style.cssText = `\n width: 28px; height: 28px; border-radius: 6px; display: flex;\n align-items: center; justify-content: center; font-size: 11px;\n font-weight: 600; cursor: pointer; background: ${text}33; color: ${text};\n transition: transform 0.1s;\n `;\n btn.textContent = String(i);\n const value = i;\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (submitUrl) {\n this._submitTokenResponse(submitUrl, {\n sub_type: 'nps_survey', value,\n });\n }\n });\n scale.appendChild(btn);\n }\n body.appendChild(scale);\n\n const labels = document.createElement('div');\n labels.style.cssText =\n 'display: flex; justify-content: space-between; font-size: 11px; opacity: 0.6; margin-bottom: 16px;';\n const notLikely = document.createElement('span');\n notLikely.textContent = 'Not likely';\n const veryLikely = document.createElement('span');\n veryLikely.textContent = 'Very likely';\n labels.appendChild(notLikely);\n labels.appendChild(veryLikely);\n body.appendChild(labels);\n return body;\n }\n\n // Slot variant: 11-column grid of bordered buttons — fits inline\n // card width without scrolling. No Not/Very labels (slot is small\n // enough that the customer sees both ends without prompting).\n const grid = document.createElement('div');\n grid.style.cssText =\n 'display: grid; grid-template-columns: repeat(11, 1fr); gap: 4px;';\n for (let n = 0; n <= 10; n++) {\n const btn = document.createElement('button');\n btn.style.cssText = `\n padding: 8px 0; border-radius: 6px; border: 1px solid ${text}33;\n background: transparent; color: ${text}; font-size: 13px; font-weight: 600;\n cursor: pointer; transition: background 0.15s;\n `;\n btn.textContent = String(n);\n const value = n;\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (submitUrl) {\n this._submitTokenResponse(submitUrl, {\n sub_type: 'nps_survey', value,\n });\n }\n });\n grid.appendChild(btn);\n }\n body.appendChild(grid);\n return body;\n }\n\n /**\n * Evaluate the currently armed campaigns against a client-side event\n * and render any that match their `client_trigger`.\n *\n * Called by the host app (e.g., the EcommerceTracker on product_viewed),\n * or by future TriggerEngine bridges. Safe to call repeatedly for the\n * same event — dedup happens via `displayedCampaigns`.\n *\n * Supported trigger types (align with the cell-plane server-side\n * `display_rules.trigger_type`):\n * - `custom_event` : fire when eventName matches config.event\n * - `product_match` : fire on `product_viewed` when eventData.product_id\n * matches config.product_id (string or string[])\n * - `delay` : client-side setTimeout — evaluated at armeng\n * time (kicked off from `displayCampaign`), not here.\n */\n onClientEvent(eventName: string, eventData: Record<string, unknown> = {}): void {\n for (const c of this.campaigns) {\n if (this.displayedCampaigns.has(c.id)) continue;\n if (!c.client_trigger) continue;\n // Conversion-aware suppression: trigger-gated campaigns silenced by\n // a prior notifyConversion() call are skipped until expiry.\n if (this.isSuppressed(c.id)) continue;\n if (this.matchesClientTrigger(c.client_trigger, eventName, eventData)) {\n this.displayCampaign(c);\n }\n }\n }\n\n private matchesClientTrigger(\n trigger: { type: string; config?: Record<string, unknown> },\n eventName: string,\n eventData: Record<string, unknown>,\n ): boolean {\n const cfg = trigger.config || {};\n switch (trigger.type) {\n case 'custom_event':\n return typeof cfg.event === 'string' && cfg.event === eventName;\n case 'product_match': {\n if (eventName !== 'product_viewed' && eventName !== 'product_view') {\n return false;\n }\n const wantedRaw = cfg.product_id;\n const wanted: string[] = Array.isArray(wantedRaw)\n ? (wantedRaw as string[])\n : typeof wantedRaw === 'string'\n ? [wantedRaw]\n : [];\n if (wanted.length === 0) return false;\n const actual = String(\n eventData.product_id ??\n eventData.productId ??\n (eventData.product as Record<string, unknown> | undefined)?.id ??\n '',\n );\n return wanted.includes(actual);\n }\n default:\n // Unknown or server-evaluated — do not auto-fire from the client.\n return false;\n }\n }\n \n private displayCampaign(campaign: InAppCampaign): void {\n // Lifecycle: `campaign-will-show` — CANCELLABLE. Host returns `false` to\n // suppress render (e.g. route-aware blocking, host-side frequency caps,\n // self-handled mode). When suppressed we DO NOT mark the campaign as\n // displayed so a subsequent `tryDisplayNextCampaign` (after the host\n // unblocks) can re-evaluate it.\n const proceed = this.emit('campaign-will-show', campaign);\n if (!proceed) {\n this.log(`campaign ${campaign.id} suppressed by campaign-will-show handler`);\n return;\n }\n\n this.displayedCampaigns.add(campaign.id);\n\n // Sub-type routing: interactive types are handled by dedicated renderers\n const interactiveSubTypes = new Set([\n 'spin_wheel', 'scratch_card', 'nps_survey', 'quiz',\n 'countdown_offer', 'star_rating', 'quick_poll',\n ]);\n\n if (campaign.sub_type && interactiveSubTypes.has(campaign.sub_type)) {\n this.renderInteractive(campaign);\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n switch (campaign.type) {\n case 'modal':\n this.renderModal(campaign);\n break;\n case 'banner':\n this.renderBanner(campaign);\n break;\n case 'full_screen':\n this.renderFullScreen(campaign);\n break;\n case 'half_interstitial':\n this.renderHalfInterstitial(campaign);\n break;\n case 'alert':\n this.renderAlert(campaign);\n break;\n case 'pip':\n this.renderPIP(campaign);\n break;\n case 'tooltip':\n this.renderTooltip(campaign);\n break;\n // Preload-first display types (2026-04-22). These renderers own their\n // own impression tracking so we don't double-count alongside the\n // trailing `this.trackEvent(... 'impression')` below — return early.\n case 'carousel_cards':\n renderCarouselCards(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'sticky_bar':\n renderStickyBar(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'progress_bar':\n renderProgressBar(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'coachmark_tour':\n // coachmark_tour fires its own impression/dismiss/click events\n // from within its state machine — don't double-report here.\n renderCoachmarkTour(this.buildRenderContext(campaign));\n this.emit('campaign-shown', campaign);\n return;\n case 'product_recommendation':\n renderProductRecommendation(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n\n /**\n * Build the shared context passed into the preload-first renderers.\n * Matches the interface in `./renderers/types.ts`. Kept as a private\n * method (rather than inlined at each call-site) so future renderer\n * additions stay consistent and easy to audit.\n */\n private buildRenderContext(campaign: InAppCampaign): RenderContext {\n return {\n campaign,\n trackEvent: (id, evt) => {\n void this.trackEvent(id, evt);\n },\n sanitizeUrl: (url: string) => this.sanitizeUrl(url),\n sanitizeColor: (color: string) => this.sanitizeColor(color),\n log: (msg: string, level?: 'log' | 'warn' | 'error') => this.log(msg, level),\n addAnimationStyles: () => this.addAnimationStyles(),\n };\n }\n\n /**\n * Renders interactive sub-type campaigns (spin wheel, NPS, quiz, etc.)\n * using DOM-safe rendering. These sub-types use the campaign's\n * interactive_config payload for type-specific behavior.\n */\n private renderInteractive(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const bg = this.sanitizeColor(campaign.background_color || '#4169e1');\n const text = this.sanitizeColor(campaign.text_color || '#ffffff');\n\n switch (campaign.sub_type) {\n case 'nps_survey':\n this.renderNPSSurvey(campaign, ic, bg, text);\n break;\n case 'countdown_offer':\n this.renderCountdownOffer(campaign, ic, bg, text);\n break;\n case 'star_rating':\n this.renderStarRating(campaign, ic, bg, text);\n break;\n case 'quick_poll':\n this.renderQuickPoll(campaign, ic, bg, text);\n break;\n case 'quiz':\n this.renderQuiz(campaign, ic, bg, text);\n break;\n case 'spin_wheel':\n case 'scratch_card':\n if (this.onInteractiveCampaign) {\n this.onInteractiveCampaign(campaign);\n } else {\n this.log(\n `${campaign.sub_type} campaign received but no onInteractiveCampaign handler wired — install via AegisMessageRuntime`,\n 'warn',\n );\n }\n break;\n default:\n this.log(`Unknown interactive sub_type: ${campaign.sub_type}`, 'warn');\n this.renderModal(campaign);\n }\n }\n\n // --- Interactive Renderers ---\n\n private renderNPSSurvey(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-nps-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 360px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = this._buildNPSSurveyBody(campaign, ic, text, 'overlay');\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderCountdownOffer(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-countdown-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 20px; font-weight: 700; margin-bottom: 8px;';\n title.textContent = campaign.title || 'Flash Sale';\n body.appendChild(title);\n\n const label = document.createElement('div');\n label.style.cssText = 'font-size: 13px; opacity: 0.8; margin-bottom: 12px;';\n label.textContent = (ic.countdown_label as string) || 'Sale ends in:';\n body.appendChild(label);\n\n // Countdown digits\n const digits = document.createElement('div');\n digits.style.cssText = 'display: flex; gap: 8px; justify-content: center; margin-bottom: 16px;';\n const digitStyle = `padding: 8px 12px; border-radius: 8px; font-size: 24px; font-weight: 700; font-family: monospace; background: ${text}22;`;\n for (const val of ['00', ':', '00', ':', '00']) {\n const el = document.createElement('span');\n if (val === ':') {\n el.style.cssText = 'font-size: 24px; font-weight: 700; align-self: center;';\n } else {\n el.style.cssText = digitStyle;\n }\n el.textContent = val;\n digits.appendChild(el);\n }\n body.appendChild(digits);\n\n // Start countdown from target or 2h default\n const targetStr = ic.countdown_target as string | undefined;\n if (targetStr) {\n const target = new Date(targetStr).getTime();\n const update = () => {\n const diff = Math.max(0, target - Date.now());\n const h = String(Math.floor(diff / 3600000)).padStart(2, '0');\n const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2, '0');\n const s = String(Math.floor((diff % 60000) / 1000)).padStart(2, '0');\n const spans = digits.querySelectorAll('span');\n if (spans.length >= 5) {\n spans[0].textContent = h;\n spans[2].textContent = m;\n spans[4].textContent = s;\n }\n if (diff > 0) requestAnimationFrame(update);\n };\n update();\n }\n\n if (campaign.body) {\n const desc = document.createElement('div');\n desc.style.cssText = 'font-size: 14px; opacity: 0.85; margin-bottom: 16px;';\n desc.textContent = campaign.body;\n body.appendChild(desc);\n }\n\n if (campaign.button_text) {\n const btn = this.createCTAButton(campaign, bg, text);\n body.appendChild(btn);\n }\n\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderStarRating(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-rating-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = this._buildStarRatingBody(campaign, ic, text, 'overlay');\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderQuickPoll(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-poll-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 16px; font-weight: 700; margin-bottom: 16px;';\n title.textContent = campaign.title || 'Quick question';\n body.appendChild(title);\n\n const options = (ic.poll_options as string[]) || [];\n const optionsList = document.createElement('div');\n optionsList.style.cssText = 'display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px;';\n for (const opt of options) {\n const optBtn = document.createElement('button');\n optBtn.style.cssText = `\n padding: 10px 16px; border-radius: 10px; border: 1px solid ${text}33;\n background: transparent; color: ${text}; font-size: 14px; cursor: pointer;\n text-align: left; transition: background 0.15s;\n `;\n optBtn.textContent = opt;\n optBtn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n });\n optionsList.appendChild(optBtn);\n }\n body.appendChild(optionsList);\n\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderQuiz(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-quiz-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 360px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 18px; font-weight: 700; margin-bottom: 8px;';\n title.textContent = campaign.title || 'Quiz';\n body.appendChild(title);\n\n const questions = (ic.questions as Array<{ question: string; options: string[] }>) || [];\n let currentQ = 0;\n\n const renderQuestion = () => {\n // Clear previous content except title\n while (body.childNodes.length > 1) {\n body.removeChild(body.lastChild!);\n }\n\n if (currentQ >= questions.length) {\n const result = document.createElement('div');\n result.style.cssText = 'font-size: 16px; margin: 16px 0;';\n result.textContent = (ic.thank_you_message as string) || 'Thanks for completing the quiz!';\n body.appendChild(result);\n this.trackEvent(campaign.id, 'clicked');\n this.addCloseButton(body, overlay, campaign.id);\n return;\n }\n\n const q = questions[currentQ];\n const progress = document.createElement('div');\n progress.style.cssText = 'font-size: 12px; opacity: 0.6; margin-bottom: 12px;';\n progress.textContent = `Question ${currentQ + 1} of ${questions.length}`;\n body.appendChild(progress);\n\n const questionText = document.createElement('div');\n questionText.style.cssText = 'font-size: 14px; font-weight: 600; margin-bottom: 16px;';\n questionText.textContent = q.question;\n body.appendChild(questionText);\n\n const optionsDiv = document.createElement('div');\n optionsDiv.style.cssText = 'display: flex; flex-direction: column; gap: 8px;';\n for (const opt of q.options) {\n const optBtn = document.createElement('button');\n optBtn.style.cssText = `\n padding: 10px 16px; border-radius: 10px; border: 1px solid ${text}33;\n background: transparent; color: ${text}; font-size: 14px; cursor: pointer;\n text-align: left; transition: background 0.15s;\n `;\n optBtn.textContent = opt;\n optBtn.addEventListener('click', () => {\n currentQ++;\n renderQuestion();\n });\n optionsDiv.appendChild(optBtn);\n }\n body.appendChild(optionsDiv);\n this.addCloseButton(body, overlay, campaign.id);\n };\n\n renderQuestion();\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n // --- Shared Rendering Helpers ---\n\n private createOverlay(className: string): HTMLElement {\n const overlay = document.createElement('div');\n overlay.className = className;\n overlay.style.cssText = `\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5); display: flex; align-items: center;\n justify-content: center; z-index: 99999; animation: aegisFadeIn 0.3s ease;\n `;\n return overlay;\n }\n\n private createCTAButton(campaign: InAppCampaign, bg: string, text: string): HTMLElement {\n const btn = document.createElement('button');\n btn.style.cssText = `\n display: inline-block; padding: 10px 28px; border-radius: 999px;\n font-size: 14px; font-weight: 600; cursor: pointer; border: none;\n background: ${text}; color: ${bg}; transition: transform 0.15s;\n `;\n btn.textContent = campaign.button_text || 'OK';\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) window.open(safeUrl, '_blank');\n }\n });\n return btn;\n }\n\n private addCloseButton(container: HTMLElement, overlay: HTMLElement, campaignId: string): void {\n const close = document.createElement('div');\n close.style.cssText = 'margin-top: 12px; font-size: 12px; opacity: 0.6; cursor: pointer;';\n close.textContent = 'Close';\n close.addEventListener('click', () => {\n this.trackEvent(campaignId, 'dismissed');\n this.removeModal(overlay);\n });\n container.appendChild(close);\n }\n \n /**\n * Navigate to a campaign CTA URL.\n *\n * Dispatches a cancellable `aegis:campaign-click` CustomEvent so host\n * apps (e.g., the storefront SPA) can intercept and route in-place via\n * their client-side router, avoiding a full page reload that re-runs\n * SSR + re-renders the location picker / banner / cart drawer state.\n *\n * The host calls `event.preventDefault()` to suppress the hard nav and\n * handle the URL itself. If no listener calls preventDefault, the SDK\n * falls through to `window.location.href` so plain HTML pages without a\n * SPA still get the user to the destination.\n *\n * Industry parallel: CleverTap dispatches `clevertap-deeplink-click`,\n * MoEngage dispatches `MoE_inappRedirect`. The pattern lets the same\n * SDK serve both vanilla websites (full nav fallback) and SPAs\n * (in-place state change) without per-host configuration.\n */\n private navigateToCampaignAction(\n campaign: InAppCampaign,\n safeUrl: string,\n buttonId: string = 'cta',\n ): void {\n if (typeof window === 'undefined') return;\n\n // Typed lifecycle hook (1.8.0). Mirror the DOM `preventDefault` contract\n // — a handler returning `false` OR mutating `defaultPrevented` via the\n // helper suppresses the SDK's hard-nav. Built first so handlers attached\n // via the typed API see the click before the DOM event fires.\n let typedDefaultPrevented = false;\n const typedEvent: CampaignClickEvent = {\n campaign,\n action_url: safeUrl,\n button_id: buttonId,\n preventDefault: () => {\n typedDefaultPrevented = true;\n typedEvent.defaultPrevented = true;\n },\n defaultPrevented: false,\n };\n const typedProceed = this.emit('campaign-click', typedEvent);\n\n // DOM CustomEvent path — preserved for backward compat (1.7.x consumers\n // already listen for `aegis:campaign-click`) and for advanced patterns\n // (multi-frame embeds, host-app code that runs before the SDK is\n // instantiated, debugging via DevTools event listener panel).\n const domEvent = new CustomEvent('aegis:campaign-click', {\n detail: {\n campaign_id: campaign.id,\n campaign_type: campaign.type,\n action_url: safeUrl,\n button_id: buttonId,\n },\n cancelable: true,\n });\n const domProceed = window.dispatchEvent(domEvent);\n\n // Either path can suppress the hard-nav. We OR the prevent signals so\n // host apps don't have to remember to handle both — registering on\n // either API is enough.\n const suppressed =\n !typedProceed ||\n typedDefaultPrevented ||\n !domProceed ||\n domEvent.defaultPrevented;\n\n if (!suppressed) {\n window.location.href = safeUrl;\n }\n }\n\n private renderBanner(campaign: InAppCampaign): void {\n const banner = document.createElement('div');\n banner.className = 'aegis-in-app-banner';\n banner.setAttribute('data-campaign-id', campaign.id);\n \n banner.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n background: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.text_color || '#ffffff')};\n padding: 16px;\n z-index: 999999;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideDown 0.3s ease-out;\n `;\n \n const contentContainer = document.createElement('div');\n contentContainer.style.cssText = 'flex: 1; display: flex; align-items: center; gap: 12px;';\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 40px; height: 40px; border-radius: 4px; object-fit: cover;';\n contentContainer.appendChild(img);\n }\n }\n \n const textContainer = document.createElement('div');\n textContainer.style.cssText = 'flex: 1;';\n \n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 600; font-size: 14px; margin-bottom: 4px;';\n textContainer.appendChild(title);\n \n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.9;';\n textContainer.appendChild(body);\n \n contentContainer.appendChild(textContainer);\n \n const actionsContainer = document.createElement('div');\n actionsContainer.style.cssText = 'display: flex; align-items: center; gap: 12px; margin-left: 16px;';\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: white;\n color: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n border: none;\n padding: 8px 16px;\n border-radius: 4px;\n font-weight: 600;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeBanner(banner);\n });\n \n actionsContainer.appendChild(ctaButton);\n }\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n background: transparent;\n border: none;\n color: inherit;\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.7;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeBanner(banner);\n });\n \n actionsContainer.appendChild(closeButton);\n \n banner.appendChild(contentContainer);\n banner.appendChild(actionsContainer);\n \n this.addAnimationStyles();\n document.body.appendChild(banner);\n }\n \n private renderModal(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-modal-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-in-app-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 8px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n box-shadow: 0 10px 40px rgba(0,0,0,0.2);\n animation: aegisScaleIn 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 200px; object-fit: cover; border-radius: 8px 8px 0 0;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 24px;';\n \n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n content.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0 0 20px 0; font-size: 15px; line-height: 1.5; color: #4a4a4a;';\n content.appendChild(body);\n \n const actions = document.createElement('div');\n actions.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end;';\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.text_color || '#ffffff')};\n border: none;\n padding: 10px 24px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n actions.appendChild(ctaButton);\n }\n \n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: transparent;\n border: 1px solid #d0d0d0;\n color: #4a4a4a;\n padding: 10px 24px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n actions.appendChild(closeButton);\n \n content.appendChild(actions);\n modal.appendChild(content);\n overlay.appendChild(modal);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderFullScreen(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-fullscreen-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: ${this.sanitizeColor(campaign.background_color || '#ffffff')};\n color: ${this.sanitizeColor(campaign.text_color || '#1a1a1a')};\n z-index: 1000001;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n animation: aegisFadeIn 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n overflow-y: auto;\n `;\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'max-width: 100%; max-height: 40vh; object-fit: contain; margin-bottom: 32px;';\n overlay.appendChild(img);\n }\n }\n \n const contentContainer = document.createElement('div');\n contentContainer.style.cssText = 'max-width: 600px; text-align: center;';\n \n const title = document.createElement('h1');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 32px; font-weight: 700;';\n contentContainer.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0 0 32px 0; font-size: 18px; line-height: 1.6; opacity: 0.9;';\n contentContainer.appendChild(body);\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(campaign.text_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.background_color || '#ffffff')};\n border: none;\n padding: 16px 48px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 18px;\n cursor: pointer;\n margin-bottom: 16px;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n contentContainer.appendChild(ctaButton);\n }\n \n overlay.appendChild(contentContainer);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 20px;\n right: 20px;\n background: transparent;\n border: none;\n color: inherit;\n font-size: 32px;\n cursor: pointer;\n padding: 0;\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.6;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n overlay.appendChild(closeButton);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderHalfInterstitial(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-half-interstitial-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 1000000;\n display: flex;\n align-items: flex-end;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-in-app-half-interstitial';\n modal.style.cssText = `\n background: white;\n border-radius: 16px 16px 0 0;\n width: 100%;\n max-width: 600px;\n max-height: 60vh;\n overflow: auto;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.2);\n animation: aegisSlideUp 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 200px; object-fit: cover;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 32px 24px;';\n \n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; line-height: 1.5; color: #4a4a4a;';\n content.appendChild(body);\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.text_color || '#ffffff')};\n border: none;\n padding: 14px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n content.appendChild(ctaButton);\n }\n \n modal.appendChild(content);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: rgba(255,255,255,0.9);\n border: none;\n color: #333;\n font-size: 24px;\n cursor: pointer;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderAlert(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-alert-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.2s ease-out;\n `;\n \n const alert = document.createElement('div');\n alert.className = 'aegis-in-app-alert';\n alert.style.cssText = `\n background: white;\n border-radius: 12px;\n max-width: 320px;\n width: 85%;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n animation: aegisScaleIn 0.2s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n overflow: hidden;\n `;\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 24px 20px; text-align: center;';\n \n const title = document.createElement('h3');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 8px 0; font-size: 18px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0; font-size: 14px; line-height: 1.4; color: #666;';\n content.appendChild(body);\n \n alert.appendChild(content);\n \n const buttonContainer = document.createElement('div');\n buttonContainer.style.cssText = 'display: flex; border-top: 1px solid #e0e0e0;';\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n flex: 1;\n background: transparent;\n border: none;\n border-right: 1px solid #e0e0e0;\n color: #1a73e8;\n padding: 14px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n buttonContainer.appendChild(ctaButton);\n }\n \n const cancelButton = document.createElement('button');\n cancelButton.textContent = 'Cancel';\n cancelButton.style.cssText = `\n flex: 1;\n background: transparent;\n border: none;\n color: #666;\n padding: 14px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n `;\n \n cancelButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n buttonContainer.appendChild(cancelButton);\n \n alert.appendChild(buttonContainer);\n overlay.appendChild(alert);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderPIP(campaign: InAppCampaign): void {\n const pip = document.createElement('div');\n pip.className = 'aegis-in-app-pip';\n pip.setAttribute('data-campaign-id', campaign.id);\n \n pip.style.cssText = `\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 320px;\n background: black;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 8px 24px rgba(0,0,0,0.3);\n z-index: 999999;\n animation: aegisSlideUp 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (campaign.video_url) {\n const video = document.createElement('video');\n const safeUrl = this.sanitizeUrl(campaign.video_url);\n if (safeUrl) {\n video.src = safeUrl;\n video.controls = true;\n video.autoplay = true;\n video.muted = true;\n video.style.cssText = 'width: 100%; display: block;';\n pip.appendChild(video);\n }\n } else if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; display: block;';\n pip.appendChild(img);\n }\n }\n \n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: linear-gradient(to top, rgba(0,0,0,0.9), transparent);\n padding: 40px 16px 16px;\n color: white;\n `;\n \n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-size: 14px; font-weight: 600; margin-bottom: 4px;';\n overlay.appendChild(title);\n \n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 12px; opacity: 0.9;';\n overlay.appendChild(body);\n \n pip.appendChild(overlay);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 8px;\n right: 8px;\n background: rgba(0,0,0,0.6);\n border: none;\n color: white;\n font-size: 18px;\n cursor: pointer;\n width: 28px;\n height: 28px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n pip.style.animation = 'aegisSlideDown 0.3s ease-out';\n setTimeout(() => {\n if (pip.parentNode) {\n pip.parentNode.removeChild(pip);\n }\n }, 300);\n });\n \n pip.appendChild(closeButton);\n \n if (campaign.action_url) {\n pip.style.cursor = 'pointer';\n pip.addEventListener('click', (e) => {\n if (e.target !== closeButton) {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n window.open(safeUrl, '_blank');\n }\n }\n });\n }\n \n this.addAnimationStyles();\n document.body.appendChild(pip);\n }\n \n private removeBanner(banner: HTMLElement): void {\n banner.style.animation = 'aegisSlideUp 0.3s ease-out';\n setTimeout(() => {\n if (banner.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n }, 300);\n }\n \n private removeModal(overlay: HTMLElement): void {\n overlay.style.animation = 'aegisFadeOut 0.3s ease-out';\n setTimeout(() => {\n if (overlay.parentNode) {\n overlay.parentNode.removeChild(overlay);\n }\n }, 300);\n }\n \n private renderTooltip(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const anchorSelector = (ic.tooltip_anchor_selector as string) || '[data-aegis-tooltip]';\n const preferredPosition = (ic.tooltip_position as string) || 'bottom'; // top | bottom | left | right\n\n const anchor = document.querySelector(anchorSelector);\n if (!anchor) {\n this.log(`Tooltip anchor not found: ${anchorSelector}`, 'warn');\n return;\n }\n\n const bg = this.sanitizeColor(campaign.background_color || '#1a1a1a');\n const textColor = this.sanitizeColor(campaign.text_color || '#ffffff');\n\n // Tooltip container (positioned absolute to body)\n const tooltip = document.createElement('div');\n tooltip.className = 'aegis-in-app-tooltip';\n tooltip.setAttribute('data-campaign-id', campaign.id);\n tooltip.style.cssText = `\n position: absolute; z-index: 1000001; max-width: 280px; width: max-content;\n background: ${bg}; color: ${textColor}; border-radius: 10px; padding: 14px 16px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18); font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.2s ease-out; pointer-events: auto;\n `;\n\n // Arrow\n const arrow = document.createElement('div');\n arrow.className = 'aegis-tooltip-arrow';\n arrow.style.cssText = `\n position: absolute; width: 10px; height: 10px; background: ${bg};\n transform: rotate(45deg);\n `;\n\n // Close button\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '\\u00d7';\n closeBtn.style.cssText = `\n position: absolute; top: 6px; right: 8px; background: none; border: none;\n color: ${textColor}; opacity: 0.6; font-size: 16px; cursor: pointer; padding: 0; line-height: 1;\n `;\n\n const dismiss = () => {\n tooltip.style.animation = 'aegisFadeOut 0.2s ease-out';\n setTimeout(() => { tooltip.parentNode?.removeChild(tooltip); }, 200);\n this.trackEvent(campaign.id, 'dismissed');\n };\n\n closeBtn.addEventListener('click', (e) => { e.stopPropagation(); dismiss(); });\n\n // Title\n if (campaign.title) {\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-size: 13px; font-weight: 700; margin-bottom: 4px; padding-right: 16px;';\n tooltip.appendChild(title);\n }\n\n // Body\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 12px; line-height: 1.4; opacity: 0.9;';\n tooltip.appendChild(body);\n\n // CTA button\n if (campaign.button_text && campaign.action_url) {\n const cta = document.createElement('a');\n cta.textContent = campaign.button_text;\n cta.href = campaign.action_url;\n cta.style.cssText = `\n display: inline-block; margin-top: 8px; font-size: 12px; font-weight: 600;\n color: ${textColor}; text-decoration: underline; cursor: pointer;\n `;\n cta.addEventListener('click', () => { this.trackEvent(campaign.id, 'clicked'); });\n tooltip.appendChild(cta);\n }\n\n tooltip.appendChild(closeBtn);\n tooltip.appendChild(arrow);\n document.body.appendChild(tooltip);\n\n // Position relative to anchor\n const anchorRect = anchor.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n const gap = 10;\n\n let top = 0;\n let left = 0;\n let arrowTop = '';\n let arrowLeft = '';\n let arrowBottom = '';\n let arrowRight = '';\n\n switch (preferredPosition) {\n case 'top':\n top = anchorRect.top + scrollY - tooltipRect.height - gap;\n left = anchorRect.left + scrollX + (anchorRect.width - tooltipRect.width) / 2;\n arrowBottom = '-5px';\n arrowLeft = `${tooltipRect.width / 2 - 5}px`;\n break;\n case 'left':\n top = anchorRect.top + scrollY + (anchorRect.height - tooltipRect.height) / 2;\n left = anchorRect.left + scrollX - tooltipRect.width - gap;\n arrowRight = '-5px';\n arrowTop = `${tooltipRect.height / 2 - 5}px`;\n break;\n case 'right':\n top = anchorRect.top + scrollY + (anchorRect.height - tooltipRect.height) / 2;\n left = anchorRect.right + scrollX + gap;\n arrowLeft = '-5px';\n arrowTop = `${tooltipRect.height / 2 - 5}px`;\n break;\n default: // bottom\n top = anchorRect.bottom + scrollY + gap;\n left = anchorRect.left + scrollX + (anchorRect.width - tooltipRect.width) / 2;\n arrowTop = '-5px';\n arrowLeft = `${tooltipRect.width / 2 - 5}px`;\n break;\n }\n\n // Clamp to viewport\n left = Math.max(8, Math.min(left, window.innerWidth + scrollX - tooltipRect.width - 8));\n top = Math.max(8, top);\n\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${left}px`;\n arrow.style.top = arrowTop || '';\n arrow.style.left = arrowLeft || '';\n arrow.style.bottom = arrowBottom || '';\n arrow.style.right = arrowRight || '';\n\n // Dismiss on outside click\n const outsideClickHandler = (e: MouseEvent) => {\n if (!tooltip.contains(e.target as Node) && !anchor.contains(e.target as Node)) {\n document.removeEventListener('click', outsideClickHandler);\n dismiss();\n }\n };\n setTimeout(() => document.addEventListener('click', outsideClickHandler), 100);\n }\n\n private addAnimationStyles(): void {\n if (document.getElementById('aegis-in-app-styles')) {\n return;\n }\n \n const style = document.createElement('style');\n style.id = 'aegis-in-app-styles';\n style.textContent = `\n @keyframes aegisSlideDown {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes aegisSlideUp {\n from { transform: translateY(0); opacity: 1; }\n to { transform: translateY(-100%); opacity: 0; }\n }\n \n @keyframes aegisFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes aegisFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n }\n \n @keyframes aegisScaleIn {\n from { transform: scale(0.9); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n\n /* Bottom-anchored slide IN (opposite of aegisSlideUp which slides OUT).\n Used by the preload-first renderers: sticky_bar (bottom),\n progress_bar, carousel_cards, product_recommendation. */\n @keyframes aegisSlideInFromBottom {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n `;\n \n document.head.appendChild(style);\n }\n \n private sanitizeUrl(url: string): string | null {\n try {\n const parsedUrl = new URL(url, window.location.origin);\n \n if (parsedUrl.protocol === 'javascript:' || parsedUrl.protocol === 'data:') {\n this.log(`Blocked unsafe URL: ${url}`, 'error');\n return null;\n }\n \n return parsedUrl.href;\n } catch (error) {\n this.log(`Invalid URL: ${url}`, 'error');\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n if (/^#[0-9A-Fa-f]{3,6}$/.test(color)) {\n return color;\n }\n \n if (/^rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)$/.test(color)) {\n return color;\n }\n \n if (/^rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)$/.test(color)) {\n return color;\n }\n \n const namedColors = ['white', 'black', 'red', 'green', 'blue', 'yellow', 'orange', 'purple', 'pink', 'gray', 'transparent'];\n if (namedColors.includes(color.toLowerCase())) {\n return color;\n }\n \n return '#000000';\n }\n \n /**\n * Track in-app event via the standard Event Ingress pipeline.\n *\n * Events flow: SDK → Event Ingress → Kafka → Governance Consumer\n * → Event Engine → ClickHouse (in_app_events + events_with_ttl)\n *\n * The event_type uses the canonical prefix `in_app.` so the event\n * switchboard maps it to the correct canonical form:\n * in_app.impression → engagement.view\n * in_app.clicked → engagement.click\n * in_app.dismissed → engagement.dismiss\n */\n private async trackEvent(campaignId: string, eventType: 'impression' | 'clicked' | 'dismissed'): Promise<void> {\n // Lifecycle: `campaign-dismiss` — fire from this single chokepoint so\n // every renderer (8 sites today + future ones) emits consistently.\n // Source granularity (close_button / overlay / esc / auto_timeout) can\n // be refined in a future minor by passing source through to trackEvent;\n // for 1.8.0 we surface the event itself since that's the high-value\n // signal host apps need (inbox state updates, dismiss analytics).\n if (eventType === 'dismissed') {\n const campaign = this.campaigns.find((c) => c.id === campaignId);\n if (campaign) {\n this.emit('campaign-dismiss', {\n campaign,\n source: 'close_button',\n });\n }\n }\n\n try {\n // Use the public SDK scheme (/v1/in_app/events) — the gateway has an\n // explicit handler that auths via X-Aegis-Write-Key and forwards to\n // event-switchboard. Direct POST to /api/v1/events bypasses the SDK\n // auth model and hits the JWT preHandler → 401.\n // See docs/architecture/API_ROUTING.md §5.\n const url = `${this.apiHost}/v1/in_app/events`;\n const externalEventId = `inapp_${campaignId}_${eventType}_${Date.now()}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n };\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n\n // Plumb the same workspace cascade the main analytics SDK uses\n // (config → ?ws= → URL path slug → setWorkspace runtime → session\n // storage). Without this, in-app engagement events arrive at\n // event-ingress unscoped and fall back to the org's primary\n // workspace — see PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n const workspaceId = this.getWorkspaceId?.();\n\n const body = {\n campaign_id: campaignId,\n event_type: eventType,\n user_id: this.userId,\n contact_id: this.contactId,\n anonymous_id: this.userId,\n platform: 'web',\n workspace_id: workspaceId,\n metadata: {\n property_id: this.propertyId,\n variant_id: this.getVariantId(campaignId) ?? undefined,\n },\n idempotency_key: externalEventId,\n };\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n credentials: 'include',\n }).catch(error => {\n this.log(`Error tracking event: ${error}`, 'error');\n });\n\n this.log(`Tracked ${eventType} event for campaign ${campaignId}`);\n } catch (error) {\n this.log(`Error tracking event: ${error}`, 'error');\n }\n }\n \n private log(message: string, level: 'log' | 'warn' | 'error' = 'log'): void {\n if (this.debugMode) {\n console[level](`[AegisInApp] ${message}`);\n }\n }\n \n destroy(options?: { clearABState?: boolean }): void {\n this.disconnectSSE();\n\n if (typeof document !== 'undefined') {\n document.querySelectorAll(\n '.aegis-in-app-banner, .aegis-in-app-modal-overlay, .aegis-in-app-fullscreen-overlay, ' +\n '.aegis-in-app-half-interstitial-overlay, .aegis-in-app-alert-overlay, .aegis-in-app-pip, ' +\n '.aegis-in-app-nps-overlay, .aegis-in-app-countdown-overlay, .aegis-in-app-rating-overlay, ' +\n '.aegis-in-app-poll-overlay, .aegis-in-app-quiz-overlay, ' +\n // F2/F3 / U8 — slot-rendered card classes\n '.aegis-in-app-rating-card, .aegis-in-app-nps-card'\n ).forEach(el => {\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n });\n\n const styles = document.getElementById('aegis-in-app-styles');\n if (styles && styles.parentNode) {\n styles.parentNode.removeChild(styles);\n }\n }\n\n // Optionally clear A/B assignment state\n if (options?.clearABState && typeof localStorage !== 'undefined') {\n localStorage.removeItem('aegis_ab_assignments');\n }\n\n this.isInitialized = false;\n this.log('AegisInApp destroyed');\n }\n}\n","/**\n * ProductRecommendation renderer — personalized product grid/row rendered\n * from `interactive_config.cards[]` that the cell-plane populated at\n * bundle-assembly time. The SDK never calls a product-API at render\n * time — the preload contract is that the recommended products are\n * already in the bundle.\n *\n * The layout is narrower than the generic carousel because product recs\n * should feel like a first-class product grid, not a marketing slider.\n * Three layouts: grid (2-col / 3-col responsive), row (horizontal-scroll),\n * carousel (same as carousel_cards but different framing copy).\n *\n * interactive_config:\n * rec_source?: 'viewed' | 'abandoned' | 'cross_sell' | 'trending' | 'personalized'\n * (metadata only — the actual product list is in `cards[]`)\n * rec_count?: number (display hint; rendering is driven by cards.length)\n * rec_layout?: 'grid' | 'row' | 'carousel' default 'grid'\n * rec_cta_text?: string default 'Shop now'\n * cards: Array<{ image_url?, title?, body?, cta_url?, metadata? }>\n */\n\nimport type { RenderContext } from './types';\n\ntype Card = {\n image_url?: string;\n title?: string;\n body?: string;\n cta_text?: string;\n cta_url?: string;\n metadata?: Record<string, unknown>;\n};\n\nconst MAX_PRODUCTS = 24;\n\nexport function renderProductRecommendation(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_PRODUCTS);\n if (cards.length === 0) {\n log('product_recommendation rendered with zero products — skipping', 'warn');\n return;\n }\n\n const layout = (ic.rec_layout as string) || 'grid';\n const ctaDefault = (ic.rec_cta_text as string) || 'Shop now';\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#ffffff');\n const fg = sanitizeColor((campaign.text_color as string) || '#0f172a');\n const accent = sanitizeColor('#4169e1');\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-product-rec';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; inset: 0;\n background: rgba(15,23,42,0.55); backdrop-filter: blur(4px);\n display: flex; align-items: flex-end; justify-content: center;\n z-index: 99999; animation: aegisFadeIn 0.25s ease;\n `;\n\n const sheet = document.createElement('div');\n sheet.style.cssText = `\n background: ${bg}; color: ${fg};\n max-width: 560px; width: 100%; max-height: 80vh; overflow-y: auto;\n border-radius: 20px 20px 0 0;\n padding: 18px 16px 22px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n box-shadow: 0 -12px 30px rgba(0,0,0,0.12);\n `;\n\n // Drag handle (decorative, for mobile context)\n const handle = document.createElement('div');\n handle.style.cssText = `\n width: 36px; height: 4px; border-radius: 999px; background: ${fg}1f;\n margin: 0 auto 12px;\n `;\n sheet.appendChild(handle);\n\n const header = document.createElement('div');\n header.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;';\n\n const headerText = document.createElement('div');\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 16px; margin-bottom: 2px;';\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.7; line-height: 1.4;';\n headerText.appendChild(title);\n headerText.appendChild(body);\n header.appendChild(headerText);\n\n const close = document.createElement('button');\n close.textContent = '✕';\n close.setAttribute('aria-label', 'Close');\n close.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 18px; cursor: pointer; padding: 4px 8px; opacity: 0.7;\n `;\n close.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n });\n header.appendChild(close);\n sheet.appendChild(header);\n\n const grid = document.createElement('div');\n if (layout === 'row' || layout === 'carousel') {\n grid.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto;\n scroll-snap-type: x mandatory; padding: 14px 0 4px;\n `;\n } else {\n grid.style.cssText = `\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;\n padding-top: 14px;\n `;\n }\n\n cards.forEach((c) => {\n const tile = document.createElement('div');\n const isRow = layout === 'row' || layout === 'carousel';\n tile.style.cssText = `\n background: ${fg}08; border-radius: 12px;\n padding: 10px; display: flex; flex-direction: column; gap: 6px;\n cursor: ${c.cta_url ? 'pointer' : 'default'};\n ${isRow ? 'flex: 0 0 150px; scroll-snap-align: start;' : ''}\n transition: transform 0.15s ease;\n `;\n\n if (c.image_url) {\n const img = document.createElement('img');\n const safe = sanitizeUrl(c.image_url);\n if (safe) {\n img.src = safe;\n img.alt = '';\n img.loading = 'lazy';\n img.style.cssText = 'width: 100%; aspect-ratio: 1 / 1; border-radius: 8px; object-fit: cover;';\n tile.appendChild(img);\n }\n }\n\n if (c.title) {\n const t = document.createElement('div');\n t.textContent = c.title;\n t.style.cssText = 'font-weight: 600; font-size: 13px; line-height: 1.3;';\n tile.appendChild(t);\n }\n\n // Price lives in metadata.price per the dashboard schema; we show it\n // prominently if present.\n const price = c.metadata && typeof c.metadata === 'object' ? (c.metadata as Record<string, unknown>).price : undefined;\n if (price !== undefined) {\n const p = document.createElement('div');\n p.textContent = String(price);\n p.style.cssText = `color: ${accent}; font-weight: 700; font-size: 13px;`;\n tile.appendChild(p);\n } else if (c.body) {\n const b = document.createElement('div');\n b.textContent = c.body;\n b.style.cssText = 'font-size: 11.5px; opacity: 0.72; line-height: 1.3;';\n tile.appendChild(b);\n }\n\n const cta = document.createElement('button');\n cta.textContent = c.cta_text || ctaDefault;\n cta.style.cssText = `\n margin-top: auto;\n background: ${accent}; color: #fff;\n border: none; padding: 7px 10px; border-radius: 999px;\n font-size: 12px; font-weight: 600; cursor: pointer;\n `;\n const go = (e: Event) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked');\n if (c.cta_url) {\n const safe = sanitizeUrl(c.cta_url);\n if (safe) window.location.href = safe;\n }\n };\n cta.addEventListener('click', go);\n tile.appendChild(cta);\n if (c.cta_url) tile.addEventListener('click', go);\n\n grid.appendChild(tile);\n });\n\n sheet.appendChild(grid);\n overlay.appendChild(sheet);\n\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n }\n });\n\n document.body.appendChild(overlay);\n}\n","/**\n * AegisWidgetManager - Web SDK Widget Module\n * \n * Smart Pull architecture for persistent web widgets.\n * \n * Features:\n * - Gamification (spin wheel, scratch card) with server-side prize generation\n * - Chat bubbles (WhatsApp, support)\n * - Social proof toasts (real-time purchase notifications)\n * - Feedback forms (NPS, surveys)\n * \n * Security:\n * - XSS Protection: Uses DOM APIs instead of innerHTML\n * - Server-side prize generation prevents client manipulation\n * - URL validation prevents javascript: protocol injection\n * \n * Architecture:\n * - Fetches widget configs from /v1/widgets/config on page load\n * - Integrates with TriggerEngine for behavioral triggers\n * - Tracks widget events (show, click, dismiss, submit)\n * - Server-side prize generation for gamification\n */\n\nimport { TriggerEngine } from '../triggers/TriggerEngine';\nimport { Storage } from '../utils/storage';\n\n// Read the SDK's persisted anonymous_id from localStorage/cookie if available.\n// Returns undefined when called server-side or when no id has been minted yet.\n// See AegisInAppManager for the same pattern + canonical contract in\n// docs/architecture/API_ROUTING.md §3.\nfunction readAnonIdFromStorage(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const storage = new Storage();\n return storage.get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface WidgetConfig {\n widget_id: string;\n widget_type: 'chat_bubble' | 'spin_wheel' | 'scratch_card' | 'toast' | 'feedback_form' | 'exit_intent_popup';\n name: string;\n config: Record<string, any>;\n position?: string;\n priority: number;\n trigger_rules?: {\n type: 'exit_intent' | 'scroll_depth' | 'time_on_page' | 'immediate';\n config?: Record<string, any>;\n };\n}\n\nexport interface GamificationPrize {\n prize_label: string;\n prize_value?: string;\n coupon_code?: string;\n play_id: string;\n}\n\nexport interface AegisWidgetConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n organizationId?: string;\n debugMode?: boolean;\n triggerEngine?: TriggerEngine;\n /** If true, WidgetManager takes ownership of the TriggerEngine and\n * will call `triggerEngine.stop()` on destroy(). Leave false when\n * the caller shares the engine across managers (e.g. the facade). */\n ownsTriggerEngine?: boolean;\n enablePrefetch?: boolean;\n cssCustomization?: WidgetCSSCustomization;\n onEvent?: (eventType: string, data: any) => void;\n /** Source platform passed as X-Source-Platform header so the API\n * can filter campaigns whose display_rules.sources restricts them\n * to specific integrations (e.g. 'shopify', 'woocommerce'). */\n sourcePlatform?: 'web' | 'shopify' | 'woocommerce' | 'mobile_sdk';\n /**\n * Workspace cascade reader. Host SDK sets this to\n * `() => aegis.getEffectiveWorkspaceId()` so widget interaction events\n * (impressions, clicks, spin/scratch plays, feedback submissions)\n * arrive at the gateway with the same workspace context the main\n * analytics SDK uses. See PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n */\n getWorkspaceId?: () => string | undefined;\n}\n\nexport interface WidgetCSSCustomization {\n exitIntent?: {\n backgroundColor?: string;\n textColor?: string;\n accentColor?: string;\n borderRadius?: string;\n fontFamily?: string;\n overlayOpacity?: number;\n };\n spinWheel?: {\n backgroundColor?: string;\n textColor?: string;\n accentColor?: string;\n borderRadius?: string;\n fontFamily?: string;\n wheelColors?: string[];\n buttonColor?: string;\n };\n chatBubble?: {\n backgroundColor?: string;\n textColor?: string;\n position?: 'bottom_right' | 'bottom_left' | 'top_right' | 'top_left';\n size?: 'small' | 'medium' | 'large';\n };\n}\n\nexport interface PrefetchWidgetConfig {\n exit_intent?: {\n enabled: boolean;\n type: 'cart_recovery' | 'lead_gen';\n tier?: string;\n title?: string;\n message?: string;\n discount_code?: string;\n discount_percentage?: number;\n cta_text?: string;\n cta_url?: string;\n background_color?: string;\n text_color?: string;\n accent_color?: string;\n show_cart_items?: boolean;\n show_timer?: boolean;\n timer_minutes?: number;\n priority: number;\n mobile_triggers?: {\n scroll_velocity?: boolean;\n scroll_threshold?: number;\n scroll_min_position?: number;\n scroll_cooldown?: number;\n idle_timer?: boolean;\n idle_seconds?: number;\n visibility_change?: boolean;\n back_button?: boolean;\n };\n };\n spin_wheel?: {\n enabled: boolean;\n type: 'lead_gen' | 'cart_recovery';\n title?: string;\n prizes?: any[];\n priority: number;\n };\n}\n\nexport interface CartData {\n cart_id: string;\n cart_total: number;\n cart_currency: string;\n cart_items: Array<{\n product_id: string;\n product_name?: string;\n quantity: number;\n price?: number;\n }>;\n}\n\nexport class AegisWidgetManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private organizationId?: string;\n private debugMode: boolean;\n private triggerEngine?: TriggerEngine;\n private enablePrefetch: boolean;\n private cssCustomization: WidgetCSSCustomization;\n private onEvent?: (eventType: string, data: any) => void;\n private sourcePlatform?: string;\n private getWorkspaceId?: () => string | undefined;\n\n private widgets: WidgetConfig[] = [];\n private renderedWidgets = new Set<string>();\n private isInitialized = false;\n private isDestroyed = false;\n private ownsTriggerEngine: boolean;\n\n private prefetchWidgetConfigs: PrefetchWidgetConfig = {};\n private cartData?: CartData;\n \n constructor(config: AegisWidgetConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n // Fall back to the SDK's persisted anonymous_id when callers don't pass\n // userId explicitly. Without this, /v1/widgets/config requests omit\n // X-User-ID and the cell-plane buckets the call into \"anonymous\", which\n // misses any user-targeted widget configs. Mirror in AegisInAppManager.\n this.userId = config.userId ?? readAnonIdFromStorage();\n this.contactId = config.contactId;\n this.organizationId = config.organizationId;\n this.debugMode = config.debugMode || false;\n this.triggerEngine = config.triggerEngine;\n this.ownsTriggerEngine = config.ownsTriggerEngine === true;\n this.enablePrefetch = config.enablePrefetch !== false;\n this.cssCustomization = config.cssCustomization || {};\n this.onEvent = config.onEvent;\n this.sourcePlatform = config.sourcePlatform;\n this.getWorkspaceId = config.getWorkspaceId;\n }\n \n updateCSSCustomization(customization: WidgetCSSCustomization): void {\n this.cssCustomization = { ...this.cssCustomization, ...customization };\n this.log('CSS customization updated');\n }\n\n /**\n * Swap the identity that outgoing widget requests are attributed to.\n * Safe to call at any time; if prefetch is enabled and the manager\n * has already initialised, this will re-fetch the per-contact widget\n * configs so subsequent renders target the new identity. Does NOT\n * re-render already-visible widgets — a full teardown requires destroy().\n */\n async updateContactId(contactId: string | undefined): Promise<void> {\n if (this.contactId === contactId) return;\n this.contactId = contactId;\n this.log(`Contact ID updated → ${contactId ?? '(cleared)'}`);\n if (this.isInitialized && !this.isDestroyed && this.enablePrefetch && contactId) {\n await this.fetchPrefetchConfigs();\n this.preloadWidgetAssets();\n }\n }\n\n /**\n * Render an in-app campaign whose sub_type routes it to the\n * gamification-widget code path (spin_wheel / scratch_card).\n * InAppCampaign is adapted to the minimal WidgetConfig shape that\n * the internal renderers already understand — no rewrite needed.\n *\n * AegisMessageRuntime wires this method as AegisInAppManager's\n * `onInteractiveCampaign` callback so impressions from the inbox\n * prefetch bundle actually land on screen.\n */\n renderInteractiveCampaign(campaign: {\n id: string;\n title: string;\n sub_type?: string;\n priority?: number;\n interactive_config?: Record<string, unknown>;\n }): void {\n if (this.isDestroyed) return;\n if (typeof document === 'undefined') return;\n const subType = campaign.sub_type;\n if (subType !== 'spin_wheel' && subType !== 'scratch_card') {\n this.log(`renderInteractiveCampaign: unsupported sub_type \"${subType}\"`, true);\n return;\n }\n const widget: WidgetConfig = {\n widget_id: campaign.id,\n widget_type: subType,\n name: campaign.title,\n config: (campaign.interactive_config ?? {}) as Record<string, any>,\n priority: campaign.priority ?? 0,\n };\n if (this.renderedWidgets.has(widget.widget_id)) return;\n this.renderedWidgets.add(widget.widget_id);\n if (subType === 'spin_wheel') {\n this.renderSpinWheel(widget);\n } else {\n this.renderScratchCard(widget);\n }\n }\n\n /**\n * Tear down everything the manager installed: trigger listeners (if\n * we own the TriggerEngine), injected style sheets, and any rendered\n * widget roots. Further render attempts from in-flight triggers are\n * short-circuited by the `isDestroyed` flag.\n */\n destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n if (this.triggerEngine && this.ownsTriggerEngine) {\n this.triggerEngine.stop();\n }\n\n if (typeof document !== 'undefined') {\n const animationStyle = document.getElementById('aegis-widget-animations');\n animationStyle?.remove();\n document.querySelectorAll('[data-aegis-widget-root]').forEach((node) => {\n node.remove();\n });\n }\n\n this.renderedWidgets.clear();\n this.widgets = [];\n this.isInitialized = false;\n this.log('AegisWidgets destroyed');\n this.emitEvent('destroyed', {});\n }\n\n /**\n * Fire a lifecycle event to the consumer-supplied callback. Public-API\n * hook configured via `AegisWidgetConfig.onEvent`. Currently only used\n * by external callers passing their own logger; internal widget lifecycle\n * events get wired through here as they're added.\n */\n private emitEvent(eventType: string, data: unknown): void {\n if (this.onEvent) {\n try {\n this.onEvent(eventType, data);\n } catch (error) {\n this.log(`Error in event callback: ${error}`, true);\n }\n }\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisWidgets already initialized');\n return;\n }\n \n if (this.enablePrefetch && this.contactId) {\n await this.fetchPrefetchConfigs();\n this.preloadWidgetAssets();\n }\n \n await this.fetchWidgets();\n \n this.renderImmediateWidgets();\n \n this.setupTriggerListeners();\n \n this.setupExitIntentWithPrefetch();\n \n this.isInitialized = true;\n this.log('AegisWidgets initialized successfully');\n }\n \n setCartData(cartData: CartData): void {\n this.cartData = cartData;\n this.log(`Cart data updated: ${cartData.cart_items.length} items, total: ${cartData.cart_currency} ${cartData.cart_total}`);\n }\n \n private detectPlatformCart(): CartData | null {\n const shopifyCart = this.normalizeShopifyCart();\n if (shopifyCart) {\n this.log('Detected Shopify cart via browser globals');\n return shopifyCart;\n }\n \n const wooCart = this.normalizeWooCart();\n if (wooCart) {\n this.log('Detected WooCommerce cart via injected data');\n return wooCart;\n }\n \n const magentoCart = this.normalizeMagentoCart();\n if (magentoCart) {\n this.log('Detected Magento cart via localStorage');\n return magentoCart;\n }\n \n if (this.cartData) {\n this.log('Using manually set cart data');\n return this.cartData;\n }\n \n return null;\n }\n \n private normalizeShopifyCart(): CartData | null {\n try {\n if (typeof window === 'undefined') return null;\n \n const win = window as any;\n \n if (win.Shopify?.checkout) {\n const checkout = win.Shopify.checkout;\n return {\n cart_id: checkout.token || `shopify_${Date.now()}`,\n cart_total: parseFloat(checkout.total_price) || 0,\n cart_currency: checkout.currency || 'USD',\n cart_items: (checkout.line_items || []).map((item: any) => ({\n product_id: String(item.product_id || item.id),\n product_name: item.title || item.product_title,\n quantity: item.quantity || 1,\n price: parseFloat(item.price) || 0\n }))\n };\n }\n \n const cartJsonEl = document.getElementById('cart-json');\n if (cartJsonEl?.textContent) {\n const cart = JSON.parse(cartJsonEl.textContent);\n return {\n cart_id: cart.token || `shopify_${Date.now()}`,\n cart_total: (cart.total_price || 0) / 100,\n cart_currency: cart.currency || 'USD',\n cart_items: (cart.items || []).map((item: any) => ({\n product_id: String(item.product_id || item.id),\n product_name: item.product_title || item.title,\n quantity: item.quantity || 1,\n price: (item.price || 0) / 100\n }))\n };\n }\n \n return null;\n } catch (error) {\n this.log(`Error detecting Shopify cart: ${error}`, true);\n return null;\n }\n }\n \n private normalizeWooCart(): CartData | null {\n try {\n if (typeof window === 'undefined') return null;\n \n const win = window as any;\n \n if (win.aegis_cart) {\n const cart = win.aegis_cart;\n return {\n cart_id: cart.cart_id || `woo_${Date.now()}`,\n cart_total: parseFloat(cart.cart_total) || 0,\n cart_currency: cart.cart_currency || 'USD',\n cart_items: (cart.cart_items || []).map((item: any) => ({\n product_id: String(item.product_id),\n product_name: item.product_name,\n quantity: item.quantity || 1,\n price: parseFloat(item.price) || 0\n }))\n };\n }\n \n return null;\n } catch (error) {\n this.log(`Error detecting WooCommerce cart: ${error}`, true);\n return null;\n }\n }\n \n private normalizeMagentoCart(): CartData | null {\n try {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n \n const mageCacheStr = localStorage.getItem('mage-cache-storage');\n if (!mageCacheStr) return null;\n \n const mageCache = JSON.parse(mageCacheStr);\n if (!mageCache?.cart) return null;\n \n const cart = mageCache.cart;\n \n return {\n cart_id: cart.quote_id || cart.id || `magento_${Date.now()}`,\n cart_total: parseFloat(cart.subtotalAmount || cart.subtotal || 0),\n cart_currency: cart.currencyCode || 'USD',\n cart_items: (cart.items || []).map((item: any) => ({\n product_id: String(item.item_id || item.product_id),\n product_name: item.product_name || item.name,\n quantity: item.qty || 1,\n price: parseFloat(item.product_price_value || item.price || 0)\n }))\n };\n } catch (error) {\n this.log(`Error detecting Magento cart: ${error}`, true);\n return null;\n }\n }\n \n private preloadWidgetAssets(): void {\n try {\n const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;\n if (exitIntentConfig?.enabled) {\n const imageUrl = (exitIntentConfig as any).image_url;\n if (imageUrl) {\n const img = new Image();\n img.src = imageUrl;\n this.log(`Preloading exit intent image: ${imageUrl}`);\n }\n }\n \n const spinWheelConfig = this.prefetchWidgetConfigs.spin_wheel;\n if (spinWheelConfig?.enabled) {\n const imageUrl = (spinWheelConfig as any).image_url;\n if (imageUrl) {\n const img = new Image();\n img.src = imageUrl;\n this.log(`Preloading spin wheel image: ${imageUrl}`);\n }\n }\n } catch (error) {\n this.log(`Error preloading widget assets: ${error}`, true);\n }\n }\n \n private async fetchPrefetchConfigs(): Promise<void> {\n try {\n const startTime = performance.now();\n const url = `${this.apiHost}/v1/widgets/config/prefetch`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n };\n \n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, { headers, credentials: 'include' });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch prefetch configs: ${response.status}`);\n }\n \n this.prefetchWidgetConfigs = await response.json();\n \n const elapsed = performance.now() - startTime;\n this.log(`Fetched prefetch widget configs in ${elapsed.toFixed(2)}ms`);\n \n } catch (error) {\n this.log(`Error fetching prefetch configs: ${error}`, true);\n }\n }\n \n private async fetchWidgets(): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/widgets/config`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Platform': 'web',\n 'X-Device-Type': this.getDeviceType()\n };\n\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.sourcePlatform) headers['X-Source-Platform'] = this.sourcePlatform;\n \n const response = await fetch(url, { headers, credentials: 'include' });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch widgets: ${response.status}`);\n }\n \n this.widgets = await response.json();\n \n this.log(`Fetched ${this.widgets.length} widgets`);\n \n } catch (error) {\n this.log(`Error fetching widgets: ${error}`, true);\n }\n }\n \n private renderImmediateWidgets(): void {\n const immediateWidgets = this.widgets.filter(w => \n !w.trigger_rules || w.trigger_rules.type === 'immediate'\n );\n \n immediateWidgets.forEach(widget => this.renderWidget(widget));\n }\n \n private setupTriggerListeners(): void {\n if (!this.triggerEngine) {\n this.log('TriggerEngine not provided, skipping trigger-based widgets');\n return;\n }\n \n this.widgets.forEach(widget => {\n if (!widget.trigger_rules) return;\n \n const { type, config } = widget.trigger_rules;\n \n switch (type) {\n case 'exit_intent':\n this.triggerEngine!.registerExitIntent();\n this.triggerEngine!.on('exit_intent', () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n \n case 'scroll_depth':\n const depthPercent = (config as any)?.depth_percent || 50;\n this.triggerEngine!.registerScrollDepth(depthPercent);\n this.triggerEngine!.on(`scroll_depth_${depthPercent}`, () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n \n case 'time_on_page':\n const seconds = (config as any)?.seconds || 30;\n this.triggerEngine!.registerTimeOnPage(seconds);\n this.triggerEngine!.on(`time_on_page_${seconds}`, () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n }\n });\n }\n \n private setupExitIntentWithPrefetch(): void {\n if (!this.enablePrefetch || !this.triggerEngine) {\n return;\n }\n \n const spinWheelConfig = this.prefetchWidgetConfigs.spin_wheel;\n const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;\n \n if (spinWheelConfig && spinWheelConfig.enabled) {\n const isMobile = this.isMobileDevice();\n const mobileConfig = exitIntentConfig?.mobile_triggers || {};\n \n const handleSpinWheelIntent = () => {\n const widgetId = `prefetch_spin_wheel_${Date.now()}`;\n \n if (this.renderedWidgets.has(widgetId)) {\n return;\n }\n \n this.renderedWidgets.add(widgetId);\n \n const detectedCart = this.detectPlatformCart();\n \n if (spinWheelConfig.type === 'cart_recovery' && detectedCart) {\n this.cartData = detectedCart;\n this.renderSpinWheelWidget(widgetId, spinWheelConfig);\n this.sendCartAbandonmentBeacon();\n } else if (spinWheelConfig.type === 'lead_gen') {\n this.renderSpinWheelWidget(widgetId, spinWheelConfig);\n }\n };\n \n if (isMobile) {\n const scrollVelocityEnabled = mobileConfig.scroll_velocity !== false;\n if (scrollVelocityEnabled) {\n this.triggerEngine.registerScrollVelocity({\n threshold: mobileConfig.scroll_threshold || 0.5,\n minScrollPosition: mobileConfig.scroll_min_position || 100,\n cooldown: mobileConfig.scroll_cooldown || 5000\n });\n this.triggerEngine.on('mobile_exit_intent', handleSpinWheelIntent);\n this.log('Registered mobile scroll velocity trigger for spin wheel');\n }\n \n const idleTimerEnabled = mobileConfig.idle_timer !== false;\n if (idleTimerEnabled) {\n const idleSeconds = mobileConfig.idle_seconds || 15;\n this.triggerEngine.registerInactivity(idleSeconds);\n this.triggerEngine.on(`inactivity_${idleSeconds}`, handleSpinWheelIntent);\n this.log(`Registered mobile idle timer trigger for spin wheel: ${idleSeconds}s`);\n }\n \n const visibilityChangeEnabled = mobileConfig.visibility_change !== false;\n if (visibilityChangeEnabled) {\n const detectedCart = this.detectPlatformCart();\n if (detectedCart && detectedCart.cart_items && detectedCart.cart_items.length > 0) {\n this.triggerEngine.registerVisibilityChange();\n this.triggerEngine.on('visibility_hidden', handleSpinWheelIntent);\n this.log('Registered mobile visibility change trigger for spin wheel (cart detected)');\n }\n }\n \n const backButtonEnabled = mobileConfig.back_button === true;\n if (backButtonEnabled) {\n this.triggerEngine.registerBackButton();\n this.triggerEngine.on('back_button', handleSpinWheelIntent);\n this.log('Registered mobile back button trigger for spin wheel');\n }\n \n this.log(`Setup mobile spin wheel: type=${spinWheelConfig.type}, priority=${spinWheelConfig.priority}`);\n } else {\n this.triggerEngine.registerExitIntent();\n this.triggerEngine.on('exit_intent', handleSpinWheelIntent);\n this.log(`Setup desktop spin wheel exit intent: type=${spinWheelConfig.type}, priority=${spinWheelConfig.priority}`);\n }\n \n return;\n }\n \n if (!exitIntentConfig || !exitIntentConfig.enabled) {\n this.log('No exit intent config found in prefetch');\n return;\n }\n \n const isMobile = this.isMobileDevice();\n const mobileConfig = exitIntentConfig.mobile_triggers || {};\n \n const handleExitIntent = () => {\n const widgetId = `prefetch_exit_intent_${Date.now()}`;\n \n if (this.renderedWidgets.has(widgetId)) {\n return;\n }\n \n this.renderedWidgets.add(widgetId);\n \n const detectedCart = this.detectPlatformCart();\n \n if (exitIntentConfig.type === 'cart_recovery' && detectedCart) {\n this.cartData = detectedCart;\n this.renderCartRecoveryPopup(widgetId, exitIntentConfig);\n this.sendCartAbandonmentBeacon();\n } else if (exitIntentConfig.type === 'lead_gen') {\n this.renderLeadGenPopup(widgetId, exitIntentConfig);\n } else {\n this.log('Unknown exit intent type or missing cart data');\n }\n };\n \n if (isMobile) {\n const scrollVelocityEnabled = mobileConfig.scroll_velocity !== false;\n if (scrollVelocityEnabled) {\n this.triggerEngine.registerScrollVelocity({\n threshold: mobileConfig.scroll_threshold || 0.5,\n minScrollPosition: mobileConfig.scroll_min_position || 100,\n cooldown: mobileConfig.scroll_cooldown || 5000\n });\n this.triggerEngine.on('mobile_exit_intent', handleExitIntent);\n this.log('Registered mobile scroll velocity trigger');\n }\n \n const idleTimerEnabled = mobileConfig.idle_timer !== false;\n if (idleTimerEnabled) {\n const idleSeconds = mobileConfig.idle_seconds || 15;\n this.triggerEngine.registerInactivity(idleSeconds);\n this.triggerEngine.on(`inactivity_${idleSeconds}`, handleExitIntent);\n this.log(`Registered mobile idle timer trigger: ${idleSeconds}s`);\n }\n \n const visibilityChangeEnabled = mobileConfig.visibility_change !== false;\n if (visibilityChangeEnabled) {\n const detectedCart = this.detectPlatformCart();\n if (detectedCart && detectedCart.cart_items && detectedCart.cart_items.length > 0) {\n this.triggerEngine.registerVisibilityChange();\n this.triggerEngine.on('visibility_hidden', handleExitIntent);\n this.log('Registered mobile visibility change trigger (cart detected)');\n } else {\n this.log('Skipped visibility change trigger (no cart items)');\n }\n }\n \n const backButtonEnabled = mobileConfig.back_button === true;\n if (backButtonEnabled) {\n this.triggerEngine.registerBackButton();\n this.triggerEngine.on('back_button', handleExitIntent);\n this.log('Registered mobile back button trigger (aggressive mode)');\n }\n \n this.log(`Setup mobile exit intent: type=${exitIntentConfig.type}, priority=${exitIntentConfig.priority}`);\n } else {\n this.triggerEngine.registerExitIntent();\n this.triggerEngine.on('exit_intent', handleExitIntent);\n this.log(`Setup desktop exit intent: type=${exitIntentConfig.type}, priority=${exitIntentConfig.priority}`);\n }\n }\n \n private sendCartAbandonmentBeacon(): void {\n if (!this.cartData) {\n this.log('No cart data available for beacon', true);\n return;\n }\n \n const beaconData = {\n organization_id: this.organizationId || 'default',\n contact_id: this.contactId,\n cart_id: this.cartData.cart_id,\n cart_total: this.cartData.cart_total,\n cart_currency: this.cartData.cart_currency,\n cart_items: this.cartData.cart_items,\n user_email: this.userId,\n abandoned_at: new Date().toISOString(),\n page_url: window.location.href,\n referrer: document.referrer\n };\n \n const beaconUrl = `${this.apiHost}/v1/widgets/beacon/cart-abandoned`;\n \n if (navigator.sendBeacon) {\n const blob = new Blob([JSON.stringify(beaconData)], { type: 'application/json' });\n const sent = navigator.sendBeacon(beaconUrl, blob);\n \n if (sent) {\n this.log('Cart abandonment beacon sent successfully');\n } else {\n this.log('Failed to send cart abandonment beacon', true);\n }\n } else {\n fetch(beaconUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey\n },\n body: JSON.stringify(beaconData),\n keepalive: true,\n credentials: 'include',\n }).catch(err => {\n this.log(`Error sending cart abandonment via fetch: ${err}`, true);\n });\n }\n }\n \n private renderWidget(widget: WidgetConfig): void {\n if (this.isDestroyed) return;\n if (this.renderedWidgets.has(widget.widget_id)) {\n return;\n }\n\n this.renderedWidgets.add(widget.widget_id);\n \n switch (widget.widget_type) {\n case 'chat_bubble':\n this.renderChatBubble(widget);\n break;\n case 'spin_wheel':\n this.renderSpinWheel(widget);\n break;\n case 'scratch_card':\n this.renderScratchCard(widget);\n break;\n case 'toast':\n this.renderToast(widget);\n break;\n case 'feedback_form':\n this.renderFeedbackForm(widget);\n break;\n case 'exit_intent_popup':\n this.renderExitIntentPopup(widget);\n break;\n default:\n this.log(`Unknown widget type: ${widget.widget_type}`, true);\n }\n \n this.trackEvent(widget.widget_id, 'show');\n }\n \n private renderChatBubble(widget: WidgetConfig): void {\n const { text, icon_url, link_url, background_color, text_color } = widget.config;\n const position = widget.position || 'bottom_right';\n \n const bubble = document.createElement('div');\n bubble.className = 'aegis-chat-bubble';\n bubble.setAttribute('data-widget-id', widget.widget_id);\n \n const positionStyles: Record<string, string> = {\n bottom_right: 'bottom: 20px; right: 20px;',\n bottom_left: 'bottom: 20px; left: 20px;',\n top_right: 'top: 20px; right: 20px;',\n top_left: 'top: 20px; left: 20px;'\n };\n \n bubble.style.cssText = `\n position: fixed;\n ${positionStyles[position] || positionStyles.bottom_right}\n background: ${this.sanitizeColor(background_color || '#25D366')};\n color: ${this.sanitizeColor(text_color || '#ffffff')};\n padding: 16px 24px;\n border-radius: 50px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n gap: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-weight: 600;\n font-size: 14px;\n animation: aegisBubbleIn 0.3s ease-out;\n `;\n \n if (icon_url) {\n const icon = document.createElement('img');\n const safeUrl = this.sanitizeUrl(icon_url);\n if (safeUrl) {\n icon.src = safeUrl;\n icon.alt = '';\n icon.style.cssText = 'width: 24px; height: 24px;';\n bubble.appendChild(icon);\n }\n }\n \n const textEl = document.createElement('span');\n textEl.textContent = text || 'Chat with us';\n bubble.appendChild(textEl);\n \n bubble.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'click');\n const safeUrl = this.sanitizeUrl(link_url);\n if (safeUrl) {\n window.open(safeUrl, '_blank');\n }\n });\n \n this.addAnimationStyles();\n bubble.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(bubble);\n }\n \n private renderSpinWheel(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-gamification-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-spin-wheel-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n padding: 32px;\n max-width: 500px;\n width: 90%;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'Spin to Win!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Try your luck and win exclusive prizes!';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n modal.appendChild(description);\n \n // Draw an SVG wheel that honours the tenant's configured segments.\n // Each segment's `color` + `label` is painted as a pie slice; falls\n // back to a 4-color placeholder if no segments are provided.\n const segments: Array<{ label: string; color?: string }> = Array.isArray(\n (widget.config as any).segments,\n )\n ? (widget.config as any).segments\n : [\n { label: 'Prize 1', color: '#ff6b6b' },\n { label: 'Prize 2', color: '#feca57' },\n { label: 'Prize 3', color: '#48dbfb' },\n { label: 'Prize 4', color: '#ff6348' },\n ];\n\n const SVG_NS = 'http://www.w3.org/2000/svg';\n const wheel = document.createElementNS(SVG_NS, 'svg');\n wheel.setAttribute('viewBox', '-160 -160 320 320');\n wheel.setAttribute('width', '300');\n wheel.setAttribute('height', '300');\n wheel.style.cssText = 'margin: 0 auto 24px; display: block;';\n\n const n = segments.length;\n const anglePer = (2 * Math.PI) / n;\n const radius = 140;\n for (let i = 0; i < n; i++) {\n const start = i * anglePer - Math.PI / 2;\n const end = start + anglePer;\n const x1 = Math.cos(start) * radius;\n const y1 = Math.sin(start) * radius;\n const x2 = Math.cos(end) * radius;\n const y2 = Math.sin(end) * radius;\n const largeArc = anglePer > Math.PI ? 1 : 0;\n const path = document.createElementNS(SVG_NS, 'path');\n path.setAttribute(\n 'd',\n `M 0 0 L ${x1.toFixed(2)} ${y1.toFixed(2)} A ${radius} ${radius} 0 ${largeArc} 1 ${x2.toFixed(2)} ${y2.toFixed(2)} Z`,\n );\n path.setAttribute('fill', this.sanitizeColor(segments[i].color || '#cccccc'));\n path.setAttribute('stroke', '#ffffff');\n path.setAttribute('stroke-width', '2');\n wheel.appendChild(path);\n\n const labelAngle = start + anglePer / 2;\n const lx = Math.cos(labelAngle) * radius * 0.65;\n const ly = Math.sin(labelAngle) * radius * 0.65;\n const text = document.createElementNS(SVG_NS, 'text');\n text.setAttribute('x', lx.toFixed(2));\n text.setAttribute('y', ly.toFixed(2));\n text.setAttribute('fill', '#ffffff');\n text.setAttribute('font-size', '13');\n text.setAttribute('font-weight', '600');\n text.setAttribute('text-anchor', 'middle');\n text.setAttribute('dominant-baseline', 'middle');\n text.textContent = segments[i].label || `#${i + 1}`;\n wheel.appendChild(text);\n }\n modal.appendChild(wheel);\n \n const spinButton = document.createElement('button');\n spinButton.textContent = 'SPIN NOW!';\n spinButton.style.cssText = `\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n padding: 16px 48px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n margin-bottom: 16px;\n `;\n \n spinButton.addEventListener('click', async () => {\n spinButton.disabled = true;\n spinButton.textContent = 'SPINNING...';\n \n try {\n const prize = await this.generatePrize(widget.config.config_id);\n\n wheel.style.animation = 'aegisSpin 2s ease-out';\n\n setTimeout(() => {\n this.showPrizeResult(modal, prize);\n }, 2000);\n \n } catch (error) {\n this.log(`Error generating prize: ${error}`, true);\n spinButton.disabled = false;\n spinButton.textContent = 'TRY AGAIN';\n }\n });\n \n modal.appendChild(spinButton);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderSpinWheelWidget(widgetId: string, config: NonNullable<PrefetchWidgetConfig['spin_wheel']>): void {\n if (this.isDestroyed) return;\n const cart = this.detectPlatformCart();\n \n if (config.type === 'cart_recovery' && !cart) {\n this.log('Spin wheel requires cart, but none detected');\n return;\n }\n \n if (cart) {\n this.cartData = cart;\n }\n \n const customization = this.cssCustomization.spinWheel || {};\n const accentColor = customization.accentColor || (config as any).accent_color || '#FF6B6B';\n const backgroundColor = customization.backgroundColor || (config as any).background_color || '#FFFFFF';\n const textColor = customization.textColor || (config as any).text_color || '#333333';\n const buttonColor = customization.buttonColor || (config as any).button_color || accentColor;\n const wheelColors = customization.wheelColors || ['#FF6B6B', '#4ECDC4', '#FFE66D', '#95E1D3'];\n \n const overlay = document.createElement('div');\n overlay.className = 'aegis-spin-wheel-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n animation: aegisFadeIn 0.3s ease;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-spin-wheel-modal';\n modal.style.cssText = `\n background: ${this.sanitizeColor(backgroundColor)};\n border-radius: 16px;\n padding: 32px;\n max-width: 500px;\n width: 90%;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n position: relative;\n animation: aegisScaleIn 0.4s ease;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n const closeBtn = document.createElement('button');\n closeBtn.innerHTML = '✕';\n closeBtn.className = 'aegis-spin-wheel-close';\n closeBtn.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #999;\n `;\n closeBtn.onclick = () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'spin_wheel' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n };\n modal.appendChild(closeBtn);\n \n const title = document.createElement('h2');\n title.textContent = this.interpolateCartVariables((config as any).title || 'Spin to Win!');\n title.style.cssText = `\n margin: 0 0 16px 0;\n font-size: 28px;\n font-weight: bold;\n text-align: center;\n color: ${this.sanitizeColor(textColor)};\n `;\n modal.appendChild(title);\n \n if (cart) {\n const subtitle = document.createElement('p');\n subtitle.textContent = `Complete your ${cart.cart_currency} ${cart.cart_total.toFixed(2)} order and save!`;\n subtitle.style.cssText = `\n margin: 0 0 24px 0;\n font-size: 16px;\n text-align: center;\n color: #666;\n `;\n modal.appendChild(subtitle);\n }\n \n const wheelContainer = document.createElement('div');\n wheelContainer.className = 'aegis-wheel-container';\n wheelContainer.style.cssText = `\n width: 300px;\n height: 300px;\n margin: 0 auto 24px auto;\n background: conic-gradient(\n from 0deg,\n ${wheelColors[0]} 0deg 90deg,\n ${wheelColors[1]} 90deg 180deg,\n ${wheelColors[2]} 180deg 270deg,\n ${wheelColors[3]} 270deg 360deg\n );\n border-radius: 50%;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n \n const form = document.createElement('form');\n form.className = 'aegis-spin-form';\n form.style.cssText = 'display: block;';\n \n const phoneInput = document.createElement('input');\n phoneInput.type = 'tel';\n phoneInput.placeholder = 'Enter your phone number';\n phoneInput.required = true;\n phoneInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 12px;\n box-sizing: border-box;\n `;\n form.appendChild(phoneInput);\n \n const emailInput = document.createElement('input');\n emailInput.type = 'email';\n emailInput.placeholder = 'Enter your email (optional)';\n emailInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 12px;\n box-sizing: border-box;\n `;\n form.appendChild(emailInput);\n \n const nameInput = document.createElement('input');\n nameInput.type = 'text';\n nameInput.placeholder = 'Enter your name (optional)';\n nameInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 12px;\n box-sizing: border-box;\n `;\n form.appendChild(nameInput);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Spin the Wheel!';\n submitButton.style.cssText = `\n width: 100%;\n padding: 14px;\n background: ${this.sanitizeColor(buttonColor)};\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 700;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n const errorMessage = document.createElement('div');\n errorMessage.className = 'aegis-spin-error';\n errorMessage.style.cssText = `\n color: #d32f2f;\n font-size: 14px;\n margin-top: 12px;\n text-align: center;\n display: none;\n `;\n form.appendChild(errorMessage);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n const phone = phoneInput.value.trim();\n const email = emailInput.value.trim();\n const name = nameInput.value.trim();\n \n if (!this.validatePhone(phone)) {\n errorMessage.textContent = 'Please enter a valid phone number (e.g., +1234567890)';\n errorMessage.style.display = 'block';\n return;\n }\n \n if (email && !this.validateEmail(email)) {\n errorMessage.textContent = 'Please enter a valid email address';\n errorMessage.style.display = 'block';\n return;\n }\n \n errorMessage.style.display = 'none';\n submitButton.disabled = true;\n submitButton.textContent = 'Spinning...';\n \n wheelContainer.style.animation = 'aegisSpin 2s ease-out';\n \n setTimeout(async () => {\n try {\n const prize = await this.submitSpinWheel({\n phone,\n email,\n name,\n cart,\n widgetId\n });\n \n this.showSpinWheelPrize(modal, prize);\n this.trackEvent(widgetId, 'submit', {\n type: 'spin_wheel',\n prize_label: prize.prize_label,\n has_email: !!email\n });\n \n } catch (error) {\n this.log(`Error submitting spin wheel: ${error}`, true);\n errorMessage.textContent = 'Failed to submit. Please try again.';\n errorMessage.style.display = 'block';\n submitButton.disabled = false;\n submitButton.textContent = 'Spin the Wheel!';\n wheelContainer.style.animation = '';\n }\n }, 2000);\n });\n \n modal.appendChild(wheelContainer);\n modal.appendChild(form);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'spin_wheel' });\n }\n \n private validatePhone(phone: string): boolean {\n const e164Regex = /^\\+?[1-9]\\d{1,14}$/;\n return e164Regex.test(phone.replace(/[\\s()-]/g, ''));\n }\n \n private validateEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n }\n \n private async submitSpinWheel(data: {\n phone: string;\n email: string;\n name: string;\n cart: CartData | null;\n widgetId: string;\n }): Promise<GamificationPrize> {\n const url = `${this.apiHost}/v1/widgets/spin-wheel/submit`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const geoRegion = this.detectGeoRegion();\n const deviceType = this.getDeviceType();\n const utmParams = this.getUTMParams();\n \n const body: any = {\n phone: data.phone.replace(/[\\s()-]/g, ''),\n email: data.email || undefined,\n name: data.name || undefined,\n cart_id: data.cart?.cart_id || `web_${Date.now()}`,\n cart_token: data.cart?.cart_id,\n cart_total: data.cart?.cart_total || 0,\n cart_currency: data.cart?.cart_currency || 'USD',\n cart_items: data.cart?.cart_items || [],\n cart_url: window.location.href,\n platform: 'web',\n geo_region: geoRegion,\n device_type: deviceType,\n session_id: this.getSessionId() || undefined,\n anonymous_id: this.getAnonymousId() || undefined,\n utm_source: utmParams.utm_source,\n utm_medium: utmParams.utm_medium,\n utm_campaign: utmParams.utm_campaign\n };\n \n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n credentials: 'include',\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to submit spin wheel: ${response.status} - ${errorText}`);\n }\n \n return await response.json();\n }\n \n private showSpinWheelPrize(modal: HTMLElement, prize: GamificationPrize): void {\n modal.innerHTML = '';\n \n const resultContainer = document.createElement('div');\n resultContainer.style.cssText = 'text-align: center; padding: 40px 20px;';\n \n const emoji = document.createElement('div');\n emoji.textContent = '🎉';\n emoji.style.cssText = 'font-size: 60px; margin-bottom: 20px;';\n resultContainer.appendChild(emoji);\n \n const title = document.createElement('h2');\n title.textContent = 'Congratulations!';\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 28px; font-weight: 700; color: #1a73e8;';\n resultContainer.appendChild(title);\n \n const prizeLabel = document.createElement('p');\n prizeLabel.textContent = prize.prize_label;\n prizeLabel.style.cssText = 'margin: 0 0 20px 0; font-size: 20px; font-weight: 600; color: #333;';\n resultContainer.appendChild(prizeLabel);\n \n if (prize.coupon_code) {\n const couponContainer = document.createElement('div');\n couponContainer.style.cssText = `\n background: #f5f5f5;\n padding: 16px;\n border-radius: 8px;\n margin-bottom: 20px;\n border: 2px dashed #1a73e8;\n `;\n \n const couponLabel = document.createElement('div');\n couponLabel.textContent = 'Your coupon code:';\n couponLabel.style.cssText = 'font-size: 12px; color: #666; margin-bottom: 8px;';\n couponContainer.appendChild(couponLabel);\n \n const couponCode = document.createElement('div');\n couponCode.textContent = prize.coupon_code;\n couponCode.style.cssText = 'font-size: 24px; font-weight: 700; color: #1a73e8; letter-spacing: 2px;';\n couponContainer.appendChild(couponCode);\n \n resultContainer.appendChild(couponContainer);\n }\n \n const message = document.createElement('p');\n message.textContent = 'Check your WhatsApp/SMS for your discount code and cart link!';\n message.style.cssText = 'margin: 0 0 20px 0; font-size: 14px; color: #666;';\n resultContainer.appendChild(message);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n \n closeButton.addEventListener('click', () => {\n const overlay = modal.parentElement;\n if (overlay && document.body.contains(overlay)) {\n document.body.removeChild(overlay);\n }\n });\n \n resultContainer.appendChild(closeButton);\n modal.appendChild(resultContainer);\n }\n \n private detectGeoRegion(): string {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n \n if (timezone.includes('America')) return 'north_america';\n if (timezone.includes('Europe')) return 'europe';\n if (timezone.includes('Asia/Kolkata') || timezone.includes('Asia/Calcutta')) return 'india';\n if (timezone.includes('Asia/Singapore') || timezone.includes('Asia/Bangkok') || timezone.includes('Asia/Jakarta')) return 'southeast_asia';\n if (timezone.includes('Asia/Dubai') || timezone.includes('Asia/Riyadh')) return 'middle_east';\n if (timezone.includes('America') && (timezone.includes('Sao_Paulo') || timezone.includes('Buenos_Aires'))) return 'latin_america';\n if (timezone.includes('Australia') || timezone.includes('Pacific/Auckland')) return 'oceania';\n \n return 'north_america';\n }\n \n private getUTMParams(): { utm_source?: string; utm_medium?: string; utm_campaign?: string } {\n const params = new URLSearchParams(window.location.search);\n return {\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined\n };\n }\n \n private getSessionId(): string | null {\n return sessionStorage.getItem('aegis_session_id');\n }\n \n private getAnonymousId(): string | null {\n return localStorage.getItem('aegis_anonymous_id');\n }\n \n private renderScratchCard(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-gamification-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-scratch-card-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n padding: 32px;\n max-width: 500px;\n width: 90%;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'Scratch & Win!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Scratch to reveal your prize!';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n modal.appendChild(description);\n \n const canvas = document.createElement('canvas');\n canvas.width = 300;\n canvas.height = 200;\n canvas.style.cssText = `\n border-radius: 8px;\n cursor: pointer;\n margin: 0 auto 24px;\n display: block;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n `;\n modal.appendChild(canvas);\n \n const ctx = canvas.getContext('2d');\n if (ctx) {\n ctx.fillStyle = '#c0c0c0';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = '#888';\n ctx.font = '20px Arial';\n ctx.textAlign = 'center';\n ctx.fillText('Scratch here!', canvas.width / 2, canvas.height / 2);\n \n let isScratching = false;\n \n const scratch = (x: number, y: number) => {\n ctx.globalCompositeOperation = 'destination-out';\n ctx.beginPath();\n ctx.arc(x, y, 20, 0, Math.PI * 2);\n ctx.fill();\n };\n \n canvas.addEventListener('mousedown', () => { isScratching = true; });\n canvas.addEventListener('mouseup', () => { isScratching = false; });\n canvas.addEventListener('mousemove', (e) => {\n if (isScratching) {\n const rect = canvas.getBoundingClientRect();\n scratch(e.clientX - rect.left, e.clientY - rect.top);\n }\n });\n }\n \n const scratchButton = document.createElement('button');\n scratchButton.textContent = 'REVEAL PRIZE';\n scratchButton.style.cssText = `\n background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);\n color: white;\n border: none;\n padding: 16px 48px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n `;\n \n scratchButton.addEventListener('click', async () => {\n scratchButton.disabled = true;\n scratchButton.textContent = 'REVEALING...';\n \n try {\n const prize = await this.generatePrize(widget.config.config_id);\n this.showPrizeResult(modal, prize);\n } catch (error) {\n this.log(`Error generating prize: ${error}`, true);\n scratchButton.disabled = false;\n scratchButton.textContent = 'TRY AGAIN';\n }\n });\n \n modal.appendChild(scratchButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderToast(widget: WidgetConfig): void {\n const { message, icon, duration } = widget.config;\n const position = widget.position || 'bottom_left';\n \n const toast = document.createElement('div');\n toast.className = 'aegis-toast';\n toast.setAttribute('data-widget-id', widget.widget_id);\n \n const positionStyles: Record<string, string> = {\n bottom_left: 'bottom: 20px; left: 20px;',\n bottom_right: 'bottom: 20px; right: 20px;',\n top_left: 'top: 20px; left: 20px;',\n top_right: 'top: 20px; right: 20px;'\n };\n \n toast.style.cssText = `\n position: fixed;\n ${positionStyles[position] || positionStyles.bottom_left}\n background: white;\n padding: 16px 20px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n z-index: 999998;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: 350px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisToastIn 0.3s ease-out;\n `;\n \n if (icon) {\n const iconEl = document.createElement('div');\n iconEl.textContent = icon;\n iconEl.style.cssText = 'font-size: 20px;';\n toast.appendChild(iconEl);\n }\n \n const messageEl = document.createElement('div');\n messageEl.textContent = message || 'Someone just made a purchase!';\n messageEl.style.cssText = 'flex: 1; font-size: 13px; color: #333;';\n toast.appendChild(messageEl);\n \n this.addAnimationStyles();\n toast.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(toast);\n \n setTimeout(() => {\n toast.style.animation = 'aegisToastOut 0.3s ease-out';\n setTimeout(() => {\n if (document.body.contains(toast)) {\n document.body.removeChild(toast);\n }\n this.renderedWidgets.delete(widget.widget_id);\n }, 300);\n }, duration || 5000);\n }\n \n private renderFeedbackForm(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-feedback-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 400px;\n background: white;\n box-shadow: -4px 0 20px rgba(0,0,0,0.15);\n z-index: 1000000;\n padding: 32px;\n overflow-y: auto;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInRight 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'We value your feedback!';\n title.style.cssText = 'margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n overlay.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Help us improve your experience.';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n overlay.appendChild(description);\n \n const form = document.createElement('form');\n \n const label = document.createElement('label');\n label.textContent = 'How likely are you to recommend us? (0-10)';\n label.style.cssText = 'display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: #333;';\n form.appendChild(label);\n \n const input = document.createElement('input');\n input.type = 'number';\n input.min = '0';\n input.max = '10';\n input.style.cssText = `\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n margin-bottom: 16px;\n `;\n form.appendChild(input);\n \n const textareaLabel = document.createElement('label');\n textareaLabel.textContent = 'Tell us more (optional)';\n textareaLabel.style.cssText = 'display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: #333;';\n form.appendChild(textareaLabel);\n \n const textarea = document.createElement('textarea');\n textarea.rows = 4;\n textarea.style.cssText = `\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n resize: vertical;\n margin-bottom: 16px;\n font-family: inherit;\n `;\n form.appendChild(textarea);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Submit Feedback';\n submitButton.style.cssText = `\n width: 100%;\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px;\n border-radius: 6px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n \n try {\n await this.submitFeedback(widget.widget_id, {\n score: parseInt(input.value),\n comment: textarea.value\n });\n \n overlay.innerHTML = '<div style=\"text-align: center; padding: 60px 20px;\"><h3 style=\"margin: 0 0 12px 0; color: #1a73e8;\">Thank you!</h3><p style=\"margin: 0; color: #666;\">Your feedback helps us improve.</p></div>';\n \n setTimeout(() => {\n document.body.removeChild(overlay);\n }, 2000);\n \n } catch (error) {\n this.log(`Error submitting feedback: ${error}`, true);\n submitButton.disabled = false;\n submitButton.textContent = 'Submit Feedback';\n }\n });\n \n overlay.appendChild(form);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n overlay.appendChild(closeButton);\n \n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderExitIntentPopup(widget: WidgetConfig): void {\n const { title, description, cta_text, cta_url, image_url } = widget.config;\n \n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-exit-popup-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n max-width: 600px;\n width: 90%;\n overflow: hidden;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 250px; object-fit: cover;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 32px;';\n \n const titleEl = document.createElement('h2');\n titleEl.textContent = title || 'Wait! Don\\'t leave yet!';\n titleEl.style.cssText = 'margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(titleEl);\n \n const descEl = document.createElement('p');\n descEl.textContent = description || 'Get 10% off your first order!';\n descEl.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; color: #666; line-height: 1.5;';\n content.appendChild(descEl);\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n content.appendChild(ctaButton);\n }\n \n modal.appendChild(content);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: white;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #666;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private async generatePrize(configId: string): Promise<GamificationPrize> {\n const url = `${this.apiHost}/v1/widgets/gamification/generate-prize`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ config_id: configId }),\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to generate prize: ${response.status}`);\n }\n \n return await response.json();\n }\n \n private showPrizeResult(modal: HTMLElement, prize: GamificationPrize): void {\n modal.innerHTML = '';\n \n const resultContainer = document.createElement('div');\n resultContainer.style.cssText = 'text-align: center; padding: 40px 20px;';\n \n const emoji = document.createElement('div');\n emoji.textContent = '🎉';\n emoji.style.cssText = 'font-size: 60px; margin-bottom: 20px;';\n resultContainer.appendChild(emoji);\n \n const title = document.createElement('h2');\n title.textContent = 'Congratulations!';\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 28px; font-weight: 700; color: #1a73e8;';\n resultContainer.appendChild(title);\n \n const prizeLabel = document.createElement('p');\n prizeLabel.textContent = prize.prize_label;\n prizeLabel.style.cssText = 'margin: 0 0 20px 0; font-size: 20px; font-weight: 600; color: #333;';\n resultContainer.appendChild(prizeLabel);\n \n if (prize.coupon_code) {\n const couponContainer = document.createElement('div');\n couponContainer.style.cssText = `\n background: #f5f5f5;\n padding: 16px;\n border-radius: 8px;\n margin-bottom: 20px;\n border: 2px dashed #1a73e8;\n `;\n \n const couponLabel = document.createElement('div');\n couponLabel.textContent = 'Your coupon code:';\n couponLabel.style.cssText = 'font-size: 12px; color: #666; margin-bottom: 8px;';\n couponContainer.appendChild(couponLabel);\n \n const couponCode = document.createElement('div');\n couponCode.textContent = prize.coupon_code;\n couponCode.style.cssText = 'font-size: 24px; font-weight: 700; color: #1a73e8; letter-spacing: 2px;';\n couponContainer.appendChild(couponCode);\n \n resultContainer.appendChild(couponContainer);\n }\n \n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n \n closeButton.addEventListener('click', () => {\n const overlay = modal.parentElement;\n if (overlay && document.body.contains(overlay)) {\n document.body.removeChild(overlay);\n }\n });\n \n resultContainer.appendChild(closeButton);\n modal.appendChild(resultContainer);\n }\n \n private async submitFeedback(widgetId: string, data: Record<string, any>): Promise<void> {\n const url = `${this.apiHost}/v1/widgets/track-event`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n // Plumb workspace cascade so feedback submissions are attributed to\n // the same outlet the customer was viewing. See\n // PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n const workspaceId = this.getWorkspaceId?.();\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n widget_id: widgetId,\n event_type: 'submit',\n event_data: data,\n workspace_id: workspaceId,\n }),\n credentials: 'include',\n });\n \n if (!response.ok) {\n throw new Error(`Failed to submit feedback: ${response.status}`);\n }\n }\n \n private async trackEvent(widgetId: string, eventType: string, metadata?: Record<string, any>): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/widgets/track-event`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n\n const workspaceId = this.getWorkspaceId?.();\n\n await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n widget_id: widgetId,\n event_type: eventType,\n event_data: metadata || {},\n workspace_id: workspaceId,\n }),\n credentials: 'include',\n });\n\n } catch (error) {\n this.log(`Error tracking widget event: ${error}`, true);\n }\n }\n \n private sanitizeUrl(url?: string): string | null {\n if (!url) return null;\n \n try {\n const parsed = new URL(url, window.location.href);\n if (parsed.protocol === 'javascript:' || parsed.protocol === 'data:') {\n this.log(`Blocked dangerous URL: ${url}`, true);\n return null;\n }\n return parsed.href;\n } catch {\n this.log(`Invalid URL: ${url}`, true);\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n if (/^#[0-9A-F]{3,8}$/i.test(color)) {\n return color;\n }\n \n if (/^rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)$/i.test(color)) {\n return color;\n }\n \n if (/^rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)$/i.test(color)) {\n return color;\n }\n \n return '#1a73e8';\n }\n \n private getDeviceType(): string {\n const ua = navigator.userAgent;\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\n return 'tablet';\n }\n if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\n return 'mobile';\n }\n return 'desktop';\n }\n \n private isMobileDevice(): boolean {\n const deviceType = this.getDeviceType();\n return deviceType === 'mobile' || deviceType === 'tablet';\n }\n \n private addAnimationStyles(): void {\n if (document.getElementById('aegis-widget-animations')) {\n return;\n }\n \n const style = document.createElement('style');\n style.id = 'aegis-widget-animations';\n style.textContent = `\n @keyframes aegisFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes aegisScaleIn {\n from { transform: scale(0.8); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n \n @keyframes aegisBubbleIn {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes aegisToastIn {\n from { transform: translateX(-100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n \n @keyframes aegisToastOut {\n from { transform: translateX(0); opacity: 1; }\n to { transform: translateX(-100%); opacity: 0; }\n }\n \n @keyframes aegisSlideInRight {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n \n @keyframes aegisSpin {\n from { transform: rotate(0deg); }\n to { transform: rotate(1440deg); }\n }\n `;\n document.head.appendChild(style);\n }\n \n private renderCartRecoveryPopup(widgetId: string, config: NonNullable<PrefetchWidgetConfig['exit_intent']>): void {\n if (this.isDestroyed) return;\n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n overlay.setAttribute('data-aegis-widget-root', '');\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-cart-recovery-modal';\n modal.style.cssText = `\n background: ${this.sanitizeColor(config.background_color || '#FFFFFF')};\n color: ${this.sanitizeColor(config.text_color || '#333333')};\n border-radius: 16px;\n max-width: 600px;\n width: 90%;\n padding: 40px;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n position: relative;\n `;\n \n const title = document.createElement('h2');\n const titleText = this.interpolateCartVariables(config.title || 'Complete Your Order');\n title.textContent = titleText;\n title.style.cssText = `margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: ${this.sanitizeColor(config.accent_color || '#FF6B00')};`;\n modal.appendChild(title);\n \n const message = document.createElement('p');\n const messageText = this.interpolateCartVariables(config.message || 'Your cart is waiting for you!');\n message.textContent = messageText;\n message.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; line-height: 1.5;';\n modal.appendChild(message);\n \n if (config.show_cart_items && this.cartData) {\n const cartItems = document.createElement('div');\n cartItems.style.cssText = 'margin: 0 0 24px 0; padding: 16px; background: rgba(0,0,0,0.05); border-radius: 8px;';\n \n const cartTitle = document.createElement('div');\n cartTitle.textContent = `${this.cartData.cart_items.length} items in your cart`;\n cartTitle.style.cssText = 'font-weight: 600; margin-bottom: 12px;';\n cartItems.appendChild(cartTitle);\n \n this.cartData.cart_items.slice(0, 3).forEach(item => {\n const itemDiv = document.createElement('div');\n itemDiv.textContent = `${item.quantity}× ${item.product_name || item.product_id}`;\n itemDiv.style.cssText = 'font-size: 14px; margin-bottom: 4px;';\n cartItems.appendChild(itemDiv);\n });\n \n modal.appendChild(cartItems);\n }\n \n if (config.discount_code) {\n const discountBox = document.createElement('div');\n discountBox.style.cssText = `\n margin: 0 0 24px 0;\n padding: 16px;\n background: ${this.sanitizeColor(config.accent_color || '#FF6B00')};\n color: white;\n border-radius: 8px;\n text-align: center;\n font-weight: 700;\n font-size: 18px;\n `;\n discountBox.textContent = `Use code: ${config.discount_code} for ${config.discount_percentage || 10}% off!`;\n modal.appendChild(discountBox);\n }\n \n if (config.show_timer && config.timer_minutes) {\n const timer = document.createElement('div');\n timer.style.cssText = 'margin: 0 0 24px 0; text-align: center; font-size: 14px; color: #999;';\n timer.textContent = `⏰ Offer expires in ${config.timer_minutes} minutes`;\n modal.appendChild(timer);\n }\n \n const ctaButton = document.createElement('button');\n ctaButton.textContent = config.cta_text || 'Complete Checkout';\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(config.accent_color || '#FF6B00')};\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'click', { type: 'cart_recovery' });\n const checkoutUrl = this.sanitizeUrl(config.cta_url || '/checkout');\n if (checkoutUrl) {\n window.location.href = checkoutUrl;\n }\n });\n \n modal.appendChild(ctaButton);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'cart_recovery' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n });\n \n modal.appendChild(closeButton);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'cart_recovery', tier: config.tier });\n }\n \n private renderLeadGenPopup(widgetId: string, config: NonNullable<PrefetchWidgetConfig['exit_intent']>): void {\n if (this.isDestroyed) return;\n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n overlay.setAttribute('data-aegis-widget-root', '');\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-leadgen-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n max-width: 500px;\n width: 90%;\n padding: 40px;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n position: relative;\n `;\n \n const title = document.createElement('h2');\n title.textContent = config.title || 'Get 10% Off Your First Order!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const message = document.createElement('p');\n message.textContent = config.message || 'Subscribe to our newsletter and get an exclusive discount code.';\n message.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; color: #666;';\n modal.appendChild(message);\n \n const form = document.createElement('form');\n \n const emailInput = document.createElement('input');\n emailInput.type = 'email';\n emailInput.placeholder = 'Enter your email';\n emailInput.required = true;\n emailInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 16px;\n box-sizing: border-box;\n `;\n form.appendChild(emailInput);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Get My Discount';\n submitButton.style.cssText = `\n width: 100%;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n \n try {\n await this.trackEvent(widgetId, 'submit', { \n type: 'lead_gen',\n email: emailInput.value \n });\n \n modal.innerHTML = `\n <div style=\"text-align: center; padding: 20px;\">\n <div style=\"font-size: 48px; margin-bottom: 16px;\">✅</div>\n <h3 style=\"margin: 0 0 12px 0; color: #1a73e8;\">Thank You!</h3>\n <p style=\"margin: 0; color: #666;\">Check your email for your discount code!</p>\n </div>\n `;\n \n setTimeout(() => {\n document.body.removeChild(overlay);\n }, 2500);\n \n } catch (error) {\n this.log(`Error submitting lead gen form: ${error}`, true);\n submitButton.disabled = false;\n submitButton.textContent = 'Get My Discount';\n }\n });\n \n modal.appendChild(form);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'lead_gen' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n });\n \n modal.appendChild(closeButton);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'lead_gen' });\n }\n \n private interpolateCartVariables(template: string): string {\n if (!this.cartData) return template;\n \n return template\n .replace(/\\{\\{cart_total\\}\\}/g, `${this.cartData.cart_currency} ${this.cartData.cart_total.toFixed(2)}`)\n .replace(/\\{\\{cart_currency\\}\\}/g, this.cartData.cart_currency)\n .replace(/\\{\\{cart_items_count\\}\\}/g, this.cartData.cart_items.length.toString());\n }\n \n private log(message: string, isError: boolean = false): void {\n if (this.debugMode || isError) {\n const method = isError ? 'error' : 'log';\n console[method](`[AegisWidgets] ${message}`);\n }\n }\n}\n","/**\n * TriggerEngine - Client-Side Trigger Evaluation\n * \n * Evaluates behavioral triggers that require real-time client state:\n * - Scroll depth (e.g., user scrolled >= 50%)\n * - Time on page (e.g., >= 30 seconds on current page)\n * - Exit intent (mouse leaving viewport toward browser controls)\n * - Inactivity (no user interaction for X seconds)\n * - Scroll velocity (mobile exit intent - fast upward scroll)\n * - Visibility change (tab/app switching)\n * - Back button (history navigation)\n * \n * Architecture:\n * - Server evaluates historical triggers (event counts, segments)\n * - Client evaluates behavioral triggers (scroll, time, exit intent)\n * - Zero latency - no server round trip needed\n * \n * Usage:\n * const trigger = new TriggerEngine();\n * trigger.on('scroll_depth_50', (data) => {\n * console.log('User scrolled to 50%', data);\n * });\n * trigger.start();\n */\n\nexport interface TriggerRule {\n type: 'scroll_depth' | 'time_on_page' | 'exit_intent' | 'inactivity' | 'scroll_velocity' | 'visibility_change' | 'back_button';\n config: ScrollDepthConfig | TimeOnPageConfig | ExitIntentConfig | InactivityConfig | ScrollVelocityConfig | VisibilityChangeConfig | BackButtonConfig;\n}\n\nexport interface ScrollDepthConfig {\n depth_percent: number;\n}\n\nexport interface TimeOnPageConfig {\n seconds: number;\n}\n\nexport interface ExitIntentConfig {\n enabled: boolean;\n}\n\nexport interface InactivityConfig {\n idle_seconds: number;\n}\n\nexport interface ScrollVelocityConfig {\n threshold: number;\n minScrollPosition: number;\n cooldown: number;\n}\n\nexport interface VisibilityChangeConfig {\n enabled: boolean;\n}\n\nexport interface BackButtonConfig {\n enabled: boolean;\n}\n\n/**\n * Rage-click detection (Micro-Intent Engine P2 Task 1).\n *\n * Per-intent ring buffer: when ≥ `threshold` clicks land on the same\n * intent inside `windowMs`, the engine emits `rage_click` with the\n * intent name + current count + window. Frustration signal used by the\n * Stuck-at-Checkout pattern (e.g., rage-clicking the coupon input four\n * times in 800ms).\n *\n * Defaults match the plan:\n * threshold = 3 clicks\n * windowMs = 1000 ms\n *\n * TriggerEngine does NOT attach its own pointerdown listener — the\n * SelectorBinder (P1 Task 5) owns the global, delegated listener and\n * dispatches per-intent click events. The IntentSnapshotCollector\n * (P2 Task 5) wires `SelectorBinder.onIntent` to\n * `TriggerEngine.noteClick(intent)` so TriggerEngine sees a clean\n * intent-stamped stream instead of raw DOM events.\n */\nexport interface RageClickConfig {\n threshold: number;\n windowMs: number;\n}\n\n/** Payload emitted on the `rage_click` event. */\nexport interface RageClickData {\n intent: string;\n count: number;\n window_ms: number;\n}\n\n/**\n * Hover-dwell tracking (Micro-Intent Engine P2 Task 2).\n *\n * Tracks per-intent hover duration in milliseconds. `getHoverMs(intent)`\n * returns:\n * - if currently hovering the intent: ms since hover_start (live)\n * - else: ms duration of the most recent hover on that intent (sticky)\n * - if never hovered: 0\n *\n * The sticky behaviour is deliberate. The canonical use case\n * (MICRO_INTENT_ENGINE.md Story 1):\n * \"Priya hovers price for 1.8s, considers, moves cursor away.\n * Exit_intent fires. Rule checks price_hover_ms >= 1500 → true.\"\n * The rule evaluator queries `price_hover_ms` AFTER the hover ends. If\n * we reset to 0 on hover_end, the rule never matches the very pattern\n * the engine exists to detect.\n *\n * Threshold-based emission: when a hover crosses one of the registered\n * dwell targets (e.g. 1500ms), a `<intent>_hover_dwell_<ms>` event\n * fires. Useful for journeys that want event-style hooks; the\n * snapshot-style `getHoverMs` is the primary surface for the\n * compound-rule evaluator.\n */\nexport interface HoverDwellConfig {\n /** Dwell thresholds (ms) per intent. The engine fires\n * `<intent>_hover_dwell_<ms>` when an active hover crosses each\n * threshold. Defaults: price→[1500], cta→[1500]. */\n thresholdsByIntent?: Partial<Record<string, ReadonlyArray<number>>>;\n}\n\n/**\n * Mouse velocity toward the viewport top (Micro-Intent Engine P2 Task 3).\n *\n * Detects the early phase of exit-intent: the mouse moving rapidly\n * upward before it actually crosses out of the viewport. The signal is\n * a continuous px/ms value (positive = moving up), updated as samples\n * arrive. When velocity crosses `threshold`, a `mouse_velocity_to_top`\n * event fires (once per session-window cooldown to avoid spamming on a\n * single gesture).\n *\n * Sampling: callers feed positions via `noteMousePosition(x, y, ts)`.\n * The engine keeps a sliding `windowMs` ring buffer of samples and\n * computes velocity as the average dy / dt over samples in the window.\n * Per the plan: sampled at 100ms, 400ms window. That bound is\n * configurable.\n *\n * Why \"to top\" specifically: exit-intent fires only when y crosses 0;\n * we want the warning BEFORE that. A typical exit-intent gesture has\n * upward velocity well above any normal browsing motion.\n */\nexport interface MouseVelocityConfig {\n /** Threshold in px/ms (positive = moving up). Default 1.5 px/ms,\n * ≈ moving 600px in 400ms, which only happens during a deliberate\n * upward sweep. */\n threshold: number;\n /** Rolling-window length over which to compute velocity. */\n windowMs: number;\n /** Cooldown after a fire before the signal can fire again. */\n cooldownMs: number;\n}\n\nexport interface TriggerEvent {\n type: string;\n data: Record<string, any>;\n timestamp: number;\n}\n\ntype TriggerCallback = (event: TriggerEvent) => void;\n\nexport class TriggerEngine {\n private listeners: Map<string, Set<TriggerCallback>> = new Map();\n private isStarted = false;\n \n private scrollDepthTargets = new Set<number>();\n private scrollDepthReached = new Set<number>();\n \n private timeOnPageTargets = new Map<number, number>();\n private pageLoadTime?: number;\n \n private exitIntentEnabled = false;\n private exitIntentFired = false;\n \n private inactivityTargets = new Map<number, number>();\n private lastActivityTime = Date.now();\n private inactivityCheckInterval?: number;\n \n private scrollVelocityEnabled = false;\n private scrollVelocityFired = false;\n private scrollVelocityConfig: ScrollVelocityConfig = {\n threshold: 0.5,\n minScrollPosition: 100,\n cooldown: 5000\n };\n private lastScrollY = 0;\n private lastScrollTime = Date.now();\n private scrollVelocityCooldownTimer?: number;\n \n private visibilityChangeEnabled = false;\n\n private backButtonEnabled = false;\n private backButtonFired = false;\n\n // Rage-click state (P2 Task 1). Buffer indexed by intent name (price /\n // cta / coupon / checkout). Per-intent so rage-clicking the coupon\n // input doesn't get conflated with rapid CTA presses.\n private rageClickEnabled = false;\n private rageClickConfig: RageClickConfig = { threshold: 3, windowMs: 1000 };\n private rageClickBuffer = new Map<string, number[]>();\n /** Tracks whether a rage_click event has already fired for an intent\n * in the current burst. Reset when the buffer empties below threshold.\n * Prevents the same burst from firing the event N-2 times as each\n * click after threshold pushes the count higher. */\n private rageClickFiredInBurst = new Set<string>();\n\n // Hover-dwell state (P2 Task 2). Tracks live + sticky-last dwell per\n // intent. `hoverStartAt` undefined means no active hover.\n private hoverEnabled = false;\n private hoverStartAt = new Map<string, number>();\n private hoverLastMs = new Map<string, number>();\n private hoverThresholds = new Map<string, ReadonlyArray<number>>([\n ['price', [1500]],\n ['cta', [1500]],\n ]);\n /** Track which (intent, threshold) pairs have already fired in the\n * current hover so a single long hover doesn't double-fire as time\n * marches on. Reset on hover_start. */\n private hoverThresholdsFired = new Map<string, Set<number>>();\n /** Pending threshold timers, keyed by intent. Cleared on hover_end. */\n private hoverThresholdTimers = new Map<string, number[]>();\n\n // Mouse-velocity-to-top state (P2 Task 3).\n private mouseVelEnabled = false;\n private mouseVelConfig: MouseVelocityConfig = {\n threshold: 1.5,\n windowMs: 400,\n cooldownMs: 5000,\n };\n /** Ring buffer of (x, y, ts) samples within the rolling window. */\n private mouseSamples: Array<{ x: number; y: number; ts: number }> = [];\n /** Last computed velocity-to-top in px/ms (positive = moving up). */\n private mouseVelLast = 0;\n /** Epoch ms when the velocity event last fired — gates cooldown. */\n private mouseVelLastFiredAt = 0;\n\n constructor() {}\n \n on(eventType: string, callback: TriggerCallback): void {\n if (!this.listeners.has(eventType)) {\n this.listeners.set(eventType, new Set());\n }\n this.listeners.get(eventType)!.add(callback);\n }\n \n off(eventType: string, callback: TriggerCallback): void {\n const callbacks = this.listeners.get(eventType);\n if (callbacks) {\n callbacks.delete(callback);\n }\n }\n \n registerScrollDepth(depthPercent: number): void {\n this.scrollDepthTargets.add(depthPercent);\n }\n \n registerTimeOnPage(seconds: number): void {\n if (!this.timeOnPageTargets.has(seconds)) {\n const timerId = window.setTimeout(() => {\n this.emit(`time_on_page_${seconds}`, {\n seconds,\n page_url: window.location.href\n });\n this.timeOnPageTargets.delete(seconds);\n }, seconds * 1000);\n \n this.timeOnPageTargets.set(seconds, timerId);\n }\n }\n \n registerExitIntent(): void {\n this.exitIntentEnabled = true;\n }\n \n registerInactivity(idleSeconds: number): void {\n if (!this.inactivityTargets.has(idleSeconds)) {\n const timerId = window.setTimeout(() => {\n const idleTime = (Date.now() - this.lastActivityTime) / 1000;\n \n if (idleTime >= idleSeconds) {\n this.emit(`inactivity_${idleSeconds}`, {\n idle_seconds: idleSeconds,\n actual_idle_time: idleTime\n });\n }\n \n this.inactivityTargets.delete(idleSeconds);\n }, idleSeconds * 1000);\n \n this.inactivityTargets.set(idleSeconds, timerId);\n }\n }\n \n registerScrollVelocity(config?: Partial<ScrollVelocityConfig>): void {\n this.scrollVelocityEnabled = true;\n \n if (config) {\n this.scrollVelocityConfig = {\n threshold: config.threshold ?? this.scrollVelocityConfig.threshold,\n minScrollPosition: config.minScrollPosition ?? this.scrollVelocityConfig.minScrollPosition,\n cooldown: config.cooldown ?? this.scrollVelocityConfig.cooldown\n };\n }\n }\n \n registerVisibilityChange(): void {\n this.visibilityChangeEnabled = true;\n }\n \n registerBackButton(): void {\n this.backButtonEnabled = true;\n }\n\n /**\n * Enable rage-click detection (P2 Task 1).\n *\n * After calling this, feed click events via `noteClick(intent)`.\n * SelectorBinder (P1 Task 5) is the upstream source; the wiring lives\n * in IntentSnapshotCollector (P2 Task 5). Calling `noteClick` without\n * a prior `registerRageClick()` is a no-op so legacy callers don't\n * accidentally enable the feature.\n */\n registerRageClick(config?: Partial<RageClickConfig>): void {\n this.rageClickEnabled = true;\n if (config) {\n this.rageClickConfig = {\n threshold: config.threshold ?? this.rageClickConfig.threshold,\n windowMs: config.windowMs ?? this.rageClickConfig.windowMs,\n };\n }\n }\n\n /**\n * Feed a click event for rage-click detection. Returns the current\n * count within-window for the intent — useful for the snapshot\n * collector (P2 Task 5) which updates IntentRuleEvaluator with\n * `rage_click_count` continuously, not just at the burst threshold.\n *\n * Idempotent w.r.t. `registerRageClick` — when not enabled, this\n * returns 0 without buffering.\n */\n noteClick(intent: string, timestamp: number = Date.now()): number {\n if (!this.rageClickEnabled) return 0;\n const buf = this.rageClickBuffer.get(intent) ?? [];\n const cutoff = timestamp - this.rageClickConfig.windowMs;\n // Drop expired timestamps (clicks older than the window).\n // The buffer is append-only and timestamps are non-decreasing in\n // practice (Date.now is monotonic per device, modulo system-clock\n // adjustments), so an in-place trim from the head is correct.\n let trimStart = 0;\n while (trimStart < buf.length && buf[trimStart] < cutoff) trimStart++;\n const recent = trimStart === 0 ? buf : buf.slice(trimStart);\n recent.push(timestamp);\n this.rageClickBuffer.set(intent, recent);\n\n // Burst reset: when the recent count drops below threshold (because\n // older clicks aged out), the burst is over — re-arm the per-intent\n // fire-once latch so a fresh burst can fire again.\n if (recent.length < this.rageClickConfig.threshold) {\n this.rageClickFiredInBurst.delete(intent);\n }\n\n if (\n recent.length >= this.rageClickConfig.threshold &&\n !this.rageClickFiredInBurst.has(intent)\n ) {\n this.rageClickFiredInBurst.add(intent);\n this.emit('rage_click', {\n intent,\n count: recent.length,\n window_ms: this.rageClickConfig.windowMs,\n });\n }\n\n return recent.length;\n }\n\n /** Snapshot the current rage_click count for an intent. The\n * IntentSnapshotCollector (P2 Task 5) calls this when building the\n * `rage_click_count` signal value for the IntentRuleEvaluator. */\n getRageClickCount(intent: string): number {\n if (!this.rageClickEnabled) return 0;\n const buf = this.rageClickBuffer.get(intent);\n if (!buf) return 0;\n const cutoff = Date.now() - this.rageClickConfig.windowMs;\n // Lazy prune on read so the count reflects only currently-valid\n // clicks. Cheap because buffers are tiny (cap at threshold + 1 in\n // practice — older entries get dropped on every note).\n let live = 0;\n for (const ts of buf) {\n if (ts >= cutoff) live++;\n }\n return live;\n }\n\n /**\n * Enable hover-dwell tracking (P2 Task 2). After registration, feed\n * hover events via `noteHoverStart(intent, ts)` / `noteHoverEnd`.\n * SelectorBinder (P1 Task 5) is the upstream source; wiring lands in\n * P2 Task 5.\n */\n registerHoverDwell(config?: HoverDwellConfig): void {\n this.hoverEnabled = true;\n if (config?.thresholdsByIntent) {\n for (const [intent, thresholds] of Object.entries(config.thresholdsByIntent)) {\n if (thresholds) this.hoverThresholds.set(intent, thresholds);\n }\n }\n }\n\n /** Record the start of a hover on the named intent. Schedules\n * per-threshold timers that emit `<intent>_hover_dwell_<ms>` when\n * the active hover passes each registered threshold. */\n noteHoverStart(intent: string, timestamp: number = Date.now()): void {\n if (!this.hoverEnabled) return;\n // If a prior hover is still pending (no hover_end fired — possible\n // when the DOM node was unmounted mid-hover), close it out first to\n // keep `hoverLastMs` consistent.\n if (this.hoverStartAt.has(intent)) {\n this.noteHoverEnd(intent, timestamp);\n }\n this.hoverStartAt.set(intent, timestamp);\n this.hoverThresholdsFired.set(intent, new Set());\n\n // Schedule threshold timers. Uses the global setTimeout (works in\n // both browser and node, and vitest's fake-timers stub the global).\n // registerHoverDwell with no thresholds for an intent => no events,\n // only the snapshot value.\n const thresholds = this.hoverThresholds.get(intent);\n if (thresholds) {\n const timers: number[] = [];\n for (const ms of thresholds) {\n const timerId = setTimeout(() => {\n // Only fire if still hovering this intent — the event must\n // not fire after hover_end.\n if (!this.hoverStartAt.has(intent)) return;\n const firedSet = this.hoverThresholdsFired.get(intent);\n if (firedSet?.has(ms)) return;\n firedSet?.add(ms);\n this.emit(`${intent}_hover_dwell_${ms}`, {\n intent,\n threshold_ms: ms,\n actual_ms: Date.now() - timestamp,\n });\n }, ms) as unknown as number;\n timers.push(timerId);\n }\n this.hoverThresholdTimers.set(intent, timers);\n }\n }\n\n /** Record the end of a hover on the named intent. Updates the sticky\n * `lastHoverMs` so `getHoverMs` keeps returning the last-known\n * duration until the next hover_start. */\n noteHoverEnd(intent: string, timestamp: number = Date.now()): void {\n if (!this.hoverEnabled) return;\n const startAt = this.hoverStartAt.get(intent);\n if (startAt === undefined) return;\n const dwell = Math.max(0, timestamp - startAt);\n this.hoverLastMs.set(intent, dwell);\n this.hoverStartAt.delete(intent);\n // Clear pending threshold timers for this intent.\n const pending = this.hoverThresholdTimers.get(intent);\n if (pending) {\n for (const id of pending) clearTimeout(id);\n this.hoverThresholdTimers.delete(intent);\n }\n this.hoverThresholdsFired.delete(intent);\n }\n\n /**\n * Enable mouse-velocity-to-top tracking (P2 Task 3). After\n * registration, feed positions via `noteMousePosition(x, y, ts?)`.\n * IntentSnapshotCollector (P2 Task 5) wires window 'mousemove' to\n * this method, with throttling to ~100ms per the plan.\n */\n registerMouseVelocityToTop(config?: Partial<MouseVelocityConfig>): void {\n this.mouseVelEnabled = true;\n if (config) {\n this.mouseVelConfig = {\n threshold: config.threshold ?? this.mouseVelConfig.threshold,\n windowMs: config.windowMs ?? this.mouseVelConfig.windowMs,\n cooldownMs: config.cooldownMs ?? this.mouseVelConfig.cooldownMs,\n };\n }\n }\n\n /** Feed a mouse-position sample. Recomputes the rolling-window\n * velocity-to-top and fires `mouse_velocity_to_top` when the value\n * crosses the configured threshold (subject to cooldown). */\n noteMousePosition(x: number, y: number, timestamp: number = Date.now()): number {\n if (!this.mouseVelEnabled) return 0;\n const cutoff = timestamp - this.mouseVelConfig.windowMs;\n // Trim expired samples from the head; keep the buffer small.\n let trimStart = 0;\n while (trimStart < this.mouseSamples.length && this.mouseSamples[trimStart].ts < cutoff) {\n trimStart++;\n }\n if (trimStart > 0) this.mouseSamples = this.mouseSamples.slice(trimStart);\n this.mouseSamples.push({ x, y, ts: timestamp });\n\n // Velocity-to-top = -(dy / dt) over the oldest-to-newest pair in\n // the window. Positive value = moving up. With 2+ samples this is\n // the instantaneous slope across the window; with 1 sample we\n // return 0 (cannot compute velocity from a single point).\n if (this.mouseSamples.length < 2) {\n this.mouseVelLast = 0;\n return 0;\n }\n const oldest = this.mouseSamples[0];\n const newest = this.mouseSamples[this.mouseSamples.length - 1];\n const dt = newest.ts - oldest.ts;\n if (dt <= 0) {\n this.mouseVelLast = 0;\n return 0;\n }\n const velocity = -(newest.y - oldest.y) / dt; // px/ms; up = +\n this.mouseVelLast = velocity;\n\n // Threshold + cooldown firing. Initial state allows the first fire\n // — only subsequent fires within cooldownMs of the prior are gated.\n const cooldownOk =\n this.mouseVelLastFiredAt === 0 ||\n timestamp - this.mouseVelLastFiredAt >= this.mouseVelConfig.cooldownMs;\n if (velocity >= this.mouseVelConfig.threshold && cooldownOk) {\n this.mouseVelLastFiredAt = timestamp;\n this.emit('mouse_velocity_to_top', {\n velocity,\n threshold: this.mouseVelConfig.threshold,\n window_ms: this.mouseVelConfig.windowMs,\n samples: this.mouseSamples.length,\n });\n }\n return velocity;\n }\n\n /** Snapshot the current velocity-to-top in px/ms (positive = up).\n * Returns 0 when not enabled or insufficient samples. */\n getMouseVelocityToTop(): number {\n return this.mouseVelEnabled ? this.mouseVelLast : 0;\n }\n\n /**\n * Snapshot the hover dwell for an intent.\n * - Currently hovering: returns `Date.now() - hover_start_at`.\n * - Hovered before, not currently: returns the most recent hover's\n * duration (sticky — the rule evaluator queries this AFTER\n * mouseleave to see \"did Priya hover the price ≥ 1.5s?\").\n * - Never hovered: returns 0.\n */\n getHoverMs(intent: string): number {\n if (!this.hoverEnabled) return 0;\n const startAt = this.hoverStartAt.get(intent);\n if (startAt !== undefined) {\n return Math.max(0, Date.now() - startAt);\n }\n return this.hoverLastMs.get(intent) ?? 0;\n }\n\n start(): void {\n if (this.isStarted) {\n return;\n }\n \n this.pageLoadTime = Date.now();\n this.lastActivityTime = Date.now();\n this.lastScrollY = window.scrollY || 0;\n this.lastScrollTime = Date.now();\n \n if (this.scrollDepthTargets.size > 0) {\n this.attachScrollListener();\n }\n \n if (this.exitIntentEnabled) {\n this.attachExitIntentListener();\n }\n \n if (this.scrollVelocityEnabled) {\n this.attachScrollVelocityListener();\n }\n \n if (this.visibilityChangeEnabled) {\n this.attachVisibilityChangeListener();\n }\n \n if (this.backButtonEnabled) {\n this.attachBackButtonListener();\n }\n \n this.attachActivityListeners();\n \n this.startInactivityCheck();\n \n this.isStarted = true;\n }\n \n stop(): void {\n if (!this.isStarted) {\n return;\n }\n \n this.removeScrollListener();\n this.removeExitIntentListener();\n this.removeScrollVelocityListener();\n this.removeVisibilityChangeListener();\n this.removeBackButtonListener();\n this.removeActivityListeners();\n \n this.timeOnPageTargets.forEach(timerId => clearTimeout(timerId));\n this.timeOnPageTargets.clear();\n \n this.inactivityTargets.forEach(timerId => clearTimeout(timerId));\n this.inactivityTargets.clear();\n \n if (this.inactivityCheckInterval) {\n clearInterval(this.inactivityCheckInterval);\n this.inactivityCheckInterval = undefined;\n }\n \n if (this.scrollVelocityCooldownTimer) {\n clearTimeout(this.scrollVelocityCooldownTimer);\n this.scrollVelocityCooldownTimer = undefined;\n }\n \n this.isStarted = false;\n }\n \n reset(): void {\n this.scrollDepthReached.clear();\n this.exitIntentFired = false;\n this.scrollVelocityFired = false;\n this.backButtonFired = false;\n this.pageLoadTime = Date.now();\n this.lastActivityTime = Date.now();\n this.lastScrollY = window.scrollY || 0;\n this.lastScrollTime = Date.now();\n \n if (this.scrollVelocityCooldownTimer) {\n clearTimeout(this.scrollVelocityCooldownTimer);\n this.scrollVelocityCooldownTimer = undefined;\n }\n }\n \n private attachScrollListener(): void {\n window.addEventListener('scroll', this.handleScroll, { passive: true });\n this.handleScroll();\n }\n \n private removeScrollListener(): void {\n window.removeEventListener('scroll', this.handleScroll);\n }\n \n private handleScroll = (): void => {\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;\n const scrollPercent = scrollHeight > 0 ? (scrollTop / scrollHeight) * 100 : 0;\n \n for (const targetDepth of this.scrollDepthTargets) {\n if (scrollPercent >= targetDepth && !this.scrollDepthReached.has(targetDepth)) {\n this.scrollDepthReached.add(targetDepth);\n \n this.emit(`scroll_depth_${targetDepth}`, {\n depth_percent: targetDepth,\n actual_percent: scrollPercent,\n scroll_top: scrollTop,\n scroll_height: scrollHeight\n });\n }\n }\n };\n \n private attachExitIntentListener(): void {\n document.addEventListener('mouseleave', this.handleExitIntent);\n }\n \n private removeExitIntentListener(): void {\n document.removeEventListener('mouseleave', this.handleExitIntent);\n }\n \n private handleExitIntent = (event: MouseEvent): void => {\n if (this.exitIntentFired) {\n return;\n }\n \n if (event.clientY < 10) {\n this.exitIntentFired = true;\n \n this.emit('exit_intent', {\n client_y: event.clientY,\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n }\n };\n \n private attachScrollVelocityListener(): void {\n window.addEventListener('scroll', this.handleScrollVelocity, { passive: true });\n }\n \n private removeScrollVelocityListener(): void {\n window.removeEventListener('scroll', this.handleScrollVelocity);\n }\n \n private handleScrollVelocity = (): void => {\n if (this.scrollVelocityFired) {\n return;\n }\n \n const currentY = window.scrollY || document.documentElement.scrollTop;\n const currentTime = Date.now();\n const timeDiff = currentTime - this.lastScrollTime;\n \n if (timeDiff > 100) {\n const distance = this.lastScrollY - currentY;\n const velocity = Math.abs(distance / timeDiff);\n \n if (\n distance > 0 &&\n velocity > this.scrollVelocityConfig.threshold &&\n currentY > this.scrollVelocityConfig.minScrollPosition\n ) {\n this.scrollVelocityFired = true;\n \n this.emit('mobile_exit_intent', {\n scroll_velocity: velocity,\n scroll_distance: distance,\n current_position: currentY,\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n \n this.scrollVelocityCooldownTimer = window.setTimeout(() => {\n this.scrollVelocityFired = false;\n }, this.scrollVelocityConfig.cooldown);\n }\n \n this.lastScrollY = currentY;\n this.lastScrollTime = currentTime;\n }\n };\n \n private attachVisibilityChangeListener(): void {\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n }\n \n private removeVisibilityChangeListener(): void {\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n }\n \n private handleVisibilityChange = (): void => {\n if (document.hidden) {\n this.emit('visibility_hidden', {\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n } else {\n this.emit('visibility_visible', {\n page_url: window.location.href\n });\n }\n };\n \n private attachBackButtonListener(): void {\n if (typeof window !== 'undefined' && window.history) {\n window.history.pushState(null, '', window.location.href);\n window.addEventListener('popstate', this.handleBackButton);\n }\n }\n \n private removeBackButtonListener(): void {\n window.removeEventListener('popstate', this.handleBackButton);\n }\n \n private handleBackButton = (): void => {\n if (this.backButtonFired) {\n return;\n }\n \n this.backButtonFired = true;\n \n window.history.pushState(null, '', window.location.href);\n \n this.emit('back_button', {\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n };\n \n private attachActivityListeners(): void {\n const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n events.forEach(event => {\n document.addEventListener(event, this.handleActivity, { passive: true });\n });\n }\n \n private removeActivityListeners(): void {\n const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n events.forEach(event => {\n document.removeEventListener(event, this.handleActivity);\n });\n }\n \n private handleActivity = (): void => {\n this.lastActivityTime = Date.now();\n };\n \n private startInactivityCheck(): void {\n this.inactivityCheckInterval = window.setInterval(() => {\n const idleTime = (Date.now() - this.lastActivityTime) / 1000;\n \n for (const [idleSeconds] of this.inactivityTargets) {\n if (idleTime >= idleSeconds) {\n this.emit(`inactivity_${idleSeconds}`, {\n idle_seconds: idleSeconds,\n actual_idle_time: idleTime\n });\n \n const timerId = this.inactivityTargets.get(idleSeconds);\n if (timerId) {\n clearTimeout(timerId);\n this.inactivityTargets.delete(idleSeconds);\n }\n }\n }\n }, 1000);\n }\n \n private emit(eventType: string, data: Record<string, any>): void {\n const event: TriggerEvent = {\n type: eventType,\n data,\n timestamp: Date.now()\n };\n \n const callbacks = this.listeners.get(eventType);\n if (callbacks) {\n callbacks.forEach(callback => {\n try {\n callback(event);\n } catch (error) {\n console.error(`Error in trigger callback for ${eventType}:`, error);\n }\n });\n }\n \n const wildcardCallbacks = this.listeners.get('*');\n if (wildcardCallbacks) {\n wildcardCallbacks.forEach(callback => {\n try {\n callback(event);\n } catch (error) {\n console.error(`Error in wildcard trigger callback:`, error);\n }\n });\n }\n }\n}\n","/**\n * SDK Bootstrap\n *\n * Strict origin-assertion handshake against the control-plane. Called once\n * at SDK init — resolves writeKey → propertyId, asserts origin, and returns\n * the VAPID public key + first-party cookie id the SDK needs for the rest\n * of the session.\n *\n * A failed bootstrap (401/403) aborts SDK init — the SDK refuses to emit\n * events or request push permission against an origin it cannot prove it\n * owns.\n *\n * See docs/architecture/SUBSCRIPTION_PROPERTY_ARCHITECTURE.md §5.\n */\n\nconst COOKIE_NAME = 'aegis_fpc';\nconst COOKIE_MAX_AGE_DAYS = 365 * 2;\n\nexport interface BootstrapRequest {\n writeKey: string;\n currentOrigin?: string;\n firstPartyCookieId?: string;\n attestationToken?: string;\n userAgent?: string;\n}\n\nimport type { EventGovernanceHint } from '../governance';\n\nexport interface BootstrapResult {\n propertyId: string;\n organizationId: string;\n workspaceId: string;\n propertyType: 'shopify_store' | 'hosted_commerce' | 'custom_website' | 'mobile_app';\n vapidPublicKey: string | null;\n allowedOrigins: string[];\n pushEnabled: boolean;\n inAppEnabled: boolean;\n transportMode: string;\n firstPartyCookieId: string;\n /**\n * Event-governance hint for SDK-side unique-event-name cap enforcement.\n * Null = Enterprise plan / CP outage / org has no cap. Safe to pass\n * directly to `aegis.ingestGovernanceHint()`.\n */\n eventGovernance: EventGovernanceHint | null;\n /**\n * Plugin-handshake P1 — workspace_code allowlist for SDK path-segment\n * detection. Empty array = no workspace codes registered for this org\n * (single-outlet tenant). SDK uses this set to decide whether\n * `location.pathname.split('/')[1]` is a valid workspace slug vs a\n * generic route (e.g. `/products`, `/collections`). Pass to\n * `aegis.ingestWorkspaceCodes()` after bootstrap completes.\n */\n workspaceCodes: string[];\n}\n\nexport class BootstrapError extends Error {\n constructor(public readonly status: number, message: string) {\n super(message);\n this.name = 'BootstrapError';\n }\n}\n\n/**\n * Read the first-party cookie id from document.cookie. Returns null if\n * unset — caller should pass undefined so the server issues a fresh one.\n */\nexport function readFirstPartyCookie(): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${COOKIE_NAME}=([^;]+)`));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Persist the first-party cookie id returned by the server. Stored as a\n * first-party cookie (readable by JS — it's not a secret, it's a device\n * id) so that the same browser hitting a second origin on the same\n * registrable domain gets the same cookie back.\n */\nexport function writeFirstPartyCookie(cookieId: string): void {\n if (typeof document === 'undefined') return;\n const expires = new Date();\n expires.setDate(expires.getDate() + COOKIE_MAX_AGE_DAYS);\n // Derive the registrable-domain boundary where possible so the cookie is\n // shared across subdomains (e.g., shop.sweettruths.com + www.sweettruths.com).\n // We conservatively use the current host without a leading dot —\n // browsers scope the cookie to that host. For cross-registrable-domain\n // siblings (Shopify myshopify.com + actii.me), each domain gets its own\n // cookie, which is the expected per-origin behavior.\n document.cookie =\n `${COOKIE_NAME}=${encodeURIComponent(cookieId)};` +\n `expires=${expires.toUTCString()};` +\n `path=/;SameSite=Lax`;\n // Also mirror into localStorage so the SDK can read it synchronously at\n // init time even in environments where document.cookie is delayed.\n try {\n window.localStorage.setItem(COOKIE_NAME, cookieId);\n } catch {\n // private mode / storage disabled — cookie alone is sufficient\n }\n}\n\n/**\n * Perform the bootstrap handshake.\n *\n * @throws BootstrapError on 4xx — the SDK should stop init and surface the\n * error to the console. 5xx are retried once.\n */\nexport async function bootstrap(\n apiHost: string,\n req: BootstrapRequest\n): Promise<BootstrapResult> {\n const body: Record<string, unknown> = { writeKey: req.writeKey };\n if (req.currentOrigin) body.currentOrigin = req.currentOrigin;\n if (req.firstPartyCookieId) body.firstPartyCookieId = req.firstPartyCookieId;\n if (req.attestationToken) body.attestationToken = req.attestationToken;\n if (req.userAgent) body.userAgent = req.userAgent;\n\n const url = `${apiHost.replace(/\\/$/, '')}/v1/sdk/bootstrap`;\n\n const doFetch = async (): Promise<Response> =>\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n credentials: 'omit',\n });\n\n let resp = await doFetch();\n if (resp.status >= 500) {\n await new Promise((r) => setTimeout(r, 750));\n resp = await doFetch();\n }\n\n if (resp.status === 401) {\n throw new BootstrapError(401, 'writeKey not recognized or revoked');\n }\n if (resp.status === 403) {\n throw new BootstrapError(403, 'Origin validation failed — this writeKey is bound to a different property');\n }\n if (!resp.ok) {\n throw new BootstrapError(resp.status, `Bootstrap failed: HTTP ${resp.status}`);\n }\n\n const json = (await resp.json()) as BootstrapResult;\n writeFirstPartyCookie(json.firstPartyCookieId);\n return json;\n}\n\n/**\n * Derive a deterministic device fingerprint from the first-party cookie id\n * and a few stable UA parts. This is the value sent on every push-subscribe\n * call — its stability lets the backend UPSERT on (property_id, fingerprint)\n * across re-subscribes and VAPID rotations.\n */\nexport async function deriveDeviceFingerprint(firstPartyCookieId: string): Promise<string> {\n const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n const platform = typeof navigator !== 'undefined' ? (navigator.platform || '') : '';\n const language = typeof navigator !== 'undefined' ? (navigator.language || '') : '';\n const input = `${firstPartyCookieId}|${ua}|${platform}|${language}`;\n\n // Prefer subtle.crypto — available in all push-capable browsers.\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const bytes = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest('SHA-256', bytes);\n const hex = Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return hex;\n }\n\n // Last-resort fallback — basic 32-bit FNV hash (non-cryptographic but\n // stable). Push-capable browsers always have subtle.crypto so this path\n // is practically unreachable; included only for completeness.\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return `fnv-${h.toString(16)}`;\n}\n","import { Aegis } from './core/analytics';\nimport { AegisMessageRuntime } from './runtime';\nimport { AegisPlacementManager } from './placements/AegisPlacementManager';\nimport { TriggerEngine } from './triggers';\nimport { bootstrap } from './core/bootstrap';\n\ndeclare global {\n interface Window {\n aegis: any;\n Aegis: typeof Aegis;\n AegisMessageRuntime: typeof AegisMessageRuntime;\n AegisPlacementManager: typeof AegisPlacementManager;\n TriggerEngine: typeof TriggerEngine;\n /**\n * `/v1/sdk/bootstrap` handshake. Exposed for integrations that embed the\n * CDN bundle and need to retrieve `eventGovernance`, `vapidPublicKey`, etc.\n * Added in SDK 1.4.0 alongside SDK-side event-governance.\n */\n aegisBootstrap: typeof bootstrap;\n }\n}\n\nconst globalName = 'aegis';\nconst existingQueue: any = window[globalName] || [];\n\nconst instance = new Aegis();\n\nif (Array.isArray(existingQueue)) {\n const loadOptions = (existingQueue as any)._loadOptions;\n\n existingQueue.forEach((item: any) => {\n if (!Array.isArray(item)) {\n return;\n }\n\n const [method, ...args] = item;\n\n if (typeof method !== 'string' || method.length === 0) {\n return;\n }\n\n // Resolve dotted paths (e.g. 'user.login') by walking the object\n // graph. Snippet callers can use `aegis.user.login(id)` before the\n // bundle finishes loading; the queue captures ['user.login', id]\n // and we replay it as instance.user.login(id).\n const parts = method.split('.');\n let owner: any = null;\n let fn: any = instance;\n for (let i = 0; i < parts.length; i += 1) {\n if (fn == null) break;\n owner = fn;\n fn = fn[parts[i]];\n }\n\n if (typeof fn === 'function') {\n try {\n fn.apply(owner, args);\n } catch (error) {\n console.error(`[Aegis SDK] Error executing queued method \"${method}\":`, error);\n }\n } else {\n console.warn(`[Aegis SDK] Unknown method \"${method}\"`);\n }\n });\n\n if (loadOptions?.key) {\n instance.init(loadOptions.key, loadOptions.options).catch((error) => {\n console.error('[Aegis SDK] Initialization failed:', error);\n });\n }\n}\n\nwindow[globalName] = instance;\nwindow.Aegis = Aegis;\nwindow.AegisMessageRuntime = AegisMessageRuntime;\nwindow.AegisPlacementManager = AegisPlacementManager;\nwindow.TriggerEngine = TriggerEngine;\nwindow.aegisBootstrap = bootstrap;\n\nexport default instance;\n","/**\n * AegisMessageRuntime — unified facade for in-app messaging.\n *\n * The sole public entry point for all 20 in-app types (announce +\n * collect + assist buckets) plus the legacy gamification surface\n * (chat_bubble / toast / exit_intent_popup configured via\n * /v1/widgets/config). Under the hood it composes an\n * `AegisInAppManager` + `AegisWidgetManager`; callers never touch them\n * directly.\n *\n * Public API:\n * new AegisMessageRuntime({ writeKey, apiHost, ... })\n * await runtime.initialize()\n * await runtime.updateContactId(id)\n * runtime.onClientEvent(eventName, data)\n * runtime.destroy()\n *\n * Internal wiring: spin_wheel + scratch_card campaigns land in the\n * inbox prefetch bundle but need DOM renderers that live in\n * WidgetManager. The facade passes an `onInteractiveCampaign` callback\n * into the InAppManager constructor; the callback forwards to\n * `widgets.renderInteractiveCampaign(campaign)` — no CustomEvent bus,\n * no listeners, no registration-order bugs.\n */\n\nimport { AegisInAppManager, type AegisInAppConfig, type InAppCampaign } from '../inapp';\nimport { AegisWidgetManager, type AegisWidgetConfig } from '../widgets';\nimport { TriggerEngine } from '../triggers/TriggerEngine';\n\nexport interface AegisMessageRuntimeConfig extends AegisInAppConfig {\n /** If set, a `TriggerEngine` from the SDK is wired to the WidgetManager\n * for its exit-intent / scroll-velocity / inactivity handlers. Leave\n * undefined to have the runtime construct one internally. */\n triggerEngine?: AegisWidgetConfig['triggerEngine'];\n /** WidgetManager prefetch toggle. Defaults to true — matches the\n * existing behaviour where spin_wheel / scratch_card prefetch their\n * configs from `/v1/widgets/config/prefetch`. */\n enableWidgetPrefetch?: boolean;\n /** Source platform for gamification attribution (shopify / woocommerce\n * / magento / mobile_sdk / web). Passed through to WidgetManager. */\n sourcePlatform?: AegisWidgetConfig['sourcePlatform'];\n}\n\n/**\n * One runtime, all in-app types. Consumers should prefer this over\n * importing AegisInAppManager / AegisWidgetManager directly. Those two\n * classes remain exported for a deprecation window — see `./deprecated.ts`.\n */\nexport class AegisMessageRuntime {\n readonly inApp: AegisInAppManager;\n readonly widgets: AegisWidgetManager;\n private initialized = false;\n\n constructor(config: AegisMessageRuntimeConfig) {\n const triggerEngine = config.triggerEngine ?? new TriggerEngine();\n const ownsTriggerEngine = !config.triggerEngine;\n // Ownership of the engine is tracked downstream by WidgetManager\n // (which is the only consumer that needs to call lifecycle methods\n // on it). The facade itself only forwards.\n\n this.widgets = new AegisWidgetManager({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n userId: config.userId,\n contactId: config.contactId,\n organizationId: config.organizationId,\n debugMode: config.debugMode,\n triggerEngine,\n ownsTriggerEngine,\n enablePrefetch: config.enableWidgetPrefetch !== false,\n sourcePlatform: config.sourcePlatform,\n getWorkspaceId: config.getWorkspaceId,\n });\n\n this.inApp = new AegisInAppManager({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n userId: config.userId,\n contactId: config.contactId,\n organizationId: config.organizationId,\n propertyId: config.propertyId,\n debugMode: config.debugMode,\n enableSSE: config.enableSSE,\n // Route gamification sub_types straight to the widget renderer —\n // replaces the old CustomEvent('aegis:render-widget') bridge which\n // had no listener and silently dropped impressions.\n onInteractiveCampaign: (campaign) => {\n this.widgets.renderInteractiveCampaign(campaign);\n },\n getWorkspaceId: config.getWorkspaceId,\n });\n }\n\n /**\n * Boots both managers in parallel. Safe to call multiple times — the\n * second + subsequent calls are no-ops.\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n this.initialized = true;\n await Promise.all([\n this.inApp.initialize(),\n this.widgets.initialize(),\n ]);\n }\n\n /**\n * Both managers carry contactId. Updates both so the server-side\n * targeting pipeline sees the identity for campaign eligibility AND\n * the widget prefetch includes per-contact segment configs. The\n * widgets call is async (may re-fetch prefetch configs) but we don't\n * wait — callers that care about the fetched state await on the\n * returned promise.\n */\n async updateContactId(contactId: string): Promise<void> {\n this.inApp.updateContactId?.(contactId);\n await this.widgets.updateContactId(contactId);\n }\n\n /**\n * Forward a client-side event (product_viewed, cart_idle_90s, etc.)\n * to the in-app manager's client-trigger evaluator. The WidgetManager\n * has its own TriggerEngine wiring (exit-intent, scroll-velocity) so\n * we don't forward there — events it cares about arrive through that\n * channel already.\n */\n onClientEvent(eventName: string, eventData: Record<string, unknown> = {}): void {\n this.inApp.onClientEvent?.(eventName, eventData);\n }\n\n /**\n * Conversion-aware suppression — call when the host app observes a\n * goal event (purchase / order_completed / checkout_completed / custom).\n *\n * Forwards to AegisInAppManager.notifyConversion() which:\n * - Persists the conversion in sessionStorage so subsequent page loads\n * in the same tab keep armed campaigns silenced.\n * - For every armed campaign whose\n * `frequency.suppress_after_conversion_seconds` is set, computes an\n * expiry epoch_ms and silences it until then.\n *\n * Storefronts integrate via the React provider's useCommerceEvents\n * helper which calls this after `ecom.orderCompleted(...)`. Direct SDK\n * users call it themselves.\n *\n * Added by Micro-Intent Engine P0a Task 6 (2026-04-30).\n */\n notifyConversion(goalName: string): void {\n this.inApp.notifyConversion?.(goalName);\n }\n\n /**\n * Tear down both managers. Used by React component unmounts + during\n * identity switches where we want a full reset. If the facade created\n * its own TriggerEngine, WidgetManager.destroy() will stop it;\n * caller-supplied engines are left alone.\n */\n destroy(): void {\n this.inApp.destroy?.();\n this.widgets.destroy();\n this.initialized = false;\n }\n\n /**\n * Current armed campaigns visible to the InAppManager. Accessible for\n * the Prefetch Inspector and for debugging. WidgetManager's prefetched\n * spin_wheel / scratch_card configs are NOT in this list — they live\n * in `this.widgets` and have their own lifecycle.\n */\n getCampaigns(): InAppCampaign[] {\n // AegisInAppManager keeps `campaigns` as a private field. Expose it\n // via bracket access; the field is stable as of SDK 1.5.0. Once\n // Phase 7c unifies render paths this becomes a first-class getter.\n return ((this.inApp as unknown as { campaigns?: InAppCampaign[] }).campaigns) ?? [];\n }\n}\n","/**\n * AegisPlacementManager - Web SDK Placement Module\n * \n * Real-time SSE architecture for inline content placements.\n * \n * Security:\n * - XSS Protection: Uses DOMPurify (when available) with allowlisted tags/attrs; safe text-only fallback\n * - URL Validation: Prevents javascript: protocol injection\n * - CSP Compatible: No inline styles in HTML strings\n * \n * Architecture:\n * - Connects to SSE endpoint for real-time placement updates\n * - Initial fetch on load, then SSE for updates\n * - Caches placement content locally in memory\n * - Renders content into pre-defined UI slots (inline, not overlay)\n * - Tracks events (impression, click, conversion) asynchronously\n * \n * Key Differences from In-App Messages:\n * - INLINE content (part of layout), not overlays\n * - Content rendered immediately on slot registration\n * - No trigger rules (shown when slot loads)\n * - Supports banner, card, carousel, video, HTML content types\n */\n\nexport interface PlacementContent {\n placement_id: string;\n variant_id: string;\n content_type: 'banner' | 'card' | 'carousel' | 'video' | 'html' | 'dynamic_injection';\n content: Record<string, any>;\n css_selector?: string;\n injection_mode?: 'replace' | 'append' | 'prepend';\n}\n\nexport interface PlacementSlot {\n placementId: string;\n containerId: string;\n fallbackContent?: string;\n onRender?: (content: PlacementContent) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface AegisPlacementConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n organizationId?: string;\n debugMode?: boolean;\n enableSSE?: boolean;\n /**\n * Workspace cascade reader. Host SDK sets this to\n * `() => aegis.getEffectiveWorkspaceId()` so placement\n * impression/click/conversion events arrive at the gateway with the\n * same workspace context the main analytics SDK uses. See\n * PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n */\n getWorkspaceId?: () => string | undefined;\n}\n\nexport class AegisPlacementManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private organizationId?: string;\n private debugMode: boolean;\n private enableSSE: boolean;\n private getWorkspaceId?: () => string | undefined;\n\n private placements: Map<string, PlacementContent> = new Map();\n private slots: Map<string, PlacementSlot> = new Map();\n private renderedSlots = new Set<string>();\n private eventSource?: EventSource;\n private isInitialized = false;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n\n constructor(config: AegisPlacementConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n this.userId = config.userId;\n this.contactId = config.contactId;\n this.organizationId = config.organizationId;\n this.debugMode = config.debugMode || false;\n this.enableSSE = config.enableSSE !== false;\n this.getWorkspaceId = config.getWorkspaceId;\n }\n \n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisPlacements already initialized');\n return;\n }\n \n await this.refreshPlacements();\n \n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n \n this.isInitialized = true;\n this.log('AegisPlacements initialized successfully');\n }\n \n register(placementId: string, options: Omit<PlacementSlot, 'placementId'>): void {\n const slot: PlacementSlot = {\n placementId,\n ...options\n };\n \n this.slots.set(placementId, slot);\n this.log(`Registered placement slot: ${placementId}`);\n \n const existingContent = this.placements.get(placementId);\n if (existingContent) {\n this.renderSlot(slot, existingContent);\n } else if (options.fallbackContent) {\n this.renderFallback(slot);\n }\n }\n \n unregister(placementId: string): void {\n this.slots.delete(placementId);\n this.renderedSlots.delete(placementId);\n this.log(`Unregistered placement slot: ${placementId}`);\n }\n \n async refreshPlacements(): Promise<void> {\n try {\n const placementIds = Array.from(this.slots.keys());\n \n if (placementIds.length === 0) {\n this.log('No registered slots, skipping refresh');\n return;\n }\n \n const url = `${this.apiHost}/v1/placements/content?placement_ids=${placementIds.join(',')}`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Type': this.getDeviceType(),\n 'X-Platform': 'web'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, { headers });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch placements: ${response.status}`);\n }\n \n const data = await response.json();\n \n this.placements.clear();\n \n for (const placement of data.placements || []) {\n this.placements.set(placement.placement_id, placement);\n }\n \n this.renderAllSlots();\n \n this.log(`Refreshed ${this.placements.size} placements`);\n \n } catch (error) {\n this.log(`Error refreshing placements: ${error}`, true);\n }\n }\n \n private renderAllSlots(): void {\n for (const [placementId, slot] of this.slots.entries()) {\n const content = this.placements.get(placementId);\n \n if (content) {\n this.renderSlot(slot, content);\n } else if (slot.fallbackContent) {\n this.renderFallback(slot);\n }\n }\n }\n \n private renderSlot(slot: PlacementSlot, content: PlacementContent): void {\n try {\n if (content.content_type === 'dynamic_injection') {\n this.renderDynamicInjection(content);\n } else {\n const container = document.getElementById(slot.containerId);\n \n if (!container) {\n this.log(`Container not found: ${slot.containerId}`, true);\n return;\n }\n \n container.innerHTML = '';\n \n switch (content.content_type) {\n case 'banner':\n this.renderBanner(container, content);\n break;\n case 'card':\n this.renderCard(container, content);\n break;\n case 'carousel':\n this.renderCarousel(container, content);\n break;\n case 'video':\n this.renderVideo(container, content);\n break;\n case 'html':\n this.renderHTML(container, content);\n break;\n default:\n this.log(`Unknown content type: ${content.content_type}`, true);\n return;\n }\n }\n \n if (!this.renderedSlots.has(slot.placementId)) {\n this.trackEvent(content.placement_id, content.variant_id, 'impression');\n this.renderedSlots.add(slot.placementId);\n }\n \n if (slot.onRender) {\n slot.onRender(content);\n }\n \n this.log(`Rendered placement: ${content.placement_id} (${content.content_type})`);\n \n } catch (error) {\n this.log(`Error rendering placement: ${error}`, true);\n \n if (slot.onError) {\n slot.onError(error as Error);\n }\n }\n }\n \n private renderBanner(container: HTMLElement, content: PlacementContent): void {\n const { title, body, image_url, cta_text, cta_url, background_color, text_color } = content.content;\n \n const banner = document.createElement('div');\n banner.className = 'aegis-placement-banner';\n banner.style.cssText = `\n background: ${this.sanitizeColor(background_color || '#1a73e8')};\n color: ${this.sanitizeColor(text_color || '#ffffff')};\n padding: 24px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n gap: 16px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 80px; height: 80px; border-radius: 8px; object-fit: cover;';\n banner.appendChild(img);\n }\n }\n \n const textContainer = document.createElement('div');\n textContainer.style.cssText = 'flex: 1;';\n \n if (title) {\n const titleEl = document.createElement('div');\n titleEl.textContent = title;\n titleEl.style.cssText = 'font-size: 18px; font-weight: 600; margin-bottom: 8px;';\n textContainer.appendChild(titleEl);\n }\n \n if (body) {\n const bodyEl = document.createElement('div');\n bodyEl.textContent = body;\n bodyEl.style.cssText = 'font-size: 14px; opacity: 0.9;';\n textContainer.appendChild(bodyEl);\n }\n \n banner.appendChild(textContainer);\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: white;\n color: ${this.sanitizeColor(background_color || '#1a73e8')};\n border: none;\n padding: 12px 24px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n white-space: nowrap;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n banner.appendChild(ctaButton);\n }\n \n container.appendChild(banner);\n }\n \n private renderCard(container: HTMLElement, content: PlacementContent): void {\n const { title, body, image_url, cta_text, cta_url } = content.content;\n \n const card = document.createElement('div');\n card.className = 'aegis-placement-card';\n card.style.cssText = `\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = title || '';\n img.style.cssText = 'width: 100%; height: 200px; object-fit: cover;';\n card.appendChild(img);\n }\n }\n \n const cardBody = document.createElement('div');\n cardBody.style.cssText = 'padding: 20px;';\n \n if (title) {\n const titleEl = document.createElement('h3');\n titleEl.textContent = title;\n titleEl.style.cssText = 'margin: 0 0 12px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n cardBody.appendChild(titleEl);\n }\n \n if (body) {\n const bodyEl = document.createElement('p');\n bodyEl.textContent = body;\n bodyEl.style.cssText = 'margin: 0 0 16px 0; font-size: 14px; color: #666; line-height: 1.5;';\n cardBody.appendChild(bodyEl);\n }\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n cardBody.appendChild(ctaButton);\n }\n \n card.appendChild(cardBody);\n container.appendChild(card);\n }\n \n private renderCarousel(container: HTMLElement, content: PlacementContent): void {\n const { items } = content.content;\n \n if (!Array.isArray(items) || items.length === 0) {\n this.log('Carousel items empty or invalid', true);\n return;\n }\n \n const carousel = document.createElement('div');\n carousel.className = 'aegis-placement-carousel';\n carousel.style.cssText = `\n display: flex;\n gap: 16px;\n overflow-x: auto;\n padding: 8px 0;\n scroll-behavior: smooth;\n -webkit-overflow-scrolling: touch;\n `;\n \n for (const item of items) {\n const carouselItem = document.createElement('div');\n carouselItem.style.cssText = `\n flex: 0 0 250px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n overflow: hidden;\n cursor: pointer;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (item.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(item.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = item.title || '';\n img.style.cssText = 'width: 100%; height: 150px; object-fit: cover;';\n carouselItem.appendChild(img);\n }\n }\n \n const itemContent = document.createElement('div');\n itemContent.style.cssText = 'padding: 12px;';\n \n if (item.title) {\n const title = document.createElement('div');\n title.textContent = item.title;\n title.style.cssText = 'font-size: 14px; font-weight: 600; margin-bottom: 4px; color: #1a1a1a;';\n itemContent.appendChild(title);\n }\n \n if (item.price) {\n const price = document.createElement('div');\n price.textContent = item.price;\n price.style.cssText = 'font-size: 16px; font-weight: 700; color: #1a73e8;';\n itemContent.appendChild(price);\n }\n \n carouselItem.appendChild(itemContent);\n \n if (item.url) {\n carouselItem.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click', { item_index: items.indexOf(item) });\n const safeUrl = this.sanitizeUrl(item.url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n }\n \n carousel.appendChild(carouselItem);\n }\n \n container.appendChild(carousel);\n }\n \n private renderVideo(container: HTMLElement, content: PlacementContent): void {\n const { video_url, poster_url, autoplay, muted } = content.content;\n \n if (!video_url) {\n this.log('Video URL missing', true);\n return;\n }\n \n const safeVideoUrl = this.sanitizeUrl(video_url);\n if (!safeVideoUrl) {\n this.log('Invalid video URL', true);\n return;\n }\n \n const video = document.createElement('video');\n video.src = safeVideoUrl;\n video.controls = true;\n video.autoplay = autoplay || false;\n video.muted = muted || false;\n video.style.cssText = 'width: 100%; border-radius: 8px;';\n \n if (poster_url) {\n const safePosterUrl = this.sanitizeUrl(poster_url);\n if (safePosterUrl) {\n video.poster = safePosterUrl;\n }\n }\n \n video.addEventListener('play', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click', { action: 'video_play' });\n });\n \n container.appendChild(video);\n }\n \n private renderHTML(container: HTMLElement, content: PlacementContent): void {\n const { html } = content.content;\n \n if (!html) {\n this.log('HTML content missing', true);\n return;\n }\n \n const wrapper = document.createElement('div');\n wrapper.className = 'aegis-placement-html';\n wrapper.innerHTML = this.sanitizeHTML(html);\n \n const links = wrapper.querySelectorAll('a');\n links.forEach((link) => {\n link.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n });\n });\n \n container.appendChild(wrapper);\n }\n \n private renderDynamicInjection(content: PlacementContent): void {\n const { html } = content.content;\n const cssSelector = content.css_selector;\n const injectionMode = content.injection_mode || 'replace';\n \n if (!cssSelector) {\n this.log('CSS selector missing for dynamic injection', true);\n return;\n }\n \n if (!html) {\n this.log('HTML content missing for dynamic injection', true);\n return;\n }\n \n let targetElement: HTMLElement | null = null;\n \n try {\n targetElement = document.querySelector(cssSelector);\n } catch (error) {\n this.log(`Invalid CSS selector: ${cssSelector}`, true);\n return;\n }\n \n if (!targetElement) {\n this.log(`Target element not found for selector: ${cssSelector}`, true);\n return;\n }\n \n const wrapper = document.createElement('div');\n wrapper.className = 'aegis-dynamic-injection';\n wrapper.setAttribute('data-placement-id', content.placement_id);\n wrapper.setAttribute('data-variant-id', content.variant_id);\n wrapper.innerHTML = this.sanitizeHTML(html);\n \n const links = wrapper.querySelectorAll('a');\n links.forEach((link) => {\n link.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n });\n });\n \n switch (injectionMode) {\n case 'replace':\n targetElement.innerHTML = '';\n targetElement.appendChild(wrapper);\n this.log(`Replaced content in ${cssSelector}`, false);\n break;\n \n case 'append':\n targetElement.appendChild(wrapper);\n this.log(`Appended content to ${cssSelector}`, false);\n break;\n \n case 'prepend':\n targetElement.insertBefore(wrapper, targetElement.firstChild);\n this.log(`Prepended content to ${cssSelector}`, false);\n break;\n \n default:\n this.log(`Unknown injection mode: ${injectionMode}`, true);\n return;\n }\n \n this.log(`Dynamically injected content for ${content.placement_id} into ${cssSelector} (${injectionMode})`);\n }\n \n private renderFallback(slot: PlacementSlot): void {\n if (!slot.fallbackContent) return;\n \n const container = document.getElementById(slot.containerId);\n if (!container) return;\n \n container.innerHTML = slot.fallbackContent;\n this.log(`Rendered fallback content for: ${slot.placementId}`);\n }\n \n private async trackEvent(\n placementId: string,\n variantId: string,\n eventType: 'impression' | 'click' | 'conversion',\n metadata: Record<string, any> = {}\n ): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/placements/track`;\n \n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Type': this.getDeviceType(),\n 'X-Platform': 'web'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n // Plumb the same workspace cascade the main analytics SDK uses\n // (config → ?ws= → URL path slug → setWorkspace runtime → session\n // storage). Without this, placement events arrive at event-ingress\n // unscoped and silently attribute to the org's primary workspace.\n const workspaceId = this.getWorkspaceId?.();\n\n const body = {\n placement_id: placementId,\n variant_id: variantId,\n event_type: eventType,\n workspace_id: workspaceId,\n metadata: {\n device_type: this.getDeviceType(),\n platform: 'web',\n ...metadata\n }\n };\n \n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n }).catch(err => this.log(`Track event failed: ${err}`, true));\n \n this.log(`Tracked ${eventType}: ${placementId}`);\n \n } catch (error) {\n this.log(`Error tracking event: ${error}`, true);\n }\n }\n \n private connectSSE(): void {\n if (this.eventSource) {\n this.disconnectSSE();\n }\n \n if (!this.organizationId) {\n this.log('Cannot connect SSE without organization ID', true);\n return;\n }\n \n const url = new URL('/v1/stream/realtime', this.apiHost);\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n \n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n \n const queryParams = new URLSearchParams();\n Object.entries(headers).forEach(([key, value]) => {\n queryParams.append(key, value);\n });\n \n this.eventSource = new EventSource(`${url}?${queryParams.toString()}`);\n \n this.eventSource.addEventListener('open', () => {\n this.log('SSE connection established');\n this.reconnectAttempts = 0;\n });\n \n this.eventSource.addEventListener('placement_content_updated', (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data);\n this.log(`Received placement content update: ${data.placement_id}`);\n this.refreshPlacements();\n } catch (error) {\n this.log(`Error parsing SSE event: ${error}`, true);\n }\n });\n \n this.eventSource.addEventListener('heartbeat', () => {\n this.log('SSE heartbeat received');\n });\n\n this.eventSource.addEventListener('error', () => {\n this.log('SSE connection error', true);\n \n if (this.eventSource?.readyState === EventSource.CLOSED) {\n this.attemptReconnect();\n }\n });\n }\n \n private disconnectSSE(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = undefined;\n this.log('SSE connection closed');\n }\n }\n \n private attemptReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log('Max reconnect attempts reached, giving up', true);\n return;\n }\n \n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n \n this.log(`Reconnecting SSE in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n setTimeout(() => {\n if (this.isInitialized && this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n }, delay);\n }\n \n private getDeviceType(): string {\n const width = window.innerWidth;\n if (width < 768) return 'mobile_web';\n if (width < 1024) return 'tablet_web';\n return 'desktop_web';\n }\n \n private sanitizeUrl(url: string): string | null {\n try {\n const parsed = new URL(url, window.location.href);\n \n if (parsed.protocol === 'javascript:' || parsed.protocol === 'data:') {\n this.log(`Blocked unsafe URL protocol: ${parsed.protocol}`, true);\n return null;\n }\n \n return parsed.href;\n } catch {\n this.log(`Invalid URL: ${url}`, true);\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n const hexPattern = /^#[0-9A-Fa-f]{3,6}$/;\n const rgbPattern = /^rgba?\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*(,\\s*[\\d.]+\\s*)?\\)$/;\n const namedColors = ['white', 'black', 'red', 'blue', 'green', 'yellow', 'transparent'];\n \n if (hexPattern.test(color) || rgbPattern.test(color) || namedColors.includes(color.toLowerCase())) {\n return color;\n }\n \n this.log(`Invalid color: ${color}`, true);\n return '#000000';\n }\n \n private sanitizeHTML(html: string): string {\n // Use DOMPurify if available (recommended for production)\n const purify = (window as unknown as Record<string, unknown>).DOMPurify as\n | { sanitize: (dirty: string, cfg?: Record<string, unknown>) => string }\n | undefined;\n\n if (purify?.sanitize) {\n return purify.sanitize(html, {\n ALLOWED_TAGS: [\n 'a', 'b', 'br', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'i', 'img', 'li', 'ol', 'p', 'span', 'strong', 'u', 'ul', 'table',\n 'thead', 'tbody', 'tr', 'th', 'td', 'blockquote', 'hr', 'figure',\n 'figcaption', 'picture', 'source', 'video', 'section', 'article',\n ],\n ALLOWED_ATTR: [\n 'href', 'target', 'rel', 'src', 'alt', 'width', 'height', 'class',\n 'id', 'style', 'title', 'loading', 'srcset', 'sizes', 'type',\n 'media', 'controls', 'poster',\n ],\n ALLOW_DATA_ATTR: false,\n });\n }\n\n // Built-in fallback: strip all tags via DOM parsing, then re-allow safe subset\n this.log(\n 'DOMPurify not found — falling back to built-in sanitizer. ' +\n 'For full HTML placement support, add <script src=\"https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.4/purify.min.js\"></script>',\n false\n );\n const tempDiv = document.createElement('div');\n tempDiv.textContent = html;\n return tempDiv.innerHTML;\n }\n \n private log(message: string, isError = false): void {\n if (this.debugMode || isError) {\n console[isError ? 'error' : 'log'](`[AegisPlacements] ${message}`);\n }\n }\n \n destroy(): void {\n this.disconnectSSE();\n this.slots.clear();\n this.placements.clear();\n this.renderedSlots.clear();\n this.isInitialized = false;\n this.log('AegisPlacements destroyed');\n }\n}\n"],"names":["DEFAULT_CONFIG","workspace_id","batch_size","batch_interval","capture_utm","capture_referrer","auto_page_view","session_timeout","debug","respect_dnt","cross_domain_tracking","secure_cookie","enable_offline_mode","max_offline_events","retry_failed_requests","max_retries","retry_backoff_multiplier","request_timeout","rate_limit_burst","rate_limit_per_second","auto_region_detection","wait_for_consent","enable_consent_mode","integrate_onetrust","integrate_cookiebot","integrate_google_consent_mode","default_consent","analytics","marketing","functional","initialized","api_host","cell_endpoints","preferred_region","cookie_domain","plugins","active_cell","generateUUID","crypto","randomUUID","replace","c","r","Math","random","toString","generateMessageId","Date","now","slice","logger","constructor","this","enabled","prefix","enable","disable","isEnabled","message","args","info","console","warn","error","Identity","storage","userId","traits","anonymousId","getOrCreateAnonymousId","loadUserIdentity","id","get","set","storedUserId","storedTraits","JSON","parse","getAnonymousId","setUserId","setTraits","getUserId","stringify","getTraits","reset","remove","alias","newUserId","previousId","getState","SessionManager","sessionTimeoutMinutes","eventCount","checkInterval","activityThrottle","lastActivityUpdate","adClickIDs","sessionTimeout","lastActivityTime","sessionStartTime","session","loadSession","sessionId","startTime","landingPage","createNewSession","startActivityTracking","startExpiryCheck","sessionData","newSessionId","persistSession","activityHandler","onActivity","forEach","event","window","addEventListener","passive","document","visibilityState","setInterval","checkSessionExpiry","getSessionId","incrementEventCount","getSessionDuration","getEventCount","setAdClickIDs","Object","keys","length","getAdClickIDs","getLandingPage","destroy","clearInterval","EventQueue","config","transport","buffer","flushInterval","isFlushing","offlineQueue","isOnline","navigator","onLine","enableOfflineMode","loadOfflineQueue","setupOnlineListener","startFlushTimer","setupUnloadHandlers","offlineData","events","Array","isArray","maxOfflineEvents","flushOfflineQueue","saveOfflineQueue","data","flush","send","success","push","type","batchSize","addToOfflineQueue","shift","result","unshift","batchInterval","flushBeforeUnload","persistBufferToOfflineQueue","getQueueSize","getOfflineQueueSize","clear","Transport","activeCell","healthCheckInterval","regionLatencyMap","Map","cellEndpoints","initializeCellularArchitecture","cachedCell","loadCachedCell","isCellHealthy","selectOptimalCell","startHealthChecks","cached","saveCachedCell","cell","healthy","some","region","url","preferredRegion","preferredCell","find","autoRegionDetection","detectOptimalRegion","selectCellByPriority","healthyCells","filter","latencyPromises","map","async","latency","measureLatency","results","Promise","all","sort","a","b","optimalCell","performance","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","method","signal","clearTimeout","ok","Infinity","sortedCells","priority","performHealthChecks","healthPromises","isHealthy","checkCellHealth","endpoint","getActiveEndpoint","payload","buildPayload","sendBeacon","sendViaBeacon","resolve","sendViaFetch","apiHost","Error","batch","sentAt","toISOString","writeKey","context","blob","Blob","attempt","requestTimeout","headers","Authorization","body","keepalive","json","errorText","text","status","statusText","shouldRetry","retryRequest","statusCode","retryFailedRequests","maxRetries","delay","pow","retryBackoffMultiplier","getActiveCell","getRegionLatencies","STORAGE_PREFIX","Storage","options","useLocalStorage","isLocalStorageAvailable","test","localStorage","setItem","removeItem","key","value","expiryDays","fullKey","item","expiry","setCookie","itemStr","getItem","getCookie","deleteCookie","startsWith","cookie","split","trim","name","days","date","setTime","getTime","cookieStr","toUTCString","secureCookie","location","protocol","cookieDomain","crossDomain","domain","getRootDomain","nameEQ","ca","i","charAt","substring","indexOf","hostname","parts","join","buildContext","utm","searchParams","URL","URLSearchParams","search","parseUTMParameters","device","ua","userAgent","detectDevice","os","version","match","detectOS","browser","detectBrowser","network","connection","mozConnection","webkitConnection","effectiveType","downlink","rtt","detectNetworkInfo","library","page","path","pathname","referrer","title","href","hash","locale","language","timezone","Intl","DateTimeFormat","resolvedOptions","timeZone","screen","width","height","density","devicePixelRatio","viewport","innerWidth","innerHeight","campaign","PluginRegistry","register","plugin","has","unregister","pluginName","delete","getAll","from","values","executeHook","hookName","hook","apply","String","onError","onErrorError","executeHookChain","initialValue","additionalArgs","executeHookParallel","promises","promise","ConsentManager","listeners","defaultConsent","waitForConsent","consentStorageKey","preferences","loadPreferences","stored","parsed","mergeWithDefaults","getDefaultPreferences","necessary","savePreferences","setConsent","updated","notifyListeners","grantAll","denyAll","hasConsent","category","getPreferences","getStatus","isAnalyticsEnabled","isMarketingEnabled","onChange","callback","index","splice","listener","integrateOneTrust","OneTrust","syncWithOneTrust","activeGroups","OnetrustActiveGroups","includes","OptanonWrapper","originalWrapper","integrateCookiebot","cookiebot","Cookiebot","syncWithCookiebot","consent","statistics","integrateGoogleConsentMode","gtag","updateGoogleConsent","ad_storage","analytics_storage","ad_user_data","ad_personalization","functionality_storage","personalization_storage","security_storage","FBP_COOKIE","FBC_COOKIE","readCookie","target","decodeURIComponent","writeCookie","host","isHttps","encodeURIComponent","subdomainIndex","max","ensureFbp","existing","fbp","floor","padStart","ensureFbc","fbclid","readFbclidFromUrl","fbc","sumItems","products","reduce","sum","p","quantity","mapProducts","product_id","sku","price","currency","brand","variant_id","variant_label","position","EcommerceTracker","aegis","productViewed","product","track","image_url","productListViewed","list","list_id","list_name","productClicked","source","section","productImpressed","categoryFiltered","previous_category","result_count","searchPerformed","query","results_count","filters","addToCart","removeFromCart","cartViewed","cart","cart_id","num_items","checkoutStarted","checkout","checkout_id","coupon","shipping","tax","checkoutStep","step","orderCompleted","order","order_id","revenue","discount","payment_method","orderRefunded","orderId","couponApplied","couponRemoved","wishlistItemAdded","wishlist","wishlist_id","wishlist_name","productWaitlisted","waitlist","channels","promotionViewed","promo","promotionClicked","RateLimiter","droppedThisWindow","firstDroppedName","windowFlushTimer","capacity","burstCapacity","refillPerSecond","windowMs","dropCoalesceWindowMs","onDropBatch","tokens","lastRefillMs","tryConsume","eventName","refill","flushWindow","getAvailableTokens","elapsedSec","newTokens","min","err","C1","C2","murmurhash3_x86_32","input","seed","bytes","len","nBlocks","h1","offset","k1","imul","tailStart","tailLen","murmurhash3_bytes","TextEncoder","encode","BloomFilter","buf","params","m","mask","fromBase64","bloomB64","b64","atob","bin","out","Uint8Array","charCodeAt","B","globalThis","Buffer","base64ToBytes","seedA","h2","seedB","k","idx","NameGovernor","bloom","remainingNewNames","graceActive","localNovelNames","Set","droppedSinceLastReport","reportWindowStart","hasWarnedThisSession","ingestHint","hint","bloom_algo","bloom_b64","seed_a","seed_b","remaining_new_names","grace_active","shouldSend","add","prev","drainDropReport","size","total","count","since","_debugState","hasBloom","remaining","localNovel","RESERVED_PREFIXES","CAMEL_BOUNDARY_1","CAMEL_BOUNDARY_2","NON_SNAKE","DUPE_UNDERSCORE","ISO_8601","DATE_KEY_HINTS","normalizeKey","toLowerCase","startsWithReserved","lower","looksLikeDateKey","part","parseDateValue","Number","isFinite","s","ms","isNaN","asNum","_TraitGovernor","warnCounts","process","sanitized","drops","rawKey","originalKey","verdict","reason","normalizedKey","drop","maybeWarn","ws","perVerdict","WARN_CAP","TraitGovernor","VALID_CHANNELS","EMAIL_RE","PHONE_RE","SHA256_HEX_RE","ISO_DATE_RE","UserNamespace","pendingTraits","login","merged","identify","logout","setAttribute","writeTraits","setAttributes","setEmail","email","setPhone","phone","setHashedEmail","sha256Hex","email_sha256","setHashedPhone","phone_sha256","setBirthDate","iso","birth_date","setOptIn","channel","granted","setSecureToken","token","_secure_token","_getPendingTraits","assign","Aegis","queue","initPromise","_ecommerce","_user","rateLimiter","nameGovernor","traitGovernor","_lastPageUrl","_popstateHandler","_originalPushState","_originalReplaceState","_lastEventIds","_workspaceCodes","_runtimeWorkspace","init","_init","_a","shouldRespectDNT","pattern","isBot","buildConfig","identity","captureAdClickIDsOnInit","write_key","sampleName","emitRateLimitMeta","initializePlugins","startSPATracking","onRouteChange","currentUrl","history","pushState","bind","replaceState","stopSPATracking","removeEventListener","dnt","doNotTrack","msDoNotTrack","parseAdClickIDs","hasAdClickIDs","use","catch","removePlugin","ingestWorkspaceCodes","codes","setWorkspace","codeOrId","sessionStorage","clearWorkspace","getPathWorkspaceCode","segments","Boolean","first","getEffectiveWorkspaceId","configured","pathWs","properties","assertInitialized","messageId","timestamp","captureEvent","wsForGovernor","governedTraits","group","groupId","trackEvent","eventLabel","ctx","props","campaign_id","campaign_source","medium","campaign_medium","content","campaign_content","term","campaign_term","clicks","clickId","gclid","ctwa_clid","msclkid","ttclid","campaign_click_id","meta","transformedEvent","sampleEventName","dropped_count","sample_event_name","burst_capacity","refill_per_second","_b","ingestGovernanceHint","emitGovernanceDropMeta","report","distinct_names","sample_names","entries","window_start","window_end","lastEventId","setCell","cellEndpoint","existingCell","getCellInfo","latencies","grantConsent","denyConsent","getConsentPreferences","onConsentChange","ecommerce","user","renderCarouselCards","sanitizeUrl","sanitizeColor","log","addAnimationStyles","ic","interactive_config","cards","autoplay","autoplay_ms","loop","bg","background_color","fg","text_color","overlay","createElement","className","style","cssText","card","header","headerText","textContent","appendChild","closeBtn","msOverflowStyle","e","abs","deltaX","deltaY","scrollLeft","tile","cta_url","img","safe","src","alt","loading","t","cta_text","cta","goto","stopPropagation","dots","dotEls","dot","setActive","d","opacity","activeIdx","tiles","querySelectorAll","autoplayTimer","next","el","scrollIntoView","behavior","inline","block","approx","round","DISMISS_STORAGE_PREFIX","renderProgressBar","goalType","progress_goal_type","threshold","progress_threshold","rewardText","progress_reward_text","progress_source","fill","bar","label","leading","numeric","shell","fillEl","footnote","lastFiredUnlock","update","cur","__aegisProgressSSE","win","Shopify","v","parseFloat","total_price","aegis_cart","cart_items","cart_total","cacheStr","subtotalAmount","items","readCurrentValue","pct","toFixed","pollTimer","cleanup","once","RESUME_PREFIX","writeResumeIdx","resumeKey","renderCoachmarkTour","resume_key","steps","allowSkip","allow_skip","showDots","show_progress_dots","current","raw","n","parseInt","readResumeIdx","pointerEl","tipEl","highlightEl","cleanupOne","finish","opts","skipped","showStep","selector","anchor_web","anchor","querySelector","rect","getBoundingClientRect","top","left","placement","titleEl","bodyEl","controls","_","buttons","skip","isLast","tipRect","bottom","right","aegisResetTour","clearResume","_AegisInAppManager","campaigns","displayedCampaigns","suppressedUntil","isInitialized","reconnectAttempts","maxReconnectAttempts","hooks","ready","readyResolve","filledSlots","WeakSet","readAnonIdFromStorage","contactId","organizationId","propertyId","debugMode","enableSSE","onInteractiveCampaign","getWorkspaceId","emit","handlers","proceed","handler","emitError","on","onCampaignsLoaded","onCampaignWillShow","onCampaignShown","onCampaignClick","onCampaignDismiss","initialize","refreshCampaigns","connectSSE","updateUserId","updateContactId","disconnectSSE","notifyConversion","goalName","ts","CONVERSION_STORAGE_PREFIX","applySuppressionFromCampaigns","convertedAt","seconds","frequency","suppress_after_conversion_seconds","scope","expiresAt","rehydrateSuppressionFromStorage","earliestTs","isSuppressed","campaignId","eventSource","queryParams","append","EventSource","readyState","CLOSED","attemptReconnect","close","device_type","detectDeviceType","page_url","isNewUser","abAssignments","getABAssignments","btoa","credentials","processABAssignments","tryDisplayNextCampaign","renderIntoSlots","renderTokenAnchors","stage","assigned_variant_id","getVariantId","client_trigger","displayCampaign","slots","eligibleByCategory","modes","delivery_modes","widget_category","slot","getAttribute","renderCampaignIntoSlot","anchors","submitUrl","submit_url","rendered","sub_type","renderStarRatingSlot","renderNPSSurveySlot","_wrapInSlotCard","_buildStarRatingBody","_buildNPSSurveyBody","_submitTokenResponse","variant","isOverlay","padding","titleSize","titleWeight","titleMargin","starSize","starsMarginBottom","hoverScale","stars","maxStars","rating_scale","star","transform","nps_question","scale","btn","labels","notLikely","veryLikely","grid","onClientEvent","eventData","matchesClientTrigger","trigger","cfg","wantedRaw","wanted","actual","productId","interactiveSubTypes","renderInteractive","renderModal","renderBanner","renderFullScreen","renderHalfInterstitial","renderAlert","renderPIP","renderTooltip","buildRenderContext","sticky_position","dismissible","sticky_dismissible","autoHide","sticky_auto_hide_ms","sticky_bg_color","positionCss","strong","createTextNode","action_url","button_text","autoHideTimer","persist","renderStickyBar","layout","rec_layout","ctaDefault","rec_cta_text","accent","sheet","handle","isRow","metadata","go","renderProductRecommendation","evt","color","msg","level","renderNPSSurvey","renderCountdownOffer","renderStarRating","renderQuickPoll","renderQuiz","createOverlay","modal","addCloseButton","countdown_label","digits","digitStyle","val","targetStr","countdown_target","diff","h","spans","requestAnimationFrame","desc","createCTAButton","poll_options","optionsList","opt","optBtn","questions","currentQ","renderQuestion","childNodes","removeChild","lastChild","thank_you_message","q","progress","questionText","question","optionsDiv","safeUrl","open","container","removeModal","navigateToCampaignAction","buttonId","typedDefaultPrevented","typedEvent","button_id","preventDefault","defaultPrevented","typedProceed","domEvent","CustomEvent","detail","campaign_type","cancelable","domProceed","dispatchEvent","banner","contentContainer","textContainer","actionsContainer","ctaButton","removeBanner","closeButton","actions","alert","buttonContainer","cancelButton","pip","video_url","video","muted","animation","parentNode","cursor","anchorSelector","tooltip_anchor_selector","preferredPosition","tooltip_position","textColor","tooltip","arrow","dismiss","anchorRect","tooltipRect","scrollX","scrollY","arrowTop","arrowLeft","arrowBottom","arrowRight","outsideClickHandler","contains","getElementById","head","parsedUrl","origin","eventType","externalEventId","workspaceId","call","event_type","user_id","contact_id","anonymous_id","platform","property_id","idempotency_key","styles","clearABState","AegisInAppManager","AegisWidgetManager","widgets","renderedWidgets","isDestroyed","prefetchWidgetConfigs","triggerEngine","ownsTriggerEngine","enablePrefetch","cssCustomization","onEvent","sourcePlatform","updateCSSCustomization","customization","fetchPrefetchConfigs","preloadWidgetAssets","renderInteractiveCampaign","subType","widget","widget_id","widget_type","renderSpinWheel","renderScratchCard","stop","animationStyle","node","emitEvent","fetchWidgets","renderImmediateWidgets","setupTriggerListeners","setupExitIntentWithPrefetch","setCartData","cartData","cart_currency","detectPlatformCart","shopifyCart","normalizeShopifyCart","wooCart","normalizeWooCart","magentoCart","normalizeMagentoCart","line_items","product_name","product_title","cartJsonEl","mageCacheStr","mageCache","quote_id","subtotal","currencyCode","item_id","qty","product_price_value","exitIntentConfig","exit_intent","imageUrl","Image","spinWheelConfig","spin_wheel","elapsed","getDeviceType","w","trigger_rules","renderWidget","registerExitIntent","depthPercent","depth_percent","registerScrollDepth","registerTimeOnPage","isMobile","isMobileDevice","mobileConfig","mobile_triggers","handleSpinWheelIntent","widgetId","detectedCart","renderSpinWheelWidget","sendCartAbandonmentBeacon","scroll_velocity","registerScrollVelocity","scroll_threshold","minScrollPosition","scroll_min_position","cooldown","scroll_cooldown","idle_timer","idleSeconds","idle_seconds","registerInactivity","visibility_change","registerVisibilityChange","back_button","registerBackButton","handleExitIntent","renderCartRecoveryPopup","renderLeadGenPopup","beaconData","organization_id","user_email","abandoned_at","beaconUrl","renderChatBubble","renderToast","renderFeedbackForm","renderExitIntentPopup","icon_url","link_url","bubble","positionStyles","bottom_right","bottom_left","top_right","top_left","icon","textEl","description","SVG_NS","wheel","createElementNS","anglePer","PI","radius","start","end","x1","cos","y1","sin","x2","y2","largeArc","labelAngle","lx","ly","spinButton","disabled","prize","generatePrize","config_id","showPrizeResult","spinWheel","accentColor","accent_color","backgroundColor","buttonColor","button_color","wheelColors","innerHTML","onclick","interpolateCartVariables","subtitle","wheelContainer","form","phoneInput","placeholder","required","emailInput","nameInput","submitButton","errorMessage","validatePhone","validateEmail","display","submitSpinWheel","showSpinWheelPrize","prize_label","has_email","geoRegion","detectGeoRegion","deviceType","utmParams","getUTMParams","cart_token","_c","_d","_e","cart_url","geo_region","session_id","utm_source","utm_medium","utm_campaign","resultContainer","emoji","prizeLabel","coupon_code","couponContainer","couponLabel","couponCode","parentElement","canvas","getContext","fillStyle","fillRect","font","textAlign","fillText","isScratching","scratch","x","y","globalCompositeOperation","beginPath","arc","clientX","clientY","scratchButton","duration","toast","iconEl","messageEl","textareaLabel","textarea","rows","submitFeedback","score","comment","descEl","configId","event_data","titleText","messageText","show_cart_items","cartItems","cartTitle","itemDiv","discount_code","discountBox","discount_percentage","show_timer","timer_minutes","timer","checkoutUrl","tier","template","isError","TriggerEngine","isStarted","scrollDepthTargets","scrollDepthReached","timeOnPageTargets","exitIntentEnabled","exitIntentFired","inactivityTargets","scrollVelocityEnabled","scrollVelocityFired","scrollVelocityConfig","lastScrollY","lastScrollTime","visibilityChangeEnabled","backButtonEnabled","backButtonFired","rageClickEnabled","rageClickConfig","rageClickBuffer","rageClickFiredInBurst","hoverEnabled","hoverStartAt","hoverLastMs","hoverThresholds","hoverThresholdsFired","hoverThresholdTimers","mouseVelEnabled","mouseVelConfig","cooldownMs","mouseSamples","mouseVelLast","mouseVelLastFiredAt","handleScroll","scrollTop","pageYOffset","documentElement","scrollHeight","scrollPercent","targetDepth","actual_percent","scroll_top","scroll_height","client_y","time_on_page","pageLoadTime","handleScrollVelocity","currentY","currentTime","timeDiff","distance","velocity","scroll_distance","current_position","scrollVelocityCooldownTimer","handleVisibilityChange","hidden","handleBackButton","handleActivity","off","callbacks","timerId","idleTime","actual_idle_time","registerRageClick","noteClick","intent","cutoff","trimStart","recent","window_ms","getRageClickCount","live","registerHoverDwell","thresholdsByIntent","thresholds","noteHoverStart","noteHoverEnd","timers","firedSet","threshold_ms","actual_ms","startAt","dwell","pending","registerMouseVelocityToTop","noteMousePosition","oldest","newest","dt","cooldownOk","samples","getMouseVelocityToTop","getHoverMs","attachScrollListener","attachExitIntentListener","attachScrollVelocityListener","attachVisibilityChangeListener","attachBackButtonListener","attachActivityListeners","startInactivityCheck","removeScrollListener","removeExitIntentListener","removeScrollVelocityListener","removeVisibilityChangeListener","removeBackButtonListener","removeActivityListeners","inactivityCheckInterval","wildcardCallbacks","COOKIE_NAME","BootstrapError","super","globalName","existingQueue","instance","loadOptions","_loadOptions","owner","fn","AegisMessageRuntime","enableWidgetPrefetch","inApp","getCampaigns","AegisPlacementManager","placements","renderedSlots","refreshPlacements","placementId","existingContent","renderSlot","fallbackContent","renderFallback","placementIds","placement_id","renderAllSlots","content_type","renderDynamicInjection","containerId","renderCard","renderCarousel","renderVideo","renderHTML","onRender","cardBody","carousel","carouselItem","itemContent","item_index","poster_url","safeVideoUrl","safePosterUrl","poster","action","html","wrapper","sanitizeHTML","link","cssSelector","css_selector","injectionMode","injection_mode","targetElement","insertBefore","firstChild","variantId","purify","DOMPurify","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","ALLOW_DATA_ATTR","tempDiv","aegisBootstrap","req","currentOrigin","firstPartyCookieId","attestationToken","doFetch","resp","cookieId","expires","setDate","getDate","writeFirstPartyCookie"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"aegis.min.js","sources":["../src/types/config.ts","../src/utils/uuid.ts","../src/utils/logger.ts","../src/utils/identity.ts","../src/core/session.ts","../src/core/queue.ts","../src/core/transport.ts","../src/utils/storage.ts","../src/utils/device.ts","../src/utils/url-parser.ts","../src/plugins/registry.ts","../src/utils/consent.ts","../src/utils/meta-cookies.ts","../src/ecommerce/index.ts","../src/core/rate-limiter.ts","../src/governance/murmur3.ts","../src/governance/bloom-filter.ts","../src/governance/name-governor.ts","../src/governance/trait-governor.ts","../src/core/user-namespace.ts","../src/core/analytics.ts","../src/inapp/renderers/carousel-cards.ts","../src/inapp/renderers/sticky-bar.ts","../src/inapp/renderers/progress-bar.ts","../src/inapp/renderers/coachmark-tour.ts","../src/inapp/AegisInAppManager.ts","../src/inapp/renderers/product-recommendation.ts","../src/widgets/AegisWidgetManager.ts","../src/triggers/TriggerEngine.ts","../src/core/bootstrap.ts","../src/cdn.ts","../src/runtime/AegisMessageRuntime.ts","../src/placements/AegisPlacementManager.ts"],"sourcesContent":["export type CellRegion = 'us-east' | 'us-west' | 'eu-central' | 'ap-south' | 'ap-southeast';\n\nexport interface CellEndpoint {\n region: CellRegion;\n url: string;\n priority: number;\n healthy: boolean;\n}\n\nexport interface AegisConfig {\n write_key: string;\n\n workspace_id?: string;\n \n api_host?: string;\n \n cell_endpoints?: CellEndpoint[];\n \n preferred_region?: CellRegion;\n \n auto_region_detection?: boolean;\n \n batch_size?: number;\n \n batch_interval?: number;\n \n capture_utm?: boolean;\n \n capture_referrer?: boolean;\n \n auto_page_view?: boolean;\n \n session_timeout?: number;\n \n debug?: boolean;\n \n respect_dnt?: boolean;\n \n cross_domain_tracking?: boolean;\n \n cookie_domain?: string;\n \n secure_cookie?: boolean;\n \n enable_offline_mode?: boolean;\n \n max_offline_events?: number;\n \n retry_failed_requests?: boolean;\n \n max_retries?: number;\n \n retry_backoff_multiplier?: number;\n \n request_timeout?: number;\n\n /** Client-side rate limiter — burst (token bucket capacity). Default 100. */\n rate_limit_burst?: number;\n\n /** Client-side rate limiter — sustained tokens per second. Default 20. */\n rate_limit_per_second?: number;\n\n plugins?: string[];\n\n wait_for_consent?: boolean;\n\n default_consent?: {\n analytics?: boolean;\n marketing?: boolean;\n functional?: boolean;\n };\n\n enable_consent_mode?: boolean;\n\n integrate_onetrust?: boolean;\n\n integrate_cookiebot?: boolean;\n\n integrate_google_consent_mode?: boolean;\n\n /** Push notification configuration. When provided, AegisWebPush is initialized. */\n push?: {\n /** VAPID public key for Web Push subscription. */\n vapidPublicKey: string;\n /** Automatically prompt for push permission after SDK init. Default: false. */\n autoPrompt?: boolean;\n /** Delay in ms before showing auto-prompt. Default: 5000. */\n promptDelay?: number;\n /** Path to the service worker file. Default: '/aegis-sw.js'. */\n serviceWorkerPath?: string;\n /** Scope for the service worker registration. Default: '/'. Critical for e-commerce platforms. */\n serviceWorkerScope?: string;\n };\n}\n\nexport interface AegisConfigInternal extends Required<Omit<AegisConfig, 'api_host' | 'cell_endpoints' | 'preferred_region' | 'cookie_domain' | 'plugins' | 'default_consent' | 'workspace_id'>> {\n initialized: boolean;\n workspace_id: string | null;\n api_host: string | null;\n cell_endpoints: CellEndpoint[];\n preferred_region: CellRegion | null;\n cookie_domain: string | null;\n plugins: string[];\n active_cell: CellEndpoint | null;\n default_consent: {\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n };\n}\n\nexport const DEFAULT_CONFIG: Partial<AegisConfigInternal> = {\n workspace_id: null,\n batch_size: 10,\n batch_interval: 1000,\n capture_utm: true,\n capture_referrer: true,\n auto_page_view: false,\n session_timeout: 30,\n debug: false,\n respect_dnt: true,\n cross_domain_tracking: false,\n secure_cookie: true,\n enable_offline_mode: true,\n max_offline_events: 100,\n retry_failed_requests: true,\n max_retries: 3,\n retry_backoff_multiplier: 2,\n request_timeout: 5000,\n rate_limit_burst: 100,\n rate_limit_per_second: 20,\n auto_region_detection: true,\n wait_for_consent: false,\n enable_consent_mode: false,\n integrate_onetrust: false,\n integrate_cookiebot: false,\n integrate_google_consent_mode: false,\n default_consent: {\n analytics: false,\n marketing: false,\n functional: true,\n },\n initialized: false,\n api_host: null,\n cell_endpoints: [],\n preferred_region: null,\n cookie_domain: null,\n plugins: [],\n active_cell: null,\n};\n","export function generateUUID(): string {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\nexport function generateSessionId(): string {\n return `sess_${Date.now()}_${generateUUID().slice(0, 8)}`;\n}\n\nexport function generateMessageId(): string {\n return `msg_${Date.now()}_${generateUUID().slice(0, 8)}`;\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\nclass Logger {\n private enabled: boolean = false;\n private prefix: string = '[Active Reach SDK]';\n\n enable(): void {\n this.enabled = true;\n }\n\n disable(): void {\n this.enabled = false;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n debug(message: string, ...args: any[]): void {\n if (!this.enabled) return;\n console.debug(`${this.prefix} ${message}`, ...args);\n }\n\n info(message: string, ...args: any[]): void {\n if (!this.enabled) return;\n console.info(`${this.prefix} ${message}`, ...args);\n }\n\n warn(message: string, ...args: any[]): void {\n console.warn(`${this.prefix} ${message}`, ...args);\n }\n\n error(message: string, ...args: any[]): void {\n console.error(`${this.prefix} ${message}`, ...args);\n }\n}\n\nexport const logger = new Logger();\n","import { generateUUID } from './uuid';\nimport { Storage } from './storage';\nimport { logger } from './logger';\n\nexport interface IdentityState {\n anonymousId: string;\n userId: string | null;\n traits: Record<string, any>;\n}\n\nexport class Identity {\n private storage: Storage;\n private anonymousId: string;\n private userId: string | null = null;\n private traits: Record<string, any> = {};\n\n constructor(storage: Storage) {\n this.storage = storage;\n this.anonymousId = this.getOrCreateAnonymousId();\n this.loadUserIdentity();\n }\n\n private getOrCreateAnonymousId(): string {\n let id = this.storage.get('anon_id');\n\n if (!id) {\n id = generateUUID();\n this.storage.set('anon_id', id, 365);\n logger.debug('Created new anonymous ID:', id);\n } else {\n logger.debug('Loaded existing anonymous ID:', id);\n }\n\n return id;\n }\n\n private loadUserIdentity(): void {\n const storedUserId = this.storage.get('user_id');\n const storedTraits = this.storage.get('user_traits');\n\n if (storedUserId) {\n this.userId = storedUserId;\n logger.debug('Loaded user ID:', storedUserId);\n }\n\n if (storedTraits) {\n try {\n this.traits = JSON.parse(storedTraits);\n logger.debug('Loaded user traits:', this.traits);\n } catch (error) {\n logger.warn('Failed to parse stored traits:', error);\n this.traits = {};\n }\n }\n }\n\n getAnonymousId(): string {\n return this.anonymousId;\n }\n\n setUserId(userId: string, traits?: Record<string, any>): void {\n this.userId = userId;\n this.storage.set('user_id', userId, 365);\n logger.info('User identified:', userId);\n\n if (traits) {\n this.setTraits(traits);\n }\n }\n\n getUserId(): string | null {\n return this.userId;\n }\n\n setTraits(traits: Record<string, any>): void {\n this.traits = { ...this.traits, ...traits };\n this.storage.set('user_traits', JSON.stringify(this.traits), 365);\n logger.debug('User traits updated:', this.traits);\n }\n\n getTraits(): Record<string, any> {\n return { ...this.traits };\n }\n\n reset(): void {\n this.userId = null;\n this.traits = {};\n this.anonymousId = generateUUID();\n\n this.storage.remove('user_id');\n this.storage.remove('user_traits');\n this.storage.set('anon_id', this.anonymousId, 365);\n\n logger.info('Identity reset, new anonymous ID:', this.anonymousId);\n }\n\n alias(newUserId: string): { previousId: string; newUserId: string } {\n const previousId = this.userId || this.anonymousId;\n\n this.setUserId(newUserId);\n\n logger.info('User aliased:', { previousId, newUserId });\n\n return { previousId, newUserId };\n }\n\n getState(): IdentityState {\n return {\n anonymousId: this.anonymousId,\n userId: this.userId,\n traits: this.getTraits(),\n };\n }\n}\n","import { Storage } from '../utils/storage';\nimport { generateSessionId } from '../utils/uuid';\nimport { logger } from '../utils/logger';\nimport { AdClickIDs } from '../utils/url-parser';\n\nexport interface SessionState {\n sessionId: string;\n startTime: number;\n lastActivityTime: number;\n eventCount: number;\n adClickIDs?: AdClickIDs;\n landingPage?: string;\n}\n\nexport class SessionManager {\n private storage: Storage;\n private sessionId: string;\n private sessionTimeout: number;\n private lastActivityTime: number;\n private sessionStartTime: number;\n private eventCount: number = 0;\n private checkInterval: number | null = null;\n private activityThrottle: number = 1000;\n private lastActivityUpdate: number = 0;\n private adClickIDs: AdClickIDs = {};\n private landingPage?: string;\n\n constructor(storage: Storage, sessionTimeoutMinutes: number = 30) {\n this.storage = storage;\n this.sessionTimeout = sessionTimeoutMinutes * 60 * 1000;\n this.lastActivityTime = Date.now();\n this.sessionStartTime = Date.now();\n\n const session = this.loadSession();\n if (session) {\n this.sessionId = session.sessionId;\n this.sessionStartTime = session.startTime;\n this.lastActivityTime = session.lastActivityTime;\n this.eventCount = session.eventCount;\n this.adClickIDs = session.adClickIDs || {};\n this.landingPage = session.landingPage;\n logger.debug('Session loaded:', session);\n } else {\n this.sessionId = this.createNewSession();\n }\n\n this.startActivityTracking();\n this.startExpiryCheck();\n }\n\n private loadSession(): SessionState | null {\n const sessionData = this.storage.get('session');\n\n if (!sessionData) {\n return null;\n }\n\n try {\n const session: SessionState = JSON.parse(sessionData);\n const elapsed = Date.now() - session.lastActivityTime;\n\n if (elapsed < this.sessionTimeout) {\n return session;\n } else {\n logger.debug('Session expired, creating new session');\n this.storage.remove('session');\n return null;\n }\n } catch (error) {\n logger.warn('Failed to parse session data:', error);\n this.storage.remove('session');\n return null;\n }\n }\n\n private createNewSession(): string {\n const newSessionId = generateSessionId();\n this.sessionStartTime = Date.now();\n this.lastActivityTime = Date.now();\n this.eventCount = 0;\n // Assign BEFORE persisting — persistSession reads `this.sessionId`,\n // and prior to this assignment that field is still uninitialized\n // (the constructor only assigns it from createNewSession's return\n // value AFTER this call returns). Pre-fix, the first cookie write\n // contained the session JSON minus its sessionId field\n // (JSON.stringify drops undefined keys), and the next instance\n // reading that cookie would adopt the broken state.\n this.sessionId = newSessionId;\n\n this.persistSession();\n logger.info('New session created:', newSessionId);\n\n return newSessionId;\n }\n\n private persistSession(): void {\n const sessionData: SessionState = {\n sessionId: this.sessionId,\n startTime: this.sessionStartTime,\n lastActivityTime: this.lastActivityTime,\n eventCount: this.eventCount,\n adClickIDs: this.adClickIDs,\n landingPage: this.landingPage,\n };\n\n this.storage.set('session', JSON.stringify(sessionData));\n }\n\n private startActivityTracking(): void {\n const events = ['click', 'scroll', 'keypress', 'mousemove', 'touchstart'];\n\n const activityHandler = () => this.onActivity();\n\n events.forEach((event) => {\n window.addEventListener(event, activityHandler, { passive: true });\n });\n\n window.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'visible') {\n this.onActivity();\n }\n });\n }\n\n private startExpiryCheck(): void {\n this.checkInterval = window.setInterval(() => {\n this.checkSessionExpiry();\n }, 60000);\n }\n\n private onActivity(): void {\n const now = Date.now();\n const timeSinceLastUpdate = now - this.lastActivityUpdate;\n\n if (timeSinceLastUpdate < this.activityThrottle) {\n return;\n }\n\n this.lastActivityUpdate = now;\n const timeSinceLastActivity = now - this.lastActivityTime;\n\n if (timeSinceLastActivity > this.sessionTimeout) {\n logger.info('Session expired due to inactivity, creating new session');\n this.sessionId = this.createNewSession();\n } else {\n this.lastActivityTime = now;\n this.persistSession();\n }\n }\n\n private checkSessionExpiry(): void {\n const elapsed = Date.now() - this.lastActivityTime;\n\n if (elapsed > this.sessionTimeout) {\n logger.info('Session expired during check, creating new session');\n this.sessionId = this.createNewSession();\n }\n }\n\n getSessionId(): string {\n return this.sessionId;\n }\n\n incrementEventCount(): void {\n this.eventCount++;\n this.lastActivityTime = Date.now();\n this.persistSession();\n }\n\n getSessionDuration(): number {\n return Date.now() - this.sessionStartTime;\n }\n\n getEventCount(): number {\n return this.eventCount;\n }\n\n getState(): SessionState {\n return {\n sessionId: this.sessionId,\n startTime: this.sessionStartTime,\n lastActivityTime: this.lastActivityTime,\n eventCount: this.eventCount,\n adClickIDs: this.adClickIDs,\n landingPage: this.landingPage,\n };\n }\n\n setAdClickIDs(adClickIDs: AdClickIDs, landingPage?: string): void {\n if (Object.keys(adClickIDs).length > 0) {\n this.adClickIDs = { ...this.adClickIDs, ...adClickIDs };\n if (landingPage) {\n this.landingPage = landingPage;\n }\n this.persistSession();\n logger.info('Ad click IDs stored in session:', this.adClickIDs);\n }\n }\n\n getAdClickIDs(): AdClickIDs {\n return this.adClickIDs;\n }\n\n getLandingPage(): string | undefined {\n return this.landingPage;\n }\n\n destroy(): void {\n if (this.checkInterval) {\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n }\n }\n}\n","import { AegisEvent } from '../types/events';\nimport { Transport } from './transport';\nimport { Storage } from '../utils/storage';\nimport { logger } from '../utils/logger';\n\nexport interface QueueConfig {\n batchSize: number;\n batchInterval: number;\n enableOfflineMode: boolean;\n maxOfflineEvents: number;\n}\n\nexport class EventQueue {\n private buffer: AegisEvent[] = [];\n private config: QueueConfig;\n private transport: Transport;\n private storage: Storage;\n private flushInterval: number | null = null;\n private isFlushing: boolean = false;\n private offlineQueue: AegisEvent[] = [];\n private isOnline: boolean = navigator.onLine;\n\n constructor(config: QueueConfig, transport: Transport, storage: Storage) {\n this.config = config;\n this.transport = transport;\n this.storage = storage;\n\n if (config.enableOfflineMode) {\n this.loadOfflineQueue();\n this.setupOnlineListener();\n }\n\n this.startFlushTimer();\n this.setupUnloadHandlers();\n }\n\n private loadOfflineQueue(): void {\n const offlineData = this.storage.get('offline_queue');\n\n if (!offlineData) {\n return;\n }\n\n try {\n const events = JSON.parse(offlineData) as AegisEvent[];\n\n if (Array.isArray(events) && events.length > 0) {\n this.offlineQueue = events.slice(0, this.config.maxOfflineEvents);\n logger.info(`Loaded ${this.offlineQueue.length} offline events`);\n\n if (this.isOnline) {\n this.flushOfflineQueue();\n }\n }\n } catch (error) {\n logger.warn('Failed to load offline queue:', error);\n this.storage.remove('offline_queue');\n }\n }\n\n private saveOfflineQueue(): void {\n if (this.offlineQueue.length === 0) {\n this.storage.remove('offline_queue');\n return;\n }\n\n try {\n const data = JSON.stringify(this.offlineQueue);\n this.storage.set('offline_queue', data);\n logger.debug(`Saved ${this.offlineQueue.length} events to offline queue`);\n } catch (error) {\n logger.warn('Failed to save offline queue:', error);\n }\n }\n\n private setupOnlineListener(): void {\n window.addEventListener('online', () => {\n logger.info('Connection restored');\n this.isOnline = true;\n this.flushOfflineQueue();\n this.flush();\n });\n\n window.addEventListener('offline', () => {\n logger.warn('Connection lost, entering offline mode');\n this.isOnline = false;\n });\n }\n\n private async flushOfflineQueue(): Promise<void> {\n if (this.offlineQueue.length === 0) {\n return;\n }\n\n logger.info(`Flushing ${this.offlineQueue.length} offline events`);\n\n const events = [...this.offlineQueue];\n this.offlineQueue = [];\n this.storage.remove('offline_queue');\n\n const result = await this.transport.send(events);\n\n if (!result.success) {\n logger.warn('Failed to send offline events, re-queueing');\n this.offlineQueue = [...events, ...this.offlineQueue].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n } else {\n logger.info('Offline events sent successfully');\n }\n }\n\n push(event: AegisEvent): void {\n if (!this.isOnline && this.config.enableOfflineMode) {\n this.addToOfflineQueue(event);\n return;\n }\n\n this.buffer.push(event);\n logger.debug('Event queued:', event.type, event);\n\n if (this.buffer.length >= this.config.batchSize) {\n this.flush();\n }\n }\n\n private addToOfflineQueue(event: AegisEvent): void {\n if (this.offlineQueue.length >= this.config.maxOfflineEvents) {\n logger.warn('Offline queue full, dropping oldest event');\n this.offlineQueue.shift();\n }\n\n this.offlineQueue.push(event);\n this.saveOfflineQueue();\n logger.debug('Event added to offline queue');\n }\n\n async flush(): Promise<void> {\n if (this.isFlushing) {\n logger.debug('Flush already in progress, skipping');\n return;\n }\n\n if (this.buffer.length === 0) {\n return;\n }\n\n this.isFlushing = true;\n\n const events = [...this.buffer];\n this.buffer = [];\n\n logger.info(`Flushing ${events.length} events`);\n\n try {\n const result = await this.transport.send(events);\n\n if (!result.success) {\n logger.error('Failed to send events:', result.error);\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...events].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n logger.info('Events saved to offline queue');\n } else {\n this.buffer.unshift(...events);\n logger.warn('Events re-queued for retry');\n }\n } else {\n logger.info('Events sent successfully');\n }\n } catch (error) {\n logger.error('Flush error:', error);\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...events].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n }\n } finally {\n this.isFlushing = false;\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n }\n\n this.flushInterval = window.setInterval(() => {\n this.flush();\n }, this.config.batchInterval);\n\n logger.debug(\n `Flush timer started with interval: ${this.config.batchInterval}ms`\n );\n }\n\n private setupUnloadHandlers(): void {\n const flushBeforeUnload = () => {\n if (this.buffer.length > 0 || this.offlineQueue.length > 0) {\n this.persistBufferToOfflineQueue();\n this.flush();\n }\n };\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n flushBeforeUnload();\n }\n });\n\n window.addEventListener('beforeunload', flushBeforeUnload);\n\n window.addEventListener('pagehide', flushBeforeUnload);\n }\n\n private persistBufferToOfflineQueue(): void {\n if (this.buffer.length === 0) {\n return;\n }\n\n if (this.config.enableOfflineMode) {\n this.offlineQueue = [...this.offlineQueue, ...this.buffer].slice(\n 0,\n this.config.maxOfflineEvents\n );\n this.saveOfflineQueue();\n logger.debug('Buffer persisted to offline queue before unload');\n }\n }\n\n getQueueSize(): number {\n return this.buffer.length;\n }\n\n getOfflineQueueSize(): number {\n return this.offlineQueue.length;\n }\n\n clear(): void {\n this.buffer = [];\n this.offlineQueue = [];\n this.storage.remove('offline_queue');\n logger.info('Queue cleared');\n }\n\n destroy(): void {\n if (this.flushInterval) {\n clearInterval(this.flushInterval);\n this.flushInterval = null;\n }\n\n this.flush();\n }\n}\n","import { AegisEvent, BatchPayload, BatchResponse } from '../types/events';\nimport { CellEndpoint, CellRegion } from '../types/config';\nimport { logger } from '../utils/logger';\nimport { Storage } from '../utils/storage';\n\nexport interface TransportConfig {\n writeKey: string;\n apiHost: string | null;\n cellEndpoints: CellEndpoint[];\n preferredRegion: CellRegion | null;\n autoRegionDetection: boolean;\n requestTimeout: number;\n retryFailedRequests: boolean;\n maxRetries: number;\n retryBackoffMultiplier: number;\n}\n\nexport interface SendResult {\n success: boolean;\n response?: BatchResponse;\n error?: Error;\n endpoint?: string;\n}\n\nexport class Transport {\n private config: TransportConfig;\n private storage: Storage;\n private activeCell: CellEndpoint | null = null;\n private healthCheckInterval: number | null = null;\n private regionLatencyMap: Map<string, number> = new Map();\n\n constructor(config: TransportConfig, storage: Storage) {\n this.config = config;\n this.storage = storage;\n\n if (config.cellEndpoints.length > 0) {\n this.initializeCellularArchitecture();\n }\n }\n\n private async initializeCellularArchitecture(): Promise<void> {\n const cachedCell = this.loadCachedCell();\n\n if (cachedCell && this.isCellHealthy(cachedCell)) {\n this.activeCell = cachedCell;\n logger.info('Using cached cell:', cachedCell);\n } else {\n await this.selectOptimalCell();\n }\n\n this.startHealthChecks();\n }\n\n private loadCachedCell(): CellEndpoint | null {\n const cached = this.storage.get('active_cell');\n\n if (!cached) {\n return null;\n }\n\n try {\n return JSON.parse(cached) as CellEndpoint;\n } catch (error) {\n logger.warn('Failed to parse cached cell:', error);\n return null;\n }\n }\n\n private saveCachedCell(cell: CellEndpoint): void {\n this.storage.set('active_cell', JSON.stringify(cell), 7);\n }\n\n private isCellHealthy(cell: CellEndpoint): boolean {\n return (\n cell.healthy &&\n this.config.cellEndpoints.some(\n (c) => c.region === cell.region && c.url === cell.url\n )\n );\n }\n\n private async selectOptimalCell(): Promise<void> {\n logger.info('Selecting optimal cell...');\n\n if (this.config.preferredRegion) {\n const preferredCell = this.config.cellEndpoints.find(\n (cell) => cell.region === this.config.preferredRegion && cell.healthy\n );\n\n if (preferredCell) {\n this.activeCell = preferredCell;\n this.saveCachedCell(preferredCell);\n logger.info('Using preferred region cell:', preferredCell);\n return;\n }\n }\n\n if (this.config.autoRegionDetection) {\n await this.detectOptimalRegion();\n } else {\n this.selectCellByPriority();\n }\n }\n\n private async detectOptimalRegion(): Promise<void> {\n const healthyCells = this.config.cellEndpoints.filter((cell) => cell.healthy);\n\n if (healthyCells.length === 0) {\n logger.error('No healthy cells available');\n return;\n }\n\n const latencyPromises = healthyCells.map(async (cell) => {\n const latency = await this.measureLatency(cell);\n this.regionLatencyMap.set(cell.region, latency);\n return { cell, latency };\n });\n\n const results = await Promise.all(latencyPromises);\n results.sort((a, b) => a.latency - b.latency);\n\n const optimalCell = results[0].cell;\n this.activeCell = optimalCell;\n this.saveCachedCell(optimalCell);\n\n logger.info('Optimal cell selected:', {\n region: optimalCell.region,\n latency: results[0].latency,\n });\n }\n\n private async measureLatency(cell: CellEndpoint): Promise<number> {\n const startTime = performance.now();\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 2000);\n\n const response = await fetch(`${cell.url}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n return performance.now() - startTime;\n } else {\n return Infinity;\n }\n } catch (error) {\n logger.warn(`Health check failed for ${cell.region}:`, error);\n return Infinity;\n }\n }\n\n private selectCellByPriority(): void {\n const sortedCells = [...this.config.cellEndpoints]\n .filter((cell) => cell.healthy)\n .sort((a, b) => a.priority - b.priority);\n\n if (sortedCells.length > 0) {\n this.activeCell = sortedCells[0];\n this.saveCachedCell(sortedCells[0]);\n logger.info('Cell selected by priority:', sortedCells[0]);\n } else {\n logger.error('No healthy cells available');\n }\n }\n\n private startHealthChecks(): void {\n this.healthCheckInterval = window.setInterval(() => {\n this.performHealthChecks();\n }, 60000);\n }\n\n private async performHealthChecks(): Promise<void> {\n const healthPromises = this.config.cellEndpoints.map(async (cell) => {\n const isHealthy = await this.checkCellHealth(cell);\n cell.healthy = isHealthy;\n return { cell, isHealthy };\n });\n\n const results = await Promise.all(healthPromises);\n\n if (this.activeCell && !this.activeCell.healthy) {\n logger.warn('Active cell unhealthy, selecting new cell');\n await this.selectOptimalCell();\n }\n\n logger.debug('Health check results:', results);\n }\n\n private async checkCellHealth(cell: CellEndpoint): Promise<boolean> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 3000);\n\n const response = await fetch(`${cell.url}/health`, {\n method: 'GET',\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n return response.ok;\n } catch (error) {\n return false;\n }\n }\n\n async send(events: AegisEvent[]): Promise<SendResult> {\n if (events.length === 0) {\n return { success: true };\n }\n\n const endpoint = this.getActiveEndpoint();\n const payload = this.buildPayload(events);\n\n logger.debug('Sending batch:', { endpoint, eventCount: events.length });\n\n if (document.visibilityState === 'hidden' && typeof navigator.sendBeacon === 'function') {\n const result = this.sendViaBeacon(endpoint, payload);\n return Promise.resolve(result);\n } else {\n return this.sendViaFetch(endpoint, payload);\n }\n }\n\n private getActiveEndpoint(): string {\n if (this.activeCell) {\n return `${this.activeCell.url}/v1/batch`;\n }\n\n if (this.config.apiHost) {\n return `${this.config.apiHost}/v1/batch`;\n }\n\n throw new Error('No active endpoint available');\n }\n\n private buildPayload(events: AegisEvent[]): BatchPayload {\n return {\n batch: events,\n sentAt: new Date().toISOString(),\n writeKey: this.config.writeKey,\n context: this.activeCell\n ? {\n cell: {\n region: this.activeCell.region,\n endpoint: this.activeCell.url,\n },\n }\n : undefined,\n };\n }\n\n private sendViaBeacon(endpoint: string, payload: BatchPayload): SendResult | Promise<SendResult> {\n try {\n const blob = new Blob([JSON.stringify(payload)], {\n type: 'application/json',\n });\n\n const sent = navigator.sendBeacon(endpoint, blob);\n\n if (sent) {\n logger.debug('Batch sent via Beacon');\n return { success: true, endpoint };\n } else {\n logger.warn('Beacon send failed, falling back to fetch');\n return this.sendViaFetch(endpoint, payload);\n }\n } catch (error) {\n logger.error('Beacon send error:', error);\n return {\n success: false,\n error: error as Error,\n endpoint,\n };\n }\n }\n\n private async sendViaFetch(\n endpoint: string,\n payload: BatchPayload,\n attempt: number = 1\n ): Promise<SendResult> {\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(\n () => controller.abort(),\n this.config.requestTimeout\n );\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.config.writeKey}`,\n },\n body: JSON.stringify(payload),\n keepalive: true,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (response.ok) {\n const result: BatchResponse = await response.json();\n logger.debug('Batch sent successfully:', result);\n return { success: true, response: result, endpoint };\n } else {\n const errorText = await response.text();\n const error = new Error(\n `HTTP ${response.status}: ${errorText || response.statusText}`\n );\n\n logger.error('Batch send failed:', error);\n\n if (this.shouldRetry(response.status, attempt)) {\n return this.retryRequest(endpoint, payload, attempt);\n }\n\n return { success: false, error, endpoint };\n }\n } catch (error) {\n logger.error('Fetch error:', error);\n\n if (this.shouldRetry(0, attempt)) {\n return this.retryRequest(endpoint, payload, attempt);\n }\n\n return {\n success: false,\n error: error as Error,\n endpoint,\n };\n }\n }\n\n private shouldRetry(statusCode: number, attempt: number): boolean {\n if (!this.config.retryFailedRequests) {\n return false;\n }\n\n if (attempt >= this.config.maxRetries) {\n return false;\n }\n\n if (statusCode === 0) {\n return true;\n }\n\n return statusCode >= 500 || statusCode === 429;\n }\n\n private async retryRequest(\n endpoint: string,\n payload: BatchPayload,\n attempt: number\n ): Promise<SendResult> {\n const delay =\n Math.pow(this.config.retryBackoffMultiplier, attempt) * 1000;\n\n logger.info(`Retrying request in ${delay}ms (attempt ${attempt + 1})`);\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n return this.sendViaFetch(endpoint, payload, attempt + 1);\n }\n\n getActiveCell(): CellEndpoint | null {\n return this.activeCell;\n }\n\n getRegionLatencies(): Map<string, number> {\n return new Map(this.regionLatencyMap);\n }\n\n destroy(): void {\n if (this.healthCheckInterval) {\n clearInterval(this.healthCheckInterval);\n this.healthCheckInterval = null;\n }\n }\n}\n","const STORAGE_PREFIX = 'aegis_';\n\nexport interface StorageOptions {\n cookieDomain?: string | null;\n secureCookie?: boolean;\n crossDomain?: boolean;\n}\n\nexport class Storage {\n private useLocalStorage: boolean;\n private options: StorageOptions;\n\n constructor(options: StorageOptions = {}) {\n this.options = options;\n this.useLocalStorage = this.isLocalStorageAvailable();\n }\n\n private isLocalStorageAvailable(): boolean {\n try {\n const test = '__aegis_test__';\n localStorage.setItem(test, test);\n localStorage.removeItem(test);\n return true;\n } catch {\n return false;\n }\n }\n\n set(key: string, value: string, expiryDays: number = 365): void {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n const item = {\n value,\n expiry: Date.now() + expiryDays * 24 * 60 * 60 * 1000,\n };\n localStorage.setItem(fullKey, JSON.stringify(item));\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.setItem failed:', error);\n }\n }\n\n this.setCookie(fullKey, value, expiryDays);\n }\n\n get(key: string): string | null {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n const itemStr = localStorage.getItem(fullKey);\n if (itemStr) {\n const item = JSON.parse(itemStr);\n if (Date.now() < item.expiry) {\n return item.value;\n } else {\n localStorage.removeItem(fullKey);\n }\n }\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.getItem failed:', error);\n }\n }\n\n return this.getCookie(fullKey);\n }\n\n remove(key: string): void {\n const fullKey = STORAGE_PREFIX + key;\n\n if (this.useLocalStorage) {\n try {\n localStorage.removeItem(fullKey);\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.removeItem failed:', error);\n }\n }\n\n this.deleteCookie(fullKey);\n }\n\n clear(): void {\n if (this.useLocalStorage) {\n try {\n const keys = Object.keys(localStorage);\n keys.forEach((key) => {\n if (key.startsWith(STORAGE_PREFIX)) {\n localStorage.removeItem(key);\n }\n });\n } catch (error) {\n console.warn('[Active Reach Storage] localStorage.clear failed:', error);\n }\n }\n\n const cookies = document.cookie.split(';');\n cookies.forEach((cookie) => {\n const key = cookie.split('=')[0].trim();\n if (key.startsWith(STORAGE_PREFIX)) {\n this.deleteCookie(key);\n }\n });\n }\n\n private setCookie(name: string, value: string, days: number): void {\n try {\n const date = new Date();\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\n const expires = `expires=${date.toUTCString()}`;\n \n let cookieStr = `${name}=${value};${expires};path=/`;\n \n if (this.options.secureCookie && window.location.protocol === 'https:') {\n cookieStr += ';Secure';\n }\n \n cookieStr += ';SameSite=Lax';\n \n if (this.options.cookieDomain) {\n cookieStr += `;domain=${this.options.cookieDomain}`;\n } else if (this.options.crossDomain) {\n const domain = this.getRootDomain();\n if (domain) {\n cookieStr += `;domain=${domain}`;\n }\n }\n \n document.cookie = cookieStr;\n } catch (error) {\n console.warn('[Active Reach Storage] setCookie failed:', error);\n }\n }\n\n private getCookie(name: string): string | null {\n try {\n const nameEQ = name + '=';\n const ca = document.cookie.split(';');\n \n for (let i = 0; i < ca.length; i++) {\n let c = ca[i];\n while (c.charAt(0) === ' ') {\n c = c.substring(1, c.length);\n }\n if (c.indexOf(nameEQ) === 0) {\n return c.substring(nameEQ.length, c.length);\n }\n }\n } catch (error) {\n console.warn('[Active Reach Storage] getCookie failed:', error);\n }\n \n return null;\n }\n\n private deleteCookie(name: string): void {\n try {\n let cookieStr = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/`;\n \n if (this.options.cookieDomain) {\n cookieStr += `;domain=${this.options.cookieDomain}`;\n } else if (this.options.crossDomain) {\n const domain = this.getRootDomain();\n if (domain) {\n cookieStr += `;domain=${domain}`;\n }\n }\n \n document.cookie = cookieStr;\n } catch (error) {\n console.warn('[Active Reach Storage] deleteCookie failed:', error);\n }\n }\n\n private getRootDomain(): string | null {\n try {\n const hostname = window.location.hostname;\n \n if (/^(\\d{1,3}\\.){3}\\d{1,3}$/.test(hostname)) {\n return null;\n }\n \n if (hostname === 'localhost') {\n return null;\n }\n \n const parts = hostname.split('.');\n \n if (parts.length <= 2) {\n return `.${hostname}`;\n }\n \n return `.${parts.slice(-2).join('.')}`;\n } catch (error) {\n console.warn('[Active Reach Storage] getRootDomain failed:', error);\n return null;\n }\n }\n}\n","import { EventContext } from '../types/events';\nimport { parseUTMParameters } from './url-parser';\nimport { AegisConfigInternal } from '../types/config';\nimport type { SessionManager } from '../core/session';\n\nexport function buildContext(config: AegisConfigInternal, session?: SessionManager): EventContext {\n const utm = config.capture_utm ? parseUTMParameters() : undefined;\n const device = detectDevice();\n const os = detectOS();\n const browser = detectBrowser();\n const network = detectNetworkInfo();\n\n const adClickIDs = session?.getAdClickIDs();\n const landingPage = session?.getLandingPage();\n\n return {\n library: {\n name: '@active-reach/web-sdk',\n version: '1.0.0',\n },\n page: {\n path: window.location.pathname,\n referrer: config.capture_referrer ? document.referrer : '',\n search: window.location.search,\n title: document.title,\n url: window.location.href,\n hash: window.location.hash,\n },\n userAgent: navigator.userAgent,\n locale: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n screen: {\n width: window.screen.width,\n height: window.screen.height,\n density: window.devicePixelRatio || 1,\n },\n viewport: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n ...(utm && Object.keys(utm).length > 0 && { campaign: utm }),\n ...(adClickIDs && Object.keys(adClickIDs).length > 0 && { adClickIDs }),\n ...(landingPage && { landingPage }),\n ...(network && { network }),\n ...(device && { device }),\n ...(os && { os }),\n ...(browser && { browser }),\n ...(config.active_cell && {\n cell: {\n region: config.active_cell.region,\n endpoint: config.active_cell.url,\n },\n }),\n };\n}\n\nfunction detectDevice(): EventContext['device'] {\n const ua = navigator.userAgent;\n\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\n return { type: 'tablet' };\n }\n\n if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\n return { type: 'mobile' };\n }\n\n return { type: 'desktop' };\n}\n\nfunction detectOS(): EventContext['os'] {\n const ua = navigator.userAgent;\n\n if (/Windows NT 10/i.test(ua)) return { name: 'Windows', version: '10' };\n if (/Windows NT 6.3/i.test(ua)) return { name: 'Windows', version: '8.1' };\n if (/Windows NT 6.2/i.test(ua)) return { name: 'Windows', version: '8' };\n if (/Windows NT 6.1/i.test(ua)) return { name: 'Windows', version: '7' };\n if (/Windows/i.test(ua)) return { name: 'Windows', version: 'Unknown' };\n\n if (/Mac OS X (\\d+)[._](\\d+)/.test(ua)) {\n const match = ua.match(/Mac OS X (\\d+)[._](\\d+)/);\n return { name: 'macOS', version: `${match![1]}.${match![2]}` };\n }\n if (/Mac/i.test(ua)) return { name: 'macOS', version: 'Unknown' };\n\n if (/Android (\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Android (\\d+\\.\\d+)/);\n return { name: 'Android', version: match![1] };\n }\n if (/Android/i.test(ua)) return { name: 'Android', version: 'Unknown' };\n\n if (/iPhone OS (\\d+)_(\\d+)/.test(ua)) {\n const match = ua.match(/iPhone OS (\\d+)_(\\d+)/);\n return { name: 'iOS', version: `${match![1]}.${match![2]}` };\n }\n if (/iPad.*OS (\\d+)_(\\d+)/.test(ua)) {\n const match = ua.match(/iPad.*OS (\\d+)_(\\d+)/);\n return { name: 'iOS', version: `${match![1]}.${match![2]}` };\n }\n if (/iPhone|iPad/i.test(ua)) return { name: 'iOS', version: 'Unknown' };\n\n if (/Linux/i.test(ua)) return { name: 'Linux', version: 'Unknown' };\n\n return { name: 'Unknown', version: 'Unknown' };\n}\n\nfunction detectBrowser(): EventContext['browser'] {\n const ua = navigator.userAgent;\n\n if (/Edg\\/(\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Edg\\/(\\d+\\.\\d+)/);\n return { name: 'Edge', version: match![1] };\n }\n\n if (/Chrome\\/(\\d+\\.\\d+)/.test(ua) && !/Edg/.test(ua)) {\n const match = ua.match(/Chrome\\/(\\d+\\.\\d+)/);\n return { name: 'Chrome', version: match![1] };\n }\n\n if (/Firefox\\/(\\d+\\.\\d+)/.test(ua)) {\n const match = ua.match(/Firefox\\/(\\d+\\.\\d+)/);\n return { name: 'Firefox', version: match![1] };\n }\n\n if (/Safari\\/(\\d+\\.\\d+)/.test(ua) && !/Chrome/.test(ua)) {\n const match = ua.match(/Version\\/(\\d+\\.\\d+)/);\n return { name: 'Safari', version: match ? match[1] : 'Unknown' };\n }\n\n if (/MSIE (\\d+\\.\\d+)/.test(ua) || /Trident\\//.test(ua)) {\n const match = ua.match(/MSIE (\\d+\\.\\d+)/) || ua.match(/rv:(\\d+\\.\\d+)/);\n return { name: 'Internet Explorer', version: match ? match[1] : 'Unknown' };\n }\n\n return { name: 'Unknown', version: 'Unknown' };\n}\n\nfunction detectNetworkInfo(): EventContext['network'] | undefined {\n try {\n const connection =\n (navigator as any).connection ||\n (navigator as any).mozConnection ||\n (navigator as any).webkitConnection;\n\n if (connection) {\n return {\n effectiveType: connection.effectiveType,\n downlink: connection.downlink,\n rtt: connection.rtt,\n };\n }\n } catch (error) {\n console.warn('[Active Reach Device] detectNetworkInfo failed:', error);\n }\n\n return undefined;\n}\n\nexport function isBot(): boolean {\n const ua = navigator.userAgent.toLowerCase();\n const botPatterns = [\n 'bot',\n 'crawl',\n 'spider',\n 'slurp',\n 'mediapartners',\n 'googlebot',\n 'bingbot',\n 'yahoo',\n 'duckduckbot',\n 'baiduspider',\n 'yandex',\n 'facebookexternalhit',\n 'linkedinbot',\n 'twitterbot',\n 'slackbot',\n 'telegrambot',\n 'whatsapp',\n 'pingdom',\n 'uptimerobot',\n ];\n\n return botPatterns.some((pattern) => ua.includes(pattern));\n}\n","export interface UTMParams {\n source?: string;\n medium?: string;\n campaign?: string;\n term?: string;\n content?: string;\n name?: string;\n}\n\nexport function parseUTMParameters(url?: string): UTMParams {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const utm: UTMParams = {};\n\n const utmKeys: Array<keyof UTMParams> = [\n 'source',\n 'medium',\n 'campaign',\n 'term',\n 'content',\n 'name',\n ];\n\n utmKeys.forEach((key) => {\n const value = searchParams.get(`utm_${key}`);\n if (value) {\n utm[key] = value;\n }\n });\n\n return utm;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseUTMParameters failed:', error);\n return {};\n }\n}\n\nexport function parseQueryParameters(url?: string): Record<string, string> {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const params: Record<string, string> = {};\n\n searchParams.forEach((value, key) => {\n params[key] = value;\n });\n\n return params;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseQueryParameters failed:', error);\n return {};\n }\n}\n\nexport function getCanonicalURL(): string {\n try {\n const canonical = document.querySelector('link[rel=\"canonical\"]');\n if (canonical && canonical.getAttribute('href')) {\n return canonical.getAttribute('href')!;\n }\n \n return window.location.href;\n } catch (error) {\n console.warn('[Active Reach URL Parser] getCanonicalURL failed:', error);\n return window.location.href;\n }\n}\n\nexport function stripQueryParameters(url: string): string {\n try {\n const urlObj = new URL(url);\n return `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;\n } catch (error) {\n console.warn('[Active Reach URL Parser] stripQueryParameters failed:', error);\n return url;\n }\n}\n\nexport interface AdClickIDs {\n gclid?: string;\n fbclid?: string;\n msclkid?: string;\n ctwa_clid?: string;\n ttclid?: string;\n li_fat_id?: string;\n}\n\nexport function parseAdClickIDs(url?: string): AdClickIDs {\n try {\n const searchParams = url\n ? new URL(url).searchParams\n : new URLSearchParams(window.location.search);\n\n const adClickIDs: AdClickIDs = {};\n\n const clickIDKeys: Array<keyof AdClickIDs> = [\n 'gclid',\n 'fbclid',\n 'msclkid',\n 'ctwa_clid',\n 'ttclid',\n 'li_fat_id',\n ];\n\n clickIDKeys.forEach((key) => {\n const value = searchParams.get(key);\n if (value) {\n adClickIDs[key] = value;\n }\n });\n\n return adClickIDs;\n } catch (error) {\n console.warn('[Active Reach URL Parser] parseAdClickIDs failed:', error);\n return {};\n }\n}\n\nexport function hasAdClickIDs(adClickIDs: AdClickIDs): boolean {\n return Object.keys(adClickIDs).length > 0;\n}\n","import { Plugin } from '../types/plugin';\nimport { logger } from '../utils/logger';\n\nexport class PluginRegistry {\n private plugins: Map<string, Plugin> = new Map();\n\n register(plugin: Plugin): void {\n if (this.plugins.has(plugin.name)) {\n logger.warn(`Plugin \"${plugin.name}\" already registered, skipping`);\n return;\n }\n\n this.plugins.set(plugin.name, plugin);\n logger.info(`Plugin registered: ${plugin.name} v${plugin.version}`);\n }\n\n unregister(pluginName: string): void {\n const plugin = this.plugins.get(pluginName);\n\n if (!plugin) {\n logger.warn(`Plugin \"${pluginName}\" not found`);\n return;\n }\n\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch (error) {\n logger.error(`Error destroying plugin \"${pluginName}\":`, error);\n }\n }\n\n this.plugins.delete(pluginName);\n logger.info(`Plugin unregistered: ${pluginName}`);\n }\n\n get(pluginName: string): Plugin | undefined {\n return this.plugins.get(pluginName);\n }\n\n getAll(): Plugin[] {\n return Array.from(this.plugins.values());\n }\n\n async executeHook<T = any>(\n hookName: keyof Plugin,\n ...args: any[]\n ): Promise<T | undefined> {\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n try {\n const result = await (hook as Function).apply(plugin, args);\n\n if (result !== undefined) {\n return result as T;\n }\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, args });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n }\n }\n }\n\n return undefined;\n }\n\n async executeHookChain<T = any>(\n hookName: keyof Plugin,\n initialValue: T,\n ...additionalArgs: any[]\n ): Promise<T> {\n let value = initialValue;\n\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n try {\n const result = await (hook as Function).apply(plugin, [\n value,\n ...additionalArgs,\n ]);\n\n if (result !== undefined && result !== null) {\n value = result;\n }\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, value, additionalArgs });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n }\n }\n }\n\n return value;\n }\n\n async executeHookParallel<T = any>(\n hookName: keyof Plugin,\n ...args: any[]\n ): Promise<T[]> {\n const promises: Promise<T>[] = [];\n\n for (const plugin of this.plugins.values()) {\n const hook = plugin[hookName];\n\n if (typeof hook === 'function') {\n const promise = (async () => {\n try {\n return await (hook as Function).apply(plugin, args);\n } catch (error) {\n logger.error(\n `Error executing ${String(hookName)} in plugin \"${plugin.name}\":`,\n error\n );\n\n if (plugin.onError) {\n try {\n plugin.onError(error as Error, { hookName, args });\n } catch (onErrorError) {\n logger.error(\n `Error in onError handler for plugin \"${plugin.name}\":`,\n onErrorError\n );\n }\n }\n\n return undefined;\n }\n })();\n\n promises.push(promise);\n }\n }\n\n const results = await Promise.all(promises);\n return results.filter((r) => r !== undefined) as T[];\n }\n\n clear(): void {\n for (const plugin of this.plugins.values()) {\n if (plugin.destroy) {\n try {\n plugin.destroy();\n } catch (error) {\n logger.error(`Error destroying plugin \"${plugin.name}\":`, error);\n }\n }\n }\n\n this.plugins.clear();\n logger.info('All plugins cleared');\n }\n}\n","import { Storage } from './storage';\nimport { logger } from './logger';\n\nexport type ConsentCategory =\n | 'analytics'\n | 'marketing'\n | 'functional'\n | 'necessary';\n\nexport interface ConsentPreferences {\n analytics: boolean;\n marketing: boolean;\n functional: boolean;\n necessary: boolean;\n}\n\nexport type ConsentStatus = 'granted' | 'denied' | 'pending';\n\nexport interface ConsentManagerConfig {\n defaultConsent?: Partial<ConsentPreferences>;\n waitForConsent?: boolean;\n consentStorageKey?: string;\n}\n\nexport class ConsentManager {\n private storage: Storage;\n private config: Required<ConsentManagerConfig>;\n private preferences: ConsentPreferences;\n private listeners: Array<(preferences: ConsentPreferences) => void> = [];\n\n constructor(storage: Storage, config: ConsentManagerConfig = {}) {\n this.storage = storage;\n this.config = {\n defaultConsent: config.defaultConsent || {},\n waitForConsent: config.waitForConsent ?? false,\n consentStorageKey: config.consentStorageKey || 'aegis_consent',\n };\n\n this.preferences = this.loadPreferences();\n }\n\n private loadPreferences(): ConsentPreferences {\n const stored = this.storage.get(this.config.consentStorageKey);\n\n if (stored) {\n try {\n const parsed = JSON.parse(stored);\n logger.info('Loaded consent preferences:', parsed);\n return this.mergeWithDefaults(parsed);\n } catch (error) {\n logger.warn('Failed to parse consent preferences:', error);\n }\n }\n\n return this.getDefaultPreferences();\n }\n\n private getDefaultPreferences(): ConsentPreferences {\n return {\n analytics: this.config.defaultConsent.analytics ?? false,\n marketing: this.config.defaultConsent.marketing ?? false,\n functional: this.config.defaultConsent.functional ?? true,\n necessary: true,\n };\n }\n\n private mergeWithDefaults(\n preferences: Partial<ConsentPreferences>\n ): ConsentPreferences {\n return {\n necessary: true,\n analytics: preferences.analytics ?? this.config.defaultConsent.analytics ?? false,\n marketing: preferences.marketing ?? this.config.defaultConsent.marketing ?? false,\n functional: preferences.functional ?? this.config.defaultConsent.functional ?? true,\n };\n }\n\n private savePreferences(): void {\n try {\n this.storage.set(\n this.config.consentStorageKey,\n JSON.stringify(this.preferences),\n 365\n );\n logger.info('Saved consent preferences:', this.preferences);\n } catch (error) {\n logger.error('Failed to save consent preferences:', error);\n }\n }\n\n setConsent(preferences: Partial<ConsentPreferences>): void {\n const updated = { ...this.preferences, ...preferences };\n updated.necessary = true;\n\n this.preferences = updated;\n this.savePreferences();\n this.notifyListeners();\n\n logger.info('Consent updated:', this.preferences);\n }\n\n grantAll(): void {\n this.setConsent({\n analytics: true,\n marketing: true,\n functional: true,\n necessary: true,\n });\n }\n\n denyAll(): void {\n this.setConsent({\n analytics: false,\n marketing: false,\n functional: false,\n necessary: true,\n });\n }\n\n hasConsent(category: ConsentCategory): boolean {\n return this.preferences[category];\n }\n\n getPreferences(): ConsentPreferences {\n return { ...this.preferences };\n }\n\n getStatus(category: ConsentCategory): ConsentStatus {\n if (this.hasConsent(category)) {\n return 'granted';\n }\n\n const stored = this.storage.get(this.config.consentStorageKey);\n if (!stored && this.config.waitForConsent) {\n return 'pending';\n }\n\n return 'denied';\n }\n\n isAnalyticsEnabled(): boolean {\n return this.hasConsent('analytics');\n }\n\n isMarketingEnabled(): boolean {\n return this.hasConsent('marketing');\n }\n\n onChange(callback: (preferences: ConsentPreferences) => void): () => void {\n this.listeners.push(callback);\n\n return () => {\n const index = this.listeners.indexOf(callback);\n if (index > -1) {\n this.listeners.splice(index, 1);\n }\n };\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((listener) => {\n try {\n listener(this.getPreferences());\n } catch (error) {\n logger.error('Consent listener error:', error);\n }\n });\n }\n\n reset(): void {\n this.storage.remove(this.config.consentStorageKey);\n this.preferences = this.getDefaultPreferences();\n this.notifyListeners();\n logger.info('Consent preferences reset');\n }\n\n integrateOneTrust(): void {\n if (typeof window === 'undefined') return;\n\n const oneTrust = (window as any).OneTrust;\n if (!oneTrust) {\n logger.warn('OneTrust not detected');\n return;\n }\n\n const syncWithOneTrust = () => {\n const activeGroups = (window as any).OnetrustActiveGroups || '';\n\n this.setConsent({\n necessary: true,\n functional: activeGroups.includes('C0003'),\n analytics: activeGroups.includes('C0002'),\n marketing: activeGroups.includes('C0004'),\n });\n\n logger.info('Synced with OneTrust consent');\n };\n\n if ((window as any).OptanonWrapper) {\n const originalWrapper = (window as any).OptanonWrapper;\n (window as any).OptanonWrapper = function () {\n originalWrapper();\n syncWithOneTrust();\n };\n } else {\n (window as any).OptanonWrapper = syncWithOneTrust;\n }\n\n syncWithOneTrust();\n }\n\n integrateCookiebot(): void {\n if (typeof window === 'undefined') return;\n\n const cookiebot = (window as any).Cookiebot;\n if (!cookiebot) {\n logger.warn('Cookiebot not detected');\n return;\n }\n\n const syncWithCookiebot = () => {\n const consent = cookiebot.consent || {};\n\n this.setConsent({\n necessary: true,\n functional: consent.preferences || false,\n analytics: consent.statistics || false,\n marketing: consent.marketing || false,\n });\n\n logger.info('Synced with Cookiebot consent');\n };\n\n window.addEventListener('CookiebotOnAccept', syncWithCookiebot);\n window.addEventListener('CookiebotOnDecline', syncWithCookiebot);\n\n if (cookiebot.consent) {\n syncWithCookiebot();\n }\n }\n\n integrateGoogleConsentMode(): void {\n if (typeof window === 'undefined') return;\n\n const gtag = (window as any).gtag;\n if (!gtag) {\n logger.warn('Google Tag Manager not detected');\n return;\n }\n\n const updateGoogleConsent = () => {\n gtag('consent', 'update', {\n ad_storage: this.preferences.marketing ? 'granted' : 'denied',\n analytics_storage: this.preferences.analytics ? 'granted' : 'denied',\n ad_user_data: this.preferences.marketing ? 'granted' : 'denied',\n ad_personalization: this.preferences.marketing ? 'granted' : 'denied',\n functionality_storage: this.preferences.functional ? 'granted' : 'denied',\n personalization_storage: this.preferences.functional ? 'granted' : 'denied',\n security_storage: 'granted',\n });\n\n logger.info('Updated Google Consent Mode');\n };\n\n this.onChange(updateGoogleConsent);\n updateGoogleConsent();\n }\n}\n","/**\n * Meta Pixel companion cookies (_fbp, _fbc) — B1.2 of META_CAPI_PHASE1_TRACKER.\n *\n * We do NOT fire `fbq()` browser-side. We write the cookies Meta uses to\n * identify the same browser so server-side CAPI events (forwarded from\n * `meta_conversions_api_service.py`) carry the high-EMQ match keys\n * (fbp + fbc) without the cost of running a full Meta Pixel.\n *\n * Cookie spec (Meta docs, verified May 2026):\n * _fbp = fb.<subdomain_index>.<creation_ms>.<random>\n * _fbc = fb.<subdomain_index>.<click_ms>.<fbclid>\n * subdomain_index = number of dots in cookie domain\n * (example.com → 1, sub.example.com → 2)\n * random = 10-digit random integer\n * Lifetime: 90 days (Meta's default; longer doesn't increase EMQ)\n *\n * Reasoning for raw document.cookie writes (instead of utils/storage.ts):\n * The Storage helper prefixes every key with `aegis_` so writes go to\n * `aegis__fbp` / `aegis__fbc`. Meta only honors the exact names\n * `_fbp` and `_fbc`. We bypass the helper and write directly.\n *\n * Domain scope: written to the eTLD+1 (root domain) so a tenant who\n * later wires their own Meta Pixel via GTM sees the same cookies a\n * subdomain-rooted set would not.\n *\n * SSR-safe: every helper is no-op when `document` / `window` are\n * undefined. Callers don't need to guard.\n */\n\nconst FBP_COOKIE = '_fbp';\nconst FBC_COOKIE = '_fbc';\nconst COOKIE_LIFETIME_DAYS = 90;\n\nexport interface MetaCookies {\n fbp: string | null;\n fbc: string | null;\n fbclid: string | null;\n}\n\n/** Read a cookie value or null. SSR-safe. */\nfunction readCookie(name: string): string | null {\n if (typeof document === 'undefined') return null;\n try {\n const target = `${name}=`;\n const parts = document.cookie.split(';');\n for (let i = 0; i < parts.length; i++) {\n let c = parts[i];\n while (c.charAt(0) === ' ') c = c.substring(1);\n if (c.indexOf(target) === 0) {\n return decodeURIComponent(c.substring(target.length));\n }\n }\n } catch {\n /* swallow — cookie access can throw in cross-origin / sandboxed iframes */\n }\n return null;\n}\n\n/** Write a cookie at root-domain scope so cross-subdomain reads work. SSR-safe. */\nfunction writeCookie(name: string, value: string, days: number): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n try {\n const expiry = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toUTCString();\n const domain = getRootDomain();\n const isHttps = window.location.protocol === 'https:';\n let cookieStr = `${name}=${encodeURIComponent(value)};expires=${expiry};path=/;SameSite=Lax`;\n if (isHttps) cookieStr += ';Secure';\n if (domain) cookieStr += `;domain=${domain}`;\n document.cookie = cookieStr;\n } catch {\n /* ignore — best effort */\n }\n}\n\n/**\n * Get eTLD+1 of current host. Returns null on localhost / IP / single-label\n * hosts so we fall through to host-only cookie scope (browser default).\n *\n * Heuristic, not PSL-perfect: produces `example.com` for `a.b.example.com`.\n * Edge cases (`co.uk` etc.) get the wrong answer, but Meta itself uses\n * the same heuristic when their own pixel writes _fbp, so we stay aligned.\n */\nfunction getRootDomain(): string | null {\n if (typeof window === 'undefined') return null;\n const host = window.location.hostname;\n if (!host || host === 'localhost') return null;\n // IP address — host-only cookie scope.\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return null;\n const parts = host.split('.');\n if (parts.length < 2) return null;\n return '.' + parts.slice(-2).join('.');\n}\n\n/**\n * Subdomain index per Meta spec — count of dots in the cookie domain.\n * Counts components, not literal dots:\n * example.com → 1\n * sub.example.com → 2\n * sub.sub.example.com → 3\n */\nfunction subdomainIndex(): number {\n if (typeof window === 'undefined') return 1;\n const host = window.location.hostname;\n if (!host || host === 'localhost' || /^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(host)) return 1;\n const parts = host.split('.');\n return Math.max(1, parts.length - 1);\n}\n\n/** 10-digit random integer (Meta's spec format). */\nfunction randomToken(): string {\n // Math.random() * 1e10 produces a 10-digit-ish integer; pad-left to be safe.\n const n = Math.floor(Math.random() * 1e10);\n return n.toString().padStart(10, '0');\n}\n\n/**\n * Read `_fbp` from cookies, write a fresh one if absent.\n * Returns the resulting `_fbp` value or null if cookies unavailable.\n */\nexport function ensureFbp(): string | null {\n const existing = readCookie(FBP_COOKIE);\n if (existing) return existing;\n const fbp = `fb.${subdomainIndex()}.${Date.now()}.${randomToken()}`;\n writeCookie(FBP_COOKIE, fbp, COOKIE_LIFETIME_DAYS);\n return fbp;\n}\n\n/**\n * Read `_fbc` from cookies. If absent and the URL carries `fbclid`, write\n * a fresh `_fbc` per Meta's spec. Returns the resulting value or null.\n *\n * Note: `_fbc` has no value to write without an `fbclid` source — unlike\n * `_fbp` which is browser-identity-only and can be self-generated.\n */\nexport function ensureFbc(): string | null {\n const existing = readCookie(FBC_COOKIE);\n if (existing) return existing;\n const fbclid = readFbclidFromUrl();\n if (!fbclid) return null;\n const fbc = `fb.${subdomainIndex()}.${Date.now()}.${fbclid}`;\n writeCookie(FBC_COOKIE, fbc, COOKIE_LIFETIME_DAYS);\n return fbc;\n}\n\n/** Pull `fbclid` from `?fbclid=...` query string. Null if absent or SSR. */\nexport function readFbclidFromUrl(): string | null {\n if (typeof window === 'undefined') return null;\n try {\n return new URLSearchParams(window.location.search).get('fbclid');\n } catch {\n return null;\n }\n}\n\n/**\n * Single call that returns `{fbp, fbc, fbclid}` for injection into every\n * event's `properties` (which lands in ClickHouse `in_app_events.metadata`,\n * which the cell-side CAPI service reads directly).\n *\n * Side effects: ensures both cookies are present before returning. Idempotent\n * — safe to call on every event capture.\n */\nexport function captureMetaCookies(): MetaCookies {\n return {\n fbp: ensureFbp(),\n fbc: ensureFbc(),\n fbclid: readFbclidFromUrl(),\n };\n}\n","/**\n * E-Commerce helper methods for the Active Reach SDK.\n *\n * Provides standardized e-commerce event tracking that maps to the\n * Active Reach Intelligence data governance pipeline.\n *\n * Event names follow the canonical mapper:\n * product_viewed → engagement.product_view\n * product_list_viewed → engagement.product_list_view\n * cart_item_added → cart_item_added\n * cart_item_removed → cart_item_removed\n * cart_viewed → cart_viewed\n * checkout_started → checkout_started\n * checkout_step → checkout_step\n * order_completed → transaction.purchase\n * order_refunded → transaction.refund\n * coupon_applied → coupon_applied\n * coupon_removed → coupon_removed\n * search_performed → search_performed\n * wishlist_item_added → wishlist_item_added\n * product_waitlisted → product_waitlisted (back-in-stock waitlist opt-in;\n * consumed by product_event_trigger_service\n * _get_waitlisted_contacts; fires catalog.back_in_stock\n * journey trigger on the next ON_STOCK flip)\n * promotion_viewed → promotion_viewed\n * promotion_clicked → promotion_clicked\n */\n\nimport type { Aegis } from '../core/analytics';\nimport type {\n EcommerceProduct,\n EcommerceCart,\n EcommerceCheckout,\n EcommerceOrder,\n EcommerceCoupon,\n EcommerceSearch,\n EcommerceProductList,\n EcommerceWishlist,\n EcommerceWaitlist,\n EcommercePromotion,\n} from './types';\n\nfunction sumItems(products: EcommerceProduct[]): number {\n return products.reduce((sum, p) => sum + (p.quantity ?? 1), 0);\n}\n\n// Cart abandonment detection is SERVER-SIDE. The SDK fires the canonical\n// e-commerce events (`cart_item_added`, `cart_item_removed`, `cart_viewed`,\n// `transaction.purchase`); a Celery beat task on cell-plane scans\n// `aegis.events_with_ttl` every 5 minutes for shoppers with a\n// `cart_item_added` >30 min old and no subsequent purchase, emitting\n// `cart_abandoned` to the `behavioral-events_{cell_id}` Kafka topic.\n//\n// The SPA storefront has no persistent server-side cart object, so cart\n// identity is keyed off the shopper themselves: `COALESCE(user_id,\n// anonymous_id)`. Provider-issued cart tokens (Shopify/Woo/Magento pixel\n// bridge via Widget Manager) are surfaced on the emitted event when\n// present but are not required for detection.\n//\n// We deliberately do NOT run a JS timer on the client — mobile browsers\n// throttle background timers to ~1Hz and they're unreliable for any\n// abandonment threshold beyond a few seconds. Industry alignment: WebEngage,\n// MoEngage, CleverTap, Bitespeed, Klaviyo all detect server-side.\n//\n// See: `apps/cell-plane/app/workers/storefront_cart_abandonment_scanner.py`\n\nfunction mapProducts(products: EcommerceProduct[]) {\n return products.map((p) => ({\n product_id: p.product_id,\n sku: p.sku ?? p.product_id,\n name: p.name,\n price: p.price,\n quantity: p.quantity ?? 1,\n currency: p.currency,\n category: p.category,\n brand: p.brand,\n variant_id: p.variant_id,\n variant_label: p.variant_label,\n position: p.position,\n }));\n}\n\nexport class EcommerceTracker {\n constructor(private aegis: Aegis) {}\n\n // -- Product Discovery --\n\n productViewed(product: EcommerceProduct): void {\n this.aegis.track('product_viewed', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n variant_label: product.variant_label,\n image_url: product.image_url,\n url: product.url,\n });\n }\n\n productListViewed(list: EcommerceProductList): void {\n this.aegis.track('product_list_viewed', {\n list_id: list.list_id,\n list_name: list.list_name,\n category: list.category,\n products: mapProducts(list.products),\n });\n }\n\n productClicked(product: EcommerceProduct, source?: { list_id?: string; position?: number; section?: string }): void {\n this.aegis.track('product_clicked', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n list_id: source?.list_id,\n position: source?.position ?? product.position,\n section: source?.section,\n });\n }\n\n productImpressed(product: EcommerceProduct, source?: { list_id?: string; position?: number; section?: string }): void {\n this.aegis.track('product_impression', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n currency: product.currency ?? 'INR',\n category: product.category,\n list_id: source?.list_id,\n position: source?.position ?? product.position,\n section: source?.section,\n });\n }\n\n categoryFiltered(category: string, options?: { previous_category?: string; result_count?: number }): void {\n this.aegis.track('category_filtered', {\n category,\n previous_category: options?.previous_category,\n result_count: options?.result_count,\n });\n }\n\n searchPerformed(search: EcommerceSearch): void {\n this.aegis.track('search_performed', {\n query: search.query,\n results_count: search.results_count,\n filters: search.filters,\n });\n }\n\n // -- Cart --\n\n addToCart(product: EcommerceProduct): void {\n this.aegis.track('cart_item_added', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n quantity: product.quantity ?? 1,\n currency: product.currency ?? 'INR',\n category: product.category,\n brand: product.brand,\n variant_id: product.variant_id,\n variant_label: product.variant_label,\n });\n }\n\n removeFromCart(product: EcommerceProduct): void {\n this.aegis.track('cart_item_removed', {\n product_id: product.product_id,\n sku: product.sku ?? product.product_id,\n name: product.name,\n price: product.price,\n quantity: product.quantity ?? 1,\n currency: product.currency ?? 'INR',\n variant_id: product.variant_id,\n });\n }\n\n cartViewed(cart: EcommerceCart): void {\n this.aegis.track('cart_viewed', {\n cart_id: cart.cart_id,\n value: cart.value,\n currency: cart.currency ?? 'INR',\n num_items: sumItems(cart.products),\n products: mapProducts(cart.products),\n });\n }\n\n // -- Checkout --\n\n checkoutStarted(checkout: EcommerceCheckout): void {\n this.aegis.track('checkout_started', {\n checkout_id: checkout.checkout_id,\n value: checkout.value,\n currency: checkout.currency ?? 'INR',\n num_items: sumItems(checkout.products),\n coupon: checkout.coupon,\n shipping: checkout.shipping,\n tax: checkout.tax,\n products: mapProducts(checkout.products),\n });\n }\n\n checkoutStep(step: number, options?: Record<string, unknown>): void {\n this.aegis.track('checkout_step', {\n step,\n ...options,\n });\n }\n\n // -- Order --\n\n orderCompleted(order: EcommerceOrder): void {\n this.aegis.track('order_completed', {\n order_id: order.order_id,\n value: order.value,\n revenue: order.revenue ?? order.value,\n currency: order.currency ?? 'INR',\n num_items: sumItems(order.products),\n coupon: order.coupon,\n shipping: order.shipping,\n tax: order.tax,\n discount: order.discount,\n payment_method: order.payment_method,\n products: mapProducts(order.products),\n });\n }\n\n orderRefunded(orderId: string, value?: number, products?: EcommerceProduct[]): void {\n this.aegis.track('order_refunded', {\n order_id: orderId,\n value,\n products: products ? mapProducts(products) : undefined,\n });\n }\n\n // -- Coupons --\n\n couponApplied(coupon: EcommerceCoupon): void {\n this.aegis.track('coupon_applied', { ...coupon });\n }\n\n couponRemoved(coupon: EcommerceCoupon): void {\n this.aegis.track('coupon_removed', { ...coupon });\n }\n\n // -- Wishlist --\n\n wishlistItemAdded(wishlist: EcommerceWishlist): void {\n this.aegis.track('wishlist_item_added', {\n wishlist_id: wishlist.wishlist_id,\n wishlist_name: wishlist.wishlist_name,\n product_id: wishlist.product.product_id,\n sku: wishlist.product.sku ?? wishlist.product.product_id,\n name: wishlist.product.name,\n price: wishlist.product.price,\n variant_id: wishlist.product.variant_id,\n });\n }\n\n // -- Back-in-stock waitlist --\n //\n // Server-side substrate: contact_events row keyed on\n // (organization_id, contact_id, event_name='product_waitlisted',\n // event_properties['product_id']). Resolved by\n // product_event_trigger_service._get_waitlisted_contacts and fanned out via\n // catalog.back_in_stock journey trigger when stock_event_handler_worker\n // detects the SKU flipping back into stock.\n productWaitlisted(waitlist: EcommerceWaitlist): void {\n this.aegis.track('product_waitlisted', {\n product_id: waitlist.product.product_id,\n sku: waitlist.product.sku ?? waitlist.product.product_id,\n variant_id: waitlist.product.variant_id,\n name: waitlist.product.name,\n price: waitlist.product.price,\n channels: waitlist.channels,\n });\n }\n\n // -- Promotions --\n\n promotionViewed(promo: EcommercePromotion): void {\n this.aegis.track('promotion_viewed', { ...promo });\n }\n\n promotionClicked(promo: EcommercePromotion): void {\n this.aegis.track('promotion_clicked', { ...promo });\n }\n}\n\nexport type {\n EcommerceProduct,\n EcommerceCart,\n EcommerceCheckout,\n EcommerceOrder,\n EcommerceCoupon,\n EcommerceSearch,\n EcommerceProductList,\n EcommerceWishlist,\n EcommerceWaitlist,\n EcommercePromotion,\n} from './types';\n","/**\n * Token bucket rate limiter for client-side event throttling.\n *\n * Limits per session: burst (default 100) + sustained (default 20/sec).\n * Drops events with a console warning once a burst sustains. Drops are\n * emitted as a single coalesced `aegis.client.rate_limited` meta-event\n * per second so we don't flood ingress with drop notifications.\n *\n * Defaults are deliberately generous — the goal is to prevent runaway\n * loops (a buggy app firing 10k events/sec) without affecting normal use.\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface RateLimiterOptions {\n /** Max tokens (burst size). Default 100. */\n burstCapacity?: number;\n /** Sustained rate per second. Default 20. */\n refillPerSecond?: number;\n /** Window in ms over which dropped events are coalesced. Default 1000. */\n dropCoalesceWindowMs?: number;\n /** Optional callback fired with the count of dropped events per coalesce window. */\n onDropBatch?: (droppedCount: number, sampleEventName: string | null) => void;\n}\n\nexport class RateLimiter {\n private tokens: number;\n private readonly capacity: number;\n private readonly refillPerSecond: number;\n private lastRefillMs: number;\n\n private droppedThisWindow = 0;\n private firstDroppedName: string | null = null;\n private windowFlushTimer: ReturnType<typeof setTimeout> | null = null;\n private readonly windowMs: number;\n private readonly onDropBatch?: (droppedCount: number, sampleEventName: string | null) => void;\n\n constructor(options: RateLimiterOptions = {}) {\n this.capacity = Math.max(1, options.burstCapacity ?? 100);\n this.refillPerSecond = Math.max(1, options.refillPerSecond ?? 20);\n this.windowMs = options.dropCoalesceWindowMs ?? 1000;\n this.onDropBatch = options.onDropBatch;\n this.tokens = this.capacity;\n this.lastRefillMs = Date.now();\n }\n\n /**\n * Try to consume 1 token. Returns true if allowed, false if throttled.\n * Throttled events are coalesced into a single onDropBatch callback per window.\n */\n tryConsume(eventName: string): boolean {\n this.refill();\n\n if (this.tokens >= 1) {\n this.tokens -= 1;\n return true;\n }\n\n this.droppedThisWindow += 1;\n if (this.firstDroppedName === null) {\n this.firstDroppedName = eventName;\n logger.warn(\n `[Aegis] Rate limit reached — dropping event \"${eventName}\" and any further events for ${this.windowMs}ms. Burst=${this.capacity}, refill=${this.refillPerSecond}/s.`,\n );\n }\n\n if (!this.windowFlushTimer) {\n this.windowFlushTimer = setTimeout(() => this.flushWindow(), this.windowMs);\n }\n\n return false;\n }\n\n /** Diagnostics — current token count (rounded down). */\n getAvailableTokens(): number {\n this.refill();\n return Math.floor(this.tokens);\n }\n\n /** Cleanup. Idempotent. */\n destroy(): void {\n if (this.windowFlushTimer) {\n clearTimeout(this.windowFlushTimer);\n this.windowFlushTimer = null;\n }\n this.flushWindow();\n }\n\n // ---------------------------------------------------------------------\n\n private refill(): void {\n const now = Date.now();\n const elapsedSec = (now - this.lastRefillMs) / 1000;\n if (elapsedSec <= 0) return;\n\n const newTokens = elapsedSec * this.refillPerSecond;\n this.tokens = Math.min(this.capacity, this.tokens + newTokens);\n this.lastRefillMs = now;\n }\n\n private flushWindow(): void {\n if (this.windowFlushTimer) {\n clearTimeout(this.windowFlushTimer);\n this.windowFlushTimer = null;\n }\n if (this.droppedThisWindow > 0 && this.onDropBatch) {\n try {\n this.onDropBatch(this.droppedThisWindow, this.firstDroppedName);\n } catch (err) {\n logger.warn('[Active Reach] RateLimiter.onDropBatch threw:', err);\n }\n }\n this.droppedThisWindow = 0;\n this.firstDroppedName = null;\n }\n}\n","/**\n * MurmurHash3 x86-32 — deterministic, byte-identical with Python `mmh3.hash`.\n *\n * We hand-roll rather than pull a dep because (a) our bundle-budget is\n * aggressive for storefront first-paint, and (b) this is the authoritative\n * cross-language hash for the event-governance bloom filter — any behavior\n * drift between Python and JS produces silent FP-rate spikes in prod.\n *\n * Contract:\n * • Input is a JS string. It is UTF-8 encoded via TextEncoder before hashing.\n * • Output is an unsigned 32-bit integer (0 .. 2^32-1).\n * • Matches `mmh3.hash(s.encode('utf-8'), seed=seed, signed=False)` in\n * Python exactly — verified by libs/shared-types/bloom-test-vectors.json\n * in CI.\n *\n * Reference: Austin Appleby, MurmurHash3 (public domain).\n * https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\n *\n * Implementation notes:\n * • All arithmetic on 32-bit ints. JS does bitwise ops on 32-bit ints natively;\n * `Math.imul` gives us correct 32-bit multiplication without overflow.\n * • We use `>>> 0` to coerce to unsigned after every step that could produce\n * a negative or >32-bit value.\n * • UTF-8 encoding matters — a naive `charCodeAt` loop would mis-hash any\n * string with non-ASCII characters (e.g. emoji, accented chars) and the\n * Python side would disagree. We always encode via TextEncoder first.\n */\n\nconst C1 = 0xcc9e2d51;\nconst C2 = 0x1b873593;\n\n/**\n * Compute MurmurHash3 x86-32 of a UTF-8 encoded string.\n *\n * @param input The string to hash.\n * @param seed 32-bit unsigned seed.\n * @returns Unsigned 32-bit integer hash.\n */\nexport function murmurhash3_x86_32(input: string, seed: number = 0): number {\n // Encode to UTF-8 bytes — matches Python's `s.encode('utf-8')`.\n const bytes = new TextEncoder().encode(input);\n return murmurhash3_bytes(bytes, seed);\n}\n\n/**\n * Compute MurmurHash3 x86-32 over a byte buffer directly.\n *\n * Exposed for callers that already have UTF-8 bytes and want to skip the\n * encode step (e.g., internal bloom-hash paths).\n */\nexport function murmurhash3_bytes(bytes: Uint8Array, seed: number = 0): number {\n const len = bytes.length;\n const nBlocks = Math.floor(len / 4);\n\n let h1 = seed >>> 0;\n\n // Body — consume 4-byte blocks.\n for (let i = 0; i < nBlocks; i++) {\n const offset = i * 4;\n let k1 =\n bytes[offset] |\n (bytes[offset + 1] << 8) |\n (bytes[offset + 2] << 16) |\n (bytes[offset + 3] << 24);\n\n k1 = Math.imul(k1, C1);\n k1 = (k1 << 15) | (k1 >>> 17);\n k1 = Math.imul(k1, C2);\n\n h1 ^= k1;\n h1 = (h1 << 13) | (h1 >>> 19);\n h1 = (Math.imul(h1, 5) + 0xe6546b64) >>> 0;\n }\n\n // Tail — up to 3 trailing bytes.\n const tailStart = nBlocks * 4;\n let k1 = 0;\n const tailLen = len - tailStart;\n if (tailLen === 3) k1 ^= bytes[tailStart + 2] << 16;\n if (tailLen >= 2) k1 ^= bytes[tailStart + 1] << 8;\n if (tailLen >= 1) {\n k1 ^= bytes[tailStart];\n k1 = Math.imul(k1, C1);\n k1 = (k1 << 15) | (k1 >>> 17);\n k1 = Math.imul(k1, C2);\n h1 ^= k1;\n }\n\n // Finalization — avalanche (fmix32).\n h1 ^= len;\n h1 ^= h1 >>> 16;\n h1 = Math.imul(h1, 0x85ebca6b);\n h1 ^= h1 >>> 13;\n h1 = Math.imul(h1, 0xc2b2ae35);\n h1 ^= h1 >>> 16;\n\n return h1 >>> 0;\n}\n","/**\n * Bloom filter — wire-compatible with the Python builder in\n * apps/control-plane/app/services/event_governance_bloom.py\n *\n * This SDK-side variant is QUERY-ONLY. The filter is built server-side\n * from the org's registered event-name set, base64-encoded, and shipped\n * to the browser on `/v1/sdk/bootstrap`. The SDK reads it once, uses it\n * to gate `track()` calls, and discards it on the next bootstrap.\n *\n * Wire format:\n * • `m` bits of storage, represented as m/8 bytes, base64-encoded.\n * • Bit `i` is at byte `i >> 3`, bitmask `1 << (i & 7)` — LSB-first.\n * • `m` MUST be a power of two — allows `idx & (m-1)` modulo.\n * • `k` hash functions synthesized via Kirsch-Mitzenmacher from two\n * MurmurHash3 x86-32 hashes with seeds (seedA, seedB):\n * h_i(x) = (h1(x) + i * h2(x)) mod m\n * • Input strings are UTF-8 encoded (handled inside murmurhash3_x86_32).\n */\n\nimport { murmurhash3_x86_32 } from './murmur3';\n\n/** Decode a base64 string to a Uint8Array. Accepts web (atob) and node (Buffer). */\nfunction base64ToBytes(b64: string): Uint8Array {\n if (typeof atob !== 'undefined') {\n const bin = atob(b64);\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n }\n // Node.js fallback — used by tests and SSR integrations.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const B = (globalThis as any).Buffer;\n if (B && typeof B.from === 'function') {\n const buf: Uint8Array = B.from(b64, 'base64');\n // Copy into a plain Uint8Array so callers don't observe Buffer semantics.\n return new Uint8Array(buf);\n }\n throw new Error('No base64 decoder available (neither atob nor Buffer)');\n}\n\nexport interface BloomFilterParams {\n /** Bits in the filter (must be power of 2). */\n m: number;\n /** Number of hash functions (Kirsch-Mitzenmacher synthesized). */\n k: number;\n /** Seed for the first MurmurHash3 call. */\n seedA: number;\n /** Seed for the second MurmurHash3 call. */\n seedB: number;\n}\n\nexport class BloomFilter {\n private readonly buf: Uint8Array;\n private readonly mask: number;\n\n constructor(buf: Uint8Array, private readonly params: BloomFilterParams) {\n if ((params.m & (params.m - 1)) !== 0) {\n throw new Error(`Bloom filter m must be a power of 2, got ${params.m}`);\n }\n if (buf.length !== params.m >> 3) {\n throw new Error(\n `Bloom filter buffer size mismatch: expected ${params.m >> 3} bytes, got ${buf.length}`\n );\n }\n this.buf = buf;\n this.mask = params.m - 1;\n }\n\n /** Build from the wire format (base64 bytes + explicit params). */\n static fromBase64(bloomB64: string, params: BloomFilterParams): BloomFilter {\n const bytes = base64ToBytes(bloomB64);\n return new BloomFilter(bytes, params);\n }\n\n /**\n * Returns true if `name` is probably in the set — possibly with the\n * filter's configured false-positive rate. FALSE is always authoritative.\n *\n * FP here means: SDK thinks a name is already registered when it isn't.\n * That costs one wasted server round-trip (gateway does the exact check\n * and catches it) — strictly safer than a false-negative, which could\n * leak a novel name past the SDK.\n */\n has(name: string): boolean {\n const h1 = murmurhash3_x86_32(name, this.params.seedA);\n const h2 = murmurhash3_x86_32(name, this.params.seedB);\n\n for (let i = 0; i < this.params.k; i++) {\n // >>> 0 at each step to keep arithmetic in unsigned 32-bit land;\n // we can't just take & mask at the end because h1 + i*h2 could\n // overflow past 2^32 when i*h2 is large.\n const combined = (h1 + Math.imul(i, h2)) >>> 0;\n const idx = combined & this.mask;\n const bit = this.buf[idx >> 3] & (1 << (idx & 7));\n if (bit === 0) return false;\n }\n return true;\n }\n}\n","/**\n * NameGovernor — client-side event-name cap enforcement.\n *\n * The governor consumes an `EventGovernanceHint` from the bootstrap response\n * and decides whether to let a `track()` call proceed to the network or drop\n * it locally.\n *\n * Why this exists:\n * Without it, a page that calls `track('new_btn_' + Date.now())` in a\n * render loop would (a) pass the local rate-limiter if within burst, (b)\n * force the gateway to ask control-plane for a verdict on every unique\n * name, and (c) amplify the CP `/event-governance/check` load quadratically\n * in the number of tabs firing the same bug. The bloom filter gives the\n * SDK enough information to drop novel names locally once the org hits\n * its cap, collapsing the amplification to zero.\n *\n * Design constraints:\n * • FAIL-OPEN — missing hint (Enterprise org, Redis outage) lets every\n * name through. The gateway is still the authoritative cap.\n * • FALSE-POSITIVE-SAFE — if the bloom says \"known\" for a name that isn't\n * actually registered, we send to gateway and the gateway's exact check\n * catches it. FPs cost one round trip, not a cap bypass.\n * • LOCAL-MEMO — a novel name that has already charged `remainingNewNames`\n * in this tab must NOT charge again on subsequent calls within the same\n * hint TTL. Without this, `track('new_btn_click')` fired 50 times would\n * drain 50 from the counter and block every OTHER legitimate novel name.\n * See `localNovelNames`.\n *\n * See docs/architecture/RFC_2026_04_SDK_GOVERNANCE_HINTS.md §3.3.\n */\n\nimport { BloomFilter } from './bloom-filter';\n\n/** Shape of the hint delivered by `/v1/sdk/bootstrap`. */\nexport interface EventGovernanceHint {\n bloom_algo: string;\n seed_a: number;\n seed_b: number;\n k: number;\n m: number;\n bloom_b64: string;\n remaining_new_names: number | null;\n /**\n * When true, the server is in its 7-day soft-cap grace window: it accepts\n * novel event names past the cap. SDK must NOT drop locally in this mode —\n * doing so would enforce harder than the server. See\n * apps/control-plane/app/schemas/event_governance_hint.py for the canonical\n * contract.\n */\n grace_active?: boolean;\n ttl_seconds: number;\n}\n\nexport interface DropReport {\n /** Map of event_name → count of local drops since last drain. */\n events: Record<string, number>;\n /** Total events dropped across all names (sum of values). */\n total: number;\n /** When the report window started (ms since epoch). */\n since: number;\n}\n\n/** Matches the `bloom_algo` tag shipped by the current control-plane build. */\nconst SUPPORTED_ALGO = 'mmh3_x86_32_km';\n\n/**\n * Best-effort console.warn — coalesced so a runaway render loop doesn't\n * flood the dev console. We only warn on the FIRST distinct dropped name;\n * subsequent names silently increment the telemetry counter.\n */\nfunction warnOncePerSession(message: string): void {\n if (typeof console === 'undefined' || typeof console.warn !== 'function') return;\n console.warn(message);\n}\n\nexport class NameGovernor {\n private bloom: BloomFilter | null = null;\n private remainingNewNames: number = Infinity;\n private graceActive: boolean = false;\n\n /**\n * Names this SDK instance has seen AS NOVEL and already charged against\n * `remainingNewNames`. Reset on every `ingestHint()` so we don't leak\n * accounting across hint refreshes.\n */\n private localNovelNames: Set<string> = new Set();\n\n /** Coalesced telemetry — flushed to the transport via drainDropReport(). */\n private droppedSinceLastReport: Map<string, number> = new Map();\n private reportWindowStart: number = Date.now();\n private hasWarnedThisSession = false;\n\n /**\n * Ingest a freshly-bootstrapped hint. Call on every successful bootstrap.\n * Passing `null` disables governance (fail-open).\n */\n ingestHint(hint: EventGovernanceHint | null): void {\n if (!hint || hint.bloom_algo !== SUPPORTED_ALGO) {\n // Unknown algo — a newer CP ships a version this SDK can't decode.\n // Silently disable governance; gateway remains authoritative.\n this.bloom = null;\n this.remainingNewNames = Infinity;\n this.graceActive = false;\n this.localNovelNames.clear();\n return;\n }\n\n try {\n this.bloom = BloomFilter.fromBase64(hint.bloom_b64, {\n m: hint.m,\n k: hint.k,\n seedA: hint.seed_a,\n seedB: hint.seed_b,\n });\n } catch {\n // Malformed hint — fail-open. Logged by the caller.\n this.bloom = null;\n }\n\n this.remainingNewNames = hint.remaining_new_names ?? Infinity;\n this.graceActive = hint.grace_active === true;\n this.localNovelNames.clear();\n }\n\n /**\n * Decide whether a `track()` call should proceed.\n *\n * Returns true = send to network (rate-limiter still runs after).\n * Returns false = drop locally; caller should return early.\n */\n shouldSend(eventName: string): boolean {\n // No hint = unlimited plan or fail-open. Send everything.\n if (!this.bloom) return true;\n\n // 7-day soft-cap grace window is active — server accepts novel names\n // past the cap, so SDK must not enforce harder. Fail-open to gateway.\n if (this.graceActive) return true;\n\n // Known name — send.\n if (this.bloom.has(eventName)) return true;\n\n // Already charged in this session — send (gateway has it cached too).\n if (this.localNovelNames.has(eventName)) return true;\n\n // Novel name, within headroom — charge once, send.\n if (this.remainingNewNames > 0) {\n this.localNovelNames.add(eventName);\n this.remainingNewNames -= 1;\n return true;\n }\n\n // Novel name, over the cap — drop locally, record for telemetry.\n const prev = this.droppedSinceLastReport.get(eventName) ?? 0;\n this.droppedSinceLastReport.set(eventName, prev + 1);\n\n // Only warn once per session, only on the first distinct dropped name —\n // a runaway loop will fill droppedSinceLastReport silently.\n if (!this.hasWarnedThisSession) {\n this.hasWarnedThisSession = true;\n warnOncePerSession(\n `[aegis] Event-name cap reached — \"${eventName}\" dropped locally. ` +\n `Upgrade your plan or remove dynamically-generated event names.`\n );\n }\n return false;\n }\n\n /**\n * Snapshot + reset the dropped-names counter. Called by the telemetry\n * beacon on batch flush so the gateway gets visibility into client-side\n * drops for ops dashboards.\n */\n drainDropReport(): DropReport | null {\n if (this.droppedSinceLastReport.size === 0) return null;\n\n const events: Record<string, number> = {};\n let total = 0;\n for (const [name, count] of this.droppedSinceLastReport) {\n events[name] = count;\n total += count;\n }\n\n const since = this.reportWindowStart;\n this.droppedSinceLastReport.clear();\n this.reportWindowStart = Date.now();\n\n return { events, total, since };\n }\n\n // Test-only accessors — not part of public API but easier than reflection.\n /** @internal */\n _debugState(): {\n hasBloom: boolean;\n remaining: number;\n localNovel: number;\n graceActive: boolean;\n } {\n return {\n hasBloom: this.bloom !== null,\n remaining: this.remainingNewNames,\n localNovel: this.localNovelNames.size,\n graceActive: this.graceActive,\n };\n }\n}\n","/**\n * TraitGovernor — client-side guards for trait writes (P10.5).\n *\n * Runs the same 5 ingestion guards client-side that the cell-plane backend\n * runs at the trait-write chokepoint (`record_attribute_keys`), so the SDK\n * can warn developers about problematic keys/values BEFORE network send.\n *\n * Why two chokepoints:\n * - **Backend** (`record_attribute_keys` → DLQ): authoritative; rejects\n * payloads that bypass the SDK (direct HTTP, CSV import, integration\n * workers). Operators see rejections via /Validation tab.\n * - **SDK** (this file): developer-facing; warns in console.warn at dev\n * time so the issue is caught BEFORE the bad data hits production. We\n * normalize what we can (key casing, value truncation, date format)\n * and warn-then-drop what we can't (reserved prefixes).\n *\n * \"First-3 per verdict per session\" — we warn at most 3 times for each\n * verdict per `(workspace_id, verdict)` key in any given page load. After\n * that, silent — the developer got the message; further warnings would\n * just spam the console and obscure other issues.\n *\n * Canonical contract: the verdict codes here MUST match\n * `data_governance.ingestion_guards.GuardResult.verdict` codes + the\n * `ingestion_dlq.verdict` DB CHECK + the FE `IngestionDLQVerdict` union.\n * Real-env smoke asserts BE ↔ DB drift; TS narrowing catches FE drift;\n * this file completes the four-surface drift protection.\n */\n\nexport type TraitVerdict =\n | 'bad_key_format'\n | 'value_too_long'\n | 'bad_date_format'\n | 'name_too_long' // not used here (event-name guard); kept in union for symmetry\n | 'reserved_prefix';\n\nexport interface TraitDrop {\n /** Original key as the developer wrote it. */\n originalKey: string;\n /** What the guard decided. */\n verdict: TraitVerdict;\n /** Human-readable reason — same text we'd log to ingestion_dlq.reason. */\n reason: string;\n}\n\nexport interface TraitResult {\n /**\n * Sanitized traits to actually send. Reserved-prefix keys are absent;\n * camelCase keys are rewritten to snake_case; long values truncated;\n * ISO dates → epoch ms.\n */\n sanitized: Record<string, unknown>;\n /** Keys that were dropped or modified (one row per change). */\n drops: TraitDrop[];\n}\n\n// Reserved namespace prefixes — mirrors\n// data_governance.ingestion_guards.DEFAULT_RESERVED_PREFIXES.\nconst RESERVED_PREFIXES: readonly string[] = [\n 'system.',\n 'user.',\n 'loyalty.',\n 'review.',\n 'cart.',\n 'checkout.',\n 'product.',\n 'pos.',\n 'bill.',\n 'feedback.',\n 'chat.',\n 'delivery.',\n 'event.',\n '$',\n '_',\n];\n\n// PII not detected here — that's done server-side by the canonical\n// is_likely_pii helper. SDK side handles only normalization + format\n// rejections that benefit from early warning.\n\nconst KEY_SOFT_VALUE_CAP = 512; // truncate\nconst KEY_HARD_VALUE_CAP = 10_000; // reject (DoS protection)\n\nconst CAMEL_BOUNDARY_1 = /(.)([A-Z][a-z]+)/g;\nconst CAMEL_BOUNDARY_2 = /([a-z0-9])([A-Z])/g;\nconst NON_SNAKE = /[\\s\\-.]+/g;\nconst DUPE_UNDERSCORE = /_+/g;\nconst ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?(Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\nconst DATE_KEY_HINTS = new Set([\n 'at', 'on', 'date', 'time', 'timestamp', 'dob', 'birthday',\n 'joined', 'expired', 'expires', 'created', 'updated', 'started', 'ended',\n]);\n\n/**\n * Normalize camelCase / PascalCase / dash-or-space-separated keys to\n * snake_case. Returns `null` if the key reduces to empty (caller treats\n * as `bad_key_format`).\n */\nfunction normalizeKey(key: string): string | null {\n if (!key) return null;\n const stage1 = key.replace(CAMEL_BOUNDARY_1, '$1_$2').replace(CAMEL_BOUNDARY_2, '$1_$2');\n const stage2 = stage1.replace(NON_SNAKE, '_').toLowerCase();\n const result = stage2.replace(DUPE_UNDERSCORE, '_').replace(/^_+|_+$/g, '');\n return result || null;\n}\n\nfunction startsWithReserved(key: string): string | null {\n const lower = key.toLowerCase();\n for (const prefix of RESERVED_PREFIXES) {\n if (lower.startsWith(prefix)) return prefix;\n }\n return null;\n}\n\nfunction looksLikeDateKey(key: string): boolean {\n for (const part of key.toLowerCase().split(/[_\\-.\\s]+/)) {\n if (DATE_KEY_HINTS.has(part)) return true;\n }\n return false;\n}\n\n/**\n * ISO-8601 / epoch string → epoch ms (number). Returns null on parse\n * failure. Numeric inputs assumed epoch (seconds if <1e11, else ms).\n */\nfunction parseDateValue(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return value < 1e11 ? Math.floor(value * 1000) : Math.floor(value);\n }\n if (typeof value === 'string' && value) {\n const s = value.trim();\n if (ISO_8601.test(s)) {\n const ms = Date.parse(s);\n if (!Number.isNaN(ms)) return ms;\n }\n const asNum = Number(s);\n if (Number.isFinite(asNum)) {\n return asNum < 1e11 ? Math.floor(asNum * 1000) : Math.floor(asNum);\n }\n }\n return null;\n}\n\nfunction warnOnConsole(message: string): void {\n if (typeof console === 'undefined' || typeof console.warn !== 'function') return;\n console.warn(message);\n}\n\nexport class TraitGovernor {\n /** Cap warnings at 3 per (workspace_id, verdict) per session. */\n private static readonly WARN_CAP = 3;\n /** workspace_id || '__no_workspace__' → verdict → count. */\n private warnCounts: Map<string, Map<TraitVerdict, number>> = new Map();\n\n /**\n * Apply all guards to a traits object. Returns the sanitized payload\n * to send + a list of drops/modifications.\n *\n * Pass `workspace_id` so warning rate-limits scope per workspace; an\n * agency user switching workspaces won't have their warning budget\n * consumed cross-workspace.\n */\n process(\n traits: Record<string, unknown> | null | undefined,\n workspace_id?: string | null,\n ): TraitResult {\n const sanitized: Record<string, unknown> = {};\n const drops: TraitDrop[] = [];\n if (!traits || typeof traits !== 'object') {\n return { sanitized, drops };\n }\n\n for (const rawKey of Object.keys(traits)) {\n // 1. Reserved-prefix check runs on the ORIGINAL key, BEFORE\n // normalization. Otherwise `system.foo` would normalize to\n // `system_foo` (the `.` collapses to `_`) and bypass the\n // reserved check. This is the same order-of-operations bug\n // we caught at smoke time on the backend (2026-05-13).\n const prefix = startsWithReserved(rawKey);\n if (prefix !== null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'reserved_prefix',\n reason: `key uses reserved namespace ${JSON.stringify(prefix)}`,\n });\n continue;\n }\n\n // 2. Normalize key casing.\n const normalizedKey = normalizeKey(rawKey);\n if (normalizedKey === null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'bad_key_format',\n reason: `key reduced to empty after normalization`,\n });\n continue;\n }\n\n // 3. Re-check reserved on normalized form (belt-and-braces).\n if (startsWithReserved(normalizedKey) !== null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'reserved_prefix',\n reason: `normalized key ${JSON.stringify(normalizedKey)} still uses a reserved namespace`,\n });\n continue;\n }\n\n // 4. Value-side guards.\n let value = traits[rawKey];\n\n // 4a. Truncate long strings (soft cap); reject huge strings (hard cap).\n if (typeof value === 'string') {\n if (value.length > KEY_HARD_VALUE_CAP) {\n drops.push({\n originalKey: rawKey,\n verdict: 'value_too_long',\n reason: `value length ${value.length} exceeds hard cap ${KEY_HARD_VALUE_CAP}`,\n });\n continue;\n }\n if (value.length > KEY_SOFT_VALUE_CAP) {\n drops.push({\n originalKey: rawKey,\n verdict: 'value_too_long',\n reason: `value truncated from ${value.length} to ${KEY_SOFT_VALUE_CAP} chars`,\n });\n value = value.slice(0, KEY_SOFT_VALUE_CAP);\n }\n }\n\n // 4b. Date normalization — only when the key hints at date semantics\n // AND the value looks string-shaped. Numeric values are assumed\n // epoch already. Free-form strings on non-date-keyed properties\n // pass through (caller meant a string).\n if (looksLikeDateKey(normalizedKey) && typeof value === 'string') {\n const parsed = parseDateValue(value);\n if (parsed === null) {\n drops.push({\n originalKey: rawKey,\n verdict: 'bad_date_format',\n reason: `value ${JSON.stringify(value)} on date-keyed field ${JSON.stringify(normalizedKey)} didn't parse as ISO-8601 / epoch`,\n });\n continue;\n }\n value = parsed;\n }\n\n sanitized[normalizedKey] = value;\n }\n\n // Emit first-3-per-verdict warnings (silent past the cap).\n for (const drop of drops) {\n this.maybeWarn(workspace_id ?? null, drop);\n }\n\n return { sanitized, drops };\n }\n\n private maybeWarn(workspace_id: string | null, drop: TraitDrop): void {\n const ws = workspace_id ?? '__no_workspace__';\n let perVerdict = this.warnCounts.get(ws);\n if (!perVerdict) {\n perVerdict = new Map();\n this.warnCounts.set(ws, perVerdict);\n }\n const count = perVerdict.get(drop.verdict) ?? 0;\n if (count >= TraitGovernor.WARN_CAP) return;\n perVerdict.set(drop.verdict, count + 1);\n warnOnConsole(\n `[Active Reach SDK] trait ${drop.verdict}: ${drop.reason} ` +\n `(original key: ${JSON.stringify(drop.originalKey)}). ` +\n `Backend will reject; fix the SDK call to silence this warning.`,\n );\n }\n}\n","/**\n * UserNamespace — typed PII + per-channel opt-in setters that delegate to\n * `Aegis.identify()`. WebEngage-/CleverTap-parity ergonomics.\n *\n * Pre-login `setX` calls accumulate in `pendingTraits` and flush as one\n * identify() when `login()` fires — avoids polluting the contact graph\n * with anonymous-id-as-userId rows.\n *\n * See docs/architecture/SDK_USER_NAMESPACE_AND_SCREEN.md.\n */\n\nimport type { Aegis } from './analytics';\nimport { logger } from '../utils/logger';\n\nexport type OptInChannel =\n | 'email'\n | 'sms'\n | 'push'\n | 'webpush'\n | 'whatsapp'\n | 'rcs'\n | 'inapp';\n\nconst VALID_CHANNELS: ReadonlySet<OptInChannel> = new Set<OptInChannel>([\n 'email',\n 'sms',\n 'push',\n 'webpush',\n 'whatsapp',\n 'rcs',\n 'inapp',\n]);\n\n// Conservative RFC 5322 subset — same shape every CEP peer uses on the\n// browser side. Server-side validation is authoritative.\nconst EMAIL_RE = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n// E.164: leading '+' then 8–15 digits.\nconst PHONE_RE = /^\\+[1-9]\\d{7,14}$/;\n// SHA-256 hex string: exactly 64 hex chars.\nconst SHA256_HEX_RE = /^[a-f0-9]{64}$/i;\n// ISO date: YYYY-MM-DD (we don't accept full ISO datetime — birth dates\n// don't have a time component).\nconst ISO_DATE_RE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nexport class UserNamespace {\n private readonly aegis: Aegis;\n private pendingTraits: Record<string, unknown> = {};\n\n constructor(aegis: Aegis) {\n this.aegis = aegis;\n }\n\n /**\n * Authoritative identity write. Flushes any pending pre-login traits\n * along with the supplied `traits` argument as a single identify call.\n */\n login(userId: string, traits?: Record<string, unknown>): void {\n if (typeof userId !== 'string' || userId.length === 0) {\n logger.warn('[user.login] userId must be a non-empty string');\n return;\n }\n const merged = { ...this.pendingTraits, ...(traits || {}) };\n this.pendingTraits = {};\n this.aegis.identify(userId, merged);\n }\n\n /**\n * Drops the current userId and any pending pre-login traits. Does NOT\n * fire a server-side logout event — that's a separate explicit `track`.\n */\n logout(): void {\n this.pendingTraits = {};\n this.aegis.reset();\n }\n\n setAttribute(key: string, value: unknown): void {\n if (typeof key !== 'string' || key.length === 0) {\n logger.warn('[user.setAttribute] key must be a non-empty string');\n return;\n }\n this.writeTraits({ [key]: value });\n }\n\n setAttributes(map: Record<string, unknown>): void {\n if (!map || typeof map !== 'object') {\n logger.warn('[user.setAttributes] map must be an object');\n return;\n }\n this.writeTraits(map);\n }\n\n setEmail(email: string): void {\n if (!EMAIL_RE.test(email)) {\n logger.warn('[user.setEmail] invalid email format');\n return;\n }\n this.writeTraits({ email: email.toLowerCase() });\n }\n\n setPhone(phone: string): void {\n if (!PHONE_RE.test(phone)) {\n logger.warn('[user.setPhone] phone must be E.164 (e.g. +15551234567)');\n return;\n }\n this.writeTraits({ phone });\n }\n\n setHashedEmail(sha256Hex: string): void {\n if (!SHA256_HEX_RE.test(sha256Hex)) {\n logger.warn('[user.setHashedEmail] expected 64-char hex SHA-256');\n return;\n }\n this.writeTraits({ email_sha256: sha256Hex.toLowerCase() });\n }\n\n setHashedPhone(sha256Hex: string): void {\n if (!SHA256_HEX_RE.test(sha256Hex)) {\n logger.warn('[user.setHashedPhone] expected 64-char hex SHA-256');\n return;\n }\n this.writeTraits({ phone_sha256: sha256Hex.toLowerCase() });\n }\n\n setBirthDate(iso: string): void {\n if (!ISO_DATE_RE.test(iso)) {\n logger.warn('[user.setBirthDate] expected YYYY-MM-DD');\n return;\n }\n this.writeTraits({ birth_date: iso });\n }\n\n setOptIn(channel: OptInChannel, granted: boolean): void {\n if (!VALID_CHANNELS.has(channel)) {\n logger.warn(`[user.setOptIn] unknown channel: ${channel}`);\n return;\n }\n if (typeof granted !== 'boolean') {\n logger.warn('[user.setOptIn] granted must be a boolean');\n return;\n }\n this.writeTraits({ [`opt_in_${channel}`]: granted });\n }\n\n /**\n * HMAC-signed identity token. Ingress-side verification not yet wired —\n * the trait is forwarded as-is and persisted on the contact record\n * until that lands.\n */\n setSecureToken(token: string): void {\n if (typeof token !== 'string' || token.length === 0) {\n logger.warn('[user.setSecureToken] token must be a non-empty string');\n return;\n }\n this.writeTraits({ _secure_token: token });\n }\n\n /**\n * Test/inspection hook — returns a copy of the pending traits buffer.\n * Used by the e2e smoke test to assert pre-login accumulation.\n */\n _getPendingTraits(): Record<string, unknown> {\n return { ...this.pendingTraits };\n }\n\n // ---------------------------------------------------------------------------\n\n private writeTraits(traits: Record<string, unknown>): void {\n const userId = this.aegis.getUserId();\n if (userId) {\n this.aegis.identify(userId, traits);\n return;\n }\n Object.assign(this.pendingTraits, traits);\n }\n}\n","import {\n AegisConfig,\n AegisConfigInternal,\n DEFAULT_CONFIG,\n CellEndpoint,\n} from '../types/config';\nimport {\n TrackEvent,\n IdentifyEvent,\n PageEvent,\n GroupEvent,\n AliasEvent,\n ScreenEvent,\n AegisEvent,\n} from '../types/events';\nimport { Plugin } from '../types/plugin';\nimport { Identity } from '../utils/identity';\nimport { SessionManager } from './session';\nimport { EventQueue } from './queue';\nimport { Transport } from './transport';\nimport { Storage } from '../utils/storage';\nimport { buildContext, isBot } from '../utils/device';\nimport { generateMessageId } from '../utils/uuid';\nimport { logger } from '../utils/logger';\nimport { PluginRegistry } from '../plugins/registry';\nimport { ConsentManager, ConsentPreferences, ConsentCategory } from '../utils/consent';\nimport { captureMetaCookies } from '../utils/meta-cookies';\nimport { parseAdClickIDs, hasAdClickIDs } from '../utils/url-parser';\nimport { EcommerceTracker } from '../ecommerce';\nimport { RateLimiter } from './rate-limiter';\nimport { NameGovernor, EventGovernanceHint, TraitGovernor } from '../governance';\nimport { UserNamespace } from './user-namespace';\n\nexport class Aegis {\n private config: AegisConfigInternal | null = null;\n private storage!: Storage;\n private identity!: Identity;\n private session: SessionManager | null = null;\n private queue: EventQueue | null = null;\n private transport: Transport | null = null;\n private plugins: PluginRegistry = new PluginRegistry();\n private initPromise: Promise<void> | null = null;\n private consent: ConsentManager | null = null;\n private _ecommerce: EcommerceTracker | null = null;\n private _user: UserNamespace | null = null;\n private rateLimiter: RateLimiter | null = null;\n private nameGovernor: NameGovernor = new NameGovernor();\n // P10.5 — client-side guards for trait writes. Mirrors the BE\n // chokepoint in `record_attribute_keys` so developers see console\n // warnings about reserved-prefix / bad-key / bad-date traits at\n // dev time, BEFORE the data hits production.\n private traitGovernor: TraitGovernor = new TraitGovernor();\n private _lastPageUrl: string | null = null;\n private _popstateHandler: (() => void) | null = null;\n private _originalPushState: typeof history.pushState | null = null;\n private _originalReplaceState: typeof history.replaceState | null = null;\n\n // B1.2 — last messageId per event name. Tenants who run their own Meta\n // Pixel via GTM call `aegis.lastEventId('Purchase')` to retrieve the id\n // we generated, then pass it as `eventID` to their `fbq('track', ...)`\n // call. Meta dedups on (pixel_id, event_name, event_id) so the\n // browser-fired Pixel event and our server-side CAPI event collapse to\n // one. See docs/integrations/META_PIXEL_COEXISTENCE.md.\n private _lastEventIds: Map<string, string> = new Map();\n\n // Plugin-handshake P1 — workspace_code allowlist for path-segment\n // detection. Ingested from the bootstrap response via\n // `ingestWorkspaceCodes()`. Empty set = no codes registered for this\n // org (single-outlet tenant); path-detection cascade is skipped.\n private _workspaceCodes: Set<string> = new Set();\n\n // Plugin-handshake P1 (Track E) — runtime workspace selection via\n // aegis.setWorkspace(). Persisted to sessionStorage for the page-load\n // session so SPA navigations + reloads on the same outlet inherit it.\n private _runtimeWorkspace: string | null = null;\n\n // P15.1 — runtime LOCATION selection (outlet code or id), via\n // aegis.setLocation(). Independent of _runtimeWorkspace under the\n // post-2026-05-26 hierarchy: workspace = brand-tenant, location =\n // outlet within a brand. Both can be set on the same SDK instance\n // (e.g. multi-brand agency dashboard navigating between brands +\n // selecting outlets within each).\n private _runtimeLocation: string | null = null;\n\n async init(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): Promise<void> {\n if (this.initPromise) {\n return this.initPromise;\n }\n\n this.initPromise = this._init(writeKey, config);\n return this.initPromise;\n }\n\n private async _init(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): Promise<void> {\n if (this.config?.initialized) {\n logger.warn('SDK already initialized');\n return;\n }\n\n if (this.shouldRespectDNT(config)) {\n logger.info('Do Not Track is enabled, SDK disabled');\n return;\n }\n\n if (isBot()) {\n logger.info('Bot detected, SDK disabled');\n return;\n }\n\n this.config = this.buildConfig(writeKey, config);\n\n if (this.config.debug) {\n logger.enable();\n }\n\n logger.info('Initializing Active Reach SDK...', this.config);\n\n this.storage = new Storage({\n cookieDomain: this.config.cookie_domain,\n secureCookie: this.config.secure_cookie,\n crossDomain: this.config.cross_domain_tracking,\n });\n\n this.identity = new Identity(this.storage);\n\n this.consent = new ConsentManager(this.storage, {\n defaultConsent: this.config.default_consent,\n waitForConsent: this.config.wait_for_consent,\n });\n\n if (this.config.integrate_onetrust) {\n this.consent.integrateOneTrust();\n }\n\n if (this.config.integrate_cookiebot) {\n this.consent.integrateCookiebot();\n }\n\n if (this.config.integrate_google_consent_mode) {\n this.consent.integrateGoogleConsentMode();\n }\n\n this.session = new SessionManager(\n this.storage,\n this.config.session_timeout\n );\n\n this.captureAdClickIDsOnInit();\n\n this.transport = new Transport(\n {\n writeKey: this.config.write_key,\n apiHost: this.config.api_host,\n cellEndpoints: this.config.cell_endpoints,\n preferredRegion: this.config.preferred_region,\n autoRegionDetection: this.config.auto_region_detection,\n requestTimeout: this.config.request_timeout,\n retryFailedRequests: this.config.retry_failed_requests,\n maxRetries: this.config.max_retries,\n retryBackoffMultiplier: this.config.retry_backoff_multiplier,\n },\n this.storage\n );\n\n this.config.active_cell = this.transport.getActiveCell();\n\n this.queue = new EventQueue(\n {\n batchSize: this.config.batch_size,\n batchInterval: this.config.batch_interval,\n enableOfflineMode: this.config.enable_offline_mode,\n maxOfflineEvents: this.config.max_offline_events,\n },\n this.transport,\n this.storage\n );\n\n this.rateLimiter = new RateLimiter({\n burstCapacity: this.config.rate_limit_burst,\n refillPerSecond: this.config.rate_limit_per_second,\n onDropBatch: (count, sampleName) => this.emitRateLimitMeta(count, sampleName),\n });\n\n await this.initializePlugins();\n\n if (this.config.auto_page_view) {\n this.page();\n this.startSPATracking();\n }\n\n logger.info('Active Reach SDK initialized successfully');\n }\n\n /**\n * SPA navigation tracking — monkey-patches pushState/replaceState and\n * listens for popstate to fire page() on every client-side route change.\n */\n private startSPATracking(): void {\n this._lastPageUrl = window.location.href;\n\n const onRouteChange = () => {\n // Debounce: only fire if URL actually changed\n const currentUrl = window.location.href;\n if (currentUrl === this._lastPageUrl) return;\n this._lastPageUrl = currentUrl;\n\n // Small delay to let the framework update document.title\n setTimeout(() => {\n this.page();\n }, 50);\n };\n\n // Listen for browser back/forward\n this._popstateHandler = onRouteChange;\n window.addEventListener('popstate', this._popstateHandler);\n\n // Monkey-patch pushState and replaceState\n this._originalPushState = history.pushState.bind(history);\n this._originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<typeof history.pushState>) => {\n this._originalPushState!(...args);\n onRouteChange();\n };\n\n history.replaceState = (...args: Parameters<typeof history.replaceState>) => {\n this._originalReplaceState!(...args);\n onRouteChange();\n };\n }\n\n private stopSPATracking(): void {\n if (this._popstateHandler) {\n window.removeEventListener('popstate', this._popstateHandler);\n this._popstateHandler = null;\n }\n\n if (this._originalPushState) {\n history.pushState = this._originalPushState;\n this._originalPushState = null;\n }\n\n if (this._originalReplaceState) {\n history.replaceState = this._originalReplaceState;\n this._originalReplaceState = null;\n }\n }\n\n private buildConfig(\n writeKey: string,\n config?: Partial<AegisConfig>\n ): AegisConfigInternal {\n return {\n ...DEFAULT_CONFIG,\n ...config,\n write_key: writeKey,\n initialized: true,\n } as AegisConfigInternal;\n }\n\n private shouldRespectDNT(config?: Partial<AegisConfig>): boolean {\n const respectDNT = config?.respect_dnt !== false;\n if (!respectDNT) return false;\n\n const dnt =\n navigator.doNotTrack ||\n (window as any).doNotTrack ||\n (navigator as any).msDoNotTrack;\n\n return dnt === '1' || dnt === 'yes';\n }\n\n private captureAdClickIDsOnInit(): void {\n if (!this.session) return;\n\n const adClickIDs = parseAdClickIDs();\n\n if (hasAdClickIDs(adClickIDs)) {\n this.session.setAdClickIDs(adClickIDs, window.location.href);\n logger.info('Ad click IDs captured on page load:', adClickIDs);\n }\n }\n\n private async initializePlugins(): Promise<void> {\n if (!this.config) return;\n\n for (const pluginName of this.config.plugins) {\n logger.debug(`Initializing plugin: ${pluginName}`);\n }\n\n await this.plugins.executeHook('init', this.config);\n }\n\n use(plugin: Plugin): void {\n this.plugins.register(plugin);\n\n if (this.config?.initialized && plugin.init) {\n Promise.resolve(plugin.init(this.config)).catch((error: any) => {\n logger.error(`Failed to initialize plugin \"${plugin.name}\":`, error);\n });\n }\n }\n\n /**\n * Symmetric counterpart to `use(plugin)`. Calls the plugin's `destroy()`\n * hook (if defined) and removes it from the registry so its hooks no\n * longer fire on subsequent events. Safe to call with a name that\n * isn't registered — logs a warning and returns.\n *\n * Primary use case: React components that register a plugin in\n * `useEffect` and need to unregister in the cleanup function so HMR /\n * provider remount doesn't leak listeners. See cashier-portal's\n * MetaPixelInjector for an example.\n */\n removePlugin(name: string): void {\n this.plugins.unregister(name);\n }\n\n /**\n * Plugin-handshake P1 — ingest the workspace_code allowlist from the\n * bootstrap response. Pass `result.workspaceCodes` from `bootstrap()`\n * here so the SDK can do path-segment workspace detection on\n * customer-facing pages like `/south/rewards`, `/ghatkopar/feedback`.\n *\n * Empty array is the right answer for single-outlet tenants — SDK\n * will skip the path cascade and fall through to query param /\n * setWorkspace() / gateway origin lookup.\n *\n * Safe to call before init() — the set is consulted at event-fire time.\n */\n ingestWorkspaceCodes(codes: string[] | null | undefined): void {\n this._workspaceCodes = new Set(\n (codes ?? []).filter((c) => typeof c === 'string' && c.length > 0),\n );\n logger.debug('Workspace codes ingested', { count: this._workspaceCodes.size });\n }\n\n /**\n * Plugin-handshake P1 (Track E) — explicitly set the workspace for\n * subsequent events. Used by:\n * - SPAs where workspace context changes via in-app routing\n * - Mobile / RN apps that have no URL\n * - Custom integrations that resolve workspace from their own state\n *\n * Pass null to clear (or call clearWorkspace()).\n *\n * Persisted to sessionStorage so SPA reloads / page transitions on the\n * same outlet inherit it without re-calling.\n */\n setWorkspace(codeOrId: string | null): void {\n this._runtimeWorkspace = codeOrId;\n if (typeof window !== 'undefined' && window.sessionStorage) {\n try {\n if (codeOrId) {\n window.sessionStorage.setItem('aegis_runtime_ws', codeOrId);\n } else {\n window.sessionStorage.removeItem('aegis_runtime_ws');\n }\n } catch {\n // Quota / private mode — non-fatal; in-memory value still applies.\n }\n }\n logger.debug('Runtime workspace set', { value: codeOrId });\n }\n\n /** Symmetric helper to clear the runtime workspace. */\n clearWorkspace(): void {\n this.setWorkspace(null);\n }\n\n // ── P15.1 — location (outlet) facade ─────────────────────────\n //\n // Under the post-2026-05-26 canonical hierarchy:\n // workspace_id = brand-tenant (one per brand)\n // location_id = outlet within a brand\n // The two are distinct dimensions. Both can be stamped on the\n // same event. These methods mirror the workspace facade but\n // resolve the outlet specifically.\n\n /**\n * P15.1 — ingest the outlet-code allowlist from bootstrap. The\n * SDK consults this set when reading the URL path segment to\n * decide whether `location.pathname.split('/')[1]` is a valid\n * outlet code vs a generic route.\n *\n * Empty array = single-outlet tenant; SDK skips the path-segment\n * cascade for location resolution.\n *\n * Safe to call before init(); the set is consulted at event-\n * fire time.\n */\n ingestLocationCodes(codes: string[] | null | undefined): void {\n // Shares the same underlying set as workspace codes — under the\n // new hierarchy the path segment IS the outlet code (was\n // workspace code pre-2026-05-26 when the dimensions collapsed).\n // The set is consulted by both `getPathWorkspaceCode()` and\n // `getPathLocationCode()` to avoid maintaining two copies of\n // the same allowlist.\n this._workspaceCodes = new Set(\n (codes ?? []).filter((c) => typeof c === 'string' && c.length > 0),\n );\n logger.debug('Location codes ingested', { count: this._workspaceCodes.size });\n }\n\n /**\n * P15.1 — explicitly set the active outlet for subsequent events.\n * Used by:\n * - Storefronts where outlet picker fires `aegis.setLocation()`\n * on selection (so events that follow carry that outlet's\n * location_id without the SDK having to re-parse the URL).\n * - SPAs where outlet context changes via in-app routing.\n * - Mobile/RN apps that have no URL.\n *\n * Pass null to clear (or call clearLocation()).\n *\n * Persisted to sessionStorage so SPA reloads + route transitions\n * on the same outlet inherit it without re-calling.\n */\n setLocation(codeOrId: string | null): void {\n this._runtimeLocation = codeOrId;\n if (typeof window !== 'undefined' && window.sessionStorage) {\n try {\n if (codeOrId) {\n window.sessionStorage.setItem('aegis_runtime_location', codeOrId);\n } else {\n window.sessionStorage.removeItem('aegis_runtime_location');\n }\n } catch {\n // Quota / private mode — non-fatal; in-memory value still applies.\n }\n }\n logger.debug('Runtime location set', { value: codeOrId });\n }\n\n /** Symmetric helper to clear the runtime location. */\n clearLocation(): void {\n this.setLocation(null);\n }\n\n /**\n * P15.1 — same logic as `getPathWorkspaceCode()` but expresses\n * the semantic correctly: under the new hierarchy, the first\n * path segment after the brand subdomain IS the outlet code.\n * Returns `undefined` when no outlet code matches the allowlist.\n */\n private getPathLocationCode(): string | undefined {\n // Same allowlist as workspace codes (single set, two names).\n return this.getPathWorkspaceCode();\n }\n\n /**\n * P15.1 — resolve the effective `location_id` for the next event.\n * Independent of `getEffectiveWorkspaceId()`: both run on every\n * event so workspace_id (brand) and location_id (outlet) land\n * on the same event when both are resolvable.\n *\n * Cascade (highest precedence first):\n * 1. `this.config.location_id` — explicit operator config.\n * 2. `?location=` query param (also accepts `?ws=` for back-\n * compat with storefronts that pass outlet via the old\n * param name — the value is the same; param naming only).\n * 3. URL path segment matching the outlet-code allowlist.\n * 4. Runtime `aegis.setLocation()` (in-memory).\n * 5. sessionStorage `aegis_runtime_location`.\n * 6. `undefined` — event-ingress treats as brand-tier\n * (no outlet attribution).\n */\n public getEffectiveLocationId(): string | undefined {\n // 1. Explicit operator config wins\n const configured = (this.config as { location_id?: string } | undefined)\n ?.location_id;\n if (configured) return configured;\n if (typeof window === 'undefined') return undefined;\n\n // 2. ?location= / ?ws= query param (both accepted)\n try {\n const search = new URLSearchParams(window.location.search);\n const loc = search.get('location') || search.get('ws');\n if (loc && loc.length > 0) return loc;\n } catch {\n /* malformed search — fall through */\n }\n\n // 3. URL path segment matching outlet-code allowlist\n const pathLoc = this.getPathLocationCode();\n if (pathLoc) return pathLoc;\n\n // 4. Runtime aegis.setLocation() value (in-memory)\n if (this._runtimeLocation) return this._runtimeLocation;\n\n // 5. sessionStorage fallback (set by prior setLocation() this session)\n try {\n if (window.sessionStorage) {\n const cached = window.sessionStorage.getItem('aegis_runtime_location');\n if (cached && cached.length > 0) return cached;\n }\n } catch {\n /* private mode — fall through */\n }\n\n // 6. No outlet resolvable — event is brand-tier\n return undefined;\n }\n\n /**\n * Inspect the URL's first path segment and return it if it's in the\n * org's workspace_code allowlist. Skips well-known non-workspace prefixes\n * (e.g. `b` for bill short codes; `s` is the storefront app slug).\n *\n * Returns undefined when:\n * - window is undefined (SSR / Node)\n * - path is empty / root\n * - first segment isn't in `_workspaceCodes` (e.g. /products, /cart)\n *\n * The returned value is a workspace CODE (slug like \"south\"), not a\n * UUID. The gateway normalizes code → UUID server-side via\n * workspace_subaccounts lookup with Redis cache.\n */\n private getPathWorkspaceCode(): string | undefined {\n if (typeof window === 'undefined') return undefined;\n if (this._workspaceCodes.size === 0) return undefined;\n try {\n const segments = window.location.pathname.split('/').filter(Boolean);\n if (segments.length === 0) return undefined;\n const first = segments[0].toLowerCase();\n return this._workspaceCodes.has(first) ? first : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Resolve the effective `workspace_id` for the next event.\n *\n * Cascade (highest precedence first):\n * 1. `this.config.workspace_id` — explicit operator config (headless\n * SDK usage, server-rendered apps, native shells).\n * 2. `?ws=` query param on the current URL — the P3 storefront URL\n * contract. The outlet picker writes this via `router.replace`.\n * 3. URL path segment (P1 Track A) — `/south/rewards` → \"south\" when\n * \"south\" is in the org's workspace_code allowlist.\n * 4. `aegis.setWorkspace()` runtime override (P1 Track E) — SPAs +\n * mobile + custom integrations.\n * 5. sessionStorage `aegis_runtime_ws` — persists setWorkspace across\n * reloads/SPA route changes.\n * 6. `undefined` — gateway falls back to `resolveByOrigin` lookup\n * against the property's allowed_origins.\n *\n * The returned value can be EITHER a UUID (from config / ?ws=) or a\n * workspace CODE slug (from path / setWorkspace). The gateway\n * normalizes slug → UUID server-side; analytics layer never needs to\n * worry about the difference.\n *\n * See docs/architecture/MULTI_OUTLET_URL_STRATEGY.md §2.3 and\n * docs/architecture/PLUGIN_HANDSHAKE_AUTOMATION.md §2.\n */\n /**\n * Public so peer SDK surfaces (AegisMessageRuntime / AegisInAppManager /\n * AegisPlacementManager / AegisWidgetManager) can plumb the same\n * resolved workspace into their own gateway POSTs. Each manager calls\n * the gateway on its own endpoint (`/v1/in_app/events`,\n * `/v1/placements/track`, `/v1/widgets/track-event`); without this,\n * those events arrive at event-ingress with no workspace_id stamped\n * → impressions/clicks fall back to the org's primary workspace and\n * cross-outlet attribution breaks.\n */\n public getEffectiveWorkspaceId(): string | undefined {\n // 1. Explicit operator config wins\n const configured = this.config?.workspace_id;\n if (configured) return configured;\n if (typeof window === 'undefined') return undefined;\n\n // 2. ?ws= query param\n try {\n const ws = new URLSearchParams(window.location.search).get('ws');\n if (ws && ws.length > 0) return ws;\n } catch {\n /* malformed search — fall through */\n }\n\n // 3. URL path segment matching workspace_code allowlist\n const pathWs = this.getPathWorkspaceCode();\n if (pathWs) return pathWs;\n\n // 4. Runtime aegis.setWorkspace() value (in-memory)\n if (this._runtimeWorkspace) return this._runtimeWorkspace;\n\n // 5. sessionStorage fallback (set by prior setWorkspace() this session)\n try {\n if (window.sessionStorage) {\n const cached = window.sessionStorage.getItem('aegis_runtime_ws');\n if (cached && cached.length > 0) return cached;\n }\n } catch {\n /* private mode — fall through */\n }\n\n // 6. Gateway handles via resolveByOrigin\n return undefined;\n }\n\n track(eventName: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n const messageId = generateMessageId();\n const event: TrackEvent = {\n type: 'track',\n event: eventName,\n properties: properties || {},\n messageId,\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n // Record so tenants who run their own Meta Pixel via GTM can pull the\n // event_id and pass it as `eventID` in fbq('track', ...). See B1.2.\n this._lastEventIds.set(eventName, messageId);\n\n this.captureEvent(event);\n }\n\n identify(userId: string, traits?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n // P10.5 — apply trait governance BEFORE setUserId + send. The BE\n // applies the same guards in `record_attribute_keys`, but doing it\n // client-side lets us warn the developer in console.warn and emit\n // sanitized values so what they see locally matches what gets stored.\n const wsForGovernor = this.getEffectiveWorkspaceId() || null;\n const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);\n\n this.identity.setUserId(userId, governedTraits);\n\n const event: IdentifyEvent = {\n type: 'identify',\n traits: governedTraits,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: userId,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n page(name?: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n const event: PageEvent = {\n type: 'page',\n name: name || document.title,\n properties: properties || {},\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n group(groupId: string, traits?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n\n // P10.5 — same trait governance as identify(); group traits ride\n // the same `record_attribute_keys` BE chokepoint.\n const wsForGovernor = this.getEffectiveWorkspaceId() || null;\n const { sanitized: governedTraits } = this.traitGovernor.process(traits, wsForGovernor);\n\n const event: GroupEvent = {\n type: 'group',\n groupId,\n traits: governedTraits,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n alias(newUserId: string): void {\n if (!this.assertInitialized()) return;\n\n const { previousId } = this.identity.alias(newUserId);\n\n const event: AliasEvent = {\n type: 'alias',\n previousId,\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: newUserId,\n sessionId: this.session!.getSessionId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n /**\n * SPA-logical screen view. Distinct from `page()` (URL-bound) — fires\n * when the user enters a logical surface like cart / checkout / drawer\n * without a URL change. Gateway maps `screen` → `engagement.screen_view`.\n */\n screen(name: string, properties?: Record<string, any>): void {\n if (!this.assertInitialized()) return;\n if (typeof name !== 'string' || name.length === 0) {\n logger.warn('aegis.screen: name must be a non-empty string');\n return;\n }\n\n const event: ScreenEvent = {\n type: 'screen',\n name,\n properties: properties || {},\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session!.getSessionId(),\n workspace_id: this.getEffectiveWorkspaceId(),\n location_id: this.getEffectiveLocationId(),\n context: buildContext(this.config!, this.session!),\n };\n\n this.captureEvent(event);\n }\n\n private async captureEvent(event: AegisEvent): Promise<void> {\n if (this.config?.enable_consent_mode && this.consent) {\n if (event.type === 'track' || event.type === 'page') {\n if (!this.consent.hasConsent('analytics')) {\n logger.debug('Event blocked: analytics consent not granted');\n return;\n }\n }\n }\n\n // Event-name governance — drops novel names once the org's unique-event\n // cap is exhausted. Meta events (aegis.client.*) bypass the governor so\n // the telemetry channel stays open even under a cap-exhaustion storm.\n // See governance/name-governor.ts.\n if (event.type === 'track') {\n const trackEvent = event as TrackEvent;\n const isMetaEvent =\n typeof trackEvent.event === 'string' &&\n trackEvent.event.startsWith('aegis.client.');\n if (!isMetaEvent && !this.nameGovernor.shouldSend(trackEvent.event)) {\n return;\n }\n }\n\n if (this.rateLimiter) {\n const eventLabel =\n event.type === 'track' && (event as TrackEvent).event\n ? (event as TrackEvent).event\n : event.type;\n // Meta-events bypass the rate limiter to avoid recursion / lost telemetry.\n const isMetaEvent =\n event.type === 'track' &&\n typeof (event as TrackEvent).event === 'string' &&\n (event as TrackEvent).event.startsWith('aegis.client.');\n if (!isMetaEvent && !this.rateLimiter.tryConsume(eventLabel)) {\n return;\n }\n }\n\n // Auto-inject campaign attribution from UTM context into event properties\n // so downstream consumers (event-ingress, ClickHouse) receive campaign_id\n // without requiring manual mapping by the integrator.\n const ctx = (event as any).context;\n const props = (event as any).properties;\n if (ctx?.campaign && props && !props.campaign_id) {\n const utm = ctx.campaign;\n if (utm.campaign) props.campaign_id = utm.campaign;\n if (utm.source) props.campaign_source = utm.source;\n if (utm.medium) props.campaign_medium = utm.medium;\n if (utm.content) props.campaign_content = utm.content;\n if (utm.term) props.campaign_term = utm.term;\n }\n if (ctx?.adClickIDs && props) {\n const clicks = ctx.adClickIDs;\n const clickId = clicks.gclid || clicks.fbclid || clicks.ctwa_clid || clicks.msclkid || clicks.ttclid;\n if (clickId && !props.campaign_click_id) props.campaign_click_id = clickId;\n }\n\n // B1.2 — Auto-inject Meta companion cookies (_fbp, _fbc, fbclid) into\n // event properties so the cell-side CAPI service can pass them as\n // user_data fields to Meta. Lifts EMQ from ~6-7 (PII-only) to ≥8.0\n // without firing fbq() browser-side. fbp is self-generated if absent;\n // fbc only set when an inbound fbclid landed in the URL.\n // See libs/web-sdk/src/utils/meta-cookies.ts for the spec.\n if (props) {\n const meta = captureMetaCookies();\n if (meta.fbp && !props.fbp) props.fbp = meta.fbp;\n if (meta.fbc && !props.fbc) props.fbc = meta.fbc;\n if (meta.fbclid && !props.fbclid) props.fbclid = meta.fbclid;\n }\n\n logger.debug('Capturing event:', event);\n\n const transformedEvent = await this.plugins.executeHookChain(\n 'beforeEventCapture',\n event\n );\n\n if (!transformedEvent) {\n logger.debug('Event cancelled by plugin');\n return;\n }\n\n this.queue!.push(transformedEvent);\n this.session!.incrementEventCount();\n\n await this.plugins.executeHook('afterEventCapture', transformedEvent);\n }\n\n /**\n * Emit a coalesced meta-event reporting client-side rate-limit drops.\n * Bypasses the limiter (see captureEvent guard).\n */\n private emitRateLimitMeta(count: number, sampleEventName: string | null): void {\n if (!this.config?.initialized || !this.session || !this.identity) return;\n const event: TrackEvent = {\n type: 'track',\n event: 'aegis.client.rate_limited',\n properties: {\n dropped_count: count,\n sample_event_name: sampleEventName,\n burst_capacity: this.config.rate_limit_burst,\n refill_per_second: this.config.rate_limit_per_second,\n },\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session.getSessionId(),\n context: buildContext(this.config, this.session),\n };\n this.queue?.push(event);\n }\n\n reset(): void {\n if (!this.assertInitialized()) return;\n\n this.identity.reset();\n // Drop pending pre-login traits buffered on the user namespace —\n // a reset() either through `aegis.reset()` or `aegis.user.logout()`\n // should leave no residual identity state behind.\n this._user = null;\n logger.info('User identity reset');\n }\n\n /**\n * Ingest the event-governance hint returned from `/v1/sdk/bootstrap`.\n *\n * Callers (Shopify pixel, snippet, react integration) run `bootstrap()`\n * themselves and pass the resulting `eventGovernance` field here so the\n * SDK can self-throttle novel event names before they hit the gateway.\n *\n * Passing null/undefined disables governance (Enterprise plan / outage\n * fail-open). Safe to call before `init()` — the hint is stored and\n * applied as soon as init completes.\n */\n ingestGovernanceHint(hint: EventGovernanceHint | null | undefined): void {\n this.nameGovernor.ingestHint(hint ?? null);\n logger.debug('Governance hint ingested', hint ? {\n k: hint.k,\n m: hint.m,\n remaining: hint.remaining_new_names,\n } : 'disabled');\n }\n\n /**\n * Drain the client-side drop counter and emit a meta-event. Called\n * periodically by the queue flush path so ops dashboards see novel-name\n * amplification patterns in near-real-time.\n */\n private emitGovernanceDropMeta(): void {\n if (!this.config?.initialized || !this.session || !this.identity) return;\n const report = this.nameGovernor.drainDropReport();\n if (!report) return;\n\n const event: TrackEvent = {\n type: 'track',\n event: 'aegis.client.name_governor_dropped',\n properties: {\n dropped_count: report.total,\n distinct_names: Object.keys(report.events).length,\n // Cap the payload — a runaway loop could produce thousands of names;\n // we ship the top 10 for diagnostics and the total for counting.\n sample_names: Object.entries(report.events)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([name, count]) => ({ name, count })),\n window_start: report.since,\n window_end: Date.now(),\n },\n messageId: generateMessageId(),\n timestamp: new Date().toISOString(),\n anonymousId: this.identity.getAnonymousId(),\n userId: this.identity.getUserId() || undefined,\n sessionId: this.session.getSessionId(),\n context: buildContext(this.config, this.session),\n };\n this.queue?.push(event);\n }\n\n async flush(): Promise<void> {\n if (!this.assertInitialized()) return;\n\n // Emit any coalesced governance-drop telemetry BEFORE flushing so the\n // meta event rides in the same batch as the rest.\n this.emitGovernanceDropMeta();\n await this.queue!.flush();\n }\n\n getAnonymousId(): string | null {\n if (!this.identity) return null;\n return this.identity.getAnonymousId();\n }\n\n getUserId(): string | null {\n if (!this.identity) return null;\n return this.identity.getUserId();\n }\n\n getSessionId(): string | null {\n if (!this.session) return null;\n return this.session.getSessionId();\n }\n\n /**\n * B1.2 — Tenant-Pixel coexistence handoff. Returns the messageId of the\n * most recent `track('<eventName>', ...)` call, or null if no event of\n * that name has been tracked since SDK init.\n *\n * Use this from a tenant's own Meta Pixel script (e.g. via GTM) to pass\n * the same event_id to `fbq('track', '<eventName>', payload, {eventID: id})`.\n * Meta dedups on (pixel_id, event_name, event_id) so the browser-fired\n * Pixel event and our server-side CAPI event collapse to one — preventing\n * double-counting.\n *\n * Event names are case-sensitive (\"Purchase\" ≠ \"purchase\") — Meta's dedup\n * is also case-sensitive, so be exact.\n *\n * See docs/integrations/META_PIXEL_COEXISTENCE.md.\n */\n lastEventId(eventName: string): string | null {\n return this._lastEventIds.get(eventName) ?? null;\n }\n\n debug(enable: boolean = true): void {\n if (enable) {\n logger.enable();\n } else {\n logger.disable();\n }\n\n if (this.config) {\n this.config.debug = enable;\n }\n }\n\n setCell(cellEndpoint: CellEndpoint): void {\n if (!this.config) {\n logger.warn('SDK not initialized');\n return;\n }\n\n const existingCell = this.config.cell_endpoints.find(\n (c) => c.region === cellEndpoint.region\n );\n\n if (existingCell) {\n Object.assign(existingCell, cellEndpoint);\n } else {\n this.config.cell_endpoints.push(cellEndpoint);\n }\n\n logger.info('Cell endpoint configured:', cellEndpoint);\n }\n\n getCellInfo() {\n if (!this.transport) return null;\n\n return {\n activeCell: this.transport.getActiveCell(),\n latencies: this.transport.getRegionLatencies(),\n };\n }\n\n setConsent(preferences: Partial<ConsentPreferences>): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n this.consent.setConsent(preferences);\n logger.info('Consent preferences updated');\n }\n\n grantConsent(category?: ConsentCategory): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n if (category) {\n this.consent.setConsent({ [category]: true });\n } else {\n this.consent.grantAll();\n }\n }\n\n denyConsent(category?: ConsentCategory): void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return;\n }\n\n if (category) {\n this.consent.setConsent({ [category]: false });\n } else {\n this.consent.denyAll();\n }\n }\n\n hasConsent(category: ConsentCategory): boolean {\n if (!this.consent) {\n return true;\n }\n\n return this.consent.hasConsent(category);\n }\n\n getConsentPreferences(): ConsentPreferences | null {\n if (!this.consent) {\n return null;\n }\n\n return this.consent.getPreferences();\n }\n\n onConsentChange(callback: (preferences: ConsentPreferences) => void): () => void {\n if (!this.consent) {\n logger.warn('Consent manager not initialized');\n return () => {};\n }\n\n return this.consent.onChange(callback);\n }\n\n private assertInitialized(): boolean {\n if (!this.config?.initialized) {\n logger.warn('SDK not initialized. Call aegis.init() first.');\n return false;\n }\n return true;\n }\n\n get ecommerce(): EcommerceTracker {\n if (!this._ecommerce) {\n this._ecommerce = new EcommerceTracker(this);\n }\n return this._ecommerce;\n }\n\n get user(): UserNamespace {\n if (!this._user) {\n this._user = new UserNamespace(this);\n }\n return this._user;\n }\n\n destroy(): void {\n this.stopSPATracking();\n\n if (this.queue) {\n this.queue.destroy();\n }\n\n if (this.session) {\n this.session.destroy();\n }\n\n if (this.transport) {\n this.transport.destroy();\n }\n\n if (this.rateLimiter) {\n this.rateLimiter.destroy();\n this.rateLimiter = null;\n }\n\n this.plugins.clear();\n\n logger.info('SDK destroyed');\n }\n}\n","/**\n * CarouselCards renderer — a swipeable / autoplaying sequence of product\n * cards. Used for post-purchase upsells, cross-sell, and \"recently viewed\"\n * surfaces. The entire card list is pre-bundled — no network call on\n * trigger; slide advance is pure client state.\n *\n * Data shape (interactive_config, populated at campaign create time and\n * validated server-side):\n * cards: Array<{ image_url?, title, body?, cta_text?, cta_url? }>\n * autoplay_ms?: number // 0 = manual advance only\n * loop?: boolean // wrap to card 0 on last-next\n */\n\nimport type { RenderContext } from './types';\n\ntype Card = {\n image_url?: string;\n title?: string;\n body?: string;\n cta_text?: string;\n cta_url?: string;\n};\n\nconst MAX_CARDS_RENDERED = 10;\n\nexport function renderCarouselCards(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_CARDS_RENDERED);\n\n if (cards.length === 0) {\n log('carousel_cards rendered with zero cards — skipping', 'warn');\n return;\n }\n\n const autoplay = Number(ic.autoplay_ms) || 0;\n const loop = ic.loop !== false; // default true\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#ffffff');\n const fg = sanitizeColor((campaign.text_color as string) || '#0f172a');\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-carousel-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; left: 0; right: 0; bottom: 0;\n z-index: 99999; padding: 12px 12px 20px;\n background: rgba(0,0,0,0.12); backdrop-filter: blur(8px);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `;\n\n const card = document.createElement('div');\n card.style.cssText = `\n background: ${bg}; color: ${fg}; border-radius: 16px;\n box-shadow: 0 8px 20px rgba(0,0,0,0.08); padding: 16px;\n max-width: 520px; margin: 0 auto; position: relative;\n `;\n\n const header = document.createElement('div');\n header.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 12px;';\n\n const headerText = document.createElement('div');\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 15px; margin-bottom: 2px;';\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.75;';\n headerText.appendChild(title);\n headerText.appendChild(body);\n header.appendChild(headerText);\n\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '✕';\n closeBtn.setAttribute('aria-label', 'Close');\n closeBtn.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 16px; cursor: pointer; opacity: 0.6; padding: 2px 6px;\n `;\n closeBtn.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n });\n header.appendChild(closeBtn);\n card.appendChild(header);\n\n const track = document.createElement('div');\n track.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory;\n scrollbar-width: none; -ms-overflow-style: none; padding-bottom: 4px;\n `;\n (track.style as unknown as { msOverflowStyle?: string }).msOverflowStyle = 'none';\n track.addEventListener('wheel', (e) => {\n if (Math.abs(e.deltaX) < Math.abs(e.deltaY)) return;\n track.scrollLeft += e.deltaX;\n });\n\n cards.forEach((c, i) => {\n const tile = document.createElement('div');\n tile.setAttribute('data-card-index', String(i));\n tile.style.cssText = `\n flex: 0 0 auto; width: 140px; scroll-snap-align: start;\n background: ${fg}0a; border-radius: 12px; padding: 10px;\n display: flex; flex-direction: column; gap: 6px;\n cursor: ${c.cta_url ? 'pointer' : 'default'};\n `;\n if (c.image_url) {\n const img = document.createElement('img');\n const safe = sanitizeUrl(c.image_url);\n if (safe) {\n img.src = safe;\n img.alt = '';\n img.loading = 'lazy';\n img.style.cssText = 'width: 100%; height: 96px; border-radius: 8px; object-fit: cover;';\n tile.appendChild(img);\n }\n }\n if (c.title) {\n const t = document.createElement('div');\n t.textContent = c.title;\n t.style.cssText = 'font-weight: 600; font-size: 13px; line-height: 1.3;';\n tile.appendChild(t);\n }\n if (c.body) {\n const b = document.createElement('div');\n b.textContent = c.body;\n b.style.cssText = 'font-size: 11.5px; opacity: 0.72; line-height: 1.3;';\n tile.appendChild(b);\n }\n if (c.cta_text && c.cta_url) {\n const cta = document.createElement('button');\n cta.textContent = c.cta_text;\n cta.style.cssText = `\n margin-top: auto; background: ${fg}; color: ${bg};\n border: none; padding: 6px 10px; border-radius: 999px;\n font-size: 12px; font-weight: 600; cursor: pointer;\n `;\n const goto = (e: Event) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked');\n const safe = sanitizeUrl(c.cta_url!);\n if (safe) window.location.href = safe;\n };\n cta.addEventListener('click', goto);\n tile.appendChild(cta);\n tile.addEventListener('click', goto);\n }\n track.appendChild(tile);\n });\n card.appendChild(track);\n\n // Dots + autoplay\n const dots = document.createElement('div');\n dots.style.cssText = 'display: flex; justify-content: center; gap: 6px; margin-top: 10px;';\n const dotEls: HTMLSpanElement[] = [];\n cards.forEach(() => {\n const dot = document.createElement('span');\n dot.style.cssText = `\n width: 6px; height: 6px; border-radius: 999px; background: ${fg};\n opacity: 0.25; transition: opacity 0.2s;\n `;\n dotEls.push(dot);\n dots.appendChild(dot);\n });\n card.appendChild(dots);\n\n const setActive = (idx: number) => {\n dotEls.forEach((d, i) => (d.style.opacity = i === idx ? '0.9' : '0.25'));\n };\n setActive(0);\n\n let activeIdx = 0;\n const tiles = track.querySelectorAll<HTMLDivElement>('[data-card-index]');\n const goto = (idx: number) => {\n activeIdx = ((idx % cards.length) + cards.length) % cards.length;\n const el = tiles[activeIdx];\n if (el) {\n el.scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });\n setActive(activeIdx);\n }\n };\n\n let autoplayTimer: ReturnType<typeof setInterval> | null = null;\n if (autoplay > 0 && cards.length > 1) {\n autoplayTimer = setInterval(() => {\n const next = activeIdx + 1;\n if (next >= cards.length && !loop) {\n if (autoplayTimer) clearInterval(autoplayTimer);\n return;\n }\n goto(next);\n }, autoplay);\n }\n\n overlay.addEventListener('remove', () => {\n if (autoplayTimer) clearInterval(autoplayTimer);\n });\n\n // Observe scroll to keep dots in sync when the user swipes.\n track.addEventListener('scroll', () => {\n const approx = Math.round(track.scrollLeft / 150);\n if (approx !== activeIdx && approx >= 0 && approx < cards.length) {\n activeIdx = approx;\n setActive(activeIdx);\n }\n });\n\n overlay.appendChild(card);\n document.body.appendChild(overlay);\n}\n","/**\n * StickyBar renderer — a pinned notification strip at the top or bottom\n * of the viewport. Used for free-shipping hints, flash-sale banners,\n * cart-reminder strips. Dismissible, optionally auto-hiding.\n *\n * interactive_config:\n * sticky_position: 'top' | 'bottom' (required)\n * sticky_bg_color?: string\n * sticky_dismissible?: boolean default true\n * sticky_auto_hide_ms?: number 0 = never\n */\n\nimport type { RenderContext } from './types';\n\n// Persist per-campaign dismissal across reloads so we don't re-show a\n// bar the user already closed. Scoped per campaign ID; storage key\n// survives navigation but is cleared when the user explicitly clears site data.\nconst DISMISS_STORAGE_PREFIX = 'aegis_sticky_dismissed:';\n\nexport function renderStickyBar(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const position = ic.sticky_position === 'top' ? 'top' : 'bottom';\n const dismissible = ic.sticky_dismissible !== false;\n const autoHide = Number(ic.sticky_auto_hide_ms) || 0;\n\n // Already dismissed this session? Skip silently — the prefetch bundle\n // keeps re-offering it, but the user's explicit close stands until TTL.\n try {\n if (\n typeof localStorage !== 'undefined' &&\n localStorage.getItem(DISMISS_STORAGE_PREFIX + campaign.id)\n ) {\n log(`sticky_bar ${campaign.id} suppressed — user dismissed earlier`);\n return;\n }\n } catch {\n // localStorage blocked — fall through and render\n }\n\n addAnimationStyles();\n\n const bg = sanitizeColor(\n (ic.sticky_bg_color as string) || (campaign.background_color as string) || '#4169e1',\n );\n const fg = sanitizeColor((campaign.text_color as string) || '#ffffff');\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-sticky-bar';\n bar.setAttribute('data-campaign-id', campaign.id);\n\n const positionCss =\n position === 'top'\n ? 'top: 0; left: 0; right: 0; animation: aegisSlideDown 0.3s ease-out;'\n : 'bottom: 0; left: 0; right: 0; animation: aegisSlideInFromBottom 0.3s ease-out;';\n\n bar.style.cssText = `\n position: fixed; ${positionCss}\n background: ${bg}; color: ${fg};\n padding: 10px 14px; z-index: 999998;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n display: flex; align-items: center; gap: 12px; justify-content: center;\n box-shadow: 0 ${position === 'top' ? '2px' : '-2px'} 8px rgba(0,0,0,0.08);\n `;\n\n const label = document.createElement('div');\n label.style.cssText = 'flex: 0 1 auto; font-size: 13px; font-weight: 500; text-align: center;';\n const strong = document.createElement('strong');\n strong.textContent = campaign.title;\n strong.style.cssText = 'margin-right: 6px; font-weight: 700;';\n label.appendChild(strong);\n label.appendChild(document.createTextNode(campaign.body));\n bar.appendChild(label);\n\n if (campaign.action_url && campaign.button_text) {\n const cta = document.createElement('button');\n cta.textContent = campaign.button_text;\n cta.style.cssText = `\n background: ${fg}; color: ${bg};\n border: none; padding: 6px 14px; border-radius: 999px;\n font-size: 12px; font-weight: 700; cursor: pointer; white-space: nowrap;\n `;\n cta.addEventListener('click', () => {\n trackEvent(campaign.id, 'clicked');\n const safe = sanitizeUrl(campaign.action_url!);\n if (safe) window.location.href = safe;\n });\n bar.appendChild(cta);\n }\n\n let autoHideTimer: ReturnType<typeof setTimeout> | null = null;\n const remove = (persist: boolean) => {\n if (autoHideTimer) clearTimeout(autoHideTimer);\n if (persist) {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(DISMISS_STORAGE_PREFIX + campaign.id, '1');\n }\n } catch {\n // swallow — localStorage may be disabled\n }\n }\n bar.remove();\n };\n\n if (dismissible) {\n const close = document.createElement('button');\n close.textContent = '✕';\n close.setAttribute('aria-label', 'Dismiss');\n close.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 16px; cursor: pointer; padding: 0 4px; opacity: 0.75;\n `;\n close.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n remove(true);\n });\n bar.appendChild(close);\n }\n\n if (autoHide > 0) {\n autoHideTimer = setTimeout(() => remove(false), autoHide);\n }\n\n document.body.appendChild(bar);\n}\n","/**\n * ProgressBar renderer — a persistent bar that shows the user's progress\n * toward a reward threshold (free shipping, loyalty tier, referral bonus).\n *\n * Two sources of the current value:\n * 'client' — cheap: read from window.Shopify / WooCommerce / Magento\n * cart globals. Untrusted; fine for visual-only hints.\n * 'sse' — trusted: cell-plane pushes current_value via SSE to the\n * already-shipped realtime server. For cart-gated rewards\n * (must agree with server-side checkout guard), prefer SSE.\n *\n * For Phase 1 we implement CLIENT mode end-to-end. SSE mode reads\n * `window.__aegisProgressSSE[campaign.id]` if populated by a parent app\n * — higher-level integration (the SSE bridge) can push values there.\n *\n * interactive_config:\n * progress_goal_type: 'cart_total' | 'items_in_cart' | 'loyalty_points' | 'referrals'\n * progress_threshold: number\n * progress_reward_text?: string\n * progress_source?: 'client' | 'sse' default 'client'\n */\n\nimport type { RenderContext } from './types';\n\ntype GoalType = 'cart_total' | 'items_in_cart' | 'loyalty_points' | 'referrals';\n\nexport function renderProgressBar(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const goalType = (ic.progress_goal_type as GoalType) || 'cart_total';\n const threshold = Number(ic.progress_threshold);\n if (!(threshold > 0)) {\n log('progress_bar requires a positive progress_threshold — skipping', 'warn');\n return;\n }\n const rewardText =\n (ic.progress_reward_text as string) || (campaign.body as string) || 'Unlocked!';\n const source = ic.progress_source === 'sse' ? 'sse' : 'client';\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#f8fafc');\n const fill = sanitizeColor((campaign.text_color as string) || '#4169e1');\n const fg = '#0f172a';\n\n const bar = document.createElement('div');\n bar.className = 'aegis-in-app-progress-bar';\n bar.setAttribute('data-campaign-id', campaign.id);\n bar.style.cssText = `\n position: fixed; left: 12px; right: 12px; bottom: 12px;\n max-width: 540px; margin: 0 auto;\n background: ${bg}; color: ${fg}; border-radius: 12px;\n padding: 10px 14px; z-index: 999997;\n box-shadow: 0 8px 20px rgba(0,0,0,0.08);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n `;\n\n const label = document.createElement('div');\n label.style.cssText = 'display: flex; justify-content: space-between; font-size: 13px; font-weight: 600; margin-bottom: 6px;';\n\n const leading = document.createElement('span');\n leading.textContent = campaign.title;\n label.appendChild(leading);\n\n const numeric = document.createElement('span');\n numeric.style.cssText = 'opacity: 0.7; font-weight: 500;';\n label.appendChild(numeric);\n bar.appendChild(label);\n\n const shell = document.createElement('div');\n shell.style.cssText = `\n height: 8px; border-radius: 999px; background: ${fill}22;\n overflow: hidden;\n `;\n const fillEl = document.createElement('div');\n fillEl.style.cssText = `\n height: 100%; border-radius: 999px; background: ${fill};\n width: 0%; transition: width 0.4s cubic-bezier(.4,0,.2,1);\n `;\n shell.appendChild(fillEl);\n bar.appendChild(shell);\n\n const footnote = document.createElement('div');\n footnote.style.cssText = 'font-size: 11.5px; opacity: 0.65; margin-top: 6px;';\n bar.appendChild(footnote);\n\n const readCurrentValue = (): number => {\n if (source === 'sse') {\n const hook = (window as unknown as {\n __aegisProgressSSE?: Record<string, number>;\n }).__aegisProgressSSE;\n return (hook && typeof hook[campaign.id] === 'number' ? hook[campaign.id] : 0) as number;\n }\n // client mode — read the platform cart globals the widget manager\n // already normalises (Shopify/Woo/Magento). We peek at the same\n // locations without pulling in AegisWidgetManager's 2k-line module.\n try {\n if (goalType === 'cart_total' || goalType === 'items_in_cart') {\n const win = window as unknown as {\n Shopify?: { checkout?: { total_price?: string | number } };\n aegis_cart?: { cart_total?: number; cart_items?: unknown[] };\n localStorage?: Storage;\n };\n if (win.Shopify?.checkout) {\n const v = parseFloat(String(win.Shopify.checkout.total_price || 0));\n return goalType === 'cart_total' ? v : 0;\n }\n if (win.aegis_cart) {\n if (goalType === 'items_in_cart') {\n return Array.isArray(win.aegis_cart.cart_items) ? win.aegis_cart.cart_items.length : 0;\n }\n return Number(win.aegis_cart.cart_total || 0);\n }\n // Magento cache fallback\n try {\n const cacheStr = window.localStorage?.getItem('mage-cache-storage');\n if (cacheStr) {\n const cache = JSON.parse(cacheStr) as { cart?: { subtotalAmount?: number; items?: unknown[] } };\n const cart = cache.cart;\n if (cart) {\n return goalType === 'cart_total'\n ? Number(cart.subtotalAmount || 0)\n : Array.isArray(cart.items)\n ? cart.items.length\n : 0;\n }\n }\n } catch {\n /* noop */\n }\n }\n // loyalty_points / referrals are expected to flow via SSE/push; in\n // client mode we cannot trust a local counter.\n return 0;\n } catch {\n return 0;\n }\n };\n\n let lastFiredUnlock = false;\n const update = () => {\n const cur = readCurrentValue();\n const pct = Math.max(0, Math.min(100, (cur / threshold) * 100));\n fillEl.style.width = `${pct}%`;\n numeric.textContent = `${cur.toFixed(0)} / ${threshold.toFixed(0)}`;\n if (pct >= 100) {\n footnote.textContent = rewardText;\n if (!lastFiredUnlock) {\n lastFiredUnlock = true;\n trackEvent(campaign.id, 'clicked'); // \"unlocked\" recorded as click\n }\n } else {\n const remaining = Math.max(0, threshold - cur);\n footnote.textContent = `Add ${remaining.toFixed(0)} more to unlock: ${rewardText}`;\n }\n };\n\n update();\n\n // Client-mode cart state changes fire events on the storefront globals\n // (Shopify emits `cart:updated`, Woo emits its own). Poll fallback at\n // 1s — negligible overhead because every tick is synchronous.\n const pollTimer = setInterval(update, 1000);\n const cleanup = () => clearInterval(pollTimer);\n window.addEventListener('beforeunload', cleanup, { once: true });\n bar.addEventListener('remove', cleanup);\n\n document.body.appendChild(bar);\n}\n","/**\n * CoachmarkTour renderer — a guided, multi-step walkthrough anchored to\n * DOM elements via CSS selectors. One tooltip appears at a time with\n * next/back navigation; completion + skip are persisted per contact via\n * localStorage under the `resume_key`, so an abandoned tour resumes on\n * the next visit instead of restarting or re-bothering the user.\n *\n * interactive_config:\n * steps: Array<{\n * anchor_web?: string, // CSS selector for THIS platform\n * anchor_android?: string, // ignored on web\n * anchor_ios?: string, // ignored on web\n * title: string,\n * body: string,\n * placement?: 'top' | 'bottom' | 'left' | 'right',\n * cta_text?: string,\n * }>\n * resume_key: string,\n * allow_skip?: boolean,\n * show_progress_dots?: boolean,\n */\n\nimport type { RenderContext } from './types';\n\ntype Step = {\n anchor_web?: string;\n anchor_android?: string;\n anchor_ios?: string;\n title: string;\n body: string;\n placement?: 'top' | 'bottom' | 'left' | 'right';\n cta_text?: string;\n};\n\nconst RESUME_PREFIX = 'aegis_coachmark_progress:';\n\nfunction readResumeIdx(resumeKey: string): number {\n try {\n if (typeof localStorage === 'undefined') return 0;\n const raw = localStorage.getItem(RESUME_PREFIX + resumeKey);\n const n = raw ? parseInt(raw, 10) : 0;\n return Number.isFinite(n) && n >= 0 ? n : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeResumeIdx(resumeKey: string, idx: number): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(RESUME_PREFIX + resumeKey, String(idx));\n }\n } catch {\n /* noop */\n }\n}\n\nfunction clearResume(resumeKey: string): void {\n try {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(RESUME_PREFIX + resumeKey);\n }\n } catch {\n /* noop */\n }\n}\n\nexport function renderCoachmarkTour(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const resumeKey = ic.resume_key as string | undefined;\n if (!resumeKey) {\n log('coachmark_tour requires interactive_config.resume_key — skipping', 'warn');\n return;\n }\n\n const steps = Array.isArray(ic.steps) ? (ic.steps as Step[]) : [];\n if (steps.length === 0) {\n log('coachmark_tour has no steps — skipping', 'warn');\n return;\n }\n\n const allowSkip = ic.allow_skip !== false;\n const showDots = ic.show_progress_dots !== false;\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#0f172a');\n const fg = sanitizeColor((campaign.text_color as string) || '#ffffff');\n\n // Resume or start at 0.\n let current = readResumeIdx(resumeKey);\n if (current >= steps.length) {\n log(`coachmark_tour ${resumeKey} already complete — not re-showing`);\n return;\n }\n\n // Track the single overall impression once per mount.\n trackEvent(campaign.id, 'impression');\n\n let pointerEl: HTMLElement | null = null;\n let tipEl: HTMLElement | null = null;\n let highlightEl: HTMLElement | null = null;\n\n const cleanupOne = () => {\n pointerEl?.remove();\n tipEl?.remove();\n highlightEl?.remove();\n pointerEl = null;\n tipEl = null;\n highlightEl = null;\n };\n\n const finish = (opts: { skipped: boolean }) => {\n cleanupOne();\n if (opts.skipped) {\n trackEvent(campaign.id, 'dismissed');\n } else {\n trackEvent(campaign.id, 'clicked'); // completion\n }\n // Mark tour as complete so we never restart it for this contact.\n writeResumeIdx(resumeKey, steps.length);\n };\n\n const showStep = (idx: number) => {\n cleanupOne();\n const step = steps[idx];\n if (!step) {\n finish({ skipped: false });\n return;\n }\n writeResumeIdx(resumeKey, idx);\n\n const selector = step.anchor_web;\n let anchor: Element | null = null;\n if (selector) {\n try {\n anchor = document.querySelector(selector);\n } catch {\n anchor = null;\n }\n }\n if (!anchor) {\n log(`coachmark step ${idx} — selector '${selector}' not found; advancing`, 'warn');\n showStep(idx + 1);\n return;\n }\n\n const rect = anchor.getBoundingClientRect();\n // Slight highlight around the anchor so the user's eye snaps to it.\n highlightEl = document.createElement('div');\n highlightEl.style.cssText = `\n position: fixed;\n top: ${rect.top - 6}px; left: ${rect.left - 6}px;\n width: ${rect.width + 12}px; height: ${rect.height + 12}px;\n border-radius: 10px; z-index: 999998;\n box-shadow: 0 0 0 2px ${fg}, 0 0 0 9999px rgba(0,0,0,0.4);\n pointer-events: none;\n transition: all 0.2s ease;\n `;\n document.body.appendChild(highlightEl);\n\n const placement = step.placement || 'bottom';\n tipEl = document.createElement('div');\n tipEl.setAttribute('data-campaign-id', campaign.id);\n tipEl.style.cssText = `\n position: fixed; z-index: 999999;\n background: ${bg}; color: ${fg};\n padding: 12px 14px; border-radius: 12px;\n max-width: 260px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.25);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisFadeIn 0.2s ease;\n `;\n\n const titleEl = document.createElement('div');\n titleEl.textContent = step.title;\n titleEl.style.cssText = 'font-weight: 700; font-size: 13px; margin-bottom: 4px;';\n tipEl.appendChild(titleEl);\n\n const bodyEl = document.createElement('div');\n bodyEl.textContent = step.body;\n bodyEl.style.cssText = 'font-size: 12.5px; line-height: 1.4; opacity: 0.9;';\n tipEl.appendChild(bodyEl);\n\n const controls = document.createElement('div');\n controls.style.cssText = 'display: flex; align-items: center; justify-content: space-between; margin-top: 10px; gap: 8px;';\n\n // Dots\n if (showDots) {\n const dots = document.createElement('div');\n dots.style.cssText = 'display: flex; gap: 4px;';\n steps.forEach((_, i) => {\n const d = document.createElement('span');\n d.style.cssText = `\n width: 5px; height: 5px; border-radius: 999px;\n background: ${fg}; opacity: ${i === idx ? '0.95' : '0.3'};\n `;\n dots.appendChild(d);\n });\n controls.appendChild(dots);\n } else {\n controls.appendChild(document.createElement('span'));\n }\n\n const buttons = document.createElement('div');\n buttons.style.cssText = 'display: flex; gap: 6px;';\n\n if (allowSkip && idx < steps.length - 1) {\n const skip = document.createElement('button');\n skip.textContent = 'Skip';\n skip.style.cssText = `\n background: transparent; color: inherit; opacity: 0.7;\n border: none; font-size: 12px; cursor: pointer; padding: 6px 8px;\n `;\n skip.addEventListener('click', () => finish({ skipped: true }));\n buttons.appendChild(skip);\n }\n\n const next = document.createElement('button');\n const isLast = idx === steps.length - 1;\n next.textContent = step.cta_text || (isLast ? 'Done' : 'Next');\n next.style.cssText = `\n background: ${fg}; color: ${bg};\n border: none; padding: 6px 12px; border-radius: 999px;\n font-size: 12px; font-weight: 700; cursor: pointer;\n `;\n next.addEventListener('click', () => {\n if (isLast) {\n finish({ skipped: false });\n } else {\n showStep(idx + 1);\n }\n });\n buttons.appendChild(next);\n\n controls.appendChild(buttons);\n tipEl.appendChild(controls);\n document.body.appendChild(tipEl);\n\n // Position after insertion so we know the tip's own dimensions.\n const tipRect = tipEl.getBoundingClientRect();\n const margin = 12;\n let top = rect.bottom + margin;\n let left = rect.left;\n if (placement === 'top') {\n top = rect.top - tipRect.height - margin;\n } else if (placement === 'left') {\n top = rect.top;\n left = rect.left - tipRect.width - margin;\n } else if (placement === 'right') {\n top = rect.top;\n left = rect.right + margin;\n }\n // Clamp to viewport.\n top = Math.max(8, Math.min(window.innerHeight - tipRect.height - 8, top));\n left = Math.max(8, Math.min(window.innerWidth - tipRect.width - 8, left));\n tipEl.style.top = `${top}px`;\n tipEl.style.left = `${left}px`;\n };\n\n showStep(current);\n\n // If the host page unloads mid-tour, leave the current index persisted so\n // the next session resumes from there. Nothing else to do — writeResumeIdx\n // was called in showStep.\n // Expose a cancel hook so app code can reset the tour from Dev Tools etc.\n (window as unknown as { aegisResetTour?: (key: string) => void }).aegisResetTour = (key: string) => clearResume(key);\n}\n","/**\n * AegisInAppManager - Web SDK In-App Messaging Module\n *\n * HTTP polling architecture with client-side evaluation.\n *\n * Security:\n * - XSS Protection: Uses DOM APIs (createElement, textContent) instead of innerHTML\n * - URL Validation: Prevents javascript: protocol injection\n * - CSP Compatible: No inline styles in HTML strings\n *\n * Architecture (RFC 2026-04-16):\n * - Polls /v1/in-app/active on initialize + on identity change\n * - Caches campaigns locally in memory\n * - Evaluates trigger rules client-side\n * - Tracks events (impression, click, dismiss) asynchronously\n *\n * NOTE: Persistent SSE for downstream delivery is disabled by default\n * (enableSSE=false). Tenant dashboards that need realtime (inbox, chat,\n * live page) open SSE directly via Next.js API routes — not via this SDK.\n * End-user storefronts use polling + Web Push for background delivery.\n */\n\nimport type { RenderContext } from './renderers';\nimport {\n renderCarouselCards,\n renderCoachmarkTour,\n renderProductRecommendation,\n renderProgressBar,\n renderStickyBar,\n} from './renderers';\nimport { Storage } from '../utils/storage';\n\n// Read the SDK's persisted anonymous_id from localStorage/cookie if available.\n// Returns undefined when called server-side (no document/window) or when no\n// id has been minted yet — caller falls through to leaving userId unset.\nfunction readAnonIdFromStorage(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const storage = new Storage();\n return storage.get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface InAppCampaign {\n id: string;\n type:\n | 'modal'\n | 'banner'\n | 'tooltip'\n | 'full_screen'\n | 'half_interstitial'\n | 'alert'\n | 'pip'\n // Preload-first display types (2026-04-22 §5.4 of the expansion plan)\n | 'carousel_cards'\n | 'sticky_bar'\n | 'progress_bar'\n | 'coachmark_tour'\n | 'product_recommendation';\n sub_type?: string;\n title: string;\n body: string;\n image_url?: string;\n video_url?: string;\n action_url?: string;\n button_text?: string;\n background_color?: string;\n text_color?: string;\n priority: number;\n expires_at?: string;\n frequency?: {\n max_impressions?: number;\n max_impressions_per_day?: number;\n cooldown_seconds?: number;\n // Conversion-aware suppression — added by Micro-Intent Engine P0a (2026-04-30).\n // Mirrors cell-plane FrequencyCap. When the host app calls\n // `notifyConversion(goalName)`, every armed campaign whose\n // `suppress_after_conversion_seconds` is set gets silenced for that\n // many seconds (session-scoped via sessionStorage). Prevents the\n // \"showing 10% off to a buyer who just paid full price\" failure mode.\n suppress_after_conversion_seconds?: number;\n suppress_after_dismiss_seconds?: number;\n scope?: 'session' | 'user' | 'user_sku';\n };\n interactive_config?: Record<string, unknown>;\n client_trigger?: {\n type: string;\n config?: Record<string, unknown>;\n };\n assigned_variant_id?: string;\n // F2 — campaign-delivery surface selection. When the array contains\n // 'embedded_card' AND the page has a `[data-aegis-slot=\"<widget_category>\"]`\n // anchor, the SDK renders this campaign INTO the slot (in addition to or\n // instead of an overlay; see `renderIntoSlots`). Defaults to\n // `['in_app_overlay']` server-side so legacy campaigns keep auto-overlay\n // behavior unchanged.\n delivery_modes?: Array<'in_app_overlay' | 'embedded_card' | 'standalone_page'>;\n // F3 — wide grouping discriminator used as the slot lookup key. A campaign\n // with `widget_category='feedback'` will fill the first\n // `[data-aegis-slot=\"feedback\"]` element on the page (if `embedded_card` is\n // among its delivery_modes).\n widget_category?: 'feedback' | 'loyalty' | 'promo' | 'commerce' | 'support' | 'custom';\n // F2 — page-key for `standalone_page` delivery mode. Read by the Next.js\n // route handler in `apps/cashier-portal/src/app/s/[slug]/[workspace]/[page_key]`.\n // Matches the existing storefront vocabulary (`rewards`/`members`)\n // in apps/cashier-portal/src/app/s/[slug]/{rewards,members}/page.tsx.\n page_key?: 'bill' | 'rewards' | 'feedback' | 'reviews' | 'members';\n}\n\n/**\n * Payload for `campaign-click` lifecycle event. CANCELLABLE — return `false`\n * (or call `evt.preventDefault()`) from the handler to suppress the SDK's\n * default `window.location.href` navigation, e.g. to route via a SPA router.\n */\nexport interface CampaignClickEvent {\n campaign: InAppCampaign;\n /** Sanitized destination URL — already passed through `sanitizeUrl()`. */\n action_url: string;\n /** Best-effort button identifier — populated when the click came from a\n * named button in a multi-button campaign (modals, alerts). `cta` for the\n * primary CTA on single-button campaigns. */\n button_id?: string;\n /** Mutate via `evt.preventDefault()` to signal the SDK to skip its\n * default hard-navigation. Identical semantics to DOM CustomEvent. */\n preventDefault: () => void;\n defaultPrevented: boolean;\n}\n\n/** Payload for `campaign-dismiss` lifecycle event. */\nexport interface CampaignDismissEvent {\n campaign: InAppCampaign;\n /** Source of dismissal — close button, swipe, ESC key, programmatic. */\n source: 'close_button' | 'overlay' | 'esc' | 'auto_timeout' | 'programmatic';\n}\n\n/** Payload for `error` lifecycle event. Aggregates non-fatal SDK errors so\n * host apps can wire them into Sentry / Datadog RUM / similar. */\nexport interface AegisErrorEvent {\n message: string;\n error: unknown;\n /** Optional structured context — `{ event: 'campaign-click' }` for handler\n * throws, `{ campaign_id, stage }` for render path failures, etc. */\n context: Record<string, unknown>;\n}\n\n/**\n * Map of lifecycle event name → handler signature. Used by the typed\n * `on(event, handler)` overload below so TS correctly narrows the payload\n * type per event without a discriminated union at the call site.\n */\nexport interface InAppLifecycleEventMap {\n 'campaigns-loaded': (campaigns: InAppCampaign[]) => void;\n 'campaign-will-show': (campaign: InAppCampaign) => void | false;\n 'campaign-shown': (campaign: InAppCampaign) => void;\n 'campaign-click': (evt: CampaignClickEvent) => void | false;\n 'campaign-dismiss': (evt: CampaignDismissEvent) => void;\n 'error': (evt: AegisErrorEvent) => void;\n}\n\nexport interface AegisInAppConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n organizationId?: string;\n // Resolved by POST /v1/sdk/bootstrap — required in production. Campaigns\n // with a non-empty `target_properties` list are filtered against this on\n // the server, so passing the wrong id (or omitting it) narrows visible\n // campaigns to the property-agnostic ones only.\n propertyId?: string;\n debugMode?: boolean;\n enableSSE?: boolean;\n /**\n * Callback invoked when the manager receives an in-app campaign with\n * a gamification sub_type (spin_wheel / scratch_card). The facade\n * (AegisMessageRuntime) wires this to the widget-renderer path so\n * interactive campaigns render correctly. When this is undefined,\n * gamification campaigns are logged and skipped.\n */\n onInteractiveCampaign?: (campaign: InAppCampaign) => void;\n /**\n * Workspace cascade reader. Set this from the host SDK as\n * `getWorkspaceId: () => aegis.getEffectiveWorkspaceId()` so in-app\n * impressions/clicks/dismisses carry the same workspace context the\n * main analytics SDK sees (URL path slug, ?ws=, setWorkspace runtime).\n * Without it, in-app engagement events arrive at event-ingress with\n * no workspace_id stamped → cross-outlet attribution silently falls\n * back to the org's primary workspace.\n */\n getWorkspaceId?: () => string | undefined;\n}\n\nexport class AegisInAppManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private organizationId?: string;\n private propertyId?: string;\n private debugMode: boolean;\n private enableSSE: boolean;\n private onInteractiveCampaign?: (campaign: InAppCampaign) => void;\n private getWorkspaceId?: () => string | undefined;\n\n private campaigns: InAppCampaign[] = [];\n private displayedCampaigns = new Set<string>();\n // Conversion-aware suppression — campaign_id -> epoch_ms when suppression expires.\n // Populated by `notifyConversion()` and rehydrated from sessionStorage in\n // `refreshCampaigns()` so a navigation between pages preserves silence.\n private suppressedUntil = new Map<string, number>();\n private eventSource?: EventSource;\n private isInitialized = false;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n // sessionStorage key prefix — one entry per goal so we can introspect what\n // converted and when. Format: { ts: epoch_ms }.\n private static readonly CONVERSION_STORAGE_PREFIX = 'aegis_conv_';\n\n // ── Lifecycle event bus (1.8.0) ────────────────────────────────────────\n // Typed callback registry — host apps register handlers via the public\n // `on*` methods below. Each handler is stored once; calling the\n // returned unsubscribe fn removes it. Multiple subscribers per event are\n // supported. Callbacks that return `false` (or call `evt.preventDefault()`\n // on cancellable events) signal the SDK to skip its default behaviour.\n //\n // Industry parity: matches CleverTap `notificationCallback`, WebEngage\n // `onCallback`, MoEngage `onSelfHandledClick`, Iterable url-delegate, etc.\n // The DOM CustomEvent path (window.dispatchEvent('aegis:campaign-click'))\n // is preserved for backward compat + advanced multi-subscriber + pre-init\n // patterns where typed methods aren't reachable yet.\n private hooks = new Map<string, Set<(...args: unknown[]) => void | false | undefined>>();\n // `aegis.inApp.ready` resolves when the first refreshCampaigns completes\n // — matches CleverTap `onLoad`, WebEngage `onReady`. Lets host apps await\n // a known-good state before reading `campaigns` or registering handlers\n // that need armed campaigns visible.\n private readyResolve!: () => void;\n readonly ready: Promise<void> = new Promise((resolve) => {\n this.readyResolve = resolve;\n });\n\n private emit<T>(eventName: string, payload: T): boolean {\n const handlers = this.hooks.get(eventName);\n if (!handlers || handlers.size === 0) return true;\n let proceed = true;\n for (const handler of handlers) {\n try {\n const result = (handler as (p: T) => void | false | undefined)(payload);\n if (result === false) proceed = false;\n } catch (err) {\n // Don't let a misbehaving host handler crash the SDK render path.\n // Surface via onError but keep going for the remaining handlers.\n this.emitError(err, { event: eventName });\n }\n }\n return proceed;\n }\n\n private emitError(err: unknown, context?: Record<string, unknown>): void {\n const handlers = this.hooks.get('error');\n if (!handlers || handlers.size === 0) {\n // No subscriber — log to console so errors aren't completely silent.\n this.log(`Active Reach SDK error: ${err instanceof Error ? err.message : String(err)}`, 'error');\n return;\n }\n for (const handler of handlers) {\n try {\n (handler as (e: AegisErrorEvent) => void)({\n message: err instanceof Error ? err.message : String(err),\n error: err,\n context: context ?? {},\n });\n } catch {\n // ignore — we already tried to surface\n }\n }\n }\n\n private register<T>(\n eventName: string,\n handler: (payload: T) => void | false | undefined,\n ): () => void {\n if (!this.hooks.has(eventName)) this.hooks.set(eventName, new Set());\n this.hooks.get(eventName)!.add(handler as (...args: unknown[]) => void | false | undefined);\n return () => {\n this.hooks.get(eventName)?.delete(handler as (...args: unknown[]) => void | false | undefined);\n };\n }\n\n /**\n * Subscribe to in-app message lifecycle events. Returns an unsubscribe\n * function — call to remove the handler (matches React `useEffect` cleanup\n * convention).\n *\n * Supported events: `campaigns-loaded`, `campaign-will-show` (cancellable),\n * `campaign-shown`, `campaign-click` (cancellable), `campaign-dismiss`,\n * `error`. Cancellable events: handler returns `false` to suppress the\n * SDK's default behaviour (e.g., prevent the hard navigation on click,\n * skip rendering on will-show).\n */\n on<E extends keyof InAppLifecycleEventMap>(\n event: E,\n handler: InAppLifecycleEventMap[E],\n ): () => void {\n return this.register(event, handler as never);\n }\n\n /** Sugar: subscribe to `campaigns-loaded`. Fires after every successful\n * `/v1/in-app/active` poll with the full active-campaign list. */\n onCampaignsLoaded(\n handler: (campaigns: InAppCampaign[]) => void,\n ): () => void {\n return this.register('campaigns-loaded', handler);\n }\n\n /** Sugar: subscribe to `campaign-will-show`. CANCELLABLE — return `false`\n * to suppress rendering this campaign (host-side suppression rules,\n * route-aware blocking, etc.). */\n onCampaignWillShow(\n handler: (campaign: InAppCampaign) => void | false,\n ): () => void {\n return this.register('campaign-will-show', handler);\n }\n\n /** Sugar: subscribe to `campaign-shown`. Fires after the SDK has painted\n * the campaign to the DOM (post-impression). */\n onCampaignShown(\n handler: (campaign: InAppCampaign) => void,\n ): () => void {\n return this.register('campaign-shown', handler);\n }\n\n /** Sugar: subscribe to `campaign-click`. CANCELLABLE — return `false` (or\n * call `evt.preventDefault()`) to suppress the SDK's default\n * `window.location.href` navigation, e.g. to route via a SPA router. */\n onCampaignClick(\n handler: (evt: CampaignClickEvent) => void | false,\n ): () => void {\n return this.register('campaign-click', handler);\n }\n\n /** Sugar: subscribe to `campaign-dismiss`. Fires when user closes/swipes\n * away the campaign before clicking through. */\n onCampaignDismiss(\n handler: (evt: CampaignDismissEvent) => void,\n ): () => void {\n return this.register('campaign-dismiss', handler);\n }\n\n /** Sugar: subscribe to `error`. Aggregates non-fatal SDK errors — fetch\n * failures, render exceptions, host-handler throws. Wire this into your\n * error monitoring (Sentry, Datadog RUM, etc.). */\n onError(\n handler: (evt: AegisErrorEvent) => void,\n ): () => void {\n return this.register('error', handler);\n }\n \n constructor(config: AegisInAppConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n // Fall back to the SDK's persisted anonymous_id (set on first /v1/sdk/bootstrap\n // and reused across the main analytics SDK) so anonymous storefront users\n // get journey-driven in-app messages keyed to the same identity used in\n // /v1/batch payloads. Without this, polling defaults to \"anonymous\" on the\n // server and bucket-misses every per-contact priority queue.\n this.userId = config.userId ?? readAnonIdFromStorage();\n this.contactId = config.contactId;\n this.organizationId = config.organizationId;\n this.propertyId = config.propertyId;\n this.debugMode = config.debugMode || false;\n // SSE is disabled by default. Kept as opt-in capability for future tenant\n // dashboards that embed the Active Reach SDK and need realtime updates. End-user\n // storefronts must NOT enable this — persistent-per-tab SSE doesn't scale.\n this.enableSSE = config.enableSSE === true;\n this.onInteractiveCampaign = config.onInteractiveCampaign;\n this.getWorkspaceId = config.getWorkspaceId;\n }\n \n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisInApp already initialized');\n return;\n }\n\n // Mark user as returning for subsequent visits\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem('aegis_returning_user', '1');\n }\n\n await this.refreshCampaigns();\n\n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n \n this.isInitialized = true;\n this.log('AegisInApp initialized successfully');\n }\n \n updateUserId(userId: string): void {\n this.userId = userId;\n this.refreshCampaigns();\n }\n \n updateContactId(contactId: string): void {\n this.contactId = contactId;\n this.disconnectSSE();\n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n this.refreshCampaigns();\n }\n\n /**\n * Conversion-aware suppression. Call this when the host app observes a\n * goal event (purchase, order_placed, checkout_completed, or any custom\n * goal the workspace has configured per-campaign).\n *\n * For every armed campaign whose `frequency.suppress_after_conversion_seconds`\n * is set, this:\n * 1. Persists the conversion in sessionStorage (so subsequent page\n * loads in the same tab retain silence).\n * 2. Computes an expiry epoch_ms and stores it in `suppressedUntil`.\n * 3. The two campaign-evaluation paths (`tryDisplayNextCampaign`\n * and `onClientEvent`) skip campaigns whose `suppressedUntil`\n * has not yet passed.\n *\n * Scope: only `session` is implemented in P0a. `user` and `user_sku`\n * scopes degrade to session-only with a debug log; the schema accepts\n * them so future expansion is non-breaking.\n *\n * Safe to call repeatedly for the same goal — each call refreshes the\n * sessionStorage timestamp and re-applies suppression to any campaigns\n * loaded since the last call.\n */\n notifyConversion(goalName: string): void {\n if (!goalName) {\n this.log('notifyConversion called with empty goalName; ignored', 'warn');\n return;\n }\n const ts = Date.now();\n if (typeof sessionStorage !== 'undefined') {\n try {\n sessionStorage.setItem(\n `${AegisInAppManager.CONVERSION_STORAGE_PREFIX}${goalName}`,\n JSON.stringify({ ts }),\n );\n } catch {\n // sessionStorage may be disabled (Safari private mode, blocked\n // third-party context). Suppression still applies for the\n // current page lifetime via the in-memory map.\n }\n }\n this.applySuppressionFromCampaigns(ts);\n this.log(`notifyConversion: ${goalName} — suppressing ${this.suppressedUntil.size} armed campaigns`);\n }\n\n /**\n * Compute and store `suppressedUntil` entries for every armed campaign\n * whose `frequency.suppress_after_conversion_seconds` is set. Called by\n * `notifyConversion()` and `refreshCampaigns()` (the latter rehydrates\n * suppression for newly-loaded campaigns when a prior conversion event\n * exists in sessionStorage).\n *\n * `convertedAt` is the epoch_ms of the conversion; pass `Date.now()`\n * when called from `notifyConversion`, or the `ts` recovered from\n * sessionStorage when rehydrating.\n */\n private applySuppressionFromCampaigns(convertedAt: number): void {\n for (const c of this.campaigns) {\n const seconds = c.frequency?.suppress_after_conversion_seconds;\n if (typeof seconds !== 'number' || seconds <= 0) continue;\n const scope = c.frequency?.scope ?? 'session';\n if (scope !== 'session') {\n // P0a only implements session scope. Schema accepts user / user_sku\n // so future work doesn't break wire compatibility, but for now we\n // degrade to session and log.\n this.log(\n `applySuppressionFromCampaigns: scope=${scope} not implemented; degrading to session for campaign ${c.id}`,\n 'warn',\n );\n }\n const expiresAt = convertedAt + seconds * 1000;\n // Take the max so a later/longer suppression wins over an earlier one.\n const existing = this.suppressedUntil.get(c.id);\n if (existing === undefined || existing < expiresAt) {\n this.suppressedUntil.set(c.id, expiresAt);\n }\n }\n }\n\n /**\n * Recover any prior conversion timestamps from sessionStorage and apply\n * suppression to currently-loaded campaigns. Called from\n * `refreshCampaigns()` after `this.campaigns` is populated. This is the\n * mechanism that prevents a converted buyer from seeing a discount popup\n * on the next page load within the same session.\n */\n private rehydrateSuppressionFromStorage(): void {\n if (typeof sessionStorage === 'undefined') return;\n let earliestTs: number | null = null;\n try {\n for (let i = 0; i < sessionStorage.length; i++) {\n const key = sessionStorage.key(i);\n if (!key || !key.startsWith(AegisInAppManager.CONVERSION_STORAGE_PREFIX)) continue;\n const raw = sessionStorage.getItem(key);\n if (!raw) continue;\n const parsed = JSON.parse(raw) as { ts?: number };\n if (typeof parsed.ts === 'number') {\n earliestTs = earliestTs === null ? parsed.ts : Math.min(earliestTs, parsed.ts);\n }\n }\n } catch {\n // Malformed entries are silently skipped — suppression simply\n // doesn't apply for them.\n return;\n }\n if (earliestTs !== null) {\n this.applySuppressionFromCampaigns(earliestTs);\n }\n }\n\n /**\n * Returns true if the campaign is currently suppressed (Date.now() is\n * before the stored expiry). Used by both display paths.\n */\n private isSuppressed(campaignId: string): boolean {\n const expiresAt = this.suppressedUntil.get(campaignId);\n if (expiresAt === undefined) return false;\n if (Date.now() >= expiresAt) {\n this.suppressedUntil.delete(campaignId);\n return false;\n }\n return true;\n }\n \n private connectSSE(): void {\n if (this.eventSource) {\n this.disconnectSSE();\n }\n \n if (!this.organizationId) {\n this.log('Cannot connect SSE without organization ID', 'warn');\n return;\n }\n \n const url = new URL('/v1/stream/realtime', this.apiHost);\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n \n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n \n const queryParams = new URLSearchParams();\n Object.entries(headers).forEach(([key, value]) => {\n queryParams.append(key, value);\n });\n \n this.eventSource = new EventSource(`${url}?${queryParams.toString()}`);\n \n this.eventSource.addEventListener('open', () => {\n this.log('SSE connection established');\n this.reconnectAttempts = 0;\n });\n \n this.eventSource.addEventListener('in_app_campaign_updated', (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data);\n this.log(`Received in-app campaign update: ${data.campaign_id}`);\n this.refreshCampaigns();\n } catch (error) {\n this.log(`Error parsing SSE event: ${error}`, 'error');\n }\n });\n \n this.eventSource.addEventListener('heartbeat', () => {\n this.log('SSE heartbeat received');\n });\n\n this.eventSource.addEventListener('error', () => {\n this.log('SSE connection error', 'error');\n\n if (this.eventSource?.readyState === EventSource.CLOSED) {\n this.attemptReconnect();\n }\n });\n }\n \n private disconnectSSE(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = undefined;\n this.log('SSE connection closed');\n }\n }\n \n private attemptReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log('Max reconnect attempts reached, giving up', 'warn');\n return;\n }\n \n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n \n this.log(`Reconnecting SSE in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n setTimeout(() => {\n if (this.isInitialized && this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n }, delay);\n }\n \n private async refreshCampaigns(): Promise<void> {\n try {\n // Build URL with client context for server-side targeting (Critical Fix B)\n const context = new URLSearchParams({\n device_type: this.detectDeviceType(),\n page_url: typeof window !== 'undefined' ? window.location.pathname : '/',\n });\n\n // Geo is resolved server-side from IP — no need to send\n context.set('is_new_user', this.isNewUser() ? 'true' : 'false');\n\n const url = `${this.apiHost}/v1/in-app/active?${context.toString()}`;\n\n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n\n if (this.userId) {\n headers['X-User-ID'] = this.userId;\n }\n\n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n\n if (this.organizationId) {\n headers['X-Organization-ID'] = this.organizationId;\n }\n\n if (this.propertyId) {\n headers['X-Property-Id'] = this.propertyId;\n }\n\n // Send cached A/B assignments so server can skip re-evaluation (Critical Fix C)\n const abAssignments = this.getABAssignments();\n if (Object.keys(abAssignments).length > 0) {\n headers['X-AB-Assignments'] = btoa(JSON.stringify(abAssignments));\n }\n\n const response = await fetch(url, {\n method: 'GET',\n headers,\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch campaigns: ${response.status}`);\n }\n\n const payload = await response.json();\n // Server returns an array; defensively handle error bodies (HTML\n // from misconfigured gateways, {} from maintenance endpoints, etc.)\n // so a bad response doesn't turn `updateContactId` into a thrower.\n this.campaigns = Array.isArray(payload) ? payload : [];\n\n // Process A/B assignments from server response (Critical Fix C)\n this.processABAssignments(this.campaigns);\n\n // Rehydrate conversion suppression from sessionStorage so a buyer\n // who converted on a prior page load in the same tab does not see\n // a discount popup on this load. Must run after this.campaigns is\n // populated and BEFORE tryDisplayNextCampaign so the first display\n // attempt honors suppression.\n this.rehydrateSuppressionFromStorage();\n\n this.log(`Fetched ${this.campaigns.length} campaigns`);\n\n // Lifecycle: `campaigns-loaded` — fires after every successful refresh\n // with the full active-campaign list. Host apps subscribe to drive\n // route-aware allowlists, sticky inboxes, badge counts, etc.\n this.emit('campaigns-loaded', this.campaigns);\n\n // Resolve `aegis.inApp.ready` after the FIRST successful poll so host\n // apps can `await aegis.inApp.ready` before reading `campaigns` or\n // making decisions that depend on a known-good initial state. Idempotent\n // — calling resolve() twice is a no-op for an already-settled promise.\n this.readyResolve();\n\n this.tryDisplayNextCampaign();\n\n // F2/F3 / U8 — fill any `[data-aegis-slot]` anchors with matching\n // `embedded_card`-mode campaigns. Independent of the overlay path:\n // a campaign with delivery_modes=[overlay,card] renders both ways\n // (overlay if conditions match + card if the page has a slot).\n // No-op when no slots are on the page.\n this.renderIntoSlots();\n\n // PR-B U7 — also fill any `[data-aegis-token-data]` anchors with\n // a server-injected campaign payload. Used by the standalone-page\n // route at `/s/[slug]/[workspace]/[page_key]?t=<token>`: the page\n // handler verifies the token server-side (via F16 internal),\n // embeds the resolved campaign data inline, and the SDK uses the\n // SAME renderer dispatch as the slot path. No separate widget\n // code, no public token-resolve endpoint.\n this.renderTokenAnchors();\n\n } catch (error) {\n this.emitError(error, { stage: 'refresh-campaigns' });\n this.log(`Error refreshing campaigns: ${error}`, 'error');\n }\n }\n\n // --- Client Context Helpers (Critical Fix B) ---\n\n private detectDeviceType(): 'mobile' | 'desktop' | 'tablet' {\n if (typeof navigator === 'undefined') return 'desktop';\n const ua = navigator.userAgent;\n if (/Mobi|Android/i.test(ua)) return 'mobile';\n if (/iPad|Tablet/i.test(ua)) return 'tablet';\n return 'desktop';\n }\n\n private isNewUser(): boolean {\n if (typeof localStorage === 'undefined') return true;\n return !localStorage.getItem('aegis_returning_user');\n }\n\n // --- A/B Assignment Persistence (Critical Fix C) ---\n\n private getABAssignments(): Record<string, string> {\n if (typeof localStorage === 'undefined') return {};\n try {\n return JSON.parse(localStorage.getItem('aegis_ab_assignments') || '{}');\n } catch {\n return {};\n }\n }\n\n private processABAssignments(campaigns: InAppCampaign[]): void {\n if (typeof localStorage === 'undefined') return;\n const stored = this.getABAssignments();\n for (const campaign of campaigns) {\n if (campaign.assigned_variant_id) {\n stored[campaign.id] = campaign.assigned_variant_id;\n }\n }\n localStorage.setItem('aegis_ab_assignments', JSON.stringify(stored));\n }\n\n private getVariantId(campaignId: string): string | undefined {\n const assignments = this.getABAssignments();\n return assignments[campaignId] ?? undefined;\n }\n \n private tryDisplayNextCampaign(): void {\n // Only auto-display campaigns that DO NOT have a client_trigger.\n // Trigger-gated campaigns wait for the host app to call\n // `onClientEvent()` or for TriggerEngine to fire the matching\n // behavioural event. This preserves the preload-first contract —\n // the campaign is armed but the render is gated.\n // Conversion-aware suppression: campaigns silenced by a prior\n // notifyConversion() call (or rehydrated from sessionStorage) are\n // skipped until their suppression window expires.\n const campaign = this.campaigns.find((c) =>\n !this.displayedCampaigns.has(c.id) && !c.client_trigger && !this.isSuppressed(c.id),\n );\n\n if (campaign) {\n this.displayCampaign(campaign);\n }\n }\n\n // --- F2/F3 / U8: embedded-card slot rendering ---------------------------\n //\n // Tracks slot DOM elements we've already filled so a second\n // refresh-cycle (SSE update, polling tick) doesn't re-render the same\n // campaign on top of itself. WeakSet so DOM removals get GC'd.\n private filledSlots = new WeakSet<Element>();\n\n /**\n * Scan the page for `[data-aegis-slot]` anchors and render any\n * campaign whose `widget_category` matches the slot key AND whose\n * `delivery_modes` includes `'embedded_card'`.\n *\n * Runs after every refresh-campaigns success — the WeakSet guard\n * keeps it idempotent across re-fetches. The slot key is the campaign's\n * `widget_category` (the wide grouping discriminator from F3), so a\n * `<div data-aegis-slot=\"feedback\">` will accept the first active\n * campaign with `widget_category='feedback'` + embedded_card mode.\n */\n private renderIntoSlots(): void {\n if (typeof document === 'undefined') return;\n const slots = document.querySelectorAll('[data-aegis-slot]');\n if (slots.length === 0) return;\n\n // First-active-per-category wins. Caller-side ordering of\n // `this.campaigns` is priority-sorted by the cell-plane endpoint.\n const eligibleByCategory = new Map<string, InAppCampaign>();\n for (const c of this.campaigns) {\n const modes = c.delivery_modes;\n const category = c.widget_category;\n if (!modes || !modes.includes('embedded_card')) continue;\n if (!category) continue;\n if (!eligibleByCategory.has(category)) {\n eligibleByCategory.set(category, c);\n }\n }\n if (eligibleByCategory.size === 0) return;\n\n slots.forEach((slot) => {\n if (this.filledSlots.has(slot)) return;\n const key = slot.getAttribute('data-aegis-slot');\n if (!key) return;\n const campaign = eligibleByCategory.get(key);\n if (!campaign) return;\n this.renderCampaignIntoSlot(campaign, slot as HTMLElement);\n this.filledSlots.add(slot);\n });\n }\n\n /**\n * U7 — render server-injected token-bound campaigns.\n *\n * The standalone-page route at `apps/cashier-portal/src/app/s/[slug]/\n * [workspace]/[page_key]/page.tsx` (and the `/c/[token]` Aegis-hosted\n * fallback) verify the URL token server-side via the F16 internal\n * endpoint, then embed the resolved campaign payload as JSON inside\n * a `<div data-aegis-token-data=\"...\">` anchor. This SDK method\n * scans for those anchors, parses the payload, and dispatches to\n * the SAME `renderCampaignIntoSlot` used by the [data-aegis-slot]\n * path — so widget rendering code is single-sourced.\n *\n * Why server-injected (not client-fetched): keeps INTERNAL_API_SECRET\n * server-side, avoids an extra browser round-trip for verify, gives\n * Next.js SSR-friendly initial HTML for SEO/no-JS users (rendered\n * via the page handler's React tree alongside the SDK anchor).\n *\n * Idempotency: filledSlots is the same WeakSet used by\n * renderIntoSlots — re-running on SSE/poll refresh is a no-op for\n * already-filled anchors.\n */\n private renderTokenAnchors(): void {\n if (typeof document === 'undefined') return;\n const anchors = document.querySelectorAll('[data-aegis-token-data]');\n if (anchors.length === 0) return;\n\n anchors.forEach((anchor) => {\n if (this.filledSlots.has(anchor)) return;\n const raw = anchor.getAttribute('data-aegis-token-data');\n if (!raw) return;\n\n let payload:\n | { campaign?: InAppCampaign; submit_url?: string }\n | null = null;\n try {\n payload = JSON.parse(raw);\n } catch (e) {\n this.log(\n `data-aegis-token-data: invalid JSON, skipping anchor (${e})`,\n 'warn',\n );\n this.filledSlots.add(anchor); // don't retry on next refresh\n return;\n }\n if (!payload || !payload.campaign || !payload.campaign.id) {\n this.log(\n 'data-aegis-token-data: missing `campaign` in payload, skipping',\n 'warn',\n );\n this.filledSlots.add(anchor);\n return;\n }\n\n // U7 step 3 — pass the submit_url through to the renderer so\n // click handlers can POST the structured response (rating value,\n // NPS score, etc.) and atomically consume the token.\n this.renderCampaignIntoSlot(\n payload.campaign,\n anchor as HTMLElement,\n { submitUrl: payload.submit_url },\n );\n this.filledSlots.add(anchor);\n });\n }\n\n /**\n * Slot-mode counterpart to `displayCampaign`. Reuses the existing\n * lifecycle hooks (`campaign-will-show`, `displayedCampaigns`,\n * `campaign-shown`, impression tracking) so analytics + frequency\n * caps work the same way as overlay renders.\n *\n * Only the sub_types that make UX sense embedded are handled; the\n * rest (modal, full_screen, half_interstitial, alert, pip) silently\n * skip — those types only make sense as fullscreen overlays.\n */\n private renderCampaignIntoSlot(\n campaign: InAppCampaign,\n target: HTMLElement,\n options?: { submitUrl?: string },\n ): void {\n const proceed = this.emit('campaign-will-show', campaign);\n if (!proceed) {\n this.log(\n `slot campaign ${campaign.id} suppressed by campaign-will-show handler`,\n );\n return;\n }\n\n this.displayedCampaigns.add(campaign.id);\n this.addAnimationStyles();\n\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const bg = this.sanitizeColor(campaign.background_color || '#4169e1');\n const text = this.sanitizeColor(campaign.text_color || '#ffffff');\n const submitUrl = options?.submitUrl;\n\n let rendered = false;\n switch (campaign.sub_type) {\n case 'star_rating':\n rendered = this.renderStarRatingSlot(\n campaign, ic, bg, text, target, submitUrl,\n );\n break;\n case 'nps_survey':\n rendered = this.renderNPSSurveySlot(\n campaign, ic, bg, text, target, submitUrl,\n );\n break;\n // Future sub_types (quick_poll, countdown_offer, quiz, sticky_bar,\n // progress_bar, carousel_cards, product_recommendation) get added\n // here as merchants need them embedded. Until then, fall through\n // to the warn below — campaigns will still render as overlays via\n // the parallel `tryDisplayNextCampaign` path.\n default:\n this.log(\n `slot mode not yet supported for sub_type: ${campaign.sub_type ?? campaign.type}`,\n 'warn',\n );\n // Roll back the displayed marker so the overlay path can still\n // run — slot fallback shouldn't burn the campaign.\n this.displayedCampaigns.delete(campaign.id);\n return;\n }\n\n if (!rendered) return;\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n\n /**\n * Slot-mode wrappers. They build a small card container (no overlay\n * positioning, no close button) and append the SHARED body element\n * the overlay path also uses. Body construction lives in the\n * `_buildXxxBody` helpers below so visual output stays identical\n * between overlay and slot modes — adding a renderer feature in\n * one place updates both.\n */\n private renderStarRatingSlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n submitUrl?: string,\n ): boolean {\n const card = this._wrapInSlotCard(\n 'aegis-in-app-rating-card', campaign.id, bg, text,\n );\n card.appendChild(\n this._buildStarRatingBody(campaign, ic, text, 'slot', submitUrl),\n );\n target.appendChild(card);\n return true;\n }\n\n private renderNPSSurveySlot(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string,\n target: HTMLElement,\n submitUrl?: string,\n ): boolean {\n const card = this._wrapInSlotCard(\n 'aegis-in-app-nps-card', campaign.id, bg, text,\n );\n card.appendChild(\n this._buildNPSSurveyBody(campaign, ic, text, 'slot', submitUrl),\n );\n target.appendChild(card);\n return true;\n }\n\n /**\n * Token-bound submission helper. POSTs the structured response to\n * the per-token submit URL the page handler embedded. Best-effort\n * — failure is logged but doesn't throw (the customer already\n * clicked; we can't undo their interaction).\n */\n private _submitTokenResponse(\n submitUrl: string,\n payload: Record<string, unknown>,\n ): void {\n if (!submitUrl) return;\n // Fire-and-forget; we don't gate the UI on the response.\n fetch(submitUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ response: payload }),\n keepalive: true,\n }).catch((err) => {\n this.log(`token submit failed: ${err}`, 'warn');\n });\n }\n\n // --- Shared body builders (used by both overlay + slot paths) ----------\n //\n // Sizing: `'overlay'` matches the legacy fullscreen-modal scale (24px\n // padding, 18px title, 32px stars). `'slot'` is the embedded card\n // scale (20px padding, 16px title, 28px stars) — proportional, doesn't\n // dwarf merchant page chrome. Adding a new variant means a new tuple\n // here, not a new renderer.\n\n private _wrapInSlotCard(\n className: string,\n campaignId: string,\n bg: string,\n text: string,\n ): HTMLElement {\n const card = document.createElement('div');\n card.className = className;\n card.setAttribute('data-campaign-id', campaignId);\n card.style.cssText = `\n width: 100%; border-radius: 12px; overflow: hidden;\n background: ${bg}; color: ${text};\n box-shadow: 0 4px 12px rgba(0,0,0,0.06);\n `;\n return card;\n }\n\n private _buildStarRatingBody(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n text: string,\n variant: 'overlay' | 'slot',\n submitUrl?: string,\n ): HTMLElement {\n const isOverlay = variant === 'overlay';\n const padding = isOverlay ? '24px' : '20px';\n const titleSize = isOverlay ? '18px' : '16px';\n const titleWeight = isOverlay ? '700' : '600';\n const titleMargin = isOverlay ? '16px' : '12px';\n const starSize = isOverlay ? '32px' : '28px';\n const starsMarginBottom = isOverlay ? '16px' : '0';\n const hoverScale = isOverlay ? '1.2' : '1.15';\n\n const body = document.createElement('div');\n body.style.cssText = `padding: ${padding}; text-align: center;`;\n\n const title = document.createElement('div');\n title.style.cssText =\n `font-size: ${titleSize}; font-weight: ${titleWeight}; margin-bottom: ${titleMargin};`;\n title.textContent = campaign.title || 'Rate your experience';\n body.appendChild(title);\n\n const stars = document.createElement('div');\n stars.style.cssText =\n `display: flex; gap: 8px; justify-content: center;` +\n (starsMarginBottom !== '0' ? ` margin-bottom: ${starsMarginBottom};` : '');\n const maxStars = (ic.rating_scale as number) || 5;\n for (let i = 1; i <= maxStars; i++) {\n const star = document.createElement('span');\n star.style.cssText =\n `font-size: ${starSize}; cursor: pointer; transition: transform 0.1s; user-select: none;` +\n (text ? ` color: ${text};` : '');\n star.textContent = '☆'; // ☆\n const value = i;\n star.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (submitUrl) {\n this._submitTokenResponse(submitUrl, {\n sub_type: 'star_rating', value,\n });\n }\n });\n star.addEventListener('mouseenter', () => {\n star.style.transform = `scale(${hoverScale})`;\n });\n star.addEventListener('mouseleave', () => {\n star.style.transform = 'scale(1)';\n });\n stars.appendChild(star);\n }\n body.appendChild(stars);\n return body;\n }\n\n private _buildNPSSurveyBody(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n text: string,\n variant: 'overlay' | 'slot',\n submitUrl?: string,\n ): HTMLElement {\n const isOverlay = variant === 'overlay';\n const padding = isOverlay ? '24px' : '20px';\n const titleAlign = isOverlay ? 'center' : 'center';\n\n const body = document.createElement('div');\n body.style.cssText = `padding: ${padding};` +\n (isOverlay ? ' text-align: center;' : '');\n\n const title = document.createElement('div');\n title.style.cssText =\n `font-size: 16px; font-weight: ${isOverlay ? '700' : '600'}; ` +\n `margin-bottom: ${isOverlay ? '16px' : '12px'}; text-align: ${titleAlign};`;\n title.textContent =\n (ic.nps_question as string) || campaign.title ||\n 'How likely are you to recommend us?';\n body.appendChild(title);\n\n if (isOverlay) {\n // Overlay variant uses the round-pip scale + Not/Very labels\n // below — keeps the legacy modal look pixel-identical.\n const scale = document.createElement('div');\n scale.style.cssText =\n 'display: flex; gap: 4px; justify-content: center; flex-wrap: wrap; margin-bottom: 12px;';\n for (let i = 0; i <= 10; i++) {\n const btn = document.createElement('span');\n btn.style.cssText = `\n width: 28px; height: 28px; border-radius: 6px; display: flex;\n align-items: center; justify-content: center; font-size: 11px;\n font-weight: 600; cursor: pointer; background: ${text}33; color: ${text};\n transition: transform 0.1s;\n `;\n btn.textContent = String(i);\n const value = i;\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (submitUrl) {\n this._submitTokenResponse(submitUrl, {\n sub_type: 'nps_survey', value,\n });\n }\n });\n scale.appendChild(btn);\n }\n body.appendChild(scale);\n\n const labels = document.createElement('div');\n labels.style.cssText =\n 'display: flex; justify-content: space-between; font-size: 11px; opacity: 0.6; margin-bottom: 16px;';\n const notLikely = document.createElement('span');\n notLikely.textContent = 'Not likely';\n const veryLikely = document.createElement('span');\n veryLikely.textContent = 'Very likely';\n labels.appendChild(notLikely);\n labels.appendChild(veryLikely);\n body.appendChild(labels);\n return body;\n }\n\n // Slot variant: 11-column grid of bordered buttons — fits inline\n // card width without scrolling. No Not/Very labels (slot is small\n // enough that the customer sees both ends without prompting).\n const grid = document.createElement('div');\n grid.style.cssText =\n 'display: grid; grid-template-columns: repeat(11, 1fr); gap: 4px;';\n for (let n = 0; n <= 10; n++) {\n const btn = document.createElement('button');\n btn.style.cssText = `\n padding: 8px 0; border-radius: 6px; border: 1px solid ${text}33;\n background: transparent; color: ${text}; font-size: 13px; font-weight: 600;\n cursor: pointer; transition: background 0.15s;\n `;\n btn.textContent = String(n);\n const value = n;\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (submitUrl) {\n this._submitTokenResponse(submitUrl, {\n sub_type: 'nps_survey', value,\n });\n }\n });\n grid.appendChild(btn);\n }\n body.appendChild(grid);\n return body;\n }\n\n /**\n * Evaluate the currently armed campaigns against a client-side event\n * and render any that match their `client_trigger`.\n *\n * Called by the host app (e.g., the EcommerceTracker on product_viewed),\n * or by future TriggerEngine bridges. Safe to call repeatedly for the\n * same event — dedup happens via `displayedCampaigns`.\n *\n * Supported trigger types (align with the cell-plane server-side\n * `display_rules.trigger_type`):\n * - `custom_event` : fire when eventName matches config.event\n * - `product_match` : fire on `product_viewed` when eventData.product_id\n * matches config.product_id (string or string[])\n * - `delay` : client-side setTimeout — evaluated at armeng\n * time (kicked off from `displayCampaign`), not here.\n */\n onClientEvent(eventName: string, eventData: Record<string, unknown> = {}): void {\n for (const c of this.campaigns) {\n if (this.displayedCampaigns.has(c.id)) continue;\n if (!c.client_trigger) continue;\n // Conversion-aware suppression: trigger-gated campaigns silenced by\n // a prior notifyConversion() call are skipped until expiry.\n if (this.isSuppressed(c.id)) continue;\n if (this.matchesClientTrigger(c.client_trigger, eventName, eventData)) {\n this.displayCampaign(c);\n }\n }\n }\n\n private matchesClientTrigger(\n trigger: { type: string; config?: Record<string, unknown> },\n eventName: string,\n eventData: Record<string, unknown>,\n ): boolean {\n const cfg = trigger.config || {};\n switch (trigger.type) {\n case 'custom_event':\n return typeof cfg.event === 'string' && cfg.event === eventName;\n case 'product_match': {\n if (eventName !== 'product_viewed' && eventName !== 'product_view') {\n return false;\n }\n const wantedRaw = cfg.product_id;\n const wanted: string[] = Array.isArray(wantedRaw)\n ? (wantedRaw as string[])\n : typeof wantedRaw === 'string'\n ? [wantedRaw]\n : [];\n if (wanted.length === 0) return false;\n const actual = String(\n eventData.product_id ??\n eventData.productId ??\n (eventData.product as Record<string, unknown> | undefined)?.id ??\n '',\n );\n return wanted.includes(actual);\n }\n default:\n // Unknown or server-evaluated — do not auto-fire from the client.\n return false;\n }\n }\n \n private displayCampaign(campaign: InAppCampaign): void {\n // Lifecycle: `campaign-will-show` — CANCELLABLE. Host returns `false` to\n // suppress render (e.g. route-aware blocking, host-side frequency caps,\n // self-handled mode). When suppressed we DO NOT mark the campaign as\n // displayed so a subsequent `tryDisplayNextCampaign` (after the host\n // unblocks) can re-evaluate it.\n const proceed = this.emit('campaign-will-show', campaign);\n if (!proceed) {\n this.log(`campaign ${campaign.id} suppressed by campaign-will-show handler`);\n return;\n }\n\n this.displayedCampaigns.add(campaign.id);\n\n // Sub-type routing: interactive types are handled by dedicated renderers\n const interactiveSubTypes = new Set([\n 'spin_wheel', 'scratch_card', 'nps_survey', 'quiz',\n 'countdown_offer', 'star_rating', 'quick_poll',\n ]);\n\n if (campaign.sub_type && interactiveSubTypes.has(campaign.sub_type)) {\n this.renderInteractive(campaign);\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n switch (campaign.type) {\n case 'modal':\n this.renderModal(campaign);\n break;\n case 'banner':\n this.renderBanner(campaign);\n break;\n case 'full_screen':\n this.renderFullScreen(campaign);\n break;\n case 'half_interstitial':\n this.renderHalfInterstitial(campaign);\n break;\n case 'alert':\n this.renderAlert(campaign);\n break;\n case 'pip':\n this.renderPIP(campaign);\n break;\n case 'tooltip':\n this.renderTooltip(campaign);\n break;\n // Preload-first display types (2026-04-22). These renderers own their\n // own impression tracking so we don't double-count alongside the\n // trailing `this.trackEvent(... 'impression')` below — return early.\n case 'carousel_cards':\n renderCarouselCards(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'sticky_bar':\n renderStickyBar(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'progress_bar':\n renderProgressBar(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n case 'coachmark_tour':\n // coachmark_tour fires its own impression/dismiss/click events\n // from within its state machine — don't double-report here.\n renderCoachmarkTour(this.buildRenderContext(campaign));\n this.emit('campaign-shown', campaign);\n return;\n case 'product_recommendation':\n renderProductRecommendation(this.buildRenderContext(campaign));\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n return;\n }\n\n this.trackEvent(campaign.id, 'impression');\n this.emit('campaign-shown', campaign);\n }\n\n /**\n * Build the shared context passed into the preload-first renderers.\n * Matches the interface in `./renderers/types.ts`. Kept as a private\n * method (rather than inlined at each call-site) so future renderer\n * additions stay consistent and easy to audit.\n */\n private buildRenderContext(campaign: InAppCampaign): RenderContext {\n return {\n campaign,\n trackEvent: (id, evt) => {\n void this.trackEvent(id, evt);\n },\n sanitizeUrl: (url: string) => this.sanitizeUrl(url),\n sanitizeColor: (color: string) => this.sanitizeColor(color),\n log: (msg: string, level?: 'log' | 'warn' | 'error') => this.log(msg, level),\n addAnimationStyles: () => this.addAnimationStyles(),\n };\n }\n\n /**\n * Renders interactive sub-type campaigns (spin wheel, NPS, quiz, etc.)\n * using DOM-safe rendering. These sub-types use the campaign's\n * interactive_config payload for type-specific behavior.\n */\n private renderInteractive(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const bg = this.sanitizeColor(campaign.background_color || '#4169e1');\n const text = this.sanitizeColor(campaign.text_color || '#ffffff');\n\n switch (campaign.sub_type) {\n case 'nps_survey':\n this.renderNPSSurvey(campaign, ic, bg, text);\n break;\n case 'countdown_offer':\n this.renderCountdownOffer(campaign, ic, bg, text);\n break;\n case 'star_rating':\n this.renderStarRating(campaign, ic, bg, text);\n break;\n case 'quick_poll':\n this.renderQuickPoll(campaign, ic, bg, text);\n break;\n case 'quiz':\n this.renderQuiz(campaign, ic, bg, text);\n break;\n case 'spin_wheel':\n case 'scratch_card':\n if (this.onInteractiveCampaign) {\n this.onInteractiveCampaign(campaign);\n } else {\n this.log(\n `${campaign.sub_type} campaign received but no onInteractiveCampaign handler wired — install via AegisMessageRuntime`,\n 'warn',\n );\n }\n break;\n default:\n this.log(`Unknown interactive sub_type: ${campaign.sub_type}`, 'warn');\n this.renderModal(campaign);\n }\n }\n\n // --- Interactive Renderers ---\n\n private renderNPSSurvey(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-nps-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 360px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = this._buildNPSSurveyBody(campaign, ic, text, 'overlay');\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderCountdownOffer(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-countdown-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 20px; font-weight: 700; margin-bottom: 8px;';\n title.textContent = campaign.title || 'Flash Sale';\n body.appendChild(title);\n\n const label = document.createElement('div');\n label.style.cssText = 'font-size: 13px; opacity: 0.8; margin-bottom: 12px;';\n label.textContent = (ic.countdown_label as string) || 'Sale ends in:';\n body.appendChild(label);\n\n // Countdown digits\n const digits = document.createElement('div');\n digits.style.cssText = 'display: flex; gap: 8px; justify-content: center; margin-bottom: 16px;';\n const digitStyle = `padding: 8px 12px; border-radius: 8px; font-size: 24px; font-weight: 700; font-family: monospace; background: ${text}22;`;\n for (const val of ['00', ':', '00', ':', '00']) {\n const el = document.createElement('span');\n if (val === ':') {\n el.style.cssText = 'font-size: 24px; font-weight: 700; align-self: center;';\n } else {\n el.style.cssText = digitStyle;\n }\n el.textContent = val;\n digits.appendChild(el);\n }\n body.appendChild(digits);\n\n // Start countdown from target or 2h default\n const targetStr = ic.countdown_target as string | undefined;\n if (targetStr) {\n const target = new Date(targetStr).getTime();\n const update = () => {\n const diff = Math.max(0, target - Date.now());\n const h = String(Math.floor(diff / 3600000)).padStart(2, '0');\n const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2, '0');\n const s = String(Math.floor((diff % 60000) / 1000)).padStart(2, '0');\n const spans = digits.querySelectorAll('span');\n if (spans.length >= 5) {\n spans[0].textContent = h;\n spans[2].textContent = m;\n spans[4].textContent = s;\n }\n if (diff > 0) requestAnimationFrame(update);\n };\n update();\n }\n\n if (campaign.body) {\n const desc = document.createElement('div');\n desc.style.cssText = 'font-size: 14px; opacity: 0.85; margin-bottom: 16px;';\n desc.textContent = campaign.body;\n body.appendChild(desc);\n }\n\n if (campaign.button_text) {\n const btn = this.createCTAButton(campaign, bg, text);\n body.appendChild(btn);\n }\n\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderStarRating(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-rating-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = this._buildStarRatingBody(campaign, ic, text, 'overlay');\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderQuickPoll(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-poll-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 320px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 16px; font-weight: 700; margin-bottom: 16px;';\n title.textContent = campaign.title || 'Quick question';\n body.appendChild(title);\n\n const options = (ic.poll_options as string[]) || [];\n const optionsList = document.createElement('div');\n optionsList.style.cssText = 'display: flex; flex-direction: column; gap: 8px; margin-bottom: 16px;';\n for (const opt of options) {\n const optBtn = document.createElement('button');\n optBtn.style.cssText = `\n padding: 10px 16px; border-radius: 10px; border: 1px solid ${text}33;\n background: transparent; color: ${text}; font-size: 14px; cursor: pointer;\n text-align: left; transition: background 0.15s;\n `;\n optBtn.textContent = opt;\n optBtn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n });\n optionsList.appendChild(optBtn);\n }\n body.appendChild(optionsList);\n\n this.addCloseButton(body, overlay, campaign.id);\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n private renderQuiz(\n campaign: InAppCampaign,\n ic: Record<string, unknown>,\n bg: string,\n text: string\n ): void {\n const overlay = this.createOverlay('aegis-in-app-quiz-overlay');\n const modal = document.createElement('div');\n modal.style.cssText = `\n max-width: 360px; width: 90%; border-radius: 16px; overflow: hidden;\n background: ${bg}; color: ${text}; animation: aegisScaleIn 0.3s ease;\n box-shadow: 0 20px 60px rgba(0,0,0,0.15);\n `;\n\n const body = document.createElement('div');\n body.style.cssText = 'padding: 24px; text-align: center;';\n\n const title = document.createElement('div');\n title.style.cssText = 'font-size: 18px; font-weight: 700; margin-bottom: 8px;';\n title.textContent = campaign.title || 'Quiz';\n body.appendChild(title);\n\n const questions = (ic.questions as Array<{ question: string; options: string[] }>) || [];\n let currentQ = 0;\n\n const renderQuestion = () => {\n // Clear previous content except title\n while (body.childNodes.length > 1) {\n body.removeChild(body.lastChild!);\n }\n\n if (currentQ >= questions.length) {\n const result = document.createElement('div');\n result.style.cssText = 'font-size: 16px; margin: 16px 0;';\n result.textContent = (ic.thank_you_message as string) || 'Thanks for completing the quiz!';\n body.appendChild(result);\n this.trackEvent(campaign.id, 'clicked');\n this.addCloseButton(body, overlay, campaign.id);\n return;\n }\n\n const q = questions[currentQ];\n const progress = document.createElement('div');\n progress.style.cssText = 'font-size: 12px; opacity: 0.6; margin-bottom: 12px;';\n progress.textContent = `Question ${currentQ + 1} of ${questions.length}`;\n body.appendChild(progress);\n\n const questionText = document.createElement('div');\n questionText.style.cssText = 'font-size: 14px; font-weight: 600; margin-bottom: 16px;';\n questionText.textContent = q.question;\n body.appendChild(questionText);\n\n const optionsDiv = document.createElement('div');\n optionsDiv.style.cssText = 'display: flex; flex-direction: column; gap: 8px;';\n for (const opt of q.options) {\n const optBtn = document.createElement('button');\n optBtn.style.cssText = `\n padding: 10px 16px; border-radius: 10px; border: 1px solid ${text}33;\n background: transparent; color: ${text}; font-size: 14px; cursor: pointer;\n text-align: left; transition: background 0.15s;\n `;\n optBtn.textContent = opt;\n optBtn.addEventListener('click', () => {\n currentQ++;\n renderQuestion();\n });\n optionsDiv.appendChild(optBtn);\n }\n body.appendChild(optionsDiv);\n this.addCloseButton(body, overlay, campaign.id);\n };\n\n renderQuestion();\n modal.appendChild(body);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n\n // --- Shared Rendering Helpers ---\n\n private createOverlay(className: string): HTMLElement {\n const overlay = document.createElement('div');\n overlay.className = className;\n overlay.style.cssText = `\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.5); display: flex; align-items: center;\n justify-content: center; z-index: 99999; animation: aegisFadeIn 0.3s ease;\n `;\n return overlay;\n }\n\n private createCTAButton(campaign: InAppCampaign, bg: string, text: string): HTMLElement {\n const btn = document.createElement('button');\n btn.style.cssText = `\n display: inline-block; padding: 10px 28px; border-radius: 999px;\n font-size: 14px; font-weight: 600; cursor: pointer; border: none;\n background: ${text}; color: ${bg}; transition: transform 0.15s;\n `;\n btn.textContent = campaign.button_text || 'OK';\n btn.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n if (campaign.action_url) {\n const safeUrl = this.sanitizeUrl(campaign.action_url);\n if (safeUrl) window.open(safeUrl, '_blank');\n }\n });\n return btn;\n }\n\n private addCloseButton(container: HTMLElement, overlay: HTMLElement, campaignId: string): void {\n const close = document.createElement('div');\n close.style.cssText = 'margin-top: 12px; font-size: 12px; opacity: 0.6; cursor: pointer;';\n close.textContent = 'Close';\n close.addEventListener('click', () => {\n this.trackEvent(campaignId, 'dismissed');\n this.removeModal(overlay);\n });\n container.appendChild(close);\n }\n \n /**\n * Navigate to a campaign CTA URL.\n *\n * Dispatches a cancellable `aegis:campaign-click` CustomEvent so host\n * apps (e.g., the storefront SPA) can intercept and route in-place via\n * their client-side router, avoiding a full page reload that re-runs\n * SSR + re-renders the location picker / banner / cart drawer state.\n *\n * The host calls `event.preventDefault()` to suppress the hard nav and\n * handle the URL itself. If no listener calls preventDefault, the SDK\n * falls through to `window.location.href` so plain HTML pages without a\n * SPA still get the user to the destination.\n *\n * Industry parallel: CleverTap dispatches `clevertap-deeplink-click`,\n * MoEngage dispatches `MoE_inappRedirect`. The pattern lets the same\n * SDK serve both vanilla websites (full nav fallback) and SPAs\n * (in-place state change) without per-host configuration.\n */\n private navigateToCampaignAction(\n campaign: InAppCampaign,\n safeUrl: string,\n buttonId: string = 'cta',\n ): void {\n if (typeof window === 'undefined') return;\n\n // Typed lifecycle hook (1.8.0). Mirror the DOM `preventDefault` contract\n // — a handler returning `false` OR mutating `defaultPrevented` via the\n // helper suppresses the SDK's hard-nav. Built first so handlers attached\n // via the typed API see the click before the DOM event fires.\n let typedDefaultPrevented = false;\n const typedEvent: CampaignClickEvent = {\n campaign,\n action_url: safeUrl,\n button_id: buttonId,\n preventDefault: () => {\n typedDefaultPrevented = true;\n typedEvent.defaultPrevented = true;\n },\n defaultPrevented: false,\n };\n const typedProceed = this.emit('campaign-click', typedEvent);\n\n // DOM CustomEvent path — preserved for backward compat (1.7.x consumers\n // already listen for `aegis:campaign-click`) and for advanced patterns\n // (multi-frame embeds, host-app code that runs before the SDK is\n // instantiated, debugging via DevTools event listener panel).\n const domEvent = new CustomEvent('aegis:campaign-click', {\n detail: {\n campaign_id: campaign.id,\n campaign_type: campaign.type,\n action_url: safeUrl,\n button_id: buttonId,\n },\n cancelable: true,\n });\n const domProceed = window.dispatchEvent(domEvent);\n\n // Either path can suppress the hard-nav. We OR the prevent signals so\n // host apps don't have to remember to handle both — registering on\n // either API is enough.\n const suppressed =\n !typedProceed ||\n typedDefaultPrevented ||\n !domProceed ||\n domEvent.defaultPrevented;\n\n if (!suppressed) {\n window.location.href = safeUrl;\n }\n }\n\n private renderBanner(campaign: InAppCampaign): void {\n const banner = document.createElement('div');\n banner.className = 'aegis-in-app-banner';\n banner.setAttribute('data-campaign-id', campaign.id);\n \n banner.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n background: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.text_color || '#ffffff')};\n padding: 16px;\n z-index: 999999;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideDown 0.3s ease-out;\n `;\n \n const contentContainer = document.createElement('div');\n contentContainer.style.cssText = 'flex: 1; display: flex; align-items: center; gap: 12px;';\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 40px; height: 40px; border-radius: 4px; object-fit: cover;';\n contentContainer.appendChild(img);\n }\n }\n \n const textContainer = document.createElement('div');\n textContainer.style.cssText = 'flex: 1;';\n \n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 600; font-size: 14px; margin-bottom: 4px;';\n textContainer.appendChild(title);\n \n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.9;';\n textContainer.appendChild(body);\n \n contentContainer.appendChild(textContainer);\n \n const actionsContainer = document.createElement('div');\n actionsContainer.style.cssText = 'display: flex; align-items: center; gap: 12px; margin-left: 16px;';\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: white;\n color: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n border: none;\n padding: 8px 16px;\n border-radius: 4px;\n font-weight: 600;\n cursor: pointer;\n font-size: 13px;\n white-space: nowrap;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeBanner(banner);\n });\n \n actionsContainer.appendChild(ctaButton);\n }\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n background: transparent;\n border: none;\n color: inherit;\n font-size: 20px;\n cursor: pointer;\n padding: 0;\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.7;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeBanner(banner);\n });\n \n actionsContainer.appendChild(closeButton);\n \n banner.appendChild(contentContainer);\n banner.appendChild(actionsContainer);\n \n this.addAnimationStyles();\n document.body.appendChild(banner);\n }\n \n private renderModal(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-modal-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-in-app-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 8px;\n max-width: 500px;\n width: 90%;\n max-height: 80vh;\n overflow: auto;\n box-shadow: 0 10px 40px rgba(0,0,0,0.2);\n animation: aegisScaleIn 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 200px; object-fit: cover; border-radius: 8px 8px 0 0;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 24px;';\n \n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n content.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0 0 20px 0; font-size: 15px; line-height: 1.5; color: #4a4a4a;';\n content.appendChild(body);\n \n const actions = document.createElement('div');\n actions.style.cssText = 'display: flex; gap: 12px; justify-content: flex-end;';\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.text_color || '#ffffff')};\n border: none;\n padding: 10px 24px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n actions.appendChild(ctaButton);\n }\n \n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: transparent;\n border: 1px solid #d0d0d0;\n color: #4a4a4a;\n padding: 10px 24px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n actions.appendChild(closeButton);\n \n content.appendChild(actions);\n modal.appendChild(content);\n overlay.appendChild(modal);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderFullScreen(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-fullscreen-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: ${this.sanitizeColor(campaign.background_color || '#ffffff')};\n color: ${this.sanitizeColor(campaign.text_color || '#1a1a1a')};\n z-index: 1000001;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px 20px;\n animation: aegisFadeIn 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n overflow-y: auto;\n `;\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'max-width: 100%; max-height: 40vh; object-fit: contain; margin-bottom: 32px;';\n overlay.appendChild(img);\n }\n }\n \n const contentContainer = document.createElement('div');\n contentContainer.style.cssText = 'max-width: 600px; text-align: center;';\n \n const title = document.createElement('h1');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 32px; font-weight: 700;';\n contentContainer.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0 0 32px 0; font-size: 18px; line-height: 1.6; opacity: 0.9;';\n contentContainer.appendChild(body);\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(campaign.text_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.background_color || '#ffffff')};\n border: none;\n padding: 16px 48px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 18px;\n cursor: pointer;\n margin-bottom: 16px;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n contentContainer.appendChild(ctaButton);\n }\n \n overlay.appendChild(contentContainer);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 20px;\n right: 20px;\n background: transparent;\n border: none;\n color: inherit;\n font-size: 32px;\n cursor: pointer;\n padding: 0;\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0.6;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n overlay.appendChild(closeButton);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderHalfInterstitial(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-half-interstitial-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n z-index: 1000000;\n display: flex;\n align-items: flex-end;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-in-app-half-interstitial';\n modal.style.cssText = `\n background: white;\n border-radius: 16px 16px 0 0;\n width: 100%;\n max-width: 600px;\n max-height: 60vh;\n overflow: auto;\n box-shadow: 0 -4px 20px rgba(0,0,0,0.2);\n animation: aegisSlideUp 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 200px; object-fit: cover;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 32px 24px;';\n \n const title = document.createElement('h2');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; line-height: 1.5; color: #4a4a4a;';\n content.appendChild(body);\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(campaign.background_color || '#1a73e8')};\n color: ${this.sanitizeColor(campaign.text_color || '#ffffff')};\n border: none;\n padding: 14px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n content.appendChild(ctaButton);\n }\n \n modal.appendChild(content);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: rgba(255,255,255,0.9);\n border: none;\n color: #333;\n font-size: 24px;\n cursor: pointer;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderAlert(campaign: InAppCampaign): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-alert-overlay';\n overlay.setAttribute('data-campaign-id', campaign.id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.6);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.2s ease-out;\n `;\n \n const alert = document.createElement('div');\n alert.className = 'aegis-in-app-alert';\n alert.style.cssText = `\n background: white;\n border-radius: 12px;\n max-width: 320px;\n width: 85%;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n animation: aegisScaleIn 0.2s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n overflow: hidden;\n `;\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 24px 20px; text-align: center;';\n \n const title = document.createElement('h3');\n title.textContent = campaign.title;\n title.style.cssText = 'margin: 0 0 8px 0; font-size: 18px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(title);\n \n const body = document.createElement('p');\n body.textContent = campaign.body;\n body.style.cssText = 'margin: 0; font-size: 14px; line-height: 1.4; color: #666;';\n content.appendChild(body);\n \n alert.appendChild(content);\n \n const buttonContainer = document.createElement('div');\n buttonContainer.style.cssText = 'display: flex; border-top: 1px solid #e0e0e0;';\n \n if (campaign.action_url && campaign.button_text) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = campaign.button_text;\n ctaButton.style.cssText = `\n flex: 1;\n background: transparent;\n border: none;\n border-right: 1px solid #e0e0e0;\n color: #1a73e8;\n padding: 14px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n this.navigateToCampaignAction(campaign, safeUrl);\n }\n this.removeModal(overlay);\n });\n \n buttonContainer.appendChild(ctaButton);\n }\n \n const cancelButton = document.createElement('button');\n cancelButton.textContent = 'Cancel';\n cancelButton.style.cssText = `\n flex: 1;\n background: transparent;\n border: none;\n color: #666;\n padding: 14px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n `;\n \n cancelButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n this.removeModal(overlay);\n });\n \n buttonContainer.appendChild(cancelButton);\n \n alert.appendChild(buttonContainer);\n overlay.appendChild(alert);\n \n this.addAnimationStyles();\n document.body.appendChild(overlay);\n }\n \n private renderPIP(campaign: InAppCampaign): void {\n const pip = document.createElement('div');\n pip.className = 'aegis-in-app-pip';\n pip.setAttribute('data-campaign-id', campaign.id);\n \n pip.style.cssText = `\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 320px;\n background: black;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 8px 24px rgba(0,0,0,0.3);\n z-index: 999999;\n animation: aegisSlideUp 0.3s ease-out;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (campaign.video_url) {\n const video = document.createElement('video');\n const safeUrl = this.sanitizeUrl(campaign.video_url);\n if (safeUrl) {\n video.src = safeUrl;\n video.controls = true;\n video.autoplay = true;\n video.muted = true;\n video.style.cssText = 'width: 100%; display: block;';\n pip.appendChild(video);\n }\n } else if (campaign.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(campaign.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; display: block;';\n pip.appendChild(img);\n }\n }\n \n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: linear-gradient(to top, rgba(0,0,0,0.9), transparent);\n padding: 40px 16px 16px;\n color: white;\n `;\n \n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-size: 14px; font-weight: 600; margin-bottom: 4px;';\n overlay.appendChild(title);\n \n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 12px; opacity: 0.9;';\n overlay.appendChild(body);\n \n pip.appendChild(overlay);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '✕';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 8px;\n right: 8px;\n background: rgba(0,0,0,0.6);\n border: none;\n color: white;\n font-size: 18px;\n cursor: pointer;\n width: 28px;\n height: 28px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(campaign.id, 'dismissed');\n pip.style.animation = 'aegisSlideDown 0.3s ease-out';\n setTimeout(() => {\n if (pip.parentNode) {\n pip.parentNode.removeChild(pip);\n }\n }, 300);\n });\n \n pip.appendChild(closeButton);\n \n if (campaign.action_url) {\n pip.style.cursor = 'pointer';\n pip.addEventListener('click', (e) => {\n if (e.target !== closeButton) {\n this.trackEvent(campaign.id, 'clicked');\n const safeUrl = this.sanitizeUrl(campaign.action_url!);\n if (safeUrl) {\n window.open(safeUrl, '_blank');\n }\n }\n });\n }\n \n this.addAnimationStyles();\n document.body.appendChild(pip);\n }\n \n private removeBanner(banner: HTMLElement): void {\n banner.style.animation = 'aegisSlideUp 0.3s ease-out';\n setTimeout(() => {\n if (banner.parentNode) {\n banner.parentNode.removeChild(banner);\n }\n }, 300);\n }\n \n private removeModal(overlay: HTMLElement): void {\n overlay.style.animation = 'aegisFadeOut 0.3s ease-out';\n setTimeout(() => {\n if (overlay.parentNode) {\n overlay.parentNode.removeChild(overlay);\n }\n }, 300);\n }\n \n private renderTooltip(campaign: InAppCampaign): void {\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n const anchorSelector = (ic.tooltip_anchor_selector as string) || '[data-aegis-tooltip]';\n const preferredPosition = (ic.tooltip_position as string) || 'bottom'; // top | bottom | left | right\n\n const anchor = document.querySelector(anchorSelector);\n if (!anchor) {\n this.log(`Tooltip anchor not found: ${anchorSelector}`, 'warn');\n return;\n }\n\n const bg = this.sanitizeColor(campaign.background_color || '#1a1a1a');\n const textColor = this.sanitizeColor(campaign.text_color || '#ffffff');\n\n // Tooltip container (positioned absolute to body)\n const tooltip = document.createElement('div');\n tooltip.className = 'aegis-in-app-tooltip';\n tooltip.setAttribute('data-campaign-id', campaign.id);\n tooltip.style.cssText = `\n position: absolute; z-index: 1000001; max-width: 280px; width: max-content;\n background: ${bg}; color: ${textColor}; border-radius: 10px; padding: 14px 16px;\n box-shadow: 0 8px 24px rgba(0,0,0,0.18); font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.2s ease-out; pointer-events: auto;\n `;\n\n // Arrow\n const arrow = document.createElement('div');\n arrow.className = 'aegis-tooltip-arrow';\n arrow.style.cssText = `\n position: absolute; width: 10px; height: 10px; background: ${bg};\n transform: rotate(45deg);\n `;\n\n // Close button\n const closeBtn = document.createElement('button');\n closeBtn.textContent = '\\u00d7';\n closeBtn.style.cssText = `\n position: absolute; top: 6px; right: 8px; background: none; border: none;\n color: ${textColor}; opacity: 0.6; font-size: 16px; cursor: pointer; padding: 0; line-height: 1;\n `;\n\n const dismiss = () => {\n tooltip.style.animation = 'aegisFadeOut 0.2s ease-out';\n setTimeout(() => { tooltip.parentNode?.removeChild(tooltip); }, 200);\n this.trackEvent(campaign.id, 'dismissed');\n };\n\n closeBtn.addEventListener('click', (e) => { e.stopPropagation(); dismiss(); });\n\n // Title\n if (campaign.title) {\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-size: 13px; font-weight: 700; margin-bottom: 4px; padding-right: 16px;';\n tooltip.appendChild(title);\n }\n\n // Body\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 12px; line-height: 1.4; opacity: 0.9;';\n tooltip.appendChild(body);\n\n // CTA button\n if (campaign.button_text && campaign.action_url) {\n const cta = document.createElement('a');\n cta.textContent = campaign.button_text;\n cta.href = campaign.action_url;\n cta.style.cssText = `\n display: inline-block; margin-top: 8px; font-size: 12px; font-weight: 600;\n color: ${textColor}; text-decoration: underline; cursor: pointer;\n `;\n cta.addEventListener('click', () => { this.trackEvent(campaign.id, 'clicked'); });\n tooltip.appendChild(cta);\n }\n\n tooltip.appendChild(closeBtn);\n tooltip.appendChild(arrow);\n document.body.appendChild(tooltip);\n\n // Position relative to anchor\n const anchorRect = anchor.getBoundingClientRect();\n const tooltipRect = tooltip.getBoundingClientRect();\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n const gap = 10;\n\n let top = 0;\n let left = 0;\n let arrowTop = '';\n let arrowLeft = '';\n let arrowBottom = '';\n let arrowRight = '';\n\n switch (preferredPosition) {\n case 'top':\n top = anchorRect.top + scrollY - tooltipRect.height - gap;\n left = anchorRect.left + scrollX + (anchorRect.width - tooltipRect.width) / 2;\n arrowBottom = '-5px';\n arrowLeft = `${tooltipRect.width / 2 - 5}px`;\n break;\n case 'left':\n top = anchorRect.top + scrollY + (anchorRect.height - tooltipRect.height) / 2;\n left = anchorRect.left + scrollX - tooltipRect.width - gap;\n arrowRight = '-5px';\n arrowTop = `${tooltipRect.height / 2 - 5}px`;\n break;\n case 'right':\n top = anchorRect.top + scrollY + (anchorRect.height - tooltipRect.height) / 2;\n left = anchorRect.right + scrollX + gap;\n arrowLeft = '-5px';\n arrowTop = `${tooltipRect.height / 2 - 5}px`;\n break;\n default: // bottom\n top = anchorRect.bottom + scrollY + gap;\n left = anchorRect.left + scrollX + (anchorRect.width - tooltipRect.width) / 2;\n arrowTop = '-5px';\n arrowLeft = `${tooltipRect.width / 2 - 5}px`;\n break;\n }\n\n // Clamp to viewport\n left = Math.max(8, Math.min(left, window.innerWidth + scrollX - tooltipRect.width - 8));\n top = Math.max(8, top);\n\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${left}px`;\n arrow.style.top = arrowTop || '';\n arrow.style.left = arrowLeft || '';\n arrow.style.bottom = arrowBottom || '';\n arrow.style.right = arrowRight || '';\n\n // Dismiss on outside click\n const outsideClickHandler = (e: MouseEvent) => {\n if (!tooltip.contains(e.target as Node) && !anchor.contains(e.target as Node)) {\n document.removeEventListener('click', outsideClickHandler);\n dismiss();\n }\n };\n setTimeout(() => document.addEventListener('click', outsideClickHandler), 100);\n }\n\n private addAnimationStyles(): void {\n if (document.getElementById('aegis-in-app-styles')) {\n return;\n }\n \n const style = document.createElement('style');\n style.id = 'aegis-in-app-styles';\n style.textContent = `\n @keyframes aegisSlideDown {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes aegisSlideUp {\n from { transform: translateY(0); opacity: 1; }\n to { transform: translateY(-100%); opacity: 0; }\n }\n \n @keyframes aegisFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes aegisFadeOut {\n from { opacity: 1; }\n to { opacity: 0; }\n }\n \n @keyframes aegisScaleIn {\n from { transform: scale(0.9); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n\n /* Bottom-anchored slide IN (opposite of aegisSlideUp which slides OUT).\n Used by the preload-first renderers: sticky_bar (bottom),\n progress_bar, carousel_cards, product_recommendation. */\n @keyframes aegisSlideInFromBottom {\n from { transform: translateY(100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n `;\n \n document.head.appendChild(style);\n }\n \n private sanitizeUrl(url: string): string | null {\n try {\n const parsedUrl = new URL(url, window.location.origin);\n \n if (parsedUrl.protocol === 'javascript:' || parsedUrl.protocol === 'data:') {\n this.log(`Blocked unsafe URL: ${url}`, 'error');\n return null;\n }\n \n return parsedUrl.href;\n } catch (error) {\n this.log(`Invalid URL: ${url}`, 'error');\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n if (/^#[0-9A-Fa-f]{3,6}$/.test(color)) {\n return color;\n }\n \n if (/^rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)$/.test(color)) {\n return color;\n }\n \n if (/^rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)$/.test(color)) {\n return color;\n }\n \n const namedColors = ['white', 'black', 'red', 'green', 'blue', 'yellow', 'orange', 'purple', 'pink', 'gray', 'transparent'];\n if (namedColors.includes(color.toLowerCase())) {\n return color;\n }\n \n return '#000000';\n }\n \n /**\n * Track in-app event via the standard Event Ingress pipeline.\n *\n * Events flow: SDK → Event Ingress → Kafka → Governance Consumer\n * → Event Engine → ClickHouse (in_app_events + events_with_ttl)\n *\n * The event_type uses the canonical prefix `in_app.` so the event\n * switchboard maps it to the correct canonical form:\n * in_app.impression → engagement.view\n * in_app.clicked → engagement.click\n * in_app.dismissed → engagement.dismiss\n */\n private async trackEvent(campaignId: string, eventType: 'impression' | 'clicked' | 'dismissed'): Promise<void> {\n // Lifecycle: `campaign-dismiss` — fire from this single chokepoint so\n // every renderer (8 sites today + future ones) emits consistently.\n // Source granularity (close_button / overlay / esc / auto_timeout) can\n // be refined in a future minor by passing source through to trackEvent;\n // for 1.8.0 we surface the event itself since that's the high-value\n // signal host apps need (inbox state updates, dismiss analytics).\n if (eventType === 'dismissed') {\n const campaign = this.campaigns.find((c) => c.id === campaignId);\n if (campaign) {\n this.emit('campaign-dismiss', {\n campaign,\n source: 'close_button',\n });\n }\n }\n\n try {\n // Use the public SDK scheme (/v1/in_app/events) — the gateway has an\n // explicit handler that auths via X-Aegis-Write-Key and forwards to\n // event-switchboard. Direct POST to /api/v1/events bypasses the SDK\n // auth model and hits the JWT preHandler → 401.\n // See docs/architecture/API_ROUTING.md §5.\n const url = `${this.apiHost}/v1/in_app/events`;\n const externalEventId = `inapp_${campaignId}_${eventType}_${Date.now()}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n };\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n\n // Plumb the same workspace cascade the main analytics SDK uses\n // (config → ?ws= → URL path slug → setWorkspace runtime → session\n // storage). Without this, in-app engagement events arrive at\n // event-ingress unscoped and fall back to the org's primary\n // workspace — see PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n const workspaceId = this.getWorkspaceId?.();\n\n const body = {\n campaign_id: campaignId,\n event_type: eventType,\n user_id: this.userId,\n contact_id: this.contactId,\n anonymous_id: this.userId,\n platform: 'web',\n workspace_id: workspaceId,\n metadata: {\n property_id: this.propertyId,\n variant_id: this.getVariantId(campaignId) ?? undefined,\n },\n idempotency_key: externalEventId,\n };\n\n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n credentials: 'include',\n }).catch(error => {\n this.log(`Error tracking event: ${error}`, 'error');\n });\n\n this.log(`Tracked ${eventType} event for campaign ${campaignId}`);\n } catch (error) {\n this.log(`Error tracking event: ${error}`, 'error');\n }\n }\n \n private log(message: string, level: 'log' | 'warn' | 'error' = 'log'): void {\n if (this.debugMode) {\n console[level](`[AegisInApp] ${message}`);\n }\n }\n \n destroy(options?: { clearABState?: boolean }): void {\n this.disconnectSSE();\n\n if (typeof document !== 'undefined') {\n document.querySelectorAll(\n '.aegis-in-app-banner, .aegis-in-app-modal-overlay, .aegis-in-app-fullscreen-overlay, ' +\n '.aegis-in-app-half-interstitial-overlay, .aegis-in-app-alert-overlay, .aegis-in-app-pip, ' +\n '.aegis-in-app-nps-overlay, .aegis-in-app-countdown-overlay, .aegis-in-app-rating-overlay, ' +\n '.aegis-in-app-poll-overlay, .aegis-in-app-quiz-overlay, ' +\n // F2/F3 / U8 — slot-rendered card classes\n '.aegis-in-app-rating-card, .aegis-in-app-nps-card'\n ).forEach(el => {\n if (el.parentNode) {\n el.parentNode.removeChild(el);\n }\n });\n\n const styles = document.getElementById('aegis-in-app-styles');\n if (styles && styles.parentNode) {\n styles.parentNode.removeChild(styles);\n }\n }\n\n // Optionally clear A/B assignment state\n if (options?.clearABState && typeof localStorage !== 'undefined') {\n localStorage.removeItem('aegis_ab_assignments');\n }\n\n this.isInitialized = false;\n this.log('AegisInApp destroyed');\n }\n}\n","/**\n * ProductRecommendation renderer — personalized product grid/row rendered\n * from `interactive_config.cards[]` that the cell-plane populated at\n * bundle-assembly time. The SDK never calls a product-API at render\n * time — the preload contract is that the recommended products are\n * already in the bundle.\n *\n * The layout is narrower than the generic carousel because product recs\n * should feel like a first-class product grid, not a marketing slider.\n * Three layouts: grid (2-col / 3-col responsive), row (horizontal-scroll),\n * carousel (same as carousel_cards but different framing copy).\n *\n * interactive_config:\n * rec_source?: 'viewed' | 'abandoned' | 'cross_sell' | 'trending' | 'personalized'\n * (metadata only — the actual product list is in `cards[]`)\n * rec_count?: number (display hint; rendering is driven by cards.length)\n * rec_layout?: 'grid' | 'row' | 'carousel' default 'grid'\n * rec_cta_text?: string default 'Shop now'\n * cards: Array<{ image_url?, title?, body?, cta_url?, metadata? }>\n */\n\nimport type { RenderContext } from './types';\n\ntype Card = {\n image_url?: string;\n title?: string;\n body?: string;\n cta_text?: string;\n cta_url?: string;\n metadata?: Record<string, unknown>;\n};\n\nconst MAX_PRODUCTS = 24;\n\nexport function renderProductRecommendation(ctx: RenderContext): void {\n const { campaign, trackEvent, sanitizeUrl, sanitizeColor, log, addAnimationStyles } = ctx;\n const ic = (campaign.interactive_config || {}) as Record<string, unknown>;\n\n const rawCards = Array.isArray(ic.cards) ? (ic.cards as Card[]) : [];\n const cards = rawCards.slice(0, MAX_PRODUCTS);\n if (cards.length === 0) {\n log('product_recommendation rendered with zero products — skipping', 'warn');\n return;\n }\n\n const layout = (ic.rec_layout as string) || 'grid';\n const ctaDefault = (ic.rec_cta_text as string) || 'Shop now';\n\n addAnimationStyles();\n\n const bg = sanitizeColor((campaign.background_color as string) || '#ffffff');\n const fg = sanitizeColor((campaign.text_color as string) || '#0f172a');\n const accent = sanitizeColor('#4169e1');\n\n const overlay = document.createElement('div');\n overlay.className = 'aegis-in-app-product-rec';\n overlay.setAttribute('data-campaign-id', campaign.id);\n overlay.style.cssText = `\n position: fixed; inset: 0;\n background: rgba(15,23,42,0.55); backdrop-filter: blur(4px);\n display: flex; align-items: flex-end; justify-content: center;\n z-index: 99999; animation: aegisFadeIn 0.25s ease;\n `;\n\n const sheet = document.createElement('div');\n sheet.style.cssText = `\n background: ${bg}; color: ${fg};\n max-width: 560px; width: 100%; max-height: 80vh; overflow-y: auto;\n border-radius: 20px 20px 0 0;\n padding: 18px 16px 22px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInFromBottom 0.3s ease-out;\n box-shadow: 0 -12px 30px rgba(0,0,0,0.12);\n `;\n\n // Drag handle (decorative, for mobile context)\n const handle = document.createElement('div');\n handle.style.cssText = `\n width: 36px; height: 4px; border-radius: 999px; background: ${fg}1f;\n margin: 0 auto 12px;\n `;\n sheet.appendChild(handle);\n\n const header = document.createElement('div');\n header.style.cssText = 'display: flex; justify-content: space-between; align-items: flex-start; gap: 12px;';\n\n const headerText = document.createElement('div');\n const title = document.createElement('div');\n title.textContent = campaign.title;\n title.style.cssText = 'font-weight: 700; font-size: 16px; margin-bottom: 2px;';\n const body = document.createElement('div');\n body.textContent = campaign.body;\n body.style.cssText = 'font-size: 13px; opacity: 0.7; line-height: 1.4;';\n headerText.appendChild(title);\n headerText.appendChild(body);\n header.appendChild(headerText);\n\n const close = document.createElement('button');\n close.textContent = '✕';\n close.setAttribute('aria-label', 'Close');\n close.style.cssText = `\n background: transparent; border: none; color: inherit;\n font-size: 18px; cursor: pointer; padding: 4px 8px; opacity: 0.7;\n `;\n close.addEventListener('click', () => {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n });\n header.appendChild(close);\n sheet.appendChild(header);\n\n const grid = document.createElement('div');\n if (layout === 'row' || layout === 'carousel') {\n grid.style.cssText = `\n display: flex; gap: 10px; overflow-x: auto;\n scroll-snap-type: x mandatory; padding: 14px 0 4px;\n `;\n } else {\n grid.style.cssText = `\n display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px;\n padding-top: 14px;\n `;\n }\n\n cards.forEach((c) => {\n const tile = document.createElement('div');\n const isRow = layout === 'row' || layout === 'carousel';\n tile.style.cssText = `\n background: ${fg}08; border-radius: 12px;\n padding: 10px; display: flex; flex-direction: column; gap: 6px;\n cursor: ${c.cta_url ? 'pointer' : 'default'};\n ${isRow ? 'flex: 0 0 150px; scroll-snap-align: start;' : ''}\n transition: transform 0.15s ease;\n `;\n\n if (c.image_url) {\n const img = document.createElement('img');\n const safe = sanitizeUrl(c.image_url);\n if (safe) {\n img.src = safe;\n img.alt = '';\n img.loading = 'lazy';\n img.style.cssText = 'width: 100%; aspect-ratio: 1 / 1; border-radius: 8px; object-fit: cover;';\n tile.appendChild(img);\n }\n }\n\n if (c.title) {\n const t = document.createElement('div');\n t.textContent = c.title;\n t.style.cssText = 'font-weight: 600; font-size: 13px; line-height: 1.3;';\n tile.appendChild(t);\n }\n\n // Price lives in metadata.price per the dashboard schema; we show it\n // prominently if present.\n const price = c.metadata && typeof c.metadata === 'object' ? (c.metadata as Record<string, unknown>).price : undefined;\n if (price !== undefined) {\n const p = document.createElement('div');\n p.textContent = String(price);\n p.style.cssText = `color: ${accent}; font-weight: 700; font-size: 13px;`;\n tile.appendChild(p);\n } else if (c.body) {\n const b = document.createElement('div');\n b.textContent = c.body;\n b.style.cssText = 'font-size: 11.5px; opacity: 0.72; line-height: 1.3;';\n tile.appendChild(b);\n }\n\n const cta = document.createElement('button');\n cta.textContent = c.cta_text || ctaDefault;\n cta.style.cssText = `\n margin-top: auto;\n background: ${accent}; color: #fff;\n border: none; padding: 7px 10px; border-radius: 999px;\n font-size: 12px; font-weight: 600; cursor: pointer;\n `;\n const go = (e: Event) => {\n e.stopPropagation();\n trackEvent(campaign.id, 'clicked');\n if (c.cta_url) {\n const safe = sanitizeUrl(c.cta_url);\n if (safe) window.location.href = safe;\n }\n };\n cta.addEventListener('click', go);\n tile.appendChild(cta);\n if (c.cta_url) tile.addEventListener('click', go);\n\n grid.appendChild(tile);\n });\n\n sheet.appendChild(grid);\n overlay.appendChild(sheet);\n\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n trackEvent(campaign.id, 'dismissed');\n overlay.remove();\n }\n });\n\n document.body.appendChild(overlay);\n}\n","/**\n * AegisWidgetManager - Web SDK Widget Module\n * \n * Smart Pull architecture for persistent web widgets.\n * \n * Features:\n * - Gamification (spin wheel, scratch card) with server-side prize generation\n * - Chat bubbles (WhatsApp, support)\n * - Social proof toasts (real-time purchase notifications)\n * - Feedback forms (NPS, surveys)\n * \n * Security:\n * - XSS Protection: Uses DOM APIs instead of innerHTML\n * - Server-side prize generation prevents client manipulation\n * - URL validation prevents javascript: protocol injection\n * \n * Architecture:\n * - Fetches widget configs from /v1/widgets/config on page load\n * - Integrates with TriggerEngine for behavioral triggers\n * - Tracks widget events (show, click, dismiss, submit)\n * - Server-side prize generation for gamification\n */\n\nimport { TriggerEngine } from '../triggers/TriggerEngine';\nimport { Storage } from '../utils/storage';\n\n// Read the SDK's persisted anonymous_id from localStorage/cookie if available.\n// Returns undefined when called server-side or when no id has been minted yet.\n// See AegisInAppManager for the same pattern + canonical contract in\n// docs/architecture/API_ROUTING.md §3.\nfunction readAnonIdFromStorage(): string | undefined {\n if (typeof document === 'undefined') return undefined;\n try {\n const storage = new Storage();\n return storage.get('anon_id') ?? undefined;\n } catch {\n return undefined;\n }\n}\n\nexport interface WidgetConfig {\n widget_id: string;\n widget_type: 'chat_bubble' | 'spin_wheel' | 'scratch_card' | 'toast' | 'feedback_form' | 'exit_intent_popup';\n name: string;\n config: Record<string, any>;\n position?: string;\n priority: number;\n trigger_rules?: {\n type: 'exit_intent' | 'scroll_depth' | 'time_on_page' | 'immediate';\n config?: Record<string, any>;\n };\n}\n\nexport interface GamificationPrize {\n prize_label: string;\n prize_value?: string;\n coupon_code?: string;\n play_id: string;\n}\n\nexport interface AegisWidgetConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n organizationId?: string;\n debugMode?: boolean;\n triggerEngine?: TriggerEngine;\n /** If true, WidgetManager takes ownership of the TriggerEngine and\n * will call `triggerEngine.stop()` on destroy(). Leave false when\n * the caller shares the engine across managers (e.g. the facade). */\n ownsTriggerEngine?: boolean;\n enablePrefetch?: boolean;\n cssCustomization?: WidgetCSSCustomization;\n onEvent?: (eventType: string, data: any) => void;\n /** Source platform passed as X-Source-Platform header so the API\n * can filter campaigns whose display_rules.sources restricts them\n * to specific integrations (e.g. 'shopify', 'woocommerce'). */\n sourcePlatform?: 'web' | 'shopify' | 'woocommerce' | 'mobile_sdk';\n /**\n * Workspace cascade reader. Host SDK sets this to\n * `() => aegis.getEffectiveWorkspaceId()` so widget interaction events\n * (impressions, clicks, spin/scratch plays, feedback submissions)\n * arrive at the gateway with the same workspace context the main\n * analytics SDK uses. See PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n */\n getWorkspaceId?: () => string | undefined;\n}\n\nexport interface WidgetCSSCustomization {\n exitIntent?: {\n backgroundColor?: string;\n textColor?: string;\n accentColor?: string;\n borderRadius?: string;\n fontFamily?: string;\n overlayOpacity?: number;\n };\n spinWheel?: {\n backgroundColor?: string;\n textColor?: string;\n accentColor?: string;\n borderRadius?: string;\n fontFamily?: string;\n wheelColors?: string[];\n buttonColor?: string;\n };\n chatBubble?: {\n backgroundColor?: string;\n textColor?: string;\n position?: 'bottom_right' | 'bottom_left' | 'top_right' | 'top_left';\n size?: 'small' | 'medium' | 'large';\n };\n}\n\nexport interface PrefetchWidgetConfig {\n exit_intent?: {\n enabled: boolean;\n type: 'cart_recovery' | 'lead_gen';\n tier?: string;\n title?: string;\n message?: string;\n discount_code?: string;\n discount_percentage?: number;\n cta_text?: string;\n cta_url?: string;\n background_color?: string;\n text_color?: string;\n accent_color?: string;\n show_cart_items?: boolean;\n show_timer?: boolean;\n timer_minutes?: number;\n priority: number;\n mobile_triggers?: {\n scroll_velocity?: boolean;\n scroll_threshold?: number;\n scroll_min_position?: number;\n scroll_cooldown?: number;\n idle_timer?: boolean;\n idle_seconds?: number;\n visibility_change?: boolean;\n back_button?: boolean;\n };\n };\n spin_wheel?: {\n enabled: boolean;\n type: 'lead_gen' | 'cart_recovery';\n title?: string;\n prizes?: any[];\n priority: number;\n };\n}\n\nexport interface CartData {\n cart_id: string;\n cart_total: number;\n cart_currency: string;\n cart_items: Array<{\n product_id: string;\n product_name?: string;\n quantity: number;\n price?: number;\n }>;\n}\n\nexport class AegisWidgetManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private organizationId?: string;\n private debugMode: boolean;\n private triggerEngine?: TriggerEngine;\n private enablePrefetch: boolean;\n private cssCustomization: WidgetCSSCustomization;\n private onEvent?: (eventType: string, data: any) => void;\n private sourcePlatform?: string;\n private getWorkspaceId?: () => string | undefined;\n\n private widgets: WidgetConfig[] = [];\n private renderedWidgets = new Set<string>();\n private isInitialized = false;\n private isDestroyed = false;\n private ownsTriggerEngine: boolean;\n\n private prefetchWidgetConfigs: PrefetchWidgetConfig = {};\n private cartData?: CartData;\n \n constructor(config: AegisWidgetConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n // Fall back to the SDK's persisted anonymous_id when callers don't pass\n // userId explicitly. Without this, /v1/widgets/config requests omit\n // X-User-ID and the cell-plane buckets the call into \"anonymous\", which\n // misses any user-targeted widget configs. Mirror in AegisInAppManager.\n this.userId = config.userId ?? readAnonIdFromStorage();\n this.contactId = config.contactId;\n this.organizationId = config.organizationId;\n this.debugMode = config.debugMode || false;\n this.triggerEngine = config.triggerEngine;\n this.ownsTriggerEngine = config.ownsTriggerEngine === true;\n this.enablePrefetch = config.enablePrefetch !== false;\n this.cssCustomization = config.cssCustomization || {};\n this.onEvent = config.onEvent;\n this.sourcePlatform = config.sourcePlatform;\n this.getWorkspaceId = config.getWorkspaceId;\n }\n \n updateCSSCustomization(customization: WidgetCSSCustomization): void {\n this.cssCustomization = { ...this.cssCustomization, ...customization };\n this.log('CSS customization updated');\n }\n\n /**\n * Swap the identity that outgoing widget requests are attributed to.\n * Safe to call at any time; if prefetch is enabled and the manager\n * has already initialised, this will re-fetch the per-contact widget\n * configs so subsequent renders target the new identity. Does NOT\n * re-render already-visible widgets — a full teardown requires destroy().\n */\n async updateContactId(contactId: string | undefined): Promise<void> {\n if (this.contactId === contactId) return;\n this.contactId = contactId;\n this.log(`Contact ID updated → ${contactId ?? '(cleared)'}`);\n if (this.isInitialized && !this.isDestroyed && this.enablePrefetch && contactId) {\n await this.fetchPrefetchConfigs();\n this.preloadWidgetAssets();\n }\n }\n\n /**\n * Render an in-app campaign whose sub_type routes it to the\n * gamification-widget code path (spin_wheel / scratch_card).\n * InAppCampaign is adapted to the minimal WidgetConfig shape that\n * the internal renderers already understand — no rewrite needed.\n *\n * AegisMessageRuntime wires this method as AegisInAppManager's\n * `onInteractiveCampaign` callback so impressions from the inbox\n * prefetch bundle actually land on screen.\n */\n renderInteractiveCampaign(campaign: {\n id: string;\n title: string;\n sub_type?: string;\n priority?: number;\n interactive_config?: Record<string, unknown>;\n }): void {\n if (this.isDestroyed) return;\n if (typeof document === 'undefined') return;\n const subType = campaign.sub_type;\n if (subType !== 'spin_wheel' && subType !== 'scratch_card') {\n this.log(`renderInteractiveCampaign: unsupported sub_type \"${subType}\"`, true);\n return;\n }\n const widget: WidgetConfig = {\n widget_id: campaign.id,\n widget_type: subType,\n name: campaign.title,\n config: (campaign.interactive_config ?? {}) as Record<string, any>,\n priority: campaign.priority ?? 0,\n };\n if (this.renderedWidgets.has(widget.widget_id)) return;\n this.renderedWidgets.add(widget.widget_id);\n if (subType === 'spin_wheel') {\n this.renderSpinWheel(widget);\n } else {\n this.renderScratchCard(widget);\n }\n }\n\n /**\n * Tear down everything the manager installed: trigger listeners (if\n * we own the TriggerEngine), injected style sheets, and any rendered\n * widget roots. Further render attempts from in-flight triggers are\n * short-circuited by the `isDestroyed` flag.\n */\n destroy(): void {\n if (this.isDestroyed) return;\n this.isDestroyed = true;\n\n if (this.triggerEngine && this.ownsTriggerEngine) {\n this.triggerEngine.stop();\n }\n\n if (typeof document !== 'undefined') {\n const animationStyle = document.getElementById('aegis-widget-animations');\n animationStyle?.remove();\n document.querySelectorAll('[data-aegis-widget-root]').forEach((node) => {\n node.remove();\n });\n }\n\n this.renderedWidgets.clear();\n this.widgets = [];\n this.isInitialized = false;\n this.log('AegisWidgets destroyed');\n this.emitEvent('destroyed', {});\n }\n\n /**\n * Fire a lifecycle event to the consumer-supplied callback. Public-API\n * hook configured via `AegisWidgetConfig.onEvent`. Currently only used\n * by external callers passing their own logger; internal widget lifecycle\n * events get wired through here as they're added.\n */\n private emitEvent(eventType: string, data: unknown): void {\n if (this.onEvent) {\n try {\n this.onEvent(eventType, data);\n } catch (error) {\n this.log(`Error in event callback: ${error}`, true);\n }\n }\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisWidgets already initialized');\n return;\n }\n \n if (this.enablePrefetch && this.contactId) {\n await this.fetchPrefetchConfigs();\n this.preloadWidgetAssets();\n }\n \n await this.fetchWidgets();\n \n this.renderImmediateWidgets();\n \n this.setupTriggerListeners();\n \n this.setupExitIntentWithPrefetch();\n \n this.isInitialized = true;\n this.log('AegisWidgets initialized successfully');\n }\n \n setCartData(cartData: CartData): void {\n this.cartData = cartData;\n this.log(`Cart data updated: ${cartData.cart_items.length} items, total: ${cartData.cart_currency} ${cartData.cart_total}`);\n }\n \n private detectPlatformCart(): CartData | null {\n const shopifyCart = this.normalizeShopifyCart();\n if (shopifyCart) {\n this.log('Detected Shopify cart via browser globals');\n return shopifyCart;\n }\n \n const wooCart = this.normalizeWooCart();\n if (wooCart) {\n this.log('Detected WooCommerce cart via injected data');\n return wooCart;\n }\n \n const magentoCart = this.normalizeMagentoCart();\n if (magentoCart) {\n this.log('Detected Magento cart via localStorage');\n return magentoCart;\n }\n \n if (this.cartData) {\n this.log('Using manually set cart data');\n return this.cartData;\n }\n \n return null;\n }\n \n private normalizeShopifyCart(): CartData | null {\n try {\n if (typeof window === 'undefined') return null;\n \n const win = window as any;\n \n if (win.Shopify?.checkout) {\n const checkout = win.Shopify.checkout;\n return {\n cart_id: checkout.token || `shopify_${Date.now()}`,\n cart_total: parseFloat(checkout.total_price) || 0,\n cart_currency: checkout.currency || 'USD',\n cart_items: (checkout.line_items || []).map((item: any) => ({\n product_id: String(item.product_id || item.id),\n product_name: item.title || item.product_title,\n quantity: item.quantity || 1,\n price: parseFloat(item.price) || 0\n }))\n };\n }\n \n const cartJsonEl = document.getElementById('cart-json');\n if (cartJsonEl?.textContent) {\n const cart = JSON.parse(cartJsonEl.textContent);\n return {\n cart_id: cart.token || `shopify_${Date.now()}`,\n cart_total: (cart.total_price || 0) / 100,\n cart_currency: cart.currency || 'USD',\n cart_items: (cart.items || []).map((item: any) => ({\n product_id: String(item.product_id || item.id),\n product_name: item.product_title || item.title,\n quantity: item.quantity || 1,\n price: (item.price || 0) / 100\n }))\n };\n }\n \n return null;\n } catch (error) {\n this.log(`Error detecting Shopify cart: ${error}`, true);\n return null;\n }\n }\n \n private normalizeWooCart(): CartData | null {\n try {\n if (typeof window === 'undefined') return null;\n \n const win = window as any;\n \n if (win.aegis_cart) {\n const cart = win.aegis_cart;\n return {\n cart_id: cart.cart_id || `woo_${Date.now()}`,\n cart_total: parseFloat(cart.cart_total) || 0,\n cart_currency: cart.cart_currency || 'USD',\n cart_items: (cart.cart_items || []).map((item: any) => ({\n product_id: String(item.product_id),\n product_name: item.product_name,\n quantity: item.quantity || 1,\n price: parseFloat(item.price) || 0\n }))\n };\n }\n \n return null;\n } catch (error) {\n this.log(`Error detecting WooCommerce cart: ${error}`, true);\n return null;\n }\n }\n \n private normalizeMagentoCart(): CartData | null {\n try {\n if (typeof window === 'undefined' || !window.localStorage) return null;\n \n const mageCacheStr = localStorage.getItem('mage-cache-storage');\n if (!mageCacheStr) return null;\n \n const mageCache = JSON.parse(mageCacheStr);\n if (!mageCache?.cart) return null;\n \n const cart = mageCache.cart;\n \n return {\n cart_id: cart.quote_id || cart.id || `magento_${Date.now()}`,\n cart_total: parseFloat(cart.subtotalAmount || cart.subtotal || 0),\n cart_currency: cart.currencyCode || 'USD',\n cart_items: (cart.items || []).map((item: any) => ({\n product_id: String(item.item_id || item.product_id),\n product_name: item.product_name || item.name,\n quantity: item.qty || 1,\n price: parseFloat(item.product_price_value || item.price || 0)\n }))\n };\n } catch (error) {\n this.log(`Error detecting Magento cart: ${error}`, true);\n return null;\n }\n }\n \n private preloadWidgetAssets(): void {\n try {\n const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;\n if (exitIntentConfig?.enabled) {\n const imageUrl = (exitIntentConfig as any).image_url;\n if (imageUrl) {\n const img = new Image();\n img.src = imageUrl;\n this.log(`Preloading exit intent image: ${imageUrl}`);\n }\n }\n \n const spinWheelConfig = this.prefetchWidgetConfigs.spin_wheel;\n if (spinWheelConfig?.enabled) {\n const imageUrl = (spinWheelConfig as any).image_url;\n if (imageUrl) {\n const img = new Image();\n img.src = imageUrl;\n this.log(`Preloading spin wheel image: ${imageUrl}`);\n }\n }\n } catch (error) {\n this.log(`Error preloading widget assets: ${error}`, true);\n }\n }\n \n private async fetchPrefetchConfigs(): Promise<void> {\n try {\n const startTime = performance.now();\n const url = `${this.apiHost}/v1/widgets/config/prefetch`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n };\n \n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, { headers, credentials: 'include' });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch prefetch configs: ${response.status}`);\n }\n \n this.prefetchWidgetConfigs = await response.json();\n \n const elapsed = performance.now() - startTime;\n this.log(`Fetched prefetch widget configs in ${elapsed.toFixed(2)}ms`);\n \n } catch (error) {\n this.log(`Error fetching prefetch configs: ${error}`, true);\n }\n }\n \n private async fetchWidgets(): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/widgets/config`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Platform': 'web',\n 'X-Device-Type': this.getDeviceType()\n };\n\n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n if (this.sourcePlatform) headers['X-Source-Platform'] = this.sourcePlatform;\n \n const response = await fetch(url, { headers, credentials: 'include' });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch widgets: ${response.status}`);\n }\n \n this.widgets = await response.json();\n \n this.log(`Fetched ${this.widgets.length} widgets`);\n \n } catch (error) {\n this.log(`Error fetching widgets: ${error}`, true);\n }\n }\n \n private renderImmediateWidgets(): void {\n const immediateWidgets = this.widgets.filter(w => \n !w.trigger_rules || w.trigger_rules.type === 'immediate'\n );\n \n immediateWidgets.forEach(widget => this.renderWidget(widget));\n }\n \n private setupTriggerListeners(): void {\n if (!this.triggerEngine) {\n this.log('TriggerEngine not provided, skipping trigger-based widgets');\n return;\n }\n \n this.widgets.forEach(widget => {\n if (!widget.trigger_rules) return;\n \n const { type, config } = widget.trigger_rules;\n \n switch (type) {\n case 'exit_intent':\n this.triggerEngine!.registerExitIntent();\n this.triggerEngine!.on('exit_intent', () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n \n case 'scroll_depth':\n const depthPercent = (config as any)?.depth_percent || 50;\n this.triggerEngine!.registerScrollDepth(depthPercent);\n this.triggerEngine!.on(`scroll_depth_${depthPercent}`, () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n \n case 'time_on_page':\n const seconds = (config as any)?.seconds || 30;\n this.triggerEngine!.registerTimeOnPage(seconds);\n this.triggerEngine!.on(`time_on_page_${seconds}`, () => {\n if (!this.renderedWidgets.has(widget.widget_id)) {\n this.renderWidget(widget);\n }\n });\n break;\n }\n });\n }\n \n private setupExitIntentWithPrefetch(): void {\n if (!this.enablePrefetch || !this.triggerEngine) {\n return;\n }\n \n const spinWheelConfig = this.prefetchWidgetConfigs.spin_wheel;\n const exitIntentConfig = this.prefetchWidgetConfigs.exit_intent;\n \n if (spinWheelConfig && spinWheelConfig.enabled) {\n const isMobile = this.isMobileDevice();\n const mobileConfig = exitIntentConfig?.mobile_triggers || {};\n \n const handleSpinWheelIntent = () => {\n const widgetId = `prefetch_spin_wheel_${Date.now()}`;\n \n if (this.renderedWidgets.has(widgetId)) {\n return;\n }\n \n this.renderedWidgets.add(widgetId);\n \n const detectedCart = this.detectPlatformCart();\n \n if (spinWheelConfig.type === 'cart_recovery' && detectedCart) {\n this.cartData = detectedCart;\n this.renderSpinWheelWidget(widgetId, spinWheelConfig);\n this.sendCartAbandonmentBeacon();\n } else if (spinWheelConfig.type === 'lead_gen') {\n this.renderSpinWheelWidget(widgetId, spinWheelConfig);\n }\n };\n \n if (isMobile) {\n const scrollVelocityEnabled = mobileConfig.scroll_velocity !== false;\n if (scrollVelocityEnabled) {\n this.triggerEngine.registerScrollVelocity({\n threshold: mobileConfig.scroll_threshold || 0.5,\n minScrollPosition: mobileConfig.scroll_min_position || 100,\n cooldown: mobileConfig.scroll_cooldown || 5000\n });\n this.triggerEngine.on('mobile_exit_intent', handleSpinWheelIntent);\n this.log('Registered mobile scroll velocity trigger for spin wheel');\n }\n \n const idleTimerEnabled = mobileConfig.idle_timer !== false;\n if (idleTimerEnabled) {\n const idleSeconds = mobileConfig.idle_seconds || 15;\n this.triggerEngine.registerInactivity(idleSeconds);\n this.triggerEngine.on(`inactivity_${idleSeconds}`, handleSpinWheelIntent);\n this.log(`Registered mobile idle timer trigger for spin wheel: ${idleSeconds}s`);\n }\n \n const visibilityChangeEnabled = mobileConfig.visibility_change !== false;\n if (visibilityChangeEnabled) {\n const detectedCart = this.detectPlatformCart();\n if (detectedCart && detectedCart.cart_items && detectedCart.cart_items.length > 0) {\n this.triggerEngine.registerVisibilityChange();\n this.triggerEngine.on('visibility_hidden', handleSpinWheelIntent);\n this.log('Registered mobile visibility change trigger for spin wheel (cart detected)');\n }\n }\n \n const backButtonEnabled = mobileConfig.back_button === true;\n if (backButtonEnabled) {\n this.triggerEngine.registerBackButton();\n this.triggerEngine.on('back_button', handleSpinWheelIntent);\n this.log('Registered mobile back button trigger for spin wheel');\n }\n \n this.log(`Setup mobile spin wheel: type=${spinWheelConfig.type}, priority=${spinWheelConfig.priority}`);\n } else {\n this.triggerEngine.registerExitIntent();\n this.triggerEngine.on('exit_intent', handleSpinWheelIntent);\n this.log(`Setup desktop spin wheel exit intent: type=${spinWheelConfig.type}, priority=${spinWheelConfig.priority}`);\n }\n \n return;\n }\n \n if (!exitIntentConfig || !exitIntentConfig.enabled) {\n this.log('No exit intent config found in prefetch');\n return;\n }\n \n const isMobile = this.isMobileDevice();\n const mobileConfig = exitIntentConfig.mobile_triggers || {};\n \n const handleExitIntent = () => {\n const widgetId = `prefetch_exit_intent_${Date.now()}`;\n \n if (this.renderedWidgets.has(widgetId)) {\n return;\n }\n \n this.renderedWidgets.add(widgetId);\n \n const detectedCart = this.detectPlatformCart();\n \n if (exitIntentConfig.type === 'cart_recovery' && detectedCart) {\n this.cartData = detectedCart;\n this.renderCartRecoveryPopup(widgetId, exitIntentConfig);\n this.sendCartAbandonmentBeacon();\n } else if (exitIntentConfig.type === 'lead_gen') {\n this.renderLeadGenPopup(widgetId, exitIntentConfig);\n } else {\n this.log('Unknown exit intent type or missing cart data');\n }\n };\n \n if (isMobile) {\n const scrollVelocityEnabled = mobileConfig.scroll_velocity !== false;\n if (scrollVelocityEnabled) {\n this.triggerEngine.registerScrollVelocity({\n threshold: mobileConfig.scroll_threshold || 0.5,\n minScrollPosition: mobileConfig.scroll_min_position || 100,\n cooldown: mobileConfig.scroll_cooldown || 5000\n });\n this.triggerEngine.on('mobile_exit_intent', handleExitIntent);\n this.log('Registered mobile scroll velocity trigger');\n }\n \n const idleTimerEnabled = mobileConfig.idle_timer !== false;\n if (idleTimerEnabled) {\n const idleSeconds = mobileConfig.idle_seconds || 15;\n this.triggerEngine.registerInactivity(idleSeconds);\n this.triggerEngine.on(`inactivity_${idleSeconds}`, handleExitIntent);\n this.log(`Registered mobile idle timer trigger: ${idleSeconds}s`);\n }\n \n const visibilityChangeEnabled = mobileConfig.visibility_change !== false;\n if (visibilityChangeEnabled) {\n const detectedCart = this.detectPlatformCart();\n if (detectedCart && detectedCart.cart_items && detectedCart.cart_items.length > 0) {\n this.triggerEngine.registerVisibilityChange();\n this.triggerEngine.on('visibility_hidden', handleExitIntent);\n this.log('Registered mobile visibility change trigger (cart detected)');\n } else {\n this.log('Skipped visibility change trigger (no cart items)');\n }\n }\n \n const backButtonEnabled = mobileConfig.back_button === true;\n if (backButtonEnabled) {\n this.triggerEngine.registerBackButton();\n this.triggerEngine.on('back_button', handleExitIntent);\n this.log('Registered mobile back button trigger (aggressive mode)');\n }\n \n this.log(`Setup mobile exit intent: type=${exitIntentConfig.type}, priority=${exitIntentConfig.priority}`);\n } else {\n this.triggerEngine.registerExitIntent();\n this.triggerEngine.on('exit_intent', handleExitIntent);\n this.log(`Setup desktop exit intent: type=${exitIntentConfig.type}, priority=${exitIntentConfig.priority}`);\n }\n }\n \n private sendCartAbandonmentBeacon(): void {\n if (!this.cartData) {\n this.log('No cart data available for beacon', true);\n return;\n }\n \n const beaconData = {\n organization_id: this.organizationId || 'default',\n contact_id: this.contactId,\n cart_id: this.cartData.cart_id,\n cart_total: this.cartData.cart_total,\n cart_currency: this.cartData.cart_currency,\n cart_items: this.cartData.cart_items,\n user_email: this.userId,\n abandoned_at: new Date().toISOString(),\n page_url: window.location.href,\n referrer: document.referrer\n };\n \n const beaconUrl = `${this.apiHost}/v1/widgets/beacon/cart-abandoned`;\n \n if (navigator.sendBeacon) {\n const blob = new Blob([JSON.stringify(beaconData)], { type: 'application/json' });\n const sent = navigator.sendBeacon(beaconUrl, blob);\n \n if (sent) {\n this.log('Cart abandonment beacon sent successfully');\n } else {\n this.log('Failed to send cart abandonment beacon', true);\n }\n } else {\n fetch(beaconUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey\n },\n body: JSON.stringify(beaconData),\n keepalive: true,\n credentials: 'include',\n }).catch(err => {\n this.log(`Error sending cart abandonment via fetch: ${err}`, true);\n });\n }\n }\n \n private renderWidget(widget: WidgetConfig): void {\n if (this.isDestroyed) return;\n if (this.renderedWidgets.has(widget.widget_id)) {\n return;\n }\n\n this.renderedWidgets.add(widget.widget_id);\n \n switch (widget.widget_type) {\n case 'chat_bubble':\n this.renderChatBubble(widget);\n break;\n case 'spin_wheel':\n this.renderSpinWheel(widget);\n break;\n case 'scratch_card':\n this.renderScratchCard(widget);\n break;\n case 'toast':\n this.renderToast(widget);\n break;\n case 'feedback_form':\n this.renderFeedbackForm(widget);\n break;\n case 'exit_intent_popup':\n this.renderExitIntentPopup(widget);\n break;\n default:\n this.log(`Unknown widget type: ${widget.widget_type}`, true);\n }\n \n this.trackEvent(widget.widget_id, 'show');\n }\n \n private renderChatBubble(widget: WidgetConfig): void {\n const { text, icon_url, link_url, background_color, text_color } = widget.config;\n const position = widget.position || 'bottom_right';\n \n const bubble = document.createElement('div');\n bubble.className = 'aegis-chat-bubble';\n bubble.setAttribute('data-widget-id', widget.widget_id);\n \n const positionStyles: Record<string, string> = {\n bottom_right: 'bottom: 20px; right: 20px;',\n bottom_left: 'bottom: 20px; left: 20px;',\n top_right: 'top: 20px; right: 20px;',\n top_left: 'top: 20px; left: 20px;'\n };\n \n bubble.style.cssText = `\n position: fixed;\n ${positionStyles[position] || positionStyles.bottom_right}\n background: ${this.sanitizeColor(background_color || '#25D366')};\n color: ${this.sanitizeColor(text_color || '#ffffff')};\n padding: 16px 24px;\n border-radius: 50px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n cursor: pointer;\n z-index: 999999;\n display: flex;\n align-items: center;\n gap: 12px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n font-weight: 600;\n font-size: 14px;\n animation: aegisBubbleIn 0.3s ease-out;\n `;\n \n if (icon_url) {\n const icon = document.createElement('img');\n const safeUrl = this.sanitizeUrl(icon_url);\n if (safeUrl) {\n icon.src = safeUrl;\n icon.alt = '';\n icon.style.cssText = 'width: 24px; height: 24px;';\n bubble.appendChild(icon);\n }\n }\n \n const textEl = document.createElement('span');\n textEl.textContent = text || 'Chat with us';\n bubble.appendChild(textEl);\n \n bubble.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'click');\n const safeUrl = this.sanitizeUrl(link_url);\n if (safeUrl) {\n window.open(safeUrl, '_blank');\n }\n });\n \n this.addAnimationStyles();\n bubble.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(bubble);\n }\n \n private renderSpinWheel(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-gamification-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-spin-wheel-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n padding: 32px;\n max-width: 500px;\n width: 90%;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'Spin to Win!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Try your luck and win exclusive prizes!';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n modal.appendChild(description);\n \n // Draw an SVG wheel that honours the tenant's configured segments.\n // Each segment's `color` + `label` is painted as a pie slice; falls\n // back to a 4-color placeholder if no segments are provided.\n const segments: Array<{ label: string; color?: string }> = Array.isArray(\n (widget.config as any).segments,\n )\n ? (widget.config as any).segments\n : [\n { label: 'Prize 1', color: '#ff6b6b' },\n { label: 'Prize 2', color: '#feca57' },\n { label: 'Prize 3', color: '#48dbfb' },\n { label: 'Prize 4', color: '#ff6348' },\n ];\n\n const SVG_NS = 'http://www.w3.org/2000/svg';\n const wheel = document.createElementNS(SVG_NS, 'svg');\n wheel.setAttribute('viewBox', '-160 -160 320 320');\n wheel.setAttribute('width', '300');\n wheel.setAttribute('height', '300');\n wheel.style.cssText = 'margin: 0 auto 24px; display: block;';\n\n const n = segments.length;\n const anglePer = (2 * Math.PI) / n;\n const radius = 140;\n for (let i = 0; i < n; i++) {\n const start = i * anglePer - Math.PI / 2;\n const end = start + anglePer;\n const x1 = Math.cos(start) * radius;\n const y1 = Math.sin(start) * radius;\n const x2 = Math.cos(end) * radius;\n const y2 = Math.sin(end) * radius;\n const largeArc = anglePer > Math.PI ? 1 : 0;\n const path = document.createElementNS(SVG_NS, 'path');\n path.setAttribute(\n 'd',\n `M 0 0 L ${x1.toFixed(2)} ${y1.toFixed(2)} A ${radius} ${radius} 0 ${largeArc} 1 ${x2.toFixed(2)} ${y2.toFixed(2)} Z`,\n );\n path.setAttribute('fill', this.sanitizeColor(segments[i].color || '#cccccc'));\n path.setAttribute('stroke', '#ffffff');\n path.setAttribute('stroke-width', '2');\n wheel.appendChild(path);\n\n const labelAngle = start + anglePer / 2;\n const lx = Math.cos(labelAngle) * radius * 0.65;\n const ly = Math.sin(labelAngle) * radius * 0.65;\n const text = document.createElementNS(SVG_NS, 'text');\n text.setAttribute('x', lx.toFixed(2));\n text.setAttribute('y', ly.toFixed(2));\n text.setAttribute('fill', '#ffffff');\n text.setAttribute('font-size', '13');\n text.setAttribute('font-weight', '600');\n text.setAttribute('text-anchor', 'middle');\n text.setAttribute('dominant-baseline', 'middle');\n text.textContent = segments[i].label || `#${i + 1}`;\n wheel.appendChild(text);\n }\n modal.appendChild(wheel);\n \n const spinButton = document.createElement('button');\n spinButton.textContent = 'SPIN NOW!';\n spinButton.style.cssText = `\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n padding: 16px 48px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n margin-bottom: 16px;\n `;\n \n spinButton.addEventListener('click', async () => {\n spinButton.disabled = true;\n spinButton.textContent = 'SPINNING...';\n \n try {\n const prize = await this.generatePrize(widget.config.config_id);\n\n wheel.style.animation = 'aegisSpin 2s ease-out';\n\n setTimeout(() => {\n this.showPrizeResult(modal, prize);\n }, 2000);\n \n } catch (error) {\n this.log(`Error generating prize: ${error}`, true);\n spinButton.disabled = false;\n spinButton.textContent = 'TRY AGAIN';\n }\n });\n \n modal.appendChild(spinButton);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderSpinWheelWidget(widgetId: string, config: NonNullable<PrefetchWidgetConfig['spin_wheel']>): void {\n if (this.isDestroyed) return;\n const cart = this.detectPlatformCart();\n \n if (config.type === 'cart_recovery' && !cart) {\n this.log('Spin wheel requires cart, but none detected');\n return;\n }\n \n if (cart) {\n this.cartData = cart;\n }\n \n const customization = this.cssCustomization.spinWheel || {};\n const accentColor = customization.accentColor || (config as any).accent_color || '#FF6B6B';\n const backgroundColor = customization.backgroundColor || (config as any).background_color || '#FFFFFF';\n const textColor = customization.textColor || (config as any).text_color || '#333333';\n const buttonColor = customization.buttonColor || (config as any).button_color || accentColor;\n const wheelColors = customization.wheelColors || ['#FF6B6B', '#4ECDC4', '#FFE66D', '#95E1D3'];\n \n const overlay = document.createElement('div');\n overlay.className = 'aegis-spin-wheel-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.7);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n animation: aegisFadeIn 0.3s ease;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-spin-wheel-modal';\n modal.style.cssText = `\n background: ${this.sanitizeColor(backgroundColor)};\n border-radius: 16px;\n padding: 32px;\n max-width: 500px;\n width: 90%;\n box-shadow: 0 20px 60px rgba(0,0,0,0.3);\n position: relative;\n animation: aegisScaleIn 0.4s ease;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n const closeBtn = document.createElement('button');\n closeBtn.innerHTML = '✕';\n closeBtn.className = 'aegis-spin-wheel-close';\n closeBtn.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: none;\n border: none;\n font-size: 24px;\n cursor: pointer;\n color: #999;\n `;\n closeBtn.onclick = () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'spin_wheel' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n };\n modal.appendChild(closeBtn);\n \n const title = document.createElement('h2');\n title.textContent = this.interpolateCartVariables((config as any).title || 'Spin to Win!');\n title.style.cssText = `\n margin: 0 0 16px 0;\n font-size: 28px;\n font-weight: bold;\n text-align: center;\n color: ${this.sanitizeColor(textColor)};\n `;\n modal.appendChild(title);\n \n if (cart) {\n const subtitle = document.createElement('p');\n subtitle.textContent = `Complete your ${cart.cart_currency} ${cart.cart_total.toFixed(2)} order and save!`;\n subtitle.style.cssText = `\n margin: 0 0 24px 0;\n font-size: 16px;\n text-align: center;\n color: #666;\n `;\n modal.appendChild(subtitle);\n }\n \n const wheelContainer = document.createElement('div');\n wheelContainer.className = 'aegis-wheel-container';\n wheelContainer.style.cssText = `\n width: 300px;\n height: 300px;\n margin: 0 auto 24px auto;\n background: conic-gradient(\n from 0deg,\n ${wheelColors[0]} 0deg 90deg,\n ${wheelColors[1]} 90deg 180deg,\n ${wheelColors[2]} 180deg 270deg,\n ${wheelColors[3]} 270deg 360deg\n );\n border-radius: 50%;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n `;\n \n const form = document.createElement('form');\n form.className = 'aegis-spin-form';\n form.style.cssText = 'display: block;';\n \n const phoneInput = document.createElement('input');\n phoneInput.type = 'tel';\n phoneInput.placeholder = 'Enter your phone number';\n phoneInput.required = true;\n phoneInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 12px;\n box-sizing: border-box;\n `;\n form.appendChild(phoneInput);\n \n const emailInput = document.createElement('input');\n emailInput.type = 'email';\n emailInput.placeholder = 'Enter your email (optional)';\n emailInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 12px;\n box-sizing: border-box;\n `;\n form.appendChild(emailInput);\n \n const nameInput = document.createElement('input');\n nameInput.type = 'text';\n nameInput.placeholder = 'Enter your name (optional)';\n nameInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 12px;\n box-sizing: border-box;\n `;\n form.appendChild(nameInput);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Spin the Wheel!';\n submitButton.style.cssText = `\n width: 100%;\n padding: 14px;\n background: ${this.sanitizeColor(buttonColor)};\n color: white;\n border: none;\n border-radius: 8px;\n font-size: 16px;\n font-weight: 700;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n const errorMessage = document.createElement('div');\n errorMessage.className = 'aegis-spin-error';\n errorMessage.style.cssText = `\n color: #d32f2f;\n font-size: 14px;\n margin-top: 12px;\n text-align: center;\n display: none;\n `;\n form.appendChild(errorMessage);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n const phone = phoneInput.value.trim();\n const email = emailInput.value.trim();\n const name = nameInput.value.trim();\n \n if (!this.validatePhone(phone)) {\n errorMessage.textContent = 'Please enter a valid phone number (e.g., +1234567890)';\n errorMessage.style.display = 'block';\n return;\n }\n \n if (email && !this.validateEmail(email)) {\n errorMessage.textContent = 'Please enter a valid email address';\n errorMessage.style.display = 'block';\n return;\n }\n \n errorMessage.style.display = 'none';\n submitButton.disabled = true;\n submitButton.textContent = 'Spinning...';\n \n wheelContainer.style.animation = 'aegisSpin 2s ease-out';\n \n setTimeout(async () => {\n try {\n const prize = await this.submitSpinWheel({\n phone,\n email,\n name,\n cart,\n widgetId\n });\n \n this.showSpinWheelPrize(modal, prize);\n this.trackEvent(widgetId, 'submit', {\n type: 'spin_wheel',\n prize_label: prize.prize_label,\n has_email: !!email\n });\n \n } catch (error) {\n this.log(`Error submitting spin wheel: ${error}`, true);\n errorMessage.textContent = 'Failed to submit. Please try again.';\n errorMessage.style.display = 'block';\n submitButton.disabled = false;\n submitButton.textContent = 'Spin the Wheel!';\n wheelContainer.style.animation = '';\n }\n }, 2000);\n });\n \n modal.appendChild(wheelContainer);\n modal.appendChild(form);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'spin_wheel' });\n }\n \n private validatePhone(phone: string): boolean {\n const e164Regex = /^\\+?[1-9]\\d{1,14}$/;\n return e164Regex.test(phone.replace(/[\\s()-]/g, ''));\n }\n \n private validateEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(email);\n }\n \n private async submitSpinWheel(data: {\n phone: string;\n email: string;\n name: string;\n cart: CartData | null;\n widgetId: string;\n }): Promise<GamificationPrize> {\n const url = `${this.apiHost}/v1/widgets/spin-wheel/submit`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const geoRegion = this.detectGeoRegion();\n const deviceType = this.getDeviceType();\n const utmParams = this.getUTMParams();\n \n const body: any = {\n phone: data.phone.replace(/[\\s()-]/g, ''),\n email: data.email || undefined,\n name: data.name || undefined,\n cart_id: data.cart?.cart_id || `web_${Date.now()}`,\n cart_token: data.cart?.cart_id,\n cart_total: data.cart?.cart_total || 0,\n cart_currency: data.cart?.cart_currency || 'USD',\n cart_items: data.cart?.cart_items || [],\n cart_url: window.location.href,\n platform: 'web',\n geo_region: geoRegion,\n device_type: deviceType,\n session_id: this.getSessionId() || undefined,\n anonymous_id: this.getAnonymousId() || undefined,\n utm_source: utmParams.utm_source,\n utm_medium: utmParams.utm_medium,\n utm_campaign: utmParams.utm_campaign\n };\n \n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n credentials: 'include',\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to submit spin wheel: ${response.status} - ${errorText}`);\n }\n \n return await response.json();\n }\n \n private showSpinWheelPrize(modal: HTMLElement, prize: GamificationPrize): void {\n modal.innerHTML = '';\n \n const resultContainer = document.createElement('div');\n resultContainer.style.cssText = 'text-align: center; padding: 40px 20px;';\n \n const emoji = document.createElement('div');\n emoji.textContent = '🎉';\n emoji.style.cssText = 'font-size: 60px; margin-bottom: 20px;';\n resultContainer.appendChild(emoji);\n \n const title = document.createElement('h2');\n title.textContent = 'Congratulations!';\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 28px; font-weight: 700; color: #1a73e8;';\n resultContainer.appendChild(title);\n \n const prizeLabel = document.createElement('p');\n prizeLabel.textContent = prize.prize_label;\n prizeLabel.style.cssText = 'margin: 0 0 20px 0; font-size: 20px; font-weight: 600; color: #333;';\n resultContainer.appendChild(prizeLabel);\n \n if (prize.coupon_code) {\n const couponContainer = document.createElement('div');\n couponContainer.style.cssText = `\n background: #f5f5f5;\n padding: 16px;\n border-radius: 8px;\n margin-bottom: 20px;\n border: 2px dashed #1a73e8;\n `;\n \n const couponLabel = document.createElement('div');\n couponLabel.textContent = 'Your coupon code:';\n couponLabel.style.cssText = 'font-size: 12px; color: #666; margin-bottom: 8px;';\n couponContainer.appendChild(couponLabel);\n \n const couponCode = document.createElement('div');\n couponCode.textContent = prize.coupon_code;\n couponCode.style.cssText = 'font-size: 24px; font-weight: 700; color: #1a73e8; letter-spacing: 2px;';\n couponContainer.appendChild(couponCode);\n \n resultContainer.appendChild(couponContainer);\n }\n \n const message = document.createElement('p');\n message.textContent = 'Check your WhatsApp/SMS for your discount code and cart link!';\n message.style.cssText = 'margin: 0 0 20px 0; font-size: 14px; color: #666;';\n resultContainer.appendChild(message);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n \n closeButton.addEventListener('click', () => {\n const overlay = modal.parentElement;\n if (overlay && document.body.contains(overlay)) {\n document.body.removeChild(overlay);\n }\n });\n \n resultContainer.appendChild(closeButton);\n modal.appendChild(resultContainer);\n }\n \n private detectGeoRegion(): string {\n const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;\n \n if (timezone.includes('America')) return 'north_america';\n if (timezone.includes('Europe')) return 'europe';\n if (timezone.includes('Asia/Kolkata') || timezone.includes('Asia/Calcutta')) return 'india';\n if (timezone.includes('Asia/Singapore') || timezone.includes('Asia/Bangkok') || timezone.includes('Asia/Jakarta')) return 'southeast_asia';\n if (timezone.includes('Asia/Dubai') || timezone.includes('Asia/Riyadh')) return 'middle_east';\n if (timezone.includes('America') && (timezone.includes('Sao_Paulo') || timezone.includes('Buenos_Aires'))) return 'latin_america';\n if (timezone.includes('Australia') || timezone.includes('Pacific/Auckland')) return 'oceania';\n \n return 'north_america';\n }\n \n private getUTMParams(): { utm_source?: string; utm_medium?: string; utm_campaign?: string } {\n const params = new URLSearchParams(window.location.search);\n return {\n utm_source: params.get('utm_source') || undefined,\n utm_medium: params.get('utm_medium') || undefined,\n utm_campaign: params.get('utm_campaign') || undefined\n };\n }\n \n private getSessionId(): string | null {\n return sessionStorage.getItem('aegis_session_id');\n }\n \n private getAnonymousId(): string | null {\n return localStorage.getItem('aegis_anonymous_id');\n }\n \n private renderScratchCard(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-gamification-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-scratch-card-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n padding: 32px;\n max-width: 500px;\n width: 90%;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'Scratch & Win!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 24px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Scratch to reveal your prize!';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n modal.appendChild(description);\n \n const canvas = document.createElement('canvas');\n canvas.width = 300;\n canvas.height = 200;\n canvas.style.cssText = `\n border-radius: 8px;\n cursor: pointer;\n margin: 0 auto 24px;\n display: block;\n box-shadow: 0 4px 12px rgba(0,0,0,0.1);\n `;\n modal.appendChild(canvas);\n \n const ctx = canvas.getContext('2d');\n if (ctx) {\n ctx.fillStyle = '#c0c0c0';\n ctx.fillRect(0, 0, canvas.width, canvas.height);\n ctx.fillStyle = '#888';\n ctx.font = '20px Arial';\n ctx.textAlign = 'center';\n ctx.fillText('Scratch here!', canvas.width / 2, canvas.height / 2);\n \n let isScratching = false;\n \n const scratch = (x: number, y: number) => {\n ctx.globalCompositeOperation = 'destination-out';\n ctx.beginPath();\n ctx.arc(x, y, 20, 0, Math.PI * 2);\n ctx.fill();\n };\n \n canvas.addEventListener('mousedown', () => { isScratching = true; });\n canvas.addEventListener('mouseup', () => { isScratching = false; });\n canvas.addEventListener('mousemove', (e) => {\n if (isScratching) {\n const rect = canvas.getBoundingClientRect();\n scratch(e.clientX - rect.left, e.clientY - rect.top);\n }\n });\n }\n \n const scratchButton = document.createElement('button');\n scratchButton.textContent = 'REVEAL PRIZE';\n scratchButton.style.cssText = `\n background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);\n color: white;\n border: none;\n padding: 16px 48px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n `;\n \n scratchButton.addEventListener('click', async () => {\n scratchButton.disabled = true;\n scratchButton.textContent = 'REVEALING...';\n \n try {\n const prize = await this.generatePrize(widget.config.config_id);\n this.showPrizeResult(modal, prize);\n } catch (error) {\n this.log(`Error generating prize: ${error}`, true);\n scratchButton.disabled = false;\n scratchButton.textContent = 'TRY AGAIN';\n }\n });\n \n modal.appendChild(scratchButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderToast(widget: WidgetConfig): void {\n const { message, icon, duration } = widget.config;\n const position = widget.position || 'bottom_left';\n \n const toast = document.createElement('div');\n toast.className = 'aegis-toast';\n toast.setAttribute('data-widget-id', widget.widget_id);\n \n const positionStyles: Record<string, string> = {\n bottom_left: 'bottom: 20px; left: 20px;',\n bottom_right: 'bottom: 20px; right: 20px;',\n top_left: 'top: 20px; left: 20px;',\n top_right: 'top: 20px; right: 20px;'\n };\n \n toast.style.cssText = `\n position: fixed;\n ${positionStyles[position] || positionStyles.bottom_left}\n background: white;\n padding: 16px 20px;\n border-radius: 8px;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n z-index: 999998;\n display: flex;\n align-items: center;\n gap: 12px;\n max-width: 350px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisToastIn 0.3s ease-out;\n `;\n \n if (icon) {\n const iconEl = document.createElement('div');\n iconEl.textContent = icon;\n iconEl.style.cssText = 'font-size: 20px;';\n toast.appendChild(iconEl);\n }\n \n const messageEl = document.createElement('div');\n messageEl.textContent = message || 'Someone just made a purchase!';\n messageEl.style.cssText = 'flex: 1; font-size: 13px; color: #333;';\n toast.appendChild(messageEl);\n \n this.addAnimationStyles();\n toast.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(toast);\n \n setTimeout(() => {\n toast.style.animation = 'aegisToastOut 0.3s ease-out';\n setTimeout(() => {\n if (document.body.contains(toast)) {\n document.body.removeChild(toast);\n }\n this.renderedWidgets.delete(widget.widget_id);\n }, 300);\n }, duration || 5000);\n }\n \n private renderFeedbackForm(widget: WidgetConfig): void {\n const overlay = document.createElement('div');\n overlay.className = 'aegis-feedback-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 400px;\n background: white;\n box-shadow: -4px 0 20px rgba(0,0,0,0.15);\n z-index: 1000000;\n padding: 32px;\n overflow-y: auto;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisSlideInRight 0.3s ease-out;\n `;\n \n const title = document.createElement('h2');\n title.textContent = widget.config.title || 'We value your feedback!';\n title.style.cssText = 'margin: 0 0 8px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n overlay.appendChild(title);\n \n const description = document.createElement('p');\n description.textContent = widget.config.description || 'Help us improve your experience.';\n description.style.cssText = 'margin: 0 0 24px 0; font-size: 14px; color: #666;';\n overlay.appendChild(description);\n \n const form = document.createElement('form');\n \n const label = document.createElement('label');\n label.textContent = 'How likely are you to recommend us? (0-10)';\n label.style.cssText = 'display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: #333;';\n form.appendChild(label);\n \n const input = document.createElement('input');\n input.type = 'number';\n input.min = '0';\n input.max = '10';\n input.style.cssText = `\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n margin-bottom: 16px;\n `;\n form.appendChild(input);\n \n const textareaLabel = document.createElement('label');\n textareaLabel.textContent = 'Tell us more (optional)';\n textareaLabel.style.cssText = 'display: block; font-size: 14px; font-weight: 600; margin-bottom: 8px; color: #333;';\n form.appendChild(textareaLabel);\n \n const textarea = document.createElement('textarea');\n textarea.rows = 4;\n textarea.style.cssText = `\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 14px;\n resize: vertical;\n margin-bottom: 16px;\n font-family: inherit;\n `;\n form.appendChild(textarea);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Submit Feedback';\n submitButton.style.cssText = `\n width: 100%;\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px;\n border-radius: 6px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n \n try {\n await this.submitFeedback(widget.widget_id, {\n score: parseInt(input.value),\n comment: textarea.value\n });\n \n overlay.innerHTML = '<div style=\"text-align: center; padding: 60px 20px;\"><h3 style=\"margin: 0 0 12px 0; color: #1a73e8;\">Thank you!</h3><p style=\"margin: 0; color: #666;\">Your feedback helps us improve.</p></div>';\n \n setTimeout(() => {\n document.body.removeChild(overlay);\n }, 2000);\n \n } catch (error) {\n this.log(`Error submitting feedback: ${error}`, true);\n submitButton.disabled = false;\n submitButton.textContent = 'Submit Feedback';\n }\n });\n \n overlay.appendChild(form);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n overlay.appendChild(closeButton);\n \n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private renderExitIntentPopup(widget: WidgetConfig): void {\n const { title, description, cta_text, cta_url, image_url } = widget.config;\n \n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widget.widget_id);\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-exit-popup-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n max-width: 600px;\n width: 90%;\n overflow: hidden;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 100%; height: 250px; object-fit: cover;';\n modal.appendChild(img);\n }\n }\n \n const content = document.createElement('div');\n content.style.cssText = 'padding: 32px;';\n \n const titleEl = document.createElement('h2');\n titleEl.textContent = title || 'Wait! Don\\'t leave yet!';\n titleEl.style.cssText = 'margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: #1a1a1a;';\n content.appendChild(titleEl);\n \n const descEl = document.createElement('p');\n descEl.textContent = description || 'Get 10% off your first order!';\n descEl.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; color: #666; line-height: 1.5;';\n content.appendChild(descEl);\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n content.appendChild(ctaButton);\n }\n \n modal.appendChild(content);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: white;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #666;\n width: 40px;\n height: 40px;\n border-radius: 50%;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widget.widget_id, 'dismiss');\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widget.widget_id);\n });\n \n modal.style.position = 'relative';\n modal.appendChild(closeButton);\n \n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n }\n \n private async generatePrize(configId: string): Promise<GamificationPrize> {\n const url = `${this.apiHost}/v1/widgets/gamification/generate-prize`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ config_id: configId }),\n credentials: 'include',\n });\n\n if (!response.ok) {\n throw new Error(`Failed to generate prize: ${response.status}`);\n }\n \n return await response.json();\n }\n \n private showPrizeResult(modal: HTMLElement, prize: GamificationPrize): void {\n modal.innerHTML = '';\n \n const resultContainer = document.createElement('div');\n resultContainer.style.cssText = 'text-align: center; padding: 40px 20px;';\n \n const emoji = document.createElement('div');\n emoji.textContent = '🎉';\n emoji.style.cssText = 'font-size: 60px; margin-bottom: 20px;';\n resultContainer.appendChild(emoji);\n \n const title = document.createElement('h2');\n title.textContent = 'Congratulations!';\n title.style.cssText = 'margin: 0 0 12px 0; font-size: 28px; font-weight: 700; color: #1a73e8;';\n resultContainer.appendChild(title);\n \n const prizeLabel = document.createElement('p');\n prizeLabel.textContent = prize.prize_label;\n prizeLabel.style.cssText = 'margin: 0 0 20px 0; font-size: 20px; font-weight: 600; color: #333;';\n resultContainer.appendChild(prizeLabel);\n \n if (prize.coupon_code) {\n const couponContainer = document.createElement('div');\n couponContainer.style.cssText = `\n background: #f5f5f5;\n padding: 16px;\n border-radius: 8px;\n margin-bottom: 20px;\n border: 2px dashed #1a73e8;\n `;\n \n const couponLabel = document.createElement('div');\n couponLabel.textContent = 'Your coupon code:';\n couponLabel.style.cssText = 'font-size: 12px; color: #666; margin-bottom: 8px;';\n couponContainer.appendChild(couponLabel);\n \n const couponCode = document.createElement('div');\n couponCode.textContent = prize.coupon_code;\n couponCode.style.cssText = 'font-size: 24px; font-weight: 700; color: #1a73e8; letter-spacing: 2px;';\n couponContainer.appendChild(couponCode);\n \n resultContainer.appendChild(couponContainer);\n }\n \n const closeButton = document.createElement('button');\n closeButton.textContent = 'Close';\n closeButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 12px 32px;\n border-radius: 8px;\n font-weight: 600;\n font-size: 14px;\n cursor: pointer;\n `;\n \n closeButton.addEventListener('click', () => {\n const overlay = modal.parentElement;\n if (overlay && document.body.contains(overlay)) {\n document.body.removeChild(overlay);\n }\n });\n \n resultContainer.appendChild(closeButton);\n modal.appendChild(resultContainer);\n }\n \n private async submitFeedback(widgetId: string, data: Record<string, any>): Promise<void> {\n const url = `${this.apiHost}/v1/widgets/track-event`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n // Plumb workspace cascade so feedback submissions are attributed to\n // the same outlet the customer was viewing. See\n // PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n const workspaceId = this.getWorkspaceId?.();\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n widget_id: widgetId,\n event_type: 'submit',\n event_data: data,\n workspace_id: workspaceId,\n }),\n credentials: 'include',\n });\n \n if (!response.ok) {\n throw new Error(`Failed to submit feedback: ${response.status}`);\n }\n }\n \n private async trackEvent(widgetId: string, eventType: string, metadata?: Record<string, any>): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/widgets/track-event`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'Content-Type': 'application/json'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n\n const workspaceId = this.getWorkspaceId?.();\n\n await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n widget_id: widgetId,\n event_type: eventType,\n event_data: metadata || {},\n workspace_id: workspaceId,\n }),\n credentials: 'include',\n });\n\n } catch (error) {\n this.log(`Error tracking widget event: ${error}`, true);\n }\n }\n \n private sanitizeUrl(url?: string): string | null {\n if (!url) return null;\n \n try {\n const parsed = new URL(url, window.location.href);\n if (parsed.protocol === 'javascript:' || parsed.protocol === 'data:') {\n this.log(`Blocked dangerous URL: ${url}`, true);\n return null;\n }\n return parsed.href;\n } catch {\n this.log(`Invalid URL: ${url}`, true);\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n if (/^#[0-9A-F]{3,8}$/i.test(color)) {\n return color;\n }\n \n if (/^rgb\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*\\)$/i.test(color)) {\n return color;\n }\n \n if (/^rgba\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*[\\d.]+\\s*\\)$/i.test(color)) {\n return color;\n }\n \n return '#1a73e8';\n }\n \n private getDeviceType(): string {\n const ua = navigator.userAgent;\n if (/(tablet|ipad|playbook|silk)|(android(?!.*mobi))/i.test(ua)) {\n return 'tablet';\n }\n if (/Mobile|Android|iP(hone|od)|IEMobile|BlackBerry|Kindle|Silk-Accelerated|(hpw|web)OS|Opera M(obi|ini)/.test(ua)) {\n return 'mobile';\n }\n return 'desktop';\n }\n \n private isMobileDevice(): boolean {\n const deviceType = this.getDeviceType();\n return deviceType === 'mobile' || deviceType === 'tablet';\n }\n \n private addAnimationStyles(): void {\n if (document.getElementById('aegis-widget-animations')) {\n return;\n }\n \n const style = document.createElement('style');\n style.id = 'aegis-widget-animations';\n style.textContent = `\n @keyframes aegisFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n \n @keyframes aegisScaleIn {\n from { transform: scale(0.8); opacity: 0; }\n to { transform: scale(1); opacity: 1; }\n }\n \n @keyframes aegisBubbleIn {\n from { transform: translateY(20px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n }\n \n @keyframes aegisToastIn {\n from { transform: translateX(-100%); opacity: 0; }\n to { transform: translateX(0); opacity: 1; }\n }\n \n @keyframes aegisToastOut {\n from { transform: translateX(0); opacity: 1; }\n to { transform: translateX(-100%); opacity: 0; }\n }\n \n @keyframes aegisSlideInRight {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n \n @keyframes aegisSpin {\n from { transform: rotate(0deg); }\n to { transform: rotate(1440deg); }\n }\n `;\n document.head.appendChild(style);\n }\n \n private renderCartRecoveryPopup(widgetId: string, config: NonNullable<PrefetchWidgetConfig['exit_intent']>): void {\n if (this.isDestroyed) return;\n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n overlay.setAttribute('data-aegis-widget-root', '');\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-cart-recovery-modal';\n modal.style.cssText = `\n background: ${this.sanitizeColor(config.background_color || '#FFFFFF')};\n color: ${this.sanitizeColor(config.text_color || '#333333')};\n border-radius: 16px;\n max-width: 600px;\n width: 90%;\n padding: 40px;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n position: relative;\n `;\n \n const title = document.createElement('h2');\n const titleText = this.interpolateCartVariables(config.title || 'Complete Your Order');\n title.textContent = titleText;\n title.style.cssText = `margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: ${this.sanitizeColor(config.accent_color || '#FF6B00')};`;\n modal.appendChild(title);\n \n const message = document.createElement('p');\n const messageText = this.interpolateCartVariables(config.message || 'Your cart is waiting for you!');\n message.textContent = messageText;\n message.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; line-height: 1.5;';\n modal.appendChild(message);\n \n if (config.show_cart_items && this.cartData) {\n const cartItems = document.createElement('div');\n cartItems.style.cssText = 'margin: 0 0 24px 0; padding: 16px; background: rgba(0,0,0,0.05); border-radius: 8px;';\n \n const cartTitle = document.createElement('div');\n cartTitle.textContent = `${this.cartData.cart_items.length} items in your cart`;\n cartTitle.style.cssText = 'font-weight: 600; margin-bottom: 12px;';\n cartItems.appendChild(cartTitle);\n \n this.cartData.cart_items.slice(0, 3).forEach(item => {\n const itemDiv = document.createElement('div');\n itemDiv.textContent = `${item.quantity}× ${item.product_name || item.product_id}`;\n itemDiv.style.cssText = 'font-size: 14px; margin-bottom: 4px;';\n cartItems.appendChild(itemDiv);\n });\n \n modal.appendChild(cartItems);\n }\n \n if (config.discount_code) {\n const discountBox = document.createElement('div');\n discountBox.style.cssText = `\n margin: 0 0 24px 0;\n padding: 16px;\n background: ${this.sanitizeColor(config.accent_color || '#FF6B00')};\n color: white;\n border-radius: 8px;\n text-align: center;\n font-weight: 700;\n font-size: 18px;\n `;\n discountBox.textContent = `Use code: ${config.discount_code} for ${config.discount_percentage || 10}% off!`;\n modal.appendChild(discountBox);\n }\n \n if (config.show_timer && config.timer_minutes) {\n const timer = document.createElement('div');\n timer.style.cssText = 'margin: 0 0 24px 0; text-align: center; font-size: 14px; color: #999;';\n timer.textContent = `⏰ Offer expires in ${config.timer_minutes} minutes`;\n modal.appendChild(timer);\n }\n \n const ctaButton = document.createElement('button');\n ctaButton.textContent = config.cta_text || 'Complete Checkout';\n ctaButton.style.cssText = `\n background: ${this.sanitizeColor(config.accent_color || '#FF6B00')};\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n width: 100%;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'click', { type: 'cart_recovery' });\n const checkoutUrl = this.sanitizeUrl(config.cta_url || '/checkout');\n if (checkoutUrl) {\n window.location.href = checkoutUrl;\n }\n });\n \n modal.appendChild(ctaButton);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'cart_recovery' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n });\n \n modal.appendChild(closeButton);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'cart_recovery', tier: config.tier });\n }\n \n private renderLeadGenPopup(widgetId: string, config: NonNullable<PrefetchWidgetConfig['exit_intent']>): void {\n if (this.isDestroyed) return;\n const overlay = document.createElement('div');\n overlay.className = 'aegis-exit-popup-overlay';\n overlay.setAttribute('data-widget-id', widgetId);\n overlay.setAttribute('data-aegis-widget-root', '');\n \n overlay.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.7);\n z-index: 1000000;\n display: flex;\n align-items: center;\n justify-content: center;\n animation: aegisFadeIn 0.3s ease-out;\n `;\n \n const modal = document.createElement('div');\n modal.className = 'aegis-leadgen-modal';\n modal.style.cssText = `\n background: white;\n border-radius: 16px;\n max-width: 500px;\n width: 90%;\n padding: 40px;\n box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n animation: aegisScaleIn 0.3s ease-out;\n position: relative;\n `;\n \n const title = document.createElement('h2');\n title.textContent = config.title || 'Get 10% Off Your First Order!';\n title.style.cssText = 'margin: 0 0 16px 0; font-size: 28px; font-weight: 700; color: #1a1a1a;';\n modal.appendChild(title);\n \n const message = document.createElement('p');\n message.textContent = config.message || 'Subscribe to our newsletter and get an exclusive discount code.';\n message.style.cssText = 'margin: 0 0 24px 0; font-size: 16px; color: #666;';\n modal.appendChild(message);\n \n const form = document.createElement('form');\n \n const emailInput = document.createElement('input');\n emailInput.type = 'email';\n emailInput.placeholder = 'Enter your email';\n emailInput.required = true;\n emailInput.style.cssText = `\n width: 100%;\n padding: 14px;\n border: 2px solid #ddd;\n border-radius: 8px;\n font-size: 16px;\n margin-bottom: 16px;\n box-sizing: border-box;\n `;\n form.appendChild(emailInput);\n \n const submitButton = document.createElement('button');\n submitButton.type = 'submit';\n submitButton.textContent = 'Get My Discount';\n submitButton.style.cssText = `\n width: 100%;\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n color: white;\n border: none;\n padding: 16px 32px;\n border-radius: 8px;\n font-weight: 700;\n font-size: 16px;\n cursor: pointer;\n `;\n form.appendChild(submitButton);\n \n form.addEventListener('submit', async (e) => {\n e.preventDefault();\n \n submitButton.disabled = true;\n submitButton.textContent = 'Submitting...';\n \n try {\n await this.trackEvent(widgetId, 'submit', { \n type: 'lead_gen',\n email: emailInput.value \n });\n \n modal.innerHTML = `\n <div style=\"text-align: center; padding: 20px;\">\n <div style=\"font-size: 48px; margin-bottom: 16px;\">✅</div>\n <h3 style=\"margin: 0 0 12px 0; color: #1a73e8;\">Thank You!</h3>\n <p style=\"margin: 0; color: #666;\">Check your email for your discount code!</p>\n </div>\n `;\n \n setTimeout(() => {\n document.body.removeChild(overlay);\n }, 2500);\n \n } catch (error) {\n this.log(`Error submitting lead gen form: ${error}`, true);\n submitButton.disabled = false;\n submitButton.textContent = 'Get My Discount';\n }\n });\n \n modal.appendChild(form);\n \n const closeButton = document.createElement('button');\n closeButton.textContent = '×';\n closeButton.setAttribute('aria-label', 'Close');\n closeButton.style.cssText = `\n position: absolute;\n top: 16px;\n right: 16px;\n background: transparent;\n border: none;\n font-size: 28px;\n cursor: pointer;\n color: #999;\n `;\n \n closeButton.addEventListener('click', () => {\n this.trackEvent(widgetId, 'dismiss', { type: 'lead_gen' });\n document.body.removeChild(overlay);\n this.renderedWidgets.delete(widgetId);\n });\n \n modal.appendChild(closeButton);\n overlay.appendChild(modal);\n this.addAnimationStyles();\n overlay.setAttribute('data-aegis-widget-root', '');\n document.body.appendChild(overlay);\n \n this.trackEvent(widgetId, 'show', { type: 'lead_gen' });\n }\n \n private interpolateCartVariables(template: string): string {\n if (!this.cartData) return template;\n \n return template\n .replace(/\\{\\{cart_total\\}\\}/g, `${this.cartData.cart_currency} ${this.cartData.cart_total.toFixed(2)}`)\n .replace(/\\{\\{cart_currency\\}\\}/g, this.cartData.cart_currency)\n .replace(/\\{\\{cart_items_count\\}\\}/g, this.cartData.cart_items.length.toString());\n }\n \n private log(message: string, isError: boolean = false): void {\n if (this.debugMode || isError) {\n const method = isError ? 'error' : 'log';\n console[method](`[AegisWidgets] ${message}`);\n }\n }\n}\n","/**\n * TriggerEngine - Client-Side Trigger Evaluation\n * \n * Evaluates behavioral triggers that require real-time client state:\n * - Scroll depth (e.g., user scrolled >= 50%)\n * - Time on page (e.g., >= 30 seconds on current page)\n * - Exit intent (mouse leaving viewport toward browser controls)\n * - Inactivity (no user interaction for X seconds)\n * - Scroll velocity (mobile exit intent - fast upward scroll)\n * - Visibility change (tab/app switching)\n * - Back button (history navigation)\n * \n * Architecture:\n * - Server evaluates historical triggers (event counts, segments)\n * - Client evaluates behavioral triggers (scroll, time, exit intent)\n * - Zero latency - no server round trip needed\n * \n * Usage:\n * const trigger = new TriggerEngine();\n * trigger.on('scroll_depth_50', (data) => {\n * console.log('User scrolled to 50%', data);\n * });\n * trigger.start();\n */\n\nexport interface TriggerRule {\n type: 'scroll_depth' | 'time_on_page' | 'exit_intent' | 'inactivity' | 'scroll_velocity' | 'visibility_change' | 'back_button';\n config: ScrollDepthConfig | TimeOnPageConfig | ExitIntentConfig | InactivityConfig | ScrollVelocityConfig | VisibilityChangeConfig | BackButtonConfig;\n}\n\nexport interface ScrollDepthConfig {\n depth_percent: number;\n}\n\nexport interface TimeOnPageConfig {\n seconds: number;\n}\n\nexport interface ExitIntentConfig {\n enabled: boolean;\n}\n\nexport interface InactivityConfig {\n idle_seconds: number;\n}\n\nexport interface ScrollVelocityConfig {\n threshold: number;\n minScrollPosition: number;\n cooldown: number;\n}\n\nexport interface VisibilityChangeConfig {\n enabled: boolean;\n}\n\nexport interface BackButtonConfig {\n enabled: boolean;\n}\n\n/**\n * Rage-click detection (Micro-Intent Engine P2 Task 1).\n *\n * Per-intent ring buffer: when ≥ `threshold` clicks land on the same\n * intent inside `windowMs`, the engine emits `rage_click` with the\n * intent name + current count + window. Frustration signal used by the\n * Stuck-at-Checkout pattern (e.g., rage-clicking the coupon input four\n * times in 800ms).\n *\n * Defaults match the plan:\n * threshold = 3 clicks\n * windowMs = 1000 ms\n *\n * TriggerEngine does NOT attach its own pointerdown listener — the\n * SelectorBinder (P1 Task 5) owns the global, delegated listener and\n * dispatches per-intent click events. The IntentSnapshotCollector\n * (P2 Task 5) wires `SelectorBinder.onIntent` to\n * `TriggerEngine.noteClick(intent)` so TriggerEngine sees a clean\n * intent-stamped stream instead of raw DOM events.\n */\nexport interface RageClickConfig {\n threshold: number;\n windowMs: number;\n}\n\n/** Payload emitted on the `rage_click` event. */\nexport interface RageClickData {\n intent: string;\n count: number;\n window_ms: number;\n}\n\n/**\n * Hover-dwell tracking (Micro-Intent Engine P2 Task 2).\n *\n * Tracks per-intent hover duration in milliseconds. `getHoverMs(intent)`\n * returns:\n * - if currently hovering the intent: ms since hover_start (live)\n * - else: ms duration of the most recent hover on that intent (sticky)\n * - if never hovered: 0\n *\n * The sticky behaviour is deliberate. The canonical use case\n * (MICRO_INTENT_ENGINE.md Story 1):\n * \"Priya hovers price for 1.8s, considers, moves cursor away.\n * Exit_intent fires. Rule checks price_hover_ms >= 1500 → true.\"\n * The rule evaluator queries `price_hover_ms` AFTER the hover ends. If\n * we reset to 0 on hover_end, the rule never matches the very pattern\n * the engine exists to detect.\n *\n * Threshold-based emission: when a hover crosses one of the registered\n * dwell targets (e.g. 1500ms), a `<intent>_hover_dwell_<ms>` event\n * fires. Useful for journeys that want event-style hooks; the\n * snapshot-style `getHoverMs` is the primary surface for the\n * compound-rule evaluator.\n */\nexport interface HoverDwellConfig {\n /** Dwell thresholds (ms) per intent. The engine fires\n * `<intent>_hover_dwell_<ms>` when an active hover crosses each\n * threshold. Defaults: price→[1500], cta→[1500]. */\n thresholdsByIntent?: Partial<Record<string, ReadonlyArray<number>>>;\n}\n\n/**\n * Mouse velocity toward the viewport top (Micro-Intent Engine P2 Task 3).\n *\n * Detects the early phase of exit-intent: the mouse moving rapidly\n * upward before it actually crosses out of the viewport. The signal is\n * a continuous px/ms value (positive = moving up), updated as samples\n * arrive. When velocity crosses `threshold`, a `mouse_velocity_to_top`\n * event fires (once per session-window cooldown to avoid spamming on a\n * single gesture).\n *\n * Sampling: callers feed positions via `noteMousePosition(x, y, ts)`.\n * The engine keeps a sliding `windowMs` ring buffer of samples and\n * computes velocity as the average dy / dt over samples in the window.\n * Per the plan: sampled at 100ms, 400ms window. That bound is\n * configurable.\n *\n * Why \"to top\" specifically: exit-intent fires only when y crosses 0;\n * we want the warning BEFORE that. A typical exit-intent gesture has\n * upward velocity well above any normal browsing motion.\n */\nexport interface MouseVelocityConfig {\n /** Threshold in px/ms (positive = moving up). Default 1.5 px/ms,\n * ≈ moving 600px in 400ms, which only happens during a deliberate\n * upward sweep. */\n threshold: number;\n /** Rolling-window length over which to compute velocity. */\n windowMs: number;\n /** Cooldown after a fire before the signal can fire again. */\n cooldownMs: number;\n}\n\nexport interface TriggerEvent {\n type: string;\n data: Record<string, any>;\n timestamp: number;\n}\n\ntype TriggerCallback = (event: TriggerEvent) => void;\n\nexport class TriggerEngine {\n private listeners: Map<string, Set<TriggerCallback>> = new Map();\n private isStarted = false;\n \n private scrollDepthTargets = new Set<number>();\n private scrollDepthReached = new Set<number>();\n \n private timeOnPageTargets = new Map<number, number>();\n private pageLoadTime?: number;\n \n private exitIntentEnabled = false;\n private exitIntentFired = false;\n \n private inactivityTargets = new Map<number, number>();\n private lastActivityTime = Date.now();\n private inactivityCheckInterval?: number;\n \n private scrollVelocityEnabled = false;\n private scrollVelocityFired = false;\n private scrollVelocityConfig: ScrollVelocityConfig = {\n threshold: 0.5,\n minScrollPosition: 100,\n cooldown: 5000\n };\n private lastScrollY = 0;\n private lastScrollTime = Date.now();\n private scrollVelocityCooldownTimer?: number;\n \n private visibilityChangeEnabled = false;\n\n private backButtonEnabled = false;\n private backButtonFired = false;\n\n // Rage-click state (P2 Task 1). Buffer indexed by intent name (price /\n // cta / coupon / checkout). Per-intent so rage-clicking the coupon\n // input doesn't get conflated with rapid CTA presses.\n private rageClickEnabled = false;\n private rageClickConfig: RageClickConfig = { threshold: 3, windowMs: 1000 };\n private rageClickBuffer = new Map<string, number[]>();\n /** Tracks whether a rage_click event has already fired for an intent\n * in the current burst. Reset when the buffer empties below threshold.\n * Prevents the same burst from firing the event N-2 times as each\n * click after threshold pushes the count higher. */\n private rageClickFiredInBurst = new Set<string>();\n\n // Hover-dwell state (P2 Task 2). Tracks live + sticky-last dwell per\n // intent. `hoverStartAt` undefined means no active hover.\n private hoverEnabled = false;\n private hoverStartAt = new Map<string, number>();\n private hoverLastMs = new Map<string, number>();\n private hoverThresholds = new Map<string, ReadonlyArray<number>>([\n ['price', [1500]],\n ['cta', [1500]],\n ]);\n /** Track which (intent, threshold) pairs have already fired in the\n * current hover so a single long hover doesn't double-fire as time\n * marches on. Reset on hover_start. */\n private hoverThresholdsFired = new Map<string, Set<number>>();\n /** Pending threshold timers, keyed by intent. Cleared on hover_end. */\n private hoverThresholdTimers = new Map<string, number[]>();\n\n // Mouse-velocity-to-top state (P2 Task 3).\n private mouseVelEnabled = false;\n private mouseVelConfig: MouseVelocityConfig = {\n threshold: 1.5,\n windowMs: 400,\n cooldownMs: 5000,\n };\n /** Ring buffer of (x, y, ts) samples within the rolling window. */\n private mouseSamples: Array<{ x: number; y: number; ts: number }> = [];\n /** Last computed velocity-to-top in px/ms (positive = moving up). */\n private mouseVelLast = 0;\n /** Epoch ms when the velocity event last fired — gates cooldown. */\n private mouseVelLastFiredAt = 0;\n\n constructor() {}\n \n on(eventType: string, callback: TriggerCallback): void {\n if (!this.listeners.has(eventType)) {\n this.listeners.set(eventType, new Set());\n }\n this.listeners.get(eventType)!.add(callback);\n }\n \n off(eventType: string, callback: TriggerCallback): void {\n const callbacks = this.listeners.get(eventType);\n if (callbacks) {\n callbacks.delete(callback);\n }\n }\n \n registerScrollDepth(depthPercent: number): void {\n this.scrollDepthTargets.add(depthPercent);\n }\n \n registerTimeOnPage(seconds: number): void {\n if (!this.timeOnPageTargets.has(seconds)) {\n const timerId = window.setTimeout(() => {\n this.emit(`time_on_page_${seconds}`, {\n seconds,\n page_url: window.location.href\n });\n this.timeOnPageTargets.delete(seconds);\n }, seconds * 1000);\n \n this.timeOnPageTargets.set(seconds, timerId);\n }\n }\n \n registerExitIntent(): void {\n this.exitIntentEnabled = true;\n }\n \n registerInactivity(idleSeconds: number): void {\n if (!this.inactivityTargets.has(idleSeconds)) {\n const timerId = window.setTimeout(() => {\n const idleTime = (Date.now() - this.lastActivityTime) / 1000;\n \n if (idleTime >= idleSeconds) {\n this.emit(`inactivity_${idleSeconds}`, {\n idle_seconds: idleSeconds,\n actual_idle_time: idleTime\n });\n }\n \n this.inactivityTargets.delete(idleSeconds);\n }, idleSeconds * 1000);\n \n this.inactivityTargets.set(idleSeconds, timerId);\n }\n }\n \n registerScrollVelocity(config?: Partial<ScrollVelocityConfig>): void {\n this.scrollVelocityEnabled = true;\n \n if (config) {\n this.scrollVelocityConfig = {\n threshold: config.threshold ?? this.scrollVelocityConfig.threshold,\n minScrollPosition: config.minScrollPosition ?? this.scrollVelocityConfig.minScrollPosition,\n cooldown: config.cooldown ?? this.scrollVelocityConfig.cooldown\n };\n }\n }\n \n registerVisibilityChange(): void {\n this.visibilityChangeEnabled = true;\n }\n \n registerBackButton(): void {\n this.backButtonEnabled = true;\n }\n\n /**\n * Enable rage-click detection (P2 Task 1).\n *\n * After calling this, feed click events via `noteClick(intent)`.\n * SelectorBinder (P1 Task 5) is the upstream source; the wiring lives\n * in IntentSnapshotCollector (P2 Task 5). Calling `noteClick` without\n * a prior `registerRageClick()` is a no-op so legacy callers don't\n * accidentally enable the feature.\n */\n registerRageClick(config?: Partial<RageClickConfig>): void {\n this.rageClickEnabled = true;\n if (config) {\n this.rageClickConfig = {\n threshold: config.threshold ?? this.rageClickConfig.threshold,\n windowMs: config.windowMs ?? this.rageClickConfig.windowMs,\n };\n }\n }\n\n /**\n * Feed a click event for rage-click detection. Returns the current\n * count within-window for the intent — useful for the snapshot\n * collector (P2 Task 5) which updates IntentRuleEvaluator with\n * `rage_click_count` continuously, not just at the burst threshold.\n *\n * Idempotent w.r.t. `registerRageClick` — when not enabled, this\n * returns 0 without buffering.\n */\n noteClick(intent: string, timestamp: number = Date.now()): number {\n if (!this.rageClickEnabled) return 0;\n const buf = this.rageClickBuffer.get(intent) ?? [];\n const cutoff = timestamp - this.rageClickConfig.windowMs;\n // Drop expired timestamps (clicks older than the window).\n // The buffer is append-only and timestamps are non-decreasing in\n // practice (Date.now is monotonic per device, modulo system-clock\n // adjustments), so an in-place trim from the head is correct.\n let trimStart = 0;\n while (trimStart < buf.length && buf[trimStart] < cutoff) trimStart++;\n const recent = trimStart === 0 ? buf : buf.slice(trimStart);\n recent.push(timestamp);\n this.rageClickBuffer.set(intent, recent);\n\n // Burst reset: when the recent count drops below threshold (because\n // older clicks aged out), the burst is over — re-arm the per-intent\n // fire-once latch so a fresh burst can fire again.\n if (recent.length < this.rageClickConfig.threshold) {\n this.rageClickFiredInBurst.delete(intent);\n }\n\n if (\n recent.length >= this.rageClickConfig.threshold &&\n !this.rageClickFiredInBurst.has(intent)\n ) {\n this.rageClickFiredInBurst.add(intent);\n this.emit('rage_click', {\n intent,\n count: recent.length,\n window_ms: this.rageClickConfig.windowMs,\n });\n }\n\n return recent.length;\n }\n\n /** Snapshot the current rage_click count for an intent. The\n * IntentSnapshotCollector (P2 Task 5) calls this when building the\n * `rage_click_count` signal value for the IntentRuleEvaluator. */\n getRageClickCount(intent: string): number {\n if (!this.rageClickEnabled) return 0;\n const buf = this.rageClickBuffer.get(intent);\n if (!buf) return 0;\n const cutoff = Date.now() - this.rageClickConfig.windowMs;\n // Lazy prune on read so the count reflects only currently-valid\n // clicks. Cheap because buffers are tiny (cap at threshold + 1 in\n // practice — older entries get dropped on every note).\n let live = 0;\n for (const ts of buf) {\n if (ts >= cutoff) live++;\n }\n return live;\n }\n\n /**\n * Enable hover-dwell tracking (P2 Task 2). After registration, feed\n * hover events via `noteHoverStart(intent, ts)` / `noteHoverEnd`.\n * SelectorBinder (P1 Task 5) is the upstream source; wiring lands in\n * P2 Task 5.\n */\n registerHoverDwell(config?: HoverDwellConfig): void {\n this.hoverEnabled = true;\n if (config?.thresholdsByIntent) {\n for (const [intent, thresholds] of Object.entries(config.thresholdsByIntent)) {\n if (thresholds) this.hoverThresholds.set(intent, thresholds);\n }\n }\n }\n\n /** Record the start of a hover on the named intent. Schedules\n * per-threshold timers that emit `<intent>_hover_dwell_<ms>` when\n * the active hover passes each registered threshold. */\n noteHoverStart(intent: string, timestamp: number = Date.now()): void {\n if (!this.hoverEnabled) return;\n // If a prior hover is still pending (no hover_end fired — possible\n // when the DOM node was unmounted mid-hover), close it out first to\n // keep `hoverLastMs` consistent.\n if (this.hoverStartAt.has(intent)) {\n this.noteHoverEnd(intent, timestamp);\n }\n this.hoverStartAt.set(intent, timestamp);\n this.hoverThresholdsFired.set(intent, new Set());\n\n // Schedule threshold timers. Uses the global setTimeout (works in\n // both browser and node, and vitest's fake-timers stub the global).\n // registerHoverDwell with no thresholds for an intent => no events,\n // only the snapshot value.\n const thresholds = this.hoverThresholds.get(intent);\n if (thresholds) {\n const timers: number[] = [];\n for (const ms of thresholds) {\n const timerId = setTimeout(() => {\n // Only fire if still hovering this intent — the event must\n // not fire after hover_end.\n if (!this.hoverStartAt.has(intent)) return;\n const firedSet = this.hoverThresholdsFired.get(intent);\n if (firedSet?.has(ms)) return;\n firedSet?.add(ms);\n this.emit(`${intent}_hover_dwell_${ms}`, {\n intent,\n threshold_ms: ms,\n actual_ms: Date.now() - timestamp,\n });\n }, ms) as unknown as number;\n timers.push(timerId);\n }\n this.hoverThresholdTimers.set(intent, timers);\n }\n }\n\n /** Record the end of a hover on the named intent. Updates the sticky\n * `lastHoverMs` so `getHoverMs` keeps returning the last-known\n * duration until the next hover_start. */\n noteHoverEnd(intent: string, timestamp: number = Date.now()): void {\n if (!this.hoverEnabled) return;\n const startAt = this.hoverStartAt.get(intent);\n if (startAt === undefined) return;\n const dwell = Math.max(0, timestamp - startAt);\n this.hoverLastMs.set(intent, dwell);\n this.hoverStartAt.delete(intent);\n // Clear pending threshold timers for this intent.\n const pending = this.hoverThresholdTimers.get(intent);\n if (pending) {\n for (const id of pending) clearTimeout(id);\n this.hoverThresholdTimers.delete(intent);\n }\n this.hoverThresholdsFired.delete(intent);\n }\n\n /**\n * Enable mouse-velocity-to-top tracking (P2 Task 3). After\n * registration, feed positions via `noteMousePosition(x, y, ts?)`.\n * IntentSnapshotCollector (P2 Task 5) wires window 'mousemove' to\n * this method, with throttling to ~100ms per the plan.\n */\n registerMouseVelocityToTop(config?: Partial<MouseVelocityConfig>): void {\n this.mouseVelEnabled = true;\n if (config) {\n this.mouseVelConfig = {\n threshold: config.threshold ?? this.mouseVelConfig.threshold,\n windowMs: config.windowMs ?? this.mouseVelConfig.windowMs,\n cooldownMs: config.cooldownMs ?? this.mouseVelConfig.cooldownMs,\n };\n }\n }\n\n /** Feed a mouse-position sample. Recomputes the rolling-window\n * velocity-to-top and fires `mouse_velocity_to_top` when the value\n * crosses the configured threshold (subject to cooldown). */\n noteMousePosition(x: number, y: number, timestamp: number = Date.now()): number {\n if (!this.mouseVelEnabled) return 0;\n const cutoff = timestamp - this.mouseVelConfig.windowMs;\n // Trim expired samples from the head; keep the buffer small.\n let trimStart = 0;\n while (trimStart < this.mouseSamples.length && this.mouseSamples[trimStart].ts < cutoff) {\n trimStart++;\n }\n if (trimStart > 0) this.mouseSamples = this.mouseSamples.slice(trimStart);\n this.mouseSamples.push({ x, y, ts: timestamp });\n\n // Velocity-to-top = -(dy / dt) over the oldest-to-newest pair in\n // the window. Positive value = moving up. With 2+ samples this is\n // the instantaneous slope across the window; with 1 sample we\n // return 0 (cannot compute velocity from a single point).\n if (this.mouseSamples.length < 2) {\n this.mouseVelLast = 0;\n return 0;\n }\n const oldest = this.mouseSamples[0];\n const newest = this.mouseSamples[this.mouseSamples.length - 1];\n const dt = newest.ts - oldest.ts;\n if (dt <= 0) {\n this.mouseVelLast = 0;\n return 0;\n }\n const velocity = -(newest.y - oldest.y) / dt; // px/ms; up = +\n this.mouseVelLast = velocity;\n\n // Threshold + cooldown firing. Initial state allows the first fire\n // — only subsequent fires within cooldownMs of the prior are gated.\n const cooldownOk =\n this.mouseVelLastFiredAt === 0 ||\n timestamp - this.mouseVelLastFiredAt >= this.mouseVelConfig.cooldownMs;\n if (velocity >= this.mouseVelConfig.threshold && cooldownOk) {\n this.mouseVelLastFiredAt = timestamp;\n this.emit('mouse_velocity_to_top', {\n velocity,\n threshold: this.mouseVelConfig.threshold,\n window_ms: this.mouseVelConfig.windowMs,\n samples: this.mouseSamples.length,\n });\n }\n return velocity;\n }\n\n /** Snapshot the current velocity-to-top in px/ms (positive = up).\n * Returns 0 when not enabled or insufficient samples. */\n getMouseVelocityToTop(): number {\n return this.mouseVelEnabled ? this.mouseVelLast : 0;\n }\n\n /**\n * Snapshot the hover dwell for an intent.\n * - Currently hovering: returns `Date.now() - hover_start_at`.\n * - Hovered before, not currently: returns the most recent hover's\n * duration (sticky — the rule evaluator queries this AFTER\n * mouseleave to see \"did Priya hover the price ≥ 1.5s?\").\n * - Never hovered: returns 0.\n */\n getHoverMs(intent: string): number {\n if (!this.hoverEnabled) return 0;\n const startAt = this.hoverStartAt.get(intent);\n if (startAt !== undefined) {\n return Math.max(0, Date.now() - startAt);\n }\n return this.hoverLastMs.get(intent) ?? 0;\n }\n\n start(): void {\n if (this.isStarted) {\n return;\n }\n \n this.pageLoadTime = Date.now();\n this.lastActivityTime = Date.now();\n this.lastScrollY = window.scrollY || 0;\n this.lastScrollTime = Date.now();\n \n if (this.scrollDepthTargets.size > 0) {\n this.attachScrollListener();\n }\n \n if (this.exitIntentEnabled) {\n this.attachExitIntentListener();\n }\n \n if (this.scrollVelocityEnabled) {\n this.attachScrollVelocityListener();\n }\n \n if (this.visibilityChangeEnabled) {\n this.attachVisibilityChangeListener();\n }\n \n if (this.backButtonEnabled) {\n this.attachBackButtonListener();\n }\n \n this.attachActivityListeners();\n \n this.startInactivityCheck();\n \n this.isStarted = true;\n }\n \n stop(): void {\n if (!this.isStarted) {\n return;\n }\n \n this.removeScrollListener();\n this.removeExitIntentListener();\n this.removeScrollVelocityListener();\n this.removeVisibilityChangeListener();\n this.removeBackButtonListener();\n this.removeActivityListeners();\n \n this.timeOnPageTargets.forEach(timerId => clearTimeout(timerId));\n this.timeOnPageTargets.clear();\n \n this.inactivityTargets.forEach(timerId => clearTimeout(timerId));\n this.inactivityTargets.clear();\n \n if (this.inactivityCheckInterval) {\n clearInterval(this.inactivityCheckInterval);\n this.inactivityCheckInterval = undefined;\n }\n \n if (this.scrollVelocityCooldownTimer) {\n clearTimeout(this.scrollVelocityCooldownTimer);\n this.scrollVelocityCooldownTimer = undefined;\n }\n \n this.isStarted = false;\n }\n \n reset(): void {\n this.scrollDepthReached.clear();\n this.exitIntentFired = false;\n this.scrollVelocityFired = false;\n this.backButtonFired = false;\n this.pageLoadTime = Date.now();\n this.lastActivityTime = Date.now();\n this.lastScrollY = window.scrollY || 0;\n this.lastScrollTime = Date.now();\n \n if (this.scrollVelocityCooldownTimer) {\n clearTimeout(this.scrollVelocityCooldownTimer);\n this.scrollVelocityCooldownTimer = undefined;\n }\n }\n \n private attachScrollListener(): void {\n window.addEventListener('scroll', this.handleScroll, { passive: true });\n this.handleScroll();\n }\n \n private removeScrollListener(): void {\n window.removeEventListener('scroll', this.handleScroll);\n }\n \n private handleScroll = (): void => {\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;\n const scrollPercent = scrollHeight > 0 ? (scrollTop / scrollHeight) * 100 : 0;\n \n for (const targetDepth of this.scrollDepthTargets) {\n if (scrollPercent >= targetDepth && !this.scrollDepthReached.has(targetDepth)) {\n this.scrollDepthReached.add(targetDepth);\n \n this.emit(`scroll_depth_${targetDepth}`, {\n depth_percent: targetDepth,\n actual_percent: scrollPercent,\n scroll_top: scrollTop,\n scroll_height: scrollHeight\n });\n }\n }\n };\n \n private attachExitIntentListener(): void {\n document.addEventListener('mouseleave', this.handleExitIntent);\n }\n \n private removeExitIntentListener(): void {\n document.removeEventListener('mouseleave', this.handleExitIntent);\n }\n \n private handleExitIntent = (event: MouseEvent): void => {\n if (this.exitIntentFired) {\n return;\n }\n \n if (event.clientY < 10) {\n this.exitIntentFired = true;\n \n this.emit('exit_intent', {\n client_y: event.clientY,\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n }\n };\n \n private attachScrollVelocityListener(): void {\n window.addEventListener('scroll', this.handleScrollVelocity, { passive: true });\n }\n \n private removeScrollVelocityListener(): void {\n window.removeEventListener('scroll', this.handleScrollVelocity);\n }\n \n private handleScrollVelocity = (): void => {\n if (this.scrollVelocityFired) {\n return;\n }\n \n const currentY = window.scrollY || document.documentElement.scrollTop;\n const currentTime = Date.now();\n const timeDiff = currentTime - this.lastScrollTime;\n \n if (timeDiff > 100) {\n const distance = this.lastScrollY - currentY;\n const velocity = Math.abs(distance / timeDiff);\n \n if (\n distance > 0 &&\n velocity > this.scrollVelocityConfig.threshold &&\n currentY > this.scrollVelocityConfig.minScrollPosition\n ) {\n this.scrollVelocityFired = true;\n \n this.emit('mobile_exit_intent', {\n scroll_velocity: velocity,\n scroll_distance: distance,\n current_position: currentY,\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n \n this.scrollVelocityCooldownTimer = window.setTimeout(() => {\n this.scrollVelocityFired = false;\n }, this.scrollVelocityConfig.cooldown);\n }\n \n this.lastScrollY = currentY;\n this.lastScrollTime = currentTime;\n }\n };\n \n private attachVisibilityChangeListener(): void {\n document.addEventListener('visibilitychange', this.handleVisibilityChange);\n }\n \n private removeVisibilityChangeListener(): void {\n document.removeEventListener('visibilitychange', this.handleVisibilityChange);\n }\n \n private handleVisibilityChange = (): void => {\n if (document.hidden) {\n this.emit('visibility_hidden', {\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n } else {\n this.emit('visibility_visible', {\n page_url: window.location.href\n });\n }\n };\n \n private attachBackButtonListener(): void {\n if (typeof window !== 'undefined' && window.history) {\n window.history.pushState(null, '', window.location.href);\n window.addEventListener('popstate', this.handleBackButton);\n }\n }\n \n private removeBackButtonListener(): void {\n window.removeEventListener('popstate', this.handleBackButton);\n }\n \n private handleBackButton = (): void => {\n if (this.backButtonFired) {\n return;\n }\n \n this.backButtonFired = true;\n \n window.history.pushState(null, '', window.location.href);\n \n this.emit('back_button', {\n page_url: window.location.href,\n time_on_page: this.pageLoadTime ? (Date.now() - this.pageLoadTime) / 1000 : 0\n });\n };\n \n private attachActivityListeners(): void {\n const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n events.forEach(event => {\n document.addEventListener(event, this.handleActivity, { passive: true });\n });\n }\n \n private removeActivityListeners(): void {\n const events = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'];\n events.forEach(event => {\n document.removeEventListener(event, this.handleActivity);\n });\n }\n \n private handleActivity = (): void => {\n this.lastActivityTime = Date.now();\n };\n \n private startInactivityCheck(): void {\n this.inactivityCheckInterval = window.setInterval(() => {\n const idleTime = (Date.now() - this.lastActivityTime) / 1000;\n \n for (const [idleSeconds] of this.inactivityTargets) {\n if (idleTime >= idleSeconds) {\n this.emit(`inactivity_${idleSeconds}`, {\n idle_seconds: idleSeconds,\n actual_idle_time: idleTime\n });\n \n const timerId = this.inactivityTargets.get(idleSeconds);\n if (timerId) {\n clearTimeout(timerId);\n this.inactivityTargets.delete(idleSeconds);\n }\n }\n }\n }, 1000);\n }\n \n private emit(eventType: string, data: Record<string, any>): void {\n const event: TriggerEvent = {\n type: eventType,\n data,\n timestamp: Date.now()\n };\n \n const callbacks = this.listeners.get(eventType);\n if (callbacks) {\n callbacks.forEach(callback => {\n try {\n callback(event);\n } catch (error) {\n console.error(`Error in trigger callback for ${eventType}:`, error);\n }\n });\n }\n \n const wildcardCallbacks = this.listeners.get('*');\n if (wildcardCallbacks) {\n wildcardCallbacks.forEach(callback => {\n try {\n callback(event);\n } catch (error) {\n console.error(`Error in wildcard trigger callback:`, error);\n }\n });\n }\n }\n}\n","/**\n * SDK Bootstrap\n *\n * Strict origin-assertion handshake against the control-plane. Called once\n * at SDK init — resolves writeKey → propertyId, asserts origin, and returns\n * the VAPID public key + first-party cookie id the SDK needs for the rest\n * of the session.\n *\n * A failed bootstrap (401/403) aborts SDK init — the SDK refuses to emit\n * events or request push permission against an origin it cannot prove it\n * owns.\n *\n * See docs/architecture/SUBSCRIPTION_PROPERTY_ARCHITECTURE.md §5.\n */\n\nconst COOKIE_NAME = 'aegis_fpc';\nconst COOKIE_MAX_AGE_DAYS = 365 * 2;\n\nexport interface BootstrapRequest {\n writeKey: string;\n currentOrigin?: string;\n firstPartyCookieId?: string;\n attestationToken?: string;\n userAgent?: string;\n}\n\nimport type { EventGovernanceHint } from '../governance';\n\nexport interface BootstrapResult {\n propertyId: string;\n organizationId: string;\n workspaceId: string;\n propertyType: 'shopify_store' | 'hosted_commerce' | 'custom_website' | 'mobile_app';\n vapidPublicKey: string | null;\n allowedOrigins: string[];\n pushEnabled: boolean;\n inAppEnabled: boolean;\n transportMode: string;\n firstPartyCookieId: string;\n /**\n * Event-governance hint for SDK-side unique-event-name cap enforcement.\n * Null = Enterprise plan / CP outage / org has no cap. Safe to pass\n * directly to `aegis.ingestGovernanceHint()`.\n */\n eventGovernance: EventGovernanceHint | null;\n /**\n * @deprecated since P15.1 (2026-05-26) — use `locationCodes` instead.\n * Pre-2026-05-26 the path segment after the brand subdomain was a\n * workspace_code; the canonical hierarchy reversal made it a\n * location_code (outlet under a brand). Field name retained for\n * backward compat with SDK consumers that haven't migrated; new\n * consumers MUST read `locationCodes`. Server still populates BOTH\n * with the same array during the deprecation window so a stale\n * SDK keeps working.\n */\n workspaceCodes: string[];\n /**\n * P15.1 — outlet-code allowlist for SDK path-segment detection.\n * Empty array = no outlet codes registered for this org (single-\n * outlet tenant). SDK uses this set to decide whether\n * `location.pathname.split('/')[1]` is a valid outlet code vs a\n * generic route (`/products`, `/collections`). Pass to\n * `aegis.ingestLocationCodes()` after bootstrap completes.\n *\n * Same array contents as `workspaceCodes` during the deprecation\n * window — renaming the field gives semantic clarity (these ARE\n * location codes under the new hierarchy) without breaking\n * existing consumers.\n */\n locationCodes: string[];\n}\n\nexport class BootstrapError extends Error {\n constructor(public readonly status: number, message: string) {\n super(message);\n this.name = 'BootstrapError';\n }\n}\n\n/**\n * Read the first-party cookie id from document.cookie. Returns null if\n * unset — caller should pass undefined so the server issues a fresh one.\n */\nexport function readFirstPartyCookie(): string | null {\n if (typeof document === 'undefined') return null;\n const match = document.cookie.match(new RegExp(`(?:^|;\\\\s*)${COOKIE_NAME}=([^;]+)`));\n return match ? decodeURIComponent(match[1]) : null;\n}\n\n/**\n * Persist the first-party cookie id returned by the server. Stored as a\n * first-party cookie (readable by JS — it's not a secret, it's a device\n * id) so that the same browser hitting a second origin on the same\n * registrable domain gets the same cookie back.\n */\nexport function writeFirstPartyCookie(cookieId: string): void {\n if (typeof document === 'undefined') return;\n const expires = new Date();\n expires.setDate(expires.getDate() + COOKIE_MAX_AGE_DAYS);\n // Derive the registrable-domain boundary where possible so the cookie is\n // shared across subdomains (e.g., shop.sweettruths.com + www.sweettruths.com).\n // We conservatively use the current host without a leading dot —\n // browsers scope the cookie to that host. For cross-registrable-domain\n // siblings (Shopify myshopify.com + actii.me), each domain gets its own\n // cookie, which is the expected per-origin behavior.\n document.cookie =\n `${COOKIE_NAME}=${encodeURIComponent(cookieId)};` +\n `expires=${expires.toUTCString()};` +\n `path=/;SameSite=Lax`;\n // Also mirror into localStorage so the SDK can read it synchronously at\n // init time even in environments where document.cookie is delayed.\n try {\n window.localStorage.setItem(COOKIE_NAME, cookieId);\n } catch {\n // private mode / storage disabled — cookie alone is sufficient\n }\n}\n\n/**\n * Perform the bootstrap handshake.\n *\n * @throws BootstrapError on 4xx — the SDK should stop init and surface the\n * error to the console. 5xx are retried once.\n */\nexport async function bootstrap(\n apiHost: string,\n req: BootstrapRequest\n): Promise<BootstrapResult> {\n const body: Record<string, unknown> = { writeKey: req.writeKey };\n if (req.currentOrigin) body.currentOrigin = req.currentOrigin;\n if (req.firstPartyCookieId) body.firstPartyCookieId = req.firstPartyCookieId;\n if (req.attestationToken) body.attestationToken = req.attestationToken;\n if (req.userAgent) body.userAgent = req.userAgent;\n\n const url = `${apiHost.replace(/\\/$/, '')}/v1/sdk/bootstrap`;\n\n const doFetch = async (): Promise<Response> =>\n fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n credentials: 'omit',\n });\n\n let resp = await doFetch();\n if (resp.status >= 500) {\n await new Promise((r) => setTimeout(r, 750));\n resp = await doFetch();\n }\n\n if (resp.status === 401) {\n throw new BootstrapError(401, 'writeKey not recognized or revoked');\n }\n if (resp.status === 403) {\n throw new BootstrapError(403, 'Origin validation failed — this writeKey is bound to a different property');\n }\n if (!resp.ok) {\n throw new BootstrapError(resp.status, `Bootstrap failed: HTTP ${resp.status}`);\n }\n\n const json = (await resp.json()) as BootstrapResult;\n writeFirstPartyCookie(json.firstPartyCookieId);\n return json;\n}\n\n/**\n * Derive a deterministic device fingerprint from the first-party cookie id\n * and a few stable UA parts. This is the value sent on every push-subscribe\n * call — its stability lets the backend UPSERT on (property_id, fingerprint)\n * across re-subscribes and VAPID rotations.\n */\nexport async function deriveDeviceFingerprint(firstPartyCookieId: string): Promise<string> {\n const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n const platform = typeof navigator !== 'undefined' ? (navigator.platform || '') : '';\n const language = typeof navigator !== 'undefined' ? (navigator.language || '') : '';\n const input = `${firstPartyCookieId}|${ua}|${platform}|${language}`;\n\n // Prefer subtle.crypto — available in all push-capable browsers.\n if (typeof crypto !== 'undefined' && crypto.subtle) {\n const bytes = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest('SHA-256', bytes);\n const hex = Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n return hex;\n }\n\n // Last-resort fallback — basic 32-bit FNV hash (non-cryptographic but\n // stable). Push-capable browsers always have subtle.crypto so this path\n // is practically unreachable; included only for completeness.\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = (h + ((h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24))) >>> 0;\n }\n return `fnv-${h.toString(16)}`;\n}\n","import { Aegis } from './core/analytics';\nimport { AegisMessageRuntime } from './runtime';\nimport { AegisPlacementManager } from './placements/AegisPlacementManager';\nimport { TriggerEngine } from './triggers';\nimport { bootstrap } from './core/bootstrap';\n\ndeclare global {\n interface Window {\n aegis: any;\n Aegis: typeof Aegis;\n AegisMessageRuntime: typeof AegisMessageRuntime;\n AegisPlacementManager: typeof AegisPlacementManager;\n TriggerEngine: typeof TriggerEngine;\n /**\n * `/v1/sdk/bootstrap` handshake. Exposed for integrations that embed the\n * CDN bundle and need to retrieve `eventGovernance`, `vapidPublicKey`, etc.\n * Added in SDK 1.4.0 alongside SDK-side event-governance.\n */\n aegisBootstrap: typeof bootstrap;\n }\n}\n\nconst globalName = 'aegis';\nconst existingQueue: any = window[globalName] || [];\n\nconst instance = new Aegis();\n\nif (Array.isArray(existingQueue)) {\n const loadOptions = (existingQueue as any)._loadOptions;\n\n existingQueue.forEach((item: any) => {\n if (!Array.isArray(item)) {\n return;\n }\n\n const [method, ...args] = item;\n\n if (typeof method !== 'string' || method.length === 0) {\n return;\n }\n\n // Resolve dotted paths (e.g. 'user.login') by walking the object\n // graph. Snippet callers can use `aegis.user.login(id)` before the\n // bundle finishes loading; the queue captures ['user.login', id]\n // and we replay it as instance.user.login(id).\n const parts = method.split('.');\n let owner: any = null;\n let fn: any = instance;\n for (let i = 0; i < parts.length; i += 1) {\n if (fn == null) break;\n owner = fn;\n fn = fn[parts[i]];\n }\n\n if (typeof fn === 'function') {\n try {\n fn.apply(owner, args);\n } catch (error) {\n console.error(`[Active Reach SDK] Error executing queued method \"${method}\":`, error);\n }\n } else {\n console.warn(`[Active Reach SDK] Unknown method \"${method}\"`);\n }\n });\n\n if (loadOptions?.key) {\n instance.init(loadOptions.key, loadOptions.options).catch((error) => {\n console.error('[Active Reach SDK] Initialization failed:', error);\n });\n }\n}\n\nwindow[globalName] = instance;\nwindow.Aegis = Aegis;\nwindow.AegisMessageRuntime = AegisMessageRuntime;\nwindow.AegisPlacementManager = AegisPlacementManager;\nwindow.TriggerEngine = TriggerEngine;\nwindow.aegisBootstrap = bootstrap;\n\nexport default instance;\n","/**\n * AegisMessageRuntime — unified facade for in-app messaging.\n *\n * The sole public entry point for all 20 in-app types (announce +\n * collect + assist buckets) plus the legacy gamification surface\n * (chat_bubble / toast / exit_intent_popup configured via\n * /v1/widgets/config). Under the hood it composes an\n * `AegisInAppManager` + `AegisWidgetManager`; callers never touch them\n * directly.\n *\n * Public API:\n * new AegisMessageRuntime({ writeKey, apiHost, ... })\n * await runtime.initialize()\n * await runtime.updateContactId(id)\n * runtime.onClientEvent(eventName, data)\n * runtime.destroy()\n *\n * Internal wiring: spin_wheel + scratch_card campaigns land in the\n * inbox prefetch bundle but need DOM renderers that live in\n * WidgetManager. The facade passes an `onInteractiveCampaign` callback\n * into the InAppManager constructor; the callback forwards to\n * `widgets.renderInteractiveCampaign(campaign)` — no CustomEvent bus,\n * no listeners, no registration-order bugs.\n */\n\nimport { AegisInAppManager, type AegisInAppConfig, type InAppCampaign } from '../inapp';\nimport { AegisWidgetManager, type AegisWidgetConfig } from '../widgets';\nimport { TriggerEngine } from '../triggers/TriggerEngine';\n\nexport interface AegisMessageRuntimeConfig extends AegisInAppConfig {\n /** If set, a `TriggerEngine` from the SDK is wired to the WidgetManager\n * for its exit-intent / scroll-velocity / inactivity handlers. Leave\n * undefined to have the runtime construct one internally. */\n triggerEngine?: AegisWidgetConfig['triggerEngine'];\n /** WidgetManager prefetch toggle. Defaults to true — matches the\n * existing behaviour where spin_wheel / scratch_card prefetch their\n * configs from `/v1/widgets/config/prefetch`. */\n enableWidgetPrefetch?: boolean;\n /** Source platform for gamification attribution (shopify / woocommerce\n * / magento / mobile_sdk / web). Passed through to WidgetManager. */\n sourcePlatform?: AegisWidgetConfig['sourcePlatform'];\n}\n\n/**\n * One runtime, all in-app types. Consumers should prefer this over\n * importing AegisInAppManager / AegisWidgetManager directly. Those two\n * classes remain exported for a deprecation window — see `./deprecated.ts`.\n */\nexport class AegisMessageRuntime {\n readonly inApp: AegisInAppManager;\n readonly widgets: AegisWidgetManager;\n private initialized = false;\n\n constructor(config: AegisMessageRuntimeConfig) {\n const triggerEngine = config.triggerEngine ?? new TriggerEngine();\n const ownsTriggerEngine = !config.triggerEngine;\n // Ownership of the engine is tracked downstream by WidgetManager\n // (which is the only consumer that needs to call lifecycle methods\n // on it). The facade itself only forwards.\n\n this.widgets = new AegisWidgetManager({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n userId: config.userId,\n contactId: config.contactId,\n organizationId: config.organizationId,\n debugMode: config.debugMode,\n triggerEngine,\n ownsTriggerEngine,\n enablePrefetch: config.enableWidgetPrefetch !== false,\n sourcePlatform: config.sourcePlatform,\n getWorkspaceId: config.getWorkspaceId,\n });\n\n this.inApp = new AegisInAppManager({\n writeKey: config.writeKey,\n apiHost: config.apiHost,\n userId: config.userId,\n contactId: config.contactId,\n organizationId: config.organizationId,\n propertyId: config.propertyId,\n debugMode: config.debugMode,\n enableSSE: config.enableSSE,\n // Route gamification sub_types straight to the widget renderer —\n // replaces the old CustomEvent('aegis:render-widget') bridge which\n // had no listener and silently dropped impressions.\n onInteractiveCampaign: (campaign) => {\n this.widgets.renderInteractiveCampaign(campaign);\n },\n getWorkspaceId: config.getWorkspaceId,\n });\n }\n\n /**\n * Boots both managers in parallel. Safe to call multiple times — the\n * second + subsequent calls are no-ops.\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n this.initialized = true;\n await Promise.all([\n this.inApp.initialize(),\n this.widgets.initialize(),\n ]);\n }\n\n /**\n * Both managers carry contactId. Updates both so the server-side\n * targeting pipeline sees the identity for campaign eligibility AND\n * the widget prefetch includes per-contact segment configs. The\n * widgets call is async (may re-fetch prefetch configs) but we don't\n * wait — callers that care about the fetched state await on the\n * returned promise.\n */\n async updateContactId(contactId: string): Promise<void> {\n this.inApp.updateContactId?.(contactId);\n await this.widgets.updateContactId(contactId);\n }\n\n /**\n * Forward a client-side event (product_viewed, cart_idle_90s, etc.)\n * to the in-app manager's client-trigger evaluator. The WidgetManager\n * has its own TriggerEngine wiring (exit-intent, scroll-velocity) so\n * we don't forward there — events it cares about arrive through that\n * channel already.\n */\n onClientEvent(eventName: string, eventData: Record<string, unknown> = {}): void {\n this.inApp.onClientEvent?.(eventName, eventData);\n }\n\n /**\n * Conversion-aware suppression — call when the host app observes a\n * goal event (purchase / order_completed / checkout_completed / custom).\n *\n * Forwards to AegisInAppManager.notifyConversion() which:\n * - Persists the conversion in sessionStorage so subsequent page loads\n * in the same tab keep armed campaigns silenced.\n * - For every armed campaign whose\n * `frequency.suppress_after_conversion_seconds` is set, computes an\n * expiry epoch_ms and silences it until then.\n *\n * Storefronts integrate via the React provider's useCommerceEvents\n * helper which calls this after `ecom.orderCompleted(...)`. Direct SDK\n * users call it themselves.\n *\n * Added by Micro-Intent Engine P0a Task 6 (2026-04-30).\n */\n notifyConversion(goalName: string): void {\n this.inApp.notifyConversion?.(goalName);\n }\n\n /**\n * Tear down both managers. Used by React component unmounts + during\n * identity switches where we want a full reset. If the facade created\n * its own TriggerEngine, WidgetManager.destroy() will stop it;\n * caller-supplied engines are left alone.\n */\n destroy(): void {\n this.inApp.destroy?.();\n this.widgets.destroy();\n this.initialized = false;\n }\n\n /**\n * Current armed campaigns visible to the InAppManager. Accessible for\n * the Prefetch Inspector and for debugging. WidgetManager's prefetched\n * spin_wheel / scratch_card configs are NOT in this list — they live\n * in `this.widgets` and have their own lifecycle.\n */\n getCampaigns(): InAppCampaign[] {\n // AegisInAppManager keeps `campaigns` as a private field. Expose it\n // via bracket access; the field is stable as of SDK 1.5.0. Once\n // Phase 7c unifies render paths this becomes a first-class getter.\n return ((this.inApp as unknown as { campaigns?: InAppCampaign[] }).campaigns) ?? [];\n }\n}\n","/**\n * AegisPlacementManager - Web SDK Placement Module\n * \n * Real-time SSE architecture for inline content placements.\n * \n * Security:\n * - XSS Protection: Uses DOMPurify (when available) with allowlisted tags/attrs; safe text-only fallback\n * - URL Validation: Prevents javascript: protocol injection\n * - CSP Compatible: No inline styles in HTML strings\n * \n * Architecture:\n * - Connects to SSE endpoint for real-time placement updates\n * - Initial fetch on load, then SSE for updates\n * - Caches placement content locally in memory\n * - Renders content into pre-defined UI slots (inline, not overlay)\n * - Tracks events (impression, click, conversion) asynchronously\n * \n * Key Differences from In-App Messages:\n * - INLINE content (part of layout), not overlays\n * - Content rendered immediately on slot registration\n * - No trigger rules (shown when slot loads)\n * - Supports banner, card, carousel, video, HTML content types\n */\n\nexport interface PlacementContent {\n placement_id: string;\n variant_id: string;\n content_type: 'banner' | 'card' | 'carousel' | 'video' | 'html' | 'dynamic_injection';\n content: Record<string, any>;\n css_selector?: string;\n injection_mode?: 'replace' | 'append' | 'prepend';\n}\n\nexport interface PlacementSlot {\n placementId: string;\n containerId: string;\n fallbackContent?: string;\n onRender?: (content: PlacementContent) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface AegisPlacementConfig {\n writeKey: string;\n apiHost?: string;\n userId?: string;\n contactId?: string;\n organizationId?: string;\n debugMode?: boolean;\n enableSSE?: boolean;\n /**\n * Workspace cascade reader. Host SDK sets this to\n * `() => aegis.getEffectiveWorkspaceId()` so placement\n * impression/click/conversion events arrive at the gateway with the\n * same workspace context the main analytics SDK uses. See\n * PLUGIN_HANDSHAKE_AUTOMATION_TRACKER.md §P1.b.\n */\n getWorkspaceId?: () => string | undefined;\n}\n\nexport class AegisPlacementManager {\n private writeKey: string;\n private apiHost: string;\n private userId?: string;\n private contactId?: string;\n private organizationId?: string;\n private debugMode: boolean;\n private enableSSE: boolean;\n private getWorkspaceId?: () => string | undefined;\n\n private placements: Map<string, PlacementContent> = new Map();\n private slots: Map<string, PlacementSlot> = new Map();\n private renderedSlots = new Set<string>();\n private eventSource?: EventSource;\n private isInitialized = false;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n\n constructor(config: AegisPlacementConfig) {\n this.writeKey = config.writeKey;\n this.apiHost = config.apiHost || 'https://api.aegis.ai';\n this.userId = config.userId;\n this.contactId = config.contactId;\n this.organizationId = config.organizationId;\n this.debugMode = config.debugMode || false;\n this.enableSSE = config.enableSSE !== false;\n this.getWorkspaceId = config.getWorkspaceId;\n }\n \n async initialize(): Promise<void> {\n if (this.isInitialized) {\n this.log('AegisPlacements already initialized');\n return;\n }\n \n await this.refreshPlacements();\n \n if (this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n \n this.isInitialized = true;\n this.log('AegisPlacements initialized successfully');\n }\n \n register(placementId: string, options: Omit<PlacementSlot, 'placementId'>): void {\n const slot: PlacementSlot = {\n placementId,\n ...options\n };\n \n this.slots.set(placementId, slot);\n this.log(`Registered placement slot: ${placementId}`);\n \n const existingContent = this.placements.get(placementId);\n if (existingContent) {\n this.renderSlot(slot, existingContent);\n } else if (options.fallbackContent) {\n this.renderFallback(slot);\n }\n }\n \n unregister(placementId: string): void {\n this.slots.delete(placementId);\n this.renderedSlots.delete(placementId);\n this.log(`Unregistered placement slot: ${placementId}`);\n }\n \n async refreshPlacements(): Promise<void> {\n try {\n const placementIds = Array.from(this.slots.keys());\n \n if (placementIds.length === 0) {\n this.log('No registered slots, skipping refresh');\n return;\n }\n \n const url = `${this.apiHost}/v1/placements/content?placement_ids=${placementIds.join(',')}`;\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Type': this.getDeviceType(),\n 'X-Platform': 'web'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n const response = await fetch(url, { headers });\n \n if (!response.ok) {\n throw new Error(`Failed to fetch placements: ${response.status}`);\n }\n \n const data = await response.json();\n \n this.placements.clear();\n \n for (const placement of data.placements || []) {\n this.placements.set(placement.placement_id, placement);\n }\n \n this.renderAllSlots();\n \n this.log(`Refreshed ${this.placements.size} placements`);\n \n } catch (error) {\n this.log(`Error refreshing placements: ${error}`, true);\n }\n }\n \n private renderAllSlots(): void {\n for (const [placementId, slot] of this.slots.entries()) {\n const content = this.placements.get(placementId);\n \n if (content) {\n this.renderSlot(slot, content);\n } else if (slot.fallbackContent) {\n this.renderFallback(slot);\n }\n }\n }\n \n private renderSlot(slot: PlacementSlot, content: PlacementContent): void {\n try {\n if (content.content_type === 'dynamic_injection') {\n this.renderDynamicInjection(content);\n } else {\n const container = document.getElementById(slot.containerId);\n \n if (!container) {\n this.log(`Container not found: ${slot.containerId}`, true);\n return;\n }\n \n container.innerHTML = '';\n \n switch (content.content_type) {\n case 'banner':\n this.renderBanner(container, content);\n break;\n case 'card':\n this.renderCard(container, content);\n break;\n case 'carousel':\n this.renderCarousel(container, content);\n break;\n case 'video':\n this.renderVideo(container, content);\n break;\n case 'html':\n this.renderHTML(container, content);\n break;\n default:\n this.log(`Unknown content type: ${content.content_type}`, true);\n return;\n }\n }\n \n if (!this.renderedSlots.has(slot.placementId)) {\n this.trackEvent(content.placement_id, content.variant_id, 'impression');\n this.renderedSlots.add(slot.placementId);\n }\n \n if (slot.onRender) {\n slot.onRender(content);\n }\n \n this.log(`Rendered placement: ${content.placement_id} (${content.content_type})`);\n \n } catch (error) {\n this.log(`Error rendering placement: ${error}`, true);\n \n if (slot.onError) {\n slot.onError(error as Error);\n }\n }\n }\n \n private renderBanner(container: HTMLElement, content: PlacementContent): void {\n const { title, body, image_url, cta_text, cta_url, background_color, text_color } = content.content;\n \n const banner = document.createElement('div');\n banner.className = 'aegis-placement-banner';\n banner.style.cssText = `\n background: ${this.sanitizeColor(background_color || '#1a73e8')};\n color: ${this.sanitizeColor(text_color || '#ffffff')};\n padding: 24px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n gap: 16px;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = '';\n img.style.cssText = 'width: 80px; height: 80px; border-radius: 8px; object-fit: cover;';\n banner.appendChild(img);\n }\n }\n \n const textContainer = document.createElement('div');\n textContainer.style.cssText = 'flex: 1;';\n \n if (title) {\n const titleEl = document.createElement('div');\n titleEl.textContent = title;\n titleEl.style.cssText = 'font-size: 18px; font-weight: 600; margin-bottom: 8px;';\n textContainer.appendChild(titleEl);\n }\n \n if (body) {\n const bodyEl = document.createElement('div');\n bodyEl.textContent = body;\n bodyEl.style.cssText = 'font-size: 14px; opacity: 0.9;';\n textContainer.appendChild(bodyEl);\n }\n \n banner.appendChild(textContainer);\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: white;\n color: ${this.sanitizeColor(background_color || '#1a73e8')};\n border: none;\n padding: 12px 24px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n white-space: nowrap;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n banner.appendChild(ctaButton);\n }\n \n container.appendChild(banner);\n }\n \n private renderCard(container: HTMLElement, content: PlacementContent): void {\n const { title, body, image_url, cta_text, cta_url } = content.content;\n \n const card = document.createElement('div');\n card.className = 'aegis-placement-card';\n card.style.cssText = `\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.1);\n overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = title || '';\n img.style.cssText = 'width: 100%; height: 200px; object-fit: cover;';\n card.appendChild(img);\n }\n }\n \n const cardBody = document.createElement('div');\n cardBody.style.cssText = 'padding: 20px;';\n \n if (title) {\n const titleEl = document.createElement('h3');\n titleEl.textContent = title;\n titleEl.style.cssText = 'margin: 0 0 12px 0; font-size: 20px; font-weight: 600; color: #1a1a1a;';\n cardBody.appendChild(titleEl);\n }\n \n if (body) {\n const bodyEl = document.createElement('p');\n bodyEl.textContent = body;\n bodyEl.style.cssText = 'margin: 0 0 16px 0; font-size: 14px; color: #666; line-height: 1.5;';\n cardBody.appendChild(bodyEl);\n }\n \n if (cta_text && cta_url) {\n const ctaButton = document.createElement('button');\n ctaButton.textContent = cta_text;\n ctaButton.style.cssText = `\n background: #1a73e8;\n color: white;\n border: none;\n padding: 10px 20px;\n border-radius: 6px;\n font-weight: 600;\n cursor: pointer;\n font-size: 14px;\n `;\n \n ctaButton.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n const safeUrl = this.sanitizeUrl(cta_url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n \n cardBody.appendChild(ctaButton);\n }\n \n card.appendChild(cardBody);\n container.appendChild(card);\n }\n \n private renderCarousel(container: HTMLElement, content: PlacementContent): void {\n const { items } = content.content;\n \n if (!Array.isArray(items) || items.length === 0) {\n this.log('Carousel items empty or invalid', true);\n return;\n }\n \n const carousel = document.createElement('div');\n carousel.className = 'aegis-placement-carousel';\n carousel.style.cssText = `\n display: flex;\n gap: 16px;\n overflow-x: auto;\n padding: 8px 0;\n scroll-behavior: smooth;\n -webkit-overflow-scrolling: touch;\n `;\n \n for (const item of items) {\n const carouselItem = document.createElement('div');\n carouselItem.style.cssText = `\n flex: 0 0 250px;\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n overflow: hidden;\n cursor: pointer;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n `;\n \n if (item.image_url) {\n const img = document.createElement('img');\n const safeUrl = this.sanitizeUrl(item.image_url);\n if (safeUrl) {\n img.src = safeUrl;\n img.alt = item.title || '';\n img.style.cssText = 'width: 100%; height: 150px; object-fit: cover;';\n carouselItem.appendChild(img);\n }\n }\n \n const itemContent = document.createElement('div');\n itemContent.style.cssText = 'padding: 12px;';\n \n if (item.title) {\n const title = document.createElement('div');\n title.textContent = item.title;\n title.style.cssText = 'font-size: 14px; font-weight: 600; margin-bottom: 4px; color: #1a1a1a;';\n itemContent.appendChild(title);\n }\n \n if (item.price) {\n const price = document.createElement('div');\n price.textContent = item.price;\n price.style.cssText = 'font-size: 16px; font-weight: 700; color: #1a73e8;';\n itemContent.appendChild(price);\n }\n \n carouselItem.appendChild(itemContent);\n \n if (item.url) {\n carouselItem.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click', { item_index: items.indexOf(item) });\n const safeUrl = this.sanitizeUrl(item.url);\n if (safeUrl) {\n window.location.href = safeUrl;\n }\n });\n }\n \n carousel.appendChild(carouselItem);\n }\n \n container.appendChild(carousel);\n }\n \n private renderVideo(container: HTMLElement, content: PlacementContent): void {\n const { video_url, poster_url, autoplay, muted } = content.content;\n \n if (!video_url) {\n this.log('Video URL missing', true);\n return;\n }\n \n const safeVideoUrl = this.sanitizeUrl(video_url);\n if (!safeVideoUrl) {\n this.log('Invalid video URL', true);\n return;\n }\n \n const video = document.createElement('video');\n video.src = safeVideoUrl;\n video.controls = true;\n video.autoplay = autoplay || false;\n video.muted = muted || false;\n video.style.cssText = 'width: 100%; border-radius: 8px;';\n \n if (poster_url) {\n const safePosterUrl = this.sanitizeUrl(poster_url);\n if (safePosterUrl) {\n video.poster = safePosterUrl;\n }\n }\n \n video.addEventListener('play', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click', { action: 'video_play' });\n });\n \n container.appendChild(video);\n }\n \n private renderHTML(container: HTMLElement, content: PlacementContent): void {\n const { html } = content.content;\n \n if (!html) {\n this.log('HTML content missing', true);\n return;\n }\n \n const wrapper = document.createElement('div');\n wrapper.className = 'aegis-placement-html';\n wrapper.innerHTML = this.sanitizeHTML(html);\n \n const links = wrapper.querySelectorAll('a');\n links.forEach((link) => {\n link.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n });\n });\n \n container.appendChild(wrapper);\n }\n \n private renderDynamicInjection(content: PlacementContent): void {\n const { html } = content.content;\n const cssSelector = content.css_selector;\n const injectionMode = content.injection_mode || 'replace';\n \n if (!cssSelector) {\n this.log('CSS selector missing for dynamic injection', true);\n return;\n }\n \n if (!html) {\n this.log('HTML content missing for dynamic injection', true);\n return;\n }\n \n let targetElement: HTMLElement | null = null;\n \n try {\n targetElement = document.querySelector(cssSelector);\n } catch (error) {\n this.log(`Invalid CSS selector: ${cssSelector}`, true);\n return;\n }\n \n if (!targetElement) {\n this.log(`Target element not found for selector: ${cssSelector}`, true);\n return;\n }\n \n const wrapper = document.createElement('div');\n wrapper.className = 'aegis-dynamic-injection';\n wrapper.setAttribute('data-placement-id', content.placement_id);\n wrapper.setAttribute('data-variant-id', content.variant_id);\n wrapper.innerHTML = this.sanitizeHTML(html);\n \n const links = wrapper.querySelectorAll('a');\n links.forEach((link) => {\n link.addEventListener('click', () => {\n this.trackEvent(content.placement_id, content.variant_id, 'click');\n });\n });\n \n switch (injectionMode) {\n case 'replace':\n targetElement.innerHTML = '';\n targetElement.appendChild(wrapper);\n this.log(`Replaced content in ${cssSelector}`, false);\n break;\n \n case 'append':\n targetElement.appendChild(wrapper);\n this.log(`Appended content to ${cssSelector}`, false);\n break;\n \n case 'prepend':\n targetElement.insertBefore(wrapper, targetElement.firstChild);\n this.log(`Prepended content to ${cssSelector}`, false);\n break;\n \n default:\n this.log(`Unknown injection mode: ${injectionMode}`, true);\n return;\n }\n \n this.log(`Dynamically injected content for ${content.placement_id} into ${cssSelector} (${injectionMode})`);\n }\n \n private renderFallback(slot: PlacementSlot): void {\n if (!slot.fallbackContent) return;\n \n const container = document.getElementById(slot.containerId);\n if (!container) return;\n \n container.innerHTML = slot.fallbackContent;\n this.log(`Rendered fallback content for: ${slot.placementId}`);\n }\n \n private async trackEvent(\n placementId: string,\n variantId: string,\n eventType: 'impression' | 'click' | 'conversion',\n metadata: Record<string, any> = {}\n ): Promise<void> {\n try {\n const url = `${this.apiHost}/v1/placements/track`;\n \n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Device-Type': this.getDeviceType(),\n 'X-Platform': 'web'\n };\n \n if (this.userId) headers['X-User-ID'] = this.userId;\n if (this.contactId) headers['X-Contact-ID'] = this.contactId;\n if (this.organizationId) headers['X-Organization-ID'] = this.organizationId;\n \n // Plumb the same workspace cascade the main analytics SDK uses\n // (config → ?ws= → URL path slug → setWorkspace runtime → session\n // storage). Without this, placement events arrive at event-ingress\n // unscoped and silently attribute to the org's primary workspace.\n const workspaceId = this.getWorkspaceId?.();\n\n const body = {\n placement_id: placementId,\n variant_id: variantId,\n event_type: eventType,\n workspace_id: workspaceId,\n metadata: {\n device_type: this.getDeviceType(),\n platform: 'web',\n ...metadata\n }\n };\n \n fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n }).catch(err => this.log(`Track event failed: ${err}`, true));\n \n this.log(`Tracked ${eventType}: ${placementId}`);\n \n } catch (error) {\n this.log(`Error tracking event: ${error}`, true);\n }\n }\n \n private connectSSE(): void {\n if (this.eventSource) {\n this.disconnectSSE();\n }\n \n if (!this.organizationId) {\n this.log('Cannot connect SSE without organization ID', true);\n return;\n }\n \n const url = new URL('/v1/stream/realtime', this.apiHost);\n \n const headers: Record<string, string> = {\n 'X-Aegis-Write-Key': this.writeKey,\n 'X-Organization-ID': this.organizationId,\n };\n \n if (this.contactId) {\n headers['X-Contact-ID'] = this.contactId;\n }\n \n const queryParams = new URLSearchParams();\n Object.entries(headers).forEach(([key, value]) => {\n queryParams.append(key, value);\n });\n \n this.eventSource = new EventSource(`${url}?${queryParams.toString()}`);\n \n this.eventSource.addEventListener('open', () => {\n this.log('SSE connection established');\n this.reconnectAttempts = 0;\n });\n \n this.eventSource.addEventListener('placement_content_updated', (event: MessageEvent) => {\n try {\n const data = JSON.parse(event.data);\n this.log(`Received placement content update: ${data.placement_id}`);\n this.refreshPlacements();\n } catch (error) {\n this.log(`Error parsing SSE event: ${error}`, true);\n }\n });\n \n this.eventSource.addEventListener('heartbeat', () => {\n this.log('SSE heartbeat received');\n });\n\n this.eventSource.addEventListener('error', () => {\n this.log('SSE connection error', true);\n \n if (this.eventSource?.readyState === EventSource.CLOSED) {\n this.attemptReconnect();\n }\n });\n }\n \n private disconnectSSE(): void {\n if (this.eventSource) {\n this.eventSource.close();\n this.eventSource = undefined;\n this.log('SSE connection closed');\n }\n }\n \n private attemptReconnect(): void {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n this.log('Max reconnect attempts reached, giving up', true);\n return;\n }\n \n this.reconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);\n \n this.log(`Reconnecting SSE in ${delay}ms (attempt ${this.reconnectAttempts})`);\n \n setTimeout(() => {\n if (this.isInitialized && this.enableSSE && this.organizationId) {\n this.connectSSE();\n }\n }, delay);\n }\n \n private getDeviceType(): string {\n const width = window.innerWidth;\n if (width < 768) return 'mobile_web';\n if (width < 1024) return 'tablet_web';\n return 'desktop_web';\n }\n \n private sanitizeUrl(url: string): string | null {\n try {\n const parsed = new URL(url, window.location.href);\n \n if (parsed.protocol === 'javascript:' || parsed.protocol === 'data:') {\n this.log(`Blocked unsafe URL protocol: ${parsed.protocol}`, true);\n return null;\n }\n \n return parsed.href;\n } catch {\n this.log(`Invalid URL: ${url}`, true);\n return null;\n }\n }\n \n private sanitizeColor(color: string): string {\n const hexPattern = /^#[0-9A-Fa-f]{3,6}$/;\n const rgbPattern = /^rgba?\\(\\s*\\d+\\s*,\\s*\\d+\\s*,\\s*\\d+\\s*(,\\s*[\\d.]+\\s*)?\\)$/;\n const namedColors = ['white', 'black', 'red', 'blue', 'green', 'yellow', 'transparent'];\n \n if (hexPattern.test(color) || rgbPattern.test(color) || namedColors.includes(color.toLowerCase())) {\n return color;\n }\n \n this.log(`Invalid color: ${color}`, true);\n return '#000000';\n }\n \n private sanitizeHTML(html: string): string {\n // Use DOMPurify if available (recommended for production)\n const purify = (window as unknown as Record<string, unknown>).DOMPurify as\n | { sanitize: (dirty: string, cfg?: Record<string, unknown>) => string }\n | undefined;\n\n if (purify?.sanitize) {\n return purify.sanitize(html, {\n ALLOWED_TAGS: [\n 'a', 'b', 'br', 'div', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'i', 'img', 'li', 'ol', 'p', 'span', 'strong', 'u', 'ul', 'table',\n 'thead', 'tbody', 'tr', 'th', 'td', 'blockquote', 'hr', 'figure',\n 'figcaption', 'picture', 'source', 'video', 'section', 'article',\n ],\n ALLOWED_ATTR: [\n 'href', 'target', 'rel', 'src', 'alt', 'width', 'height', 'class',\n 'id', 'style', 'title', 'loading', 'srcset', 'sizes', 'type',\n 'media', 'controls', 'poster',\n ],\n ALLOW_DATA_ATTR: false,\n });\n }\n\n // Built-in fallback: strip all tags via DOM parsing, then re-allow safe subset\n this.log(\n 'DOMPurify not found — falling back to built-in sanitizer. ' +\n 'For full HTML placement support, add <script src=\"https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.4/purify.min.js\"></script>',\n false\n );\n const tempDiv = document.createElement('div');\n tempDiv.textContent = html;\n return tempDiv.innerHTML;\n }\n \n private log(message: string, isError = false): void {\n if (this.debugMode || isError) {\n console[isError ? 'error' : 'log'](`[AegisPlacements] ${message}`);\n }\n }\n \n destroy(): void {\n this.disconnectSSE();\n this.slots.clear();\n this.placements.clear();\n this.renderedSlots.clear();\n this.isInitialized = false;\n this.log('AegisPlacements destroyed');\n }\n}\n"],"names":["DEFAULT_CONFIG","workspace_id","batch_size","batch_interval","capture_utm","capture_referrer","auto_page_view","session_timeout","debug","respect_dnt","cross_domain_tracking","secure_cookie","enable_offline_mode","max_offline_events","retry_failed_requests","max_retries","retry_backoff_multiplier","request_timeout","rate_limit_burst","rate_limit_per_second","auto_region_detection","wait_for_consent","enable_consent_mode","integrate_onetrust","integrate_cookiebot","integrate_google_consent_mode","default_consent","analytics","marketing","functional","initialized","api_host","cell_endpoints","preferred_region","cookie_domain","plugins","active_cell","generateUUID","crypto","randomUUID","replace","c","r","Math","random","toString","generateMessageId","Date","now","slice","logger","constructor","this","enabled","prefix","enable","disable","isEnabled","message","args","info","console","warn","error","Identity","storage","userId","traits","anonymousId","getOrCreateAnonymousId","loadUserIdentity","id","get","set","storedUserId","storedTraits","JSON","parse","getAnonymousId","setUserId","setTraits","getUserId","stringify","getTraits","reset","remove","alias","newUserId","previousId","getState","SessionManager","sessionTimeoutMinutes","eventCount","checkInterval","activityThrottle","lastActivityUpdate","adClickIDs","sessionTimeout","lastActivityTime","sessionStartTime","session","loadSession","sessionId","startTime","landingPage","createNewSession","startActivityTracking","startExpiryCheck","sessionData","newSessionId","persistSession","activityHandler","onActivity","forEach","event","window","addEventListener","passive","document","visibilityState","setInterval","checkSessionExpiry","getSessionId","incrementEventCount","getSessionDuration","getEventCount","setAdClickIDs","Object","keys","length","getAdClickIDs","getLandingPage","destroy","clearInterval","EventQueue","config","transport","buffer","flushInterval","isFlushing","offlineQueue","isOnline","navigator","onLine","enableOfflineMode","loadOfflineQueue","setupOnlineListener","startFlushTimer","setupUnloadHandlers","offlineData","events","Array","isArray","maxOfflineEvents","flushOfflineQueue","saveOfflineQueue","data","flush","send","success","push","type","batchSize","addToOfflineQueue","shift","result","unshift","batchInterval","flushBeforeUnload","persistBufferToOfflineQueue","getQueueSize","getOfflineQueueSize","clear","Transport","activeCell","healthCheckInterval","regionLatencyMap","Map","cellEndpoints","initializeCellularArchitecture","cachedCell","loadCachedCell","isCellHealthy","selectOptimalCell","startHealthChecks","cached","saveCachedCell","cell","healthy","some","region","url","preferredRegion","preferredCell","find","autoRegionDetection","detectOptimalRegion","selectCellByPriority","healthyCells","filter","latencyPromises","map","async","latency","measureLatency","results","Promise","all","sort","a","b","optimalCell","performance","controller","AbortController","timeoutId","setTimeout","abort","response","fetch","method","signal","clearTimeout","ok","Infinity","sortedCells","priority","performHealthChecks","healthPromises","isHealthy","checkCellHealth","endpoint","getActiveEndpoint","payload","buildPayload","sendBeacon","sendViaBeacon","resolve","sendViaFetch","apiHost","Error","batch","sentAt","toISOString","writeKey","context","blob","Blob","attempt","requestTimeout","headers","Authorization","body","keepalive","json","errorText","text","status","statusText","shouldRetry","retryRequest","statusCode","retryFailedRequests","maxRetries","delay","pow","retryBackoffMultiplier","getActiveCell","getRegionLatencies","STORAGE_PREFIX","Storage","options","useLocalStorage","isLocalStorageAvailable","test","localStorage","setItem","removeItem","key","value","expiryDays","fullKey","item","expiry","setCookie","itemStr","getItem","getCookie","deleteCookie","startsWith","cookie","split","trim","name","days","date","setTime","getTime","cookieStr","toUTCString","secureCookie","location","protocol","cookieDomain","crossDomain","domain","getRootDomain","nameEQ","ca","i","charAt","substring","indexOf","hostname","parts","join","buildContext","utm","searchParams","URL","URLSearchParams","search","parseUTMParameters","device","ua","userAgent","detectDevice","os","version","match","detectOS","browser","detectBrowser","network","connection","mozConnection","webkitConnection","effectiveType","downlink","rtt","detectNetworkInfo","library","page","path","pathname","referrer","title","href","hash","locale","language","timezone","Intl","DateTimeFormat","resolvedOptions","timeZone","screen","width","height","density","devicePixelRatio","viewport","innerWidth","innerHeight","campaign","PluginRegistry","register","plugin","has","unregister","pluginName","delete","getAll","from","values","executeHook","hookName","hook","apply","String","onError","onErrorError","executeHookChain","initialValue","additionalArgs","executeHookParallel","promises","promise","ConsentManager","listeners","defaultConsent","waitForConsent","consentStorageKey","preferences","loadPreferences","stored","parsed","mergeWithDefaults","getDefaultPreferences","necessary","savePreferences","setConsent","updated","notifyListeners","grantAll","denyAll","hasConsent","category","getPreferences","getStatus","isAnalyticsEnabled","isMarketingEnabled","onChange","callback","index","splice","listener","integrateOneTrust","OneTrust","syncWithOneTrust","activeGroups","OnetrustActiveGroups","includes","OptanonWrapper","originalWrapper","integrateCookiebot","cookiebot","Cookiebot","syncWithCookiebot","consent","statistics","integrateGoogleConsentMode","gtag","updateGoogleConsent","ad_storage","analytics_storage","ad_user_data","ad_personalization","functionality_storage","personalization_storage","security_storage","FBP_COOKIE","FBC_COOKIE","readCookie","target","decodeURIComponent","writeCookie","host","isHttps","encodeURIComponent","subdomainIndex","max","ensureFbp","existing","fbp","floor","padStart","ensureFbc","fbclid","readFbclidFromUrl","fbc","sumItems","products","reduce","sum","p","quantity","mapProducts","product_id","sku","price","currency","brand","variant_id","variant_label","position","EcommerceTracker","aegis","productViewed","product","track","image_url","productListViewed","list","list_id","list_name","productClicked","source","section","productImpressed","categoryFiltered","previous_category","result_count","searchPerformed","query","results_count","filters","addToCart","removeFromCart","cartViewed","cart","cart_id","num_items","checkoutStarted","checkout","checkout_id","coupon","shipping","tax","checkoutStep","step","orderCompleted","order","order_id","revenue","discount","payment_method","orderRefunded","orderId","couponApplied","couponRemoved","wishlistItemAdded","wishlist","wishlist_id","wishlist_name","productWaitlisted","waitlist","channels","promotionViewed","promo","promotionClicked","RateLimiter","droppedThisWindow","firstDroppedName","windowFlushTimer","capacity","burstCapacity","refillPerSecond","windowMs","dropCoalesceWindowMs","onDropBatch","tokens","lastRefillMs","tryConsume","eventName","refill","flushWindow","getAvailableTokens","elapsedSec","newTokens","min","err","C1","C2","murmurhash3_x86_32","input","seed","bytes","len","nBlocks","h1","offset","k1","imul","tailStart","tailLen","murmurhash3_bytes","TextEncoder","encode","BloomFilter","buf","params","m","mask","fromBase64","bloomB64","b64","atob","bin","out","Uint8Array","charCodeAt","B","globalThis","Buffer","base64ToBytes","seedA","h2","seedB","k","idx","NameGovernor","bloom","remainingNewNames","graceActive","localNovelNames","Set","droppedSinceLastReport","reportWindowStart","hasWarnedThisSession","ingestHint","hint","bloom_algo","bloom_b64","seed_a","seed_b","remaining_new_names","grace_active","shouldSend","add","prev","drainDropReport","size","total","count","since","_debugState","hasBloom","remaining","localNovel","RESERVED_PREFIXES","CAMEL_BOUNDARY_1","CAMEL_BOUNDARY_2","NON_SNAKE","DUPE_UNDERSCORE","ISO_8601","DATE_KEY_HINTS","normalizeKey","toLowerCase","startsWithReserved","lower","looksLikeDateKey","part","parseDateValue","Number","isFinite","s","ms","isNaN","asNum","_TraitGovernor","warnCounts","process","sanitized","drops","rawKey","originalKey","verdict","reason","normalizedKey","drop","maybeWarn","ws","perVerdict","WARN_CAP","TraitGovernor","VALID_CHANNELS","EMAIL_RE","PHONE_RE","SHA256_HEX_RE","ISO_DATE_RE","UserNamespace","pendingTraits","login","merged","identify","logout","setAttribute","writeTraits","setAttributes","setEmail","email","setPhone","phone","setHashedEmail","sha256Hex","email_sha256","setHashedPhone","phone_sha256","setBirthDate","iso","birth_date","setOptIn","channel","granted","setSecureToken","token","_secure_token","_getPendingTraits","assign","Aegis","queue","initPromise","_ecommerce","_user","rateLimiter","nameGovernor","traitGovernor","_lastPageUrl","_popstateHandler","_originalPushState","_originalReplaceState","_lastEventIds","_workspaceCodes","_runtimeWorkspace","_runtimeLocation","init","_init","_a","shouldRespectDNT","pattern","isBot","buildConfig","identity","captureAdClickIDsOnInit","write_key","sampleName","emitRateLimitMeta","initializePlugins","startSPATracking","onRouteChange","currentUrl","history","pushState","bind","replaceState","stopSPATracking","removeEventListener","dnt","doNotTrack","msDoNotTrack","parseAdClickIDs","hasAdClickIDs","use","catch","removePlugin","ingestWorkspaceCodes","codes","setWorkspace","codeOrId","sessionStorage","clearWorkspace","ingestLocationCodes","setLocation","clearLocation","getPathLocationCode","getPathWorkspaceCode","getEffectiveLocationId","configured","location_id","loc","pathLoc","segments","Boolean","first","getEffectiveWorkspaceId","pathWs","properties","assertInitialized","messageId","timestamp","captureEvent","wsForGovernor","governedTraits","group","groupId","trackEvent","eventLabel","ctx","props","campaign_id","campaign_source","medium","campaign_medium","content","campaign_content","term","campaign_term","clicks","clickId","gclid","ctwa_clid","msclkid","ttclid","campaign_click_id","meta","transformedEvent","sampleEventName","dropped_count","sample_event_name","burst_capacity","refill_per_second","_b","ingestGovernanceHint","emitGovernanceDropMeta","report","distinct_names","sample_names","entries","window_start","window_end","lastEventId","setCell","cellEndpoint","existingCell","getCellInfo","latencies","grantConsent","denyConsent","getConsentPreferences","onConsentChange","ecommerce","user","renderCarouselCards","sanitizeUrl","sanitizeColor","log","addAnimationStyles","ic","interactive_config","cards","autoplay","autoplay_ms","loop","bg","background_color","fg","text_color","overlay","createElement","className","style","cssText","card","header","headerText","textContent","appendChild","closeBtn","msOverflowStyle","e","abs","deltaX","deltaY","scrollLeft","tile","cta_url","img","safe","src","alt","loading","t","cta_text","cta","goto","stopPropagation","dots","dotEls","dot","setActive","d","opacity","activeIdx","tiles","querySelectorAll","autoplayTimer","next","el","scrollIntoView","behavior","inline","block","approx","round","DISMISS_STORAGE_PREFIX","renderProgressBar","goalType","progress_goal_type","threshold","progress_threshold","rewardText","progress_reward_text","progress_source","fill","bar","label","leading","numeric","shell","fillEl","footnote","lastFiredUnlock","update","cur","__aegisProgressSSE","win","Shopify","v","parseFloat","total_price","aegis_cart","cart_items","cart_total","cacheStr","subtotalAmount","items","readCurrentValue","pct","toFixed","pollTimer","cleanup","once","RESUME_PREFIX","writeResumeIdx","resumeKey","renderCoachmarkTour","resume_key","steps","allowSkip","allow_skip","showDots","show_progress_dots","current","raw","n","parseInt","readResumeIdx","pointerEl","tipEl","highlightEl","cleanupOne","finish","opts","skipped","showStep","selector","anchor_web","anchor","querySelector","rect","getBoundingClientRect","top","left","placement","titleEl","bodyEl","controls","_","buttons","skip","isLast","tipRect","bottom","right","aegisResetTour","clearResume","_AegisInAppManager","campaigns","displayedCampaigns","suppressedUntil","isInitialized","reconnectAttempts","maxReconnectAttempts","hooks","ready","readyResolve","filledSlots","WeakSet","readAnonIdFromStorage","contactId","organizationId","propertyId","debugMode","enableSSE","onInteractiveCampaign","getWorkspaceId","emit","handlers","proceed","handler","emitError","on","onCampaignsLoaded","onCampaignWillShow","onCampaignShown","onCampaignClick","onCampaignDismiss","initialize","refreshCampaigns","connectSSE","updateUserId","updateContactId","disconnectSSE","notifyConversion","goalName","ts","CONVERSION_STORAGE_PREFIX","applySuppressionFromCampaigns","convertedAt","seconds","frequency","suppress_after_conversion_seconds","scope","expiresAt","rehydrateSuppressionFromStorage","earliestTs","isSuppressed","campaignId","eventSource","queryParams","append","EventSource","readyState","CLOSED","attemptReconnect","close","device_type","detectDeviceType","page_url","isNewUser","abAssignments","getABAssignments","btoa","credentials","processABAssignments","tryDisplayNextCampaign","renderIntoSlots","renderTokenAnchors","stage","assigned_variant_id","getVariantId","client_trigger","displayCampaign","slots","eligibleByCategory","modes","delivery_modes","widget_category","slot","getAttribute","renderCampaignIntoSlot","anchors","submitUrl","submit_url","rendered","sub_type","renderStarRatingSlot","renderNPSSurveySlot","_wrapInSlotCard","_buildStarRatingBody","_buildNPSSurveyBody","_submitTokenResponse","variant","isOverlay","padding","titleSize","titleWeight","titleMargin","starSize","starsMarginBottom","hoverScale","stars","maxStars","rating_scale","star","transform","nps_question","scale","btn","labels","notLikely","veryLikely","grid","onClientEvent","eventData","matchesClientTrigger","trigger","cfg","wantedRaw","wanted","actual","productId","interactiveSubTypes","renderInteractive","renderModal","renderBanner","renderFullScreen","renderHalfInterstitial","renderAlert","renderPIP","renderTooltip","buildRenderContext","sticky_position","dismissible","sticky_dismissible","autoHide","sticky_auto_hide_ms","sticky_bg_color","positionCss","strong","createTextNode","action_url","button_text","autoHideTimer","persist","renderStickyBar","layout","rec_layout","ctaDefault","rec_cta_text","accent","sheet","handle","isRow","metadata","go","renderProductRecommendation","evt","color","msg","level","renderNPSSurvey","renderCountdownOffer","renderStarRating","renderQuickPoll","renderQuiz","createOverlay","modal","addCloseButton","countdown_label","digits","digitStyle","val","targetStr","countdown_target","diff","h","spans","requestAnimationFrame","desc","createCTAButton","poll_options","optionsList","opt","optBtn","questions","currentQ","renderQuestion","childNodes","removeChild","lastChild","thank_you_message","q","progress","questionText","question","optionsDiv","safeUrl","open","container","removeModal","navigateToCampaignAction","buttonId","typedDefaultPrevented","typedEvent","button_id","preventDefault","defaultPrevented","typedProceed","domEvent","CustomEvent","detail","campaign_type","cancelable","domProceed","dispatchEvent","banner","contentContainer","textContainer","actionsContainer","ctaButton","removeBanner","closeButton","actions","alert","buttonContainer","cancelButton","pip","video_url","video","muted","animation","parentNode","cursor","anchorSelector","tooltip_anchor_selector","preferredPosition","tooltip_position","textColor","tooltip","arrow","dismiss","anchorRect","tooltipRect","scrollX","scrollY","arrowTop","arrowLeft","arrowBottom","arrowRight","outsideClickHandler","contains","getElementById","head","parsedUrl","origin","eventType","externalEventId","workspaceId","call","event_type","user_id","contact_id","anonymous_id","platform","property_id","idempotency_key","styles","clearABState","AegisInAppManager","AegisWidgetManager","widgets","renderedWidgets","isDestroyed","prefetchWidgetConfigs","triggerEngine","ownsTriggerEngine","enablePrefetch","cssCustomization","onEvent","sourcePlatform","updateCSSCustomization","customization","fetchPrefetchConfigs","preloadWidgetAssets","renderInteractiveCampaign","subType","widget","widget_id","widget_type","renderSpinWheel","renderScratchCard","stop","animationStyle","node","emitEvent","fetchWidgets","renderImmediateWidgets","setupTriggerListeners","setupExitIntentWithPrefetch","setCartData","cartData","cart_currency","detectPlatformCart","shopifyCart","normalizeShopifyCart","wooCart","normalizeWooCart","magentoCart","normalizeMagentoCart","line_items","product_name","product_title","cartJsonEl","mageCacheStr","mageCache","quote_id","subtotal","currencyCode","item_id","qty","product_price_value","exitIntentConfig","exit_intent","imageUrl","Image","spinWheelConfig","spin_wheel","elapsed","getDeviceType","w","trigger_rules","renderWidget","registerExitIntent","depthPercent","depth_percent","registerScrollDepth","registerTimeOnPage","isMobile","isMobileDevice","mobileConfig","mobile_triggers","handleSpinWheelIntent","widgetId","detectedCart","renderSpinWheelWidget","sendCartAbandonmentBeacon","scroll_velocity","registerScrollVelocity","scroll_threshold","minScrollPosition","scroll_min_position","cooldown","scroll_cooldown","idle_timer","idleSeconds","idle_seconds","registerInactivity","visibility_change","registerVisibilityChange","back_button","registerBackButton","handleExitIntent","renderCartRecoveryPopup","renderLeadGenPopup","beaconData","organization_id","user_email","abandoned_at","beaconUrl","renderChatBubble","renderToast","renderFeedbackForm","renderExitIntentPopup","icon_url","link_url","bubble","positionStyles","bottom_right","bottom_left","top_right","top_left","icon","textEl","description","SVG_NS","wheel","createElementNS","anglePer","PI","radius","start","end","x1","cos","y1","sin","x2","y2","largeArc","labelAngle","lx","ly","spinButton","disabled","prize","generatePrize","config_id","showPrizeResult","spinWheel","accentColor","accent_color","backgroundColor","buttonColor","button_color","wheelColors","innerHTML","onclick","interpolateCartVariables","subtitle","wheelContainer","form","phoneInput","placeholder","required","emailInput","nameInput","submitButton","errorMessage","validatePhone","validateEmail","display","submitSpinWheel","showSpinWheelPrize","prize_label","has_email","geoRegion","detectGeoRegion","deviceType","utmParams","getUTMParams","cart_token","_c","_d","_e","cart_url","geo_region","session_id","utm_source","utm_medium","utm_campaign","resultContainer","emoji","prizeLabel","coupon_code","couponContainer","couponLabel","couponCode","parentElement","canvas","getContext","fillStyle","fillRect","font","textAlign","fillText","isScratching","scratch","x","y","globalCompositeOperation","beginPath","arc","clientX","clientY","scratchButton","duration","toast","iconEl","messageEl","textareaLabel","textarea","rows","submitFeedback","score","comment","descEl","configId","event_data","titleText","messageText","show_cart_items","cartItems","cartTitle","itemDiv","discount_code","discountBox","discount_percentage","show_timer","timer_minutes","timer","checkoutUrl","tier","template","isError","TriggerEngine","isStarted","scrollDepthTargets","scrollDepthReached","timeOnPageTargets","exitIntentEnabled","exitIntentFired","inactivityTargets","scrollVelocityEnabled","scrollVelocityFired","scrollVelocityConfig","lastScrollY","lastScrollTime","visibilityChangeEnabled","backButtonEnabled","backButtonFired","rageClickEnabled","rageClickConfig","rageClickBuffer","rageClickFiredInBurst","hoverEnabled","hoverStartAt","hoverLastMs","hoverThresholds","hoverThresholdsFired","hoverThresholdTimers","mouseVelEnabled","mouseVelConfig","cooldownMs","mouseSamples","mouseVelLast","mouseVelLastFiredAt","handleScroll","scrollTop","pageYOffset","documentElement","scrollHeight","scrollPercent","targetDepth","actual_percent","scroll_top","scroll_height","client_y","time_on_page","pageLoadTime","handleScrollVelocity","currentY","currentTime","timeDiff","distance","velocity","scroll_distance","current_position","scrollVelocityCooldownTimer","handleVisibilityChange","hidden","handleBackButton","handleActivity","off","callbacks","timerId","idleTime","actual_idle_time","registerRageClick","noteClick","intent","cutoff","trimStart","recent","window_ms","getRageClickCount","live","registerHoverDwell","thresholdsByIntent","thresholds","noteHoverStart","noteHoverEnd","timers","firedSet","threshold_ms","actual_ms","startAt","dwell","pending","registerMouseVelocityToTop","noteMousePosition","oldest","newest","dt","cooldownOk","samples","getMouseVelocityToTop","getHoverMs","attachScrollListener","attachExitIntentListener","attachScrollVelocityListener","attachVisibilityChangeListener","attachBackButtonListener","attachActivityListeners","startInactivityCheck","removeScrollListener","removeExitIntentListener","removeScrollVelocityListener","removeVisibilityChangeListener","removeBackButtonListener","removeActivityListeners","inactivityCheckInterval","wildcardCallbacks","COOKIE_NAME","BootstrapError","super","globalName","existingQueue","instance","loadOptions","_loadOptions","owner","fn","AegisMessageRuntime","enableWidgetPrefetch","inApp","getCampaigns","AegisPlacementManager","placements","renderedSlots","refreshPlacements","placementId","existingContent","renderSlot","fallbackContent","renderFallback","placementIds","placement_id","renderAllSlots","content_type","renderDynamicInjection","containerId","renderCard","renderCarousel","renderVideo","renderHTML","onRender","cardBody","carousel","carouselItem","itemContent","item_index","poster_url","safeVideoUrl","safePosterUrl","poster","action","html","wrapper","sanitizeHTML","link","cssSelector","css_selector","injectionMode","injection_mode","targetElement","insertBefore","firstChild","variantId","purify","DOMPurify","sanitize","ALLOWED_TAGS","ALLOWED_ATTR","ALLOW_DATA_ATTR","tempDiv","aegisBootstrap","req","currentOrigin","firstPartyCookieId","attestationToken","doFetch","resp","cookieId","expires","setDate","getDate","writeFirstPartyCookie"],"mappings":"kCA+GO,MAAMA,EAA+C,CAC1DC,aAAc,KACdC,WAAY,GACZC,eAAgB,IAChBC,aAAa,EACbC,kBAAkB,EAClBC,gBAAgB,EAChBC,gBAAiB,GACjBC,OAAO,EACPC,aAAa,EACbC,uBAAuB,EACvBC,eAAe,EACfC,qBAAqB,EACrBC,mBAAoB,IACpBC,uBAAuB,EACvBC,YAAa,EACbC,yBAA0B,EAC1BC,gBAAiB,IACjBC,iBAAkB,IAClBC,sBAAuB,GACvBC,uBAAuB,EACvBC,kBAAkB,EAClBC,qBAAqB,EACrBC,oBAAoB,EACpBC,qBAAqB,EACrBC,+BAA+B,EAC/BC,gBAAiB,CACfC,WAAW,EACXC,WAAW,EACXC,YAAY,GAEdC,aAAa,EACbC,SAAU,KACVC,eAAgB,GAChBC,iBAAkB,KAClBC,cAAe,KACfC,QAAS,GACTC,YAAa,MCpJR,SAASC,IACd,MAAsB,oBAAXC,QAA0BA,OAAOC,WACnCD,OAAOC,aAGT,uCAAuCC,QAAQ,QAAUC,IAC9D,MAAMC,EAAqB,GAAhBC,KAAKC,SAAiB,EAEjC,OADgB,MAANH,EAAYC,EAAS,EAAJA,EAAW,GAC7BG,SAAS,KAEtB,CAMO,SAASC,IACd,MAAO,OAAOC,KAAKC,SAASX,IAAeY,MAAM,EAAG,IACtD,CCmBO,MAAMC,EAAS,IAnCtB,MAAA,WAAAC,GACEC,KAAQC,SAAmB,EAC3BD,KAAQE,OAAiB,oBAAA,CAEzB,MAAAC,GACEH,KAAKC,SAAU,CACjB,CAEA,OAAAG,GACEJ,KAAKC,SAAU,CACjB,CAEA,SAAAI,GACE,OAAOL,KAAKC,OACd,CAEA,KAAA7C,CAAMkD,KAAoBC,GACnBP,KAAKC,SACOD,KAAKE,MACxB,CAEA,IAAAM,CAAKF,KAAoBC,GAClBP,KAAKC,SACVQ,QAAQD,KAAK,GAAGR,KAAKE,UAAUI,OAAcC,EAC/C,CAEA,IAAAG,CAAKJ,KAAoBC,GACvBE,QAAQC,KAAK,GAAGV,KAAKE,UAAUI,OAAcC,EAC/C,CAEA,KAAAI,CAAML,KAAoBC,GACxBE,QAAQE,MAAM,GAAGX,KAAKE,UAAUI,OAAcC,EAChD,GCxBK,MAAMK,EAMX,WAAAb,CAAYc,GAHZb,KAAQc,OAAwB,KAChCd,KAAQe,OAA8B,CAAA,EAGpCf,KAAKa,QAAUA,EACfb,KAAKgB,YAAchB,KAAKiB,yBACxBjB,KAAKkB,kBACP,CAEQ,sBAAAD,GACN,IAAIE,EAAKnB,KAAKa,QAAQO,IAAI,WAU1B,OARKD,EAKHrB,EAAO1C,MAAM,gCAAiC+D,IAJ9CA,EAAKlC,IACLe,KAAKa,QAAQQ,IAAI,UAAWF,EAAI,KAChCrB,EAAO1C,MAAM,4BAA6B+D,IAKrCA,CACT,CAEQ,gBAAAD,GACN,MAAMI,EAAetB,KAAKa,QAAQO,IAAI,WAChCG,EAAevB,KAAKa,QAAQO,IAAI,eAOtC,GALIE,IACFtB,KAAKc,OAASQ,EACdxB,EAAO1C,MAAM,kBAAmBkE,IAG9BC,EACF,IACEvB,KAAKe,OAASS,KAAKC,MAAMF,GACzBzB,EAAO1C,MAAM,sBAAuB4C,KAAKe,OAC3C,OAASJ,GACPb,EAAOY,KAAK,iCAAkCC,GAC9CX,KAAKe,OAAS,CAAA,CAChB,CAEJ,CAEA,cAAAW,GACE,OAAO1B,KAAKgB,WACd,CAEA,SAAAW,CAAUb,EAAgBC,GACxBf,KAAKc,OAASA,EACdd,KAAKa,QAAQQ,IAAI,UAAWP,EAAQ,KACpChB,EAAOU,KAAK,mBAAoBM,GAE5BC,GACFf,KAAK4B,UAAUb,EAEnB,CAEA,SAAAc,GACE,OAAO7B,KAAKc,MACd,CAEA,SAAAc,CAAUb,GACRf,KAAKe,OAAS,IAAKf,KAAKe,UAAWA,GACnCf,KAAKa,QAAQQ,IAAI,cAAeG,KAAKM,UAAU9B,KAAKe,QAAS,KAC7DjB,EAAO1C,MAAM,uBAAwB4C,KAAKe,OAC5C,CAEA,SAAAgB,GACE,MAAO,IAAK/B,KAAKe,OACnB,CAEA,KAAAiB,GACEhC,KAAKc,OAAS,KACdd,KAAKe,OAAS,CAAA,EACdf,KAAKgB,YAAc/B,IAEnBe,KAAKa,QAAQoB,OAAO,WACpBjC,KAAKa,QAAQoB,OAAO,eACpBjC,KAAKa,QAAQQ,IAAI,UAAWrB,KAAKgB,YAAa,KAE9ClB,EAAOU,KAAK,oCAAqCR,KAAKgB,YACxD,CAEA,KAAAkB,CAAMC,GACJ,MAAMC,EAAapC,KAAKc,QAAUd,KAAKgB,YAMvC,OAJAhB,KAAK2B,UAAUQ,GAEfrC,EAAOU,KAAK,gBAAiB,CAAE4B,aAAYD,cAEpC,CAAEC,aAAYD,YACvB,CAEA,QAAAE,GACE,MAAO,CACLrB,YAAahB,KAAKgB,YAClBF,OAAQd,KAAKc,OACbC,OAAQf,KAAK+B,YAEjB,EClGK,MAAMO,EAaX,WAAAvC,CAAYc,EAAkB0B,EAAgC,IAP9DvC,KAAQwC,WAAqB,EAC7BxC,KAAQyC,cAA+B,KACvCzC,KAAQ0C,iBAA2B,IACnC1C,KAAQ2C,mBAA6B,EACrC3C,KAAQ4C,WAAyB,CAAA,EAI/B5C,KAAKa,QAAUA,EACfb,KAAK6C,eAAyC,GAAxBN,EAA6B,IACnDvC,KAAK8C,iBAAmBnD,KAAKC,MAC7BI,KAAK+C,iBAAmBpD,KAAKC,MAE7B,MAAMoD,EAAUhD,KAAKiD,cACjBD,GACFhD,KAAKkD,UAAYF,EAAQE,UACzBlD,KAAK+C,iBAAmBC,EAAQG,UAChCnD,KAAK8C,iBAAmBE,EAAQF,iBAChC9C,KAAKwC,WAAaQ,EAAQR,WAC1BxC,KAAK4C,WAAaI,EAAQJ,YAAc,CAAA,EACxC5C,KAAKoD,YAAcJ,EAAQI,YAC3BtD,EAAO1C,MAAM,kBAAmB4F,IAEhChD,KAAKkD,UAAYlD,KAAKqD,mBAGxBrD,KAAKsD,wBACLtD,KAAKuD,kBACP,CAEQ,WAAAN,GACN,MAAMO,EAAcxD,KAAKa,QAAQO,IAAI,WAErC,IAAKoC,EACH,OAAO,KAGT,IACE,MAAMR,EAAwBxB,KAAKC,MAAM+B,GAGzC,OAFgB7D,KAAKC,MAAQoD,EAAQF,iBAEvB9C,KAAK6C,eACVG,GAEPlD,EAAO1C,MAAM,yCACb4C,KAAKa,QAAQoB,OAAO,WACb,KAEX,OAAStB,GAGP,OAFAb,EAAOY,KAAK,gCAAiCC,GAC7CX,KAAKa,QAAQoB,OAAO,WACb,IACT,CACF,CAEQ,gBAAAoB,GACN,MAAMI,EH/DD,QAAQ9D,KAAKC,SAASX,IAAeY,MAAM,EAAG,KG+EnD,OAfAG,KAAK+C,iBAAmBpD,KAAKC,MAC7BI,KAAK8C,iBAAmBnD,KAAKC,MAC7BI,KAAKwC,WAAa,EAQlBxC,KAAKkD,UAAYO,EAEjBzD,KAAK0D,iBACL5D,EAAOU,KAAK,uBAAwBiD,GAE7BA,CACT,CAEQ,cAAAC,GACN,MAAMF,EAA4B,CAChCN,UAAWlD,KAAKkD,UAChBC,UAAWnD,KAAK+C,iBAChBD,iBAAkB9C,KAAK8C,iBACvBN,WAAYxC,KAAKwC,WACjBI,WAAY5C,KAAK4C,WACjBQ,YAAapD,KAAKoD,aAGpBpD,KAAKa,QAAQQ,IAAI,UAAWG,KAAKM,UAAU0B,GAC7C,CAEQ,qBAAAF,GACN,MAEMK,EAAkB,IAAM3D,KAAK4D,aAFpB,CAAC,QAAS,SAAU,WAAY,YAAa,cAIrDC,QAASC,IACdC,OAAOC,iBAAiBF,EAAOH,EAAiB,CAAEM,SAAS,MAG7DF,OAAOC,iBAAiB,mBAAoB,KACT,YAA7BE,SAASC,iBACXnE,KAAK4D,cAGX,CAEQ,gBAAAL,GACNvD,KAAKyC,cAAgBsB,OAAOK,YAAY,KACtCpE,KAAKqE,sBACJ,IACL,CAEQ,UAAAT,GACN,MAAMhE,EAAMD,KAAKC,MAGjB,GAF4BA,EAAMI,KAAK2C,mBAEb3C,KAAK0C,iBAC7B,OAGF1C,KAAK2C,mBAAqB/C,EACIA,EAAMI,KAAK8C,iBAEb9C,KAAK6C,gBAC/B/C,EAAOU,KAAK,2DACZR,KAAKkD,UAAYlD,KAAKqD,qBAEtBrD,KAAK8C,iBAAmBlD,EACxBI,KAAK0D,iBAET,CAEQ,kBAAAW,GACU1E,KAAKC,MAAQI,KAAK8C,iBAEpB9C,KAAK6C,iBACjB/C,EAAOU,KAAK,sDACZR,KAAKkD,UAAYlD,KAAKqD,mBAE1B,CAEA,YAAAiB,GACE,OAAOtE,KAAKkD,SACd,CAEA,mBAAAqB,GACEvE,KAAKwC,aACLxC,KAAK8C,iBAAmBnD,KAAKC,MAC7BI,KAAK0D,gBACP,CAEA,kBAAAc,GACE,OAAO7E,KAAKC,MAAQI,KAAK+C,gBAC3B,CAEA,aAAA0B,GACE,OAAOzE,KAAKwC,UACd,CAEA,QAAAH,GACE,MAAO,CACLa,UAAWlD,KAAKkD,UAChBC,UAAWnD,KAAK+C,iBAChBD,iBAAkB9C,KAAK8C,iBACvBN,WAAYxC,KAAKwC,WACjBI,WAAY5C,KAAK4C,WACjBQ,YAAapD,KAAKoD,YAEtB,CAEA,aAAAsB,CAAc9B,EAAwBQ,GAChCuB,OAAOC,KAAKhC,GAAYiC,OAAS,IACnC7E,KAAK4C,WAAa,IAAK5C,KAAK4C,cAAeA,GACvCQ,IACFpD,KAAKoD,YAAcA,GAErBpD,KAAK0D,iBACL5D,EAAOU,KAAK,kCAAmCR,KAAK4C,YAExD,CAEA,aAAAkC,GACE,OAAO9E,KAAK4C,UACd,CAEA,cAAAmC,GACE,OAAO/E,KAAKoD,WACd,CAEA,OAAA4B,GACMhF,KAAKyC,gBACPwC,cAAcjF,KAAKyC,eACnBzC,KAAKyC,cAAgB,KAEzB,ECxMK,MAAMyC,EAUX,WAAAnF,CAAYoF,EAAqBC,EAAsBvE,GATvDb,KAAQqF,OAAuB,GAI/BrF,KAAQsF,cAA+B,KACvCtF,KAAQuF,YAAsB,EAC9BvF,KAAQwF,aAA6B,GACrCxF,KAAQyF,SAAoBC,UAAUC,OAGpC3F,KAAKmF,OAASA,EACdnF,KAAKoF,UAAYA,EACjBpF,KAAKa,QAAUA,EAEXsE,EAAOS,oBACT5F,KAAK6F,mBACL7F,KAAK8F,uBAGP9F,KAAK+F,kBACL/F,KAAKgG,qBACP,CAEQ,gBAAAH,GACN,MAAMI,EAAcjG,KAAKa,QAAQO,IAAI,iBAErC,GAAK6E,EAIL,IACE,MAAMC,EAAS1E,KAAKC,MAAMwE,GAEtBE,MAAMC,QAAQF,IAAWA,EAAOrB,OAAS,IAC3C7E,KAAKwF,aAAeU,EAAOrG,MAAM,EAAGG,KAAKmF,OAAOkB,kBAChDvG,EAAOU,KAAK,UAAUR,KAAKwF,aAAaX,yBAEpC7E,KAAKyF,UACPzF,KAAKsG,oBAGX,OAAS3F,GACPb,EAAOY,KAAK,gCAAiCC,GAC7CX,KAAKa,QAAQoB,OAAO,gBACtB,CACF,CAEQ,gBAAAsE,GACN,GAAiC,IAA7BvG,KAAKwF,aAAaX,OAKtB,IACE,MAAM2B,EAAOhF,KAAKM,UAAU9B,KAAKwF,cACjCxF,KAAKa,QAAQQ,IAAI,gBAAiBmF,GAClC1G,EAAO1C,MAAM,SAAS4C,KAAKwF,aAAaX,iCAC1C,OAASlE,GACPb,EAAOY,KAAK,gCAAiCC,EAC/C,MAVEX,KAAKa,QAAQoB,OAAO,gBAWxB,CAEQ,mBAAA6D,GACN/B,OAAOC,iBAAiB,SAAU,KAChClE,EAAOU,KAAK,uBACZR,KAAKyF,UAAW,EAChBzF,KAAKsG,oBACLtG,KAAKyG,UAGP1C,OAAOC,iBAAiB,UAAW,KACjClE,EAAOY,KAAK,0CACZV,KAAKyF,UAAW,GAEpB,CAEA,uBAAca,GACZ,GAAiC,IAA7BtG,KAAKwF,aAAaX,OACpB,OAGF/E,EAAOU,KAAK,YAAYR,KAAKwF,aAAaX,yBAE1C,MAAMqB,EAAS,IAAIlG,KAAKwF,cACxBxF,KAAKwF,aAAe,GACpBxF,KAAKa,QAAQoB,OAAO,wBAECjC,KAAKoF,UAAUsB,KAAKR,IAE7BS,QAQV7G,EAAOU,KAAK,qCAPZV,EAAOY,KAAK,8CACZV,KAAKwF,aAAe,IAAIU,KAAWlG,KAAKwF,cAAc3F,MACpD,EACAG,KAAKmF,OAAOkB,kBAEdrG,KAAKuG,mBAIT,CAEA,IAAAK,CAAK9C,GACE9D,KAAKyF,WAAYzF,KAAKmF,OAAOS,mBAKlC5F,KAAKqF,OAAOuB,KAAK9C,GACjBhE,EAAO1C,MAAM,gBAAiB0G,EAAM+C,KAAM/C,GAEtC9D,KAAKqF,OAAOR,QAAU7E,KAAKmF,OAAO2B,WACpC9G,KAAKyG,SARLzG,KAAK+G,kBAAkBjD,EAU3B,CAEQ,iBAAAiD,CAAkBjD,GACpB9D,KAAKwF,aAAaX,QAAU7E,KAAKmF,OAAOkB,mBAC1CvG,EAAOY,KAAK,6CACZV,KAAKwF,aAAawB,SAGpBhH,KAAKwF,aAAaoB,KAAK9C,GACvB9D,KAAKuG,mBACLzG,EAAO1C,MAAM,+BACf,CAEA,WAAMqJ,GACJ,GAAIzG,KAAKuF,WAEP,YADAzF,EAAO1C,MAAM,uCAIf,GAA2B,IAAvB4C,KAAKqF,OAAOR,OACd,OAGF7E,KAAKuF,YAAa,EAElB,MAAMW,EAAS,IAAIlG,KAAKqF,QACxBrF,KAAKqF,OAAS,GAEdvF,EAAOU,KAAK,YAAY0F,EAAOrB,iBAE/B,IACE,MAAMoC,QAAejH,KAAKoF,UAAUsB,KAAKR,GAEpCe,EAAON,QAeV7G,EAAOU,KAAK,6BAdZV,EAAOa,MAAM,yBAA0BsG,EAAOtG,OAE1CX,KAAKmF,OAAOS,mBACd5F,KAAKwF,aAAe,IAAIxF,KAAKwF,gBAAiBU,GAAQrG,MACpD,EACAG,KAAKmF,OAAOkB,kBAEdrG,KAAKuG,mBACLzG,EAAOU,KAAK,mCAEZR,KAAKqF,OAAO6B,WAAWhB,GACvBpG,EAAOY,KAAK,+BAKlB,OAASC,GACPb,EAAOa,MAAM,eAAgBA,GAEzBX,KAAKmF,OAAOS,oBACd5F,KAAKwF,aAAe,IAAIxF,KAAKwF,gBAAiBU,GAAQrG,MACpD,EACAG,KAAKmF,OAAOkB,kBAEdrG,KAAKuG,mBAET,CAAA,QACEvG,KAAKuF,YAAa,CACpB,CACF,CAEQ,eAAAQ,GACF/F,KAAKsF,eACPL,cAAcjF,KAAKsF,eAGrBtF,KAAKsF,cAAgBvB,OAAOK,YAAY,KACtCpE,KAAKyG,SACJzG,KAAKmF,OAAOgC,eAEfrH,EAAO1C,MACL,sCAAsC4C,KAAKmF,OAAOgC,kBAEtD,CAEQ,mBAAAnB,GACN,MAAMoB,EAAoB,MACpBpH,KAAKqF,OAAOR,OAAS,GAAK7E,KAAKwF,aAAaX,OAAS,KACvD7E,KAAKqH,8BACLrH,KAAKyG,UAITvC,SAASF,iBAAiB,mBAAoB,KACX,WAA7BE,SAASC,iBACXiD,MAIJrD,OAAOC,iBAAiB,eAAgBoD,GAExCrD,OAAOC,iBAAiB,WAAYoD,EACtC,CAEQ,2BAAAC,GACqB,IAAvBrH,KAAKqF,OAAOR,QAIZ7E,KAAKmF,OAAOS,oBACd5F,KAAKwF,aAAe,IAAIxF,KAAKwF,gBAAiBxF,KAAKqF,QAAQxF,MACzD,EACAG,KAAKmF,OAAOkB,kBAEdrG,KAAKuG,mBACLzG,EAAO1C,MAAM,mDAEjB,CAEA,YAAAkK,GACE,OAAOtH,KAAKqF,OAAOR,MACrB,CAEA,mBAAA0C,GACE,OAAOvH,KAAKwF,aAAaX,MAC3B,CAEA,KAAA2C,GACExH,KAAKqF,OAAS,GACdrF,KAAKwF,aAAe,GACpBxF,KAAKa,QAAQoB,OAAO,iBACpBnC,EAAOU,KAAK,gBACd,CAEA,OAAAwE,GACMhF,KAAKsF,gBACPL,cAAcjF,KAAKsF,eACnBtF,KAAKsF,cAAgB,MAGvBtF,KAAKyG,OACP,EC7OK,MAAMgB,EAOX,WAAA1H,CAAYoF,EAAyBtE,GAJrCb,KAAQ0H,WAAkC,KAC1C1H,KAAQ2H,oBAAqC,KAC7C3H,KAAQ4H,qBAA4CC,IAGlD7H,KAAKmF,OAASA,EACdnF,KAAKa,QAAUA,EAEXsE,EAAO2C,cAAcjD,OAAS,GAChC7E,KAAK+H,gCAET,CAEA,oCAAcA,GACZ,MAAMC,EAAahI,KAAKiI,iBAEpBD,GAAchI,KAAKkI,cAAcF,IACnChI,KAAK0H,WAAaM,EAClBlI,EAAOU,KAAK,qBAAsBwH,UAE5BhI,KAAKmI,oBAGbnI,KAAKoI,mBACP,CAEQ,cAAAH,GACN,MAAMI,EAASrI,KAAKa,QAAQO,IAAI,eAEhC,IAAKiH,EACH,OAAO,KAGT,IACE,OAAO7G,KAAKC,MAAM4G,EACpB,OAAS1H,GAEP,OADAb,EAAOY,KAAK,+BAAgCC,GACrC,IACT,CACF,CAEQ,cAAA2H,CAAeC,GACrBvI,KAAKa,QAAQQ,IAAI,cAAeG,KAAKM,UAAUyG,GAAO,EACxD,CAEQ,aAAAL,CAAcK,GACpB,OACEA,EAAKC,SACLxI,KAAKmF,OAAO2C,cAAcW,KACvBpJ,GAAMA,EAAEqJ,SAAWH,EAAKG,QAAUrJ,EAAEsJ,MAAQJ,EAAKI,IAGxD,CAEA,uBAAcR,GAGZ,GAFArI,EAAOU,KAAK,6BAERR,KAAKmF,OAAOyD,gBAAiB,CAC/B,MAAMC,EAAgB7I,KAAKmF,OAAO2C,cAAcgB,KAC7CP,GAASA,EAAKG,SAAW1I,KAAKmF,OAAOyD,iBAAmBL,EAAKC,SAGhE,GAAIK,EAIF,OAHA7I,KAAK0H,WAAamB,EAClB7I,KAAKsI,eAAeO,QACpB/I,EAAOU,KAAK,+BAAgCqI,EAGhD,CAEI7I,KAAKmF,OAAO4D,0BACR/I,KAAKgJ,sBAEXhJ,KAAKiJ,sBAET,CAEA,yBAAcD,GACZ,MAAME,EAAelJ,KAAKmF,OAAO2C,cAAcqB,OAAQZ,GAASA,EAAKC,SAErE,GAA4B,IAAxBU,EAAarE,OAEf,YADA/E,EAAOa,MAAM,8BAIf,MAAMyI,EAAkBF,EAAaG,IAAIC,MAAOf,IAC9C,MAAMgB,QAAgBvJ,KAAKwJ,eAAejB,GAE1C,OADAvI,KAAK4H,iBAAiBvG,IAAIkH,EAAKG,OAAQa,GAChC,CAAEhB,OAAMgB,aAGXE,QAAgBC,QAAQC,IAAIP,GAClCK,EAAQG,KAAK,CAACC,EAAGC,IAAMD,EAAEN,QAAUO,EAAEP,SAErC,MAAMQ,EAAcN,EAAQ,GAAGlB,KAC/BvI,KAAK0H,WAAaqC,EAClB/J,KAAKsI,eAAeyB,GAEpBjK,EAAOU,KAAK,yBAA0B,CACpCkI,OAAQqB,EAAYrB,OACpBa,QAASE,EAAQ,GAAGF,SAExB,CAEA,oBAAcC,CAAejB,GAC3B,MAAMpF,EAAY6G,YAAYpK,MAE9B,IACE,MAAMqK,EAAa,IAAIC,gBACjBC,EAAYC,WAAW,IAAMH,EAAWI,QAAS,KAEjDC,QAAiBC,MAAM,GAAGhC,EAAKI,aAAc,CACjD6B,OAAQ,MACRC,OAAQR,EAAWQ,SAKrB,OAFAC,aAAaP,GAETG,EAASK,GACJX,YAAYpK,MAAQuD,EAEpByH,GAEX,OAASjK,GAEP,OADAb,EAAOY,KAAK,2BAA2B6H,EAAKG,UAAW/H,GAChDiK,GACT,CACF,CAEQ,oBAAA3B,GACN,MAAM4B,EAAc,IAAI7K,KAAKmF,OAAO2C,eACjCqB,OAAQZ,GAASA,EAAKC,SACtBoB,KAAK,CAACC,EAAGC,IAAMD,EAAEiB,SAAWhB,EAAEgB,UAE7BD,EAAYhG,OAAS,GACvB7E,KAAK0H,WAAamD,EAAY,GAC9B7K,KAAKsI,eAAeuC,EAAY,IAChC/K,EAAOU,KAAK,6BAA8BqK,EAAY,KAEtD/K,EAAOa,MAAM,6BAEjB,CAEQ,iBAAAyH,GACNpI,KAAK2H,oBAAsB5D,OAAOK,YAAY,KAC5CpE,KAAK+K,uBACJ,IACL,CAEA,yBAAcA,GACZ,MAAMC,EAAiBhL,KAAKmF,OAAO2C,cAAcuB,IAAIC,MAAOf,IAC1D,MAAM0C,QAAkBjL,KAAKkL,gBAAgB3C,GAE7C,OADAA,EAAKC,QAAUyC,EACR,CAAE1C,OAAM0C,eAGXxB,QAAgBC,QAAQC,IAAIqB,GAE9BhL,KAAK0H,aAAe1H,KAAK0H,WAAWc,UACtC1I,EAAOY,KAAK,mDACNV,KAAKmI,qBAGbrI,EAAO1C,MAAM,wBAAyBqM,EACxC,CAEA,qBAAcyB,CAAgB3C,GAC5B,IACE,MAAM0B,EAAa,IAAIC,gBACjBC,EAAYC,WAAW,IAAMH,EAAWI,QAAS,KAEjDC,QAAiBC,MAAM,GAAGhC,EAAKI,aAAc,CACjD6B,OAAQ,MACRC,OAAQR,EAAWQ,SAIrB,OADAC,aAAaP,GACNG,EAASK,EAClB,OAAShK,GACP,OAAO,CACT,CACF,CAEA,UAAM+F,CAAKR,GACT,GAAsB,IAAlBA,EAAOrB,OACT,MAAO,CAAE8B,SAAS,GAGpB,MAAMwE,EAAWnL,KAAKoL,oBAChBC,EAAUrL,KAAKsL,aAAapF,GAIlC,GAFApG,EAAO1C,MAAM,iBAAkB,CAAE+N,WAAU3I,WAAY0D,EAAOrB,SAE7B,WAA7BX,SAASC,iBAAgE,mBAAzBuB,UAAU6F,WAA2B,CACvF,MAAMtE,EAASjH,KAAKwL,cAAcL,EAAUE,GAC5C,OAAO3B,QAAQ+B,QAAQxE,EACzB,CACE,OAAOjH,KAAK0L,aAAaP,EAAUE,EAEvC,CAEQ,iBAAAD,GACN,GAAIpL,KAAK0H,WACP,MAAO,GAAG1H,KAAK0H,WAAWiB,eAG5B,GAAI3I,KAAKmF,OAAOwG,QACd,MAAO,GAAG3L,KAAKmF,OAAOwG,mBAGxB,MAAM,IAAIC,MAAM,+BAClB,CAEQ,YAAAN,CAAapF,GACnB,MAAO,CACL2F,MAAO3F,EACP4F,QAAA,IAAYnM,MAAOoM,cACnBC,SAAUhM,KAAKmF,OAAO6G,SACtBC,QAASjM,KAAK0H,WACV,CACEa,KAAM,CACJG,OAAQ1I,KAAK0H,WAAWgB,OACxByC,SAAUnL,KAAK0H,WAAWiB,WAG9B,EAER,CAEQ,aAAA6C,CAAcL,EAAkBE,GACtC,IACE,MAAMa,EAAO,IAAIC,KAAK,CAAC3K,KAAKM,UAAUuJ,IAAW,CAC/CxE,KAAM,qBAKR,OAFanB,UAAU6F,WAAWJ,EAAUe,IAG1CpM,EAAO1C,MAAM,yBACN,CAAEuJ,SAAS,EAAMwE,cAExBrL,EAAOY,KAAK,6CACLV,KAAK0L,aAAaP,EAAUE,GAEvC,OAAS1K,GAEP,OADAb,EAAOa,MAAM,qBAAsBA,GAC5B,CACLgG,SAAS,EACThG,QACAwK,WAEJ,CACF,CAEA,kBAAcO,CACZP,EACAE,EACAe,EAAkB,GAElB,IACE,MAAMnC,EAAa,IAAIC,gBACjBC,EAAYC,WAChB,IAAMH,EAAWI,QACjBrK,KAAKmF,OAAOkH,gBAGR/B,QAAiBC,MAAMY,EAAU,CACrCX,OAAQ,OACR8B,QAAS,CACP,eAAgB,mBAChBC,cAAe,UAAUvM,KAAKmF,OAAO6G,YAEvCQ,KAAMhL,KAAKM,UAAUuJ,GACrBoB,WAAW,EACXhC,OAAQR,EAAWQ,SAKrB,GAFAC,aAAaP,GAETG,EAASK,GAAI,CACf,MAAM1D,QAA8BqD,EAASoC,OAE7C,OADA5M,EAAO1C,MAAM,2BAA4B6J,GAClC,CAAEN,SAAS,EAAM2D,SAAUrD,EAAQkE,WAC5C,CAAO,CACL,MAAMwB,QAAkBrC,EAASsC,OAC3BjM,EAAQ,IAAIiL,MAChB,QAAQtB,EAASuC,WAAWF,GAAarC,EAASwC,cAKpD,OAFAhN,EAAOa,MAAM,qBAAsBA,GAE/BX,KAAK+M,YAAYzC,EAASuC,OAAQT,GAC7BpM,KAAKgN,aAAa7B,EAAUE,EAASe,GAGvC,CAAEzF,SAAS,EAAOhG,QAAOwK,WAClC,CACF,OAASxK,GAGP,OAFAb,EAAOa,MAAM,eAAgBA,GAEzBX,KAAK+M,YAAY,EAAGX,GACfpM,KAAKgN,aAAa7B,EAAUE,EAASe,GAGvC,CACLzF,SAAS,EACThG,QACAwK,WAEJ,CACF,CAEQ,WAAA4B,CAAYE,EAAoBb,GACtC,QAAKpM,KAAKmF,OAAO+H,wBAIbd,GAAWpM,KAAKmF,OAAOgI,cAIR,IAAfF,IAIGA,GAAc,KAAsB,MAAfA,IAC9B,CAEA,kBAAcD,CACZ7B,EACAE,EACAe,GAEA,MAAMgB,EACoD,IAAxD7N,KAAK8N,IAAIrN,KAAKmF,OAAOmI,uBAAwBlB,GAM/C,OAJAtM,EAAOU,KAAK,uBAAuB4M,gBAAoBhB,EAAU,YAE3D,IAAI1C,QAAS+B,GAAYrB,WAAWqB,EAAS2B,IAE5CpN,KAAK0L,aAAaP,EAAUE,EAASe,EAAU,EACxD,CAEA,aAAAmB,GACE,OAAOvN,KAAK0H,UACd,CAEA,kBAAA8F,GACE,OAAO,IAAI3F,IAAI7H,KAAK4H,iBACtB,CAEA,OAAA5C,GACMhF,KAAK2H,sBACP1C,cAAcjF,KAAK2H,qBACnB3H,KAAK2H,oBAAsB,KAE/B,EC/XF,MAAM8F,EAAiB,SAQhB,MAAMC,EAIX,WAAA3N,CAAY4N,EAA0B,IACpC3N,KAAK2N,QAAUA,EACf3N,KAAK4N,gBAAkB5N,KAAK6N,yBAC9B,CAEQ,uBAAAA,GACN,IACE,MAAMC,EAAO,iBAGb,OAFAC,aAAaC,QAAQF,EAAMA,GAC3BC,aAAaE,WAAWH,IACjB,CACT,CAAA,MACE,OAAO,CACT,CACF,CAEA,GAAAzM,CAAI6M,EAAaC,EAAeC,EAAqB,KACnD,MAAMC,EAAUZ,EAAiBS,EAEjC,GAAIlO,KAAK4N,gBACP,IACE,MAAMU,EAAO,CACXH,QACAI,OAAQ5O,KAAKC,MAAqB,GAAbwO,EAAkB,GAAK,GAAK,KAEnDL,aAAaC,QAAQK,EAAS7M,KAAKM,UAAUwM,GAC/C,OAAS3N,GACPF,QAAQC,KAAK,sDAAuDC,EACtE,CAGFX,KAAKwO,UAAUH,EAASF,EAAOC,EACjC,CAEA,GAAAhN,CAAI8M,GACF,MAAMG,EAAUZ,EAAiBS,EAEjC,GAAIlO,KAAK4N,gBACP,IACE,MAAMa,EAAUV,aAAaW,QAAQL,GACrC,GAAII,EAAS,CACX,MAAMH,EAAO9M,KAAKC,MAAMgN,GACxB,GAAI9O,KAAKC,MAAQ0O,EAAKC,OACpB,OAAOD,EAAKH,MAEZJ,aAAaE,WAAWI,EAE5B,CACF,OAAS1N,GACPF,QAAQC,KAAK,sDAAuDC,EACtE,CAGF,OAAOX,KAAK2O,UAAUN,EACxB,CAEA,MAAApM,CAAOiM,GACL,MAAMG,EAAUZ,EAAiBS,EAEjC,GAAIlO,KAAK4N,gBACP,IACEG,aAAaE,WAAWI,EAC1B,OAAS1N,GACPF,QAAQC,KAAK,yDAA0DC,EACzE,CAGFX,KAAK4O,aAAaP,EACpB,CAEA,KAAA7G,GACE,GAAIxH,KAAK4N,gBACP,IACejJ,OAAOC,KAAKmJ,cACpBlK,QAASqK,IACRA,EAAIW,WAAWpB,IACjBM,aAAaE,WAAWC,IAG9B,OAASvN,GACPF,QAAQC,KAAK,oDAAqDC,EACpE,CAGcuD,SAAS4K,OAAOC,MAAM,KAC9BlL,QAASiL,IACf,MAAMZ,EAAMY,EAAOC,MAAM,KAAK,GAAGC,OAC7Bd,EAAIW,WAAWpB,IACjBzN,KAAK4O,aAAaV,IAGxB,CAEQ,SAAAM,CAAUS,EAAcd,EAAee,GAC7C,IACE,MAAMC,MAAWxP,KACjBwP,EAAKC,QAAQD,EAAKE,UAAmB,GAAPH,EAAY,GAAK,GAAK,KAGpD,IAAII,EAAY,GAAGL,KAAQd,KAFX,WAAWgB,EAAKI,yBAUhC,GANIvP,KAAK2N,QAAQ6B,cAA6C,WAA7BzL,OAAO0L,SAASC,WAC/CJ,GAAa,WAGfA,GAAa,gBAETtP,KAAK2N,QAAQgC,aACfL,GAAa,WAAWtP,KAAK2N,QAAQgC,oBACvC,GAAW3P,KAAK2N,QAAQiC,YAAa,CACnC,MAAMC,EAAS7P,KAAK8P,gBAChBD,IACFP,GAAa,WAAWO,IAE5B,CAEA3L,SAAS4K,OAASQ,CACpB,OAAS3O,GACPF,QAAQC,KAAK,2CAA4CC,EAC3D,CACF,CAEQ,SAAAgO,CAAUM,GAChB,IACE,MAAMc,EAASd,EAAO,IAChBe,EAAK9L,SAAS4K,OAAOC,MAAM,KAEjC,IAAA,IAASkB,EAAI,EAAGA,EAAID,EAAGnL,OAAQoL,IAAK,CAClC,IAAI5Q,EAAI2Q,EAAGC,GACX,KAAuB,MAAhB5Q,EAAE6Q,OAAO,IACd7Q,EAAIA,EAAE8Q,UAAU,EAAG9Q,EAAEwF,QAEvB,GAA0B,IAAtBxF,EAAE+Q,QAAQL,GACZ,OAAO1Q,EAAE8Q,UAAUJ,EAAOlL,OAAQxF,EAAEwF,OAExC,CACF,OAASlE,GACPF,QAAQC,KAAK,2CAA4CC,EAC3D,CAEA,OAAO,IACT,CAEQ,YAAAiO,CAAaK,GACnB,IACE,IAAIK,EAAY,GAAGL,kDAEnB,GAAIjP,KAAK2N,QAAQgC,aACfL,GAAa,WAAWtP,KAAK2N,QAAQgC,oBACvC,GAAW3P,KAAK2N,QAAQiC,YAAa,CACnC,MAAMC,EAAS7P,KAAK8P,gBAChBD,IACFP,GAAa,WAAWO,IAE5B,CAEA3L,SAAS4K,OAASQ,CACpB,OAAS3O,GACPF,QAAQC,KAAK,8CAA+CC,EAC9D,CACF,CAEQ,aAAAmP,GACN,IACE,MAAMO,EAAWtM,OAAO0L,SAASY,SAEjC,GAAI,0BAA0BvC,KAAKuC,GACjC,OAAO,KAGT,GAAiB,cAAbA,EACF,OAAO,KAGT,MAAMC,EAAQD,EAAStB,MAAM,KAE7B,OAAIuB,EAAMzL,QAAU,EACX,IAAIwL,IAGN,IAAIC,EAAMzQ,UAAU0Q,KAAK,MAClC,OAAS5P,GAEP,OADAF,QAAQC,KAAK,+CAAgDC,GACtD,IACT,CACF,EChMK,SAAS6P,EAAarL,EAA6BnC,GACxD,MAAMyN,EAAMtL,EAAOnI,YCGd,SAA4B2L,GACjC,IACE,MAAM+H,EAAe/H,EACjB,IAAIgI,IAAIhI,GAAK+H,aACb,IAAIE,gBAAgB7M,OAAO0L,SAASoB,QAElCJ,EAAiB,CAAA,EAkBvB,MAhBwC,CACtC,SACA,SACA,WACA,OACA,UACA,QAGM5M,QAASqK,IACf,MAAMC,EAAQuC,EAAatP,IAAI,OAAO8M,KAClCC,IACFsC,EAAIvC,GAAOC,KAIRsC,CACT,OAAS9P,GAEP,OADAF,QAAQC,KAAK,uDAAwDC,GAC9D,CAAA,CACT,CACF,CDhCmCmQ,QAAuB,EAClDC,EAiDR,WACE,MAAMC,EAAKtL,UAAUuL,UAErB,GAAI,mDAAmDnD,KAAKkD,GAC1D,MAAO,CAAEnK,KAAM,UAGjB,GAAI,sGAAsGiH,KAAKkD,GAC7G,MAAO,CAAEnK,KAAM,UAGjB,MAAO,CAAEA,KAAM,UACjB,CA7DiBqK,GACTC,EA8DR,WACE,MAAMH,EAAKtL,UAAUuL,UAErB,GAAI,iBAAiBnD,KAAKkD,SAAY,CAAE/B,KAAM,UAAWmC,QAAS,MAClE,GAAI,kBAAkBtD,KAAKkD,SAAY,CAAE/B,KAAM,UAAWmC,QAAS,OACnE,GAAI,kBAAkBtD,KAAKkD,SAAY,CAAE/B,KAAM,UAAWmC,QAAS,KACnE,GAAI,kBAAkBtD,KAAKkD,SAAY,CAAE/B,KAAM,UAAWmC,QAAS,KACnE,GAAI,WAAWtD,KAAKkD,SAAY,CAAE/B,KAAM,UAAWmC,QAAS,WAE5D,GAAI,0BAA0BtD,KAAKkD,GAAK,CACtC,MAAMK,EAAQL,EAAGK,MAAM,2BACvB,MAAO,CAAEpC,KAAM,QAASmC,QAAS,GAAGC,EAAO,MAAMA,EAAO,KAC1D,CACA,GAAI,OAAOvD,KAAKkD,SAAY,CAAE/B,KAAM,QAASmC,QAAS,WAEtD,GAAI,qBAAqBtD,KAAKkD,GAAK,CAEjC,MAAO,CAAE/B,KAAM,UAAWmC,QADZJ,EAAGK,MAAM,sBACmB,GAC5C,CACA,GAAI,WAAWvD,KAAKkD,SAAY,CAAE/B,KAAM,UAAWmC,QAAS,WAE5D,GAAI,wBAAwBtD,KAAKkD,GAAK,CACpC,MAAMK,EAAQL,EAAGK,MAAM,yBACvB,MAAO,CAAEpC,KAAM,MAAOmC,QAAS,GAAGC,EAAO,MAAMA,EAAO,KACxD,CACA,GAAI,uBAAuBvD,KAAKkD,GAAK,CACnC,MAAMK,EAAQL,EAAGK,MAAM,wBACvB,MAAO,CAAEpC,KAAM,MAAOmC,QAAS,GAAGC,EAAO,MAAMA,EAAO,KACxD,CACA,MAAI,eAAevD,KAAKkD,GAAY,CAAE/B,KAAM,MAAOmC,QAAS,WAExD,SAAStD,KAAKkD,GAAY,CAAE/B,KAAM,QAASmC,QAAS,WAEjD,CAAEnC,KAAM,UAAWmC,QAAS,UACrC,CAhGaE,GACLC,EAiGR,WACE,MAAMP,EAAKtL,UAAUuL,UAErB,GAAI,kBAAkBnD,KAAKkD,GAAK,CAE9B,MAAO,CAAE/B,KAAM,OAAQmC,QADTJ,EAAGK,MAAM,mBACgB,GACzC,CAEA,GAAI,qBAAqBvD,KAAKkD,KAAQ,MAAMlD,KAAKkD,GAAK,CAEpD,MAAO,CAAE/B,KAAM,SAAUmC,QADXJ,EAAGK,MAAM,sBACkB,GAC3C,CAEA,GAAI,sBAAsBvD,KAAKkD,GAAK,CAElC,MAAO,CAAE/B,KAAM,UAAWmC,QADZJ,EAAGK,MAAM,uBACmB,GAC5C,CAEA,GAAI,qBAAqBvD,KAAKkD,KAAQ,SAASlD,KAAKkD,GAAK,CACvD,MAAMK,EAAQL,EAAGK,MAAM,uBACvB,MAAO,CAAEpC,KAAM,SAAUmC,QAASC,EAAQA,EAAM,GAAK,UACvD,CAEA,GAAI,kBAAkBvD,KAAKkD,IAAO,YAAYlD,KAAKkD,GAAK,CACtD,MAAMK,EAAQL,EAAGK,MAAM,oBAAsBL,EAAGK,MAAM,iBACtD,MAAO,CAAEpC,KAAM,oBAAqBmC,QAASC,EAAQA,EAAM,GAAK,UAClE,CAEA,MAAO,CAAEpC,KAAM,UAAWmC,QAAS,UACrC,CA9HkBI,GACVC,EA+HR,WACE,IACE,MAAMC,EACHhM,UAAkBgM,YAClBhM,UAAkBiM,eAClBjM,UAAkBkM,iBAErB,GAAIF,EACF,MAAO,CACLG,cAAeH,EAAWG,cAC1BC,SAAUJ,EAAWI,SACrBC,IAAKL,EAAWK,IAGtB,OAASpR,GACPF,QAAQC,KAAK,kDAAmDC,EAClE,CAEA,MACF,CAlJkBqR,GAEVpP,EAAa,MAAAI,OAAA,EAAAA,EAAS8B,gBACtB1B,EAAc,MAAAJ,OAAA,EAAAA,EAAS+B,iBAE7B,MAAO,CACLkN,QAAS,CACPhD,KAAM,wBACNmC,QAAS,SAEXc,KAAM,CACJC,KAAMpO,OAAO0L,SAAS2C,SACtBC,SAAUlN,EAAOlI,iBAAmBiH,SAASmO,SAAW,GACxDxB,OAAQ9M,OAAO0L,SAASoB,OACxByB,MAAOpO,SAASoO,MAChB3J,IAAK5E,OAAO0L,SAAS8C,KACrBC,KAAMzO,OAAO0L,SAAS+C,MAExBvB,UAAWvL,UAAUuL,UACrBwB,OAAQ/M,UAAUgN,SAClBC,SAAUC,KAAKC,iBAAiBC,kBAAkBC,SAClDC,OAAQ,CACNC,MAAOlP,OAAOiP,OAAOC,MACrBC,OAAQnP,OAAOiP,OAAOE,OACtBC,QAASpP,OAAOqP,kBAAoB,GAEtCC,SAAU,CACRJ,MAAOlP,OAAOuP,WACdJ,OAAQnP,OAAOwP,gBAEb9C,GAAO9L,OAAOC,KAAK6L,GAAK5L,OAAS,GAAK,CAAE2O,SAAU/C,MAClD7N,GAAc+B,OAAOC,KAAKhC,GAAYiC,OAAS,GAAK,CAAEjC,iBACtDQ,GAAe,CAAEA,kBACjBqO,GAAW,CAAEA,cACbV,GAAU,CAAEA,aACZI,GAAM,CAAEA,SACRI,GAAW,CAAEA,cACbpM,EAAOnG,aAAe,CACxBuJ,KAAM,CACJG,OAAQvD,EAAOnG,YAAY0J,OAC3ByC,SAAUhG,EAAOnG,YAAY2J,MAIrC,CEnDO,MAAM8K,EAAN,WAAA1T,GACLC,KAAQjB,YAAmC8I,GAAI,CAE/C,QAAA6L,CAASC,GACH3T,KAAKjB,QAAQ6U,IAAID,EAAO1E,MAC1BnP,EAAOY,KAAK,WAAWiT,EAAO1E,uCAIhCjP,KAAKjB,QAAQsC,IAAIsS,EAAO1E,KAAM0E,GAC9B7T,EAAOU,KAAK,sBAAsBmT,EAAO1E,SAAS0E,EAAOvC,WAC3D,CAEA,UAAAyC,CAAWC,GACT,MAAMH,EAAS3T,KAAKjB,QAAQqC,IAAI0S,GAEhC,GAAKH,EAAL,CAKA,GAAIA,EAAO3O,QACT,IACE2O,EAAO3O,SACT,OAASrE,GACPb,EAAOa,MAAM,4BAA4BmT,MAAgBnT,EAC3D,CAGFX,KAAKjB,QAAQgV,OAAOD,GACpBhU,EAAOU,KAAK,wBAAwBsT,IAXpC,MAFEhU,EAAOY,KAAK,WAAWoT,eAc3B,CAEA,GAAA1S,CAAI0S,GACF,OAAO9T,KAAKjB,QAAQqC,IAAI0S,EAC1B,CAEA,MAAAE,GACE,OAAO7N,MAAM8N,KAAKjU,KAAKjB,QAAQmV,SACjC,CAEA,iBAAMC,CACJC,KACG7T,GAEH,IAAA,MAAWoT,KAAU3T,KAAKjB,QAAQmV,SAAU,CAC1C,MAAMG,EAAOV,EAAOS,GAEpB,GAAoB,mBAATC,EACT,IACE,MAAMpN,QAAgBoN,EAAkBC,MAAMX,EAAQpT,GAEtD,QAAe,IAAX0G,EACF,OAAOA,CAEX,OAAStG,GAMP,GALAb,EAAOa,MACL,mBAAmB4T,OAAOH,iBAAwBT,EAAO1E,SACzDtO,GAGEgT,EAAOa,QACT,IACEb,EAAOa,QAAQ7T,EAAgB,CAAEyT,WAAU7T,QAC7C,OAASkU,GACP3U,EAAOa,MACL,wCAAwCgT,EAAO1E,SAC/CwF,EAEJ,CAEJ,CAEJ,CAGF,CAEA,sBAAMC,CACJN,EACAO,KACGC,GAEH,IAAIzG,EAAQwG,EAEZ,IAAA,MAAWhB,KAAU3T,KAAKjB,QAAQmV,SAAU,CAC1C,MAAMG,EAAOV,EAAOS,GAEpB,GAAoB,mBAATC,EACT,IACE,MAAMpN,QAAgBoN,EAAkBC,MAAMX,EAAQ,CACpDxF,KACGyG,IAGD3N,UACFkH,EAAQlH,EAEZ,OAAStG,GAMP,GALAb,EAAOa,MACL,mBAAmB4T,OAAOH,iBAAwBT,EAAO1E,SACzDtO,GAGEgT,EAAOa,QACT,IACEb,EAAOa,QAAQ7T,EAAgB,CAAEyT,WAAUjG,QAAOyG,kBACpD,OAASH,GACP3U,EAAOa,MACL,wCAAwCgT,EAAO1E,SAC/CwF,EAEJ,CAEJ,CAEJ,CAEA,OAAOtG,CACT,CAEA,yBAAM0G,CACJT,KACG7T,GAEH,MAAMuU,EAAyB,GAE/B,IAAA,MAAWnB,KAAU3T,KAAKjB,QAAQmV,SAAU,CAC1C,MAAMG,EAAOV,EAAOS,GAEpB,GAAoB,mBAATC,EAAqB,CAC9B,MAAMU,aACJ,IACE,aAAcV,EAAkBC,MAAMX,EAAQpT,EAChD,OAASI,GAMP,GALAb,EAAOa,MACL,mBAAmB4T,OAAOH,iBAAwBT,EAAO1E,SACzDtO,GAGEgT,EAAOa,QACT,IACEb,EAAOa,QAAQ7T,EAAgB,CAAEyT,WAAU7T,QAC7C,OAASkU,GACP3U,EAAOa,MACL,wCAAwCgT,EAAO1E,SAC/CwF,EAEJ,CAGF,MACF,CACF,KAEAK,EAASlO,KAAKmO,EAChB,CACF,CAGA,aADsBrL,QAAQC,IAAImL,IACnB3L,OAAQ7J,QAAY,IAANA,EAC/B,CAEA,KAAAkI,GACE,IAAA,MAAWmM,KAAU3T,KAAKjB,QAAQmV,SAChC,GAAIP,EAAO3O,QACT,IACE2O,EAAO3O,SACT,OAASrE,GACPb,EAAOa,MAAM,4BAA4BgT,EAAO1E,SAAUtO,EAC5D,CAIJX,KAAKjB,QAAQyI,QACb1H,EAAOU,KAAK,sBACd,EC3JK,MAAMwU,EAMX,WAAAjV,CAAYc,EAAkBsE,EAA+B,IAF7DnF,KAAQiV,UAA8D,GAGpEjV,KAAKa,QAAUA,EACfb,KAAKmF,OAAS,CACZ+P,eAAgB/P,EAAO+P,gBAAkB,CAAA,EACzCC,eAAgBhQ,EAAOgQ,iBAAkB,EACzCC,kBAAmBjQ,EAAOiQ,mBAAqB,iBAGjDpV,KAAKqV,YAAcrV,KAAKsV,iBAC1B,CAEQ,eAAAA,GACN,MAAMC,EAASvV,KAAKa,QAAQO,IAAIpB,KAAKmF,OAAOiQ,mBAE5C,GAAIG,EACF,IACE,MAAMC,EAAShU,KAAKC,MAAM8T,GAE1B,OADAzV,EAAOU,KAAK,8BAA+BgV,GACpCxV,KAAKyV,kBAAkBD,EAChC,OAAS7U,GACPb,EAAOY,KAAK,uCAAwCC,EACtD,CAGF,OAAOX,KAAK0V,uBACd,CAEQ,qBAAAA,GACN,MAAO,CACLnX,UAAWyB,KAAKmF,OAAO+P,eAAe3W,YAAa,EACnDC,UAAWwB,KAAKmF,OAAO+P,eAAe1W,YAAa,EACnDC,WAAYuB,KAAKmF,OAAO+P,eAAezW,aAAc,EACrDkX,WAAW,EAEf,CAEQ,iBAAAF,CACNJ,GAEA,MAAO,CACLM,WAAW,EACXpX,UAAW8W,EAAY9W,WAAayB,KAAKmF,OAAO+P,eAAe3W,YAAa,EAC5EC,UAAW6W,EAAY7W,WAAawB,KAAKmF,OAAO+P,eAAe1W,YAAa,EAC5EC,WAAY4W,EAAY5W,YAAcuB,KAAKmF,OAAO+P,eAAezW,aAAc,EAEnF,CAEQ,eAAAmX,GACN,IACE5V,KAAKa,QAAQQ,IACXrB,KAAKmF,OAAOiQ,kBACZ5T,KAAKM,UAAU9B,KAAKqV,aACpB,KAEFvV,EAAOU,KAAK,6BAA8BR,KAAKqV,YACjD,OAAS1U,GACPb,EAAOa,MAAM,sCAAuCA,EACtD,CACF,CAEA,UAAAkV,CAAWR,GACT,MAAMS,EAAU,IAAK9V,KAAKqV,eAAgBA,GAC1CS,EAAQH,WAAY,EAEpB3V,KAAKqV,YAAcS,EACnB9V,KAAK4V,kBACL5V,KAAK+V,kBAELjW,EAAOU,KAAK,mBAAoBR,KAAKqV,YACvC,CAEA,QAAAW,GACEhW,KAAK6V,WAAW,CACdtX,WAAW,EACXC,WAAW,EACXC,YAAY,EACZkX,WAAW,GAEf,CAEA,OAAAM,GACEjW,KAAK6V,WAAW,CACdtX,WAAW,EACXC,WAAW,EACXC,YAAY,EACZkX,WAAW,GAEf,CAEA,UAAAO,CAAWC,GACT,OAAOnW,KAAKqV,YAAYc,EAC1B,CAEA,cAAAC,GACE,MAAO,IAAKpW,KAAKqV,YACnB,CAEA,SAAAgB,CAAUF,GACR,GAAInW,KAAKkW,WAAWC,GAClB,MAAO,UAIT,OADenW,KAAKa,QAAQO,IAAIpB,KAAKmF,OAAOiQ,oBAC7BpV,KAAKmF,OAAOgQ,eAClB,UAGF,QACT,CAEA,kBAAAmB,GACE,OAAOtW,KAAKkW,WAAW,YACzB,CAEA,kBAAAK,GACE,OAAOvW,KAAKkW,WAAW,YACzB,CAEA,QAAAM,CAASC,GAGP,OAFAzW,KAAKiV,UAAUrO,KAAK6P,GAEb,KACL,MAAMC,EAAQ1W,KAAKiV,UAAU7E,QAAQqG,GACjCC,GAAQ,GACV1W,KAAKiV,UAAU0B,OAAOD,EAAO,GAGnC,CAEQ,eAAAX,GACN/V,KAAKiV,UAAUpR,QAAS+S,IACtB,IACEA,EAAS5W,KAAKoW,iBAChB,OAASzV,GACPb,EAAOa,MAAM,0BAA2BA,EAC1C,GAEJ,CAEA,KAAAqB,GACEhC,KAAKa,QAAQoB,OAAOjC,KAAKmF,OAAOiQ,mBAChCpV,KAAKqV,YAAcrV,KAAK0V,wBACxB1V,KAAK+V,kBACLjW,EAAOU,KAAK,4BACd,CAEA,iBAAAqW,GACE,GAAsB,oBAAX9S,OAAwB,OAGnC,IADkBA,OAAe+S,SAG/B,YADAhX,EAAOY,KAAK,yBAId,MAAMqW,EAAmB,KACvB,MAAMC,EAAgBjT,OAAekT,sBAAwB,GAE7DjX,KAAK6V,WAAW,CACdF,WAAW,EACXlX,WAAYuY,EAAaE,SAAS,SAClC3Y,UAAWyY,EAAaE,SAAS,SACjC1Y,UAAWwY,EAAaE,SAAS,WAGnCpX,EAAOU,KAAK,iCAGd,GAAKuD,OAAeoT,eAAgB,CAClC,MAAMC,EAAmBrT,OAAeoT,eACvCpT,OAAeoT,eAAiB,WAC/BC,IACAL,GACF,CACF,MACGhT,OAAeoT,eAAiBJ,EAGnCA,GACF,CAEA,kBAAAM,GACE,GAAsB,oBAAXtT,OAAwB,OAEnC,MAAMuT,EAAavT,OAAewT,UAClC,IAAKD,EAEH,YADAxX,EAAOY,KAAK,0BAId,MAAM8W,EAAoB,KACxB,MAAMC,EAAUH,EAAUG,SAAW,CAAA,EAErCzX,KAAK6V,WAAW,CACdF,WAAW,EACXlX,WAAYgZ,EAAQpC,cAAe,EACnC9W,UAAWkZ,EAAQC,aAAc,EACjClZ,UAAWiZ,EAAQjZ,YAAa,IAGlCsB,EAAOU,KAAK,kCAGduD,OAAOC,iBAAiB,oBAAqBwT,GAC7CzT,OAAOC,iBAAiB,qBAAsBwT,GAE1CF,EAAUG,SACZD,GAEJ,CAEA,0BAAAG,GACE,GAAsB,oBAAX5T,OAAwB,OAEnC,MAAM6T,EAAQ7T,OAAe6T,KAC7B,IAAKA,EAEH,YADA9X,EAAOY,KAAK,mCAId,MAAMmX,EAAsB,KAC1BD,EAAK,UAAW,SAAU,CACxBE,WAAY9X,KAAKqV,YAAY7W,UAAY,UAAY,SACrDuZ,kBAAmB/X,KAAKqV,YAAY9W,UAAY,UAAY,SAC5DyZ,aAAchY,KAAKqV,YAAY7W,UAAY,UAAY,SACvDyZ,mBAAoBjY,KAAKqV,YAAY7W,UAAY,UAAY,SAC7D0Z,sBAAuBlY,KAAKqV,YAAY5W,WAAa,UAAY,SACjE0Z,wBAAyBnY,KAAKqV,YAAY5W,WAAa,UAAY,SACnE2Z,iBAAkB,YAGpBtY,EAAOU,KAAK,gCAGdR,KAAKwW,SAASqB,GACdA,GACF,EC7OF,MAAMQ,EAAa,OACbC,EAAa,OAUnB,SAASC,EAAWtJ,GAClB,GAAwB,oBAAb/K,SAA0B,OAAO,KAC5C,IACE,MAAMsU,EAAS,GAAGvJ,KACZqB,EAAQpM,SAAS4K,OAAOC,MAAM,KACpC,IAAA,IAASkB,EAAI,EAAGA,EAAIK,EAAMzL,OAAQoL,IAAK,CACrC,IAAI5Q,EAAIiR,EAAML,GACd,KAAuB,MAAhB5Q,EAAE6Q,OAAO,IAAY7Q,EAAIA,EAAE8Q,UAAU,GAC5C,GAA0B,IAAtB9Q,EAAE+Q,QAAQoI,GACZ,OAAOC,mBAAmBpZ,EAAE8Q,UAAUqI,EAAO3T,QAEjD,CACF,CAAA,MAEA,CACA,OAAO,IACT,CAGA,SAAS6T,EAAYzJ,EAAcd,EAAee,GAChD,GAAwB,oBAAbhL,UAA8C,oBAAXH,OAC9C,IACE,MAAMwK,EAAS,IAAI5O,KAAKA,KAAKC,MAAe,GAAPsP,EAAY,GAAK,GAAK,KAAMK,cAC3DM,EAmBV,WACE,GAAsB,oBAAX9L,OAAwB,OAAO,KAC1C,MAAM4U,EAAO5U,OAAO0L,SAASY,SAC7B,IAAKsI,GAAiB,cAATA,EAAsB,OAAO,KAE1C,GAAI,uBAAuB7K,KAAK6K,GAAO,OAAO,KAC9C,MAAMrI,EAAQqI,EAAK5J,MAAM,KACzB,OAAIuB,EAAMzL,OAAS,EAAU,KACtB,IAAMyL,EAAMzQ,OAAM,GAAI0Q,KAAK,IACpC,CA5BmBT,GACT8I,EAAuC,WAA7B7U,OAAO0L,SAASC,SAChC,IAAIJ,EAAY,GAAGL,KAAQ4J,mBAAmB1K,cAAkBI,wBAC5DqK,IAAStJ,GAAa,WACtBO,IAAQP,GAAa,WAAWO,KACpC3L,SAAS4K,OAASQ,CACpB,CAAA,MAEA,CACF,CA4BA,SAASwJ,IACP,GAAsB,oBAAX/U,OAAwB,OAAO,EAC1C,MAAM4U,EAAO5U,OAAO0L,SAASY,SAC7B,IAAKsI,GAAiB,cAATA,GAAwB,uBAAuB7K,KAAK6K,GAAO,OAAO,EAC/E,MAAMrI,EAAQqI,EAAK5J,MAAM,KACzB,OAAOxP,KAAKwZ,IAAI,EAAGzI,EAAMzL,OAAS,EACpC,CAaO,SAASmU,IACd,MAAMC,EAAWV,EAAWF,GAC5B,GAAIY,EAAU,OAAOA,EACrB,MAAMC,EAAM,MAAMJ,OAAoBnZ,KAAKC,SAXjCL,KAAK4Z,MAAsB,KAAhB5Z,KAAKC,UACjBC,WAAW2Z,SAAS,GAAI,OAYjC,OADAV,EAAYL,EAAYa,EA5FG,IA6FpBA,CACT,CASO,SAASG,IACd,MAAMJ,EAAWV,EAAWD,GAC5B,GAAIW,EAAU,OAAOA,EACrB,MAAMK,EAASC,IACf,IAAKD,EAAQ,OAAO,KACpB,MAAME,EAAM,MAAMV,OAAoBnZ,KAAKC,SAAS0Z,IAEpD,OADAZ,EAAYJ,EAAYkB,EA7GG,IA8GpBA,CACT,CAGO,SAASD,IACd,GAAsB,oBAAXxV,OAAwB,OAAO,KAC1C,IACE,OAAO,IAAI6M,gBAAgB7M,OAAO0L,SAASoB,QAAQzP,IAAI,SACzD,CAAA,MACE,OAAO,IACT,CACF,CC9GA,SAASqY,EAASC,GAChB,OAAOA,EAASC,OAAO,CAACC,EAAKC,IAAMD,GAAOC,EAAEC,UAAY,GAAI,EAC9D,CAsBA,SAASC,EAAYL,GACnB,OAAOA,EAASrQ,IAAKwQ,IAAA,CACnBG,WAAYH,EAAEG,WACdC,IAAKJ,EAAEI,KAAOJ,EAAEG,WAChB/K,KAAM4K,EAAE5K,KACRiL,MAAOL,EAAEK,MACTJ,SAAUD,EAAEC,UAAY,EACxBK,SAAUN,EAAEM,SACZhE,SAAU0D,EAAE1D,SACZiE,MAAOP,EAAEO,MACTC,WAAYR,EAAEQ,WACdC,cAAeT,EAAES,cACjBC,SAAUV,EAAEU,WAEhB,CAEO,MAAMC,EACX,WAAAza,CAAoB0a,GAAAza,KAAAya,MAAAA,CAAe,CAInC,aAAAC,CAAcC,GACZ3a,KAAKya,MAAMG,MAAM,iBAAkB,CACjCZ,WAAYW,EAAQX,WACpBC,IAAKU,EAAQV,KAAOU,EAAQX,WAC5B/K,KAAM0L,EAAQ1L,KACdiL,MAAOS,EAAQT,MACfC,SAAUQ,EAAQR,UAAY,MAC9BhE,SAAUwE,EAAQxE,SAClBiE,MAAOO,EAAQP,MACfC,WAAYM,EAAQN,WACpBC,cAAeK,EAAQL,cACvBO,UAAWF,EAAQE,UACnBlS,IAAKgS,EAAQhS,KAEjB,CAEA,iBAAAmS,CAAkBC,GAChB/a,KAAKya,MAAMG,MAAM,sBAAuB,CACtCI,QAASD,EAAKC,QACdC,UAAWF,EAAKE,UAChB9E,SAAU4E,EAAK5E,SACfuD,SAAUK,EAAYgB,EAAKrB,WAE/B,CAEA,cAAAwB,CAAeP,EAA2BQ,GACxCnb,KAAKya,MAAMG,MAAM,kBAAmB,CAClCZ,WAAYW,EAAQX,WACpBC,IAAKU,EAAQV,KAAOU,EAAQX,WAC5B/K,KAAM0L,EAAQ1L,KACdiL,MAAOS,EAAQT,MACfC,SAAUQ,EAAQR,UAAY,MAC9BhE,SAAUwE,EAAQxE,SAClBiE,MAAOO,EAAQP,MACfC,WAAYM,EAAQN,WACpBW,QAAS,MAAAG,OAAA,EAAAA,EAAQH,QACjBT,UAAU,MAAAY,OAAA,EAAAA,EAAQZ,WAAYI,EAAQJ,SACtCa,QAAS,MAAAD,OAAA,EAAAA,EAAQC,SAErB,CAEA,gBAAAC,CAAiBV,EAA2BQ,GAC1Cnb,KAAKya,MAAMG,MAAM,qBAAsB,CACrCZ,WAAYW,EAAQX,WACpBC,IAAKU,EAAQV,KAAOU,EAAQX,WAC5B/K,KAAM0L,EAAQ1L,KACdiL,MAAOS,EAAQT,MACfC,SAAUQ,EAAQR,UAAY,MAC9BhE,SAAUwE,EAAQxE,SAClB6E,QAAS,MAAAG,OAAA,EAAAA,EAAQH,QACjBT,UAAU,MAAAY,OAAA,EAAAA,EAAQZ,WAAYI,EAAQJ,SACtCa,QAAS,MAAAD,OAAA,EAAAA,EAAQC,SAErB,CAEA,gBAAAE,CAAiBnF,EAAkBxI,GACjC3N,KAAKya,MAAMG,MAAM,oBAAqB,CACpCzE,WACAoF,kBAAmB,MAAA5N,OAAA,EAAAA,EAAS4N,kBAC5BC,aAAc,MAAA7N,OAAA,EAAAA,EAAS6N,cAE3B,CAEA,eAAAC,CAAgB5K,GACd7Q,KAAKya,MAAMG,MAAM,mBAAoB,CACnCc,MAAO7K,EAAO6K,MACdC,cAAe9K,EAAO8K,cACtBC,QAAS/K,EAAO+K,SAEpB,CAIA,SAAAC,CAAUlB,GACR3a,KAAKya,MAAMG,MAAM,kBAAmB,CAClCZ,WAAYW,EAAQX,WACpBC,IAAKU,EAAQV,KAAOU,EAAQX,WAC5B/K,KAAM0L,EAAQ1L,KACdiL,MAAOS,EAAQT,MACfJ,SAAUa,EAAQb,UAAY,EAC9BK,SAAUQ,EAAQR,UAAY,MAC9BhE,SAAUwE,EAAQxE,SAClBiE,MAAOO,EAAQP,MACfC,WAAYM,EAAQN,WACpBC,cAAeK,EAAQL,eAE3B,CAEA,cAAAwB,CAAenB,GACb3a,KAAKya,MAAMG,MAAM,oBAAqB,CACpCZ,WAAYW,EAAQX,WACpBC,IAAKU,EAAQV,KAAOU,EAAQX,WAC5B/K,KAAM0L,EAAQ1L,KACdiL,MAAOS,EAAQT,MACfJ,SAAUa,EAAQb,UAAY,EAC9BK,SAAUQ,EAAQR,UAAY,MAC9BE,WAAYM,EAAQN,YAExB,CAEA,UAAA0B,CAAWC,GACThc,KAAKya,MAAMG,MAAM,cAAe,CAC9BqB,QAASD,EAAKC,QACd9N,MAAO6N,EAAK7N,MACZgM,SAAU6B,EAAK7B,UAAY,MAC3B+B,UAAWzC,EAASuC,EAAKtC,UACzBA,SAAUK,EAAYiC,EAAKtC,WAE/B,CAIA,eAAAyC,CAAgBC,GACdpc,KAAKya,MAAMG,MAAM,mBAAoB,CACnCyB,YAAaD,EAASC,YACtBlO,MAAOiO,EAASjO,MAChBgM,SAAUiC,EAASjC,UAAY,MAC/B+B,UAAWzC,EAAS2C,EAAS1C,UAC7B4C,OAAQF,EAASE,OACjBC,SAAUH,EAASG,SACnBC,IAAKJ,EAASI,IACd9C,SAAUK,EAAYqC,EAAS1C,WAEnC,CAEA,YAAA+C,CAAaC,EAAc/O,GACzB3N,KAAKya,MAAMG,MAAM,gBAAiB,CAChC8B,UACG/O,GAEP,CAIA,cAAAgP,CAAeC,GACb5c,KAAKya,MAAMG,MAAM,kBAAmB,CAClCiC,SAAUD,EAAMC,SAChB1O,MAAOyO,EAAMzO,MACb2O,QAASF,EAAME,SAAWF,EAAMzO,MAChCgM,SAAUyC,EAAMzC,UAAY,MAC5B+B,UAAWzC,EAASmD,EAAMlD,UAC1B4C,OAAQM,EAAMN,OACdC,SAAUK,EAAML,SAChBC,IAAKI,EAAMJ,IACXO,SAAUH,EAAMG,SAChBC,eAAgBJ,EAAMI,eACtBtD,SAAUK,EAAY6C,EAAMlD,WAEhC,CAEA,aAAAuD,CAAcC,EAAiB/O,EAAgBuL,GAC7C1Z,KAAKya,MAAMG,MAAM,iBAAkB,CACjCiC,SAAUK,EACV/O,QACAuL,SAAUA,EAAWK,EAAYL,QAAY,GAEjD,CAIA,aAAAyD,CAAcb,GACZtc,KAAKya,MAAMG,MAAM,iBAAkB,IAAK0B,GAC1C,CAEA,aAAAc,CAAcd,GACZtc,KAAKya,MAAMG,MAAM,iBAAkB,IAAK0B,GAC1C,CAIA,iBAAAe,CAAkBC,GAChBtd,KAAKya,MAAMG,MAAM,sBAAuB,CACtC2C,YAAaD,EAASC,YACtBC,cAAeF,EAASE,cACxBxD,WAAYsD,EAAS3C,QAAQX,WAC7BC,IAAKqD,EAAS3C,QAAQV,KAAOqD,EAAS3C,QAAQX,WAC9C/K,KAAMqO,EAAS3C,QAAQ1L,KACvBiL,MAAOoD,EAAS3C,QAAQT,MACxBG,WAAYiD,EAAS3C,QAAQN,YAEjC,CAUA,iBAAAoD,CAAkBC,GAChB1d,KAAKya,MAAMG,MAAM,qBAAsB,CACrCZ,WAAY0D,EAAS/C,QAAQX,WAC7BC,IAAKyD,EAAS/C,QAAQV,KAAOyD,EAAS/C,QAAQX,WAC9CK,WAAYqD,EAAS/C,QAAQN,WAC7BpL,KAAMyO,EAAS/C,QAAQ1L,KACvBiL,MAAOwD,EAAS/C,QAAQT,MACxByD,SAAUD,EAASC,UAEvB,CAIA,eAAAC,CAAgBC,GACd7d,KAAKya,MAAMG,MAAM,mBAAoB,IAAKiD,GAC5C,CAEA,gBAAAC,CAAiBD,GACf7d,KAAKya,MAAMG,MAAM,oBAAqB,IAAKiD,GAC7C,EC/QK,MAAME,EAYX,WAAAhe,CAAY4N,EAA8B,IAN1C3N,KAAQge,kBAAoB,EAC5Bhe,KAAQie,iBAAkC,KAC1Cje,KAAQke,iBAAyD,KAK/Dle,KAAKme,SAAW5e,KAAKwZ,IAAI,EAAGpL,EAAQyQ,eAAiB,KACrDpe,KAAKqe,gBAAkB9e,KAAKwZ,IAAI,EAAGpL,EAAQ0Q,iBAAmB,IAC9Dre,KAAKse,SAAW3Q,EAAQ4Q,sBAAwB,IAChDve,KAAKwe,YAAc7Q,EAAQ6Q,YAC3Bxe,KAAKye,OAASze,KAAKme,SACnBne,KAAK0e,aAAe/e,KAAKC,KAC3B,CAMA,UAAA+e,CAAWC,GAGT,OAFA5e,KAAK6e,SAED7e,KAAKye,QAAU,GACjBze,KAAKye,QAAU,GACR,IAGTze,KAAKge,mBAAqB,EACI,OAA1Bhe,KAAKie,mBACPje,KAAKie,iBAAmBW,EACxB9e,EAAOY,KACL,gDAAgDke,iCAAyC5e,KAAKse,qBAAqBte,KAAKme,oBAAoBne,KAAKqe,uBAIhJre,KAAKke,mBACRle,KAAKke,iBAAmB9T,WAAW,IAAMpK,KAAK8e,cAAe9e,KAAKse,YAG7D,EACT,CAGA,kBAAAS,GAEE,OADA/e,KAAK6e,SACEtf,KAAK4Z,MAAMnZ,KAAKye,OACzB,CAGA,OAAAzZ,GACMhF,KAAKke,mBACPxT,aAAa1K,KAAKke,kBAClBle,KAAKke,iBAAmB,MAE1Ble,KAAK8e,aACP,CAIQ,MAAAD,GACN,MAAMjf,EAAMD,KAAKC,MACXof,GAAcpf,EAAMI,KAAK0e,cAAgB,IAC/C,GAAIM,GAAc,EAAG,OAErB,MAAMC,EAAYD,EAAahf,KAAKqe,gBACpCre,KAAKye,OAASlf,KAAK2f,IAAIlf,KAAKme,SAAUne,KAAKye,OAASQ,GACpDjf,KAAK0e,aAAe9e,CACtB,CAEQ,WAAAkf,GAKN,GAJI9e,KAAKke,mBACPxT,aAAa1K,KAAKke,kBAClBle,KAAKke,iBAAmB,MAEtBle,KAAKge,kBAAoB,GAAKhe,KAAKwe,YACrC,IACExe,KAAKwe,YAAYxe,KAAKge,kBAAmBhe,KAAKie,iBAChD,OAASkB,GACPrf,EAAOY,KAAK,gDAAiDye,EAC/D,CAEFnf,KAAKge,kBAAoB,EACzBhe,KAAKie,iBAAmB,IAC1B,ECtFF,MAAMmB,EAAK,WACLC,EAAK,UASJ,SAASC,EAAmBC,EAAeC,EAAe,GAG/D,OASK,SAA2BC,EAAmBD,EAAe,GAClE,MAAME,EAAMD,EAAM5a,OACZ8a,EAAUpgB,KAAK4Z,MAAMuG,EAAM,GAEjC,IAAIE,EAAKJ,IAAS,EAGlB,IAAA,IAASvP,EAAI,EAAGA,EAAI0P,EAAS1P,IAAK,CAChC,MAAM4P,EAAa,EAAJ5P,EACf,IAAI6P,EACFL,EAAMI,GACLJ,EAAMI,EAAS,IAAM,EACrBJ,EAAMI,EAAS,IAAM,GACrBJ,EAAMI,EAAS,IAAM,GAExBC,EAAKvgB,KAAKwgB,KAAKD,EAAIV,GACnBU,EAAMA,GAAM,GAAOA,IAAO,GAC1BA,EAAKvgB,KAAKwgB,KAAKD,EAAIT,GAEnBO,GAAME,EACNF,EAAMA,GAAM,GAAOA,IAAO,GAC1BA,EAAMrgB,KAAKwgB,KAAKH,EAAI,GAAK,aAAgB,CAC3C,CAGA,MAAMI,EAAsB,EAAVL,EAClB,IAAIG,EAAK,EACT,MAAMG,EAAUP,EAAMM,EACN,IAAZC,IAAeH,GAAML,EAAMO,EAAY,IAAM,IAC7CC,GAAW,IAAGH,GAAML,EAAMO,EAAY,IAAM,GAC5CC,GAAW,IACbH,GAAML,EAAMO,GACZF,EAAKvgB,KAAKwgB,KAAKD,EAAIV,GACnBU,EAAMA,GAAM,GAAOA,IAAO,GAC1BA,EAAKvgB,KAAKwgB,KAAKD,EAAIT,GACnBO,GAAME,GAWR,OAPAF,GAAMF,EACNE,GAAMA,IAAO,GACbA,EAAKrgB,KAAKwgB,KAAKH,EAAI,YACnBA,GAAMA,IAAO,GACbA,EAAKrgB,KAAKwgB,KAAKH,EAAI,YACnBA,GAAMA,IAAO,GAENA,IAAO,CAChB,CAxDSM,EADO,IAAIC,aAAcC,OAAOb,GACPC,EAClC,CCSO,MAAMa,EAIX,WAAAtgB,CAAYugB,EAAkCC,GAC5C,GAD4CvgB,KAAAugB,OAAAA,EACvCA,EAAOC,EAAKD,EAAOC,EAAI,EAC1B,MAAM,IAAI5U,MAAM,4CAA4C2U,EAAOC,KAErE,GAAIF,EAAIzb,SAAW0b,EAAOC,GAAK,EAC7B,MAAM,IAAI5U,MACR,+CAA+C2U,EAAOC,GAAK,gBAAgBF,EAAIzb,UAGnF7E,KAAKsgB,IAAMA,EACXtgB,KAAKygB,KAAOF,EAAOC,EAAI,CACzB,CAGA,iBAAOE,CAAWC,EAAkBJ,GAClC,MAAMd,EAhDV,SAAuBmB,GACrB,GAAoB,oBAATC,KAAsB,CAC/B,MAAMC,EAAMD,KAAKD,GACXG,EAAM,IAAIC,WAAWF,EAAIjc,QAC/B,IAAA,IAASoL,EAAI,EAAGA,EAAI6Q,EAAIjc,OAAQoL,IAAK8Q,EAAI9Q,GAAK6Q,EAAIG,WAAWhR,GAC7D,OAAO8Q,CACT,CAGA,MAAMG,EAAKC,WAAmBC,OAC9B,GAAIF,GAAuB,mBAAXA,EAAEjN,KAAqB,CACrC,MAAMqM,EAAkBY,EAAEjN,KAAK2M,EAAK,UAEpC,OAAO,IAAII,WAAWV,EACxB,CACA,MAAM,IAAI1U,MAAM,wDAClB,CAgCkByV,CAAcV,GAC5B,OAAO,IAAIN,EAAYZ,EAAOc,EAChC,CAWA,GAAA3M,CAAI3E,GACF,MAAM2Q,EAAKN,EAAmBrQ,EAAMjP,KAAKugB,OAAOe,OAC1CC,EAAKjC,EAAmBrQ,EAAMjP,KAAKugB,OAAOiB,OAEhD,IAAA,IAASvR,EAAI,EAAGA,EAAIjQ,KAAKugB,OAAOkB,EAAGxR,IAAK,CAItC,MACMyR,EADY9B,EAAKrgB,KAAKwgB,KAAK9P,EAAGsR,KAAS,EACtBvhB,KAAKygB,KAE5B,GAAY,KADAzgB,KAAKsgB,IAAIoB,GAAO,GAAM,IAAY,EAANA,IACzB,OAAO,CACxB,CACA,OAAO,CACT,ECtBK,MAAMC,EAAN,WAAA5hB,GACLC,KAAQ4hB,MAA4B,KACpC5hB,KAAQ6hB,kBAA4BjX,IACpC5K,KAAQ8hB,aAAuB,EAO/B9hB,KAAQ+hB,oBAAmCC,IAG3ChiB,KAAQiiB,2BAAkDpa,IAC1D7H,KAAQkiB,kBAA4BviB,KAAKC,MACzCI,KAAQmiB,sBAAuB,CAAA,CAM/B,UAAAC,CAAWC,GACT,IAAKA,GAlCc,mBAkCNA,EAAKC,WAOhB,OAJAtiB,KAAK4hB,MAAQ,KACb5hB,KAAK6hB,kBAAoBjX,IACzB5K,KAAK8hB,aAAc,OACnB9hB,KAAK+hB,gBAAgBva,QAIvB,IACExH,KAAK4hB,MAAQvB,EAAYK,WAAW2B,EAAKE,UAAW,CAClD/B,EAAG6B,EAAK7B,EACRiB,EAAGY,EAAKZ,EACRH,MAAOe,EAAKG,OACZhB,MAAOa,EAAKI,QAEhB,CAAA,MAEEziB,KAAK4hB,MAAQ,IACf,CAEA5hB,KAAK6hB,kBAAoBQ,EAAKK,qBAAuB9X,IACrD5K,KAAK8hB,aAAoC,IAAtBO,EAAKM,aACxB3iB,KAAK+hB,gBAAgBva,OACvB,CAQA,UAAAob,CAAWhE,GAET,IAAK5e,KAAK4hB,MAAO,OAAO,EAIxB,GAAI5hB,KAAK8hB,YAAa,OAAO,EAG7B,GAAI9hB,KAAK4hB,MAAMhO,IAAIgL,GAAY,OAAO,EAGtC,GAAI5e,KAAK+hB,gBAAgBnO,IAAIgL,GAAY,OAAO,EAGhD,GAAI5e,KAAK6hB,kBAAoB,EAG3B,OAFA7hB,KAAK+hB,gBAAgBc,IAAIjE,GACzB5e,KAAK6hB,mBAAqB,GACnB,EAIT,MAAMiB,EAAO9iB,KAAKiiB,uBAAuB7gB,IAAIwd,IAAc,EAlF/D,IAA4Bte,EA8FxB,OAXAN,KAAKiiB,uBAAuB5gB,IAAIud,EAAWkE,EAAO,GAI7C9iB,KAAKmiB,uBACRniB,KAAKmiB,sBAAuB,EAxFN7hB,EA0FpB,qCAAqCse,qFAzFpB,oBAAZne,SAAmD,mBAAjBA,QAAQC,MACrDD,QAAQC,KAAKJ,KA4FJ,CACT,CAOA,eAAAyiB,GACE,GAAyC,IAArC/iB,KAAKiiB,uBAAuBe,KAAY,OAAO,KAEnD,MAAM9c,EAAiC,CAAA,EACvC,IAAI+c,EAAQ,EACZ,IAAA,MAAYhU,EAAMiU,KAAUljB,KAAKiiB,uBAC/B/b,EAAO+I,GAAQiU,EACfD,GAASC,EAGX,MAAMC,EAAQnjB,KAAKkiB,kBAInB,OAHAliB,KAAKiiB,uBAAuBza,QAC5BxH,KAAKkiB,kBAAoBviB,KAAKC,MAEvB,CAAEsG,SAAQ+c,QAAOE,QAC1B,CAIA,WAAAC,GAME,MAAO,CACLC,SAAyB,OAAfrjB,KAAK4hB,MACf0B,UAAWtjB,KAAK6hB,kBAChB0B,WAAYvjB,KAAK+hB,gBAAgBiB,KACjClB,YAAa9hB,KAAK8hB,YAEtB,EClJF,MAAM0B,EAAuC,CAC3C,UACA,QACA,WACA,UACA,QACA,YACA,WACA,OACA,QACA,YACA,QACA,YACA,SACA,IACA,KAUIC,EAAmB,oBACnBC,EAAmB,qBACnBC,EAAY,YACZC,EAAkB,MAClBC,EAAW,2EAEXC,MAAqB9B,IAAI,CAC7B,KAAM,KAAM,OAAQ,OAAQ,YAAa,MAAO,WAChD,SAAU,UAAW,UAAW,UAAW,UAAW,UAAW,UAQnE,SAAS+B,EAAa7V,GACpB,IAAKA,EAAK,OAAO,KAIjB,OAHeA,EAAI9O,QAAQqkB,EAAkB,SAASrkB,QAAQskB,EAAkB,SAC1DtkB,QAAQukB,EAAW,KAAKK,cACxB5kB,QAAQwkB,EAAiB,KAAKxkB,QAAQ,WAAY,KACvD,IACnB,CAEA,SAAS6kB,EAAmB/V,GAC1B,MAAMgW,EAAQhW,EAAI8V,cAClB,IAAA,MAAW9jB,KAAUsjB,EACnB,GAAIU,EAAMrV,WAAW3O,GAAS,OAAOA,EAEvC,OAAO,IACT,CAEA,SAASikB,EAAiBjW,GACxB,IAAA,MAAWkW,KAAQlW,EAAI8V,cAAcjV,MAAM,aACzC,GAAI+U,EAAelQ,IAAIwQ,GAAO,OAAO,EAEvC,OAAO,CACT,CAMA,SAASC,EAAelW,GACtB,GAAqB,iBAAVA,GAAsBmW,OAAOC,SAASpW,GAC/C,OAAOA,EAAQ,KAAO5O,KAAK4Z,MAAc,IAARhL,GAAgB5O,KAAK4Z,MAAMhL,GAE9D,GAAqB,iBAAVA,GAAsBA,EAAO,CACtC,MAAMqW,EAAIrW,EAAMa,OAChB,GAAI6U,EAAS/V,KAAK0W,GAAI,CACpB,MAAMC,EAAK9kB,KAAK8B,MAAM+iB,GACtB,IAAKF,OAAOI,MAAMD,GAAK,OAAOA,CAChC,CACA,MAAME,EAAQL,OAAOE,GACrB,GAAIF,OAAOC,SAASI,GAClB,OAAOA,EAAQ,KAAOplB,KAAK4Z,MAAc,IAARwL,GAAgBplB,KAAK4Z,MAAMwL,EAEhE,CACA,OAAO,IACT,CAOO,MAAMC,EAAN,MAAMA,EAAN,WAAA7kB,GAILC,KAAQ6kB,eAAyDhd,GAAI,CAUrE,OAAAid,CACE/jB,EACAlE,GAEA,MAAMkoB,EAAqC,CAAA,EACrCC,EAAqB,GAC3B,IAAKjkB,GAA4B,iBAAXA,EACpB,MAAO,CAAEgkB,YAAWC,SAGtB,IAAA,MAAWC,KAAUtgB,OAAOC,KAAK7D,GAAS,CAMxC,MAAMb,EAAS+jB,EAAmBgB,GAClC,GAAe,OAAX/kB,EAAiB,CACnB8kB,EAAMpe,KAAK,CACTse,YAAaD,EACbE,QAAS,kBACTC,OAAQ,+BAA+B5jB,KAAKM,UAAU5B,OAExD,QACF,CAGA,MAAMmlB,EAAgBtB,EAAakB,GACnC,GAAsB,OAAlBI,EAAwB,CAC1BL,EAAMpe,KAAK,CACTse,YAAaD,EACbE,QAAS,iBACTC,OAAQ,6CAEV,QACF,CAGA,GAA0C,OAAtCnB,EAAmBoB,GAAyB,CAC9CL,EAAMpe,KAAK,CACTse,YAAaD,EACbE,QAAS,kBACTC,OAAQ,kBAAkB5jB,KAAKM,UAAUujB,uCAE3C,QACF,CAGA,IAAIlX,EAAQpN,EAAOkkB,GAGnB,GAAqB,iBAAV9W,EAAoB,CAC7B,GAAIA,EAAMtJ,OAtIS,IAsIoB,CACrCmgB,EAAMpe,KAAK,CACTse,YAAaD,EACbE,QAAS,iBACTC,OAAQ,gBAAgBjX,EAAMtJ,kCAEhC,QACF,CACIsJ,EAAMtJ,OA/IS,MAgJjBmgB,EAAMpe,KAAK,CACTse,YAAaD,EACbE,QAAS,iBACTC,OAAQ,wBAAwBjX,EAAMtJ,wBAExCsJ,EAAQA,EAAMtO,MAAM,EArJH,KAuJrB,CAMA,GAAIskB,EAAiBkB,IAAmC,iBAAVlX,EAAoB,CAChE,MAAMqH,EAAS6O,EAAelW,GAC9B,GAAe,OAAXqH,EAAiB,CACnBwP,EAAMpe,KAAK,CACTse,YAAaD,EACbE,QAAS,kBACTC,OAAQ,SAAS5jB,KAAKM,UAAUqM,0BAA8B3M,KAAKM,UAAUujB,wCAE/E,QACF,CACAlX,EAAQqH,CACV,CAEAuP,EAAUM,GAAiBlX,CAC7B,CAGA,IAAA,MAAWmX,KAAQN,EACjBhlB,KAAKulB,UAAU1oB,GAAgB,KAAMyoB,GAGvC,MAAO,CAAEP,YAAWC,QACtB,CAEQ,SAAAO,CAAU1oB,EAA6ByoB,GAC7C,MAAME,EAAK3oB,GAAgB,mBAC3B,IAAI4oB,EAAazlB,KAAK6kB,WAAWzjB,IAAIokB,GAChCC,IACHA,MAAiB5d,IACjB7H,KAAK6kB,WAAWxjB,IAAImkB,EAAIC,IAE1B,MAAMvC,EAAQuC,EAAWrkB,IAAIkkB,EAAKH,UAAY,EA5HlD,IAAuB7kB,EA6Hf4iB,GAAS0B,EAAcc,WAC3BD,EAAWpkB,IAAIikB,EAAKH,QAASjC,EAAQ,GA9HlB5iB,EAgIjB,4BAA4BglB,EAAKH,YAAYG,EAAKF,yBAC9B5jB,KAAKM,UAAUwjB,EAAKJ,gFAhIrB,oBAAZzkB,SAAmD,mBAAjBA,QAAQC,MACrDD,QAAQC,KAAKJ,GAkIb,GA7HAskB,EAAwBc,SAAW,EAF9B,IAAMC,EAANf,EC7HP,MAAMgB,MAAgD5D,IAAkB,CACtE,QACA,MACA,OACA,UACA,WACA,MACA,UAKI6D,EAAW,6BAEXC,EAAW,oBAEXC,EAAgB,kBAGhBC,EAAc,sBAEb,MAAMC,EAIX,WAAAlmB,CAAY0a,GAFZza,KAAQkmB,cAAyC,CAAA,EAG/ClmB,KAAKya,MAAQA,CACf,CAMA,KAAA0L,CAAMrlB,EAAgBC,GACpB,GAAsB,iBAAXD,GAAyC,IAAlBA,EAAO+D,OAEvC,YADA/E,EAAOY,KAAK,kDAGd,MAAM0lB,EAAS,IAAKpmB,KAAKkmB,iBAAmBnlB,GAAU,IACtDf,KAAKkmB,cAAgB,CAAA,EACrBlmB,KAAKya,MAAM4L,SAASvlB,EAAQslB,EAC9B,CAMA,MAAAE,GACEtmB,KAAKkmB,cAAgB,CAAA,EACrBlmB,KAAKya,MAAMzY,OACb,CAEA,YAAAukB,CAAarY,EAAaC,GACL,iBAARD,GAAmC,IAAfA,EAAIrJ,OAInC7E,KAAKwmB,YAAY,CAAEtY,CAACA,GAAMC,IAHxBrO,EAAOY,KAAK,qDAIhB,CAEA,aAAA+lB,CAAcpd,GACPA,GAAsB,iBAARA,EAInBrJ,KAAKwmB,YAAYnd,GAHfvJ,EAAOY,KAAK,6CAIhB,CAEA,QAAAgmB,CAASC,GACFd,EAAS/X,KAAK6Y,GAInB3mB,KAAKwmB,YAAY,CAAEG,MAAOA,EAAM3C,gBAH9BlkB,EAAOY,KAAK,uCAIhB,CAEA,QAAAkmB,CAASC,GACFf,EAAShY,KAAK+Y,GAInB7mB,KAAKwmB,YAAY,CAAEK,UAHjB/mB,EAAOY,KAAK,0DAIhB,CAEA,cAAAomB,CAAeC,GACRhB,EAAcjY,KAAKiZ,GAIxB/mB,KAAKwmB,YAAY,CAAEQ,aAAcD,EAAU/C,gBAHzClkB,EAAOY,KAAK,qDAIhB,CAEA,cAAAumB,CAAeF,GACRhB,EAAcjY,KAAKiZ,GAIxB/mB,KAAKwmB,YAAY,CAAEU,aAAcH,EAAU/C,gBAHzClkB,EAAOY,KAAK,qDAIhB,CAEA,YAAAymB,CAAaC,GACNpB,EAAYlY,KAAKsZ,GAItBpnB,KAAKwmB,YAAY,CAAEa,WAAYD,IAH7BtnB,EAAOY,KAAK,0CAIhB,CAEA,QAAA4mB,CAASC,EAAuBC,GACzB5B,EAAehS,IAAI2T,GAID,kBAAZC,EAIXxnB,KAAKwmB,YAAY,CAAE,CAAC,UAAUe,KAAYC,IAHxC1nB,EAAOY,KAAK,6CAJZZ,EAAOY,KAAK,oCAAoC6mB,IAQpD,CAOA,cAAAE,CAAeC,GACQ,iBAAVA,GAAuC,IAAjBA,EAAM7iB,OAIvC7E,KAAKwmB,YAAY,CAAEmB,cAAeD,IAHhC5nB,EAAOY,KAAK,yDAIhB,CAMA,iBAAAknB,GACE,MAAO,IAAK5nB,KAAKkmB,cACnB,CAIQ,WAAAM,CAAYzlB,GAClB,MAAMD,EAASd,KAAKya,MAAM5Y,YACtBf,EACFd,KAAKya,MAAM4L,SAASvlB,EAAQC,GAG9B4D,OAAOkjB,OAAO7nB,KAAKkmB,cAAenlB,EACpC,EC5IK,MAAM+mB,EAAN,WAAA/nB,GACLC,KAAQmF,OAAqC,KAG7CnF,KAAQgD,QAAiC,KACzChD,KAAQ+nB,MAA2B,KACnC/nB,KAAQoF,UAA8B,KACtCpF,KAAQjB,QAA0B,IAAI0U,EACtCzT,KAAQgoB,YAAoC,KAC5ChoB,KAAQyX,QAAiC,KACzCzX,KAAQioB,WAAsC,KAC9CjoB,KAAQkoB,MAA8B,KACtCloB,KAAQmoB,YAAkC,KAC1CnoB,KAAQooB,aAA6B,IAAIzG,EAKzC3hB,KAAQqoB,cAA+B,IAAI1C,EAC3C3lB,KAAQsoB,aAA8B,KACtCtoB,KAAQuoB,iBAAwC,KAChDvoB,KAAQwoB,mBAAsD,KAC9DxoB,KAAQyoB,sBAA4D,KAQpEzoB,KAAQ0oB,kBAAyC7gB,IAMjD7H,KAAQ2oB,oBAAmC3G,IAK3ChiB,KAAQ4oB,kBAAmC,KAQ3C5oB,KAAQ6oB,iBAAkC,IAAA,CAE1C,UAAMC,CACJ9c,EACA7G,GAEA,OAAInF,KAAKgoB,cAIThoB,KAAKgoB,YAAchoB,KAAK+oB,MAAM/c,EAAU7G,IAH/BnF,KAAKgoB,WAKhB,CAEA,WAAce,CACZ/c,EACA7G,UAEI,OAAA6jB,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAatqB,aACfoB,EAAOY,KAAK,2BAIVV,KAAKipB,iBAAiB9jB,GACxBrF,EAAOU,KAAK,0CZoDX,WACL,MAAMwQ,EAAKtL,UAAUuL,UAAU+S,cAuB/B,MAtBoB,CAClB,MACA,QACA,SACA,QACA,gBACA,YACA,UACA,QACA,cACA,cACA,SACA,sBACA,cACA,aACA,WACA,cACA,WACA,UACA,eAGiBvb,KAAMygB,GAAYlY,EAAGkG,SAASgS,GACnD,CYzEQC,IAKJnpB,KAAKmF,OAASnF,KAAKopB,YAAYpd,EAAU7G,GAErCnF,KAAKmF,OAAO/H,OACd0C,EAAOK,SAGTL,EAAOU,KAAK,mCAAoCR,KAAKmF,QAErDnF,KAAKa,QAAU,IAAI6M,EAAQ,CACzBiC,aAAc3P,KAAKmF,OAAOrG,cAC1B0Q,aAAcxP,KAAKmF,OAAO5H,cAC1BqS,YAAa5P,KAAKmF,OAAO7H,wBAG3B0C,KAAKqpB,SAAW,IAAIzoB,EAASZ,KAAKa,SAElCb,KAAKyX,QAAU,IAAIzC,EAAehV,KAAKa,QAAS,CAC9CqU,eAAgBlV,KAAKmF,OAAO7G,gBAC5B6W,eAAgBnV,KAAKmF,OAAOlH,mBAG1B+B,KAAKmF,OAAOhH,oBACd6B,KAAKyX,QAAQZ,oBAGX7W,KAAKmF,OAAO/G,qBACd4B,KAAKyX,QAAQJ,qBAGXrX,KAAKmF,OAAO9G,+BACd2B,KAAKyX,QAAQE,6BAGf3X,KAAKgD,QAAU,IAAIV,EACjBtC,KAAKa,QACLb,KAAKmF,OAAOhI,iBAGd6C,KAAKspB,0BAELtpB,KAAKoF,UAAY,IAAIqC,EACnB,CACEuE,SAAUhM,KAAKmF,OAAOokB,UACtB5d,QAAS3L,KAAKmF,OAAOxG,SACrBmJ,cAAe9H,KAAKmF,OAAOvG,eAC3BgK,gBAAiB5I,KAAKmF,OAAOtG,iBAC7BkK,oBAAqB/I,KAAKmF,OAAOnH,sBACjCqO,eAAgBrM,KAAKmF,OAAOtH,gBAC5BqP,oBAAqBlN,KAAKmF,OAAOzH,sBACjCyP,WAAYnN,KAAKmF,OAAOxH,YACxB2P,uBAAwBtN,KAAKmF,OAAOvH,0BAEtCoC,KAAKa,SAGPb,KAAKmF,OAAOnG,YAAcgB,KAAKoF,UAAUmI,gBAEzCvN,KAAK+nB,MAAQ,IAAI7iB,EACf,CACE4B,UAAW9G,KAAKmF,OAAOrI,WACvBqK,cAAenH,KAAKmF,OAAOpI,eAC3B6I,kBAAmB5F,KAAKmF,OAAO3H,oBAC/B6I,iBAAkBrG,KAAKmF,OAAO1H,oBAEhCuC,KAAKoF,UACLpF,KAAKa,SAGPb,KAAKmoB,YAAc,IAAIpK,EAAY,CACjCK,cAAepe,KAAKmF,OAAOrH,iBAC3BugB,gBAAiBre,KAAKmF,OAAOpH,sBAC7BygB,YAAa,CAAC0E,EAAOsG,IAAexpB,KAAKypB,kBAAkBvG,EAAOsG,WAG9DxpB,KAAK0pB,oBAEP1pB,KAAKmF,OAAOjI,iBACd8C,KAAKkS,OACLlS,KAAK2pB,oBAGP7pB,EAAOU,KAAK,8CArFVV,EAAOU,KAAK,6BAsFhB,CAMQ,gBAAAmpB,GACN3pB,KAAKsoB,aAAevkB,OAAO0L,SAAS8C,KAEpC,MAAMqX,EAAgB,KAEpB,MAAMC,EAAa9lB,OAAO0L,SAAS8C,KAC/BsX,IAAe7pB,KAAKsoB,eACxBtoB,KAAKsoB,aAAeuB,EAGpBzf,WAAW,KACTpK,KAAKkS,QACJ,MAILlS,KAAKuoB,iBAAmBqB,EACxB7lB,OAAOC,iBAAiB,WAAYhE,KAAKuoB,kBAGzCvoB,KAAKwoB,mBAAqBsB,QAAQC,UAAUC,KAAKF,SACjD9pB,KAAKyoB,sBAAwBqB,QAAQG,aAAaD,KAAKF,SAEvDA,QAAQC,UAAY,IAAIxpB,KACtBP,KAAKwoB,sBAAuBjoB,GAC5BqpB,KAGFE,QAAQG,aAAe,IAAI1pB,KACzBP,KAAKyoB,yBAA0BloB,GAC/BqpB,IAEJ,CAEQ,eAAAM,GACFlqB,KAAKuoB,mBACPxkB,OAAOomB,oBAAoB,WAAYnqB,KAAKuoB,kBAC5CvoB,KAAKuoB,iBAAmB,MAGtBvoB,KAAKwoB,qBACPsB,QAAQC,UAAY/pB,KAAKwoB,mBACzBxoB,KAAKwoB,mBAAqB,MAGxBxoB,KAAKyoB,wBACPqB,QAAQG,aAAejqB,KAAKyoB,sBAC5BzoB,KAAKyoB,sBAAwB,KAEjC,CAEQ,WAAAW,CACNpd,EACA7G,GAEA,MAAO,IACFvI,KACAuI,EACHokB,UAAWvd,EACXtN,aAAa,EAEjB,CAEQ,gBAAAuqB,CAAiB9jB,GAEvB,MAD2C,WAAxBA,WAAQ9H,cACV,OAAO,EAExB,MAAM+sB,EACJ1kB,UAAU2kB,YACTtmB,OAAesmB,YACf3kB,UAAkB4kB,aAErB,MAAe,MAARF,GAAuB,QAARA,CACxB,CAEQ,uBAAAd,GACN,IAAKtpB,KAAKgD,QAAS,OAEnB,MAAMJ,EX7LH,SAAyB+F,GAC9B,IACE,MAAM+H,EAAe/H,EACjB,IAAIgI,IAAIhI,GAAK+H,aACb,IAAIE,gBAAgB7M,OAAO0L,SAASoB,QAElCjO,EAAyB,CAAA,EAkB/B,MAhB6C,CAC3C,QACA,SACA,UACA,YACA,SACA,aAGUiB,QAASqK,IACnB,MAAMC,EAAQuC,EAAatP,IAAI8M,GAC3BC,IACFvL,EAAWsL,GAAOC,KAIfvL,CACT,OAASjC,GAEP,OADAF,QAAQC,KAAK,oDAAqDC,GAC3D,CAAA,CACT,CACF,CWgKuB4pB,IX9JhB,SAAuB3nB,GAC5B,OAAO+B,OAAOC,KAAKhC,GAAYiC,OAAS,CAC1C,EW8JQ2lB,CAAc5nB,KAChB5C,KAAKgD,QAAQ0B,cAAc9B,EAAYmB,OAAO0L,SAAS8C,MACvDzS,EAAOU,KAAK,sCAAuCoC,GAEvD,CAEA,uBAAc8mB,GACZ,GAAK1pB,KAAKmF,OAAV,CAEA,IAAA,MAAW2O,KAAc9T,KAAKmF,OAAOpG,QACnCe,EAAO1C,MAAM,wBAAwB0W,WAGjC9T,KAAKjB,QAAQoV,YAAY,OAAQnU,KAAKmF,OAN1B,CAOpB,CAEA,GAAAslB,CAAI9W,SACF3T,KAAKjB,QAAQ2U,SAASC,IAElB,OAAAqV,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAatqB,cAAeiV,EAAOmV,MACrCpf,QAAQ+B,QAAQkI,EAAOmV,KAAK9oB,KAAKmF,SAASulB,MAAO/pB,IAC/Cb,EAAOa,MAAM,gCAAgCgT,EAAO1E,SAAUtO,IAGpE,CAaA,YAAAgqB,CAAa1b,GACXjP,KAAKjB,QAAQ8U,WAAW5E,EAC1B,CAcA,oBAAA2b,CAAqBC,GACnB7qB,KAAK2oB,gBAAkB,IAAI3G,KACxB6I,GAAS,IAAI1hB,OAAQ9J,GAAmB,iBAANA,GAAkBA,EAAEwF,OAAS,IAElE/E,EAAO1C,MAAM,2BAA4B,CAAE8lB,MAAOljB,KAAK2oB,gBAAgB3F,MACzE,CAcA,YAAA8H,CAAaC,GAEX,GADA/qB,KAAK4oB,kBAAoBmC,EACH,oBAAXhnB,QAA0BA,OAAOinB,eAC1C,IACMD,EACFhnB,OAAOinB,eAAehd,QAAQ,mBAAoB+c,GAElDhnB,OAAOinB,eAAe/c,WAAW,mBAErC,CAAA,MAEA,CAEFnO,EAAO1C,MAAM,wBAAyB,CAAE+Q,MAAO4c,GACjD,CAGA,cAAAE,GACEjrB,KAAK8qB,aAAa,KACpB,CAuBA,mBAAAI,CAAoBL,GAOlB7qB,KAAK2oB,gBAAkB,IAAI3G,KACxB6I,GAAS,IAAI1hB,OAAQ9J,GAAmB,iBAANA,GAAkBA,EAAEwF,OAAS,IAElE/E,EAAO1C,MAAM,0BAA2B,CAAE8lB,MAAOljB,KAAK2oB,gBAAgB3F,MACxE,CAgBA,WAAAmI,CAAYJ,GAEV,GADA/qB,KAAK6oB,iBAAmBkC,EACF,oBAAXhnB,QAA0BA,OAAOinB,eAC1C,IACMD,EACFhnB,OAAOinB,eAAehd,QAAQ,yBAA0B+c,GAExDhnB,OAAOinB,eAAe/c,WAAW,yBAErC,CAAA,MAEA,CAEFnO,EAAO1C,MAAM,uBAAwB,CAAE+Q,MAAO4c,GAChD,CAGA,aAAAK,GACEprB,KAAKmrB,YAAY,KACnB,CAQQ,mBAAAE,GAEN,OAAOrrB,KAAKsrB,sBACd,CAmBO,sBAAAC,SAEL,MAAMC,EAAc,OAAAxC,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAChByC,YACJ,GAAID,EAAY,OAAOA,EACvB,GAAsB,oBAAXznB,OAAwB,OAGnC,IACE,MAAM8M,EAAS,IAAID,gBAAgB7M,OAAO0L,SAASoB,QAC7C6a,EAAM7a,EAAOzP,IAAI,aAAeyP,EAAOzP,IAAI,MACjD,GAAIsqB,GAAOA,EAAI7mB,OAAS,EAAG,OAAO6mB,CACpC,CAAA,MAEA,CAGA,MAAMC,EAAU3rB,KAAKqrB,sBACrB,GAAIM,EAAS,OAAOA,EAGpB,GAAI3rB,KAAK6oB,iBAAkB,OAAO7oB,KAAK6oB,iBAGvC,IACE,GAAI9kB,OAAOinB,eAAgB,CACzB,MAAM3iB,EAAStE,OAAOinB,eAAetc,QAAQ,0BAC7C,GAAIrG,GAAUA,EAAOxD,OAAS,EAAG,OAAOwD,CAC1C,CACF,CAAA,MAEA,CAIF,CAgBQ,oBAAAijB,GACN,GAAsB,oBAAXvnB,QACuB,IAA9B/D,KAAK2oB,gBAAgB3F,KACzB,IACE,MAAM4I,EAAW7nB,OAAO0L,SAAS2C,SAASrD,MAAM,KAAK5F,OAAO0iB,SAC5D,GAAwB,IAApBD,EAAS/mB,OAAc,OAC3B,MAAMinB,EAAQF,EAAS,GAAG5H,cAC1B,OAAOhkB,KAAK2oB,gBAAgB/U,IAAIkY,GAASA,OAAQ,CACnD,CAAA,MACE,MACF,CACF,CAqCO,uBAAAC,SAEL,MAAMP,EAAa,OAAAxC,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAansB,aAChC,GAAI2uB,EAAY,OAAOA,EACvB,GAAsB,oBAAXznB,OAAwB,OAGnC,IACE,MAAMyhB,EAAK,IAAI5U,gBAAgB7M,OAAO0L,SAASoB,QAAQzP,IAAI,MAC3D,GAAIokB,GAAMA,EAAG3gB,OAAS,EAAG,OAAO2gB,CAClC,CAAA,MAEA,CAGA,MAAMwG,EAAShsB,KAAKsrB,uBACpB,GAAIU,EAAQ,OAAOA,EAGnB,GAAIhsB,KAAK4oB,kBAAmB,OAAO5oB,KAAK4oB,kBAGxC,IACE,GAAI7kB,OAAOinB,eAAgB,CACzB,MAAM3iB,EAAStE,OAAOinB,eAAetc,QAAQ,oBAC7C,GAAIrG,GAAUA,EAAOxD,OAAS,EAAG,OAAOwD,CAC1C,CACF,CAAA,MAEA,CAIF,CAEA,KAAAuS,CAAMgE,EAAmBqN,GACvB,IAAKjsB,KAAKksB,oBAAqB,OAE/B,MAAMC,EAAYzsB,IACZoE,EAAoB,CACxB+C,KAAM,QACN/C,MAAO8a,EACPqN,WAAYA,GAAc,CAAA,EAC1BE,YACAC,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQd,KAAKqpB,SAASxnB,kBAAe,EACrCqB,UAAWlD,KAAKgD,QAASsB,eACzBzH,aAAcmD,KAAK+rB,0BACnBN,YAAazrB,KAAKurB,yBAClBtf,QAASuE,EAAaxQ,KAAKmF,OAASnF,KAAKgD,UAK3ChD,KAAK0oB,cAAcrnB,IAAIud,EAAWuN,GAElCnsB,KAAKqsB,aAAavoB,EACpB,CAEA,QAAAuiB,CAASvlB,EAAgBC,GACvB,IAAKf,KAAKksB,oBAAqB,OAM/B,MAAMI,EAAgBtsB,KAAK+rB,2BAA6B,MAChDhH,UAAWwH,GAAmBvsB,KAAKqoB,cAAcvD,QAAQ/jB,EAAQurB,GAEzEtsB,KAAKqpB,SAAS1nB,UAAUb,EAAQyrB,GAEhC,MAAMzoB,EAAuB,CAC3B+C,KAAM,WACN9F,OAAQwrB,EACRJ,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,SACAoC,UAAWlD,KAAKgD,QAASsB,eACzBzH,aAAcmD,KAAK+rB,0BACnBN,YAAazrB,KAAKurB,yBAClBtf,QAASuE,EAAaxQ,KAAKmF,OAASnF,KAAKgD,UAG3ChD,KAAKqsB,aAAavoB,EACpB,CAEA,IAAAoO,CAAKjD,EAAegd,GAClB,IAAKjsB,KAAKksB,oBAAqB,OAE/B,MAAMpoB,EAAmB,CACvB+C,KAAM,OACNoI,KAAMA,GAAQ/K,SAASoO,MACvB2Z,WAAYA,GAAc,CAAA,EAC1BE,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQd,KAAKqpB,SAASxnB,kBAAe,EACrCqB,UAAWlD,KAAKgD,QAASsB,eACzBzH,aAAcmD,KAAK+rB,0BACnBN,YAAazrB,KAAKurB,yBAClBtf,QAASuE,EAAaxQ,KAAKmF,OAASnF,KAAKgD,UAG3ChD,KAAKqsB,aAAavoB,EACpB,CAEA,KAAA0oB,CAAMC,EAAiB1rB,GACrB,IAAKf,KAAKksB,oBAAqB,OAI/B,MAAMI,EAAgBtsB,KAAK+rB,2BAA6B,MAChDhH,UAAWwH,GAAmBvsB,KAAKqoB,cAAcvD,QAAQ/jB,EAAQurB,GAEnExoB,EAAoB,CACxB+C,KAAM,QACN4lB,UACA1rB,OAAQwrB,EACRJ,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQd,KAAKqpB,SAASxnB,kBAAe,EACrCqB,UAAWlD,KAAKgD,QAASsB,eACzB2H,QAASuE,EAAaxQ,KAAKmF,OAASnF,KAAKgD,UAG3ChD,KAAKqsB,aAAavoB,EACpB,CAEA,KAAA5B,CAAMC,GACJ,IAAKnC,KAAKksB,oBAAqB,OAE/B,MAAM9pB,WAAEA,GAAepC,KAAKqpB,SAASnnB,MAAMC,GAErC2B,EAAoB,CACxB+C,KAAM,QACNzE,aACA+pB,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQqB,EACRe,UAAWlD,KAAKgD,QAASsB,eACzB2H,QAASuE,EAAaxQ,KAAKmF,OAASnF,KAAKgD,UAG3ChD,KAAKqsB,aAAavoB,EACpB,CAOA,MAAAkP,CAAO/D,EAAcgd,GACnB,IAAKjsB,KAAKksB,oBAAqB,OAC/B,GAAoB,iBAATjd,GAAqC,IAAhBA,EAAKpK,OAEnC,YADA/E,EAAOY,KAAK,iDAId,MAAMoD,EAAqB,CACzB+C,KAAM,SACNoI,OACAgd,WAAYA,GAAc,CAAA,EAC1BE,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQd,KAAKqpB,SAASxnB,kBAAe,EACrCqB,UAAWlD,KAAKgD,QAASsB,eACzBzH,aAAcmD,KAAK+rB,0BACnBN,YAAazrB,KAAKurB,yBAClBtf,QAASuE,EAAaxQ,KAAKmF,OAASnF,KAAKgD,UAG3ChD,KAAKqsB,aAAavoB,EACpB,CAEA,kBAAcuoB,CAAavoB,SACzB,IAAI,OAAAklB,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAa9qB,sBAAuB8B,KAAKyX,UACxB,UAAf3T,EAAM+C,MAAmC,SAAf/C,EAAM+C,QAC7B7G,KAAKyX,QAAQvB,WAAW,aAE3B,YADApW,EAAO1C,MAAM,gDAUnB,GAAmB,UAAf0G,EAAM+C,KAAkB,CAC1B,MAAM6lB,EAAa5oB,EAInB,KAF8B,iBAArB4oB,EAAW5oB,OAClB4oB,EAAW5oB,MAAM+K,WAAW,oBACT7O,KAAKooB,aAAaxF,WAAW8J,EAAW5oB,OAC3D,MAEJ,CAEA,GAAI9D,KAAKmoB,YAAa,CACpB,MAAMwE,EACW,UAAf7oB,EAAM+C,MAAqB/C,EAAqBA,MAC3CA,EAAqBA,MACtBA,EAAM+C,KAMZ,KAHiB,UAAf/C,EAAM+C,MACiC,iBAA/B/C,EAAqBA,OAC5BA,EAAqBA,MAAM+K,WAAW,oBACpB7O,KAAKmoB,YAAYxJ,WAAWgO,GAC/C,MAEJ,CAKA,MAAMC,EAAO9oB,EAAcmI,QACrB4gB,EAAS/oB,EAAcmoB,WAC7B,IAAI,MAAAW,OAAA,EAAAA,EAAKpZ,WAAYqZ,IAAUA,EAAMC,YAAa,CAChD,MAAMrc,EAAMmc,EAAIpZ,SACZ/C,EAAI+C,WAAUqZ,EAAMC,YAAcrc,EAAI+C,UACtC/C,EAAI0K,SAAQ0R,EAAME,gBAAkBtc,EAAI0K,QACxC1K,EAAIuc,SAAQH,EAAMI,gBAAkBxc,EAAIuc,QACxCvc,EAAIyc,UAASL,EAAMM,iBAAmB1c,EAAIyc,SAC1Czc,EAAI2c,OAAMP,EAAMQ,cAAgB5c,EAAI2c,KAC1C,CACA,IAAI,MAAAR,OAAA,EAAAA,EAAKhqB,aAAciqB,EAAO,CAC5B,MAAMS,EAASV,EAAIhqB,WACb2qB,EAAUD,EAAOE,OAASF,EAAOhU,QAAUgU,EAAOG,WAAaH,EAAOI,SAAWJ,EAAOK,OAC1FJ,IAAYV,EAAMe,sBAAyBA,kBAAoBL,EACrE,CAQA,GAAIV,EAAO,CACT,MAAMgB,ER7oBH,CACL3U,IAAKF,IACLQ,IAAKH,IACLC,OAAQC,KQ2oBFsU,EAAK3U,MAAQ2T,EAAM3T,MAAK2T,EAAM3T,IAAM2U,EAAK3U,KACzC2U,EAAKrU,MAAQqT,EAAMrT,MAAKqT,EAAMrT,IAAMqU,EAAKrU,KACzCqU,EAAKvU,SAAWuT,EAAMvT,SAAQuT,EAAMvT,OAASuU,EAAKvU,OACxD,CAEAxZ,EAAO1C,MAAM,mBAAoB0G,GAEjC,MAAMgqB,QAAyB9tB,KAAKjB,QAAQ2V,iBAC1C,qBACA5Q,GAGGgqB,GAKL9tB,KAAK+nB,MAAOnhB,KAAKknB,GACjB9tB,KAAKgD,QAASuB,4BAERvE,KAAKjB,QAAQoV,YAAY,oBAAqB2Z,IAPlDhuB,EAAO1C,MAAM,4BAQjB,CAMQ,iBAAAqsB,CAAkBvG,EAAe6K,WACvC,KAAK,OAAA/E,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAatqB,eAAgBsB,KAAKgD,UAAYhD,KAAKqpB,SAAU,OAClE,MAAMvlB,EAAoB,CACxB+C,KAAM,QACN/C,MAAO,4BACPmoB,WAAY,CACV+B,cAAe9K,EACf+K,kBAAmBF,EACnBG,eAAgBluB,KAAKmF,OAAOrH,iBAC5BqwB,kBAAmBnuB,KAAKmF,OAAOpH,uBAEjCouB,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQd,KAAKqpB,SAASxnB,kBAAe,EACrCqB,UAAWlD,KAAKgD,QAAQsB,eACxB2H,QAASuE,EAAaxQ,KAAKmF,OAAQnF,KAAKgD,UAE1C,OAAAorB,EAAApuB,KAAK+nB,UAAOnhB,KAAK9C,EACnB,CAEA,KAAA9B,GACOhC,KAAKksB,sBAEVlsB,KAAKqpB,SAASrnB,QAIdhC,KAAKkoB,MAAQ,KACbpoB,EAAOU,KAAK,uBACd,CAaA,oBAAA6tB,CAAqBhM,GACnBriB,KAAKooB,aAAahG,WAAWC,GAAQ,MACrCviB,EAAO1C,MAAM,2BAA4BilB,EAAO,CAC9CZ,EAAGY,EAAKZ,EACRjB,EAAG6B,EAAK7B,EACR8C,UAAWjB,EAAKK,qBACd,WACN,CAOQ,sBAAA4L,WACN,KAAK,OAAAtF,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAatqB,eAAgBsB,KAAKgD,UAAYhD,KAAKqpB,SAAU,OAClE,MAAMkF,EAASvuB,KAAKooB,aAAarF,kBACjC,IAAKwL,EAAQ,OAEb,MAAMzqB,EAAoB,CACxB+C,KAAM,QACN/C,MAAO,qCACPmoB,WAAY,CACV+B,cAAeO,EAAOtL,MACtBuL,eAAgB7pB,OAAOC,KAAK2pB,EAAOroB,QAAQrB,OAG3C4pB,aAAc9pB,OAAO+pB,QAAQH,EAAOroB,QACjC0D,KAAK,CAACC,EAAGC,IAAMA,EAAE,GAAKD,EAAE,IACxBhK,MAAM,EAAG,IACTwJ,IAAI,EAAE4F,EAAMiU,MAAK,CAASjU,OAAMiU,WACnCyL,aAAcJ,EAAOpL,MACrByL,WAAYjvB,KAAKC,OAEnBusB,UAAWzsB,IACX0sB,WAAA,IAAezsB,MAAOoM,cACtB/K,YAAahB,KAAKqpB,SAAS3nB,iBAC3BZ,OAAQd,KAAKqpB,SAASxnB,kBAAe,EACrCqB,UAAWlD,KAAKgD,QAAQsB,eACxB2H,QAASuE,EAAaxQ,KAAKmF,OAAQnF,KAAKgD,UAE1C,OAAAorB,EAAApuB,KAAK+nB,UAAOnhB,KAAK9C,EACnB,CAEA,WAAM2C,GACCzG,KAAKksB,sBAIVlsB,KAAKsuB,+BACCtuB,KAAK+nB,MAAOthB,QACpB,CAEA,cAAA/E,GACE,OAAK1B,KAAKqpB,SACHrpB,KAAKqpB,SAAS3nB,iBADM,IAE7B,CAEA,SAAAG,GACE,OAAK7B,KAAKqpB,SACHrpB,KAAKqpB,SAASxnB,YADM,IAE7B,CAEA,YAAAyC,GACE,OAAKtE,KAAKgD,QACHhD,KAAKgD,QAAQsB,eADM,IAE5B,CAkBA,WAAAuqB,CAAYjQ,GACV,OAAO5e,KAAK0oB,cAActnB,IAAIwd,IAAc,IAC9C,CAEA,KAAAxhB,CAAM+C,GAAkB,GAClBA,EACFL,EAAOK,SAEPL,EAAOM,UAGLJ,KAAKmF,SACPnF,KAAKmF,OAAO/H,MAAQ+C,EAExB,CAEA,OAAA2uB,CAAQC,GACN,IAAK/uB,KAAKmF,OAER,YADArF,EAAOY,KAAK,uBAId,MAAMsuB,EAAehvB,KAAKmF,OAAOvG,eAAekK,KAC7CzJ,GAAMA,EAAEqJ,SAAWqmB,EAAarmB,QAG/BsmB,EACFrqB,OAAOkjB,OAAOmH,EAAcD,GAE5B/uB,KAAKmF,OAAOvG,eAAegI,KAAKmoB,GAGlCjvB,EAAOU,KAAK,4BAA6BuuB,EAC3C,CAEA,WAAAE,GACE,OAAKjvB,KAAKoF,UAEH,CACLsC,WAAY1H,KAAKoF,UAAUmI,gBAC3B2hB,UAAWlvB,KAAKoF,UAAUoI,sBAJA,IAM9B,CAEA,UAAAqI,CAAWR,GACJrV,KAAKyX,SAKVzX,KAAKyX,QAAQ5B,WAAWR,GACxBvV,EAAOU,KAAK,gCALVV,EAAOY,KAAK,kCAMhB,CAEA,YAAAyuB,CAAahZ,GACNnW,KAAKyX,QAKNtB,EACFnW,KAAKyX,QAAQ5B,WAAW,CAAEM,CAACA,IAAW,IAEtCnW,KAAKyX,QAAQzB,WAPblW,EAAOY,KAAK,kCAShB,CAEA,WAAA0uB,CAAYjZ,GACLnW,KAAKyX,QAKNtB,EACFnW,KAAKyX,QAAQ5B,WAAW,CAAEM,CAACA,IAAW,IAEtCnW,KAAKyX,QAAQxB,UAPbnW,EAAOY,KAAK,kCAShB,CAEA,UAAAwV,CAAWC,GACT,OAAKnW,KAAKyX,SAIHzX,KAAKyX,QAAQvB,WAAWC,EACjC,CAEA,qBAAAkZ,GACE,OAAKrvB,KAAKyX,QAIHzX,KAAKyX,QAAQrB,iBAHX,IAIX,CAEA,eAAAkZ,CAAgB7Y,GACd,OAAKzW,KAAKyX,QAKHzX,KAAKyX,QAAQjB,SAASC,IAJ3B3W,EAAOY,KAAK,mCACL,OAIX,CAEQ,iBAAAwrB,SACN,SAAK,OAAAlD,EAAAhpB,KAAKmF,aAAL,EAAA6jB,EAAatqB,eAChBoB,EAAOY,KAAK,kDACL,EAGX,CAEA,aAAI6uB,GAIF,OAHKvvB,KAAKioB,aACRjoB,KAAKioB,WAAa,IAAIzN,EAAiBxa,OAElCA,KAAKioB,UACd,CAEA,QAAIuH,GAIF,OAHKxvB,KAAKkoB,QACRloB,KAAKkoB,MAAQ,IAAIjC,EAAcjmB,OAE1BA,KAAKkoB,KACd,CAEA,OAAAljB,GACEhF,KAAKkqB,kBAEDlqB,KAAK+nB,OACP/nB,KAAK+nB,MAAM/iB,UAGThF,KAAKgD,SACPhD,KAAKgD,QAAQgC,UAGXhF,KAAKoF,WACPpF,KAAKoF,UAAUJ,UAGbhF,KAAKmoB,cACPnoB,KAAKmoB,YAAYnjB,UACjBhF,KAAKmoB,YAAc,MAGrBnoB,KAAKjB,QAAQyI,QAEb1H,EAAOU,KAAK,gBACd,ECxkCK,SAASivB,EAAoB7C,GAClC,MAAMpZ,SAAEA,EAAAkZ,WAAUA,EAAAgD,YAAYA,gBAAaC,EAAAC,IAAeA,EAAAC,mBAAKA,GAAuBjD,EAChFkD,EAAMtc,EAASuc,oBAAsB,CAAA,EAErCC,GADW7pB,MAAMC,QAAQ0pB,EAAGE,OAAUF,EAAGE,MAAmB,IAC3CnwB,MAAM,EANJ,IAQzB,GAAqB,IAAjBmwB,EAAMnrB,OAER,YADA+qB,EAAI,qDAAsD,QAI5D,MAAMK,EAAW3L,OAAOwL,EAAGI,cAAgB,EACrCC,GAAmB,IAAZL,EAAGK,KAEhBN,IAEA,MAAMO,EAAKT,EAAenc,EAAS6c,kBAA+B,WAC5DC,EAAKX,EAAenc,EAAS+c,YAAyB,WAEtDC,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,gCACpBF,EAAQjK,aAAa,mBAAoB/S,EAASrS,IAClDqvB,EAAQG,MAAMC,QAAU,mTAQxB,MAAMC,EAAO3sB,SAASusB,cAAc,OACpCI,EAAKF,MAAMC,QAAU,qBACLR,aAAcE,sJAK9B,MAAMQ,EAAS5sB,SAASusB,cAAc,OACtCK,EAAOH,MAAMC,QAAU,0GAEvB,MAAMG,EAAa7sB,SAASusB,cAAc,OACpCne,EAAQpO,SAASusB,cAAc,OACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yDACtB,MAAMpkB,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,kCACrBG,EAAWE,YAAY3e,GACvBye,EAAWE,YAAYzkB,GACvBskB,EAAOG,YAAYF,GAEnB,MAAMG,EAAWhtB,SAASusB,cAAc,UACxCS,EAASF,YAAc,IACvBE,EAAS3K,aAAa,aAAc,SACpC2K,EAASP,MAAMC,QAAU,0IAIzBM,EAASltB,iBAAiB,QAAS,KACjC0oB,EAAWlZ,EAASrS,GAAI,aACxBqvB,EAAQvuB,WAEV6uB,EAAOG,YAAYC,GACnBL,EAAKI,YAAYH,GAEjB,MAAMlW,EAAQ1W,SAASusB,cAAc,OACrC7V,EAAM+V,MAAMC,QAAU,kKAIrBhW,EAAM+V,MAAkDQ,gBAAkB,OAC3EvW,EAAM5W,iBAAiB,QAAUotB,IAC3B7xB,KAAK8xB,IAAID,EAAEE,QAAU/xB,KAAK8xB,IAAID,EAAEG,UACpC3W,EAAM4W,YAAcJ,EAAEE,UAGxBtB,EAAMnsB,QAAQ,CAACxE,EAAG4Q,KAChB,MAAMwhB,EAAOvtB,SAASusB,cAAc,OAQpC,GAPAgB,EAAKlL,aAAa,kBAAmBhS,OAAOtE,IAC5CwhB,EAAKd,MAAMC,QAAU,sFAELN,mHAEJjxB,EAAEqyB,QAAU,UAAY,mBAEhCryB,EAAEwb,UAAW,CACf,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7BmB,EAAOlC,EAAYrwB,EAAEwb,WACvB+W,IACFD,EAAIE,IAAMD,EACVD,EAAIG,IAAM,GACVH,EAAII,QAAU,OACdJ,EAAIhB,MAAMC,QAAU,oEACpBa,EAAKR,YAAYU,GAErB,CACA,GAAItyB,EAAEiT,MAAO,CACX,MAAM0f,EAAI9tB,SAASusB,cAAc,OACjCuB,EAAEhB,YAAc3xB,EAAEiT,MAClB0f,EAAErB,MAAMC,QAAU,uDAClBa,EAAKR,YAAYe,EACnB,CACA,GAAI3yB,EAAEmN,KAAM,CACV,MAAM1C,EAAI5F,SAASusB,cAAc,OACjC3mB,EAAEknB,YAAc3xB,EAAEmN,KAClB1C,EAAE6mB,MAAMC,QAAU,sDAClBa,EAAKR,YAAYnnB,EACnB,CACA,GAAIzK,EAAE4yB,UAAY5yB,EAAEqyB,QAAS,CAC3B,MAAMQ,EAAMhuB,SAASusB,cAAc,UACnCyB,EAAIlB,YAAc3xB,EAAE4yB,SACpBC,EAAIvB,MAAMC,QAAU,2CACcN,aAAcF,0IAIhD,MAAM+B,EAAQf,IACZA,EAAEgB,kBACF1F,EAAWlZ,EAASrS,GAAI,WACxB,MAAMywB,EAAOlC,EAAYrwB,EAAEqyB,SACvBE,IAAM7tB,OAAO0L,SAAS8C,KAAOqf,IAEnCM,EAAIluB,iBAAiB,QAASmuB,GAC9BV,EAAKR,YAAYiB,GACjBT,EAAKztB,iBAAiB,QAASmuB,EACjC,CACAvX,EAAMqW,YAAYQ,KAEpBZ,EAAKI,YAAYrW,GAGjB,MAAMyX,EAAOnuB,SAASusB,cAAc,OACpC4B,EAAK1B,MAAMC,QAAU,sEACrB,MAAM0B,EAA4B,GAClCtC,EAAMnsB,QAAQ,KACZ,MAAM0uB,EAAMruB,SAASusB,cAAc,QACnC8B,EAAI5B,MAAMC,QAAU,sEAC2CN,2DAG/DgC,EAAO1rB,KAAK2rB,GACZF,EAAKpB,YAAYsB,KAEnB1B,EAAKI,YAAYoB,GAEjB,MAAMG,EAAa9Q,IACjB4Q,EAAOzuB,QAAQ,CAAC4uB,EAAGxiB,IAAOwiB,EAAE9B,MAAM+B,QAAUziB,IAAMyR,EAAM,MAAQ,SAElE8Q,EAAU,GAEV,IAAIG,EAAY,EAChB,MAAMC,EAAQhY,EAAMiY,iBAAiC,qBAUrD,IAAIC,EAAuD,KACvD7C,EAAW,GAAKD,EAAMnrB,OAAS,IACjCiuB,EAAgB1uB,YAAY,KAC1B,MAAM2uB,EAAOJ,EAAY,EACrBI,GAAQ/C,EAAMnrB,SAAWsrB,EACvB2C,iBAA6BA,GAd1B,CAACpR,IACZiR,GAAcjR,EAAMsO,EAAMnrB,OAAUmrB,EAAMnrB,QAAUmrB,EAAMnrB,OAC1D,MAAMmuB,EAAKJ,EAAMD,GACbK,IACFA,EAAGC,eAAe,CAAEC,SAAU,SAAUC,OAAQ,QAASC,MAAO,YAChEZ,EAAUG,KAYVR,CAAKY,IACJ9C,IAGLO,EAAQxsB,iBAAiB,SAAU,KAC7B8uB,iBAA6BA,KAInClY,EAAM5W,iBAAiB,SAAU,KAC/B,MAAMqvB,EAAS9zB,KAAK+zB,MAAM1Y,EAAM4W,WAAa,KACzC6B,IAAWV,GAAaU,GAAU,GAAKA,EAASrD,EAAMnrB,SACxD8tB,EAAYU,EACZb,EAAUG,MAIdnC,EAAQS,YAAYJ,GACpB3sB,SAASsI,KAAKykB,YAAYT,EAC5B,CCpMA,MAAM+C,EAAyB,0BCSxB,SAASC,EAAkB5G,GAChC,MAAMpZ,SAAEA,EAAAkZ,WAAUA,EAAAiD,cAAYA,EAAAC,IAAeA,EAAAC,mBAAKA,GAAuBjD,EACnEkD,EAAMtc,EAASuc,oBAAsB,CAAA,EAErC0D,EAAY3D,EAAG4D,oBAAmC,aAClDC,EAAYrP,OAAOwL,EAAG8D,oBAC5B,KAAMD,EAAY,GAEhB,YADA/D,EAAI,iEAAkE,QAGxE,MAAMiE,EACH/D,EAAGgE,sBAAoCtgB,EAAShH,MAAmB,YAChE2O,EAAgC,QAAvB2U,EAAGiE,gBAA4B,MAAQ,SAEtDlE,IAEA,MAAMO,EAAKT,EAAenc,EAAS6c,kBAA+B,WAC5D2D,EAAOrE,EAAenc,EAAS+c,YAAyB,WAGxD0D,EAAM/vB,SAASusB,cAAc,OACnCwD,EAAIvD,UAAY,4BAChBuD,EAAI1N,aAAa,mBAAoB/S,EAASrS,IAC9C8yB,EAAItD,MAAMC,QAAU,yHAGJR,iRAOhB,MAAM8D,EAAQhwB,SAASusB,cAAc,OACrCyD,EAAMvD,MAAMC,QAAU,wGAEtB,MAAMuD,EAAUjwB,SAASusB,cAAc,QACvC0D,EAAQnD,YAAcxd,EAASlB,MAC/B4hB,EAAMjD,YAAYkD,GAElB,MAAMC,EAAUlwB,SAASusB,cAAc,QACvC2D,EAAQzD,MAAMC,QAAU,kCACxBsD,EAAMjD,YAAYmD,GAClBH,EAAIhD,YAAYiD,GAEhB,MAAMG,EAAQnwB,SAASusB,cAAc,OACrC4D,EAAM1D,MAAMC,QAAU,wDAC6BoD,kCAGnD,MAAMM,EAASpwB,SAASusB,cAAc,OACtC6D,EAAO3D,MAAMC,QAAU,yDAC6BoD,yEAGpDK,EAAMpD,YAAYqD,GAClBL,EAAIhD,YAAYoD,GAEhB,MAAME,EAAWrwB,SAASusB,cAAc,OACxC8D,EAAS5D,MAAMC,QAAU,qDACzBqD,EAAIhD,YAAYsD,GAuDhB,IAAIC,GAAkB,EACtB,MAAMC,EAAS,KACb,MAAMC,EAvDiB,cACvB,GAAe,QAAXvZ,EAAkB,CACpB,MAAM9G,EAAQtQ,OAEX4wB,mBACH,OAAQtgB,GAAqC,iBAAtBA,EAAKb,EAASrS,IAAmBkT,EAAKb,EAASrS,IAAM,CAC9E,CAIA,IACE,GAAiB,eAAbsyB,GAA0C,kBAAbA,EAA8B,CAC7D,MAAMmB,EAAM7wB,OAKZ,GAAI,OAAAilB,EAAA4L,EAAIC,cAAJ,EAAA7L,EAAa5M,SAAU,CACzB,MAAM0Y,EAAIC,WAAWxgB,OAAOqgB,EAAIC,QAAQzY,SAAS4Y,aAAe,IAChE,MAAoB,eAAbvB,EAA4BqB,EAAI,CACzC,CACA,GAAIF,EAAIK,WACN,MAAiB,kBAAbxB,EACKttB,MAAMC,QAAQwuB,EAAIK,WAAWC,YAAcN,EAAIK,WAAWC,WAAWrwB,OAAS,EAEhFyf,OAAOsQ,EAAIK,WAAWE,YAAc,GAG7C,IACE,MAAMC,EAAW,OAAAhH,EAAArqB,OAAOgK,mBAAP,EAAAqgB,EAAqB1f,QAAQ,sBAC9C,GAAI0mB,EAAU,CACZ,MACMpZ,EADQxa,KAAKC,MAAM2zB,GACNpZ,KACnB,GAAIA,EACF,MAAoB,eAAbyX,EACHnP,OAAOtI,EAAKqZ,gBAAkB,GAC9BlvB,MAAMC,QAAQ4V,EAAKsZ,OACjBtZ,EAAKsZ,MAAMzwB,OACX,CAEV,CACF,CAAA,MAEA,CACF,CAGA,OAAO,CACT,CAAA,MACE,OAAO,CACT,GAKY0wB,GACNC,EAAMj2B,KAAKwZ,IAAI,EAAGxZ,KAAK2f,IAAI,IAAMwV,EAAMf,EAAa,MAG1D,GAFAW,EAAO3D,MAAM1d,MAAQ,GAAGuiB,KACxBpB,EAAQpD,YAAc,GAAG0D,EAAIe,QAAQ,QAAQ9B,EAAU8B,QAAQ,KAC3DD,GAAO,IACTjB,EAASvD,YAAc6C,EAClBW,IACHA,GAAkB,EAClB9H,EAAWlZ,EAASrS,GAAI,gBAErB,CACL,MAAMmiB,EAAY/jB,KAAKwZ,IAAI,EAAG4a,EAAYe,GAC1CH,EAASvD,YAAc,OAAO1N,EAAUmS,QAAQ,sBAAsB5B,GACxE,GAGFY,IAKA,MAAMiB,EAAYtxB,YAAYqwB,EAAQ,KAChCkB,EAAU,IAAM1wB,cAAcywB,GACpC3xB,OAAOC,iBAAiB,eAAgB2xB,EAAS,CAAEC,MAAM,IACzD3B,EAAIjwB,iBAAiB,SAAU2xB,GAE/BzxB,SAASsI,KAAKykB,YAAYgD,EAC5B,CCxIA,MAAM4B,EAAgB,4BAatB,SAASC,GAAeC,EAAmBrU,GACzC,IAC8B,oBAAjB3T,cACTA,aAAaC,QAAQ6nB,EAAgBE,EAAWxhB,OAAOmN,GAE3D,CAAA,MAEA,CACF,CAYO,SAASsU,GAAoBpJ,GAClC,MAAMpZ,SAAEA,EAAAkZ,WAAUA,EAAAiD,cAAYA,EAAAC,IAAeA,EAAAC,mBAAKA,GAAuBjD,EACnEkD,EAAMtc,EAASuc,oBAAsB,CAAA,EAErCgG,EAAYjG,EAAGmG,WACrB,IAAKF,EAEH,YADAnG,EAAI,mEAAoE,QAI1E,MAAMsG,EAAQ/vB,MAAMC,QAAQ0pB,EAAGoG,OAAUpG,EAAGoG,MAAmB,GAC/D,GAAqB,IAAjBA,EAAMrxB,OAER,YADA+qB,EAAI,yCAA0C,QAIhD,MAAMuG,GAA8B,IAAlBrG,EAAGsG,WACfC,GAAqC,IAA1BvG,EAAGwG,mBAEpBzG,IAEA,MAAMO,EAAKT,EAAenc,EAAS6c,kBAA+B,WAC5DC,EAAKX,EAAenc,EAAS+c,YAAyB,WAG5D,IAAIgG,EAxDN,SAAuBR,GACrB,IACE,GAA4B,oBAAjBhoB,aAA8B,OAAO,EAChD,MAAMyoB,EAAMzoB,aAAaW,QAAQmnB,EAAgBE,GAC3CU,EAAID,EAAME,SAASF,EAAK,IAAM,EACpC,OAAOlS,OAAOC,SAASkS,IAAMA,GAAK,EAAIA,EAAI,CAC5C,CAAA,MACE,OAAO,CACT,CACF,CA+CgBE,CAAcZ,GAC5B,GAAIQ,GAAWL,EAAMrxB,OAEnB,YADA+qB,EAAI,kBAAkBmG,uCAKxBrJ,EAAWlZ,EAASrS,GAAI,cAExB,IAAIy1B,EAAgC,KAChCC,EAA4B,KAC5BC,EAAkC,KAEtC,MAAMC,EAAa,KACjB,MAAAH,GAAAA,EAAW30B,SACX,MAAA40B,GAAAA,EAAO50B,SACP,MAAA60B,GAAAA,EAAa70B,SACb20B,EAAY,KACZC,EAAQ,KACRC,EAAc,MAGVE,EAAUC,IACdF,IACIE,EAAKC,QACPxK,EAAWlZ,EAASrS,GAAI,aAExBurB,EAAWlZ,EAASrS,GAAI,WAG1B20B,GAAeC,EAAWG,EAAMrxB,SAG5BsyB,EAAYzV,IAChBqV,IACA,MAAMra,EAAOwZ,EAAMxU,GACnB,IAAKhF,EAEH,YADAsa,EAAO,CAAEE,SAAS,IAGpBpB,GAAeC,EAAWrU,GAE1B,MAAM0V,EAAW1a,EAAK2a,WACtB,IAAIC,EAAyB,KAC7B,GAAIF,EACF,IACEE,EAASpzB,SAASqzB,cAAcH,EAClC,CAAA,MACEE,EAAS,IACX,CAEF,IAAKA,EAGH,OAFA1H,EAAI,kBAAkBlO,iBAAmB0V,0BAAkC,aAC3ED,EAASzV,EAAM,GAIjB,MAAM8V,EAAOF,EAAOG,wBAEpBX,EAAc5yB,SAASusB,cAAc,OACrCqG,EAAYnG,MAAMC,QAAU,wCAEnB4G,EAAKE,IAAM,cAAcF,EAAKG,KAAO,sBACnCH,EAAKvkB,MAAQ,iBAAiBukB,EAAKtkB,OAAS,mFAE7Bod,wGAI1BpsB,SAASsI,KAAKykB,YAAY6F,GAE1B,MAAMc,EAAYlb,EAAKkb,WAAa,SACpCf,EAAQ3yB,SAASusB,cAAc,OAC/BoG,EAAMtQ,aAAa,mBAAoB/S,EAASrS,IAChD01B,EAAMlG,MAAMC,QAAU,gEAENR,aAAcE,oQAQ9B,MAAMuH,EAAU3zB,SAASusB,cAAc,OACvCoH,EAAQ7G,YAActU,EAAKpK,MAC3BulB,EAAQlH,MAAMC,QAAU,yDACxBiG,EAAM5F,YAAY4G,GAElB,MAAMC,EAAS5zB,SAASusB,cAAc,OACtCqH,EAAO9G,YAActU,EAAKlQ,KAC1BsrB,EAAOnH,MAAMC,QAAU,qDACvBiG,EAAM5F,YAAY6G,GAElB,MAAMC,EAAW7zB,SAASusB,cAAc,OAIxC,GAHAsH,EAASpH,MAAMC,QAAU,kGAGrByF,EAAU,CACZ,MAAMhE,EAAOnuB,SAASusB,cAAc,OACpC4B,EAAK1B,MAAMC,QAAU,2BACrBsF,EAAMryB,QAAQ,CAACm0B,EAAG/nB,KAChB,MAAMwiB,EAAIvuB,SAASusB,cAAc,QACjCgC,EAAE9B,MAAMC,QAAU,qFAEFN,eAAgBrgB,IAAMyR,EAAM,OAAS,mBAErD2Q,EAAKpB,YAAYwB,KAEnBsF,EAAS9G,YAAYoB,EACvB,MACE0F,EAAS9G,YAAY/sB,SAASusB,cAAc,SAG9C,MAAMwH,EAAU/zB,SAASusB,cAAc,OAGvC,GAFAwH,EAAQtH,MAAMC,QAAU,2BAEpBuF,GAAazU,EAAMwU,EAAMrxB,OAAS,EAAG,CACvC,MAAMqzB,EAAOh0B,SAASusB,cAAc,UACpCyH,EAAKlH,YAAc,OACnBkH,EAAKvH,MAAMC,QAAU,sJAIrBsH,EAAKl0B,iBAAiB,QAAS,IAAMgzB,EAAO,CAAEE,SAAS,KACvDe,EAAQhH,YAAYiH,EACtB,CAEA,MAAMnF,EAAO7uB,SAASusB,cAAc,UAC9B0H,EAASzW,IAAQwU,EAAMrxB,OAAS,EACtCkuB,EAAK/B,YAActU,EAAKuV,WAAakG,EAAS,OAAS,QACvDpF,EAAKpC,MAAMC,QAAU,uBACLN,aAAcF,oIAI9B2C,EAAK/uB,iBAAiB,QAAS,KACzBm0B,EACFnB,EAAO,CAAEE,SAAS,IAElBC,EAASzV,EAAM,KAGnBuW,EAAQhH,YAAY8B,GAEpBgF,EAAS9G,YAAYgH,GACrBpB,EAAM5F,YAAY8G,GAClB7zB,SAASsI,KAAKykB,YAAY4F,GAG1B,MAAMuB,EAAUvB,EAAMY,wBAEtB,IAAIC,EAAMF,EAAKa,OADA,GAEXV,EAAOH,EAAKG,KACE,QAAdC,EACFF,EAAMF,EAAKE,IAAMU,EAAQllB,OAJZ,GAKU,SAAd0kB,GACTF,EAAMF,EAAKE,IACXC,EAAOH,EAAKG,KAAOS,EAAQnlB,MAPd,IAQU,UAAd2kB,IACTF,EAAMF,EAAKE,IACXC,EAAOH,EAAKc,MAVC,IAafZ,EAAMn4B,KAAKwZ,IAAI,EAAGxZ,KAAK2f,IAAInb,OAAOwP,YAAc6kB,EAAQllB,OAAS,EAAGwkB,IACpEC,EAAOp4B,KAAKwZ,IAAI,EAAGxZ,KAAK2f,IAAInb,OAAOuP,WAAa8kB,EAAQnlB,MAAQ,EAAG0kB,IACnEd,EAAMlG,MAAM+G,IAAM,GAAGA,MACrBb,EAAMlG,MAAMgH,KAAO,GAAGA,OAGxBR,EAASZ,GAMRxyB,OAAiEw0B,eAAkBrqB,GAnNtF,SAAqB6nB,GACnB,IAC8B,oBAAjBhoB,cACTA,aAAaE,WAAW4nB,EAAgBE,EAE5C,CAAA,MAEA,CACF,CA2MsGyC,CAAYtqB,EAClH,CC3EO,MAAMuqB,GAAN,MAAMA,EAqKX,WAAA14B,CAAYoF,GAzJZnF,KAAQ04B,UAA6B,GACrC14B,KAAQ24B,uBAAyB3W,IAIjChiB,KAAQ44B,oBAAsB/wB,IAE9B7H,KAAQ64B,eAAgB,EACxB74B,KAAQ84B,kBAAoB,EAC5B94B,KAAQ+4B,qBAAuB,EAiB/B/4B,KAAQg5B,UAAYnxB,IAMpB7H,KAASi5B,MAAuB,IAAIvvB,QAAS+B,IAC3CzL,KAAKk5B,aAAeztB,IAqiBtBzL,KAAQm5B,gBAAkBC,QA5axBp5B,KAAKgM,SAAW7G,EAAO6G,SACvBhM,KAAK2L,QAAUxG,EAAOwG,SAAW,uBAMjC3L,KAAKc,OAASqE,EAAOrE,QA5UzB,WACE,GAAwB,oBAAboD,SACX,IAEE,OADgB,IAAIwJ,GACLtM,IAAI,iBAAc,CACnC,CAAA,MACE,MACF,CACF,CAoUmCi4B,GAC/Br5B,KAAKs5B,UAAYn0B,EAAOm0B,UACxBt5B,KAAKu5B,eAAiBp0B,EAAOo0B,eAC7Bv5B,KAAKw5B,WAAar0B,EAAOq0B,WACzBx5B,KAAKy5B,UAAYt0B,EAAOs0B,YAAa,EAIrCz5B,KAAK05B,WAAiC,IAArBv0B,EAAOu0B,UACxB15B,KAAK25B,sBAAwBx0B,EAAOw0B,sBACpC35B,KAAK45B,eAAiBz0B,EAAOy0B,cAC/B,CAxIQ,IAAAC,CAAQjb,EAAmBvT,GACjC,MAAMyuB,EAAW95B,KAAKg5B,MAAM53B,IAAIwd,GAChC,IAAKkb,GAA8B,IAAlBA,EAAS9W,KAAY,OAAO,EAC7C,IAAI+W,GAAU,EACd,IAAA,MAAWC,KAAWF,EACpB,KAEiB,IADCE,EAA+C3uB,KACzC0uB,GAAU,EAClC,OAAS5a,GAGPnf,KAAKi6B,UAAU9a,EAAK,CAAErb,MAAO8a,GAC/B,CAEF,OAAOmb,CACT,CAEQ,SAAAE,CAAU9a,EAAclT,GAC9B,MAAM6tB,EAAW95B,KAAKg5B,MAAM53B,IAAI,SAChC,GAAK04B,GAA8B,IAAlBA,EAAS9W,KAK1B,IAAA,MAAWgX,KAAWF,EACpB,IACGE,EAAyC,CACxC15B,QAAS6e,aAAevT,MAAQuT,EAAI7e,QAAUiU,OAAO4K,GACrDxe,MAAOwe,EACPlT,QAASA,GAAW,CAAA,GAExB,CAAA,MAEA,MAZAjM,KAAK4vB,IAAI,2BAA2BzQ,aAAevT,MAAQuT,EAAI7e,QAAUiU,OAAO4K,KAAQ,QAc5F,CAEQ,QAAAzL,CACNkL,EACAob,GAIA,OAFKh6B,KAAKg5B,MAAMplB,IAAIgL,IAAY5e,KAAKg5B,MAAM33B,IAAIud,EAAW,IAAIoD,KAC9DhiB,KAAKg5B,MAAM53B,IAAIwd,GAAYiE,IAAImX,GACxB,WACL,OAAAhR,EAAAhpB,KAAKg5B,MAAM53B,IAAIwd,OAAY7K,OAAOimB,GAEtC,CAaA,EAAAE,CACEp2B,EACAk2B,GAEA,OAAOh6B,KAAK0T,SAAS5P,EAAOk2B,EAC9B,CAIA,iBAAAG,CACEH,GAEA,OAAOh6B,KAAK0T,SAAS,mBAAoBsmB,EAC3C,CAKA,kBAAAI,CACEJ,GAEA,OAAOh6B,KAAK0T,SAAS,qBAAsBsmB,EAC7C,CAIA,eAAAK,CACEL,GAEA,OAAOh6B,KAAK0T,SAAS,iBAAkBsmB,EACzC,CAKA,eAAAM,CACEN,GAEA,OAAOh6B,KAAK0T,SAAS,iBAAkBsmB,EACzC,CAIA,iBAAAO,CACEP,GAEA,OAAOh6B,KAAK0T,SAAS,mBAAoBsmB,EAC3C,CAKA,OAAAxlB,CACEwlB,GAEA,OAAOh6B,KAAK0T,SAAS,QAASsmB,EAChC,CAuBA,gBAAMQ,GACAx6B,KAAK64B,cACP74B,KAAK4vB,IAAI,mCAKiB,oBAAjB7hB,cACTA,aAAaC,QAAQ,uBAAwB,WAGzChO,KAAKy6B,mBAEPz6B,KAAK05B,WAAa15B,KAAKu5B,gBACzBv5B,KAAK06B,aAGP16B,KAAK64B,eAAgB,EACrB74B,KAAK4vB,IAAI,uCACX,CAEA,YAAA+K,CAAa75B,GACXd,KAAKc,OAASA,EACdd,KAAKy6B,kBACP,CAEA,eAAAG,CAAgBtB,GACdt5B,KAAKs5B,UAAYA,EACjBt5B,KAAK66B,gBACD76B,KAAK05B,WAAa15B,KAAKu5B,gBACzBv5B,KAAK06B,aAEP16B,KAAKy6B,kBACP,CAwBA,gBAAAK,CAAiBC,GACf,IAAKA,EAEH,YADA/6B,KAAK4vB,IAAI,uDAAwD,QAGnE,MAAMoL,EAAKr7B,KAAKC,MAChB,GAA8B,oBAAnBorB,eACT,IACEA,eAAehd,QACb,GAAGyqB,EAAkBwC,4BAA4BF,IACjDv5B,KAAKM,UAAU,CAAEk5B,OAErB,CAAA,MAIA,CAEFh7B,KAAKk7B,8BAA8BF,GACnCh7B,KAAK4vB,IAAI,qBAAqBmL,mBAA0B/6B,KAAK44B,gBAAgB5V,uBAC/E,CAaQ,6BAAAkY,CAA8BC,WACpC,IAAA,MAAW97B,KAAKW,KAAK04B,UAAW,CAC9B,MAAM0C,EAAU,OAAApS,EAAA3pB,EAAEg8B,gBAAF,EAAArS,EAAasS,kCAC7B,GAAuB,iBAAZF,GAAwBA,GAAW,EAAG,SACjD,MAAMG,GAAQ,OAAAnN,EAAA/uB,EAAEg8B,gBAAF,EAAAjN,EAAamN,QAAS,UACtB,YAAVA,GAIFv7B,KAAK4vB,IACH,wCAAwC2L,wDAA4Dl8B,EAAE8B,KACtG,QAGJ,MAAMq6B,EAAYL,EAAwB,IAAVC,EAE1BniB,EAAWjZ,KAAK44B,gBAAgBx3B,IAAI/B,EAAE8B,UAC3B,IAAb8X,GAA0BA,EAAWuiB,IACvCx7B,KAAK44B,gBAAgBv3B,IAAIhC,EAAE8B,GAAIq6B,EAEnC,CACF,CASQ,+BAAAC,GACN,GAA8B,oBAAnBzQ,eAAgC,OAC3C,IAAI0Q,EAA4B,KAChC,IACE,IAAA,IAASzrB,EAAI,EAAGA,EAAI+a,eAAenmB,OAAQoL,IAAK,CAC9C,MAAM/B,EAAM8c,eAAe9c,IAAI+B,GAC/B,IAAK/B,IAAQA,EAAIW,WAAW4pB,EAAkBwC,2BAA4B,SAC1E,MAAMzE,EAAMxL,eAAetc,QAAQR,GACnC,IAAKsoB,EAAK,SACV,MAAMhhB,EAAShU,KAAKC,MAAM+0B,GACD,iBAAdhhB,EAAOwlB,KAChBU,EAA4B,OAAfA,EAAsBlmB,EAAOwlB,GAAKz7B,KAAK2f,IAAIwc,EAAYlmB,EAAOwlB,IAE/E,CACF,CAAA,MAGE,MACF,CACmB,OAAfU,GACF17B,KAAKk7B,8BAA8BQ,EAEvC,CAMQ,YAAAC,CAAaC,GACnB,MAAMJ,EAAYx7B,KAAK44B,gBAAgBx3B,IAAIw6B,GAC3C,gBAAIJ,MACA77B,KAAKC,OAAS47B,KAChBx7B,KAAK44B,gBAAgB7kB,OAAO6nB,IACrB,GAGX,CAEQ,UAAAlB,GAKN,GAJI16B,KAAK67B,aACP77B,KAAK66B,iBAGF76B,KAAKu5B,eAER,YADAv5B,KAAK4vB,IAAI,6CAA8C,QAIzD,MAAMjnB,EAAM,IAAIgI,IAAI,sBAAuB3Q,KAAK2L,SAE1CW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,oBAAqBhM,KAAKu5B,gBAGxBv5B,KAAKs5B,YACPhtB,EAAQ,gBAAkBtM,KAAKs5B,WAGjC,MAAMwC,EAAc,IAAIlrB,gBACxBjM,OAAO+pB,QAAQpiB,GAASzI,QAAQ,EAAEqK,EAAKC,MACrC2tB,EAAYC,OAAO7tB,EAAKC,KAG1BnO,KAAK67B,YAAc,IAAIG,YAAY,GAAGrzB,KAAOmzB,EAAYr8B,cAEzDO,KAAK67B,YAAY73B,iBAAiB,OAAQ,KACxChE,KAAK4vB,IAAI,8BACT5vB,KAAK84B,kBAAoB,IAG3B94B,KAAK67B,YAAY73B,iBAAiB,0BAA4BF,IAC5D,IACE,MAAM0C,EAAOhF,KAAKC,MAAMqC,EAAM0C,MAC9BxG,KAAK4vB,IAAI,oCAAoCppB,EAAKsmB,eAClD9sB,KAAKy6B,kBACP,OAAS95B,GACPX,KAAK4vB,IAAI,4BAA4BjvB,IAAS,QAChD,IAGFX,KAAK67B,YAAY73B,iBAAiB,YAAa,KAC7ChE,KAAK4vB,IAAI,4BAGX5vB,KAAK67B,YAAY73B,iBAAiB,QAAS,WACzChE,KAAK4vB,IAAI,uBAAwB,UAE7B,OAAA5G,EAAAhpB,KAAK67B,kBAAL,EAAA7S,EAAkBiT,cAAeD,YAAYE,QAC/Cl8B,KAAKm8B,oBAGX,CAEQ,aAAAtB,GACF76B,KAAK67B,cACP77B,KAAK67B,YAAYO,QACjBp8B,KAAK67B,iBAAc,EACnB77B,KAAK4vB,IAAI,yBAEb,CAEQ,gBAAAuM,GACN,GAAIn8B,KAAK84B,mBAAqB94B,KAAK+4B,qBAEjC,YADA/4B,KAAK4vB,IAAI,4CAA6C,QAIxD5vB,KAAK84B,oBACL,MAAM1rB,EAAQ7N,KAAK2f,IAAI,IAAO3f,KAAK8N,IAAI,EAAGrN,KAAK84B,mBAAoB,KAEnE94B,KAAK4vB,IAAI,uBAAuBxiB,gBAAoBpN,KAAK84B,sBAEzD1uB,WAAW,KACLpK,KAAK64B,eAAiB74B,KAAK05B,WAAa15B,KAAKu5B,gBAC/Cv5B,KAAK06B,cAENttB,EACL,CAEA,sBAAcqtB,GACZ,IAEE,MAAMxuB,EAAU,IAAI2E,gBAAgB,CAClCyrB,YAAar8B,KAAKs8B,mBAClBC,SAA4B,oBAAXx4B,OAAyBA,OAAO0L,SAAS2C,SAAW,MAIvEnG,EAAQ5K,IAAI,cAAerB,KAAKw8B,YAAc,OAAS,SAEvD,MAAM7zB,EAAM,GAAG3I,KAAK2L,4BAA4BM,EAAQxM,aAElD6M,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,eAAgB,oBAGdhM,KAAKc,SACPwL,EAAQ,aAAetM,KAAKc,QAG1Bd,KAAKs5B,YACPhtB,EAAQ,gBAAkBtM,KAAKs5B,WAG7Bt5B,KAAKu5B,iBACPjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAGlCv5B,KAAKw5B,aACPltB,EAAQ,iBAAmBtM,KAAKw5B,YAIlC,MAAMiD,EAAgBz8B,KAAK08B,mBACvB/3B,OAAOC,KAAK63B,GAAe53B,OAAS,IACtCyH,EAAQ,oBAAsBqwB,KAAKn7B,KAAKM,UAAU26B,KAGpD,MAAMnyB,QAAiBC,MAAM5B,EAAK,CAChC6B,OAAQ,MACR8B,UACAswB,YAAa,YAGf,IAAKtyB,EAASK,GACZ,MAAM,IAAIiB,MAAM,8BAA8BtB,EAASuC,UAGzD,MAAMxB,QAAgBf,EAASoC,OAI/B1M,KAAK04B,UAAYvyB,MAAMC,QAAQiF,GAAWA,EAAU,GAGpDrL,KAAK68B,qBAAqB78B,KAAK04B,WAO/B14B,KAAKy7B,kCAELz7B,KAAK4vB,IAAI,WAAW5vB,KAAK04B,UAAU7zB,oBAKnC7E,KAAK65B,KAAK,mBAAoB75B,KAAK04B,WAMnC14B,KAAKk5B,eAELl5B,KAAK88B,yBAOL98B,KAAK+8B,kBASL/8B,KAAKg9B,oBAEP,OAASr8B,GACPX,KAAKi6B,UAAUt5B,EAAO,CAAEs8B,MAAO,sBAC/Bj9B,KAAK4vB,IAAI,+BAA+BjvB,IAAS,QACnD,CACF,CAIQ,gBAAA27B,GACN,GAAyB,oBAAd52B,UAA2B,MAAO,UAC7C,MAAMsL,EAAKtL,UAAUuL,UACrB,MAAI,gBAAgBnD,KAAKkD,GAAY,SACjC,eAAelD,KAAKkD,GAAY,SAC7B,SACT,CAEQ,SAAAwrB,GACN,MAA4B,oBAAjBzuB,eACHA,aAAaW,QAAQ,uBAC/B,CAIQ,gBAAAguB,GACN,GAA4B,oBAAjB3uB,aAA8B,MAAO,CAAA,EAChD,IACE,OAAOvM,KAAKC,MAAMsM,aAAaW,QAAQ,yBAA2B,KACpE,CAAA,MACE,MAAO,CAAA,CACT,CACF,CAEQ,oBAAAmuB,CAAqBnE,GAC3B,GAA4B,oBAAjB3qB,aAA8B,OACzC,MAAMwH,EAASvV,KAAK08B,mBACpB,IAAA,MAAWlpB,KAAYklB,EACjBllB,EAAS0pB,sBACX3nB,EAAO/B,EAASrS,IAAMqS,EAAS0pB,qBAGnCnvB,aAAaC,QAAQ,uBAAwBxM,KAAKM,UAAUyT,GAC9D,CAEQ,YAAA4nB,CAAavB,GAEnB,OADoB57B,KAAK08B,mBACNd,SAAe,CACpC,CAEQ,sBAAAkB,GASN,MAAMtpB,EAAWxT,KAAK04B,UAAU5vB,KAAMzJ,IACnCW,KAAK24B,mBAAmB/kB,IAAIvU,EAAE8B,MAAQ9B,EAAE+9B,iBAAmBp9B,KAAK27B,aAAat8B,EAAE8B,KAG9EqS,GACFxT,KAAKq9B,gBAAgB7pB,EAEzB,CAoBQ,eAAAupB,GACN,GAAwB,oBAAb74B,SAA0B,OACrC,MAAMo5B,EAAQp5B,SAAS2uB,iBAAiB,qBACxC,GAAqB,IAAjByK,EAAMz4B,OAAc,OAIxB,MAAM04B,MAAyB11B,IAC/B,IAAA,MAAWxI,KAAKW,KAAK04B,UAAW,CAC9B,MAAM8E,EAAQn+B,EAAEo+B,eACVtnB,EAAW9W,EAAEq+B,gBACdF,GAAUA,EAAMtmB,SAAS,mBACzBf,IACAonB,EAAmB3pB,IAAIuC,IAC1BonB,EAAmBl8B,IAAI8U,EAAU9W,IAErC,CACgC,IAA5Bk+B,EAAmBva,MAEvBsa,EAAMz5B,QAAS85B,IACb,GAAI39B,KAAKm5B,YAAYvlB,IAAI+pB,GAAO,OAChC,MAAMzvB,EAAMyvB,EAAKC,aAAa,mBAC9B,IAAK1vB,EAAK,OACV,MAAMsF,EAAW+pB,EAAmBn8B,IAAI8M,GACnCsF,IACLxT,KAAK69B,uBAAuBrqB,EAAUmqB,GACtC39B,KAAKm5B,YAAYtW,IAAI8a,KAEzB,CAuBQ,kBAAAX,GACN,GAAwB,oBAAb94B,SAA0B,OACrC,MAAM45B,EAAU55B,SAAS2uB,iBAAiB,2BACnB,IAAnBiL,EAAQj5B,QAEZi5B,EAAQj6B,QAASyzB,IACf,GAAIt3B,KAAKm5B,YAAYvlB,IAAI0jB,GAAS,OAClC,MAAMd,EAAMc,EAAOsG,aAAa,yBAChC,IAAKpH,EAAK,OAEV,IAAInrB,EAEO,KACX,IACEA,EAAU7J,KAAKC,MAAM+0B,EACvB,OAASpF,GAMP,OALApxB,KAAK4vB,IACH,yDAAyDwB,KACzD,aAEFpxB,KAAKm5B,YAAYtW,IAAIyU,EAEvB,CACA,IAAKjsB,IAAYA,EAAQmI,WAAanI,EAAQmI,SAASrS,GAMrD,OALAnB,KAAK4vB,IACH,iEACA,aAEF5vB,KAAKm5B,YAAYtW,IAAIyU,GAOvBt3B,KAAK69B,uBACHxyB,EAAQmI,SACR8jB,EACA,CAAEyG,UAAW1yB,EAAQ2yB,aAEvBh+B,KAAKm5B,YAAYtW,IAAIyU,IAEzB,CAYQ,sBAAAuG,CACNrqB,EACAgF,EACA7K,GAGA,IADgB3N,KAAK65B,KAAK,qBAAsBrmB,GAK9C,YAHAxT,KAAK4vB,IACH,iBAAiBpc,EAASrS,+CAK9BnB,KAAK24B,mBAAmB9V,IAAIrP,EAASrS,IACrCnB,KAAK6vB,qBAEL,MAAMC,EAAMtc,EAASuc,oBAAsB,CAAA,EACrCK,EAAKpwB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,WACrDzjB,EAAO5M,KAAK2vB,cAAcnc,EAAS+c,YAAc,WACjDwN,EAAY,MAAApwB,OAAA,EAAAA,EAASowB,UAE3B,IAAIE,GAAW,EACf,OAAQzqB,EAAS0qB,UACf,IAAK,cACHD,EAAWj+B,KAAKm+B,qBACd3qB,EAAUsc,EAAIM,EAAIxjB,EAAM4L,EAAQulB,GAElC,MACF,IAAK,aACHE,EAAWj+B,KAAKo+B,oBACd5qB,EAAUsc,EAAIM,EAAIxjB,EAAM4L,EAAQulB,GAElC,MAMF,QAQE,OAPA/9B,KAAK4vB,IACH,6CAA6Cpc,EAAS0qB,UAAY1qB,EAAS3M,OAC3E,aAIF7G,KAAK24B,mBAAmB5kB,OAAOP,EAASrS,IAIvC88B,IACLj+B,KAAK0sB,WAAWlZ,EAASrS,GAAI,cAC7BnB,KAAK65B,KAAK,iBAAkBrmB,GAC9B,CAUQ,oBAAA2qB,CACN3qB,EACAsc,EACAM,EACAxjB,EACA4L,EACAulB,GAEA,MAAMlN,EAAO7wB,KAAKq+B,gBAChB,2BAA4B7qB,EAASrS,GAAIivB,EAAIxjB,GAM/C,OAJAikB,EAAKI,YACHjxB,KAAKs+B,qBAAqB9qB,EAAUsc,EAAIljB,EAAM,OAAQmxB,IAExDvlB,EAAOyY,YAAYJ,IACZ,CACT,CAEQ,mBAAAuN,CACN5qB,EACAsc,EACAM,EACAxjB,EACA4L,EACAulB,GAEA,MAAMlN,EAAO7wB,KAAKq+B,gBAChB,wBAAyB7qB,EAASrS,GAAIivB,EAAIxjB,GAM5C,OAJAikB,EAAKI,YACHjxB,KAAKu+B,oBAAoB/qB,EAAUsc,EAAIljB,EAAM,OAAQmxB,IAEvDvlB,EAAOyY,YAAYJ,IACZ,CACT,CAQQ,oBAAA2N,CACNT,EACA1yB,GAEK0yB,GAELxzB,MAAMwzB,EAAW,CACfvzB,OAAQ,OACR8B,QAAS,CAAE,eAAgB,oBAC3BE,KAAMhL,KAAKM,UAAU,CAAEwI,SAAUe,IACjCoB,WAAW,IACVie,MAAOvL,IACRnf,KAAK4vB,IAAI,wBAAwBzQ,IAAO,SAE5C,CAUQ,eAAAkf,CACN3N,EACAkL,EACAxL,EACAxjB,GAEA,MAAMikB,EAAO3sB,SAASusB,cAAc,OAQpC,OAPAI,EAAKH,UAAYA,EACjBG,EAAKtK,aAAa,mBAAoBqV,GACtC/K,EAAKF,MAAMC,QAAU,kFAELR,aAAcxjB,2DAGvBikB,CACT,CAEQ,oBAAAyN,CACN9qB,EACAsc,EACAljB,EACA6xB,EACAV,GAEA,MAAMW,EAAwB,YAAZD,EACZE,EAAUD,EAAY,OAAS,OAC/BE,EAAYF,EAAY,OAAS,OACjCG,EAAcH,EAAY,MAAQ,MAClCI,EAAcJ,EAAY,OAAS,OACnCK,EAAWL,EAAY,OAAS,OAChCM,EAAoBN,EAAY,OAAS,IACzCO,EAAaP,EAAY,MAAQ,OAEjClyB,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKmkB,MAAMC,QAAU,YAAY+N,yBAEjC,MAAMrsB,EAAQpO,SAASusB,cAAc,OACrCne,EAAMqe,MAAMC,QACV,cAAcgO,mBAA2BC,qBAA+BC,KAC1ExsB,EAAM0e,YAAcxd,EAASlB,OAAS,uBACtC9F,EAAKykB,YAAY3e,GAEjB,MAAM4sB,EAAQh7B,SAASusB,cAAc,OACrCyO,EAAMvO,MAAMC,QACV,qDACuB,MAAtBoO,EAA4B,mBAAmBA,KAAuB,IACzE,MAAMG,EAAYrP,EAAGsP,cAA2B,EAChD,IAAA,IAASnvB,EAAI,EAAGA,GAAKkvB,EAAUlvB,IAAK,CAClC,MAAMovB,EAAOn7B,SAASusB,cAAc,QACpC4O,EAAK1O,MAAMC,QACT,cAAcmO,sEACbnyB,EAAO,WAAWA,KAAU,IAC/ByyB,EAAKrO,YAAc,IACnB,MAAM7iB,EAAQ8B,EACdovB,EAAKr7B,iBAAiB,QAAS,KAC7BhE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WACzB48B,GACF/9B,KAAKw+B,qBAAqBT,EAAW,CACnCG,SAAU,cAAe/vB,YAI/BkxB,EAAKr7B,iBAAiB,aAAc,KAClCq7B,EAAK1O,MAAM2O,UAAY,SAASL,OAElCI,EAAKr7B,iBAAiB,aAAc,KAClCq7B,EAAK1O,MAAM2O,UAAY,aAEzBJ,EAAMjO,YAAYoO,EACpB,CAEA,OADA7yB,EAAKykB,YAAYiO,GACV1yB,CACT,CAEQ,mBAAA+xB,CACN/qB,EACAsc,EACAljB,EACA6xB,EACAV,GAEA,MAAMW,EAAwB,YAAZD,EACZE,EAAUD,EAAY,OAAS,OAG/BlyB,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKmkB,MAAMC,QAAU,YAAY+N,MAC9BD,EAAY,uBAAyB,IAExC,MAAMpsB,EAAQpO,SAASusB,cAAc,OASrC,GARAne,EAAMqe,MAAMC,QACV,iCAAiC8N,EAAY,MAAQ,yBACnCA,EAAY,OAAS,8BACzCpsB,EAAM0e,YACHlB,EAAGyP,cAA2B/rB,EAASlB,OACxC,sCACF9F,EAAKykB,YAAY3e,GAEbosB,EAAW,CAGb,MAAMc,EAAQt7B,SAASusB,cAAc,OACrC+O,EAAM7O,MAAMC,QACV,0FACF,IAAA,IAAS3gB,EAAI,EAAGA,GAAK,GAAIA,IAAK,CAC5B,MAAMwvB,EAAMv7B,SAASusB,cAAc,QACnCgP,EAAI9O,MAAMC,QAAU,iNAG+BhkB,eAAkBA,sDAGrE6yB,EAAIzO,YAAczc,OAAOtE,GACzB,MAAM9B,EAAQ8B,EACdwvB,EAAIz7B,iBAAiB,QAAS,KAC5BhE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WACzB48B,GACF/9B,KAAKw+B,qBAAqBT,EAAW,CACnCG,SAAU,aAAc/vB,YAI9BqxB,EAAMvO,YAAYwO,EACpB,CACAjzB,EAAKykB,YAAYuO,GAEjB,MAAME,EAASx7B,SAASusB,cAAc,OACtCiP,EAAO/O,MAAMC,QACX,qGACF,MAAM+O,EAAYz7B,SAASusB,cAAc,QACzCkP,EAAU3O,YAAc,aACxB,MAAM4O,EAAa17B,SAASusB,cAAc,QAK1C,OAJAmP,EAAW5O,YAAc,cACzB0O,EAAOzO,YAAY0O,GACnBD,EAAOzO,YAAY2O,GACnBpzB,EAAKykB,YAAYyO,GACVlzB,CACT,CAKA,MAAMqzB,EAAO37B,SAASusB,cAAc,OACpCoP,EAAKlP,MAAMC,QACT,mEACF,IAAA,IAAS6F,EAAI,EAAGA,GAAK,GAAIA,IAAK,CAC5B,MAAMgJ,EAAMv7B,SAASusB,cAAc,UACnCgP,EAAI9O,MAAMC,QAAU,mEACsChkB,iDACtBA,wGAGpC6yB,EAAIzO,YAAczc,OAAOkiB,GACzB,MAAMtoB,EAAQsoB,EACdgJ,EAAIz7B,iBAAiB,QAAS,KAC5BhE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WACzB48B,GACF/9B,KAAKw+B,qBAAqBT,EAAW,CACnCG,SAAU,aAAc/vB,YAI9B0xB,EAAK5O,YAAYwO,EACnB,CAEA,OADAjzB,EAAKykB,YAAY4O,GACVrzB,CACT,CAkBA,aAAAszB,CAAclhB,EAAmBmhB,EAAqC,IACpE,IAAA,MAAW1gC,KAAKW,KAAK04B,UACf14B,KAAK24B,mBAAmB/kB,IAAIvU,EAAE8B,KAC7B9B,EAAE+9B,iBAGHp9B,KAAK27B,aAAat8B,EAAE8B,KACpBnB,KAAKggC,qBAAqB3gC,EAAE+9B,eAAgBxe,EAAWmhB,IACzD//B,KAAKq9B,gBAAgBh+B,GAG3B,CAEQ,oBAAA2gC,CACNC,EACArhB,EACAmhB,SAEA,MAAMG,EAAMD,EAAQ96B,QAAU,CAAA,EAC9B,OAAQ86B,EAAQp5B,MACd,IAAK,eACH,MAA4B,iBAAdq5B,EAAIp8B,OAAsBo8B,EAAIp8B,QAAU8a,EACxD,IAAK,gBAAiB,CACpB,GAAkB,mBAAdA,GAAgD,iBAAdA,EACpC,OAAO,EAET,MAAMuhB,EAAYD,EAAIlmB,WAChBomB,EAAmBj6B,MAAMC,QAAQ+5B,GAClCA,EACoB,iBAAdA,EACL,CAACA,GACD,GACN,GAAsB,IAAlBC,EAAOv7B,OAAc,OAAO,EAChC,MAAMw7B,EAAS9rB,OACbwrB,EAAU/lB,YACR+lB,EAAUO,YACT,OAAAtX,EAAA+W,EAAUplB,kBAAiDxZ,KAC5D,IAEJ,OAAOi/B,EAAOlpB,SAASmpB,EACzB,CACA,QAEE,OAAO,EAEb,CAEQ,eAAAhD,CAAgB7pB,GAOtB,IADgBxT,KAAK65B,KAAK,qBAAsBrmB,GAG9C,YADAxT,KAAK4vB,IAAI,YAAYpc,EAASrS,+CAIhCnB,KAAK24B,mBAAmB9V,IAAIrP,EAASrS,IAGrC,MAAMo/B,MAA0Bve,IAAI,CAClC,aAAc,eAAgB,aAAc,OAC5C,kBAAmB,cAAe,eAGpC,GAAIxO,EAAS0qB,UAAYqC,EAAoB3sB,IAAIJ,EAAS0qB,UAIxD,OAHAl+B,KAAKwgC,kBAAkBhtB,GACvBxT,KAAK0sB,WAAWlZ,EAASrS,GAAI,mBAC7BnB,KAAK65B,KAAK,iBAAkBrmB,GAI9B,OAAQA,EAAS3M,MACf,IAAK,QACH7G,KAAKygC,YAAYjtB,GACjB,MACF,IAAK,SACHxT,KAAK0gC,aAAaltB,GAClB,MACF,IAAK,cACHxT,KAAK2gC,iBAAiBntB,GACtB,MACF,IAAK,oBACHxT,KAAK4gC,uBAAuBptB,GAC5B,MACF,IAAK,QACHxT,KAAK6gC,YAAYrtB,GACjB,MACF,IAAK,MACHxT,KAAK8gC,UAAUttB,GACf,MACF,IAAK,UACHxT,KAAK+gC,cAAcvtB,GACnB,MAIF,IAAK,iBAIH,OAHAic,EAAoBzvB,KAAKghC,mBAAmBxtB,IAC5CxT,KAAK0sB,WAAWlZ,EAASrS,GAAI,mBAC7BnB,KAAK65B,KAAK,iBAAkBrmB,GAE9B,IAAK,aAIH,OH3xCD,SAAyBoZ,GAC9B,MAAMpZ,SAAEA,EAAAkZ,WAAUA,EAAAgD,YAAYA,gBAAaC,EAAAC,IAAeA,EAAAC,mBAAKA,GAAuBjD,EAChFkD,EAAMtc,EAASuc,oBAAsB,CAAA,EAErCxV,EAAkC,QAAvBuV,EAAGmR,gBAA4B,MAAQ,SAClDC,GAAwC,IAA1BpR,EAAGqR,mBACjBC,EAAW9c,OAAOwL,EAAGuR,sBAAwB,EAInD,IACE,GAC0B,oBAAjBtzB,cACPA,aAAaW,QAAQ6kB,EAAyB/f,EAASrS,IAGvD,YADAyuB,EAAI,cAAcpc,EAASrS,yCAG/B,CAAA,MAEA,CAEA0uB,IAEA,MAAMO,EAAKT,EACRG,EAAGwR,iBAA+B9tB,EAAS6c,kBAA+B,WAEvEC,EAAKX,EAAenc,EAAS+c,YAAyB,WAEtD0D,EAAM/vB,SAASusB,cAAc,OACnCwD,EAAIvD,UAAY,0BAChBuD,EAAI1N,aAAa,mBAAoB/S,EAASrS,IAE9C,MAAMogC,EACS,QAAbhnB,EACI,sEACA,iFAEN0Z,EAAItD,MAAMC,QAAU,0BACC2Q,sBACLnR,aAAcE,qOAIC,QAAb/V,EAAqB,MAAQ,mCAG/C,MAAM2Z,EAAQhwB,SAASusB,cAAc,OACrCyD,EAAMvD,MAAMC,QAAU,yEACtB,MAAM4Q,EAASt9B,SAASusB,cAAc,UAOtC,GANA+Q,EAAOxQ,YAAcxd,EAASlB,MAC9BkvB,EAAO7Q,MAAMC,QAAU,uCACvBsD,EAAMjD,YAAYuQ,GAClBtN,EAAMjD,YAAY/sB,SAASu9B,eAAejuB,EAAShH,OACnDynB,EAAIhD,YAAYiD,GAEZ1gB,EAASkuB,YAAcluB,EAASmuB,YAAa,CAC/C,MAAMzP,EAAMhuB,SAASusB,cAAc,UACnCyB,EAAIlB,YAAcxd,EAASmuB,YAC3BzP,EAAIvB,MAAMC,QAAU,uBACJN,aAAcF,yJAI9B8B,EAAIluB,iBAAiB,QAAS,KAC5B0oB,EAAWlZ,EAASrS,GAAI,WACxB,MAAMywB,EAAOlC,EAAYlc,EAASkuB,YAC9B9P,IAAM7tB,OAAO0L,SAAS8C,KAAOqf,KAEnCqC,EAAIhD,YAAYiB,EAClB,CAEA,IAAI0P,EAAsD,KAC1D,MAAM3/B,EAAU4/B,IAEd,GADID,gBAA4BA,GAC5BC,EACF,IAC8B,oBAAjB9zB,cACTA,aAAaC,QAAQulB,EAAyB/f,EAASrS,GAAI,IAE/D,CAAA,MAEA,CAEF8yB,EAAIhyB,UAGN,GAAIi/B,EAAa,CACf,MAAM9E,EAAQl4B,SAASusB,cAAc,UACrC2L,EAAMpL,YAAc,IACpBoL,EAAM7V,aAAa,aAAc,WACjC6V,EAAMzL,MAAMC,QAAU,+IAItBwL,EAAMp4B,iBAAiB,QAAS,KAC9B0oB,EAAWlZ,EAASrS,GAAI,aACxBc,GAAO,KAETgyB,EAAIhD,YAAYmL,EAClB,CAEIgF,EAAW,IACbQ,EAAgBx3B,WAAW,IAAMnI,GAAO,GAAQm/B,IAGlDl9B,SAASsI,KAAKykB,YAAYgD,EAC5B,CG6qCQ6N,CAAgB9hC,KAAKghC,mBAAmBxtB,IACxCxT,KAAK0sB,WAAWlZ,EAASrS,GAAI,mBAC7BnB,KAAK65B,KAAK,iBAAkBrmB,GAE9B,IAAK,eAIH,OAHAggB,EAAkBxzB,KAAKghC,mBAAmBxtB,IAC1CxT,KAAK0sB,WAAWlZ,EAASrS,GAAI,mBAC7BnB,KAAK65B,KAAK,iBAAkBrmB,GAE9B,IAAK,iBAKH,OAFAwiB,GAAoBh2B,KAAKghC,mBAAmBxtB,SAC5CxT,KAAK65B,KAAK,iBAAkBrmB,GAE9B,IAAK,yBAIH,OC5xCD,SAAqCoZ,GAC1C,MAAMpZ,SAAEA,EAAAkZ,WAAUA,EAAAgD,YAAYA,gBAAaC,EAAAC,IAAeA,EAAAC,mBAAKA,GAAuBjD,EAChFkD,EAAMtc,EAASuc,oBAAsB,CAAA,EAGrCC,GADW7pB,MAAMC,QAAQ0pB,EAAGE,OAAUF,EAAGE,MAAmB,IAC3CnwB,MAAM,EAPV,IAQnB,GAAqB,IAAjBmwB,EAAMnrB,OAER,YADA+qB,EAAI,gEAAiE,QAIvE,MAAMmS,EAAUjS,EAAGkS,YAAyB,OACtCC,EAAcnS,EAAGoS,cAA2B,WAElDrS,IAEA,MAAMO,EAAKT,EAAenc,EAAS6c,kBAA+B,WAC5DC,EAAKX,EAAenc,EAAS+c,YAAyB,WACtD4R,EAASxS,EAAc,WAEvBa,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,2BACpBF,EAAQjK,aAAa,mBAAoB/S,EAASrS,IAClDqvB,EAAQG,MAAMC,QAAU,qOAOxB,MAAMwR,EAAQl+B,SAASusB,cAAc,OACrC2R,EAAMzR,MAAMC,QAAU,qBACNR,aAAcE,6UAU9B,MAAM+R,EAASn+B,SAASusB,cAAc,OACtC4R,EAAO1R,MAAMC,QAAU,qEACyCN,qCAGhE8R,EAAMnR,YAAYoR,GAElB,MAAMvR,EAAS5sB,SAASusB,cAAc,OACtCK,EAAOH,MAAMC,QAAU,qFAEvB,MAAMG,EAAa7sB,SAASusB,cAAc,OACpCne,EAAQpO,SAASusB,cAAc,OACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yDACtB,MAAMpkB,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,mDACrBG,EAAWE,YAAY3e,GACvBye,EAAWE,YAAYzkB,GACvBskB,EAAOG,YAAYF,GAEnB,MAAMqL,EAAQl4B,SAASusB,cAAc,UACrC2L,EAAMpL,YAAc,IACpBoL,EAAM7V,aAAa,aAAc,SACjC6V,EAAMzL,MAAMC,QAAU,0IAItBwL,EAAMp4B,iBAAiB,QAAS,KAC9B0oB,EAAWlZ,EAASrS,GAAI,aACxBqvB,EAAQvuB,WAEV6uB,EAAOG,YAAYmL,GACnBgG,EAAMnR,YAAYH,GAElB,MAAM+O,EAAO37B,SAASusB,cAAc,OAElCoP,EAAKlP,MAAMC,QADE,QAAXmR,GAA+B,aAAXA,EACD,uHAKA,2GAMvB/R,EAAMnsB,QAASxE,IACb,MAAMoyB,EAAOvtB,SAASusB,cAAc,OAC9B6R,EAAmB,QAAXP,GAA+B,aAAXA,EASlC,GARAtQ,EAAKd,MAAMC,QAAU,uBACLN,mHAEJjxB,EAAEqyB,QAAU,UAAY,qBAChC4Q,EAAQ,6CAA+C,oDAIvDjjC,EAAEwb,UAAW,CACf,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7BmB,EAAOlC,EAAYrwB,EAAEwb,WACvB+W,IACFD,EAAIE,IAAMD,EACVD,EAAIG,IAAM,GACVH,EAAII,QAAU,OACdJ,EAAIhB,MAAMC,QAAU,2EACpBa,EAAKR,YAAYU,GAErB,CAEA,GAAItyB,EAAEiT,MAAO,CACX,MAAM0f,EAAI9tB,SAASusB,cAAc,OACjCuB,EAAEhB,YAAc3xB,EAAEiT,MAClB0f,EAAErB,MAAMC,QAAU,uDAClBa,EAAKR,YAAYe,EACnB,CAIA,MAAM9X,EAAQ7a,EAAEkjC,UAAkC,iBAAfljC,EAAEkjC,SAAyBljC,EAAEkjC,SAAqCroB,WAAQ,EAC7G,QAAc,IAAVA,EAAqB,CACvB,MAAML,EAAI3V,SAASusB,cAAc,OACjC5W,EAAEmX,YAAczc,OAAO2F,GACvBL,EAAE8W,MAAMC,QAAU,UAAUuR,wCAC5B1Q,EAAKR,YAAYpX,EACnB,MAAA,GAAWxa,EAAEmN,KAAM,CACjB,MAAM1C,EAAI5F,SAASusB,cAAc,OACjC3mB,EAAEknB,YAAc3xB,EAAEmN,KAClB1C,EAAE6mB,MAAMC,QAAU,sDAClBa,EAAKR,YAAYnnB,EACnB,CAEA,MAAMooB,EAAMhuB,SAASusB,cAAc,UACnCyB,EAAIlB,YAAc3xB,EAAE4yB,UAAYgQ,EAChC/P,EAAIvB,MAAMC,QAAU,gDAEJuR,iJAIhB,MAAMK,EAAMpR,IAGV,GAFAA,EAAEgB,kBACF1F,EAAWlZ,EAASrS,GAAI,WACpB9B,EAAEqyB,QAAS,CACb,MAAME,EAAOlC,EAAYrwB,EAAEqyB,SACvBE,IAAM7tB,OAAO0L,SAAS8C,KAAOqf,EACnC,GAEFM,EAAIluB,iBAAiB,QAASw+B,GAC9B/Q,EAAKR,YAAYiB,GACb7yB,EAAEqyB,SAASD,EAAKztB,iBAAiB,QAASw+B,GAE9C3C,EAAK5O,YAAYQ,KAGnB2Q,EAAMnR,YAAY4O,GAClBrP,EAAQS,YAAYmR,GAEpB5R,EAAQxsB,iBAAiB,QAAUotB,IAC7BA,EAAE5Y,SAAWgY,IACf9D,EAAWlZ,EAASrS,GAAI,aACxBqvB,EAAQvuB,YAIZiC,SAASsI,KAAKykB,YAAYT,EAC5B,CDgnCQiS,CAA4BziC,KAAKghC,mBAAmBxtB,IACpDxT,KAAK0sB,WAAWlZ,EAASrS,GAAI,mBAC7BnB,KAAK65B,KAAK,iBAAkBrmB,GAIhCxT,KAAK0sB,WAAWlZ,EAASrS,GAAI,cAC7BnB,KAAK65B,KAAK,iBAAkBrmB,EAC9B,CAQQ,kBAAAwtB,CAAmBxtB,GACzB,MAAO,CACLA,WACAkZ,WAAY,CAACvrB,EAAIuhC,KACV1iC,KAAK0sB,WAAWvrB,EAAIuhC,IAE3BhT,YAAc/mB,GAAgB3I,KAAK0vB,YAAY/mB,GAC/CgnB,cAAgBgT,GAAkB3iC,KAAK2vB,cAAcgT,GACrD/S,IAAK,CAACgT,EAAaC,IAAqC7iC,KAAK4vB,IAAIgT,EAAKC,GACtEhT,mBAAoB,IAAM7vB,KAAK6vB,qBAEnC,CAOQ,iBAAA2Q,CAAkBhtB,GACxB,MAAMsc,EAAMtc,EAASuc,oBAAsB,CAAA,EACrCK,EAAKpwB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,WACrDzjB,EAAO5M,KAAK2vB,cAAcnc,EAAS+c,YAAc,WAEvD,OAAQ/c,EAAS0qB,UACf,IAAK,aACHl+B,KAAK8iC,gBAAgBtvB,EAAUsc,EAAIM,EAAIxjB,GACvC,MACF,IAAK,kBACH5M,KAAK+iC,qBAAqBvvB,EAAUsc,EAAIM,EAAIxjB,GAC5C,MACF,IAAK,cACH5M,KAAKgjC,iBAAiBxvB,EAAUsc,EAAIM,EAAIxjB,GACxC,MACF,IAAK,aACH5M,KAAKijC,gBAAgBzvB,EAAUsc,EAAIM,EAAIxjB,GACvC,MACF,IAAK,OACH5M,KAAKkjC,WAAW1vB,EAAUsc,EAAIM,EAAIxjB,GAClC,MACF,IAAK,aACL,IAAK,eACC5M,KAAK25B,sBACP35B,KAAK25B,sBAAsBnmB,GAE3BxT,KAAK4vB,IACH,GAAGpc,EAAS0qB,0GACZ,QAGJ,MACF,QACEl+B,KAAK4vB,IAAI,iCAAiCpc,EAAS0qB,WAAY,QAC/Dl+B,KAAKygC,YAAYjtB,GAEvB,CAIQ,eAAAsvB,CACNtvB,EACAsc,EACAM,EACAxjB,GAEA,MAAM4jB,EAAUxwB,KAAKmjC,cAAc,4BAC7BC,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAMzS,MAAMC,QAAU,mGAENR,aAAcxjB,+FAI9B,MAAMJ,EAAOxM,KAAKu+B,oBAAoB/qB,EAAUsc,EAAIljB,EAAM,WAC1D5M,KAAKqjC,eAAe72B,EAAMgkB,EAAShd,EAASrS,IAC5CiiC,EAAMnS,YAAYzkB,GAClBgkB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,oBAAAuS,CACNvvB,EACAsc,EACAM,EACAxjB,GAEA,MAAM4jB,EAAUxwB,KAAKmjC,cAAc,kCAC7BC,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAMzS,MAAMC,QAAU,mGAENR,aAAcxjB,+FAI9B,MAAMJ,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKmkB,MAAMC,QAAU,qCAErB,MAAMte,EAAQpO,SAASusB,cAAc,OACrCne,EAAMqe,MAAMC,QAAU,yDACtBte,EAAM0e,YAAcxd,EAASlB,OAAS,aACtC9F,EAAKykB,YAAY3e,GAEjB,MAAM4hB,EAAQhwB,SAASusB,cAAc,OACrCyD,EAAMvD,MAAMC,QAAU,sDACtBsD,EAAMlD,YAAelB,EAAGwT,iBAA8B,gBACtD92B,EAAKykB,YAAYiD,GAGjB,MAAMqP,EAASr/B,SAASusB,cAAc,OACtC8S,EAAO5S,MAAMC,QAAU,yEACvB,MAAM4S,EAAa,iHAAiH52B,OACpI,IAAA,MAAW62B,IAAO,CAAC,KAAM,IAAK,KAAM,IAAK,MAAO,CAC9C,MAAMzQ,EAAK9uB,SAASusB,cAAc,QAEhCuC,EAAGrC,MAAMC,QADC,MAAR6S,EACiB,yDAEAD,EAErBxQ,EAAGhC,YAAcyS,EACjBF,EAAOtS,YAAY+B,EACrB,CACAxmB,EAAKykB,YAAYsS,GAGjB,MAAMG,EAAY5T,EAAG6T,iBACrB,GAAID,EAAW,CACb,MAAMlrB,EAAS,IAAI7Y,KAAK+jC,GAAWr0B,UAC7BolB,EAAS,KACb,MAAMmP,EAAOrkC,KAAKwZ,IAAI,EAAGP,EAAS7Y,KAAKC,OACjCikC,EAAItvB,OAAOhV,KAAK4Z,MAAMyqB,EAAO,OAAUxqB,SAAS,EAAG,KACnDoH,EAAIjM,OAAOhV,KAAK4Z,MAAOyqB,EAAO,KAAW,MAAQxqB,SAAS,EAAG,KAC7DoL,EAAIjQ,OAAOhV,KAAK4Z,MAAOyqB,EAAO,IAAS,MAAOxqB,SAAS,EAAG,KAC1D0qB,EAAQP,EAAO1Q,iBAAiB,QAClCiR,EAAMj/B,QAAU,IAClBi/B,EAAM,GAAG9S,YAAc6S,EACvBC,EAAM,GAAG9S,YAAcxQ,EACvBsjB,EAAM,GAAG9S,YAAcxM,GAErBof,EAAO,GAAGG,sBAAsBtP,IAEtCA,GACF,CAEA,GAAIjhB,EAAShH,KAAM,CACjB,MAAMw3B,EAAO9/B,SAASusB,cAAc,OACpCuT,EAAKrT,MAAMC,QAAU,uDACrBoT,EAAKhT,YAAcxd,EAAShH,KAC5BA,EAAKykB,YAAY+S,EACnB,CAEA,GAAIxwB,EAASmuB,YAAa,CACxB,MAAMlC,EAAMz/B,KAAKikC,gBAAgBzwB,EAAU4c,EAAIxjB,GAC/CJ,EAAKykB,YAAYwO,EACnB,CAEAz/B,KAAKqjC,eAAe72B,EAAMgkB,EAAShd,EAASrS,IAC5CiiC,EAAMnS,YAAYzkB,GAClBgkB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,gBAAAwS,CACNxvB,EACAsc,EACAM,EACAxjB,GAEA,MAAM4jB,EAAUxwB,KAAKmjC,cAAc,+BAC7BC,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAMzS,MAAMC,QAAU,mGAENR,aAAcxjB,+FAI9B,MAAMJ,EAAOxM,KAAKs+B,qBAAqB9qB,EAAUsc,EAAIljB,EAAM,WAC3D5M,KAAKqjC,eAAe72B,EAAMgkB,EAAShd,EAASrS,IAC5CiiC,EAAMnS,YAAYzkB,GAClBgkB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,eAAAyS,CACNzvB,EACAsc,EACAM,EACAxjB,GAEA,MAAM4jB,EAAUxwB,KAAKmjC,cAAc,6BAC7BC,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAMzS,MAAMC,QAAU,mGAENR,aAAcxjB,+FAI9B,MAAMJ,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKmkB,MAAMC,QAAU,qCAErB,MAAMte,EAAQpO,SAASusB,cAAc,OACrCne,EAAMqe,MAAMC,QAAU,0DACtBte,EAAM0e,YAAcxd,EAASlB,OAAS,iBACtC9F,EAAKykB,YAAY3e,GAEjB,MAAM3E,EAAWmiB,EAAGoU,cAA6B,GAC3CC,EAAcjgC,SAASusB,cAAc,OAC3C0T,EAAYxT,MAAMC,QAAU,wEAC5B,IAAA,MAAWwT,KAAOz2B,EAAS,CACzB,MAAM02B,EAASngC,SAASusB,cAAc,UACtC4T,EAAO1T,MAAMC,QAAU,wEACwChkB,iDAC3BA,wGAGpCy3B,EAAOrT,YAAcoT,EACrBC,EAAOrgC,iBAAiB,QAAS,KAC/BhE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAE/BgjC,EAAYlT,YAAYoT,EAC1B,CACA73B,EAAKykB,YAAYkT,GAEjBnkC,KAAKqjC,eAAe72B,EAAMgkB,EAAShd,EAASrS,IAC5CiiC,EAAMnS,YAAYzkB,GAClBgkB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,UAAA0S,CACN1vB,EACAsc,EACAM,EACAxjB,GAEA,MAAM4jB,EAAUxwB,KAAKmjC,cAAc,6BAC7BC,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAMzS,MAAMC,QAAU,mGAENR,aAAcxjB,+FAI9B,MAAMJ,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKmkB,MAAMC,QAAU,qCAErB,MAAMte,EAAQpO,SAASusB,cAAc,OACrCne,EAAMqe,MAAMC,QAAU,yDACtBte,EAAM0e,YAAcxd,EAASlB,OAAS,OACtC9F,EAAKykB,YAAY3e,GAEjB,MAAMgyB,EAAaxU,EAAGwU,WAAgE,GACtF,IAAIC,EAAW,EAEf,MAAMC,EAAiB,KAErB,KAAOh4B,EAAKi4B,WAAW5/B,OAAS,GAC9B2H,EAAKk4B,YAAYl4B,EAAKm4B,WAGxB,GAAIJ,GAAYD,EAAUz/B,OAAQ,CAChC,MAAMoC,EAAS/C,SAASusB,cAAc,OAMtC,OALAxpB,EAAO0pB,MAAMC,QAAU,mCACvB3pB,EAAO+pB,YAAelB,EAAG8U,mBAAgC,kCACzDp4B,EAAKykB,YAAYhqB,GACjBjH,KAAK0sB,WAAWlZ,EAASrS,GAAI,gBAC7BnB,KAAKqjC,eAAe72B,EAAMgkB,EAAShd,EAASrS,GAE9C,CAEA,MAAM0jC,EAAIP,EAAUC,GACdO,EAAW5gC,SAASusB,cAAc,OACxCqU,EAASnU,MAAMC,QAAU,sDACzBkU,EAAS9T,YAAc,YAAYuT,EAAW,QAAQD,EAAUz/B,SAChE2H,EAAKykB,YAAY6T,GAEjB,MAAMC,EAAe7gC,SAASusB,cAAc,OAC5CsU,EAAapU,MAAMC,QAAU,0DAC7BmU,EAAa/T,YAAc6T,EAAEG,SAC7Bx4B,EAAKykB,YAAY8T,GAEjB,MAAME,EAAa/gC,SAASusB,cAAc,OAC1CwU,EAAWtU,MAAMC,QAAU,mDAC3B,IAAA,MAAWwT,KAAOS,EAAEl3B,QAAS,CAC3B,MAAM02B,EAASngC,SAASusB,cAAc,UACtC4T,EAAO1T,MAAMC,QAAU,0EACwChkB,mDAC3BA,4GAGpCy3B,EAAOrT,YAAcoT,EACrBC,EAAOrgC,iBAAiB,QAAS,KAC/BugC,IACAC,MAEFS,EAAWhU,YAAYoT,EACzB,CACA73B,EAAKykB,YAAYgU,GACjBjlC,KAAKqjC,eAAe72B,EAAMgkB,EAAShd,EAASrS,KAG9CqjC,IACApB,EAAMnS,YAAYzkB,GAClBgkB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAIQ,aAAA2S,CAAczS,GACpB,MAAMF,EAAUtsB,SAASusB,cAAc,OAOvC,OANAD,EAAQE,UAAYA,EACpBF,EAAQG,MAAMC,QAAU,iOAKjBJ,CACT,CAEQ,eAAAyT,CAAgBzwB,EAAyB4c,EAAYxjB,GAC3D,MAAM6yB,EAAMv7B,SAASusB,cAAc,UAcnC,OAbAgP,EAAI9O,MAAMC,QAAU,wKAGJhkB,aAAgBwjB,wCAEhCqP,EAAIzO,YAAcxd,EAASmuB,aAAe,KAC1ClC,EAAIz7B,iBAAiB,QAAS,KAE5B,GADAhE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WACzBqS,EAASkuB,WAAY,CACvB,MAAMwD,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GAASnhC,OAAOohC,KAAKD,EAAS,SACpC,IAEKzF,CACT,CAEQ,cAAA4D,CAAe+B,EAAwB5U,EAAsBoL,GACnE,MAAMQ,EAAQl4B,SAASusB,cAAc,OACrC2L,EAAMzL,MAAMC,QAAU,oEACtBwL,EAAMpL,YAAc,QACpBoL,EAAMp4B,iBAAiB,QAAS,KAC9BhE,KAAK0sB,WAAWkP,EAAY,aAC5B57B,KAAKqlC,YAAY7U,KAEnB4U,EAAUnU,YAAYmL,EACxB,CAoBQ,wBAAAkJ,CACN9xB,EACA0xB,EACAK,EAAmB,OAEnB,GAAsB,oBAAXxhC,OAAwB,OAMnC,IAAIyhC,GAAwB,EAC5B,MAAMC,EAAiC,CACrCjyB,WACAkuB,WAAYwD,EACZQ,UAAWH,EACXI,eAAgB,KACdH,GAAwB,EACxBC,EAAWG,kBAAmB,GAEhCA,kBAAkB,GAEdC,EAAe7lC,KAAK65B,KAAK,iBAAkB4L,GAM3CK,EAAW,IAAIC,YAAY,uBAAwB,CACvDC,OAAQ,CACNlZ,YAAatZ,EAASrS,GACtB8kC,cAAezyB,EAAS3M,KACxB66B,WAAYwD,EACZQ,UAAWH,GAEbW,YAAY,IAERC,EAAapiC,OAAOqiC,cAAcN,IAMrCD,GACDL,IACCW,GACDL,EAASF,mBAGT7hC,OAAO0L,SAAS8C,KAAO2yB,EAE3B,CAEQ,YAAAxE,CAAaltB,GACnB,MAAM6yB,EAASniC,SAASusB,cAAc,OACtC4V,EAAO3V,UAAY,sBACnB2V,EAAO9f,aAAa,mBAAoB/S,EAASrS,IAEjDklC,EAAO1V,MAAMC,QAAU,+FAKP5wB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,6BACrDrwB,KAAK2vB,cAAcnc,EAAS+c,YAAc,gVAWrD,MAAM+V,EAAmBpiC,SAASusB,cAAc,OAGhD,GAFA6V,EAAiB3V,MAAMC,QAAU,0DAE7Bpd,EAASqH,UAAW,CACtB,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAYlc,EAASqH,WACtCqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,oEACpB0V,EAAiBrV,YAAYU,GAEjC,CAEA,MAAM4U,EAAgBriC,SAASusB,cAAc,OAC7C8V,EAAc5V,MAAMC,QAAU,WAE9B,MAAMte,EAAQpO,SAASusB,cAAc,OACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yDACtB2V,EAActV,YAAY3e,GAE1B,MAAM9F,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,iCACrB2V,EAActV,YAAYzkB,GAE1B85B,EAAiBrV,YAAYsV,GAE7B,MAAMC,EAAmBtiC,SAASusB,cAAc,OAGhD,GAFA+V,EAAiB7V,MAAMC,QAAU,oEAE7Bpd,EAASkuB,YAAcluB,EAASmuB,YAAa,CAC/C,MAAM8E,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAcxd,EAASmuB,YACjC8E,EAAU9V,MAAMC,QAAU,gDAEf5wB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,mNAU3DoW,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WAC7B,MAAM+jC,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GACFllC,KAAKslC,yBAAyB9xB,EAAU0xB,GAE1CllC,KAAK0mC,aAAaL,KAGpBG,EAAiBvV,YAAYwV,EAC/B,CAEA,MAAME,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,wSAe5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAC7BnB,KAAK0mC,aAAaL,KAGpBG,EAAiBvV,YAAY0V,GAE7BN,EAAOpV,YAAYqV,GACnBD,EAAOpV,YAAYuV,GAEnBxmC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYoV,EAC5B,CAEQ,WAAA5F,CAAYjtB,GAClB,MAAMgd,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,6BACpBF,EAAQjK,aAAa,mBAAoB/S,EAASrS,IAElDqvB,EAAQG,MAAMC,QAAU,kSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OAcrC,GAbA2S,EAAM1S,UAAY,qBAClB0S,EAAMzS,MAAMC,QAAU,+UAYlBpd,EAASqH,UAAW,CACtB,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAYlc,EAASqH,WACtCqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,6EACpBwS,EAAMnS,YAAYU,GAEtB,CAEA,MAAMzE,EAAUhpB,SAASusB,cAAc,OACvCvD,EAAQyD,MAAMC,QAAU,iBAExB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yEACtB1D,EAAQ+D,YAAY3e,GAEpB,MAAM9F,EAAOtI,SAASusB,cAAc,KACpCjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,yEACrB1D,EAAQ+D,YAAYzkB,GAEpB,MAAMo6B,EAAU1iC,SAASusB,cAAc,OAGvC,GAFAmW,EAAQjW,MAAMC,QAAU,uDAEpBpd,EAASkuB,YAAcluB,EAASmuB,YAAa,CAC/C,MAAM8E,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAcxd,EAASmuB,YACjC8E,EAAU9V,MAAMC,QAAU,yBACV5wB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,+BACrDrwB,KAAK2vB,cAAcnc,EAAS+c,YAAc,sLASrDkW,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WAC7B,MAAM+jC,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GACFllC,KAAKslC,yBAAyB9xB,EAAU0xB,GAE1CllC,KAAKqlC,YAAY7U,KAGnBoW,EAAQ3V,YAAYwV,EACtB,CAEA,MAAME,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,QAC1B2V,EAAYhW,MAAMC,QAAU,iOAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAC7BnB,KAAKqlC,YAAY7U,KAGnBoW,EAAQ3V,YAAY0V,GAEpBzZ,EAAQ+D,YAAY2V,GACpBxD,EAAMnS,YAAY/D,GAClBsD,EAAQS,YAAYmS,GAEpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,gBAAAmQ,CAAiBntB,GACvB,MAAMgd,EAAUtsB,SAASusB,cAAc,OAuBvC,GAtBAD,EAAQE,UAAY,kCACpBF,EAAQjK,aAAa,mBAAoB/S,EAASrS,IAElDqvB,EAAQG,MAAMC,QAAU,iHAMR5wB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,6BACrDrwB,KAAK2vB,cAAcnc,EAAS+c,YAAc,sVAYjD/c,EAASqH,UAAW,CACtB,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAYlc,EAASqH,WACtCqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,+EACpBJ,EAAQS,YAAYU,GAExB,CAEA,MAAM2U,EAAmBpiC,SAASusB,cAAc,OAChD6V,EAAiB3V,MAAMC,QAAU,wCAEjC,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yDACtB0V,EAAiBrV,YAAY3e,GAE7B,MAAM9F,EAAOtI,SAASusB,cAAc,KAKpC,GAJAjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,uEACrB0V,EAAiBrV,YAAYzkB,GAEzBgH,EAASkuB,YAAcluB,EAASmuB,YAAa,CAC/C,MAAM8E,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAcxd,EAASmuB,YACjC8E,EAAU9V,MAAMC,QAAU,yBACV5wB,KAAK2vB,cAAcnc,EAAS+c,YAAc,+BAC/CvwB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,oNAU3DoW,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WAC7B,MAAM+jC,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GACFllC,KAAKslC,yBAAyB9xB,EAAU0xB,GAE1CllC,KAAKqlC,YAAY7U,KAGnB8V,EAAiBrV,YAAYwV,EAC/B,CAEAjW,EAAQS,YAAYqV,GAEpB,MAAMK,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,yWAkB5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAC7BnB,KAAKqlC,YAAY7U,KAGnBA,EAAQS,YAAY0V,GAEpB3mC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,sBAAAoQ,CAAuBptB,GAC7B,MAAMgd,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,yCACpBF,EAAQjK,aAAa,mBAAoB/S,EAASrS,IAElDqvB,EAAQG,MAAMC,QAAU,oSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OAcrC,GAbA2S,EAAM1S,UAAY,iCAClB0S,EAAMzS,MAAMC,QAAU,0VAYlBpd,EAASqH,UAAW,CACtB,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAYlc,EAASqH,WACtCqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,iDACpBwS,EAAMnS,YAAYU,GAEtB,CAEA,MAAMzE,EAAUhpB,SAASusB,cAAc,OACvCvD,EAAQyD,MAAMC,QAAU,sBAExB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yEACtB1D,EAAQ+D,YAAY3e,GAEpB,MAAM9F,EAAOtI,SAASusB,cAAc,KAKpC,GAJAjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,yEACrB1D,EAAQ+D,YAAYzkB,GAEhBgH,EAASkuB,YAAcluB,EAASmuB,YAAa,CAC/C,MAAM8E,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAcxd,EAASmuB,YACjC8E,EAAU9V,MAAMC,QAAU,yBACV5wB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,+BACrDrwB,KAAK2vB,cAAcnc,EAAS+c,YAAc,4MAUrDkW,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WAC7B,MAAM+jC,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GACFllC,KAAKslC,yBAAyB9xB,EAAU0xB,GAE1CllC,KAAKqlC,YAAY7U,KAGnBtD,EAAQ+D,YAAYwV,EACtB,CAEArD,EAAMnS,YAAY/D,GAElB,MAAMyZ,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,iZAkB5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAC7BnB,KAAKqlC,YAAY7U,KAGnB4S,EAAMzS,MAAMpW,SAAW,WACvB6oB,EAAMnS,YAAY0V,GAElBnW,EAAQS,YAAYmS,GAEpBpjC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,WAAAqQ,CAAYrtB,GAClB,MAAMgd,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,6BACpBF,EAAQjK,aAAa,mBAAoB/S,EAASrS,IAElDqvB,EAAQG,MAAMC,QAAU,kSAcxB,MAAMiW,EAAQ3iC,SAASusB,cAAc,OACrCoW,EAAMnW,UAAY,qBAClBmW,EAAMlW,MAAMC,QAAU,yTAWtB,MAAM1D,EAAUhpB,SAASusB,cAAc,OACvCvD,EAAQyD,MAAMC,QAAU,0CAExB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,wEACtB1D,EAAQ+D,YAAY3e,GAEpB,MAAM9F,EAAOtI,SAASusB,cAAc,KACpCjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,6DACrB1D,EAAQ+D,YAAYzkB,GAEpBq6B,EAAM5V,YAAY/D,GAElB,MAAM4Z,EAAkB5iC,SAASusB,cAAc,OAG/C,GAFAqW,EAAgBnW,MAAMC,QAAU,gDAE5Bpd,EAASkuB,YAAcluB,EAASmuB,YAAa,CAC/C,MAAM8E,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAcxd,EAASmuB,YACjC8E,EAAU9V,MAAMC,QAAU,gQAY1B6V,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,WAC7B,MAAM+jC,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GACFllC,KAAKslC,yBAAyB9xB,EAAU0xB,GAE1CllC,KAAKqlC,YAAY7U,KAGnBsW,EAAgB7V,YAAYwV,EAC9B,CAEA,MAAMM,EAAe7iC,SAASusB,cAAc,UAC5CsW,EAAa/V,YAAc,SAC3B+V,EAAapW,MAAMC,QAAU,iMAW7BmW,EAAa/iC,iBAAiB,QAAS,KACrChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAC7BnB,KAAKqlC,YAAY7U,KAGnBsW,EAAgB7V,YAAY8V,GAE5BF,EAAM5V,YAAY6V,GAClBtW,EAAQS,YAAY4V,GAEpB7mC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,SAAAsQ,CAAUttB,GAChB,MAAMwzB,EAAM9iC,SAASusB,cAAc,OAkBnC,GAjBAuW,EAAItW,UAAY,mBAChBsW,EAAIzgB,aAAa,mBAAoB/S,EAASrS,IAE9C6lC,EAAIrW,MAAMC,QAAU,0XAchBpd,EAASyzB,UAAW,CACtB,MAAMC,EAAQhjC,SAASusB,cAAc,SAC/ByU,EAAUllC,KAAK0vB,YAAYlc,EAASyzB,WACtC/B,IACFgC,EAAMrV,IAAMqT,EACZgC,EAAMnP,UAAW,EACjBmP,EAAMjX,UAAW,EACjBiX,EAAMC,OAAQ,EACdD,EAAMvW,MAAMC,QAAU,+BACtBoW,EAAI/V,YAAYiW,GAEpB,MAAA,GAAW1zB,EAASqH,UAAW,CAC7B,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAYlc,EAASqH,WACtCqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,+BACpBoW,EAAI/V,YAAYU,GAEpB,CAEA,MAAMnB,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQG,MAAMC,QAAU,sNAUxB,MAAMte,EAAQpO,SAASusB,cAAc,OACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,yDACtBJ,EAAQS,YAAY3e,GAEpB,MAAM9F,EAAOtI,SAASusB,cAAc,OACpCjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,iCACrBJ,EAAQS,YAAYzkB,GAEpBw6B,EAAI/V,YAAYT,GAEhB,MAAMmW,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,4VAiB5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aAC7B6lC,EAAIrW,MAAMyW,UAAY,+BACtBh9B,WAAW,KACL48B,EAAIK,YACNL,EAAIK,WAAW3C,YAAYsC,IAE5B,OAGLA,EAAI/V,YAAY0V,GAEZnzB,EAASkuB,aACXsF,EAAIrW,MAAM2W,OAAS,UACnBN,EAAIhjC,iBAAiB,QAAUotB,IAC7B,GAAIA,EAAE5Y,SAAWmuB,EAAa,CAC5B3mC,KAAK0sB,WAAWlZ,EAASrS,GAAI,WAC7B,MAAM+jC,EAAUllC,KAAK0vB,YAAYlc,EAASkuB,YACtCwD,GACFnhC,OAAOohC,KAAKD,EAAS,SAEzB,KAIJllC,KAAK6vB,qBACL3rB,SAASsI,KAAKykB,YAAY+V,EAC5B,CAEQ,YAAAN,CAAaL,GACnBA,EAAO1V,MAAMyW,UAAY,6BACzBh9B,WAAW,KACLi8B,EAAOgB,YACThB,EAAOgB,WAAW3C,YAAY2B,IAE/B,IACL,CAEQ,WAAAhB,CAAY7U,GAClBA,EAAQG,MAAMyW,UAAY,6BAC1Bh9B,WAAW,KACLomB,EAAQ6W,YACV7W,EAAQ6W,WAAW3C,YAAYlU,IAEhC,IACL,CAEQ,aAAAuQ,CAAcvtB,GACpB,MAAMsc,EAAMtc,EAASuc,oBAAsB,CAAA,EACrCwX,EAAkBzX,EAAG0X,yBAAsC,uBAC3DC,EAAqB3X,EAAG4X,kBAA+B,SAEvDpQ,EAASpzB,SAASqzB,cAAcgQ,GACtC,IAAKjQ,EAEH,YADAt3B,KAAK4vB,IAAI,6BAA6B2X,IAAkB,QAI1D,MAAMnX,EAAKpwB,KAAK2vB,cAAcnc,EAAS6c,kBAAoB,WACrDsX,EAAY3nC,KAAK2vB,cAAcnc,EAAS+c,YAAc,WAGtDqX,EAAU1jC,SAASusB,cAAc,OACvCmX,EAAQlX,UAAY,uBACpBkX,EAAQrhB,aAAa,mBAAoB/S,EAASrS,IAClDymC,EAAQjX,MAAMC,QAAU,0GAERR,aAAcuX,wPAM9B,MAAME,EAAQ3jC,SAASusB,cAAc,OACrCoX,EAAMnX,UAAY,sBAClBmX,EAAMlX,MAAMC,QAAU,sEACyCR,4CAK/D,MAAMc,EAAWhtB,SAASusB,cAAc,UACxCS,EAASF,YAAc,IACvBE,EAASP,MAAMC,QAAU,mGAEd+W,uFAGX,MAAMG,EAAU,KACdF,EAAQjX,MAAMyW,UAAY,6BAC1Bh9B,WAAW,WAAQ,OAAA4e,EAAA4e,EAAQP,eAAY3C,YAAYkD,IAAa,KAChE5nC,KAAK0sB,WAAWlZ,EAASrS,GAAI,cAM/B,GAHA+vB,EAASltB,iBAAiB,QAAUotB,IAAQA,EAAEgB,kBAAmB0V,MAG7Dt0B,EAASlB,MAAO,CAClB,MAAMA,EAAQpO,SAASusB,cAAc,OACrCne,EAAM0e,YAAcxd,EAASlB,MAC7BA,EAAMqe,MAAMC,QAAU,8EACtBgX,EAAQ3W,YAAY3e,EACtB,CAGA,MAAM9F,EAAOtI,SAASusB,cAAc,OAMpC,GALAjkB,EAAKwkB,YAAcxd,EAAShH,KAC5BA,EAAKmkB,MAAMC,QAAU,mDACrBgX,EAAQ3W,YAAYzkB,GAGhBgH,EAASmuB,aAAenuB,EAASkuB,WAAY,CAC/C,MAAMxP,EAAMhuB,SAASusB,cAAc,KACnCyB,EAAIlB,YAAcxd,EAASmuB,YAC3BzP,EAAI3f,KAAOiB,EAASkuB,WACpBxP,EAAIvB,MAAMC,QAAU,wGAET+W,0DAEXzV,EAAIluB,iBAAiB,QAAS,KAAQhE,KAAK0sB,WAAWlZ,EAASrS,GAAI,aACnEymC,EAAQ3W,YAAYiB,EACtB,CAEA0V,EAAQ3W,YAAYC,GACpB0W,EAAQ3W,YAAY4W,GACpB3jC,SAASsI,KAAKykB,YAAY2W,GAG1B,MAAMG,EAAazQ,EAAOG,wBACpBuQ,EAAcJ,EAAQnQ,wBACtBwQ,EAAUlkC,OAAOkkC,QACjBC,EAAUnkC,OAAOmkC,QAGvB,IAAIxQ,EAAM,EACNC,EAAO,EACPwQ,EAAW,GACXC,EAAY,GACZC,EAAc,GACdC,EAAa,GAEjB,OAAQb,GACN,IAAK,MACH/P,EAAMqQ,EAAWrQ,IAAMwQ,EAAUF,EAAY90B,OAXrC,GAYRykB,EAAOoQ,EAAWpQ,KAAOsQ,GAAWF,EAAW90B,MAAQ+0B,EAAY/0B,OAAS,EAC5Eo1B,EAAc,OACdD,EAAeJ,EAAY/0B,MAAQ,EAAI,EAA3B,KACZ,MACF,IAAK,OACHykB,EAAMqQ,EAAWrQ,IAAMwQ,GAAWH,EAAW70B,OAAS80B,EAAY90B,QAAU,EAC5EykB,EAAOoQ,EAAWpQ,KAAOsQ,EAAUD,EAAY/0B,MAlBvC,GAmBRq1B,EAAa,OACbH,EAAcH,EAAY90B,OAAS,EAAI,EAA5B,KACX,MACF,IAAK,QACHwkB,EAAMqQ,EAAWrQ,IAAMwQ,GAAWH,EAAW70B,OAAS80B,EAAY90B,QAAU,EAC5EykB,EAAOoQ,EAAWzP,MAAQ2P,EAxBlB,GAyBRG,EAAY,OACZD,EAAcH,EAAY90B,OAAS,EAAI,EAA5B,KACX,MACF,QACEwkB,EAAMqQ,EAAW1P,OAAS6P,EA7BlB,GA8BRvQ,EAAOoQ,EAAWpQ,KAAOsQ,GAAWF,EAAW90B,MAAQ+0B,EAAY/0B,OAAS,EAC5Ek1B,EAAW,OACXC,EAAeJ,EAAY/0B,MAAQ,EAAI,EAA3B,KAKhB0kB,EAAOp4B,KAAKwZ,IAAI,EAAGxZ,KAAK2f,IAAIyY,EAAM5zB,OAAOuP,WAAa20B,EAAUD,EAAY/0B,MAAQ,IACpFykB,EAAMn4B,KAAKwZ,IAAI,EAAG2e,GAElBkQ,EAAQjX,MAAM+G,IAAM,GAAGA,MACvBkQ,EAAQjX,MAAMgH,KAAO,GAAGA,MACxBkQ,EAAMlX,MAAM+G,IAAMyQ,GAAY,GAC9BN,EAAMlX,MAAMgH,KAAOyQ,GAAa,GAChCP,EAAMlX,MAAM0H,OAASgQ,GAAe,GACpCR,EAAMlX,MAAM2H,MAAQgQ,GAAc,GAGlC,MAAMC,EAAuBnX,IACtBwW,EAAQY,SAASpX,EAAE5Y,SAAoB8e,EAAOkR,SAASpX,EAAE5Y,UAC5DtU,SAASimB,oBAAoB,QAASoe,GACtCT,MAGJ19B,WAAW,IAAMlG,SAASF,iBAAiB,QAASukC,GAAsB,IAC5E,CAEQ,kBAAA1Y,GACN,GAAI3rB,SAASukC,eAAe,uBAC1B,OAGF,MAAM9X,EAAQzsB,SAASusB,cAAc,SACrCE,EAAMxvB,GAAK,sBACXwvB,EAAMK,YAAc,wjCAmCpB9sB,SAASwkC,KAAKzX,YAAYN,EAC5B,CAEQ,WAAAjB,CAAY/mB,GAClB,IACE,MAAMggC,EAAY,IAAIh4B,IAAIhI,EAAK5E,OAAO0L,SAASm5B,QAE/C,MAA2B,gBAAvBD,EAAUj5B,UAAqD,UAAvBi5B,EAAUj5B,UACpD1P,KAAK4vB,IAAI,uBAAuBjnB,IAAO,SAChC,MAGFggC,EAAUp2B,IACnB,OAAS5R,GAEP,OADAX,KAAK4vB,IAAI,gBAAgBjnB,IAAO,SACzB,IACT,CACF,CAEQ,aAAAgnB,CAAcgT,GACpB,GAAI,sBAAsB70B,KAAK60B,GAC7B,OAAOA,EAGT,GAAI,yCAAyC70B,KAAK60B,GAChD,OAAOA,EAGT,GAAI,uDAAuD70B,KAAK60B,GAC9D,OAAOA,EAIT,MADoB,CAAC,QAAS,QAAS,MAAO,QAAS,OAAQ,SAAU,SAAU,SAAU,OAAQ,OAAQ,eAC7FzrB,SAASyrB,EAAM3e,eACtB2e,EAGF,SACT,CAcA,gBAAcjW,CAAWkP,EAAoBiN,SAO3C,GAAkB,cAAdA,EAA2B,CAC7B,MAAMr1B,EAAWxT,KAAK04B,UAAU5vB,KAAMzJ,GAAMA,EAAE8B,KAAOy6B,GACjDpoB,GACFxT,KAAK65B,KAAK,mBAAoB,CAC5BrmB,WACA2H,OAAQ,gBAGd,CAEA,IAME,MAAMxS,EAAM,GAAG3I,KAAK2L,2BACdm9B,EAAkB,SAASlN,KAAciN,KAAalpC,KAAKC,QAE3D0M,EAAkC,CACtC,eAAgB,mBAChB,oBAAqBtM,KAAKgM,UAExBhM,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBACzDv5B,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAOnD,MAAMyP,EAAc,OAAA/f,OAAK4Q,qBAAL,EAAA5Q,EAAAggB,KAAAhpC,MAEdwM,EAAO,CACXsgB,YAAa8O,EACbqN,WAAYJ,EACZK,QAASlpC,KAAKc,OACdqoC,WAAYnpC,KAAKs5B,UACjB8P,aAAcppC,KAAKc,OACnBuoC,SAAU,MACVxsC,aAAcksC,EACdxG,SAAU,CACR+G,YAAatpC,KAAKw5B,WAClBnf,WAAYra,KAAKm9B,aAAavB,SAAe,GAE/C2N,gBAAiBT,GAGnBv+B,MAAM5B,EAAK,CACT6B,OAAQ,OACR8B,UACAE,KAAMhL,KAAKM,UAAU0K,GACrBowB,YAAa,YACZlS,MAAM/pB,IACPX,KAAK4vB,IAAI,yBAAyBjvB,IAAS,WAG7CX,KAAK4vB,IAAI,WAAWiZ,wBAAgCjN,IACtD,OAASj7B,GACPX,KAAK4vB,IAAI,yBAAyBjvB,IAAS,QAC7C,CACF,CAEQ,GAAAivB,CAAItvB,EAAiBuiC,EAAkC,OACzD7iC,KAAKy5B,WACPh5B,QAAQoiC,GAAO,gBAAgBviC,IAEnC,CAEA,OAAA0E,CAAQ2I,GAGN,GAFA3N,KAAK66B,gBAEmB,oBAAb32B,SAA0B,CACnCA,SAAS2uB,iBACP,qXAMAhvB,QAAQmvB,IACJA,EAAGqU,YACLrU,EAAGqU,WAAW3C,YAAY1R,KAI9B,MAAMwW,EAAStlC,SAASukC,eAAe,uBACnCe,GAAUA,EAAOnC,YACnBmC,EAAOnC,WAAW3C,YAAY8E,EAElC,EAGI,MAAA77B,OAAA,EAAAA,EAAS87B,eAAwC,oBAAjB17B,cAClCA,aAAaE,WAAW,wBAG1BjO,KAAK64B,eAAgB,EACrB74B,KAAK4vB,IAAI,uBACX,GAniFA6I,GAAwBwC,0BAA4B,cAxB/C,IAAMyO,GAANjR,GE7BA,MAAMkR,GAuBX,WAAA5pC,CAAYoF,GATZnF,KAAQ4pC,QAA0B,GAClC5pC,KAAQ6pC,oBAAsB7nB,IAC9BhiB,KAAQ64B,eAAgB,EACxB74B,KAAQ8pC,aAAc,EAGtB9pC,KAAQ+pC,sBAA8C,CAAA,EAIpD/pC,KAAKgM,SAAW7G,EAAO6G,SACvBhM,KAAK2L,QAAUxG,EAAOwG,SAAW,uBAKjC3L,KAAKc,OAASqE,EAAOrE,QArKzB,WACE,GAAwB,oBAAboD,SACX,IAEE,OADgB,IAAIwJ,GACLtM,IAAI,iBAAc,CACnC,CAAA,MACE,MACF,CACF,CA6JmCi4B,GAC/Br5B,KAAKs5B,UAAYn0B,EAAOm0B,UACxBt5B,KAAKu5B,eAAiBp0B,EAAOo0B,eAC7Bv5B,KAAKy5B,UAAYt0B,EAAOs0B,YAAa,EACrCz5B,KAAKgqC,cAAgB7kC,EAAO6kC,cAC5BhqC,KAAKiqC,mBAAiD,IAA7B9kC,EAAO8kC,kBAChCjqC,KAAKkqC,gBAA2C,IAA1B/kC,EAAO+kC,eAC7BlqC,KAAKmqC,iBAAmBhlC,EAAOglC,kBAAoB,CAAA,EACnDnqC,KAAKoqC,QAAUjlC,EAAOilC,QACtBpqC,KAAKqqC,eAAiBllC,EAAOklC,eAC7BrqC,KAAK45B,eAAiBz0B,EAAOy0B,cAC/B,CAEA,sBAAA0Q,CAAuBC,GACrBvqC,KAAKmqC,iBAAmB,IAAKnqC,KAAKmqC,oBAAqBI,GACvDvqC,KAAK4vB,IAAI,4BACX,CASA,qBAAMgL,CAAgBtB,GAChBt5B,KAAKs5B,YAAcA,IACvBt5B,KAAKs5B,UAAYA,EACjBt5B,KAAK4vB,IAAI,wBAAwB0J,GAAa,eAC1Ct5B,KAAK64B,gBAAkB74B,KAAK8pC,aAAe9pC,KAAKkqC,gBAAkB5Q,UAC9Dt5B,KAAKwqC,uBACXxqC,KAAKyqC,uBAET,CAYA,yBAAAC,CAA0Bl3B,GAOxB,GAAIxT,KAAK8pC,YAAa,OACtB,GAAwB,oBAAb5lC,SAA0B,OACrC,MAAMymC,EAAUn3B,EAAS0qB,SACzB,GAAgB,eAAZyM,GAAwC,iBAAZA,EAE9B,YADA3qC,KAAK4vB,IAAI,oDAAoD+a,MAAY,GAG3E,MAAMC,EAAuB,CAC3BC,UAAWr3B,EAASrS,GACpB2pC,YAAaH,EACb17B,KAAMuE,EAASlB,MACfnN,OAASqO,EAASuc,oBAAsB,CAAA,EACxCjlB,SAAU0I,EAAS1I,UAAY,GAE7B9K,KAAK6pC,gBAAgBj2B,IAAIg3B,EAAOC,aACpC7qC,KAAK6pC,gBAAgBhnB,IAAI+nB,EAAOC,WAChB,eAAZF,EACF3qC,KAAK+qC,gBAAgBH,GAErB5qC,KAAKgrC,kBAAkBJ,GAE3B,CAQA,OAAA5lC,GACE,IAAIhF,KAAK8pC,YAAT,CAOA,GANA9pC,KAAK8pC,aAAc,EAEf9pC,KAAKgqC,eAAiBhqC,KAAKiqC,mBAC7BjqC,KAAKgqC,cAAciB,OAGG,oBAAb/mC,SAA0B,CACnC,MAAMgnC,EAAiBhnC,SAASukC,eAAe,2BAC/C,MAAAyC,GAAAA,EAAgBjpC,SAChBiC,SAAS2uB,iBAAiB,4BAA4BhvB,QAASsnC,IAC7DA,EAAKlpC,UAET,CAEAjC,KAAK6pC,gBAAgBriC,QACrBxH,KAAK4pC,QAAU,GACf5pC,KAAK64B,eAAgB,EACrB74B,KAAK4vB,IAAI,0BACT5vB,KAAKorC,UAAU,YAAa,GAnBN,CAoBxB,CAQQ,SAAAA,CAAUvC,EAAmBriC,GACnC,GAAIxG,KAAKoqC,QACP,IACEpqC,KAAKoqC,QAAQvB,EAAWriC,EAC1B,OAAS7F,GACPX,KAAK4vB,IAAI,4BAA4BjvB,KAAS,EAChD,CAEJ,CAEA,gBAAM65B,GACAx6B,KAAK64B,cACP74B,KAAK4vB,IAAI,qCAIP5vB,KAAKkqC,gBAAkBlqC,KAAKs5B,kBACxBt5B,KAAKwqC,uBACXxqC,KAAKyqC,6BAGDzqC,KAAKqrC,eAEXrrC,KAAKsrC,yBAELtrC,KAAKurC,wBAELvrC,KAAKwrC,8BAELxrC,KAAK64B,eAAgB,EACrB74B,KAAK4vB,IAAI,yCACX,CAEA,WAAA6b,CAAYC,GACV1rC,KAAK0rC,SAAWA,EAChB1rC,KAAK4vB,IAAI,sBAAsB8b,EAASxW,WAAWrwB,wBAAwB6mC,EAASC,iBAAiBD,EAASvW,aAChH,CAEQ,kBAAAyW,GACN,MAAMC,EAAc7rC,KAAK8rC,uBACzB,GAAID,EAEF,OADA7rC,KAAK4vB,IAAI,6CACFic,EAGT,MAAME,EAAU/rC,KAAKgsC,mBACrB,GAAID,EAEF,OADA/rC,KAAK4vB,IAAI,+CACFmc,EAGT,MAAME,EAAcjsC,KAAKksC,uBACzB,OAAID,GACFjsC,KAAK4vB,IAAI,0CACFqc,GAGLjsC,KAAK0rC,UACP1rC,KAAK4vB,IAAI,gCACF5vB,KAAK0rC,UAGP,IACT,CAEQ,oBAAAI,SACN,IACE,GAAsB,oBAAX/nC,OAAwB,OAAO,KAE1C,MAAM6wB,EAAM7wB,OAEZ,GAAI,OAAAilB,EAAA4L,EAAIC,cAAJ,EAAA7L,EAAa5M,SAAU,CACzB,MAAMA,EAAWwY,EAAIC,QAAQzY,SAC7B,MAAO,CACLH,QAASG,EAASsL,OAAS,WAAW/nB,KAAKC,QAC3Cu1B,WAAYJ,WAAW3Y,EAAS4Y,cAAgB,EAChD2W,cAAevvB,EAASjC,UAAY,MACpC+a,YAAa9Y,EAAS+vB,YAAc,IAAI9iC,IAAKiF,IAAA,CAC3C0L,WAAYzF,OAAOjG,EAAK0L,YAAc1L,EAAKnN,IAC3CirC,aAAc99B,EAAKgE,OAAShE,EAAK+9B,cACjCvyB,SAAUxL,EAAKwL,UAAY,EAC3BI,MAAO6a,WAAWzmB,EAAK4L,QAAU,KAGvC,CAEA,MAAMoyB,EAAapoC,SAASukC,eAAe,aAC3C,SAAI6D,WAAYtb,YAAa,CAC3B,MAAMhV,EAAOxa,KAAKC,MAAM6qC,EAAWtb,aACnC,MAAO,CACL/U,QAASD,EAAK0L,OAAS,WAAW/nB,KAAKC,QACvCu1B,YAAanZ,EAAKgZ,aAAe,GAAK,IACtC2W,cAAe3vB,EAAK7B,UAAY,MAChC+a,YAAalZ,EAAKsZ,OAAS,IAAIjsB,IAAKiF,IAAA,CAClC0L,WAAYzF,OAAOjG,EAAK0L,YAAc1L,EAAKnN,IAC3CirC,aAAc99B,EAAK+9B,eAAiB/9B,EAAKgE,MACzCwH,SAAUxL,EAAKwL,UAAY,EAC3BI,OAAQ5L,EAAK4L,OAAS,GAAK,OAGjC,CAEA,OAAO,IACT,OAASvZ,GAEP,OADAX,KAAK4vB,IAAI,iCAAiCjvB,KAAS,GAC5C,IACT,CACF,CAEQ,gBAAAqrC,GACN,IACE,GAAsB,oBAAXjoC,OAAwB,OAAO,KAE1C,MAAM6wB,EAAM7wB,OAEZ,GAAI6wB,EAAIK,WAAY,CAClB,MAAMjZ,EAAO4Y,EAAIK,WACjB,MAAO,CACLhZ,QAASD,EAAKC,SAAW,OAAOtc,KAAKC,QACrCu1B,WAAYJ,WAAW/Y,EAAKmZ,aAAe,EAC3CwW,cAAe3vB,EAAK2vB,eAAiB,MACrCzW,YAAalZ,EAAKkZ,YAAc,IAAI7rB,IAAKiF,IAAA,CACvC0L,WAAYzF,OAAOjG,EAAK0L,YACxBoyB,aAAc99B,EAAK89B,aACnBtyB,SAAUxL,EAAKwL,UAAY,EAC3BI,MAAO6a,WAAWzmB,EAAK4L,QAAU,KAGvC,CAEA,OAAO,IACT,OAASvZ,GAEP,OADAX,KAAK4vB,IAAI,qCAAqCjvB,KAAS,GAChD,IACT,CACF,CAEQ,oBAAAurC,GACN,IACE,GAAsB,oBAAXnoC,SAA2BA,OAAOgK,aAAc,OAAO,KAElE,MAAMw+B,EAAex+B,aAAaW,QAAQ,sBAC1C,IAAK69B,EAAc,OAAO,KAE1B,MAAMC,EAAYhrC,KAAKC,MAAM8qC,GAC7B,KAAK,MAAAC,OAAA,EAAAA,EAAWxwB,MAAM,OAAO,KAE7B,MAAMA,EAAOwwB,EAAUxwB,KAEvB,MAAO,CACLC,QAASD,EAAKywB,UAAYzwB,EAAK7a,IAAM,WAAWxB,KAAKC,QACrDu1B,WAAYJ,WAAW/Y,EAAKqZ,gBAAkBrZ,EAAK0wB,UAAY,GAC/Df,cAAe3vB,EAAK2wB,cAAgB,MACpCzX,YAAalZ,EAAKsZ,OAAS,IAAIjsB,IAAKiF,IAAA,CAClC0L,WAAYzF,OAAOjG,EAAKs+B,SAAWt+B,EAAK0L,YACxCoyB,aAAc99B,EAAK89B,cAAgB99B,EAAKW,KACxC6K,SAAUxL,EAAKu+B,KAAO,EACtB3yB,MAAO6a,WAAWzmB,EAAKw+B,qBAAuBx+B,EAAK4L,OAAS,MAGlE,OAASvZ,GAEP,OADAX,KAAK4vB,IAAI,iCAAiCjvB,KAAS,GAC5C,IACT,CACF,CAEQ,mBAAA8pC,GACN,IACE,MAAMsC,EAAmB/sC,KAAK+pC,sBAAsBiD,YACpD,SAAID,WAAkB9sC,QAAS,CAC7B,MAAMgtC,EAAYF,EAAyBlyB,UAC3C,GAAIoyB,EAAU,EACA,IAAIC,OACZrb,IAAMob,EACVjtC,KAAK4vB,IAAI,iCAAiCqd,IAC5C,CACF,CAEA,MAAME,EAAkBntC,KAAK+pC,sBAAsBqD,WACnD,SAAID,WAAiBltC,QAAS,CAC5B,MAAMgtC,EAAYE,EAAwBtyB,UAC1C,GAAIoyB,EAAU,EACA,IAAIC,OACZrb,IAAMob,EACVjtC,KAAK4vB,IAAI,gCAAgCqd,IAC3C,CACF,CACF,OAAStsC,GACPX,KAAK4vB,IAAI,mCAAmCjvB,KAAS,EACvD,CACF,CAEA,0BAAc6pC,GACZ,IACE,MAAMrnC,EAAY6G,YAAYpK,MACxB+I,EAAM,GAAG3I,KAAK2L,qCAEdW,EAAkC,CACtC,oBAAqBtM,KAAKgM,UAGxBhM,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAE7D,MAAMjvB,QAAiBC,MAAM5B,EAAK,CAAE2D,UAASswB,YAAa,YAE1D,IAAKtyB,EAASK,GACZ,MAAM,IAAIiB,MAAM,qCAAqCtB,EAASuC,UAGhE7M,KAAK+pC,4BAA8Bz/B,EAASoC,OAE5C,MAAM2gC,EAAUrjC,YAAYpK,MAAQuD,EACpCnD,KAAK4vB,IAAI,sCAAsCyd,EAAQ5X,QAAQ,OAEjE,OAAS90B,GACPX,KAAK4vB,IAAI,oCAAoCjvB,KAAS,EACxD,CACF,CAEA,kBAAc0qC,GACZ,IACE,MAAM1iC,EAAM,GAAG3I,KAAK2L,4BAEdW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,oBAAqB,MACrB,gBAAiBhM,KAAKstC,iBAGpBttC,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBACzDv5B,KAAKqqC,iBAAgB/9B,EAAQ,qBAAuBtM,KAAKqqC,gBAE7D,MAAM//B,QAAiBC,MAAM5B,EAAK,CAAE2D,UAASswB,YAAa,YAE1D,IAAKtyB,EAASK,GACZ,MAAM,IAAIiB,MAAM,4BAA4BtB,EAASuC,UAGvD7M,KAAK4pC,cAAgBt/B,EAASoC,OAE9B1M,KAAK4vB,IAAI,WAAW5vB,KAAK4pC,QAAQ/kC,iBAEnC,OAASlE,GACPX,KAAK4vB,IAAI,2BAA2BjvB,KAAS,EAC/C,CACF,CAEQ,sBAAA2qC,GACmBtrC,KAAK4pC,QAAQzgC,WACnCokC,EAAEC,eAA0C,cAAzBD,EAAEC,cAAc3mC,MAGrBhD,QAAQ+mC,GAAU5qC,KAAKytC,aAAa7C,GACvD,CAEQ,qBAAAW,GACDvrC,KAAKgqC,cAKVhqC,KAAK4pC,QAAQ/lC,QAAQ+mC,IACnB,IAAKA,EAAO4C,cAAe,OAE3B,MAAM3mC,KAAEA,EAAA1B,OAAMA,GAAWylC,EAAO4C,cAEhC,OAAQ3mC,GACN,IAAK,cACH7G,KAAKgqC,cAAe0D,qBACpB1tC,KAAKgqC,cAAe9P,GAAG,cAAe,KAC/Bl6B,KAAK6pC,gBAAgBj2B,IAAIg3B,EAAOC,YACnC7qC,KAAKytC,aAAa7C,KAGtB,MAEF,IAAK,eACH,MAAM+C,SAAgBxoC,WAAgByoC,gBAAiB,GACvD5tC,KAAKgqC,cAAe6D,oBAAoBF,GACxC3tC,KAAKgqC,cAAe9P,GAAG,gBAAgByT,IAAgB,KAChD3tC,KAAK6pC,gBAAgBj2B,IAAIg3B,EAAOC,YACnC7qC,KAAKytC,aAAa7C,KAGtB,MAEF,IAAK,eACH,MAAMxP,SAAWj2B,WAAgBi2B,UAAW,GAC5Cp7B,KAAKgqC,cAAe8D,mBAAmB1S,GACvCp7B,KAAKgqC,cAAe9P,GAAG,gBAAgBkB,IAAW,KAC3Cp7B,KAAK6pC,gBAAgBj2B,IAAIg3B,EAAOC,YACnC7qC,KAAKytC,aAAa7C,QAlC1B5qC,KAAK4vB,IAAI,6DAwCb,CAEQ,2BAAA4b,GACN,IAAKxrC,KAAKkqC,iBAAmBlqC,KAAKgqC,cAChC,OAGF,MAAMmD,EAAkBntC,KAAK+pC,sBAAsBqD,WAC7CL,EAAmB/sC,KAAK+pC,sBAAsBiD,YAEpD,GAAIG,GAAmBA,EAAgBltC,QAAS,CAC9C,MAAM8tC,EAAW/tC,KAAKguC,iBAChBC,GAAe,MAAAlB,OAAA,EAAAA,EAAkBmB,kBAAmB,CAAA,EAEpDC,EAAwB,KAC5B,MAAMC,EAAW,uBAAuBzuC,KAAKC,QAE7C,GAAII,KAAK6pC,gBAAgBj2B,IAAIw6B,GAC3B,OAGFpuC,KAAK6pC,gBAAgBhnB,IAAIurB,GAEzB,MAAMC,EAAeruC,KAAK4rC,qBAEG,kBAAzBuB,EAAgBtmC,MAA4BwnC,GAC9CruC,KAAK0rC,SAAW2C,EAChBruC,KAAKsuC,sBAAsBF,EAAUjB,GACrCntC,KAAKuuC,6BAC6B,aAAzBpB,EAAgBtmC,MACzB7G,KAAKsuC,sBAAsBF,EAAUjB,IAIzC,GAAIY,EAAU,EACmD,IAAjCE,EAAaO,kBAEzCxuC,KAAKgqC,cAAcyE,uBAAuB,CACxC9a,UAAWsa,EAAaS,kBAAoB,GAC5CC,kBAAmBV,EAAaW,qBAAuB,IACvDC,SAAUZ,EAAaa,iBAAmB,MAE5C9uC,KAAKgqC,cAAc9P,GAAG,qBAAsBiU,GAC5CnuC,KAAK4vB,IAAI,6DAIX,IADqD,IAA5Bqe,EAAac,WAChB,CACpB,MAAMC,EAAcf,EAAagB,cAAgB,GACjDjvC,KAAKgqC,cAAckF,mBAAmBF,GACtChvC,KAAKgqC,cAAc9P,GAAG,cAAc8U,IAAeb,GACnDnuC,KAAK4vB,IAAI,wDAAwDof,KACnE,CAGA,IADmE,IAAnCf,EAAakB,kBAChB,CAC3B,MAAMd,EAAeruC,KAAK4rC,qBACtByC,GAAgBA,EAAanZ,YAAcmZ,EAAanZ,WAAWrwB,OAAS,IAC9E7E,KAAKgqC,cAAcoF,2BACnBpvC,KAAKgqC,cAAc9P,GAAG,oBAAqBiU,GAC3CnuC,KAAK4vB,IAAI,8EAEb,EAEuD,IAA7Bqe,EAAaoB,cAErCrvC,KAAKgqC,cAAcsF,qBACnBtvC,KAAKgqC,cAAc9P,GAAG,cAAeiU,GACrCnuC,KAAK4vB,IAAI,yDAGX5vB,KAAK4vB,IAAI,iCAAiCud,EAAgBtmC,kBAAkBsmC,EAAgBriC,WAC9F,MACE9K,KAAKgqC,cAAc0D,qBACnB1tC,KAAKgqC,cAAc9P,GAAG,cAAeiU,GACrCnuC,KAAK4vB,IAAI,8CAA8Cud,EAAgBtmC,kBAAkBsmC,EAAgBriC,YAG3G,MACF,CAEA,IAAKiiC,IAAqBA,EAAiB9sC,QAEzC,YADAD,KAAK4vB,IAAI,2CAIX,MAAMme,EAAW/tC,KAAKguC,iBAChBC,EAAelB,EAAiBmB,iBAAmB,CAAA,EAEnDqB,EAAmB,KACvB,MAAMnB,EAAW,wBAAwBzuC,KAAKC,QAE9C,GAAII,KAAK6pC,gBAAgBj2B,IAAIw6B,GAC3B,OAGFpuC,KAAK6pC,gBAAgBhnB,IAAIurB,GAEzB,MAAMC,EAAeruC,KAAK4rC,qBAEI,kBAA1BmB,EAAiBlmC,MAA4BwnC,GAC/CruC,KAAK0rC,SAAW2C,EAChBruC,KAAKwvC,wBAAwBpB,EAAUrB,GACvC/sC,KAAKuuC,6BAC8B,aAA1BxB,EAAiBlmC,KAC1B7G,KAAKyvC,mBAAmBrB,EAAUrB,GAElC/sC,KAAK4vB,IAAI,kDAIb,GAAIme,EAAU,EACmD,IAAjCE,EAAaO,kBAEzCxuC,KAAKgqC,cAAcyE,uBAAuB,CACxC9a,UAAWsa,EAAaS,kBAAoB,GAC5CC,kBAAmBV,EAAaW,qBAAuB,IACvDC,SAAUZ,EAAaa,iBAAmB,MAE5C9uC,KAAKgqC,cAAc9P,GAAG,qBAAsBqV,GAC5CvvC,KAAK4vB,IAAI,8CAIX,IADqD,IAA5Bqe,EAAac,WAChB,CACpB,MAAMC,EAAcf,EAAagB,cAAgB,GACjDjvC,KAAKgqC,cAAckF,mBAAmBF,GACtChvC,KAAKgqC,cAAc9P,GAAG,cAAc8U,IAAeO,GACnDvvC,KAAK4vB,IAAI,yCAAyCof,KACpD,CAGA,IADmE,IAAnCf,EAAakB,kBAChB,CAC3B,MAAMd,EAAeruC,KAAK4rC,qBACtByC,GAAgBA,EAAanZ,YAAcmZ,EAAanZ,WAAWrwB,OAAS,GAC9E7E,KAAKgqC,cAAcoF,2BACnBpvC,KAAKgqC,cAAc9P,GAAG,oBAAqBqV,GAC3CvvC,KAAK4vB,IAAI,gEAET5vB,KAAK4vB,IAAI,oDAEb,EAEuD,IAA7Bqe,EAAaoB,cAErCrvC,KAAKgqC,cAAcsF,qBACnBtvC,KAAKgqC,cAAc9P,GAAG,cAAeqV,GACrCvvC,KAAK4vB,IAAI,4DAGX5vB,KAAK4vB,IAAI,kCAAkCmd,EAAiBlmC,kBAAkBkmC,EAAiBjiC,WACjG,MACE9K,KAAKgqC,cAAc0D,qBACnB1tC,KAAKgqC,cAAc9P,GAAG,cAAeqV,GACrCvvC,KAAK4vB,IAAI,mCAAmCmd,EAAiBlmC,kBAAkBkmC,EAAiBjiC,WAEpG,CAEQ,yBAAAyjC,GACN,IAAKvuC,KAAK0rC,SAER,YADA1rC,KAAK4vB,IAAI,qCAAqC,GAIhD,MAAM8f,EAAa,CACjBC,gBAAiB3vC,KAAKu5B,gBAAkB,UACxC4P,WAAYnpC,KAAKs5B,UACjBrd,QAASjc,KAAK0rC,SAASzvB,QACvBkZ,WAAYn1B,KAAK0rC,SAASvW,WAC1BwW,cAAe3rC,KAAK0rC,SAASC,cAC7BzW,WAAYl1B,KAAK0rC,SAASxW,WAC1B0a,WAAY5vC,KAAKc,OACjB+uC,cAAA,IAAkBlwC,MAAOoM,cACzBwwB,SAAUx4B,OAAO0L,SAAS8C,KAC1BF,SAAUnO,SAASmO,UAGfy9B,EAAY,GAAG9vC,KAAK2L,2CAE1B,GAAIjG,UAAU6F,WAAY,CACxB,MAAMW,EAAO,IAAIC,KAAK,CAAC3K,KAAKM,UAAU4tC,IAAc,CAAE7oC,KAAM,qBAC/CnB,UAAU6F,WAAWukC,EAAW5jC,GAG3ClM,KAAK4vB,IAAI,6CAET5vB,KAAK4vB,IAAI,0CAA0C,EAEvD,MACErlB,MAAMulC,EAAW,CACftlC,OAAQ,OACR8B,QAAS,CACP,eAAgB,mBAChB,oBAAqBtM,KAAKgM,UAE5BQ,KAAMhL,KAAKM,UAAU4tC,GACrBjjC,WAAW,EACXmwB,YAAa,YACZlS,MAAMvL,IACPnf,KAAK4vB,IAAI,6CAA6CzQ,KAAO,IAGnE,CAEQ,YAAAsuB,CAAa7C,GACnB,IAAI5qC,KAAK8pC,cACL9pC,KAAK6pC,gBAAgBj2B,IAAIg3B,EAAOC,WAApC,CAMA,OAFA7qC,KAAK6pC,gBAAgBhnB,IAAI+nB,EAAOC,WAExBD,EAAOE,aACb,IAAK,cACH9qC,KAAK+vC,iBAAiBnF,GACtB,MACF,IAAK,aACH5qC,KAAK+qC,gBAAgBH,GACrB,MACF,IAAK,eACH5qC,KAAKgrC,kBAAkBJ,GACvB,MACF,IAAK,QACH5qC,KAAKgwC,YAAYpF,GACjB,MACF,IAAK,gBACH5qC,KAAKiwC,mBAAmBrF,GACxB,MACF,IAAK,oBACH5qC,KAAKkwC,sBAAsBtF,GAC3B,MACF,QACE5qC,KAAK4vB,IAAI,wBAAwBgb,EAAOE,eAAe,GAG3D9qC,KAAK0sB,WAAWke,EAAOC,UAAW,OA3BlC,CA4BF,CAEQ,gBAAAkF,CAAiBnF,GACvB,MAAMh+B,KAAEA,EAAAujC,SAAMA,EAAAC,SAAUA,mBAAU/f,EAAAE,WAAkBA,GAAeqa,EAAOzlC,OACpEoV,EAAWqwB,EAAOrwB,UAAY,eAE9B81B,EAASnsC,SAASusB,cAAc,OACtC4f,EAAO3f,UAAY,oBACnB2f,EAAO9pB,aAAa,iBAAkBqkB,EAAOC,WAE7C,MAAMyF,EAAyC,CAC7CC,aAAc,6BACdC,YAAa,4BACbC,UAAW,0BACXC,SAAU,0BAsBZ,GAnBAL,EAAO1f,MAAMC,QAAU,mCAEnB0f,EAAe/1B,IAAa+1B,EAAeC,mCAC/BvwC,KAAK2vB,cAAcU,GAAoB,6BAC5CrwB,KAAK2vB,cAAcY,GAAc,saAexC4f,EAAU,CACZ,MAAMQ,EAAOzsC,SAASusB,cAAc,OAC9ByU,EAAUllC,KAAK0vB,YAAYygB,GAC7BjL,IACFyL,EAAK9e,IAAMqT,EACXyL,EAAK7e,IAAM,GACX6e,EAAKhgB,MAAMC,QAAU,6BACrByf,EAAOpf,YAAY0f,GAEvB,CAEA,MAAMC,EAAS1sC,SAASusB,cAAc,QACtCmgB,EAAO5f,YAAcpkB,GAAQ,eAC7ByjC,EAAOpf,YAAY2f,GAEnBP,EAAOrsC,iBAAiB,QAAS,KAC/BhE,KAAK0sB,WAAWke,EAAOC,UAAW,SAClC,MAAM3F,EAAUllC,KAAK0vB,YAAY0gB,GAC7BlL,GACFnhC,OAAOohC,KAAKD,EAAS,YAIzBllC,KAAK6vB,qBACLwgB,EAAO9pB,aAAa,yBAA0B,IAC9CriB,SAASsI,KAAKykB,YAAYof,EAC5B,CAEQ,eAAAtF,CAAgBH,GACtB,MAAMpa,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,6BACpBF,EAAQjK,aAAa,iBAAkBqkB,EAAOC,WAE9Cra,EAAQG,MAAMC,QAAU,kSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAM1S,UAAY,yBAClB0S,EAAMzS,MAAMC,QAAU,iVAYtB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAc4Z,EAAOzlC,OAAOmN,OAAS,eAC3CA,EAAMqe,MAAMC,QAAU,yEACtBwS,EAAMnS,YAAY3e,GAElB,MAAMu+B,EAAc3sC,SAASusB,cAAc,KAC3CogB,EAAY7f,YAAc4Z,EAAOzlC,OAAO0rC,aAAe,0CACvDA,EAAYlgB,MAAMC,QAAU,oDAC5BwS,EAAMnS,YAAY4f,GAKlB,MAAMjlB,EAAqDzlB,MAAMC,QAC9DwkC,EAAOzlC,OAAeymB,UAEpBgf,EAAOzlC,OAAeymB,SACvB,CACE,CAAEsI,MAAO,UAAWyO,MAAO,WAC3B,CAAEzO,MAAO,UAAWyO,MAAO,WAC3B,CAAEzO,MAAO,UAAWyO,MAAO,WAC3B,CAAEzO,MAAO,UAAWyO,MAAO,YAG3BmO,EAAS,6BACTC,EAAQ7sC,SAAS8sC,gBAAgBF,EAAQ,OAC/CC,EAAMxqB,aAAa,UAAW,qBAC9BwqB,EAAMxqB,aAAa,QAAS,OAC5BwqB,EAAMxqB,aAAa,SAAU,OAC7BwqB,EAAMpgB,MAAMC,QAAU,uCAEtB,MAAM6F,EAAI7K,EAAS/mB,OACbosC,EAAY,EAAI1xC,KAAK2xC,GAAMza,EAC3B0a,EAAS,IACf,IAAA,IAASlhC,EAAI,EAAGA,EAAIwmB,EAAGxmB,IAAK,CAC1B,MAAMmhC,EAAQnhC,EAAIghC,EAAW1xC,KAAK2xC,GAAK,EACjCG,EAAMD,EAAQH,EACdK,EAAK/xC,KAAKgyC,IAAIH,GAASD,EACvBK,EAAKjyC,KAAKkyC,IAAIL,GAASD,EACvBO,EAAKnyC,KAAKgyC,IAAIF,GAAOF,EACrBQ,EAAKpyC,KAAKkyC,IAAIJ,GAAOF,EACrBS,EAAWX,EAAW1xC,KAAK2xC,GAAK,EAAI,EACpC/+B,EAAOjO,SAAS8sC,gBAAgBF,EAAQ,QAC9C3+B,EAAKoU,aACH,IACA,WAAW+qB,EAAG7b,QAAQ,MAAM+b,EAAG/b,QAAQ,kBAA8Bmc,OAAcF,EAAGjc,QAAQ,MAAMkc,EAAGlc,QAAQ,QAEjHtjB,EAAKoU,aAAa,OAAQvmB,KAAK2vB,cAAc/D,EAAS3b,GAAG0yB,OAAS,YAClExwB,EAAKoU,aAAa,SAAU,WAC5BpU,EAAKoU,aAAa,eAAgB,KAClCwqB,EAAM9f,YAAY9e,GAElB,MAAM0/B,EAAaT,EAAQH,EAAW,EAChCa,EAAKvyC,KAAKgyC,IAAIM,GAAcV,EAAS,IACrCY,EAAKxyC,KAAKkyC,IAAII,GAAcV,EAAS,IACrCvkC,EAAO1I,SAAS8sC,gBAAgBF,EAAQ,QAC9ClkC,EAAK2Z,aAAa,IAAKurB,EAAGrc,QAAQ,IAClC7oB,EAAK2Z,aAAa,IAAKwrB,EAAGtc,QAAQ,IAClC7oB,EAAK2Z,aAAa,OAAQ,WAC1B3Z,EAAK2Z,aAAa,YAAa,MAC/B3Z,EAAK2Z,aAAa,cAAe,OACjC3Z,EAAK2Z,aAAa,cAAe,UACjC3Z,EAAK2Z,aAAa,oBAAqB,UACvC3Z,EAAKokB,YAAcpF,EAAS3b,GAAGikB,OAAS,IAAIjkB,EAAI,IAChD8gC,EAAM9f,YAAYrkB,EACpB,CACAw2B,EAAMnS,YAAY8f,GAElB,MAAMiB,EAAa9tC,SAASusB,cAAc,UAC1CuhB,EAAWhhB,YAAc,YACzBghB,EAAWrhB,MAAMC,QAAU,oRAY3BohB,EAAWhuC,iBAAiB,QAASsF,UACnC0oC,EAAWC,UAAW,EACtBD,EAAWhhB,YAAc,cAEzB,IACE,MAAMkhB,QAAclyC,KAAKmyC,cAAcvH,EAAOzlC,OAAOitC,WAErDrB,EAAMpgB,MAAMyW,UAAY,wBAExBh9B,WAAW,KACTpK,KAAKqyC,gBAAgBjP,EAAO8O,IAC3B,IAEL,OAASvxC,GACPX,KAAK4vB,IAAI,2BAA2BjvB,KAAS,GAC7CqxC,EAAWC,UAAW,EACtBD,EAAWhhB,YAAc,WAC3B,IAGFoS,EAAMnS,YAAY+gB,GAElB,MAAMrL,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,mMAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWke,EAAOC,UAAW,WAClC3mC,SAASsI,KAAKk4B,YAAYlU,GAC1BxwB,KAAK6pC,gBAAgB91B,OAAO62B,EAAOC,aAGrCzH,EAAMzS,MAAMpW,SAAW,WACvB6oB,EAAMnS,YAAY0V,GAElBnW,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,qBAAA8d,CAAsBF,EAAkBjpC,GAC9C,GAAInF,KAAK8pC,YAAa,OACtB,MAAM9tB,EAAOhc,KAAK4rC,qBAElB,GAAoB,kBAAhBzmC,EAAO0B,OAA6BmV,EAEtC,YADAhc,KAAK4vB,IAAI,+CAIP5T,IACFhc,KAAK0rC,SAAW1vB,GAGlB,MAAMuuB,EAAgBvqC,KAAKmqC,iBAAiBmI,WAAa,CAAA,EACnDC,EAAchI,EAAcgI,aAAgBptC,EAAeqtC,cAAgB,UAC3EC,EAAkBlI,EAAckI,iBAAoBttC,EAAekrB,kBAAoB,UACvFsX,EAAY4C,EAAc5C,WAAcxiC,EAAeorB,YAAc,UACrEmiB,EAAcnI,EAAcmI,aAAgBvtC,EAAewtC,cAAgBJ,EAC3EK,EAAcrI,EAAcqI,aAAe,CAAC,UAAW,UAAW,UAAW,WAE7EpiB,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,2BACpBF,EAAQjK,aAAa,iBAAkB6nB,GAEvC5d,EAAQG,MAAMC,QAAU,mSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAM1S,UAAY,yBAClB0S,EAAMzS,MAAMC,QAAU,uBACN5wB,KAAK2vB,cAAc8iB,sTAWnC,MAAMvhB,EAAWhtB,SAASusB,cAAc,UACxCS,EAAS2hB,UAAY,IACrB3hB,EAASR,UAAY,yBACrBQ,EAASP,MAAMC,QAAU,4LAUzBM,EAAS4hB,QAAU,KACjB9yC,KAAK0sB,WAAW0hB,EAAU,UAAW,CAAEvnC,KAAM,eAC7C3C,SAASsI,KAAKk4B,YAAYlU,GAC1BxwB,KAAK6pC,gBAAgB91B,OAAOq6B,IAE9BhL,EAAMnS,YAAYC,GAElB,MAAM5e,EAAQpO,SAASusB,cAAc,MAWrC,GAVAne,EAAM0e,YAAchxB,KAAK+yC,yBAA0B5tC,EAAemN,OAAS,gBAC3EA,EAAMqe,MAAMC,QAAU,0HAKX5wB,KAAK2vB,cAAcgY,YAE9BvE,EAAMnS,YAAY3e,GAEd0J,EAAM,CACR,MAAMg3B,EAAW9uC,SAASusB,cAAc,KACxCuiB,EAAShiB,YAAc,iBAAiBhV,EAAK2vB,iBAAiB3vB,EAAKmZ,WAAWM,QAAQ,qBACtFud,EAASriB,MAAMC,QAAU,qHAMzBwS,EAAMnS,YAAY+hB,EACpB,CAEA,MAAMC,EAAiB/uC,SAASusB,cAAc,OAC9CwiB,EAAeviB,UAAY,wBAC3BuiB,EAAetiB,MAAMC,QAAU,gJAMzBgiB,EAAY,2BACZA,EAAY,6BACZA,EAAY,8BACZA,EAAY,2KASlB,MAAMM,EAAOhvC,SAASusB,cAAc,QACpCyiB,EAAKxiB,UAAY,kBACjBwiB,EAAKviB,MAAMC,QAAU,kBAErB,MAAMuiB,EAAajvC,SAASusB,cAAc,SAC1C0iB,EAAWtsC,KAAO,MAClBssC,EAAWC,YAAc,0BACzBD,EAAWE,UAAW,EACtBF,EAAWxiB,MAAMC,QAAU,gMAS3BsiB,EAAKjiB,YAAYkiB,GAEjB,MAAMG,EAAapvC,SAASusB,cAAc,SAC1C6iB,EAAWzsC,KAAO,QAClBysC,EAAWF,YAAc,8BACzBE,EAAW3iB,MAAMC,QAAU,gMAS3BsiB,EAAKjiB,YAAYqiB,GAEjB,MAAMC,EAAYrvC,SAASusB,cAAc,SACzC8iB,EAAU1sC,KAAO,OACjB0sC,EAAUH,YAAc,6BACxBG,EAAU5iB,MAAMC,QAAU,gMAS1BsiB,EAAKjiB,YAAYsiB,GAEjB,MAAMC,EAAetvC,SAASusB,cAAc,UAC5C+iB,EAAa3sC,KAAO,SACpB2sC,EAAaxiB,YAAc,kBAC3BwiB,EAAa7iB,MAAMC,QAAU,iEAGb5wB,KAAK2vB,cAAc+iB,0JAQnCQ,EAAKjiB,YAAYuiB,GAEjB,MAAMC,EAAevvC,SAASusB,cAAc,OAC5CgjB,EAAa/iB,UAAY,mBACzB+iB,EAAa9iB,MAAMC,QAAU,kIAO7BsiB,EAAKjiB,YAAYwiB,GAEjBP,EAAKlvC,iBAAiB,SAAUsF,MAAO8nB,IACrCA,EAAEuU,iBAEF,MAAM9e,EAAQssB,EAAWhlC,MAAMa,OACzB2X,EAAQ2sB,EAAWnlC,MAAMa,OACzBC,EAAOskC,EAAUplC,MAAMa,OAE7B,OAAKhP,KAAK0zC,cAAc7sB,GAMpBF,IAAU3mB,KAAK2zC,cAAchtB,IAC/B8sB,EAAaziB,YAAc,0CAC3ByiB,EAAa9iB,MAAMijB,QAAU,WAI/BH,EAAa9iB,MAAMijB,QAAU,OAC7BJ,EAAavB,UAAW,EACxBuB,EAAaxiB,YAAc,cAE3BiiB,EAAetiB,MAAMyW,UAAY,6BAEjCh9B,WAAWd,UACT,IACE,MAAM4oC,QAAclyC,KAAK6zC,gBAAgB,CACvChtB,QACAF,QACA1X,OACA+M,OACAoyB,aAGFpuC,KAAK8zC,mBAAmB1Q,EAAO8O,GAC/BlyC,KAAK0sB,WAAW0hB,EAAU,SAAU,CAClCvnC,KAAM,aACNktC,YAAa7B,EAAM6B,YACnBC,YAAartB,GAGjB,OAAShmB,GACPX,KAAK4vB,IAAI,gCAAgCjvB,KAAS,GAClD8yC,EAAaziB,YAAc,sCAC3ByiB,EAAa9iB,MAAMijB,QAAU,QAC7BJ,EAAavB,UAAW,EACxBuB,EAAaxiB,YAAc,kBAC3BiiB,EAAetiB,MAAMyW,UAAY,EACnC,GACC,OA1CDqM,EAAaziB,YAAc,6DAC3ByiB,EAAa9iB,MAAMijB,QAAU,YA4CjCxQ,EAAMnS,YAAYgiB,GAClB7P,EAAMnS,YAAYiiB,GAClB1iB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,GAE1BxwB,KAAK0sB,WAAW0hB,EAAU,OAAQ,CAAEvnC,KAAM,cAC5C,CAEQ,aAAA6sC,CAAc7sB,GAEpB,MADkB,qBACD/Y,KAAK+Y,EAAMznB,QAAQ,WAAY,IAClD,CAEQ,aAAAu0C,CAAchtB,GAEpB,MADmB,6BACD7Y,KAAK6Y,EACzB,CAEA,qBAAcktB,CAAgBrtC,iBAO5B,MAAMmC,EAAM,GAAG3I,KAAK2L,uCAEdW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,eAAgB,oBAGdhM,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAE7D,MAAM0a,EAAYj0C,KAAKk0C,kBACjBC,EAAan0C,KAAKstC,gBAClB8G,EAAYp0C,KAAKq0C,eAEjB7nC,EAAY,CAChBqa,MAAOrgB,EAAKqgB,MAAMznB,QAAQ,WAAY,IACtCunB,MAAOngB,EAAKmgB,YAAS,EACrB1X,KAAMzI,EAAKyI,WAAQ,EACnBgN,SAAS,OAAA+M,IAAKhN,WAAL,EAAAgN,EAAW/M,UAAW,OAAOtc,KAAKC,QAC3C00C,WAAY,OAAAlmB,EAAA5nB,EAAKwV,WAAL,EAAAoS,EAAWnS,QACvBkZ,YAAY,OAAAof,EAAA/tC,EAAKwV,WAAL,EAAAu4B,EAAWpf,aAAc,EACrCwW,eAAe,OAAA6I,EAAAhuC,EAAKwV,WAAL,EAAAw4B,EAAW7I,gBAAiB,MAC3CzW,YAAY,OAAAuf,EAAAjuC,EAAKwV,WAAL,EAAAy4B,EAAWvf,aAAc,GACrCwf,SAAU3wC,OAAO0L,SAAS8C,KAC1B82B,SAAU,MACVsL,WAAYV,EACZ5X,YAAa8X,EACbS,WAAY50C,KAAKsE,qBAAkB,EACnC8kC,aAAcppC,KAAK0B,uBAAoB,EACvCmzC,WAAYT,EAAUS,WACtBC,WAAYV,EAAUU,WACtBC,aAAcX,EAAUW,cAGpBzqC,QAAiBC,MAAM5B,EAAK,CAChC6B,OAAQ,OACR8B,UACAE,KAAMhL,KAAKM,UAAU0K,GACrBowB,YAAa,YAGf,IAAKtyB,EAASK,GAAI,CAChB,MAAMgC,QAAkBrC,EAASsC,OACjC,MAAM,IAAIhB,MAAM,gCAAgCtB,EAASuC,YAAYF,IACvE,CAEA,aAAarC,EAASoC,MACxB,CAEQ,kBAAAonC,CAAmB1Q,EAAoB8O,GAC7C9O,EAAMyP,UAAY,GAElB,MAAMmC,EAAkB9wC,SAASusB,cAAc,OAC/CukB,EAAgBrkB,MAAMC,QAAU,0CAEhC,MAAMqkB,EAAQ/wC,SAASusB,cAAc,OACrCwkB,EAAMjkB,YAAc,KACpBikB,EAAMtkB,MAAMC,QAAU,wCACtBokB,EAAgB/jB,YAAYgkB,GAE5B,MAAM3iC,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAc,mBACpB1e,EAAMqe,MAAMC,QAAU,yEACtBokB,EAAgB/jB,YAAY3e,GAE5B,MAAM4iC,EAAahxC,SAASusB,cAAc,KAK1C,GAJAykB,EAAWlkB,YAAckhB,EAAM6B,YAC/BmB,EAAWvkB,MAAMC,QAAU,sEAC3BokB,EAAgB/jB,YAAYikB,GAExBhD,EAAMiD,YAAa,CACrB,MAAMC,EAAkBlxC,SAASusB,cAAc,OAC/C2kB,EAAgBzkB,MAAMC,QAAU,iKAQhC,MAAMykB,EAAcnxC,SAASusB,cAAc,OAC3C4kB,EAAYrkB,YAAc,oBAC1BqkB,EAAY1kB,MAAMC,QAAU,oDAC5BwkB,EAAgBnkB,YAAYokB,GAE5B,MAAMC,EAAapxC,SAASusB,cAAc,OAC1C6kB,EAAWtkB,YAAckhB,EAAMiD,YAC/BG,EAAW3kB,MAAMC,QAAU,0EAC3BwkB,EAAgBnkB,YAAYqkB,GAE5BN,EAAgB/jB,YAAYmkB,EAC9B,CAEA,MAAM90C,EAAU4D,SAASusB,cAAc,KACvCnwB,EAAQ0wB,YAAc,gEACtB1wB,EAAQqwB,MAAMC,QAAU,oDACxBokB,EAAgB/jB,YAAY3wB,GAE5B,MAAMqmC,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,QAC1B2V,EAAYhW,MAAMC,QAAU,8MAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpC,MAAMwsB,EAAU4S,EAAMmS,cAClB/kB,GAAWtsB,SAASsI,KAAKg8B,SAAShY,IACpCtsB,SAASsI,KAAKk4B,YAAYlU,KAI9BwkB,EAAgB/jB,YAAY0V,GAC5BvD,EAAMnS,YAAY+jB,EACpB,CAEQ,eAAAd,GACN,MAAMvhC,EAAWC,KAAKC,iBAAiBC,kBAAkBC,SAEzD,OAAIJ,EAASuE,SAAS,WAAmB,gBACrCvE,EAASuE,SAAS,UAAkB,SACpCvE,EAASuE,SAAS,iBAAmBvE,EAASuE,SAAS,iBAAyB,QAChFvE,EAASuE,SAAS,mBAAqBvE,EAASuE,SAAS,iBAAmBvE,EAASuE,SAAS,gBAAwB,iBACtHvE,EAASuE,SAAS,eAAiBvE,EAASuE,SAAS,eAAuB,cAC5EvE,EAASuE,SAAS,aAAevE,EAASuE,SAAS,cAAgBvE,EAASuE,SAAS,iBAAyB,gBAC9GvE,EAASuE,SAAS,cAAgBvE,EAASuE,SAAS,oBAA4B,UAE7E,eACT,CAEQ,YAAAm9B,GACN,MAAM9zB,EAAS,IAAI3P,gBAAgB7M,OAAO0L,SAASoB,QACnD,MAAO,CACLgkC,WAAYt0B,EAAOnf,IAAI,oBAAiB,EACxC0zC,WAAYv0B,EAAOnf,IAAI,oBAAiB,EACxC2zC,aAAcx0B,EAAOnf,IAAI,sBAAmB,EAEhD,CAEQ,YAAAkD,GACN,OAAO0mB,eAAetc,QAAQ,mBAChC,CAEQ,cAAAhN,GACN,OAAOqM,aAAaW,QAAQ,qBAC9B,CAEQ,iBAAAs8B,CAAkBJ,GACxB,MAAMpa,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,6BACpBF,EAAQjK,aAAa,iBAAkBqkB,EAAOC,WAE9Cra,EAAQG,MAAMC,QAAU,kSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAM1S,UAAY,2BAClB0S,EAAMzS,MAAMC,QAAU,iVAYtB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAc4Z,EAAOzlC,OAAOmN,OAAS,iBAC3CA,EAAMqe,MAAMC,QAAU,yEACtBwS,EAAMnS,YAAY3e,GAElB,MAAMu+B,EAAc3sC,SAASusB,cAAc,KAC3CogB,EAAY7f,YAAc4Z,EAAOzlC,OAAO0rC,aAAe,gCACvDA,EAAYlgB,MAAMC,QAAU,oDAC5BwS,EAAMnS,YAAY4f,GAElB,MAAM2E,EAAStxC,SAASusB,cAAc,UACtC+kB,EAAOviC,MAAQ,IACfuiC,EAAOtiC,OAAS,IAChBsiC,EAAO7kB,MAAMC,QAAU,8JAOvBwS,EAAMnS,YAAYukB,GAElB,MAAM5oB,EAAM4oB,EAAOC,WAAW,MAC9B,GAAI7oB,EAAK,CACPA,EAAI8oB,UAAY,UAChB9oB,EAAI+oB,SAAS,EAAG,EAAGH,EAAOviC,MAAOuiC,EAAOtiC,QACxC0Z,EAAI8oB,UAAY,OAChB9oB,EAAIgpB,KAAO,aACXhpB,EAAIipB,UAAY,SAChBjpB,EAAIkpB,SAAS,gBAAiBN,EAAOviC,MAAQ,EAAGuiC,EAAOtiC,OAAS,GAEhE,IAAI6iC,GAAe,EAEnB,MAAMC,EAAU,CAACC,EAAWC,KAC1BtpB,EAAIupB,yBAA2B,kBAC/BvpB,EAAIwpB,YACJxpB,EAAIypB,IAAIJ,EAAGC,EAAG,GAAI,EAAa,EAAV32C,KAAK2xC,IAC1BtkB,EAAIoH,QAGNwhB,EAAOxxC,iBAAiB,YAAa,KAAQ+xC,GAAe,IAC5DP,EAAOxxC,iBAAiB,UAAW,KAAQ+xC,GAAe,IAC1DP,EAAOxxC,iBAAiB,YAAcotB,IACpC,GAAI2kB,EAAc,CAChB,MAAMve,EAAOge,EAAO/d,wBACpBue,EAAQ5kB,EAAEklB,QAAU9e,EAAKG,KAAMvG,EAAEmlB,QAAU/e,EAAKE,IAClD,GAEJ,CAEA,MAAM8e,EAAgBtyC,SAASusB,cAAc,UAC7C+lB,EAAcxlB,YAAc,eAC5BwlB,EAAc7lB,MAAMC,QAAU,wPAW9B4lB,EAAcxyC,iBAAiB,QAASsF,UACtCktC,EAAcvE,UAAW,EACzBuE,EAAcxlB,YAAc,eAE5B,IACE,MAAMkhB,QAAclyC,KAAKmyC,cAAcvH,EAAOzlC,OAAOitC,WACrDpyC,KAAKqyC,gBAAgBjP,EAAO8O,EAC9B,OAASvxC,GACPX,KAAK4vB,IAAI,2BAA2BjvB,KAAS,GAC7C61C,EAAcvE,UAAW,EACzBuE,EAAcxlB,YAAc,WAC9B,IAGFoS,EAAMnS,YAAYulB,GAElBhmB,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,WAAAwf,CAAYpF,GAClB,MAAMtqC,QAAEA,EAAAqwC,KAASA,EAAA8F,SAAMA,GAAa7L,EAAOzlC,OACrCoV,EAAWqwB,EAAOrwB,UAAY,cAE9Bm8B,EAAQxyC,SAASusB,cAAc,OACrCimB,EAAMhmB,UAAY,cAClBgmB,EAAMnwB,aAAa,iBAAkBqkB,EAAOC,WAE5C,MAAMyF,EAAyC,CAC7CE,YAAa,4BACbD,aAAc,6BACdG,SAAU,yBACVD,UAAW,2BAmBb,GAhBAiG,EAAM/lB,MAAMC,QAAU,mCAElB0f,EAAe/1B,IAAa+1B,EAAeE,8YAc3CG,EAAM,CACR,MAAMgG,EAASzyC,SAASusB,cAAc,OACtCkmB,EAAO3lB,YAAc2f,EACrBgG,EAAOhmB,MAAMC,QAAU,mBACvB8lB,EAAMzlB,YAAY0lB,EACpB,CAEA,MAAMC,EAAY1yC,SAASusB,cAAc,OACzCmmB,EAAU5lB,YAAc1wB,GAAW,gCACnCs2C,EAAUjmB,MAAMC,QAAU,yCAC1B8lB,EAAMzlB,YAAY2lB,GAElB52C,KAAK6vB,qBACL6mB,EAAMnwB,aAAa,yBAA0B,IAC7CriB,SAASsI,KAAKykB,YAAYylB,GAE1BtsC,WAAW,KACTssC,EAAM/lB,MAAMyW,UAAY,8BACxBh9B,WAAW,KACLlG,SAASsI,KAAKg8B,SAASkO,IACzBxyC,SAASsI,KAAKk4B,YAAYgS,GAE5B12C,KAAK6pC,gBAAgB91B,OAAO62B,EAAOC,YAClC,MACF4L,GAAY,IACjB,CAEQ,kBAAAxG,CAAmBrF,GACzB,MAAMpa,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,yBACpBF,EAAQjK,aAAa,iBAAkBqkB,EAAOC,WAE9Cra,EAAQG,MAAMC,QAAU,qYAexB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAc4Z,EAAOzlC,OAAOmN,OAAS,0BAC3CA,EAAMqe,MAAMC,QAAU,wEACtBJ,EAAQS,YAAY3e,GAEpB,MAAMu+B,EAAc3sC,SAASusB,cAAc,KAC3CogB,EAAY7f,YAAc4Z,EAAOzlC,OAAO0rC,aAAe,mCACvDA,EAAYlgB,MAAMC,QAAU,oDAC5BJ,EAAQS,YAAY4f,GAEpB,MAAMqC,EAAOhvC,SAASusB,cAAc,QAE9ByD,EAAQhwB,SAASusB,cAAc,SACrCyD,EAAMlD,YAAc,6CACpBkD,EAAMvD,MAAMC,QAAU,sFACtBsiB,EAAKjiB,YAAYiD,GAEjB,MAAM3U,EAAQrb,SAASusB,cAAc,SACrClR,EAAM1Y,KAAO,SACb0Y,EAAML,IAAM,IACZK,EAAMxG,IAAM,KACZwG,EAAMoR,MAAMC,QAAU,iKAQtBsiB,EAAKjiB,YAAY1R,GAEjB,MAAMs3B,EAAgB3yC,SAASusB,cAAc,SAC7ComB,EAAc7lB,YAAc,0BAC5B6lB,EAAclmB,MAAMC,QAAU,sFAC9BsiB,EAAKjiB,YAAY4lB,GAEjB,MAAMC,EAAW5yC,SAASusB,cAAc,YACxCqmB,EAASC,KAAO,EAChBD,EAASnmB,MAAMC,QAAU,uNAUzBsiB,EAAKjiB,YAAY6lB,GAEjB,MAAMtD,EAAetvC,SAASusB,cAAc,UAC5C+iB,EAAa3sC,KAAO,SACpB2sC,EAAaxiB,YAAc,kBAC3BwiB,EAAa7iB,MAAMC,QAAU,6NAW7BsiB,EAAKjiB,YAAYuiB,GAEjBN,EAAKlvC,iBAAiB,SAAUsF,MAAO8nB,IACrCA,EAAEuU,iBAEF6N,EAAavB,UAAW,EACxBuB,EAAaxiB,YAAc,gBAE3B,UACQhxB,KAAKg3C,eAAepM,EAAOC,UAAW,CAC1CoM,MAAOvgB,SAASnX,EAAMpR,OACtB+oC,QAASJ,EAAS3oC,QAGpBqiB,EAAQqiB,UAAY,mMAEpBzoC,WAAW,KACTlG,SAASsI,KAAKk4B,YAAYlU,IACzB,IAEL,OAAS7vB,GACPX,KAAK4vB,IAAI,8BAA8BjvB,KAAS,GAChD6yC,EAAavB,UAAW,EACxBuB,EAAaxiB,YAAc,iBAC7B,IAGFR,EAAQS,YAAYiiB,GAEpB,MAAMvM,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,mMAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWke,EAAOC,UAAW,WAClC3mC,SAASsI,KAAKk4B,YAAYlU,GAC1BxwB,KAAK6pC,gBAAgB91B,OAAO62B,EAAOC,aAGrCra,EAAQS,YAAY0V,GAEpB3mC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEQ,qBAAA0f,CAAsBtF,GAC5B,MAAMt4B,MAAEA,EAAAu+B,YAAOA,EAAA5e,SAAaA,UAAUP,EAAA7W,UAASA,GAAc+vB,EAAOzlC,OAE9DqrB,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,2BACpBF,EAAQjK,aAAa,iBAAkBqkB,EAAOC,WAE9Cra,EAAQG,MAAMC,QAAU,kSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OAarC,GAZA2S,EAAM1S,UAAY,yBAClB0S,EAAMzS,MAAMC,QAAU,yTAWlB/V,EAAW,CACb,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAY7U,GAC7BqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,iDACpBwS,EAAMnS,YAAYU,GAEtB,CAEA,MAAMzE,EAAUhpB,SAASusB,cAAc,OACvCvD,EAAQyD,MAAMC,QAAU,iBAExB,MAAMiH,EAAU3zB,SAASusB,cAAc,MACvCoH,EAAQ7G,YAAc1e,GAAS,yBAC/BulB,EAAQlH,MAAMC,QAAU,yEACxB1D,EAAQ+D,YAAY4G,GAEpB,MAAMsf,EAASjzC,SAASusB,cAAc,KAKtC,GAJA0mB,EAAOnmB,YAAc6f,GAAe,gCACpCsG,EAAOxmB,MAAMC,QAAU,sEACvB1D,EAAQ+D,YAAYkmB,GAEhBllB,GAAYP,EAAS,CACvB,MAAM+U,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAciB,EACxBwU,EAAU9V,MAAMC,QAAU,sPAY1B6V,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWke,EAAOC,UAAW,SAClC,MAAM3F,EAAUllC,KAAK0vB,YAAYgC,GAC7BwT,IACFnhC,OAAO0L,SAAS8C,KAAO2yB,KAI3BhY,EAAQ+D,YAAYwV,EACtB,CAEArD,EAAMnS,YAAY/D,GAElB,MAAMyZ,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,+SAe5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAWke,EAAOC,UAAW,WAClC3mC,SAASsI,KAAKk4B,YAAYlU,GAC1BxwB,KAAK6pC,gBAAgB91B,OAAO62B,EAAOC,aAGrCzH,EAAMzS,MAAMpW,SAAW,WACvB6oB,EAAMnS,YAAY0V,GAElBnW,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,EAC5B,CAEA,mBAAc2hB,CAAciF,GAC1B,MAAMzuC,EAAM,GAAG3I,KAAK2L,iDAEdW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,eAAgB,oBAGdhM,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAE7D,MAAMjvB,QAAiBC,MAAM5B,EAAK,CAChC6B,OAAQ,OACR8B,UACAE,KAAMhL,KAAKM,UAAU,CAAEswC,UAAWgF,IAClCxa,YAAa,YAGf,IAAKtyB,EAASK,GACZ,MAAM,IAAIiB,MAAM,6BAA6BtB,EAASuC,UAGxD,aAAavC,EAASoC,MACxB,CAEQ,eAAA2lC,CAAgBjP,EAAoB8O,GAC1C9O,EAAMyP,UAAY,GAElB,MAAMmC,EAAkB9wC,SAASusB,cAAc,OAC/CukB,EAAgBrkB,MAAMC,QAAU,0CAEhC,MAAMqkB,EAAQ/wC,SAASusB,cAAc,OACrCwkB,EAAMjkB,YAAc,KACpBikB,EAAMtkB,MAAMC,QAAU,wCACtBokB,EAAgB/jB,YAAYgkB,GAE5B,MAAM3iC,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAc,mBACpB1e,EAAMqe,MAAMC,QAAU,yEACtBokB,EAAgB/jB,YAAY3e,GAE5B,MAAM4iC,EAAahxC,SAASusB,cAAc,KAK1C,GAJAykB,EAAWlkB,YAAckhB,EAAM6B,YAC/BmB,EAAWvkB,MAAMC,QAAU,sEAC3BokB,EAAgB/jB,YAAYikB,GAExBhD,EAAMiD,YAAa,CACrB,MAAMC,EAAkBlxC,SAASusB,cAAc,OAC/C2kB,EAAgBzkB,MAAMC,QAAU,iKAQhC,MAAMykB,EAAcnxC,SAASusB,cAAc,OAC3C4kB,EAAYrkB,YAAc,oBAC1BqkB,EAAY1kB,MAAMC,QAAU,oDAC5BwkB,EAAgBnkB,YAAYokB,GAE5B,MAAMC,EAAapxC,SAASusB,cAAc,OAC1C6kB,EAAWtkB,YAAckhB,EAAMiD,YAC/BG,EAAW3kB,MAAMC,QAAU,0EAC3BwkB,EAAgBnkB,YAAYqkB,GAE5BN,EAAgB/jB,YAAYmkB,EAC9B,CAEA,MAAMzO,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,QAC1B2V,EAAYhW,MAAMC,QAAU,8MAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpC,MAAMwsB,EAAU4S,EAAMmS,cAClB/kB,GAAWtsB,SAASsI,KAAKg8B,SAAShY,IACpCtsB,SAASsI,KAAKk4B,YAAYlU,KAI9BwkB,EAAgB/jB,YAAY0V,GAC5BvD,EAAMnS,YAAY+jB,EACpB,CAEA,oBAAcgC,CAAe5I,EAAkB5nC,SAC7C,MAAMmC,EAAM,GAAG3I,KAAK2L,iCAEdW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,eAAgB,oBAGdhM,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAK7D,MAAMwP,EAAc,OAAA/f,OAAK4Q,qBAAL,EAAA5Q,EAAAggB,KAAAhpC,MAEdsK,QAAiBC,MAAM5B,EAAK,CAChC6B,OAAQ,OACR8B,UACAE,KAAMhL,KAAKM,UAAU,CACnB+oC,UAAWuD,EACXnF,WAAY,SACZoO,WAAY7wC,EACZ3J,aAAcksC,IAEhBnM,YAAa,YAGf,IAAKtyB,EAASK,GACZ,MAAM,IAAIiB,MAAM,8BAA8BtB,EAASuC,SAE3D,CAEA,gBAAc6f,CAAW0hB,EAAkBvF,EAAmBtG,SAC5D,IACE,MAAM55B,EAAM,GAAG3I,KAAK2L,iCAEdW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,eAAgB,oBAGdhM,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAE7D,MAAMwP,EAAc,OAAA/f,OAAK4Q,qBAAL,EAAA5Q,EAAAggB,KAAAhpC,YAEduK,MAAM5B,EAAK,CACf6B,OAAQ,OACR8B,UACAE,KAAMhL,KAAKM,UAAU,CACnB+oC,UAAWuD,EACXnF,WAAYJ,EACZwO,WAAY9U,GAAY,CAAA,EACxB1lC,aAAcksC,IAEhBnM,YAAa,WAGjB,OAASj8B,GACPX,KAAK4vB,IAAI,gCAAgCjvB,KAAS,EACpD,CACF,CAEQ,WAAA+uB,CAAY/mB,GAClB,IAAKA,EAAK,OAAO,KAEjB,IACE,MAAM6M,EAAS,IAAI7E,IAAIhI,EAAK5E,OAAO0L,SAAS8C,MAC5C,MAAwB,gBAApBiD,EAAO9F,UAAkD,UAApB8F,EAAO9F,UAC9C1P,KAAK4vB,IAAI,0BAA0BjnB,KAAO,GACnC,MAEF6M,EAAOjD,IAChB,CAAA,MAEE,OADAvS,KAAK4vB,IAAI,gBAAgBjnB,KAAO,GACzB,IACT,CACF,CAEQ,aAAAgnB,CAAcgT,GACpB,MAAI,oBAAoB70B,KAAK60B,IAIzB,0CAA0C70B,KAAK60B,IAI/C,wDAAwD70B,KAAK60B,GAPxDA,EAWF,SACT,CAEQ,aAAA2K,GACN,MAAMt8B,EAAKtL,UAAUuL,UACrB,MAAI,mDAAmDnD,KAAKkD,GACnD,SAEL,sGAAsGlD,KAAKkD,GACtG,SAEF,SACT,CAEQ,cAAAg9B,GACN,MAAMmG,EAAan0C,KAAKstC,gBACxB,MAAsB,WAAf6G,GAA0C,WAAfA,CACpC,CAEQ,kBAAAtkB,GACN,GAAI3rB,SAASukC,eAAe,2BAC1B,OAGF,MAAM9X,EAAQzsB,SAASusB,cAAc,SACrCE,EAAMxvB,GAAK,0BACXwvB,EAAMK,YAAc,ugCAoCpB9sB,SAASwkC,KAAKzX,YAAYN,EAC5B,CAEQ,uBAAA6e,CAAwBpB,EAAkBjpC,GAChD,GAAInF,KAAK8pC,YAAa,OACtB,MAAMtZ,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,2BACpBF,EAAQjK,aAAa,iBAAkB6nB,GACvC5d,EAAQjK,aAAa,yBAA0B,IAE/CiK,EAAQG,MAAMC,QAAU,kSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAM1S,UAAY,4BAClB0S,EAAMzS,MAAMC,QAAU,uBACN5wB,KAAK2vB,cAAcxqB,EAAOkrB,kBAAoB,6BACnDrwB,KAAK2vB,cAAcxqB,EAAOorB,YAAc,kUAWnD,MAAMje,EAAQpO,SAASusB,cAAc,MAC/B6mB,EAAYt3C,KAAK+yC,yBAAyB5tC,EAAOmN,OAAS,uBAChEA,EAAM0e,YAAcsmB,EACpBhlC,EAAMqe,MAAMC,QAAU,iEAAiE5wB,KAAK2vB,cAAcxqB,EAAOqtC,cAAgB,cACjIpP,EAAMnS,YAAY3e,GAElB,MAAMhS,EAAU4D,SAASusB,cAAc,KACjC8mB,EAAcv3C,KAAK+yC,yBAAyB5tC,EAAO7E,SAAW,iCAKpE,GAJAA,EAAQ0wB,YAAcumB,EACtBj3C,EAAQqwB,MAAMC,QAAU,yDACxBwS,EAAMnS,YAAY3wB,GAEd6E,EAAOqyC,iBAAmBx3C,KAAK0rC,SAAU,CAC3C,MAAM+L,EAAYvzC,SAASusB,cAAc,OACzCgnB,EAAU9mB,MAAMC,QAAU,uFAE1B,MAAM8mB,EAAYxzC,SAASusB,cAAc,OACzCinB,EAAU1mB,YAAc,GAAGhxB,KAAK0rC,SAASxW,WAAWrwB,4BACpD6yC,EAAU/mB,MAAMC,QAAU,yCAC1B6mB,EAAUxmB,YAAYymB,GAEtB13C,KAAK0rC,SAASxW,WAAWr1B,MAAM,EAAG,GAAGgE,QAAQyK,IAC3C,MAAMqpC,EAAUzzC,SAASusB,cAAc,OACvCknB,EAAQ3mB,YAAc,GAAG1iB,EAAKwL,aAAaxL,EAAK89B,cAAgB99B,EAAK0L,aACrE29B,EAAQhnB,MAAMC,QAAU,uCACxB6mB,EAAUxmB,YAAY0mB,KAGxBvU,EAAMnS,YAAYwmB,EACpB,CAEA,GAAItyC,EAAOyyC,cAAe,CACxB,MAAMC,EAAc3zC,SAASusB,cAAc,OAC3ConB,EAAYlnB,MAAMC,QAAU,8EAGZ5wB,KAAK2vB,cAAcxqB,EAAOqtC,cAAgB,4JAO1DqF,EAAY7mB,YAAc,aAAa7rB,EAAOyyC,qBAAqBzyC,EAAO2yC,qBAAuB,WACjG1U,EAAMnS,YAAY4mB,EACpB,CAEA,GAAI1yC,EAAO4yC,YAAc5yC,EAAO6yC,cAAe,CAC7C,MAAMC,EAAQ/zC,SAASusB,cAAc,OACrCwnB,EAAMtnB,MAAMC,QAAU,wEACtBqnB,EAAMjnB,YAAc,sBAAsB7rB,EAAO6yC,wBACjD5U,EAAMnS,YAAYgnB,EACpB,CAEA,MAAMxR,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAc7rB,EAAO8sB,UAAY,oBAC3CwU,EAAU9V,MAAMC,QAAU,uBACV5wB,KAAK2vB,cAAcxqB,EAAOqtC,cAAgB,iNAW1D/L,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAW0hB,EAAU,QAAS,CAAEvnC,KAAM,kBAC3C,MAAMqxC,EAAcl4C,KAAK0vB,YAAYvqB,EAAOusB,SAAW,aACnDwmB,IACFn0C,OAAO0L,SAAS8C,KAAO2lC,KAI3B9U,EAAMnS,YAAYwV,GAElB,MAAME,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,mMAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAW0hB,EAAU,UAAW,CAAEvnC,KAAM,kBAC7C3C,SAASsI,KAAKk4B,YAAYlU,GAC1BxwB,KAAK6pC,gBAAgB91B,OAAOq6B,KAG9BhL,EAAMnS,YAAY0V,GAClBnW,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,GAE1BxwB,KAAK0sB,WAAW0hB,EAAU,OAAQ,CAAEvnC,KAAM,gBAAiBsxC,KAAMhzC,EAAOgzC,MAC1E,CAEQ,kBAAA1I,CAAmBrB,EAAkBjpC,GAC3C,GAAInF,KAAK8pC,YAAa,OACtB,MAAMtZ,EAAUtsB,SAASusB,cAAc,OACvCD,EAAQE,UAAY,2BACpBF,EAAQjK,aAAa,iBAAkB6nB,GACvC5d,EAAQjK,aAAa,yBAA0B,IAE/CiK,EAAQG,MAAMC,QAAU,kSAcxB,MAAMwS,EAAQl/B,SAASusB,cAAc,OACrC2S,EAAM1S,UAAY,sBAClB0S,EAAMzS,MAAMC,QAAU,4WAatB,MAAMte,EAAQpO,SAASusB,cAAc,MACrCne,EAAM0e,YAAc7rB,EAAOmN,OAAS,gCACpCA,EAAMqe,MAAMC,QAAU,yEACtBwS,EAAMnS,YAAY3e,GAElB,MAAMhS,EAAU4D,SAASusB,cAAc,KACvCnwB,EAAQ0wB,YAAc7rB,EAAO7E,SAAW,kEACxCA,EAAQqwB,MAAMC,QAAU,oDACxBwS,EAAMnS,YAAY3wB,GAElB,MAAM4yC,EAAOhvC,SAASusB,cAAc,QAE9B6iB,EAAapvC,SAASusB,cAAc,SAC1C6iB,EAAWzsC,KAAO,QAClBysC,EAAWF,YAAc,mBACzBE,EAAWD,UAAW,EACtBC,EAAW3iB,MAAMC,QAAU,gMAS3BsiB,EAAKjiB,YAAYqiB,GAEjB,MAAME,EAAetvC,SAASusB,cAAc,UAC5C+iB,EAAa3sC,KAAO,SACpB2sC,EAAaxiB,YAAc,kBAC3BwiB,EAAa7iB,MAAMC,QAAU,4QAW7BsiB,EAAKjiB,YAAYuiB,GAEjBN,EAAKlvC,iBAAiB,SAAUsF,MAAO8nB,IACrCA,EAAEuU,iBAEF6N,EAAavB,UAAW,EACxBuB,EAAaxiB,YAAc,gBAE3B,UACQhxB,KAAK0sB,WAAW0hB,EAAU,SAAU,CACxCvnC,KAAM,WACN8f,MAAO2sB,EAAWnlC,QAGpBi1B,EAAMyP,UAAY,6UAQlBzoC,WAAW,KACTlG,SAASsI,KAAKk4B,YAAYlU,IACzB,KAEL,OAAS7vB,GACPX,KAAK4vB,IAAI,mCAAmCjvB,KAAS,GACrD6yC,EAAavB,UAAW,EACxBuB,EAAaxiB,YAAc,iBAC7B,IAGFoS,EAAMnS,YAAYiiB,GAElB,MAAMvM,EAAcziC,SAASusB,cAAc,UAC3CkW,EAAY3V,YAAc,IAC1B2V,EAAYpgB,aAAa,aAAc,SACvCogB,EAAYhW,MAAMC,QAAU,mMAW5B+V,EAAY3iC,iBAAiB,QAAS,KACpChE,KAAK0sB,WAAW0hB,EAAU,UAAW,CAAEvnC,KAAM,aAC7C3C,SAASsI,KAAKk4B,YAAYlU,GAC1BxwB,KAAK6pC,gBAAgB91B,OAAOq6B,KAG9BhL,EAAMnS,YAAY0V,GAClBnW,EAAQS,YAAYmS,GACpBpjC,KAAK6vB,qBACLW,EAAQjK,aAAa,yBAA0B,IAC/CriB,SAASsI,KAAKykB,YAAYT,GAE1BxwB,KAAK0sB,WAAW0hB,EAAU,OAAQ,CAAEvnC,KAAM,YAC5C,CAEQ,wBAAAksC,CAAyBqF,GAC/B,OAAKp4C,KAAK0rC,SAEH0M,EACJh5C,QAAQ,sBAAuB,GAAGY,KAAK0rC,SAASC,iBAAiB3rC,KAAK0rC,SAASvW,WAAWM,QAAQ,MAClGr2B,QAAQ,yBAA0BY,KAAK0rC,SAASC,eAChDvsC,QAAQ,4BAA6BY,KAAK0rC,SAASxW,WAAWrwB,OAAOpF,YAL7C24C,CAM7B,CAEQ,GAAAxoB,CAAItvB,EAAiB+3C,GAAmB,GAC9C,GAAIr4C,KAAKy5B,WAAa4e,EAAS,CAE7B53C,QADe43C,EAAU,QAAU,OACnB,kBAAkB/3C,IACpC,CACF,EC9wEK,MAAMg4C,GA2EX,WAAAv4C,GA1EAC,KAAQiV,cAAmDpN,IAC3D7H,KAAQu4C,WAAY,EAEpBv4C,KAAQw4C,uBAAyBx2B,IACjChiB,KAAQy4C,uBAAyBz2B,IAEjChiB,KAAQ04C,sBAAwB7wC,IAGhC7H,KAAQ24C,mBAAoB,EAC5B34C,KAAQ44C,iBAAkB,EAE1B54C,KAAQ64C,sBAAwBhxC,IAChC7H,KAAQ8C,iBAAmBnD,KAAKC,MAGhCI,KAAQ84C,uBAAwB,EAChC94C,KAAQ+4C,qBAAsB,EAC9B/4C,KAAQg5C,qBAA6C,CACnDrlB,UAAW,GACXgb,kBAAmB,IACnBE,SAAU,KAEZ7uC,KAAQi5C,YAAc,EACtBj5C,KAAQk5C,eAAiBv5C,KAAKC,MAG9BI,KAAQm5C,yBAA0B,EAElCn5C,KAAQo5C,mBAAoB,EAC5Bp5C,KAAQq5C,iBAAkB,EAK1Br5C,KAAQs5C,kBAAmB,EAC3Bt5C,KAAQu5C,gBAAmC,CAAE5lB,UAAW,EAAGrV,SAAU,KACrEte,KAAQw5C,oBAAsB3xC,IAK9B7H,KAAQy5C,0BAA4Bz3B,IAIpChiB,KAAQ05C,cAAe,EACvB15C,KAAQ25C,iBAAmB9xC,IAC3B7H,KAAQ45C,gBAAkB/xC,IAC1B7H,KAAQ65C,oBAAsBhyC,IAAmC,CAC/D,CAAC,QAAS,CAAC,OACX,CAAC,MAAO,CAAC,SAKX7H,KAAQ85C,yBAA2BjyC,IAEnC7H,KAAQ+5C,yBAA2BlyC,IAGnC7H,KAAQg6C,iBAAkB,EAC1Bh6C,KAAQi6C,eAAsC,CAC5CtmB,UAAW,IACXrV,SAAU,IACV47B,WAAY,KAGdl6C,KAAQm6C,aAA4D,GAEpEn6C,KAAQo6C,aAAe,EAEvBp6C,KAAQq6C,oBAAsB,EAka9Br6C,KAAQs6C,aAAe,KACrB,MAAMC,EAAYx2C,OAAOy2C,aAAet2C,SAASu2C,gBAAgBF,UAC3DG,EAAex2C,SAASu2C,gBAAgBC,aAAe32C,OAAOwP,YAC9DonC,EAAgBD,EAAe,EAAKH,EAAYG,EAAgB,IAAM,EAE5E,IAAA,MAAWE,KAAe56C,KAAKw4C,mBACzBmC,GAAiBC,IAAgB56C,KAAKy4C,mBAAmB7kC,IAAIgnC,KAC/D56C,KAAKy4C,mBAAmB51B,IAAI+3B,GAE5B56C,KAAK65B,KAAK,gBAAgB+gB,IAAe,CACvChN,cAAegN,EACfC,eAAgBF,EAChBG,WAAYP,EACZQ,cAAeL,MAcvB16C,KAAQuvC,iBAAoBzrC,IACtB9D,KAAK44C,iBAIL90C,EAAMyyC,QAAU,KAClBv2C,KAAK44C,iBAAkB,EAEvB54C,KAAK65B,KAAK,cAAe,CACvBmhB,SAAUl3C,EAAMyyC,QAChBha,SAAUx4B,OAAO0L,SAAS8C,KAC1B0oC,aAAcj7C,KAAKk7C,cAAgBv7C,KAAKC,MAAQI,KAAKk7C,cAAgB,IAAO,MAalFl7C,KAAQm7C,qBAAuB,KAC7B,GAAIn7C,KAAK+4C,oBACP,OAGF,MAAMqC,EAAWr3C,OAAOmkC,SAAWhkC,SAASu2C,gBAAgBF,UACtDc,EAAc17C,KAAKC,MACnB07C,EAAWD,EAAcr7C,KAAKk5C,eAEpC,GAAIoC,EAAW,IAAK,CAClB,MAAMC,EAAWv7C,KAAKi5C,YAAcmC,EAC9BI,EAAWj8C,KAAK8xB,IAAIkqB,EAAWD,GAGnCC,EAAW,GACXC,EAAWx7C,KAAKg5C,qBAAqBrlB,WACrCynB,EAAWp7C,KAAKg5C,qBAAqBrK,oBAErC3uC,KAAK+4C,qBAAsB,EAE3B/4C,KAAK65B,KAAK,qBAAsB,CAC9B2U,gBAAiBgN,EACjBC,gBAAiBF,EACjBG,iBAAkBN,EAClB7e,SAAUx4B,OAAO0L,SAAS8C,KAC1B0oC,aAAcj7C,KAAKk7C,cAAgBv7C,KAAKC,MAAQI,KAAKk7C,cAAgB,IAAO,IAG9El7C,KAAK27C,4BAA8B53C,OAAOqG,WAAW,KACnDpK,KAAK+4C,qBAAsB,GAC1B/4C,KAAKg5C,qBAAqBnK,WAG/B7uC,KAAKi5C,YAAcmC,EACnBp7C,KAAKk5C,eAAiBmC,CACxB,GAWFr7C,KAAQ47C,uBAAyB,KAC3B13C,SAAS23C,OACX77C,KAAK65B,KAAK,oBAAqB,CAC7B0C,SAAUx4B,OAAO0L,SAAS8C,KAC1B0oC,aAAcj7C,KAAKk7C,cAAgBv7C,KAAKC,MAAQI,KAAKk7C,cAAgB,IAAO,IAG9El7C,KAAK65B,KAAK,qBAAsB,CAC9B0C,SAAUx4B,OAAO0L,SAAS8C,QAgBhCvS,KAAQ87C,iBAAmB,KACrB97C,KAAKq5C,kBAITr5C,KAAKq5C,iBAAkB,EAEvBt1C,OAAO+lB,QAAQC,UAAU,KAAM,GAAIhmB,OAAO0L,SAAS8C,MAEnDvS,KAAK65B,KAAK,cAAe,CACvB0C,SAAUx4B,OAAO0L,SAAS8C,KAC1B0oC,aAAcj7C,KAAKk7C,cAAgBv7C,KAAKC,MAAQI,KAAKk7C,cAAgB,IAAO,MAkBhFl7C,KAAQ+7C,eAAiB,KACvB/7C,KAAK8C,iBAAmBnD,KAAKC,MAvjBhB,CAEf,EAAAs6B,CAAG2O,EAAmBpyB,GACfzW,KAAKiV,UAAUrB,IAAIi1B,IACtB7oC,KAAKiV,UAAU5T,IAAIwnC,EAAW,IAAI7mB,KAEpChiB,KAAKiV,UAAU7T,IAAIynC,GAAYhmB,IAAIpM,EACrC,CAEA,GAAAulC,CAAInT,EAAmBpyB,GACrB,MAAMwlC,EAAYj8C,KAAKiV,UAAU7T,IAAIynC,GACjCoT,GACFA,EAAUloC,OAAO0C,EAErB,CAEA,mBAAAo3B,CAAoBF,GAClB3tC,KAAKw4C,mBAAmB31B,IAAI8qB,EAC9B,CAEA,kBAAAG,CAAmB1S,GACjB,IAAKp7B,KAAK04C,kBAAkB9kC,IAAIwnB,GAAU,CACxC,MAAM8gB,EAAUn4C,OAAOqG,WAAW,KAChCpK,KAAK65B,KAAK,gBAAgBuB,IAAW,CACnCA,UACAmB,SAAUx4B,OAAO0L,SAAS8C,OAE5BvS,KAAK04C,kBAAkB3kC,OAAOqnB,IACnB,IAAVA,GAEHp7B,KAAK04C,kBAAkBr3C,IAAI+5B,EAAS8gB,EACtC,CACF,CAEA,kBAAAxO,GACE1tC,KAAK24C,mBAAoB,CAC3B,CAEA,kBAAAzJ,CAAmBF,GACjB,IAAKhvC,KAAK64C,kBAAkBjlC,IAAIo7B,GAAc,CAC5C,MAAMkN,EAAUn4C,OAAOqG,WAAW,KAChC,MAAM+xC,GAAYx8C,KAAKC,MAAQI,KAAK8C,kBAAoB,IAEpDq5C,GAAYnN,GACdhvC,KAAK65B,KAAK,cAAcmV,IAAe,CACrCC,aAAcD,EACdoN,iBAAkBD,IAItBn8C,KAAK64C,kBAAkB9kC,OAAOi7B,IACf,IAAdA,GAEHhvC,KAAK64C,kBAAkBx3C,IAAI2tC,EAAakN,EAC1C,CACF,CAEA,sBAAAzN,CAAuBtpC,GACrBnF,KAAK84C,uBAAwB,EAEzB3zC,IACFnF,KAAKg5C,qBAAuB,CAC1BrlB,UAAWxuB,EAAOwuB,WAAa3zB,KAAKg5C,qBAAqBrlB,UACzDgb,kBAAmBxpC,EAAOwpC,mBAAqB3uC,KAAKg5C,qBAAqBrK,kBACzEE,SAAU1pC,EAAO0pC,UAAY7uC,KAAKg5C,qBAAqBnK,UAG7D,CAEA,wBAAAO,GACEpvC,KAAKm5C,yBAA0B,CACjC,CAEA,kBAAA7J,GACEtvC,KAAKo5C,mBAAoB,CAC3B,CAWA,iBAAAiD,CAAkBl3C,GAChBnF,KAAKs5C,kBAAmB,EACpBn0C,IACFnF,KAAKu5C,gBAAkB,CACrB5lB,UAAWxuB,EAAOwuB,WAAa3zB,KAAKu5C,gBAAgB5lB,UACpDrV,SAAUnZ,EAAOmZ,UAAYte,KAAKu5C,gBAAgBj7B,UAGxD,CAWA,SAAAg+B,CAAUC,EAAgBnwB,EAAoBzsB,KAAKC,OACjD,IAAKI,KAAKs5C,iBAAkB,OAAO,EACnC,MAAMh5B,EAAMtgB,KAAKw5C,gBAAgBp4C,IAAIm7C,IAAW,GAC1CC,EAASpwB,EAAYpsB,KAAKu5C,gBAAgBj7B,SAKhD,IAAIm+B,EAAY,EAChB,KAAOA,EAAYn8B,EAAIzb,QAAUyb,EAAIm8B,GAAaD,GAAQC,IAC1D,MAAMC,EAAuB,IAAdD,EAAkBn8B,EAAMA,EAAIzgB,MAAM48C,GAuBjD,OAtBAC,EAAO91C,KAAKwlB,GACZpsB,KAAKw5C,gBAAgBn4C,IAAIk7C,EAAQG,GAK7BA,EAAO73C,OAAS7E,KAAKu5C,gBAAgB5lB,WACvC3zB,KAAKy5C,sBAAsB1lC,OAAOwoC,GAIlCG,EAAO73C,QAAU7E,KAAKu5C,gBAAgB5lB,YACrC3zB,KAAKy5C,sBAAsB7lC,IAAI2oC,KAEhCv8C,KAAKy5C,sBAAsB52B,IAAI05B,GAC/Bv8C,KAAK65B,KAAK,aAAc,CACtB0iB,SACAr5B,MAAOw5B,EAAO73C,OACd83C,UAAW38C,KAAKu5C,gBAAgBj7B,YAI7Bo+B,EAAO73C,MAChB,CAKA,iBAAA+3C,CAAkBL,GAChB,IAAKv8C,KAAKs5C,iBAAkB,OAAO,EACnC,MAAMh5B,EAAMtgB,KAAKw5C,gBAAgBp4C,IAAIm7C,GACrC,IAAKj8B,EAAK,OAAO,EACjB,MAAMk8B,EAAS78C,KAAKC,MAAQI,KAAKu5C,gBAAgBj7B,SAIjD,IAAIu+B,EAAO,EACX,IAAA,MAAW7hB,KAAM1a,EACX0a,GAAMwhB,GAAQK,IAEpB,OAAOA,CACT,CAQA,kBAAAC,CAAmB33C,GAEjB,GADAnF,KAAK05C,cAAe,QAChBv0C,WAAQ43C,mBACV,IAAA,MAAYR,EAAQS,KAAer4C,OAAO+pB,QAAQvpB,EAAO43C,oBACnDC,GAAYh9C,KAAK65C,gBAAgBx4C,IAAIk7C,EAAQS,EAGvD,CAKA,cAAAC,CAAeV,EAAgBnwB,EAAoBzsB,KAAKC,OACtD,IAAKI,KAAK05C,aAAc,OAIpB15C,KAAK25C,aAAa/lC,IAAI2oC,IACxBv8C,KAAKk9C,aAAaX,EAAQnwB,GAE5BpsB,KAAK25C,aAAat4C,IAAIk7C,EAAQnwB,GAC9BpsB,KAAK85C,qBAAqBz4C,IAAIk7C,EAAQ,IAAIv6B,KAM1C,MAAMg7B,EAAah9C,KAAK65C,gBAAgBz4C,IAAIm7C,GAC5C,GAAIS,EAAY,CACd,MAAMG,EAAmB,GACzB,IAAA,MAAW14B,KAAMu4B,EAAY,CAC3B,MAAMd,EAAU9xC,WAAW,KAGzB,IAAKpK,KAAK25C,aAAa/lC,IAAI2oC,GAAS,OACpC,MAAMa,EAAWp9C,KAAK85C,qBAAqB14C,IAAIm7C,IAC3C,MAAAa,OAAA,EAAAA,EAAUxpC,IAAI6Q,MAClB,MAAA24B,GAAAA,EAAUv6B,IAAI4B,GACdzkB,KAAK65B,KAAK,GAAG0iB,iBAAsB93B,IAAM,CACvC83B,SACAc,aAAc54B,EACd64B,UAAW39C,KAAKC,MAAQwsB,MAEzB3H,GACH04B,EAAOv2C,KAAKs1C,EACd,CACAl8C,KAAK+5C,qBAAqB14C,IAAIk7C,EAAQY,EACxC,CACF,CAKA,YAAAD,CAAaX,EAAgBnwB,EAAoBzsB,KAAKC,OACpD,IAAKI,KAAK05C,aAAc,OACxB,MAAM6D,EAAUv9C,KAAK25C,aAAav4C,IAAIm7C,GACtC,QAAgB,IAAZgB,EAAuB,OAC3B,MAAMC,EAAQj+C,KAAKwZ,IAAI,EAAGqT,EAAYmxB,GACtCv9C,KAAK45C,YAAYv4C,IAAIk7C,EAAQiB,GAC7Bx9C,KAAK25C,aAAa5lC,OAAOwoC,GAEzB,MAAMkB,EAAUz9C,KAAK+5C,qBAAqB34C,IAAIm7C,GAC9C,GAAIkB,EAAS,CACX,IAAA,MAAWt8C,KAAMs8C,EAAS/yC,aAAavJ,GACvCnB,KAAK+5C,qBAAqBhmC,OAAOwoC,EACnC,CACAv8C,KAAK85C,qBAAqB/lC,OAAOwoC,EACnC,CAQA,0BAAAmB,CAA2Bv4C,GACzBnF,KAAKg6C,iBAAkB,EACnB70C,IACFnF,KAAKi6C,eAAiB,CACpBtmB,UAAWxuB,EAAOwuB,WAAa3zB,KAAKi6C,eAAetmB,UACnDrV,SAAUnZ,EAAOmZ,UAAYte,KAAKi6C,eAAe37B,SACjD47B,WAAY/0C,EAAO+0C,YAAcl6C,KAAKi6C,eAAeC,YAG3D,CAKA,iBAAAyD,CAAkB1H,EAAWC,EAAW9pB,EAAoBzsB,KAAKC,OAC/D,IAAKI,KAAKg6C,gBAAiB,OAAO,EAClC,MAAMwC,EAASpwB,EAAYpsB,KAAKi6C,eAAe37B,SAE/C,IAAIm+B,EAAY,EAChB,KAAOA,EAAYz8C,KAAKm6C,aAAat1C,QAAU7E,KAAKm6C,aAAasC,GAAWzhB,GAAKwhB,GAC/EC,IASF,GAPIA,EAAY,IAAGz8C,KAAKm6C,aAAen6C,KAAKm6C,aAAat6C,MAAM48C,IAC/Dz8C,KAAKm6C,aAAavzC,KAAK,CAAEqvC,IAAGC,IAAGlb,GAAI5O,IAM/BpsB,KAAKm6C,aAAat1C,OAAS,EAE7B,OADA7E,KAAKo6C,aAAe,EACb,EAET,MAAMwD,EAAS59C,KAAKm6C,aAAa,GAC3B0D,EAAS79C,KAAKm6C,aAAan6C,KAAKm6C,aAAat1C,OAAS,GACtDi5C,EAAKD,EAAO7iB,GAAK4iB,EAAO5iB,GAC9B,GAAI8iB,GAAM,EAER,OADA99C,KAAKo6C,aAAe,EACb,EAET,MAAMoB,IAAaqC,EAAO3H,EAAI0H,EAAO1H,GAAK4H,EAC1C99C,KAAKo6C,aAAeoB,EAIpB,MAAMuC,EACyB,IAA7B/9C,KAAKq6C,qBACLjuB,EAAYpsB,KAAKq6C,qBAAuBr6C,KAAKi6C,eAAeC,WAU9D,OATIsB,GAAYx7C,KAAKi6C,eAAetmB,WAAaoqB,IAC/C/9C,KAAKq6C,oBAAsBjuB,EAC3BpsB,KAAK65B,KAAK,wBAAyB,CACjC2hB,WACA7nB,UAAW3zB,KAAKi6C,eAAetmB,UAC/BgpB,UAAW38C,KAAKi6C,eAAe37B,SAC/B0/B,QAASh+C,KAAKm6C,aAAat1C,UAGxB22C,CACT,CAIA,qBAAAyC,GACE,OAAOj+C,KAAKg6C,gBAAkBh6C,KAAKo6C,aAAe,CACpD,CAUA,UAAA8D,CAAW3B,GACT,IAAKv8C,KAAK05C,aAAc,OAAO,EAC/B,MAAM6D,EAAUv9C,KAAK25C,aAAav4C,IAAIm7C,GACtC,YAAgB,IAAZgB,EACKh+C,KAAKwZ,IAAI,EAAGpZ,KAAKC,MAAQ29C,GAE3Bv9C,KAAK45C,YAAYx4C,IAAIm7C,IAAW,CACzC,CAEA,KAAAnL,GACMpxC,KAAKu4C,YAITv4C,KAAKk7C,aAAev7C,KAAKC,MACzBI,KAAK8C,iBAAmBnD,KAAKC,MAC7BI,KAAKi5C,YAAcl1C,OAAOmkC,SAAW,EACrCloC,KAAKk5C,eAAiBv5C,KAAKC,MAEvBI,KAAKw4C,mBAAmBx1B,KAAO,GACjChjB,KAAKm+C,uBAGHn+C,KAAK24C,mBACP34C,KAAKo+C,2BAGHp+C,KAAK84C,uBACP94C,KAAKq+C,+BAGHr+C,KAAKm5C,yBACPn5C,KAAKs+C,iCAGHt+C,KAAKo5C,mBACPp5C,KAAKu+C,2BAGPv+C,KAAKw+C,0BAELx+C,KAAKy+C,uBAELz+C,KAAKu4C,WAAY,EACnB,CAEA,IAAAtN,GACOjrC,KAAKu4C,YAIVv4C,KAAK0+C,uBACL1+C,KAAK2+C,2BACL3+C,KAAK4+C,+BACL5+C,KAAK6+C,iCACL7+C,KAAK8+C,2BACL9+C,KAAK++C,0BAEL/+C,KAAK04C,kBAAkB70C,QAAQq4C,GAAWxxC,aAAawxC,IACvDl8C,KAAK04C,kBAAkBlxC,QAEvBxH,KAAK64C,kBAAkBh1C,QAAQq4C,GAAWxxC,aAAawxC,IACvDl8C,KAAK64C,kBAAkBrxC,QAEnBxH,KAAKg/C,0BACP/5C,cAAcjF,KAAKg/C,yBACnBh/C,KAAKg/C,6BAA0B,GAG7Bh/C,KAAK27C,8BACPjxC,aAAa1K,KAAK27C,6BAClB37C,KAAK27C,iCAA8B,GAGrC37C,KAAKu4C,WAAY,EACnB,CAEA,KAAAv2C,GACEhC,KAAKy4C,mBAAmBjxC,QACxBxH,KAAK44C,iBAAkB,EACvB54C,KAAK+4C,qBAAsB,EAC3B/4C,KAAKq5C,iBAAkB,EACvBr5C,KAAKk7C,aAAev7C,KAAKC,MACzBI,KAAK8C,iBAAmBnD,KAAKC,MAC7BI,KAAKi5C,YAAcl1C,OAAOmkC,SAAW,EACrCloC,KAAKk5C,eAAiBv5C,KAAKC,MAEvBI,KAAK27C,8BACPjxC,aAAa1K,KAAK27C,6BAClB37C,KAAK27C,iCAA8B,EAEvC,CAEQ,oBAAAwC,GACNp6C,OAAOC,iBAAiB,SAAUhE,KAAKs6C,aAAc,CAAEr2C,SAAS,IAChEjE,KAAKs6C,cACP,CAEQ,oBAAAoE,GACN36C,OAAOomB,oBAAoB,SAAUnqB,KAAKs6C,aAC5C,CAqBQ,wBAAA8D,GACNl6C,SAASF,iBAAiB,aAAchE,KAAKuvC,iBAC/C,CAEQ,wBAAAoP,GACNz6C,SAASimB,oBAAoB,aAAcnqB,KAAKuvC,iBAClD,CAkBQ,4BAAA8O,GACNt6C,OAAOC,iBAAiB,SAAUhE,KAAKm7C,qBAAsB,CAAEl3C,SAAS,GAC1E,CAEQ,4BAAA26C,GACN76C,OAAOomB,oBAAoB,SAAUnqB,KAAKm7C,qBAC5C,CAwCQ,8BAAAmD,GACNp6C,SAASF,iBAAiB,mBAAoBhE,KAAK47C,uBACrD,CAEQ,8BAAAiD,GACN36C,SAASimB,oBAAoB,mBAAoBnqB,KAAK47C,uBACxD,CAeQ,wBAAA2C,GACgB,oBAAXx6C,QAA0BA,OAAO+lB,UAC1C/lB,OAAO+lB,QAAQC,UAAU,KAAM,GAAIhmB,OAAO0L,SAAS8C,MACnDxO,OAAOC,iBAAiB,WAAYhE,KAAK87C,kBAE7C,CAEQ,wBAAAgD,GACN/6C,OAAOomB,oBAAoB,WAAYnqB,KAAK87C,iBAC9C,CAiBQ,uBAAA0C,GACS,CAAC,YAAa,YAAa,WAAY,SAAU,aAAc,SACvE36C,QAAQC,IACbI,SAASF,iBAAiBF,EAAO9D,KAAK+7C,eAAgB,CAAE93C,SAAS,KAErE,CAEQ,uBAAA86C,GACS,CAAC,YAAa,YAAa,WAAY,SAAU,aAAc,SACvEl7C,QAAQC,IACbI,SAASimB,oBAAoBrmB,EAAO9D,KAAK+7C,iBAE7C,CAMQ,oBAAA0C,GACNz+C,KAAKg/C,wBAA0Bj7C,OAAOK,YAAY,KAChD,MAAM+3C,GAAYx8C,KAAKC,MAAQI,KAAK8C,kBAAoB,IAExD,IAAA,MAAYksC,KAAgBhvC,KAAK64C,kBAC/B,GAAIsD,GAAYnN,EAAa,CAC3BhvC,KAAK65B,KAAK,cAAcmV,IAAe,CACrCC,aAAcD,EACdoN,iBAAkBD,IAGpB,MAAMD,EAAUl8C,KAAK64C,kBAAkBz3C,IAAI4tC,GACvCkN,IACFxxC,aAAawxC,GACbl8C,KAAK64C,kBAAkB9kC,OAAOi7B,GAElC,GAED,IACL,CAEQ,IAAAnV,CAAKgP,EAAmBriC,GAC9B,MAAM1C,EAAsB,CAC1B+C,KAAMgiC,EACNriC,OACA4lB,UAAWzsB,KAAKC,OAGZq8C,EAAYj8C,KAAKiV,UAAU7T,IAAIynC,GACjCoT,GACFA,EAAUp4C,QAAQ4S,IAChB,IACEA,EAAS3S,EACX,OAASnD,GACPF,QAAQE,MAAM,iCAAiCkoC,KAAcloC,EAC/D,IAIJ,MAAMs+C,EAAoBj/C,KAAKiV,UAAU7T,IAAI,KACzC69C,GACFA,EAAkBp7C,QAAQ4S,IACxB,IACEA,EAAS3S,EACX,OAASnD,GACPF,QAAQE,MAAM,sCAAuCA,EACvD,GAGN,ECx0BF,MAAMu+C,GAAc,YAyDb,MAAMC,WAAuBvzC,MAClC,WAAA7L,CAA4B8M,EAAgBvM,GAC1C8+C,MAAM9+C,GADoBN,KAAA6M,OAAAA,EAE1B7M,KAAKiP,KAAO,gBACd,ECtDF,MAAMowC,GAAa,QACbC,GAAqBv7C,OAAOs7C,KAAe,GAE3CE,GAAW,IAAIz3B,EAErB,GAAI3hB,MAAMC,QAAQk5C,IAAgB,CAChC,MAAME,EAAeF,GAAsBG,aAE3CH,GAAcz7C,QAASyK,IACrB,IAAKnI,MAAMC,QAAQkI,GACjB,OAGF,MAAO9D,KAAWjK,GAAQ+N,EAE1B,GAAsB,iBAAX9D,GAAyC,IAAlBA,EAAO3F,OACvC,OAOF,MAAMyL,EAAQ9F,EAAOuE,MAAM,KAC3B,IAAI2wC,EAAa,KACbC,EAAUJ,GACd,IAAA,IAAStvC,EAAI,EAAGA,EAAIK,EAAMzL,QACd,MAAN86C,EAD4B1vC,GAAK,EAErCyvC,EAAQC,EACRA,EAAKA,EAAGrvC,EAAML,IAGhB,GAAkB,mBAAP0vC,EACT,IACEA,EAAGrrC,MAAMorC,EAAOn/C,EAClB,OAASI,GACPF,QAAQE,MAAM,qDAAqD6J,MAAY7J,EACjF,MAEAF,QAAQC,KAAK,sCAAsC8J,eAInDg1C,WAAatxC,MACfqxC,GAASz2B,KAAK02B,EAAYtxC,IAAKsxC,EAAY7xC,SAAS+c,MAAO/pB,IACzDF,QAAQE,MAAM,4CAA6CA,IAGjE,QAEAoD,OAAOs7C,IAAcE,GACrBx7C,OAAO+jB,MAAQA,EACf/jB,OAAO67C,oBC1BA,MAKL,WAAA7/C,CAAYoF,GAFZnF,KAAQtB,aAAc,EAGpB,MAAMsrC,EAAgB7kC,EAAO6kC,eAAiB,IAAIsO,GAC5CrO,GAAqB9kC,EAAO6kC,cAKlChqC,KAAK4pC,QAAU,IAAID,GAAmB,CACpC39B,SAAU7G,EAAO6G,SACjBL,QAASxG,EAAOwG,QAChB7K,OAAQqE,EAAOrE,OACfw4B,UAAWn0B,EAAOm0B,UAClBC,eAAgBp0B,EAAOo0B,eACvBE,UAAWt0B,EAAOs0B,UAClBuQ,gBACAC,oBACAC,gBAAgD,IAAhC/kC,EAAO06C,qBACvBxV,eAAgBllC,EAAOklC,eACvBzQ,eAAgBz0B,EAAOy0B,iBAGzB55B,KAAK8/C,MAAQ,IAAIpW,GAAkB,CACjC19B,SAAU7G,EAAO6G,SACjBL,QAASxG,EAAOwG,QAChB7K,OAAQqE,EAAOrE,OACfw4B,UAAWn0B,EAAOm0B,UAClBC,eAAgBp0B,EAAOo0B,eACvBC,WAAYr0B,EAAOq0B,WACnBC,UAAWt0B,EAAOs0B,UAClBC,UAAWv0B,EAAOu0B,UAIlBC,sBAAwBnmB,IACtBxT,KAAK4pC,QAAQc,0BAA0Bl3B,IAEzComB,eAAgBz0B,EAAOy0B,gBAE3B,CAMA,gBAAMY,GACAx6B,KAAKtB,cACTsB,KAAKtB,aAAc,QACbgL,QAAQC,IAAI,CAChB3J,KAAK8/C,MAAMtlB,aACXx6B,KAAK4pC,QAAQpP,eAEjB,CAUA,qBAAMI,CAAgBtB,WACpB,OAAAlL,GAAApF,EAAAhpB,KAAK8/C,OAAMllB,kBAAXxM,EAAA4a,KAAAhgB,EAA6BsQ,SACvBt5B,KAAK4pC,QAAQhP,gBAAgBtB,EACrC,CASA,aAAAwG,CAAclhB,EAAmBmhB,EAAqC,YACpE,OAAA3R,GAAApF,EAAAhpB,KAAK8/C,OAAMhgB,gBAAX1R,EAAA4a,KAAAhgB,EAA2BpK,EAAWmhB,EACxC,CAmBA,gBAAAjF,CAAiBC,WACf,OAAA3M,GAAApF,EAAAhpB,KAAK8/C,OAAMhlB,mBAAX1M,EAAA4a,KAAAhgB,EAA8B+R,EAChC,CAQA,OAAA/1B,WACE,OAAAopB,GAAApF,EAAAhpB,KAAK8/C,OAAM96C,UAAXopB,EAAA4a,KAAAhgB,GACAhpB,KAAK4pC,QAAQ5kC,UACbhF,KAAKtB,aAAc,CACrB,CAQA,YAAAqhD,GAIE,OAAS//C,KAAK8/C,MAAqDpnB,WAAc,EACnF,GDnGF30B,OAAOi8C,sBEhBA,MAkBL,WAAAjgD,CAAYoF,GARZnF,KAAQigD,eAAgDp4C,IACxD7H,KAAQs9B,UAAwCz1B,IAChD7H,KAAQkgD,kBAAoBl+B,IAE5BhiB,KAAQ64B,eAAgB,EACxB74B,KAAQ84B,kBAAoB,EAC5B94B,KAAQ+4B,qBAAuB,EAG7B/4B,KAAKgM,SAAW7G,EAAO6G,SACvBhM,KAAK2L,QAAUxG,EAAOwG,SAAW,uBACjC3L,KAAKc,OAASqE,EAAOrE,OACrBd,KAAKs5B,UAAYn0B,EAAOm0B,UACxBt5B,KAAKu5B,eAAiBp0B,EAAOo0B,eAC7Bv5B,KAAKy5B,UAAYt0B,EAAOs0B,YAAa,EACrCz5B,KAAK05B,WAAiC,IAArBv0B,EAAOu0B,UACxB15B,KAAK45B,eAAiBz0B,EAAOy0B,cAC/B,CAEA,gBAAMY,GACAx6B,KAAK64B,cACP74B,KAAK4vB,IAAI,8CAIL5vB,KAAKmgD,oBAEPngD,KAAK05B,WAAa15B,KAAKu5B,gBACzBv5B,KAAK06B,aAGP16B,KAAK64B,eAAgB,EACrB74B,KAAK4vB,IAAI,4CACX,CAEA,QAAAlc,CAAS0sC,EAAqBzyC,GAC5B,MAAMgwB,EAAsB,CAC1ByiB,iBACGzyC,GAGL3N,KAAKs9B,MAAMj8B,IAAI++C,EAAaziB,GAC5B39B,KAAK4vB,IAAI,8BAA8BwwB,KAEvC,MAAMC,EAAkBrgD,KAAKigD,WAAW7+C,IAAIg/C,GACxCC,EACFrgD,KAAKsgD,WAAW3iB,EAAM0iB,GACb1yC,EAAQ4yC,iBACjBvgD,KAAKwgD,eAAe7iB,EAExB,CAEA,UAAA9pB,CAAWusC,GACTpgD,KAAKs9B,MAAMvpB,OAAOqsC,GAClBpgD,KAAKkgD,cAAcnsC,OAAOqsC,GAC1BpgD,KAAK4vB,IAAI,gCAAgCwwB,IAC3C,CAEA,uBAAMD,GACJ,IACE,MAAMM,EAAet6C,MAAM8N,KAAKjU,KAAKs9B,MAAM14B,QAE3C,GAA4B,IAAxB67C,EAAa57C,OAEf,YADA7E,KAAK4vB,IAAI,yCAIX,MAAMjnB,EAAM,GAAG3I,KAAK2L,+CAA+C80C,EAAalwC,KAAK,OAE/EjE,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,gBAAiBhM,KAAKstC,gBACtB,aAAc,OAGZttC,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAE7D,MAAMjvB,QAAiBC,MAAM5B,EAAK,CAAE2D,YAEpC,IAAKhC,EAASK,GACZ,MAAM,IAAIiB,MAAM,+BAA+BtB,EAASuC,UAG1D,MAAMrG,QAAa8D,EAASoC,OAE5B1M,KAAKigD,WAAWz4C,QAEhB,IAAA,MAAWowB,KAAapxB,EAAKy5C,YAAc,GACzCjgD,KAAKigD,WAAW5+C,IAAIu2B,EAAU8oB,aAAc9oB,GAG9C53B,KAAK2gD,iBAEL3gD,KAAK4vB,IAAI,aAAa5vB,KAAKigD,WAAWj9B,kBAExC,OAASriB,GACPX,KAAK4vB,IAAI,gCAAgCjvB,KAAS,EACpD,CACF,CAEQ,cAAAggD,GACN,IAAA,MAAYP,EAAaziB,KAAS39B,KAAKs9B,MAAM5O,UAAW,CACtD,MAAMxB,EAAUltB,KAAKigD,WAAW7+C,IAAIg/C,GAEhClzB,EACFltB,KAAKsgD,WAAW3iB,EAAMzQ,GACbyQ,EAAK4iB,iBACdvgD,KAAKwgD,eAAe7iB,EAExB,CACF,CAEQ,UAAA2iB,CAAW3iB,EAAqBzQ,GACtC,IACE,GAA6B,sBAAzBA,EAAQ0zB,aACV5gD,KAAK6gD,uBAAuB3zB,OACvB,CACL,MAAMkY,EAAYlhC,SAASukC,eAAe9K,EAAKmjB,aAE/C,IAAK1b,EAEH,YADAplC,KAAK4vB,IAAI,wBAAwB+N,EAAKmjB,eAAe,GAMvD,OAFA1b,EAAUyN,UAAY,GAEd3lB,EAAQ0zB,cACd,IAAK,SACH5gD,KAAK0gC,aAAa0E,EAAWlY,GAC7B,MACF,IAAK,OACHltB,KAAK+gD,WAAW3b,EAAWlY,GAC3B,MACF,IAAK,WACHltB,KAAKghD,eAAe5b,EAAWlY,GAC/B,MACF,IAAK,QACHltB,KAAKihD,YAAY7b,EAAWlY,GAC5B,MACF,IAAK,OACHltB,KAAKkhD,WAAW9b,EAAWlY,GAC3B,MACF,QAEE,YADAltB,KAAK4vB,IAAI,yBAAyB1C,EAAQ0zB,gBAAgB,GAGhE,CAEK5gD,KAAKkgD,cAActsC,IAAI+pB,EAAKyiB,eAC/BpgD,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,cAC1Dra,KAAKkgD,cAAcr9B,IAAI8a,EAAKyiB,cAG1BziB,EAAKwjB,UACPxjB,EAAKwjB,SAASj0B,GAGhBltB,KAAK4vB,IAAI,uBAAuB1C,EAAQwzB,iBAAiBxzB,EAAQ0zB,gBAEnE,OAASjgD,GACPX,KAAK4vB,IAAI,8BAA8BjvB,KAAS,GAE5Cg9B,EAAKnpB,SACPmpB,EAAKnpB,QAAQ7T,EAEjB,CACF,CAEQ,YAAA+/B,CAAa0E,EAAwBlY,GAC3C,MAAM5a,MAAEA,OAAO9F,EAAAqO,UAAMA,EAAAoX,SAAWA,UAAUP,EAAArB,iBAASA,EAAAE,WAAkBA,GAAerD,EAAQA,QAEtFmZ,EAASniC,SAASusB,cAAc,OAatC,GAZA4V,EAAO3V,UAAY,yBACnB2V,EAAO1V,MAAMC,QAAU,uBACP5wB,KAAK2vB,cAAcU,GAAoB,6BAC5CrwB,KAAK2vB,cAAcY,GAAc,gOASxC1V,EAAW,CACb,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAY7U,GAC7BqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAM,GACVH,EAAIhB,MAAMC,QAAU,oEACpByV,EAAOpV,YAAYU,GAEvB,CAEA,MAAM4U,EAAgBriC,SAASusB,cAAc,OAG7C,GAFA8V,EAAc5V,MAAMC,QAAU,WAE1Bte,EAAO,CACT,MAAMulB,EAAU3zB,SAASusB,cAAc,OACvCoH,EAAQ7G,YAAc1e,EACtBulB,EAAQlH,MAAMC,QAAU,yDACxB2V,EAActV,YAAY4G,EAC5B,CAEA,GAAIrrB,EAAM,CACR,MAAMsrB,EAAS5zB,SAASusB,cAAc,OACtCqH,EAAO9G,YAAcxkB,EACrBsrB,EAAOnH,MAAMC,QAAU,iCACvB2V,EAActV,YAAY6G,EAC5B,CAIA,GAFAuO,EAAOpV,YAAYsV,GAEftU,GAAYP,EAAS,CACvB,MAAM+U,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAciB,EACxBwU,EAAU9V,MAAMC,QAAU,gDAEf5wB,KAAK2vB,cAAcU,GAAoB,oNAUlDoW,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,SAC1D,MAAM6qB,EAAUllC,KAAK0vB,YAAYgC,GAC7BwT,IACFnhC,OAAO0L,SAAS8C,KAAO2yB,KAI3BmB,EAAOpV,YAAYwV,EACrB,CAEArB,EAAUnU,YAAYoV,EACxB,CAEQ,UAAA0a,CAAW3b,EAAwBlY,GACzC,MAAM5a,MAAEA,EAAA9F,KAAOA,EAAAqO,UAAMA,WAAWoX,EAAAP,QAAUA,GAAYxE,EAAQA,QAExD2D,EAAO3sB,SAASusB,cAAc,OAUpC,GATAI,EAAKH,UAAY,uBACjBG,EAAKF,MAAMC,QAAU,6NAQjB/V,EAAW,CACb,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAY7U,GAC7BqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAMxf,GAAS,GACnBqf,EAAIhB,MAAMC,QAAU,iDACpBC,EAAKI,YAAYU,GAErB,CAEA,MAAMyvB,EAAWl9C,SAASusB,cAAc,OAGxC,GAFA2wB,EAASzwB,MAAMC,QAAU,iBAErBte,EAAO,CACT,MAAMulB,EAAU3zB,SAASusB,cAAc,MACvCoH,EAAQ7G,YAAc1e,EACtBulB,EAAQlH,MAAMC,QAAU,yEACxBwwB,EAASnwB,YAAY4G,EACvB,CAEA,GAAIrrB,EAAM,CACR,MAAMsrB,EAAS5zB,SAASusB,cAAc,KACtCqH,EAAO9G,YAAcxkB,EACrBsrB,EAAOnH,MAAMC,QAAU,sEACvBwwB,EAASnwB,YAAY6G,EACvB,CAEA,GAAI7F,GAAYP,EAAS,CACvB,MAAM+U,EAAYviC,SAASusB,cAAc,UACzCgW,EAAUzV,YAAciB,EACxBwU,EAAU9V,MAAMC,QAAU,gOAW1B6V,EAAUziC,iBAAiB,QAAS,KAClChE,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,SAC1D,MAAM6qB,EAAUllC,KAAK0vB,YAAYgC,GAC7BwT,IACFnhC,OAAO0L,SAAS8C,KAAO2yB,KAI3Bkc,EAASnwB,YAAYwV,EACvB,CAEA5V,EAAKI,YAAYmwB,GACjBhc,EAAUnU,YAAYJ,EACxB,CAEQ,cAAAmwB,CAAe5b,EAAwBlY,GAC7C,MAAMoI,MAAEA,GAAUpI,EAAQA,QAE1B,IAAK/mB,MAAMC,QAAQkvB,IAA2B,IAAjBA,EAAMzwB,OAEjC,YADA7E,KAAK4vB,IAAI,mCAAmC,GAI9C,MAAMyxB,EAAWn9C,SAASusB,cAAc,OACxC4wB,EAAS3wB,UAAY,2BACrB2wB,EAAS1wB,MAAMC,QAAU,2KASzB,IAAA,MAAWtiB,KAAQgnB,EAAO,CACxB,MAAMgsB,EAAep9C,SAASusB,cAAc,OAW5C,GAVA6wB,EAAa3wB,MAAMC,QAAU,4RAUzBtiB,EAAKuM,UAAW,CAClB,MAAM8W,EAAMztB,SAASusB,cAAc,OAC7ByU,EAAUllC,KAAK0vB,YAAYphB,EAAKuM,WAClCqqB,IACFvT,EAAIE,IAAMqT,EACVvT,EAAIG,IAAMxjB,EAAKgE,OAAS,GACxBqf,EAAIhB,MAAMC,QAAU,iDACpB0wB,EAAarwB,YAAYU,GAE7B,CAEA,MAAM4vB,EAAcr9C,SAASusB,cAAc,OAG3C,GAFA8wB,EAAY5wB,MAAMC,QAAU,iBAExBtiB,EAAKgE,MAAO,CACd,MAAMA,EAAQpO,SAASusB,cAAc,OACrCne,EAAM0e,YAAc1iB,EAAKgE,MACzBA,EAAMqe,MAAMC,QAAU,yEACtB2wB,EAAYtwB,YAAY3e,EAC1B,CAEA,GAAIhE,EAAK4L,MAAO,CACd,MAAMA,EAAQhW,SAASusB,cAAc,OACrCvW,EAAM8W,YAAc1iB,EAAK4L,MACzBA,EAAMyW,MAAMC,QAAU,qDACtB2wB,EAAYtwB,YAAY/W,EAC1B,CAEAonC,EAAarwB,YAAYswB,GAErBjzC,EAAK3F,KACP24C,EAAat9C,iBAAiB,QAAS,KACrChE,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,QAAS,CAAEmnC,WAAYlsB,EAAMllB,QAAQ9B,KAC/F,MAAM42B,EAAUllC,KAAK0vB,YAAYphB,EAAK3F,KAClCu8B,IACFnhC,OAAO0L,SAAS8C,KAAO2yB,KAK7Bmc,EAASpwB,YAAYqwB,EACvB,CAEAlc,EAAUnU,YAAYowB,EACxB,CAEQ,WAAAJ,CAAY7b,EAAwBlY,GAC1C,MAAM+Z,UAAEA,EAAAwa,WAAWA,EAAAxxB,SAAYA,EAAAkX,MAAUA,GAAUja,EAAQA,QAE3D,IAAK+Z,EAEH,YADAjnC,KAAK4vB,IAAI,qBAAqB,GAIhC,MAAM8xB,EAAe1hD,KAAK0vB,YAAYuX,GACtC,IAAKya,EAEH,YADA1hD,KAAK4vB,IAAI,qBAAqB,GAIhC,MAAMsX,EAAQhjC,SAASusB,cAAc,SAOrC,GANAyW,EAAMrV,IAAM6vB,EACZxa,EAAMnP,UAAW,EACjBmP,EAAMjX,SAAWA,IAAY,EAC7BiX,EAAMC,MAAQA,IAAS,EACvBD,EAAMvW,MAAMC,QAAU,mCAElB6wB,EAAY,CACd,MAAME,EAAgB3hD,KAAK0vB,YAAY+xB,GACnCE,IACFza,EAAM0a,OAASD,EAEnB,CAEAza,EAAMljC,iBAAiB,OAAQ,KAC7BhE,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,QAAS,CAAEwnC,OAAQ,iBAG/Ezc,EAAUnU,YAAYiW,EACxB,CAEQ,UAAAga,CAAW9b,EAAwBlY,GACzC,MAAM40B,KAAEA,GAAS50B,EAAQA,QAEzB,IAAK40B,EAEH,YADA9hD,KAAK4vB,IAAI,wBAAwB,GAInC,MAAMmyB,EAAU79C,SAASusB,cAAc,OACvCsxB,EAAQrxB,UAAY,uBACpBqxB,EAAQlP,UAAY7yC,KAAKgiD,aAAaF,GAExBC,EAAQlvB,iBAAiB,KACjChvB,QAASo+C,IACbA,EAAKj+C,iBAAiB,QAAS,KAC7BhE,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,aAI9D+qB,EAAUnU,YAAY8wB,EACxB,CAEQ,sBAAAlB,CAAuB3zB,GAC7B,MAAM40B,KAAEA,GAAS50B,EAAQA,QACnBg1B,EAAch1B,EAAQi1B,aACtBC,EAAgBl1B,EAAQm1B,gBAAkB,UAEhD,IAAKH,EAEH,YADAliD,KAAK4vB,IAAI,8CAA8C,GAIzD,IAAKkyB,EAEH,YADA9hD,KAAK4vB,IAAI,8CAA8C,GAIzD,IAAI0yB,EAAoC,KAExC,IACEA,EAAgBp+C,SAASqzB,cAAc2qB,EACzC,OAASvhD,GAEP,YADAX,KAAK4vB,IAAI,yBAAyBsyB,KAAe,EAEnD,CAEA,IAAKI,EAEH,YADAtiD,KAAK4vB,IAAI,0CAA0CsyB,KAAe,GAIpE,MAAMH,EAAU79C,SAASusB,cAAc,OACvCsxB,EAAQrxB,UAAY,0BACpBqxB,EAAQx7B,aAAa,oBAAqB2G,EAAQwzB,cAClDqB,EAAQx7B,aAAa,kBAAmB2G,EAAQ7S,YAChD0nC,EAAQlP,UAAY7yC,KAAKgiD,aAAaF,GAStC,OAPcC,EAAQlvB,iBAAiB,KACjChvB,QAASo+C,IACbA,EAAKj+C,iBAAiB,QAAS,KAC7BhE,KAAK0sB,WAAWQ,EAAQwzB,aAAcxzB,EAAQ7S,WAAY,aAItD+nC,GACN,IAAK,UACHE,EAAczP,UAAY,GAC1ByP,EAAcrxB,YAAY8wB,GAC1B/hD,KAAK4vB,IAAI,uBAAuBsyB,KAAe,GAC/C,MAEF,IAAK,SACHI,EAAcrxB,YAAY8wB,GAC1B/hD,KAAK4vB,IAAI,uBAAuBsyB,KAAe,GAC/C,MAEF,IAAK,UACHI,EAAcC,aAAaR,EAASO,EAAcE,YAClDxiD,KAAK4vB,IAAI,wBAAwBsyB,KAAe,GAChD,MAEF,QAEE,YADAliD,KAAK4vB,IAAI,2BAA2BwyB,KAAiB,GAIzDpiD,KAAK4vB,IAAI,oCAAoC1C,EAAQwzB,qBAAqBwB,MAAgBE,KAC5F,CAEQ,cAAA5B,CAAe7iB,GACrB,IAAKA,EAAK4iB,gBAAiB,OAE3B,MAAMnb,EAAYlhC,SAASukC,eAAe9K,EAAKmjB,aAC1C1b,IAELA,EAAUyN,UAAYlV,EAAK4iB,gBAC3BvgD,KAAK4vB,IAAI,kCAAkC+N,EAAKyiB,eAClD,CAEA,gBAAc1zB,CACZ0zB,EACAqC,EACA5Z,EACAtG,EAAgC,CAAA,SAEhC,IACE,MAAM55B,EAAM,GAAG3I,KAAK2L,8BAEdW,EAAkC,CACtC,eAAgB,mBAChB,oBAAqBtM,KAAKgM,SAC1B,gBAAiBhM,KAAKstC,gBACtB,aAAc,OAGZttC,KAAKc,SAAQwL,EAAQ,aAAetM,KAAKc,QACzCd,KAAKs5B,YAAWhtB,EAAQ,gBAAkBtM,KAAKs5B,WAC/Ct5B,KAAKu5B,iBAAgBjtB,EAAQ,qBAAuBtM,KAAKu5B,gBAM7D,MAEM/sB,EAAO,CACXk0C,aAAcN,EACd/lC,WAAYooC,EACZxZ,WAAYJ,EACZhsC,aANkB,OAAAmsB,OAAK4Q,qBAAL,EAAA5Q,EAAAggB,KAAAhpC,MAOlBuiC,SAAU,CACRlG,YAAar8B,KAAKstC,gBAClBjE,SAAU,SACP9G,IAIPh4B,MAAM5B,EAAK,CACT6B,OAAQ,OACR8B,UACAE,KAAMhL,KAAKM,UAAU0K,KACpBke,MAAMvL,GAAOnf,KAAK4vB,IAAI,uBAAuBzQ,KAAO,IAEvDnf,KAAK4vB,IAAI,WAAWiZ,MAAcuX,IAEpC,OAASz/C,GACPX,KAAK4vB,IAAI,yBAAyBjvB,KAAS,EAC7C,CACF,CAEQ,UAAA+5B,GAKN,GAJI16B,KAAK67B,aACP77B,KAAK66B,iBAGF76B,KAAKu5B,eAER,YADAv5B,KAAK4vB,IAAI,8CAA8C,GAIzD,MAAMjnB,EAAM,IAAIgI,IAAI,sBAAuB3Q,KAAK2L,SAE1CW,EAAkC,CACtC,oBAAqBtM,KAAKgM,SAC1B,oBAAqBhM,KAAKu5B,gBAGxBv5B,KAAKs5B,YACPhtB,EAAQ,gBAAkBtM,KAAKs5B,WAGjC,MAAMwC,EAAc,IAAIlrB,gBACxBjM,OAAO+pB,QAAQpiB,GAASzI,QAAQ,EAAEqK,EAAKC,MACrC2tB,EAAYC,OAAO7tB,EAAKC,KAG1BnO,KAAK67B,YAAc,IAAIG,YAAY,GAAGrzB,KAAOmzB,EAAYr8B,cAEzDO,KAAK67B,YAAY73B,iBAAiB,OAAQ,KACxChE,KAAK4vB,IAAI,8BACT5vB,KAAK84B,kBAAoB,IAG3B94B,KAAK67B,YAAY73B,iBAAiB,4BAA8BF,IAC9D,IACE,MAAM0C,EAAOhF,KAAKC,MAAMqC,EAAM0C,MAC9BxG,KAAK4vB,IAAI,sCAAsCppB,EAAKk6C,gBACpD1gD,KAAKmgD,mBACP,OAASx/C,GACPX,KAAK4vB,IAAI,4BAA4BjvB,KAAS,EAChD,IAGFX,KAAK67B,YAAY73B,iBAAiB,YAAa,KAC7ChE,KAAK4vB,IAAI,4BAGX5vB,KAAK67B,YAAY73B,iBAAiB,QAAS,WACzChE,KAAK4vB,IAAI,wBAAwB,IAE7B,OAAA5G,EAAAhpB,KAAK67B,kBAAL,EAAA7S,EAAkBiT,cAAeD,YAAYE,QAC/Cl8B,KAAKm8B,oBAGX,CAEQ,aAAAtB,GACF76B,KAAK67B,cACP77B,KAAK67B,YAAYO,QACjBp8B,KAAK67B,iBAAc,EACnB77B,KAAK4vB,IAAI,yBAEb,CAEQ,gBAAAuM,GACN,GAAIn8B,KAAK84B,mBAAqB94B,KAAK+4B,qBAEjC,YADA/4B,KAAK4vB,IAAI,6CAA6C,GAIxD5vB,KAAK84B,oBACL,MAAM1rB,EAAQ7N,KAAK2f,IAAI,IAAO3f,KAAK8N,IAAI,EAAGrN,KAAK84B,mBAAoB,KAEnE94B,KAAK4vB,IAAI,uBAAuBxiB,gBAAoBpN,KAAK84B,sBAEzD1uB,WAAW,KACLpK,KAAK64B,eAAiB74B,KAAK05B,WAAa15B,KAAKu5B,gBAC/Cv5B,KAAK06B,cAENttB,EACL,CAEQ,aAAAkgC,GACN,MAAMr6B,EAAQlP,OAAOuP,WACrB,OAAIL,EAAQ,IAAY,aACpBA,EAAQ,KAAa,aAClB,aACT,CAEQ,WAAAyc,CAAY/mB,GAClB,IACE,MAAM6M,EAAS,IAAI7E,IAAIhI,EAAK5E,OAAO0L,SAAS8C,MAE5C,MAAwB,gBAApBiD,EAAO9F,UAAkD,UAApB8F,EAAO9F,UAC9C1P,KAAK4vB,IAAI,gCAAgCpa,EAAO9F,YAAY,GACrD,MAGF8F,EAAOjD,IAChB,CAAA,MAEE,OADAvS,KAAK4vB,IAAI,gBAAgBjnB,KAAO,GACzB,IACT,CACF,CAEQ,aAAAgnB,CAAcgT,GAKpB,MAJmB,sBAIJ70B,KAAK60B,IAHD,2DAGsB70B,KAAK60B,IAF1B,CAAC,QAAS,QAAS,MAAO,OAAQ,QAAS,SAAU,eAELzrB,SAASyrB,EAAM3e,eAC1E2e,GAGT3iC,KAAK4vB,IAAI,kBAAkB+S,KAAS,GAC7B,UACT,CAEQ,YAAAqf,CAAaF,GAEnB,MAAMY,EAAU3+C,OAA8C4+C,UAI9D,SAAID,WAAQE,SACV,OAAOF,EAAOE,SAASd,EAAM,CAC3Be,aAAc,CACZ,IAAK,IAAK,KAAM,MAAO,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAC3D,IAAK,MAAO,KAAM,KAAM,IAAK,OAAQ,SAAU,IAAK,KAAM,QAC1D,QAAS,QAAS,KAAM,KAAM,KAAM,aAAc,KAAM,SACxD,aAAc,UAAW,SAAU,QAAS,UAAW,WAEzDC,aAAc,CACZ,OAAQ,SAAU,MAAO,MAAO,MAAO,QAAS,SAAU,QAC1D,KAAM,QAAS,QAAS,UAAW,SAAU,QAAS,OACtD,QAAS,WAAY,UAEvBC,iBAAiB,IAKrB/iD,KAAK4vB,IACH,gMAEA,GAEF,MAAMozB,EAAU9+C,SAASusB,cAAc,OAEvC,OADAuyB,EAAQhyB,YAAc8wB,EACfkB,EAAQnQ,SACjB,CAEQ,GAAAjjB,CAAItvB,EAAiB+3C,GAAU,IACjCr4C,KAAKy5B,WAAa4e,IACpB53C,QAAQ43C,EAAU,QAAU,OAAO,qBAAqB/3C,IAE5D,CAEA,OAAA0E,GACEhF,KAAK66B,gBACL76B,KAAKs9B,MAAM91B,QACXxH,KAAKigD,WAAWz4C,QAChBxH,KAAKkgD,cAAc14C,QACnBxH,KAAK64B,eAAgB,EACrB74B,KAAK4vB,IAAI,4BACX,GF/tBF7rB,OAAOu0C,cAAgBA,GACvBv0C,OAAOk/C,eD+CP35C,eACEqC,EACAu3C,GAEA,MAAM12C,EAAgC,CAAER,SAAUk3C,EAAIl3C,UAClDk3C,EAAIC,gBAAe32C,EAAK22C,cAAgBD,EAAIC,eAC5CD,EAAIE,qBAAoB52C,EAAK42C,mBAAqBF,EAAIE,oBACtDF,EAAIG,mBAAkB72C,EAAK62C,iBAAmBH,EAAIG,kBAClDH,EAAIjyC,YAAWzE,EAAKyE,UAAYiyC,EAAIjyC,WAExC,MAAMtI,EAAM,GAAGgD,EAAQvM,QAAQ,MAAO,uBAEhCkkD,EAAUh6C,SACdiB,MAAM5B,EAAK,CACT6B,OAAQ,OACR8B,QAAS,CAAE,eAAgB,oBAC3BE,KAAMhL,KAAKM,UAAU0K,GACrBowB,YAAa,SAGjB,IAAI2mB,QAAaD,IAMjB,GALIC,EAAK12C,QAAU,YACX,IAAInD,QAASpK,GAAM8K,WAAW9K,EAAG,MACvCikD,QAAaD,KAGK,MAAhBC,EAAK12C,OACP,MAAM,IAAIsyC,GAAe,IAAK,sCAEhC,GAAoB,MAAhBoE,EAAK12C,OACP,MAAM,IAAIsyC,GAAe,IAAK,6EAEhC,IAAKoE,EAAK54C,GACR,MAAM,IAAIw0C,GAAeoE,EAAK12C,OAAQ,0BAA0B02C,EAAK12C,UAGvE,MAAMH,QAAc62C,EAAK72C,OAEzB,OAnEK,SAA+B82C,GACpC,GAAwB,oBAAbt/C,SAA0B,OACrC,MAAMu/C,MAAc9jD,KACpB8jD,EAAQC,QAAQD,EAAQE,UAlFE,KAyF1Bz/C,SAAS4K,OACP,GAAGowC,MAAermC,mBAAmB2qC,cAC1BC,EAAQl0C,oCAIrB,IACExL,OAAOgK,aAAaC,QAAQkxC,GAAasE,EAC3C,CAAA,MAEA,CACF,CA6CEI,CAAsBl3C,EAAK02C,oBACpB12C,CACT"}
|