@magicfeedback/popup-sdk 1.1.0 → 1.1.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react-native/index.tsx","../src/types/index.ts","../src/triggers/index.ts","../src/config/env.ts","../src/ui/surveyHtml.ts","../src/platform/react-native-renderer.ts","../src/platform/renderer.ts","../src/analytics/analytics-manager.ts","../src/analytics/feedback-payload.ts","../src/tracking/navigation-observer.ts","../src/analytics/device-info.ts","../src/analytics/geo-info.ts","../src/analytics/engagement-tracker.ts","../src/contact/contact-manager.ts","../src/core/deepdots-popups.ts","../src/react-native/setup.ts"],"sourcesContent":["/**\n * Entry de React Native: `@magicfeedback/popup-sdk/react-native`.\n *\n * El cliente solo envuelve su app:\n * <DeepdotsProvider config={{ apiKey, nodeEnv }}>...</DeepdotsProvider>\n * y el SDK hace TODO (storage persistente, device info, platform, lifecycle/engagement,\n * y render de surveys en WebView). Sin pegar nada en su proyecto.\n *\n * Peer deps: react, react-native, react-native-webview. Opcionales (auto-detectados):\n * react-native-mmkv (persistencia), react-native-device-info (device info).\n */\nimport React, { createContext, useContext, useEffect, useRef, useState } from 'react';\nimport { AppState, Modal, Platform, View } from 'react-native';\nimport { WebView } from 'react-native-webview';\n\nimport { DeepdotsPopups } from '../core/deepdots-popups';\nimport { ReactNativePopupRenderer, type ReactNativeSurveyPayload } from '../platform/react-native-renderer';\nimport { setupReactNative } from './setup';\nimport type { DeepdotsInitParams } from '../types';\n\nconst sdk = new DeepdotsPopups();\nconst DeepdotsContext = createContext<DeepdotsPopups>(sdk);\n\n/** Acceso al SDK desde cualquier componente: `const dd = useDeepdots()`. */\nexport const useDeepdots = (): DeepdotsPopups => useContext(DeepdotsContext);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction optionalRequire(name: string): any {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n return require(name);\n } catch {\n return null;\n }\n}\n\nexport interface DeepdotsProviderProps {\n config: DeepdotsInitParams;\n children?: React.ReactNode;\n}\n\nexport function DeepdotsProvider({ config, children }: DeepdotsProviderProps) {\n const [survey, setSurvey] = useState<ReactNativeSurveyPayload | null>(null);\n const rendererRef = useRef<ReactNativePopupRenderer | null>(null);\n\n useEffect(() => {\n const renderer = new ReactNativePopupRenderer({\n onShow: (p) => setSurvey(p),\n onHide: () => setSurvey(null),\n });\n rendererRef.current = renderer;\n\n const MmkvMod = optionalRequire('react-native-mmkv');\n const mmkv = MmkvMod?.MMKV ? new MmkvMod.MMKV({ id: 'deepdots-sdk' }) : null;\n const deviceInfo = optionalRequire('react-native-device-info')?.default ?? null;\n\n const dispose = setupReactNative(sdk, config, {\n mmkv,\n deviceInfo,\n appState: AppState,\n platform: Platform.OS === 'ios' ? 'ios' : 'android',\n renderer,\n });\n return dispose;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return (\n <DeepdotsContext.Provider value={sdk}>\n {children}\n {survey ? (\n <Modal visible transparent animationType=\"slide\">\n <View style={{ flex: 1 }}>\n <WebView\n originWhitelist={['*']}\n javaScriptEnabled\n source={{ html: survey.html }}\n onMessage={(e: { nativeEvent: { data: string } }) =>\n rendererRef.current?.handleMessage(e.nativeEvent.data)\n }\n />\n </View>\n </Modal>\n ) : null}\n </DeepdotsContext.Provider>\n );\n}\n\nexport { setupReactNative } from './setup';\nexport { ReactNativePopupRenderer } from '../platform/react-native-renderer';\n","/**\n * Configuration options for initializing the DeepdotsPopups SDK\n */\nimport type { KeyValueStorage } from '../tracking/tracking-manager';\nimport type { DeviceInfo } from '../analytics/device-info';\nimport type { AnalyticsKeys } from '../analytics/feedback-payload';\n\nexport interface DeepdotsInitParams {\n /** API key for authentication */\n apiKey?: string;\n /** Node environment: 'development' or 'production' */\n nodeEnv?: 'development' | 'production';\n /** Enable debug logging */\n debug?: boolean;\n /** Optional user id to send with popup events */\n userId?: string;\n /** Starts tracking enabled (default) or disabled (e.g. until consent is granted). Equivalent to calling `setTrackingEnabled` after init. */\n trackingEnabled?: boolean;\n /** Host app version (for analytics device info / Technology #11). */\n appVersion?: string;\n /** Injectable persistent storage (RN: adapter over AsyncStorage). If missing, uses localStorage/in-memory. */\n storage?: KeyValueStorage;\n /** Platform used in the analytics envelope. Default is 'web'; in RN pass 'android'/'ios'. */\n platform?: 'web' | 'android' | 'ios';\n /** Injectable device info (RN: from react-native-device-info). If missing, it is derived from the browser. */\n device?: DeviceInfo;\n /**\n * Analytics channel keys (`POST /sdk/feedback`). If provided, analytics is SENT\n * to the configured integration; if missing, it stays in dry-run mode (console.log only).\n */\n analytics?: AnalyticsKeys;\n /**\n * Internal user attributes known only by the host (language, age, plan, ...), sent\n * to the backend Contact for segmentation/targeting. Requires `userId`. Equivalent to calling\n * `setContactAttributes` after init (sends only when values changed from the last sync).\n */\n contactAttributes?: Record<string, string | number | boolean>;\n\n}\nexport interface DeepdotsConfig {\n /** API key for authentication */\n apiKey?: string;\n /** Enable debug logging */\n debug?: boolean;\n /** Optional user id to send with popup events */\n userId?: string;\n}\n\n/**\n * Options for configuring survey triggers\n */\nexport interface TriggerConfig {\n /** Trigger type: 'time' (delay), 'scroll' (scroll percentage), 'exit' (route exit), 'click' (element id), 'event' (host event) */\n type: 'time' | 'scroll' | 'exit' | 'click' | 'event';\n /** Value for the trigger (milliseconds for time, percentage for scroll, seconds for exit, element id for click, event name for event) */\n value?: number | string;\n /** Survey ID to show when triggered */\n surveyId: string;\n /** Optional popup definition id (used to disambiguate repeated survey ids) */\n popupId?: string;\n}\n\n/**\n * Event types emitted by the SDK\n */\nexport type DeepdotsEventType =\n | 'popup_shown'\n | 'popup_clicked'\n | 'survey_completed';\n\n/**\n * Event payload structure\n */\nexport interface DeepdotsEvent {\n type: DeepdotsEventType;\n surveyId: string;\n timestamp: number;\n data?: Record<string, unknown>;\n}\n\n/**\n * Event listener callback\n */\nexport type EventListener = (event: DeepdotsEvent) => void;\n\n/** Trigger type used by remote popup definitions */\nexport type PopupTriggerType = 'time_on_page' | 'scroll' | 'exit' | 'click' | 'event';\n\nexport type PopupTriggerConditionStatus = 'SHOWED' | 'PARTIAL' | 'COMPLETED';\n\nexport const POPUP_TRIGGER_CONDITION_STATUSES: PopupTriggerConditionStatus[] = [\n 'SHOWED',\n 'PARTIAL',\n 'COMPLETED',\n];\n\n/** Extra condition used to activate the popup */\nexport interface PopupTriggerCondition {\n answered: PopupTriggerConditionStatus; // user progress status in the survey\n cooldownDays: number; // cooldown days before showing again\n}\n\n/** Trigger attached to the popup definition */\nexport interface PopupTrigger {\n type: PopupTriggerType;\n value: number | string; // seconds on page, scroll percentage, click id, or event name\n}\n\n/** Accept action (open survey) */\nexport interface PopupActionAccept {\n label: string;\n surveyId: string; // survey id to launch\n}\n\n/** Decline action */\nexport interface PopupActionDecline {\n label: string;\n cooldownDays?: number; // optional: do not show again until X days pass\n}\n\n/** Start Surveys Action */\nexport interface PopupActionStart {\n label: string;\n}\n\n/** Complete action (accept and auto-complete survey) */\n// Requires the survey to support auto-completion via parameters.\nexport interface PopupActionComplete {\n label: string;\n surveyId: string; // survey id to launch\n autoCompleteParams: Record<string, unknown>; // parameters used for auto-completion\n cooldownDays?: number;\n}\n\n/** Set of actions available in the popup */\nexport interface PopupActions {\n accept?: PopupActionAccept;\n decline?: PopupActionDecline;\n complete?: PopupActionComplete;\n start?: PopupActionStart;\n back?: PopupActionDecline;\n}\n\n/** Configurable popup styles */\nexport interface PopupStyle {\n theme: 'light' | 'dark';\n position: 'bottom' | 'bottom-right' | 'bottom-left' | 'top' | 'top-right' | 'top-left' | 'center';\n /** @deprecated Not used by the SDK; kept only for API compatibility */\n imageUrl?: string | null;\n}\n\n/** Segments/targeting rules for showing the popup */\nexport interface PopupSegments {\n lang?: string[]; // allowed languages\n path?: string[]; // app routes where it can be shown\n [key: string]: unknown; // possible future extension\n}\n\n/** Full popup definition */\nexport interface PopupDefinition {\n id: string;\n title: string;\n message: string; // rendered safe HTML (recommended to sanitize upstream)\n triggers: PopupTrigger[];\n cooldown?: PopupTriggerCondition[];\n actions?: PopupActions;\n surveyId: string;\n productId: string;\n style?: PopupStyle;\n segments?: PopupSegments;\n}\n\nexport interface FormStyle\n{\n id: string;\n logo: string;\n title: string;\n companyName: string;\n backgroundColor: string;\n boxBackgroundColor: string;\n buttonOnTopColor: string;\n buttonPrimaryColor: string\n buttonSecondaryColor: string;\n loadingBarColor: string;\n favIcon: string;\n startMessage: string;\n successMessage: string;\n addLogoInMsg: boolean;\n privacyPolicyUrl: string;\n helpUrl: string;\n integrationId: string;\n redirectLink: string;\n redirectError: string;\n redirectTime: number;\n integrationThemeId: string;\n contentAlign: 'top' | 'center';\n logoSize: 'small' | 'medium' | 'large';\n logoPosition: 'left' | 'right' | 'center';\n showProgressBar: boolean;\n showProgressUnit: boolean;\n progressUnit: 'percentage' | 'fraction';\n}\n\n\nexport interface FormData {\n id: string;\n name: string;\n description: string;\n type: string;\n identity: string;\n status: string;\n createdAt: Date;\n updatedAt: Date;\n externalId?: string | null;\n companyId: string;\n productId: string;\n userId: string;\n style: FormStyle\n}\n\nexport enum POPUPSESSIONSTATUS {\n NONE = \"NONE\",\n SHOWED = \"SHOWED\",\n PARTIAL = \"PARTIAL\",\n COMPLETED = \"COMPLETED\",\n DEPRECATED = \"DEPRECATED\",\n}\n","import type { TriggerConfig } from '../types';\nimport type { DeepdotsPopups } from '../core/deepdots-popups';\n\nexport function setupTrigger(popups: DeepdotsPopups, trigger: TriggerConfig): void {\n switch (trigger.type) {\n case 'time':\n setupTimeTrigger(popups, trigger);\n break;\n case 'scroll':\n setupScrollTrigger(popups, trigger);\n break;\n case 'exit':\n setupExitTrigger(popups, trigger);\n break;\n case 'event':\n setupEventTrigger(popups, trigger);\n break;\n case 'click':\n setupClickTrigger(popups, trigger);\n break;\n }\n}\n\nfunction setupTimeTrigger(popups: DeepdotsPopups, trigger: TriggerConfig): void {\n const delay = trigger.value || 5000;\n setTimeout(() => {\n popups.triggerSurvey(trigger.surveyId, trigger.popupId);\n }, delay);\n popups.debug(`Time trigger set for ${delay}ms`);\n}\n\nfunction setupScrollTrigger(popups: DeepdotsPopups, trigger: TriggerConfig): void {\n const threshold = trigger.value || 50;\n const handler = () => {\n const scrollPercentage = (window.scrollY / (document.documentElement.scrollHeight - window.innerHeight)) * 100;\n if (scrollPercentage >= threshold) {\n popups.triggerSurvey(trigger.surveyId, trigger.popupId);\n window.removeEventListener('scroll', handler);\n }\n };\n window.addEventListener('scroll', handler);\n popups.debug(`Scroll trigger set for ${threshold}%`);\n}\n\nfunction setupExitTrigger(popups: DeepdotsPopups, trigger: TriggerConfig): void {\n if (typeof window === 'undefined') return;\n\n const delaySeconds = parseExitDelaySeconds(trigger.value);\n const historyRef = window.history;\n const originalPushState = historyRef.pushState.bind(historyRef);\n const originalReplaceState = historyRef.replaceState.bind(historyRef);\n\n let lastUrl = normalizeUrl(window.location.href);\n let lastQueuedKey = '';\n let lastQueuedAt = 0;\n\n const queueLeave = (fromRaw: string, toRaw: string) => {\n const from = normalizeUrl(fromRaw);\n const to = normalizeUrl(toRaw);\n if (!from || !to || from === to) return;\n\n const key = `${from}=>${to}`;\n const now = Date.now();\n if (key === lastQueuedKey && now - lastQueuedAt < 150) return;\n lastQueuedKey = key;\n lastQueuedAt = now;\n popups.queueExitPopup(trigger.surveyId, delaySeconds, from, trigger.popupId);\n };\n\n const handleNavigation = (to: string) => {\n const from = lastUrl;\n queueLeave(from, to);\n lastUrl = normalizeUrl(to);\n };\n\n const handleDocumentClick = (event: MouseEvent) => {\n if (event.defaultPrevented) return;\n if (event.button !== 0) return;\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n\n const anchor = (event.target as Element | null)?.closest?.('a');\n if (!(anchor instanceof HTMLAnchorElement)) return;\n\n const href = anchor.getAttribute('href');\n if (!href || href.startsWith('javascript:') || href.startsWith('mailto:') || href.startsWith('tel:')) {\n return;\n }\n const target = anchor.getAttribute('target');\n if (target && target !== '_self') return;\n\n let destination: URL;\n try {\n destination = new URL(href, window.location.href);\n } catch {\n return;\n }\n\n if (destination.origin !== window.location.origin) return;\n queueLeave(window.location.href, destination.href);\n };\n\n const handleHashChange = (event: HashChangeEvent) => {\n const to = event.newURL || window.location.href;\n const from = event.oldURL || lastUrl;\n queueLeave(from, to);\n lastUrl = normalizeUrl(to);\n };\n\n const handlePopState = () => {\n handleNavigation(window.location.href);\n };\n\n historyRef.pushState = function (data: any, unused: string, url?: string | URL | null) {\n const from = window.location.href;\n const result = originalPushState(data, unused, url as any);\n queueLeave(from, window.location.href);\n lastUrl = normalizeUrl(window.location.href);\n return result;\n };\n\n historyRef.replaceState = function (data: any, unused: string, url?: string | URL | null) {\n const from = window.location.href;\n const result = originalReplaceState(data, unused, url as any);\n queueLeave(from, window.location.href);\n lastUrl = normalizeUrl(window.location.href);\n return result;\n };\n\n document.addEventListener('click', handleDocumentClick, true);\n window.addEventListener('hashchange', handleHashChange);\n window.addEventListener('popstate', handlePopState);\n popups.debug('Exit trigger set (deferred to next route)', {\n surveyId: trigger.surveyId,\n delaySeconds,\n });\n}\n\nfunction setupEventTrigger(popups: DeepdotsPopups, trigger: TriggerConfig): void {\n // Event triggers are manually fired by host application using popups.triggerEvent(name).\n popups.debug('Event trigger registered', {\n popupId: trigger.popupId,\n surveyId: trigger.surveyId,\n eventName: String(trigger.value || ''),\n });\n}\n\nfunction parseExitDelaySeconds(value?: number | string): number {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return Math.max(0, value);\n }\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return Math.max(0, parsed);\n }\n return 0;\n}\n\nfunction normalizeUrl(value: string): string {\n if (!value) return '';\n const withoutIndex = value.replace(/\\/index\\.html(?=($|[?#]))/i, '');\n return withoutIndex.length > 1 && withoutIndex.endsWith('/') ? withoutIndex.slice(0, -1) : withoutIndex;\n}\n\nfunction setupClickTrigger(popups: DeepdotsPopups, trigger: TriggerConfig): void {\n const targetId = typeof trigger.value === 'string' ? trigger.value : '';\n if (!targetId) {\n popups.debug('Click trigger missing element id', trigger);\n return;\n }\n\n const attach = () => {\n const el = document.getElementById(targetId);\n if (!el) {\n popups.debug('Click trigger target not found', targetId);\n return false;\n }\n const handler = (e: MouseEvent) => {\n if (e.defaultPrevented) return;\n popups.triggerSurvey(trigger.surveyId, trigger.popupId);\n el.removeEventListener('click', handler);\n };\n el.addEventListener('click', handler);\n popups.debug(`Click trigger set for #${targetId}`);\n return true;\n };\n\n if (typeof document === 'undefined') return;\n if (attach()) return;\n window.addEventListener('DOMContentLoaded', () => attach(), { once: true });\n}\n","import {DeepdotsConfig, DeepdotsInitParams} from '../types';\n\n/** Variables específicas por entorno derivadas de nodeEnv */\nexport interface EnvironmentVariables {\n apiBaseUrl: string;\n}\n\nconst ENVIRONMENT_MAP: Record<\"development\" | \"production\", EnvironmentVariables> = {\n development: {\n apiBaseUrl: 'https://api-dev.deepdots.com',\n },\n production: {\n apiBaseUrl: 'https://api.deepdots.com',\n },\n};\n\n/** Resuelve las variables para el entorno indicado */\nexport function resolveEnvironment(nodeEnv: DeepdotsInitParams['nodeEnv']): EnvironmentVariables {\n return ENVIRONMENT_MAP[nodeEnv || 'production'];\n}\n\n","import type { IdentityAnswer } from '../tracking/tracking-manager';\n\n/**\n * Construye un HTML autocontenido que renderiza el survey de `@magicfeedback/native`\n * dentro de un WebView (React Native). Carga el bundle desde CDN, crea el form con la\n * identidad inyectada (`profile`/`metadata`) y reenvía los eventos del survey al host\n * vía el puente de react-native-webview: `window.ReactNativeWebView.postMessage`.\n *\n * Mensajes emitidos (JSON `{name, payload}`): `loaded`, `before_submit`, `after_submit`,\n * `survey_completed`, `back`, `popup_close`.\n */\nexport interface BuildSurveyHtmlOptions {\n surveyId: string;\n productId: string;\n env?: string; // 'production' | 'development'\n profile?: IdentityAnswer[];\n metadata?: IdentityAnswer[];\n version?: string; // versión de @magicfeedback/native\n}\n\nexport function buildSurveyHtml(opts: BuildSurveyHtmlOptions): string {\n const env = opts.env === 'development' ? 'dev' : 'prod';\n const version = opts.version ?? '2.2.4';\n const cdn = `https://cdn.jsdelivr.net/npm/@magicfeedback/native@${version}/dist/magicfeedback-sdk.browser.js`;\n const sid = JSON.stringify(opts.surveyId);\n const pid = JSON.stringify(opts.productId);\n const profileJson = JSON.stringify(opts.profile ?? []);\n const metaJson = JSON.stringify(opts.metadata ?? []);\n\n return `<!DOCTYPE html><html><head>\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/>\n<meta name=\"color-scheme\" content=\"light\"/>\n<style>html,body{margin:0;padding:0;background:transparent;font-family:-apple-system,system-ui,sans-serif}#mf{width:100%;box-sizing:border-box}#mf *{max-width:100%;box-sizing:border-box}</style>\n</head><body>\n<div id=\"mf\"></div>\n<script>\n(function(){\n function emit(s){ try{ if(window.ReactNativeWebView){ window.ReactNativeWebView.postMessage(s); } }catch(e){} }\n function emitJSON(n,p){ try{ emit(JSON.stringify({name:n,payload:p||{}})); }catch(e){} }\n window.DeepdotsClose = function(){ emitJSON('popup_close'); };\n var initialized=false;\n function initMF(){\n if(initialized || !window.magicfeedback) return initialized;\n initialized=true;\n try{\n window.magicfeedback.init({ debug:false, env:'${env}' });\n var form = window.magicfeedback.form(${sid}, ${pid}, ${profileJson}, ${metaJson});\n window.DeepdotsForm = form;\n form.generate('mf', {\n addButton:false,\n getMetaData:true,\n onLoadedEvent:function(){ emitJSON('loaded'); },\n beforeSubmitEvent:function(){ emitJSON('before_submit'); },\n afterSubmitEvent:function(p){\n try{ if(p && p.completed){ emitJSON('survey_completed', p); } else { emitJSON('after_submit', p); } }\n catch(e){ emitJSON('after_submit'); }\n },\n onBackEvent:function(p){ emitJSON('back', p); }\n }).catch(function(e){ emit('error:init'); });\n }catch(e){ emit('error:exception'); }\n return true;\n }\n var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;\n s.onload=function(){ initMF(); };\n s.onerror=function(){ emit('error:load'); };\n document.head.appendChild(s);\n var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);\n})();\n</script>\n</body></html>`;\n}\n","import type { DeepdotsEventType, PopupActions, PopupStyle } from '../types';\nimport type { PopupRenderer } from './renderer';\nimport { buildSurveyIdentity } from '../tracking/tracking-manager';\nimport { buildSurveyHtml } from '../ui/surveyHtml';\n\ntype EmitFn = (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void;\n\n/** Payload que recibe el host para montar el WebView del survey. */\nexport interface ReactNativeSurveyPayload {\n surveyId: string;\n productId: string;\n /** HTML autocontenido para `<WebView source={{ html }}>`. */\n html: string;\n}\n\nexport interface ReactNativeRendererOptions {\n /** Se llama al mostrar un popup: monta el WebView con `payload.html`. */\n onShow?: (payload: ReactNativeSurveyPayload) => void;\n /** Se llama al cerrar el popup: desmonta el WebView. */\n onHide?: () => void;\n}\n\n/**\n * Renderer de React Native: el SDK no puede pintar componentes RN, así que entrega el\n * HTML del survey al host (que lo monta en `react-native-webview`) y traduce los\n * mensajes del WebView a eventos de popup del SDK (→ `POST /sdk/popups`, Messaging #18–22).\n *\n * Uso:\n * ```tsx\n * const renderer = new ReactNativePopupRenderer({\n * onShow: (p) => setSurvey({ ...p, visible: true }),\n * onHide: () => setSurvey((s) => ({ ...s, visible: false })),\n * });\n * sdk.setRenderer(renderer);\n * // …\n * <WebView source={{ html: survey.html }}\n * onMessage={(e) => renderer.handleMessage(e.nativeEvent.data)} />\n * ```\n */\nexport class ReactNativePopupRenderer implements PopupRenderer {\n private emitFn: EmitFn | null = null;\n private onCloseFn: (() => void) | null = null;\n private currentSurveyId: string | null = null;\n private partialEmitted = false;\n\n constructor(private options: ReactNativeRendererOptions = {}) {}\n\n init(): void {\n /* nada que preparar */\n }\n\n show(\n surveyId: string,\n productId: string,\n _actions: PopupActions | undefined,\n emit: EmitFn,\n onClose: () => void,\n env: string = 'production',\n userId?: string,\n _style?: PopupStyle,\n sessionId?: string,\n miniService?: string,\n analyticsFeedbackSessionId?: string,\n ): void {\n this.emitFn = emit;\n this.onCloseFn = onClose;\n this.currentSurveyId = surveyId;\n this.partialEmitted = false;\n\n const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);\n const html = buildSurveyHtml({ surveyId, productId, env, profile, metadata });\n if (this.options.onShow) {\n this.options.onShow({ surveyId, productId, html });\n } else {\n console.warn(\n '[Deepdots] ReactNativePopupRenderer sin onShow: pasa { onShow } a new ReactNativePopupRenderer({...}) para montar el WebView del survey.',\n );\n }\n }\n\n hide(): void {\n this.options.onHide?.();\n this.currentSurveyId = null;\n }\n\n /**\n * El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a\n * eventos del SDK: primera interacción → `popup_clicked` (PARTIAL), completado →\n * `survey_completed` (COMPLETED) y cierra el popup.\n */\n handleMessage(raw: string): void {\n const surveyId = this.currentSurveyId;\n if (!surveyId || !this.emitFn) return;\n\n let name: string | undefined;\n try {\n name = (JSON.parse(raw) as { name?: string }).name;\n } catch {\n name = raw; // mensajes simples (\"error:load\")\n }\n\n switch (name) {\n case 'loaded':\n case 'before_submit':\n case 'after_submit':\n case 'back':\n if (!this.partialEmitted) {\n this.partialEmitted = true;\n this.emitFn('popup_clicked', surveyId, { action: 'partial' });\n }\n break;\n case 'survey_completed':\n this.emitFn('survey_completed', surveyId);\n this.onCloseFn?.(); // el core enruta a hidePopup() → renderer.hide() → onHide\n break;\n case 'popup_close':\n this.onCloseFn?.();\n break;\n default:\n break;\n }\n }\n}\n\n/** Detección simple de entorno React Native. */\nexport function isReactNativeEnv(): boolean {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return typeof navigator !== 'undefined' && (navigator as any).product === 'ReactNative';\n}\n\n/** Factoría usada por `createDefaultRenderer` cuando se detecta RN (el host debería pasar onShow). */\nexport function createReactNativeRenderer(): PopupRenderer {\n return new ReactNativePopupRenderer();\n}\n","import {DeepdotsEventType, PopupActions, PopupStyle} from '../types';\nimport { isReactNativeEnv, createReactNativeRenderer } from './react-native-renderer';\n// renderPopup (DOM + @magicfeedback/native) se carga PEREZOSAMENTE solo al mostrar\n// un popup en navegador, para que importar el SDK sea seguro en React Native/SSR.\n\nexport interface PopupRenderer {\n /** Preparar recursos si aplica */\n init?(): void;\n /** Mostrar popup */\n show(\n surveyId: string,\n productId: string,\n actions: PopupActions | undefined,\n emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,\n onClose: () => void,\n env?: string,\n userId?: string,\n style?: PopupStyle,\n sessionId?: string,\n miniService?: string,\n analyticsFeedbackSessionId?: string,\n ): void;\n /** Ocultar popup */\n hide(): void;\n}\n\n/** Renderer que no hace nada (SSR / entornos sin DOM) */\nexport class NoopPopupRenderer implements PopupRenderer {\n show(): void { /* no-op */ }\n hide(): void { /* no-op */ }\n}\n\n/** Renderer para navegadores usando la implementación actual basada en renderPopup */\nexport class BrowserPopupRenderer implements PopupRenderer {\n private container: HTMLElement | null = null;\n private visible = false;\n\n init(): void {\n if (typeof document === 'undefined') return;\n if (!this.container) {\n this.container = document.createElement('div');\n this.container.id = 'deepdots-popup-container';\n this.container.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: none;\n z-index: 999999;\n background: rgba(0, 0, 0, 0.5);\n justify-content: center;\n align-items: center;\n `;\n }\n if (!document.body.contains(this.container)) {\n document.body.appendChild(this.container);\n }\n }\n\n show(\n surveyId: string,\n productId: string,\n actions: PopupActions | undefined,\n emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,\n onClose: () => void,\n env: string = 'production',\n userId?: string,\n style?: PopupStyle,\n sessionId?: string,\n miniService?: string,\n analyticsFeedbackSessionId?: string,\n ): void {\n if (this.visible) return;\n if (!this.container || !document.body.contains(this.container)) this.init();\n if (!this.container) return; // aún sin DOM\n this.visible = true;\n const container = this.container;\n void import('../ui/renderPopup').then(({ renderPopup }) => {\n renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId);\n });\n }\n\n hide(): void {\n this.visible = false;\n if (this.container) {\n this.container.style.display = 'none';\n this.container.innerHTML = '';\n }\n }\n}\n\n/** Factoría para obtener renderer por defecto */\nexport function createDefaultRenderer(): PopupRenderer {\n if (typeof document === 'undefined') {\n // Entorno sin DOM: probar RN primero\n if (isReactNativeEnv()) return createReactNativeRenderer();\n return new NoopPopupRenderer();\n }\n // Si estamos en React Native y hay WebView con document emulado, aún queremos RN renderer\n if (isReactNativeEnv()) return createReactNativeRenderer();\n return new BrowserPopupRenderer();\n}\n","/**\n * Capa de Analytics (eventos GA-style) — canal SEPARADO del feedback de survey.\n *\n * El feedback de survey se envía con `magicfeedback.send()`. La analítica de\n * navegación/comportamiento (page views, mini-services, tasks/funnels, meaningful\n * interactions, findability…) va por un ENDPOINT PROPIO, vinculada por `user_id`.\n *\n * De momento NO se hace la llamada: el `sink` por defecto hace `console.log` del\n * payload exacto que se enviaría (dry-run), para poder verificarlo. Cuando el\n * endpoint exista, se sustituye el sink por un POST.\n */\n\nimport type { DeviceInfo } from './device-info';\n\nexport interface AnalyticsEvent {\n name: string;\n timestamp: number;\n params?: Record<string, unknown>;\n}\n\nexport interface AnalyticsEnvelope {\n /** Clave pública del cliente (auth). */\n publicKey?: string;\n /** Id que vincula la analítica al usuario (el backend cose por aquí). */\n userId: string | null;\n /** Session id del backend si ya se conoce (puede ir null). */\n sessionId: string | null;\n context: {\n platform: string; // 'web' | 'android' | 'ios' (RN inyecta la suya)\n language?: string;\n /** Info de dispositivo (Technology #11–13). */\n device?: DeviceInfo;\n /** User attributes del cliente, usados para breakdowns (registration_status, pass_type, …). */\n attributes: Record<string, string>;\n };\n events: AnalyticsEvent[];\n}\n\nexport type AnalyticsSink = (payload: AnalyticsEnvelope) => void;\n\n/** Identidad resuelta por el tracking, inyectada al construir el payload. */\nexport interface AnalyticsIdentity {\n userId: string | null;\n sessionId: string | null;\n}\n\n/** Sink por defecto: NO envía nada, solo pinta por consola lo que se enviaría. */\nexport const dryRunSink: AnalyticsSink = (payload) => {\n // eslint-disable-next-line no-console\n console.log(\n '[DeepdotsAnalytics] (dry-run · NO enviado · sin init.analytics) POST /sdk/feedback →',\n JSON.stringify(payload, null, 2),\n );\n};\n\nexport interface AnalyticsManagerOptions {\n sink?: AnalyticsSink;\n now?: () => number;\n publicKey?: string;\n language?: string;\n device?: DeviceInfo;\n /** Plataforma del envelope (default 'web'; RN inyecta 'android'/'ios'). */\n platform?: string;\n /** Nº máximo de eventos en buffer antes de solicitar un flush automático (default 20). */\n maxBatchSize?: number;\n /** Callback invocado cuando el buffer alcanza `maxBatchSize`. El caller hace el flush real. */\n onFlushNeeded?: () => void;\n}\n\nexport class AnalyticsManager {\n private sink: AnalyticsSink;\n private now: () => number;\n private publicKey?: string;\n private language?: string;\n private device?: DeviceInfo;\n private platform: string;\n\n private events: AnalyticsEvent[] = [];\n private attributes: Record<string, string> = {};\n private miniService: string | null = null;\n private miniServiceEnteredAt = 0;\n private maxBatchSize: number;\n private onFlushNeeded?: () => void;\n\n constructor(options: AnalyticsManagerOptions = {}) {\n this.sink = options.sink ?? dryRunSink;\n this.now = options.now ?? (() => Date.now());\n this.publicKey = options.publicKey;\n this.language = options.language;\n this.device = options.device;\n this.platform = options.platform ?? 'web';\n this.maxBatchSize = options.maxBatchSize ?? 20;\n this.onFlushNeeded = options.onFlushNeeded;\n }\n\n /** Actualiza campos de device en runtime (p.ej. country/city tras resolver geo). */\n updateDevice(partial: Partial<DeviceInfo>): void {\n this.device = { ...this.device, ...partial } as DeviceInfo;\n }\n\n /** Mezcla user attributes (se coercionan a string). Mutable en runtime. */\n setUserAttributes(attrs: Record<string, string | number | boolean>): void {\n for (const [k, v] of Object.entries(attrs)) {\n if (!k) continue;\n this.attributes[k] = String(v);\n }\n }\n\n /** Marca el inicio de un mini-service; etiqueta los eventos siguientes con `mini_service`. */\n enterMiniService(name: string, entryPointType?: string): void {\n this.miniService = name;\n this.miniServiceEnteredAt = this.now();\n this.track('mini_service_enter', { entry_point_type: entryPointType ?? null });\n }\n\n /** Cierra el mini-service activo emitiendo `mini_service_exit` con su duración (#27). No-op si no hay ninguno. */\n exitMiniService(): void {\n const name = this.miniService;\n if (name == null) return;\n const durationSeconds = Math.max(0, Math.round((this.now() - this.miniServiceEnteredAt) / 1000));\n this.miniService = null; // dejar de etiquetar antes de emitir el evento de salida\n this.track('mini_service_exit', { mini_service: name, duration_seconds: durationSeconds });\n }\n\n /** Registra un evento de analítica (modelo GA: nombre + params). */\n track(name: string, params?: Record<string, unknown>): void {\n const merged: Record<string, unknown> = {\n ...(this.miniService ? { mini_service: this.miniService } : {}),\n ...(params ?? {}),\n };\n this.events.push({\n name,\n timestamp: this.now(),\n params: Object.keys(merged).length ? merged : undefined,\n });\n if (this.events.length >= this.maxBatchSize) {\n this.onFlushNeeded?.();\n }\n }\n\n /** Mini-service activo (para inyectarlo en la metadata del survey, #33). */\n getMiniService(): string | null {\n return this.miniService;\n }\n\n /** Nº de eventos pendientes de flush. */\n pending(): number {\n return this.events.length;\n }\n\n /** Construye el envelope que se enviaría al endpoint de analytics. */\n buildPayload(identity: AnalyticsIdentity): AnalyticsEnvelope {\n return {\n publicKey: this.publicKey,\n userId: identity.userId,\n sessionId: identity.sessionId,\n context: {\n platform: this.platform,\n language: this.language,\n device: this.device,\n attributes: { ...this.attributes },\n },\n events: [...this.events],\n };\n }\n\n /** Envía (vía sink) el lote acumulado y vacía el buffer. No-op si no hay eventos. */\n flush(identity: AnalyticsIdentity): AnalyticsEnvelope | null {\n if (this.events.length === 0) return null;\n const payload = this.buildPayload(identity);\n this.sink(payload);\n this.events = [];\n return payload;\n }\n}\n","/**\n * Mapeo del envelope de analytics → body de `POST /sdk/feedback`.\n *\n * La analítica se envía como un Feedback del modelo del Surveys SDK, agrupado por una\n * INTEGRACIÓN creada en la plataforma. Se manda en streaming con `completed:false` y\n * `finished:false` (nunca se \"cierra\"); el backend cose por `sessionId` + `user_id`.\n *\n * Encoding:\n * - todo va en `feedback.metadata`: contexto (user_id, session_id, platform…) + eventos\n * - cada entrada usa `value: string[]` (array de un elemento)\n * - eventos: {key: nombre_evento, value: [JSON(timestamp + params)]}\n * - identidad (user_id) → `profile` como `external-user-id`\n * - `answers` vacío, `metrics` no se usa\n * - `text` vacío\n */\n\nimport type { AnalyticsEnvelope, AnalyticsSink } from './analytics-manager';\n\nexport interface AnalyticsKeys {\n publicKey: string;\n integration: string;\n}\n\nexport interface FeedbackKV {\n key: string;\n value: string[];\n}\n\nexport interface AnalyticsFeedbackBody {\n feedback: {\n text: string;\n answers: FeedbackKV[];\n metadata: FeedbackKV[];\n profile: FeedbackKV[];\n finished: boolean;\n };\n publicKey: string;\n integration: string;\n completed: boolean;\n finished?: boolean;\n /** sessionId devuelto por el primer POST; permite que el backend agrupe todos los eventos en un solo registro. */\n sessionId?: string;\n}\n\n/** Inserta un par key/value en la lista solo si el valor está definido y no es vacío. */\nfunction pushKV(list: FeedbackKV[], key: string, value: unknown): void {\n if (value === undefined || value === null || value === '') return;\n list.push({ key, value: [String(value)] });\n}\n\nexport function buildAnalyticsFeedbackBody(\n envelope: AnalyticsEnvelope,\n keys: AnalyticsKeys,\n feedbackSessionId?: string,\n): AnalyticsFeedbackBody {\n const { context } = envelope;\n\n const profile: FeedbackKV[] = [];\n pushKV(profile, 'external-user-id', envelope.userId);\n\n // Todo va en metadata: contexto del sistema (prefijo deepdots_) + atributos de usuario + eventos\n const metadata: FeedbackKV[] = [];\n pushKV(metadata, 'deepdots_user_id', envelope.userId);\n pushKV(metadata, 'deepdots_session_id', envelope.sessionId);\n pushKV(metadata, 'deepdots_platform', context.platform);\n pushKV(metadata, 'deepdots_language', context.language);\n if (context.device) {\n const d = context.device;\n pushKV(metadata, 'deepdots_device_type', d.device_type);\n pushKV(metadata, 'deepdots_os_version', d.os_version);\n pushKV(metadata, 'deepdots_device_model', d.device_model);\n pushKV(metadata, 'deepdots_app_version', d.app_version);\n pushKV(metadata, 'deepdots_user_agent', d.user_agent);\n pushKV(metadata, 'deepdots_timezone', d.timezone);\n pushKV(metadata, 'deepdots_referrer', d.referrer);\n pushKV(metadata, 'deepdots_viewport_size', d.viewport_size);\n pushKV(metadata, 'deepdots_screen_resolution', d.screen_resolution);\n pushKV(metadata, 'deepdots_pixel_ratio', d.pixel_ratio);\n pushKV(metadata, 'deepdots_entry_type', d.entry_type);\n pushKV(metadata, 'deepdots_page_load_ms', d.page_load_ms);\n pushKV(metadata, 'deepdots_connection_type', d.connection_type);\n pushKV(metadata, 'deepdots_country', d.country);\n pushKV(metadata, 'deepdots_city', d.city);\n }\n for (const [k, v] of Object.entries(context.attributes ?? {})) {\n pushKV(metadata, k, v);\n }\n for (const e of envelope.events) {\n metadata.push({\n key: e.name,\n value: [JSON.stringify({ timestamp: e.timestamp, ...(e.params ?? {}) })],\n });\n }\n\n return {\n feedback: {\n text: '',\n answers: [],\n metadata,\n profile,\n finished: false,\n },\n publicKey: keys.publicKey,\n integration: keys.integration,\n completed: false,\n ...(feedbackSessionId ? { sessionId: feedbackSessionId } : {}),\n };\n}\n\nexport interface FeedbackSinkOptions {\n /** Base URL del backend (api(.|-dev.)deepdots.com) ya resuelta por el entorno. */\n baseUrl: string;\n keys: AnalyticsKeys;\n /** Logger opcional (gated por debug). */\n log?: (...args: unknown[]) => void;\n /** fetch inyectable (tests / RN). */\n fetchImpl?: typeof fetch;\n /** Callback invocado cuando el backend devuelve un nuevo sessionId (primer POST). */\n onSessionId?: (id: string) => void;\n}\n\n/**\n * Sink real: transforma el envelope y hace `POST {baseUrl}/sdk/feedback`.\n * Fire-and-forget (no bloquea el flush); errores solo se loguean.\n * Stateful: cachea el `sessionId` que devuelve el backend en la primera respuesta\n * y lo envía en los siguientes POSTs para que el backend agrupe todos los eventos\n * en un único registro de feedback.\n */\nexport function createFeedbackSink(options: FeedbackSinkOptions): AnalyticsSink {\n let feedbackSessionId: string | undefined;\n\n return (envelope: AnalyticsEnvelope) => {\n const body = buildAnalyticsFeedbackBody(envelope, options.keys, feedbackSessionId);\n const f = options.fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);\n if (!f) {\n options.log?.('[DeepdotsAnalytics] no fetch disponible; payload no enviado', body);\n return;\n }\n options.log?.('[DeepdotsAnalytics] POST /sdk/feedback →', body);\n f(`${options.baseUrl}/sdk/feedback`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n })\n .then(async (res) => {\n if (res.ok) {\n try {\n const data = (await res.json()) as { sessionId?: string };\n if (data?.sessionId && data.sessionId !== feedbackSessionId) {\n feedbackSessionId = data.sessionId;\n options.log?.('[DeepdotsAnalytics] feedbackSessionId cacheado:', feedbackSessionId);\n options.onSessionId?.(feedbackSessionId);\n }\n } catch {\n /* respuesta sin JSON — ignorar */\n }\n }\n })\n .catch((err) => options.log?.('[DeepdotsAnalytics] error enviando feedback', err));\n };\n}\n","/**\n * Observador de navegación (Fase 2). Detecta cambios de pantalla y emite \"visitas\"\n * completadas (screen + entrada/salida/duración) cuando se abandona una pantalla.\n *\n * En Web se engancha a la History API (pushState/replaceState/popstate/hashchange).\n * La lógica de timing y normalización es pura y testeable sin DOM (vía `begin`/`visit`/`stop`\n * y un reloj inyectable).\n *\n * El consumidor (DeepdotsPopups) reenvía cada visita como un evento `page_view` por el\n * canal de analytics; el grafo se reconstruye en backend por orden + timestamp.\n */\n\nexport interface ScreenVisit {\n screen: string;\n entry: number;\n exit: number;\n durationSeconds: number;\n}\n\nexport type VisitListener = (visit: ScreenVisit) => void;\n\nconst UUID_PREFIX = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;\n\n/** Normaliza una URL a un nombre de pantalla: path (+hash route), sin query, IDs → :id. */\nexport function normalizeScreen(href: string): string {\n let pathAndHash: string;\n try {\n const u = new URL(href, 'http://deepdots.local');\n pathAndHash = u.pathname + (u.hash || '');\n } catch {\n pathAndHash = href;\n }\n // quitar query (incluida la que pueda venir dentro del hash route)\n pathAndHash = pathAndHash.replace(/\\?[^#]*/g, '');\n const collapsed = pathAndHash\n .split('/')\n .map((seg) => (/^[0-9]+$/.test(seg) || UUID_PREFIX.test(seg) ? ':id' : seg))\n .join('/');\n return collapsed || '/';\n}\n\nexport interface NavigationObserverOptions {\n now?: () => number;\n}\n\nexport class NavigationObserver {\n private now: () => number;\n private listeners = new Set<VisitListener>();\n private currentScreen: string | null = null;\n private entryAt = 0;\n private installed = false;\n private domHandlers: Array<() => void> = [];\n\n constructor(options: NavigationObserverOptions = {}) {\n this.now = options.now ?? (() => Date.now());\n }\n\n onVisit(listener: VisitListener): void {\n this.listeners.add(listener);\n }\n\n /** Fija la pantalla inicial (sin emitir visita; su duración se cierra al salir). */\n begin(href: string): void {\n this.currentScreen = normalizeScreen(href);\n this.entryAt = this.now();\n }\n\n /** Navega a una pantalla nueva: cierra la anterior (emite visita) y abre la nueva. */\n visit(href: string): void {\n const next = normalizeScreen(href);\n if (next === this.currentScreen) return; // misma pantalla → no es una visita nueva\n this.closeCurrent();\n this.currentScreen = next;\n this.entryAt = this.now();\n }\n\n /** Cierra la pantalla actual emitiendo su visita (p. ej. al cerrar la app/pestaña). */\n stop(): void {\n this.closeCurrent();\n this.currentScreen = null;\n }\n\n private closeCurrent(): void {\n if (this.currentScreen == null) return;\n const exit = this.now();\n const visit: ScreenVisit = {\n screen: this.currentScreen,\n entry: this.entryAt,\n exit,\n durationSeconds: Math.max(0, Math.round((exit - this.entryAt) / 1000)),\n };\n this.listeners.forEach((l) => {\n try {\n l(visit);\n } catch {\n /* listener no debe romper la navegación */\n }\n });\n }\n\n /** Instala los hooks de History en el navegador y registra la pantalla actual. */\n install(): void {\n if (this.installed || typeof window === 'undefined' || typeof history === 'undefined') return;\n this.installed = true;\n\n const handle = () => this.visit(window.location.href);\n\n const originalPush = history.pushState.bind(history);\n const originalReplace = history.replaceState.bind(history);\n history.pushState = (data: unknown, unused: string, url?: string | URL | null) => {\n const r = originalPush(data as never, unused, url as never);\n handle();\n return r;\n };\n history.replaceState = (data: unknown, unused: string, url?: string | URL | null) => {\n const r = originalReplace(data as never, unused, url as never);\n handle();\n return r;\n };\n window.addEventListener('popstate', handle);\n window.addEventListener('hashchange', handle);\n\n this.domHandlers.push(() => {\n history.pushState = originalPush;\n history.replaceState = originalReplace;\n window.removeEventListener('popstate', handle);\n window.removeEventListener('hashchange', handle);\n });\n\n this.begin(window.location.href);\n }\n\n uninstall(): void {\n this.domHandlers.forEach((off) => off());\n this.domHandlers = [];\n this.installed = false;\n }\n}\n","/**\n * Device info para el context de analytics (Technology #11–13).\n *\n * En Web enviamos `device_type` + `user_agent` crudo y dejamos que el backend\n * derive OS/navegador del UA (como hace GA). Los campos adicionales (timezone,\n * referrer, viewport, etc.) se recogen de las APIs del navegador sin permisos.\n * `country`/`city` se rellenan de forma asíncrona (ver geo-info.ts).\n * En KMP las APIs nativas dan os_version/device_model reales.\n */\n\nexport interface DeviceInfo {\n device_type: 'mobile' | 'tablet' | 'desktop';\n os_version?: string;\n device_model?: string;\n app_version?: string;\n user_agent?: string;\n timezone?: string;\n referrer?: string;\n viewport_size?: string;\n screen_resolution?: string;\n pixel_ratio?: number;\n entry_type?: string;\n page_load_ms?: number;\n connection_type?: string;\n country?: string;\n city?: string;\n}\n\nexport function parseDeviceType(ua: string): 'mobile' | 'tablet' | 'desktop' {\n const s = (ua || '').toLowerCase();\n if (/ipad|tablet|playbook|silk/.test(s) || (/android/.test(s) && !/mobile/.test(s))) {\n return 'tablet';\n }\n if (/mobi|iphone|ipod|windows phone/.test(s) || (/android/.test(s) && /mobile/.test(s))) {\n return 'mobile';\n }\n return 'desktop';\n}\n\nfunction getEntryType(): string | undefined {\n if (typeof performance === 'undefined') return undefined;\n const entries = performance.getEntriesByType('navigation');\n if (!entries.length) return undefined;\n return (entries[0] as PerformanceNavigationTiming).type;\n}\n\nfunction getPageLoadMs(): number | undefined {\n if (typeof performance === 'undefined') return undefined;\n const entries = performance.getEntriesByType('navigation');\n if (!entries.length) return undefined;\n const load = (entries[0] as PerformanceNavigationTiming).loadEventEnd;\n return load > 0 ? Math.round(load) : undefined;\n}\n\nfunction getConnectionType(): string | undefined {\n if (typeof navigator === 'undefined') return undefined;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (navigator as any).connection?.effectiveType ?? undefined;\n}\n\nexport function collectDeviceInfo(appVersion?: string): DeviceInfo {\n const ua = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n return {\n device_type: parseDeviceType(ua),\n user_agent: ua || undefined,\n app_version: appVersion,\n timezone: typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : undefined,\n referrer: typeof document !== 'undefined' && document.referrer ? document.referrer : undefined,\n viewport_size:\n typeof window !== 'undefined' ? `${window.innerWidth}x${window.innerHeight}` : undefined,\n screen_resolution:\n typeof screen !== 'undefined' ? `${screen.width}x${screen.height}` : undefined,\n pixel_ratio: typeof window !== 'undefined' ? window.devicePixelRatio : undefined,\n entry_type: getEntryType(),\n page_load_ms: getPageLoadMs(),\n connection_type: getConnectionType(),\n };\n}\n","/**\n * Geolocalización por IP vía ipapi.co (Option B: client-side).\n * Fire-and-forget: se llama en init() y rellena country/city en el DeviceInfo\n * cuando resuelve. Tolerante a fallos (ad-blockers, rate-limit, red).\n */\n\nexport interface GeoInfo {\n country?: string;\n city?: string;\n}\n\nexport async function collectGeoInfo(fetchImpl?: typeof fetch): Promise<GeoInfo | null> {\n const f = fetchImpl ?? (typeof fetch !== 'undefined' ? fetch : undefined);\n if (!f) return null;\n try {\n const res = await f('https://ipapi.co/json/');\n if (!res.ok) return null;\n const data = (await res.json()) as Record<string, unknown>;\n const country = (data.country_code as string) || undefined;\n const city = (data.city as string) || undefined;\n if (!country && !city) return null;\n return { country, city };\n } catch {\n return null;\n }\n}\n","/**\n * Mide el tiempo ACTIVO (en primer plano), estilo GA4 (engagement time).\n * Acumula mientras está \"resumed\" (pestaña visible / app en foreground) y pausa en\n * background. `consume()` devuelve los ms activos desde la última lectura y reinicia,\n * dejando el timer corriendo. El consumidor emite un evento `user_engagement` con\n * `engagement_time_msec`; el backend lo suma por sesión → \"Average Time Spent per Session\" (#8).\n */\nexport class EngagementTracker {\n private now: () => number;\n private activeMs = 0;\n private lastResumeAt: number | null = null;\n\n constructor(now: () => number = () => Date.now()) {\n this.now = now;\n }\n\n /** Reanuda el conteo (pestaña visible / app en foreground). Idempotente. */\n resume(): void {\n if (this.lastResumeAt === null) this.lastResumeAt = this.now();\n }\n\n /** Pausa el conteo (pestaña oculta / app en background), acumulando el tramo activo. */\n pause(): void {\n if (this.lastResumeAt !== null) {\n this.activeMs += this.now() - this.lastResumeAt;\n this.lastResumeAt = null;\n }\n }\n\n /** Devuelve los ms activos acumulados desde la última lectura y reinicia (sin parar el timer). */\n consume(): number {\n if (this.lastResumeAt !== null) {\n const t = this.now();\n this.activeMs += t - this.lastResumeAt;\n this.lastResumeAt = t;\n }\n const ms = this.activeMs;\n this.activeMs = 0;\n return ms;\n }\n\n isActive(): boolean {\n return this.lastResumeAt !== null;\n }\n}\n","import type { KeyValueStorage } from '../tracking/tracking-manager';\nimport { STORAGE_KEYS } from '../tracking/tracking-manager';\n\n/** Valor de un atributo de contact (info interna del usuario que solo conoce el host). */\nexport type ContactAttributeValue = string | number | boolean;\nexport type ContactAttributes = Record<string, ContactAttributeValue>;\n\n/** Body de `POST /sdk/popups/contact`. */\nexport interface ContactBody {\n publicKey: string;\n userId: string;\n userAttributes: ContactAttributes;\n}\n\nexport interface ContactManagerOptions {\n storage: KeyValueStorage;\n publicKey: string;\n userId: string;\n /** Envío real (inyectable para tests): hace el `POST /sdk/popups/contact`. */\n post: (body: ContactBody) => Promise<void>;\n}\n\n/** Serialización estable (claves ordenadas) para el diff contra lo último enviado. */\nfunction stableSerialize(attrs: ContactAttributes): string {\n const sorted: ContactAttributes = {};\n for (const key of Object.keys(attrs).sort()) {\n sorted[key] = attrs[key];\n }\n return JSON.stringify(sorted);\n}\n\n/**\n * Gestiona los atributos de contact del usuario identificado (`userId` del init).\n * Solo envía cuando cambian respecto a lo último persistido en storage, para ahorrar\n * peticiones (el host puede llamar `setAttributes` en cada identificación sin miedo).\n */\nexport class ContactManager {\n private readonly storage: KeyValueStorage;\n private readonly publicKey: string;\n private readonly userId: string;\n private readonly post: (body: ContactBody) => Promise<void>;\n\n constructor(options: ContactManagerOptions) {\n this.storage = options.storage;\n this.publicKey = options.publicKey;\n this.userId = options.userId;\n this.post = options.post;\n }\n\n /**\n * Envía los atributos si cambiaron respecto a lo último enviado.\n * @returns `true` si se envió, `false` si no hubo cambios.\n */\n async setAttributes(attributes: ContactAttributes): Promise<boolean> {\n const serialized = stableSerialize(attributes);\n const last = this.storage.getItem(STORAGE_KEYS.contactAttributes);\n if (last === serialized) return false;\n\n await this.post({\n publicKey: this.publicKey,\n userId: this.userId,\n userAttributes: attributes,\n });\n this.storage.setItem(STORAGE_KEYS.contactAttributes, serialized);\n return true;\n }\n}\n","import {\n DeepdotsConfig,\n TriggerConfig,\n DeepdotsEvent,\n DeepdotsEventType,\n EventListener,\n DeepdotsInitParams,\n PopupDefinition,\n PopupTriggerCondition,\n PopupActions,\n PopupTrigger,\n PopupTriggerConditionStatus,\n POPUP_TRIGGER_CONDITION_STATUSES, POPUPSESSIONSTATUS,\n PopupStyle,\n} from '../types';\n// renderPopup se carga de forma PEREZOSA (dynamic import) para no arrastrar\n// `@magicfeedback/native` (browser) ni el CSS al importar el SDK en entornos sin DOM (React Native).\nimport { setupTrigger } from '../triggers';\nimport { resolveEnvironment } from '../config/env';\nimport { PopupRenderer, createDefaultRenderer } from '../platform/renderer';\nimport { TrackingManager, createDefaultStorage } from '../tracking/tracking-manager';\nimport { AnalyticsManager, type AnalyticsEnvelope } from '../analytics/analytics-manager';\nimport { createFeedbackSink } from '../analytics/feedback-payload';\nimport { NavigationObserver } from '../tracking/navigation-observer';\nimport { collectDeviceInfo } from '../analytics/device-info';\nimport { collectGeoInfo } from '../analytics/geo-info';\nimport { EngagementTracker } from '../analytics/engagement-tracker';\nimport { ContactManager, type ContactAttributes } from '../contact/contact-manager';\n\nconst EXIT_QUEUE_STORAGE_KEY = '__deepdots_exit_popup_queue__';\nconst DAY_IN_MS = 24 * 60 * 60 * 1000;\nconst ANALYTICS_MAX_BATCH_SIZE = 20;\nconst ANALYTICS_FLUSH_INTERVAL_MS = 30_000;\n\ninterface DeferredExitPopup {\n id: string;\n surveyId: string;\n dueAt: number;\n sourceUrl: string;\n}\n\ninterface LegacyPopupTriggerCondition {\n answered?: boolean;\n cooldownDays?: number;\n}\n\ninterface PopupProgressState {\n status: PopupTriggerConditionStatus;\n timestamp: number;\n}\n\ntype NormalizedPopupDefinition = PopupDefinition & {\n legacyConditions?: LegacyPopupTriggerCondition[];\n};\n\n/**\n * Main class for managing survey popups\n */\nexport class DeepdotsPopups {\n private config: DeepdotsConfig | null = null;\n private listeners: Map<DeepdotsEventType, Set<EventListener>> = new Map();\n private triggers: TriggerConfig[] = [];\n private initialized = false;\n private renderer: PopupRenderer = createDefaultRenderer();\n private popupContainer: HTMLElement | null = null; // deprecated: mantenido para compatibilidad interna\n private popupDefinitions: NormalizedPopupDefinition[] = [];\n private popupsLoaded = false;\n private pendingAutoLaunch = false;\n\n private answeredSurveys: Set<string> = new Set();\n private surveyProgress: Map<string, PopupProgressState> = new Map();\n private lastShown: Map<string, number> = new Map(); // popupId -> timestamp mostrado\n private surveyToPopupId: Map<string, string> = new Map(); // surveyId -> popupId\n private deferredExitTimers: number[] = [];\n\n private baseUrl: string = '';\n private env: 'production' | 'development' = 'production';\n\n /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */\n private tracking: TrackingManager | null = null;\n /** Capa de analytics (canal separado del feedback). Null hasta init(). */\n private analytics: AnalyticsManager | null = null;\n /** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */\n private analyticsFeedbackSessionId: string | undefined = undefined;\n /** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */\n private analyticsFlushTimer: ReturnType<typeof setInterval> | undefined = undefined;\n /** Observador de navegación (Fase 2): emite page_view por el canal de analytics. */\n private navObserver: NavigationObserver | null = null;\n /** Tiempo activo (engagement time, #8). */\n private engagement: EngagementTracker | null = null;\n /** Atributos internos del usuario identificado → POST /sdk/popups/contact. Null si no hay userId. */\n private contact: ContactManager | null = null;\n /** Marca si ya se inició la navegación manual (setScreen) — para RN sin History API. */\n private navStarted = false;\n\n /** Initialize the SDK with configuration */\n init(config: DeepdotsInitParams): void {\n if (this.initialized) {\n this.log('SDK already initialized');\n return;\n }\n\n const env = resolveEnvironment(config.nodeEnv);\n\n this.baseUrl = env.apiBaseUrl;\n this.env = config.nodeEnv || 'production';\n\n this.config = {\n apiKey: config.apiKey || undefined,\n debug: config.debug || false,\n userId: config.userId || undefined,\n } as DeepdotsConfig;\n\n // user_id lo gestiona el SDK (persistente); el session_id lo provee el backend\n // (respuesta de POST /sdk/popups) y se cachea — el SDK no genera ni expira sesiones.\n const storage = config.storage ?? createDefaultStorage();\n this.tracking = new TrackingManager({\n storage,\n clientUserId: this.config.userId,\n enabled: config.trackingEnabled ?? true,\n });\n\n // Contact: info interna del usuario que solo conoce el host (segmentación/targeting).\n // Solo trackeamos usuarios IDENTIFICADOS → se crea únicamente si hay userId del init.\n if (this.config.userId) {\n const userId = this.config.userId;\n const publicKey = this.config.apiKey ?? '';\n this.contact = new ContactManager({\n storage,\n publicKey,\n userId,\n post: (body) => this.postContact(body),\n });\n // Atributos de contact pasados en init: envío fire-and-forget (solo si cambiaron).\n if (config.contactAttributes) {\n void this.setContactAttributes(config.contactAttributes);\n }\n }\n this.log('tracking · user_id:', this.tracking.getUserId(), '· new_user:', this.tracking.isNewUser(), '· enabled:', this.tracking.isTrackingEnabled());\n\n // Analytics: se envía como Feedback a la integración (`POST /sdk/feedback`) si se\n // pasan claves en init.analytics; si no, queda en dry-run (solo console.log).\n const analyticsSink = config.analytics\n ? createFeedbackSink({\n baseUrl: this.baseUrl,\n keys: config.analytics,\n log: (...a) => this.log(...a),\n onSessionId: (id) => { this.analyticsFeedbackSessionId = id; },\n })\n : undefined;\n this.analytics = new AnalyticsManager({\n sink: analyticsSink,\n publicKey: config.analytics?.publicKey ?? this.config.apiKey,\n language: typeof navigator !== 'undefined' ? navigator.language : undefined,\n platform: config.platform ?? 'web',\n device: config.device ?? collectDeviceInfo(config.appVersion),\n maxBatchSize: ANALYTICS_MAX_BATCH_SIZE,\n onFlushNeeded: () => this.flushAnalytics(),\n });\n // Geolocalización por IP (fire-and-forget): rellena country/city cuando resuelve.\n collectGeoInfo().then((geo) => { if (geo) this.analytics?.updateDevice(geo); }).catch(() => {});\n // Fase 2: navegación → eventos page_view por el canal de analytics.\n this.navObserver = new NavigationObserver();\n this.navObserver.onVisit((v) => this.track('page_view', { screen: v.screen, duration_seconds: v.durationSeconds }));\n this.navObserver.install();\n // Engagement time (#8): cuenta tiempo activo en primer plano.\n this.engagement = new EngagementTracker();\n this.engagement.resume();\n this.setupAnalyticsFlush();\n\n this.initialized = true;\n this.log('SDK initialized', this.config);\n if (this.renderer.init) this.renderer.init();\n this.setupPopupContainer();\n\n // Los popups SIEMPRE se reciben de la API (no se definen en init).\n this.fetchPopupsFromServer().then((defs) => {\n this.popupDefinitions = this.validatePopupDefinitions(defs);\n this.popupsLoaded = true;\n this.log('Popups loaded from API', this.popupDefinitions);\n this.configureTriggersFromDefinitions();\n this.processDeferredExitQueue();\n if (this.pendingAutoLaunch) {\n this.startTriggers();\n }\n });\n }\n\n /** User id actual (generado por el SDK o provisto por el cliente). Null si tracking off. */\n getUserId(): string | null {\n return this.tracking?.getUserId() ?? null;\n }\n\n /** Session id de navegación actual. Null si tracking off. */\n getSessionId(): string | null {\n return this.tracking?.getSessionId() ?? null;\n }\n\n /** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */\n setTrackingEnabled(enabled: boolean): void {\n this.tracking?.setTrackingEnabled(enabled);\n this.log('tracking · setTrackingEnabled:', enabled, '· session_id:', this.tracking?.getSessionId() ?? null);\n }\n\n // ───────── Analytics (canal separado del feedback, vinculado por user_id) ─────────\n\n /** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */\n track(name: string, params?: Record<string, unknown>): void {\n if (!this.tracking?.isTrackingEnabled()) return;\n this.analytics?.track(name, params);\n this.log('analytics · track:', name, params ?? {});\n }\n\n /** User attributes para breakdowns (registration_status, pass_type, sector, pass_status…). */\n setUserAttributes(attributes: Record<string, string | number | boolean>): void {\n if (!this.tracking?.isTrackingEnabled()) return;\n this.analytics?.setUserAttributes(attributes);\n }\n\n /**\n * Info interna del usuario que solo conoce el host (idioma, edad, plan…), persistida en el\n * Contact del backend para segmentación/targeting de popups. Identificada por el `userId` del\n * init. Solo envía si los atributos cambiaron (diff en storage). No-op si tracking off o sin userId.\n * @returns `true` si se envió al backend, `false` si no hubo cambios o está deshabilitado.\n */\n async setContactAttributes(attributes: ContactAttributes): Promise<boolean> {\n if (!this.tracking?.isTrackingEnabled()) return false;\n if (!this.contact) return false;\n return this.contact.setAttributes(attributes);\n }\n\n /** Marca el inicio de un mini-service; etiqueta los eventos siguientes. No-op si tracking off. */\n enterMiniService(name: string, entryPointType?: string): void {\n if (!this.tracking?.isTrackingEnabled()) return;\n this.analytics?.enterMiniService(name, entryPointType);\n this.log('analytics · enterMiniService:', name, entryPointType ?? '');\n }\n\n /** Cierra el mini-service activo (emite `mini_service_exit` con duración, #27). No-op si tracking off. */\n exitMiniService(): void {\n if (!this.tracking?.isTrackingEnabled()) return;\n this.analytics?.exitMiniService();\n this.log('analytics · exitMiniService');\n }\n\n /** Findability (#31/#35): registra una búsqueda. `has_results` se deriva de `resultsCount`. */\n trackSearch(query: string, resultsCount: number, params?: Record<string, unknown>): void {\n this.track('search', { query, results_count: resultsCount, has_results: resultsCount > 0, ...(params ?? {}) });\n }\n\n /** Findability friction (#34/#35): señal de fricción con su `friction_topic`. */\n trackFindabilityFriction(frictionTopic: string, params?: Record<string, unknown>): void {\n this.track('findability_friction', { friction_topic: frictionTopic, ...(params ?? {}) });\n }\n\n /** Funnel: un paso del embudo, correlacionado por `taskId`. El backend reconstruye conversión/drop-off/tiempo. */\n trackFunnelStep(funnel: string, step: string, taskId: string, params?: Record<string, unknown>): void {\n this.track('funnel_step', { funnel, step, task_id: taskId, ...(params ?? {}) });\n }\n\n /**\n * Navegación MANUAL (entornos sin History API, p. ej. React Native con React Navigation).\n * El host la llama en cada cambio de pantalla; emite `page_view` al salir de la anterior.\n */\n setScreen(name: string): void {\n if (!this.navObserver) return;\n if (!this.navStarted) {\n this.navObserver.begin(name);\n this.navStarted = true;\n } else {\n this.navObserver.visit(name);\n }\n }\n\n /** App a foreground (RN: AppState 'active'): reanuda el engagement time. */\n onForeground(): void {\n this.engagement?.resume();\n }\n\n /**\n * App a background (RN: AppState 'background'): cierra pantalla actual (page_view),\n * mini-service (mini_service_exit), emite engagement y hace flush del lote.\n */\n onBackground(): void {\n this.navObserver?.stop();\n this.navStarted = false;\n this.exitMiniService();\n this.flushEngagement();\n this.engagement?.pause();\n this.flushAnalytics();\n }\n\n /** Payload que se ENVIARÍA al endpoint de analytics (no envía ni vacía el buffer). */\n previewAnalytics(): AnalyticsEnvelope {\n return (\n this.analytics?.buildPayload(this.analyticsIdentity()) ?? {\n userId: null,\n sessionId: null,\n context: { platform: 'web', attributes: {} },\n events: [],\n }\n );\n }\n\n /** Envía (hoy dry-run → console.log) el lote acumulado de analytics y vacía el buffer. */\n flushAnalytics(): void {\n if (!this.tracking?.isTrackingEnabled()) return;\n this.analytics?.flush(this.analyticsIdentity());\n }\n\n /** Emite un evento `user_engagement` con el tiempo activo acumulado (#8). */\n private flushEngagement(): void {\n if (!this.tracking?.isTrackingEnabled()) return;\n const ms = this.engagement?.consume() ?? 0;\n if (ms > 0) this.track('user_engagement', { engagement_time_msec: ms });\n }\n\n private analyticsIdentity() {\n return {\n userId: this.tracking?.getUserId() ?? null,\n sessionId: this.tracking?.getSessionId() ?? null,\n };\n }\n\n /** Flush automático al ocultar/cerrar la página (no perder el lote pendiente). */\n private setupAnalyticsFlush(): void {\n if (typeof document === 'undefined' || typeof window === 'undefined') return;\n // flush periódico mientras la app está en primer plano\n this.analyticsFlushTimer = setInterval(() => this.flushAnalytics(), ANALYTICS_FLUSH_INTERVAL_MS);\n // al cerrar la página: cerrar pantalla (page_view) + mini-service + engagement, y enviar el lote\n window.addEventListener('pagehide', () => {\n clearInterval(this.analyticsFlushTimer);\n this.navObserver?.stop();\n this.exitMiniService();\n this.flushEngagement();\n this.flushAnalytics();\n });\n // al ocultar/mostrar la pestaña: pausar/reanudar engagement y enviar lo acumulado\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') {\n this.flushEngagement();\n this.engagement?.pause();\n this.flushAnalytics();\n } else if (document.visibilityState === 'visible') {\n this.engagement?.resume();\n }\n });\n }\n\n /** Enable auto-launch functionality with configured triggers */\n autoLaunch(): void {\n if (!this.initialized) {\n throw new Error('SDK not initialized. Call init() first.');\n }\n if (!this.popupsLoaded) {\n this.pendingAutoLaunch = true;\n this.log('Auto-launch deferred until popups are loaded');\n return;\n }\n this.startTriggers();\n }\n\n /** Inicia los triggers configurados */\n private startTriggers(): void {\n this.log('Auto-launch enabled');\n this.triggers.forEach((trigger) => setupTrigger(this, trigger));\n }\n\n /** Configure triggers for auto-launching popups (manual) */\n configureTriggers(triggers: TriggerConfig[]): void {\n if (!this.initialized) {\n throw new Error('SDK not initialized. Call init() first.');\n }\n this.triggers = triggers;\n this.log('Triggers configured', triggers);\n }\n\n /** Deriva triggers desde las definiciones de popup */\n private configureTriggersFromDefinitions(): void {\n const derived: TriggerConfig[] = [];\n this.debug('Validated popup definitions', this.popupDefinitions);\n\n this.popupDefinitions.forEach((def) => {\n def.triggers.forEach((trigger) => {\n const type = this.mapPopupTriggerType(trigger.type);\n if (!type) {\n this.debug('Unsupported trigger type', trigger.type);\n return;\n }\n const value = type === 'time' && typeof trigger.value === 'number'\n ? trigger.value * 1000\n : trigger.value;\n derived.push({ type, value, surveyId: def.surveyId, popupId: def.id });\n });\n });\n\n if (derived.length) {\n this.configureTriggers(derived);\n }\n }\n\n /** Lógica para evaluar condiciones antes de mostrar una encuesta */\n triggerSurvey(surveyId: string, popupId?: string): void {\n const def = this.findPopupDefinition(surveyId, popupId);\n if (!def) {\n this.debug('No popup definition for trigger', { surveyId, popupId });\n return;\n }\n if (!this.shouldShow(def)) {\n this.debug('Conditions prevented showing popup', def.id);\n return;\n }\n this.showDefinition(def);\n }\n\n /** Trigger popups configured with trigger.type = 'event' and matching trigger.value */\n public triggerEvent(eventName: string): void {\n if (!this.initialized) {\n throw new Error('SDK not initialized. Call init() first.');\n }\n const normalized = String(eventName || '').trim();\n if (!normalized) {\n this.debug('Ignoring empty event trigger name');\n return;\n }\n\n const candidates = this.popupDefinitions.filter((def) => {\n return def.triggers.some((trigger) => {\n const triggerValue = String(trigger.value ?? '').trim();\n return trigger.type === 'event' && triggerValue === normalized;\n });\n });\n\n if (!candidates.length) {\n this.debug('No event popup definitions found', { eventName: normalized });\n return;\n }\n\n const matched = candidates.find((def) => this.shouldShow(def));\n if (!matched) {\n this.debug('Event popup blocked by conditions/segments', { eventName: normalized });\n return;\n }\n\n this.showDefinition(matched);\n }\n\n private showDefinition(def: PopupDefinition): void {\n this.log('Showing popup (definition)', def);\n this.surveyToPopupId.set(def.surveyId, def.id);\n this.lastShown.set(def.id, Date.now());\n this.renderPopup(def.surveyId, def.productId, def.actions, def.style);\n this.emitEvent('popup_shown', def.surveyId, { popupId: def.id });\n }\n\n private shouldShow(def: NormalizedPopupDefinition, pathUrl?: string, skipPathCheck = false): boolean {\n if (!skipPathCheck && !this.matchesSegmentsPath(def, pathUrl)) {\n return false;\n }\n if (!this.matchesSegmentsLang(def)) {\n return false;\n }\n\n const cooldowns = def.cooldown || [];\n if (cooldowns.length && !cooldowns.every((cooldown) => this.evaluateCooldown(def, cooldown))) {\n return false;\n }\n\n const legacyConditions = def.legacyConditions || [];\n if (!legacyConditions.length) return true;\n return legacyConditions.every((condition) => this.evaluateLegacyCondition(def, condition));\n }\n\n private matchesSegmentsLang(def: PopupDefinition): boolean {\n const langs = def.segments?.lang;\n if (!langs || langs.length === 0) return true;\n\n // navigator.language puede ser \"en\", \"en-US\", \"da\", \"da-DK\", etc.\n const browserLang = (\n typeof navigator !== 'undefined' ? navigator.language : ''\n ).toLowerCase();\n\n if (!browserLang) {\n this.debug('No navigator.language available for lang comparison', { popupId: def.id, langs });\n return true;\n }\n\n // Acepta si el idioma del browser empieza con alguno de los configurados\n // ej: browser \"en-US\" coincide con segment \"en\"\n const matches = langs.some((lang) => browserLang.startsWith(lang.toLowerCase()));\n if (!matches) {\n this.debug('Lang mismatch for popup', { popupId: def.id, langs, browserLang });\n }\n return matches;\n }\n\n private matchesSegmentsPath(def: PopupDefinition, pathUrl?: string): boolean {\n const paths = def.segments?.path;\n if (!paths || paths.length === 0) return true;\n if (typeof window === 'undefined' || !window.location) {\n this.debug('No window.location available for path comparison', { popupId: def.id, paths });\n return true;\n }\n\n const normalizedHref = this.normalizeUrl(pathUrl || window.location.href || '');\n const currentUrl = this.safeParseUrl(normalizedHref);\n const normalizedPath = this.normalizeUrl(currentUrl?.pathname || window.location.pathname || '');\n\n const matches = paths.some((rawCandidate) => {\n const candidate = this.normalizeUrl(rawCandidate);\n let match = false;\n if (candidate.startsWith('http://') || candidate.startsWith('https://')) {\n match = normalizedHref === candidate;\n } else if (candidate.startsWith('/')) {\n match = normalizedHref.includes(candidate);\n } else {\n match = normalizedPath === candidate;\n }\n this.debug('Path comparison', {\n popupId: def.id,\n candidate,\n currentPath: normalizedPath,\n currentHref: normalizedHref,\n match,\n });\n return match;\n });\n\n if (!matches) {\n this.debug('No path match for popup', { popupId: def.id, paths, currentPath: normalizedPath });\n }\n\n return matches;\n }\n\n private evaluateCooldown(def: PopupDefinition, condition: PopupTriggerCondition): boolean {\n switch (condition.answered) {\n case 'SHOWED':\n return this.hasCooldownElapsed(this.lastShown.get(def.id), condition.cooldownDays);\n case 'PARTIAL':\n case 'COMPLETED': {\n const progress = this.surveyProgress.get(def.surveyId);\n if (!progress || progress.status !== condition.answered) {\n return true;\n }\n return this.hasCooldownElapsed(progress.timestamp, condition.cooldownDays);\n }\n default:\n return true;\n }\n }\n\n private evaluateLegacyCondition(def: PopupDefinition, condition: LegacyPopupTriggerCondition): boolean {\n if (!condition.answered && this.answeredSurveys.has(def.surveyId)) {\n return false;\n }\n return this.hasCooldownElapsed(this.lastShown.get(def.id), condition.cooldownDays);\n }\n\n private hasCooldownElapsed(timestamp: number | undefined, cooldownDays?: number): boolean {\n if (!timestamp || !cooldownDays) {\n return true;\n }\n return Date.now() - timestamp >= cooldownDays * DAY_IN_MS;\n }\n\n /** Marcar encuesta como contestada externamente */\n public markSurveyAnswered(surveyId: string): void {\n this.answeredSurveys.add(surveyId);\n this.markSurveyProgress(surveyId, 'COMPLETED');\n }\n\n /** Queue an exit popup so it can render after navigation */\n public queueExitPopup(surveyId: string, delaySeconds: number, sourceUrl?: string, popupId?: string): void {\n const def = this.findPopupDefinition(surveyId, popupId);\n if (!def) {\n this.debug('No popup definition for exit trigger', { surveyId, popupId });\n return;\n }\n\n const originUrl = sourceUrl || (typeof window !== 'undefined' ? window.location.href : '');\n if (!originUrl) {\n this.debug('Exit popup skipped: missing source URL', { surveyId });\n return;\n }\n\n if (!this.shouldShow(def, originUrl)) {\n this.debug('Exit popup skipped by conditions/path', { popupId: def.id, sourceUrl: originUrl });\n return;\n }\n\n const safeDelayMs = Number.isFinite(delaySeconds) ? Math.max(0, delaySeconds * 1000) : 0;\n const deferred: DeferredExitPopup = {\n id: def.id,\n surveyId: def.surveyId,\n sourceUrl: this.normalizeUrl(originUrl),\n dueAt: Date.now() + safeDelayMs,\n };\n\n const queue = this.getDeferredExitQueue().filter((item) => !(item.id === deferred.id && item.sourceUrl === deferred.sourceUrl));\n queue.push(deferred);\n this.setDeferredExitQueue(queue);\n this.scheduleDeferredExitPopup(deferred);\n this.debug('Exit popup queued', deferred);\n }\n\n private processDeferredExitQueue(): void {\n const queue = this.getDeferredExitQueue();\n if (!queue.length) return;\n\n const pending: DeferredExitPopup[] = [];\n const now = Date.now();\n queue.forEach((item) => {\n const def = this.popupDefinitions.find((popup) => popup.id === item.id && popup.surveyId === item.surveyId);\n if (!def) {\n return;\n }\n if (item.dueAt <= now) {\n this.tryShowDeferredExitPopup(item);\n return;\n }\n pending.push(item);\n this.scheduleDeferredExitPopup(item);\n });\n this.setDeferredExitQueue(pending);\n }\n\n private scheduleDeferredExitPopup(item: DeferredExitPopup): void {\n if (typeof window === 'undefined') return;\n const delay = Math.max(0, item.dueAt - Date.now());\n const timer = window.setTimeout(() => this.tryShowDeferredExitPopup(item), delay);\n this.deferredExitTimers.push(timer);\n }\n\n private tryShowDeferredExitPopup(item: DeferredExitPopup): void {\n const def = this.popupDefinitions.find((popup) => popup.id === item.id && popup.surveyId === item.surveyId);\n if (!def) {\n this.removeDeferredExitPopup(item);\n return;\n }\n\n const currentUrl = typeof window !== 'undefined' ? this.normalizeUrl(window.location.href || '') : '';\n if (!currentUrl || currentUrl === this.normalizeUrl(item.sourceUrl)) {\n this.removeDeferredExitPopup(item);\n this.debug('Exit popup dropped because route did not change', item);\n return;\n }\n\n if (!this.shouldShow(def, undefined, true)) {\n this.removeDeferredExitPopup(item);\n this.debug('Exit popup skipped at render-time conditions', item);\n return;\n }\n\n this.showDefinition(def);\n this.removeDeferredExitPopup(item);\n }\n\n private findPopupDefinition(surveyId: string, popupId?: string): NormalizedPopupDefinition | undefined {\n if (popupId) {\n const byId = this.popupDefinitions.find((popup) => popup.id === popupId);\n if (byId) return byId;\n }\n return this.popupDefinitions.find((popup) => popup.surveyId === surveyId);\n }\n\n private removeDeferredExitPopup(item: DeferredExitPopup): void {\n const queue = this.getDeferredExitQueue().filter((entry) => !(entry.id === item.id && entry.sourceUrl === item.sourceUrl));\n this.setDeferredExitQueue(queue);\n }\n\n private getDeferredExitQueue(): DeferredExitPopup[] {\n if (typeof window === 'undefined') return [];\n try {\n const raw = window.sessionStorage.getItem(EXIT_QUEUE_STORAGE_KEY);\n if (!raw) return [];\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return [];\n return parsed\n .filter((entry: unknown): entry is DeferredExitPopup => {\n if (typeof entry !== 'object' || entry === null) return false;\n const item = entry as Partial<DeferredExitPopup>;\n return typeof item.id === 'string'\n && typeof item.surveyId === 'string'\n && typeof item.sourceUrl === 'string'\n && typeof item.dueAt === 'number'\n && Number.isFinite(item.dueAt);\n })\n .map((item) => ({\n ...item,\n sourceUrl: this.normalizeUrl(item.sourceUrl),\n }));\n } catch {\n return [];\n }\n }\n\n private setDeferredExitQueue(queue: DeferredExitPopup[]): void {\n if (typeof window === 'undefined') return;\n try {\n if (!queue.length) {\n window.sessionStorage.removeItem(EXIT_QUEUE_STORAGE_KEY);\n return;\n }\n window.sessionStorage.setItem(EXIT_QUEUE_STORAGE_KEY, JSON.stringify(queue));\n } catch {\n // Ignore storage errors in host environments that block storage access.\n }\n }\n\n /** Fetch al servidor para obtener popups */\n private async fetchPopupsFromServer(): Promise<unknown[]> {\n const apiKey = this.config?.apiKey;\n const baseUrl = this.baseUrl;\n const userId = this.config?.userId;\n if (!apiKey || !baseUrl) {\n this.log('Missing apiKey or baseUrl. Skipping popups fetch.');\n return [];\n }\n const filter = userId ? { where: { userId } } : null;\n const query = filter ? `?filter=${encodeURIComponent(JSON.stringify(filter))}` : '';\n const endpoint = `${baseUrl}/sdk/${encodeURIComponent(apiKey)}/popups${query}`;\n try {\n const response = await fetch(endpoint);\n if (!response.ok) {\n this.log('Failed to fetch popups', response.status, response.statusText);\n return [];\n }\n const raw = await response.text();\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n this.log('Unexpected popups payload', parsed);\n return [];\n }\n this.debug('Fetched popups payload', parsed);\n return parsed;\n } catch (error) {\n this.log('Error fetching popups', error);\n return [];\n }\n }\n\n /** Envía los atributos de contact a la API (`POST /sdk/popups/contact`). */\n private async postContact(body: { publicKey: string; userId: string; userAttributes: ContactAttributes }): Promise<void> {\n const baseUrl = this.baseUrl;\n if (!baseUrl) return;\n const endpoint = `${baseUrl}/sdk/popups/contact`;\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n this.log('Failed to post contact', response.status, response.statusText);\n return;\n }\n this.log('contact · sent attributes for user:', body.userId);\n } catch (error) {\n this.log('Error posting contact', error);\n }\n }\n\n /** Notifica eventos de popup a la API */\n private async postPopupEvent(status: POPUPSESSIONSTATUS, popupId: string, userId?: string): Promise<void> {\n const apiKey = this.config?.apiKey;\n const baseUrl = this.baseUrl;\n if (!apiKey || !baseUrl) {\n return;\n }\n const endpoint = `${baseUrl}/sdk/popups`;\n try {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n publicKey: apiKey,\n status,\n popupId,\n userId: userId || undefined,\n }),\n });\n if (!response.ok) {\n this.log('Failed to post popup event', response.status, response.statusText);\n return;\n }\n // Contrato Fase 1 §6: el backend devuelve el sessionId; lo cacheamos para\n // inyectarlo en las respuestas de survey (lo cose por user_id).\n try {\n const data = await response.json();\n if (data && typeof data.sessionId === 'string') {\n this.tracking?.setSessionId(data.sessionId);\n this.log('tracking · session_id from backend:', data.sessionId);\n }\n } catch {\n // respuesta sin cuerpo JSON: nada que cachear\n }\n } catch (error) {\n this.log('Error posting popup event', error);\n }\n }\n\n /** Add an event listener */\n on(eventType: DeepdotsEventType, listener: EventListener): void {\n if (!this.listeners.has(eventType)) {\n this.listeners.set(eventType, new Set());\n }\n this.listeners.get(eventType)!.add(listener);\n }\n\n /** Remove an event listener */\n off(eventType: DeepdotsEventType, listener: EventListener): void {\n const listeners = this.listeners.get(eventType);\n if (listeners) {\n listeners.delete(listener);\n }\n }\n\n /** Setup popup container element */\n private setupPopupContainer(): void {\n if (typeof document === 'undefined') {\n return;\n }\n this.popupContainer = document.createElement('div');\n this.popupContainer.id = 'deepdots-popup-container';\n this.popupContainer.style.cssText = `\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: none;\n z-index: 999999;\n background: rgba(0, 0, 0, 0.5);\n justify-content: center;\n align-items: center;\n `;\n document.body.appendChild(this.popupContainer);\n }\n\n /** Render the popup UI */\n private renderPopup(surveyId: string, productId: string, actions?: PopupActions, style?: PopupStyle): void {\n const userId = this.tracking?.getUserId() ?? this.config?.userId;\n const sessionId = this.tracking?.getSessionId() ?? undefined;\n const miniService = this.analytics?.getMiniService() ?? undefined;\n const analyticsFeedbackSessionId = this.analyticsFeedbackSessionId;\n this.log('tracking · survey identity →', { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null, analyticsFeedbackSessionId: analyticsFeedbackSessionId ?? null });\n if (this.renderer) {\n this.renderer.show(\n surveyId,\n productId,\n actions,\n (type, id, payload) => this.emitEvent(type, id, payload),\n () => this.hidePopup(),\n this.env,\n userId,\n style,\n sessionId,\n miniService,\n analyticsFeedbackSessionId,\n );\n return;\n }\n\n if (!this.popupContainer) return;\n const container = this.popupContainer;\n void import('../ui/renderPopup').then(({ renderPopup }) => {\n renderPopup(\n container,\n surveyId,\n productId,\n actions,\n (type, id, payload) => this.emitEvent(type, id, payload),\n () => this.hidePopup(),\n this.env,\n userId,\n style,\n sessionId,\n miniService,\n analyticsFeedbackSessionId,\n );\n });\n }\n\n /** Hide the popup */\n private hidePopup(): void {\n if (this.renderer) {\n this.renderer.hide();\n return;\n }\n if (this.popupContainer) {\n this.popupContainer.style.display = 'none';\n this.popupContainer.innerHTML = '';\n }\n }\n\n /** Emit an event */\n private emitEvent(type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>): void {\n const event: DeepdotsEvent = { type, surveyId, timestamp: Date.now(), data };\n this.log('Event emitted', event);\n\n const isPartialClick = type === 'popup_clicked' && data?.action === 'partial';\n if (isPartialClick) {\n this.markSurveyProgress(surveyId, 'PARTIAL');\n }\n if (type === 'survey_completed') {\n this.markSurveyAnswered(surveyId);\n }\n if (type === 'popup_shown' || type === 'survey_completed' || isPartialClick) {\n const popupIdFromData = data?.popupId as string | undefined;\n const popupId = popupIdFromData || this.surveyToPopupId.get(surveyId);\n if (popupId) {\n const userIdFromData = data?.userId as string | undefined;\n const status = type === 'popup_shown'\n ? POPUPSESSIONSTATUS.SHOWED\n : type === 'survey_completed'\n ? POPUPSESSIONSTATUS.COMPLETED\n : POPUPSESSIONSTATUS.PARTIAL;\n void this.postPopupEvent(status, popupId, userIdFromData || this.tracking?.getUserId() || this.config?.userId);\n } else {\n this.debug('No popupId available to post event', { type, surveyId });\n }\n }\n\n const listeners = this.listeners.get(type);\n if (listeners) {\n listeners.forEach((listener) => {\n try {\n listener(event);\n } catch (error) {\n console.error('Error in event listener:', error);\n }\n });\n }\n }\n\n /** Log debug messages */\n private log(...args: unknown[]): void {\n if (this.config?.debug) {\n console.log('[DeepdotsPopups]', ...args);\n }\n }\n\n /** External debug method for triggers */\n public debug(...args: unknown[]): void {\n this.log(...args);\n }\n\n /** Set a custom renderer */\n public setRenderer(renderer: PopupRenderer): void {\n this.renderer = renderer;\n if (this.initialized && this.renderer.init) {\n this.renderer.init();\n }\n }\n\n private normalizeUrl(value: string): string {\n if (!value) return '';\n const withoutIndex = value.replace(/\\/index\\.html(?=($|[?#]))/i, '');\n return withoutIndex.length > 1 && withoutIndex.endsWith('/') ? withoutIndex.slice(0, -1) : withoutIndex;\n }\n\n private safeParseUrl(url: string): URL | null {\n if (!url) return null;\n try {\n return new URL(url, typeof window !== 'undefined' ? window.location.href : undefined);\n } catch {\n return null;\n }\n }\n\n private mapPopupTriggerType(type: PopupTrigger['type']): TriggerConfig['type'] | null {\n switch (type) {\n case 'time_on_page':\n return 'time';\n case 'scroll':\n return 'scroll';\n case 'exit':\n return 'exit';\n case 'click':\n return 'click';\n case 'event':\n return 'event';\n default:\n return null;\n }\n }\n\n private markSurveyProgress(surveyId: string, status: PopupTriggerConditionStatus): void {\n const current = this.surveyProgress.get(surveyId);\n if (current?.status === 'COMPLETED' && status !== 'COMPLETED') {\n return;\n }\n this.surveyProgress.set(surveyId, { status, timestamp: Date.now() });\n }\n\n private isPopupDefinition(value: unknown): value is NormalizedPopupDefinition {\n if (typeof value !== 'object' || value === null) return false;\n const popup = value as Partial<NormalizedPopupDefinition>;\n\n return typeof popup.id === 'string'\n && typeof popup.title === 'string'\n && typeof popup.message === 'string'\n && typeof popup.surveyId === 'string'\n && typeof popup.productId === 'string'\n && Array.isArray(popup.triggers)\n && popup.triggers.length > 0\n && popup.triggers.every((trigger) => this.isPopupTrigger(trigger))\n && (!popup.cooldown || popup.cooldown.every((condition) => this.isPopupTriggerCondition(condition)));\n }\n\n private isPopupTrigger(value: unknown): value is PopupTrigger {\n if (typeof value !== 'object' || value === null) return false;\n const trigger = value as Partial<PopupTrigger>;\n return this.isPopupTriggerType(trigger.type)\n && (typeof trigger.value === 'number' || typeof trigger.value === 'string');\n }\n\n private isPopupTriggerType(value: unknown): value is PopupTrigger['type'] {\n return value === 'time_on_page'\n || value === 'scroll'\n || value === 'exit'\n || value === 'click'\n || value === 'event';\n }\n\n private isPopupTriggerCondition(value: unknown): value is PopupTriggerCondition {\n if (typeof value !== 'object' || value === null) return false;\n const condition = value as Partial<PopupTriggerCondition>;\n return POPUP_TRIGGER_CONDITION_STATUSES.includes(condition.answered as PopupTriggerConditionStatus)\n && typeof condition.cooldownDays === 'number'\n && Number.isFinite(condition.cooldownDays)\n && condition.cooldownDays >= 0;\n }\n\n private isLegacyPopupTriggerCondition(value: unknown): value is LegacyPopupTriggerCondition {\n if (typeof value !== 'object' || value === null) return false;\n const condition = value as Partial<LegacyPopupTriggerCondition>;\n if (condition.answered !== undefined && typeof condition.answered !== 'boolean') {\n return false;\n }\n if (condition.cooldownDays !== undefined) {\n return typeof condition.cooldownDays === 'number'\n && Number.isFinite(condition.cooldownDays)\n && condition.cooldownDays >= 0;\n }\n return true;\n }\n\n private normalizeTriggers(value: unknown): PopupTrigger[] {\n const items = Array.isArray(value) ? value : value ? [value] : [];\n return items\n .filter((trigger): trigger is PopupTrigger => this.isPopupTrigger(trigger))\n .map((trigger) => ({\n type: trigger.type,\n value: trigger.value,\n }));\n }\n\n private normalizeCooldown(value: unknown): PopupTriggerCondition[] {\n if (!Array.isArray(value)) return [];\n return value.filter((condition): condition is PopupTriggerCondition => this.isPopupTriggerCondition(condition));\n }\n\n private normalizeLegacyConditions(value: unknown): LegacyPopupTriggerCondition[] {\n if (!Array.isArray(value)) return [];\n return value.filter((condition): condition is LegacyPopupTriggerCondition => this.isLegacyPopupTriggerCondition(condition));\n }\n\n private normalizePopupDefinition(def: unknown): NormalizedPopupDefinition | null {\n if (typeof def !== 'object' || def === null) return null;\n const raw = def as Partial<PopupDefinition> & {\n trigger?: PopupTrigger | PopupTrigger[];\n triggers?: PopupTrigger | PopupTrigger[];\n conditions?: LegacyPopupTriggerCondition[];\n cooldown?: PopupTriggerCondition[];\n };\n\n const rawTriggers = raw.triggers ?? raw.trigger;\n const triggers = this.normalizeTriggers(rawTriggers);\n if (!triggers.length) return null;\n\n const rawTriggerList = Array.isArray(rawTriggers) ? rawTriggers : rawTriggers ? [rawTriggers] : [];\n const legacyConditions = [\n ...this.normalizeLegacyConditions(raw.conditions),\n ...rawTriggerList.flatMap((trigger) => this.normalizeLegacyConditions((trigger as { condition?: unknown }).condition)),\n ];\n const cooldown = this.normalizeCooldown(raw.cooldown);\n\n return {\n ...(raw as PopupDefinition),\n triggers,\n cooldown,\n legacyConditions,\n };\n }\n\n private validatePopupDefinitions(defs: unknown[]): NormalizedPopupDefinition[] {\n return defs\n .map((def) => this.normalizePopupDefinition(def))\n .filter((def): def is NormalizedPopupDefinition => !!def && this.isPopupDefinition(def));\n }\n}\n","/**\n * Auto-wiring de React Native: concentra TODA la configuración en el SDK para que el\n * host solo tenga que envolver su app en un `<DeepdotsProvider config={...}>`.\n *\n * No importa React ni librerías nativas: el host (o el Provider) le pasa los módulos\n * nativos que tenga instalados (MMKV, device-info, AppState). Todo es opcional y degrada\n * con elegancia (sin MMKV → in-memory; sin device-info → sin device info).\n */\nimport type { DeepdotsPopups } from '../core/deepdots-popups';\nimport type { DeepdotsInitParams } from '../types';\nimport type { KeyValueStorage } from '../tracking/tracking-manager';\nimport type { DeviceInfo } from '../analytics/device-info';\nimport type { PopupRenderer } from '../platform/renderer';\n\n/** Forma mínima de `react-native-mmkv` (instancia MMKV). */\nexport interface MmkvLike {\n getString(key: string): string | undefined | null;\n set(key: string, value: string): void;\n delete(key: string): void;\n}\n\n/** Forma mínima de `react-native-device-info`. */\nexport interface DeviceInfoLike {\n isTablet?: () => boolean;\n getSystemVersion?: () => string;\n getDeviceId?: () => string;\n getVersion?: () => string;\n}\n\n/** Forma mínima de `AppState` de react-native. */\nexport interface AppStateLike {\n addEventListener(type: 'change', listener: (state: string) => void): { remove: () => void } | void;\n}\n\nexport interface ReactNativeSetupDeps {\n mmkv?: MmkvLike | null;\n deviceInfo?: DeviceInfoLike | null;\n appState?: AppStateLike | null;\n platform?: 'web' | 'android' | 'ios';\n renderer?: PopupRenderer;\n}\n\n/** Adaptador KeyValueStorage (síncrono) sobre una instancia de MMKV. */\nexport function mmkvStorage(mmkv: MmkvLike): KeyValueStorage {\n return {\n getItem: (k) => mmkv.getString(k) ?? null,\n setItem: (k, v) => mmkv.set(k, v),\n removeItem: (k) => mmkv.delete(k),\n };\n}\n\n/** Recoge device info desde react-native-device-info (Technology #11–13). */\nexport function collectRnDevice(d: DeviceInfoLike): DeviceInfo {\n return {\n device_type: d.isTablet?.() ? 'tablet' : 'mobile',\n os_version: d.getSystemVersion?.(),\n device_model: d.getDeviceId?.(),\n app_version: d.getVersion?.(),\n };\n}\n\n/**\n * Configura el SDK para React Native en una sola llamada: storage persistente (MMKV),\n * device info, platform, init y lifecycle (AppState → onForeground/onBackground).\n * Devuelve una función de limpieza (quita el listener de AppState).\n */\nexport function setupReactNative(\n sdk: DeepdotsPopups,\n config: DeepdotsInitParams,\n deps: ReactNativeSetupDeps = {},\n): () => void {\n if (deps.renderer) sdk.setRenderer(deps.renderer);\n\n const storage = deps.mmkv ? mmkvStorage(deps.mmkv) : config.storage;\n const device = deps.deviceInfo ? collectRnDevice(deps.deviceInfo) : config.device;\n const platform = deps.platform ?? config.platform;\n\n sdk.init({ ...config, storage, device, platform });\n\n let subscription: { remove: () => void } | void;\n if (deps.appState?.addEventListener) {\n subscription = deps.appState.addEventListener('change', (state) => {\n if (state === 'active') sdk.onForeground();\n else sdk.onBackground(); // 'background' | 'inactive'\n });\n }\n\n return () => {\n if (subscription && typeof subscription.remove === 'function') subscription.remove();\n };\n}\n"],"mappings":"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","names":["sdk"]}