{"version":3,"sources":["../src/tracking/tracking-manager.ts"],"sourcesContent":["/**\n * Identidad + sesión para el tracking de navegación (Fase 1).\n * Implementa el \"Contrato común Web+KMP\".\n *\n * Modelo: el SDK gestiona el `user_id` (persistente). El `session_id` es propiedad\n * del BACKEND: llega en la respuesta de `POST /sdk/popups` y se cachea; el backend\n * cose sesiones por `user_id` + ventana. El SDK NO genera ni expira sesiones.\n *\n * Sin dependencias de DOM: recibe el storage y (opcionalmente) el reloj y el\n * generador de uuid por inyección. El binding a `localStorage` vive en `createDefaultStorage`.\n */\n\n/** Store clave/valor mínimo (espejo conceptual del `KeyValueStorage` de KMP). */\nexport interface KeyValueStorage {\n  getItem(key: string): string | null;\n  setItem(key: string, value: string): void;\n  removeItem(key: string): void;\n}\n\n/** Impl en memoria — usada en tests y como fallback sin `localStorage`. */\nexport class InMemoryStorage implements KeyValueStorage {\n  private map = new Map<string, string>();\n  getItem(key: string): string | null {\n    return this.map.has(key) ? (this.map.get(key) as string) : null;\n  }\n  setItem(key: string, value: string): void {\n    this.map.set(key, value);\n  }\n  removeItem(key: string): void {\n    this.map.delete(key);\n  }\n}\n\n/** Storage por defecto en Web: `localStorage` si está disponible, si no in-memory. */\nexport function createDefaultStorage(): KeyValueStorage {\n  try {\n    if (typeof window !== 'undefined' && window.localStorage) {\n      const ls = window.localStorage;\n      const probe = '__deepdots_probe__';\n      ls.setItem(probe, '1');\n      ls.removeItem(probe);\n      return {\n        getItem: (k) => ls.getItem(k),\n        setItem: (k, v) => ls.setItem(k, v),\n        removeItem: (k) => ls.removeItem(k),\n      };\n    }\n  } catch {\n    // cae a in-memory\n  }\n  return new InMemoryStorage();\n}\n\n/** Claves de storage — namespace `deepdots.*`. Solo identidad (la sesión no se persiste). */\nexport const STORAGE_KEYS = {\n  userId: 'deepdots.user_id',\n  firstSeen: 'deepdots.user.first_seen',\n  contactAttributes: 'deepdots.contact.attributes',\n} as const;\n\n/** Par clave/valor con el formato `NativeAnswer` que espera `@magicfeedback/native`. */\nexport interface IdentityAnswer {\n  key: string;\n  value: string[];\n}\n\n/**\n * Construye el `profile` y el `metadata` de identidad para inyectar en el survey\n * (`magicfeedback.form(appId, publicKey, profile, metadata)`), según el contrato §5.\n * `session_id` solo se incluye si el backend ya lo proveyó.\n */\nexport function buildSurveyIdentity(\n  userId: string | null,\n  sessionId: string | null,\n  miniService: string | null = null,\n): { profile: IdentityAnswer[]; metadata: IdentityAnswer[] } {\n  const profile: IdentityAnswer[] = userId ? [{ key: 'external-user-id', value: [userId] }] : [];\n  const metadata: IdentityAnswer[] = [];\n  if (sessionId) metadata.push({ key: 'session_id', value: [sessionId] });\n  if (userId) metadata.push({ key: 'user_id', value: [userId] });\n  if (miniService) metadata.push({ key: 'mini_service', value: [miniService] });\n  return { profile, metadata };\n}\n\nexport interface TrackingManagerOptions {\n  storage: KeyValueStorage;\n  /** Id provisto por el cliente; si existe se usa y NO se persiste. */\n  clientUserId?: string;\n  /** Reloj inyectable (default `Date.now`). */\n  now?: () => number;\n  /** Generador de uuid inyectable (default uuid v4). */\n  uuid?: () => string;\n  /** Estado inicial del tracking (default `true`). */\n  enabled?: boolean;\n}\n\nfunction defaultUuid(): string {\n  const g = (typeof globalThis !== 'undefined' ? globalThis : {}) as {\n    crypto?: { randomUUID?: () => string; getRandomValues?: (a: Uint8Array) => Uint8Array };\n  };\n  if (g.crypto?.randomUUID) return g.crypto.randomUUID();\n  const bytes = new Uint8Array(16);\n  if (g.crypto?.getRandomValues) {\n    g.crypto.getRandomValues(bytes);\n  } else {\n    for (let i = 0; i < 16; i++) bytes[i] = Math.floor(Math.random() * 256);\n  }\n  bytes[6] = (bytes[6] & 0x0f) | 0x40;\n  bytes[8] = (bytes[8] & 0x3f) | 0x80;\n  const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0'));\n  return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex\n    .slice(6, 8)\n    .join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`;\n}\n\nexport class TrackingManager {\n  private storage: KeyValueStorage;\n  private clientUserId?: string;\n  private now: () => number;\n  private uuid: () => string;\n  private enabled: boolean;\n\n  /** session_id cacheado, provisto por el backend (no se genera ni se persiste). */\n  private sessionId: string | null = null;\n  private userWasJustCreated = false;\n\n  constructor(options: TrackingManagerOptions) {\n    this.storage = options.storage;\n    this.clientUserId = options.clientUserId;\n    this.now = options.now ?? (() => Date.now());\n    this.uuid = options.uuid ?? defaultUuid;\n    this.enabled = options.enabled ?? true;\n  }\n\n  isTrackingEnabled(): boolean {\n    return this.enabled;\n  }\n\n  setTrackingEnabled(enabled: boolean): void {\n    this.enabled = enabled;\n  }\n\n  /** Resuelve el user_id según la regla: cliente > persistido > generar+persistir. */\n  getUserId(): string | null {\n    if (!this.enabled) return null;\n    if (this.clientUserId) return this.clientUserId;\n\n    const persisted = this.storage.getItem(STORAGE_KEYS.userId);\n    if (persisted) return persisted;\n\n    const id = this.uuid();\n    this.storage.setItem(STORAGE_KEYS.userId, id);\n    this.storage.setItem(STORAGE_KEYS.firstSeen, String(this.now()));\n    this.userWasJustCreated = true;\n    return id;\n  }\n\n  /** `true` solo en la primera sesión tras crear el user_id propio del SDK. */\n  isNewUser(): boolean {\n    return this.userWasJustCreated;\n  }\n\n  /** session_id actual provisto por el backend, o null si aún no llegó. */\n  getSessionId(): string | null {\n    if (!this.enabled) return null;\n    return this.sessionId;\n  }\n\n  /** Cachea el session_id devuelto por el backend (respuesta de POST /sdk/popups). */\n  setSessionId(sessionId: string | null): void {\n    if (!this.enabled) return;\n    this.sessionId = sessionId;\n  }\n}\n"],"mappings":"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","names":[]}