{"version":3,"sources":["../../orbit-errors/src/index.ts","../src/api-client.ts","../src/event-stream.ts","../src/styles.ts","../src/storage.ts","../src/icons.ts","../src/orbit-chat.ts","../src/softphone.ts","../src/voice-agent/index.ts","../src/salesforce-opencti-adapter.ts","../src/cdp-analytics.ts","../src/web-analytics.ts","../src/cdp-analytics-queue.ts"],"sourcesContent":["/**\n * Canonical, zero-dependency error types for the Orbit *client* SDKs.\n *\n * `OrbitApiError` was previously declared byte-for-byte in BOTH the web SDK\n * (`@devotel/orbit-web-sdk`) and the React-Native SDK\n * (`@devotel/orbit-react-native-sdk`). Because the two packages share no common\n * dependency there was no forcing function to keep the shapes in sync, so the\n * duplicate definitions were free to drift apart over time (dead-code finding\n * act_mqyty69u_be459d45bba7). This package is the single source of truth both\n * SDKs now re-export.\n *\n * It is intentionally dependency-free and runtime-agnostic (only `Error`,\n * `number`, and `string`): the consuming SDKs are tsup-bundled, so this source\n * is inlined into their `dist` output and their published artifacts stay\n * zero-dependency and browser/Hermes-safe — exactly the constraint that ruled\n * out importing the Node-flavoured `@devotel/shared/errors`.\n *\n * NOTE: the Node SDK (`@devotel/orbit-sdk`) and the CLI (`@devotel/cli`) keep\n * their own richer `OrbitApiError` (a `code`/`details`/`docsUrl`/`requestId`\n * field set plus a status-coded subclass hierarchy). They are `tsc`-built\n * published packages with an incompatible constructor signature, so folding\n * them in here would be a breaking, multi-package change rather than this safe\n * dead-duplicate removal.\n */\n\n/**\n * Typed error thrown by the Orbit client SDKs when an HTTP request to the\n * conversations API returns a non-OK response. Callers can branch reliably on\n * `status` (e.g. a 429 throttle vs a 401 token expiry vs a 5xx) instead of\n * regex-parsing a free-text message.\n *\n *   - `status` is the literal HTTP status code.\n *   - `responseExcerpt` is the (truncated) response body, kept for diagnostics.\n *   - `retryAfterSeconds` is parsed from the `Retry-After` header when present\n *     (RFC 7231 delta-seconds or HTTP-date), so a UI can render a live\n *     countdown / disable the offending action for the throttle window.\n */\nexport class OrbitApiError extends Error {\n  readonly status: number;\n  readonly responseExcerpt: string;\n  // `declare` keeps this type-only so no class-field initializer is emitted\n  // (target ES2024 => useDefineForClassFields). That way the property is a\n  // genuine own-property ONLY when the constructor assigns it, so callers can\n  // rely on `\"retryAfterSeconds\" in err` to detect a parsed `Retry-After`.\n  declare readonly retryAfterSeconds?: number;\n  constructor(\n    status: number,\n    responseExcerpt: string,\n    message: string,\n    retryAfterSeconds?: number,\n  ) {\n    super(message);\n    this.name = \"OrbitApiError\";\n    this.status = status;\n    this.responseExcerpt = responseExcerpt;\n    if (retryAfterSeconds !== undefined) {\n      this.retryAfterSeconds = retryAfterSeconds;\n    }\n  }\n}\n","import { OrbitApiError } from \"@devotel/orbit-errors\";\nimport type {\n  ChatMessage,\n  ApiListResponse,\n  ApiResponse,\n  Conversation,\n} from \"./types\";\n\n/**\n * SCAN sdk-web-edge-02 — typed error so OrbitChat (and any other legacy\n * ApiClient consumer) can branch reliably on the HTTP status instead of\n * regex-parsing a free-text message. Before this, every non-OK response\n * threw a bare `new Error(\"Orbit API error 429: …\")`, so a 429 throttle,\n * a 401 token expiry, and a 500 were all indistinguishable to the UI.\n *\n * The class itself now lives once in the zero-dependency\n * `@devotel/orbit-errors` package and is re-exported here so existing\n * consumers keep importing `OrbitApiError` from this module. Previously it\n * was redefined byte-for-byte in both this SDK and the React-Native SDK with\n * no forcing function to keep them in sync (dead-code finding\n * act_mqyty69u_be459d45bba7). tsup bundles the shared source inline, so the\n * published web bundle stays zero-dependency and browser-safe — the same\n * constraint that ruled out importing the Node-flavoured\n * `@devotel/shared/errors`.\n */\nexport { OrbitApiError };\n\n/**\n * Parse a `Retry-After` header into delta-seconds. Per RFC 7231 the value\n * is either an integer number of seconds or an HTTP-date; we accept both\n * and ignore anything non-positive or unparseable. Returns `undefined`\n * when the header is absent or invalid so callers can fall back cleanly.\n */\nfunction parseRetryAfterSeconds(\n  headerValue: string | null,\n): number | undefined {\n  if (!headerValue) return undefined;\n  const asNumber = Number(headerValue);\n  if (Number.isFinite(asNumber) && asNumber > 0) {\n    return Math.ceil(asNumber);\n  }\n  const asDate = Date.parse(headerValue);\n  if (Number.isFinite(asDate)) {\n    const diffMs = asDate - Date.now();\n    if (diffMs > 0) return Math.ceil(diffMs / 1000);\n  }\n  return undefined;\n}\n\n/**\n * Lightweight HTTP client for the Orbit conversations API.\n * Handles authentication, conversation lifecycle, and message exchange.\n */\nexport class ApiClient {\n  private baseUrl: string;\n  private apiKey: string;\n  private conversationId: string | null = null;\n\n  constructor(baseUrl: string, apiKey: string) {\n    this.baseUrl = baseUrl.replace(/\\/+$/, \"\");\n    this.apiKey = apiKey;\n  }\n\n  /** Returns the active conversation ID (if any). */\n  getConversationId(): string | null {\n    return this.conversationId;\n  }\n\n  /** Sets the active conversation ID (restored from storage). */\n  setConversationId(id: string): void {\n    this.conversationId = id;\n  }\n\n  /**\n   * Create a new chat conversation via the widget endpoint.\n   * Falls back to finding an existing open conversation for the visitor.\n   */\n  async createConversation(\n    visitorId?: string,\n    metadata?: Record<string, string>,\n  ): Promise<Conversation> {\n    const payload: Record<string, unknown> = {\n      channel: \"web_chat\",\n      metadata: {\n        ...metadata,\n        source: \"orbit-chat-widget\",\n        visitor_id: visitorId ?? this.generateVisitorId(),\n        user_agent: typeof navigator !== \"undefined\" ? navigator.userAgent : \"\",\n        page_url: typeof location !== \"undefined\" ? location.href : \"\",\n      },\n    };\n\n    const resp = await this.request<ApiResponse<Conversation>>(\n      \"POST\",\n      \"/conversations\",\n      payload,\n    );\n    this.conversationId = resp.data.id;\n    return resp.data;\n  }\n\n  /**\n   * List conversations for the current visitor.\n   * The API filters by the API key's tenant.\n   */\n  async listConversations(status?: string): Promise<Conversation[]> {\n    const params = new URLSearchParams();\n    if (status) params.set(\"status\", status);\n    params.set(\"channel\", \"web_chat\");\n    params.set(\"limit\", \"1\");\n\n    const resp = await this.request<ApiListResponse<Conversation>>(\n      \"GET\",\n      `/conversations?${params.toString()}`,\n    );\n    return resp.data;\n  }\n\n  /** Fetch messages for the current conversation. */\n  async getMessages(limit = 50): Promise<ChatMessage[]> {\n    if (!this.conversationId) return [];\n\n    const resp = await this.request<ApiListResponse<ChatMessage>>(\n      \"GET\",\n      `/conversations/${this.conversationId}/messages?limit=${limit}`,\n    );\n    // API returns newest-first; reverse for chronological display\n    return resp.data.reverse();\n  }\n\n  /** Send a text message in the current conversation. */\n  async sendMessage(body: string): Promise<ChatMessage> {\n    if (!this.conversationId) {\n      throw new Error(\n        \"No active conversation. Call createConversation() first.\",\n      );\n    }\n\n    const resp = await this.request<ApiResponse<ChatMessage>>(\n      \"POST\",\n      `/conversations/${this.conversationId}/reply`,\n      { body },\n    );\n    return resp.data;\n  }\n\n  /** Upload a file attachment and send it as a message. */\n  async sendAttachment(file: File): Promise<ChatMessage> {\n    if (!this.conversationId) {\n      throw new Error(\n        \"No active conversation. Call createConversation() first.\",\n      );\n    }\n\n    // First upload the file\n    const formData = new FormData();\n    formData.append(\"file\", file);\n    formData.append(\"conversation_id\", this.conversationId);\n\n    const uploadResp = await this.uploadFile(formData);\n    const uploadData = uploadResp[\"data\"] as\n      | Record<string, unknown>\n      | undefined;\n    const mediaUrl = (uploadResp[\"url\"] ?? uploadData?.[\"url\"]) as\n      | string\n      | undefined;\n\n    // Then send a message referencing the uploaded file\n    const resp = await this.request<ApiResponse<ChatMessage>>(\n      \"POST\",\n      `/conversations/${this.conversationId}/reply`,\n      {\n        body: file.name,\n        media_url: mediaUrl,\n        metadata: {\n          file_name: file.name,\n          file_type: file.type,\n          file_size: String(file.size),\n        },\n      },\n    );\n    return resp.data;\n  }\n\n  /** Returns the configured widget API key (used by EventStream for header auth on polling fallback). */\n  getApiKey(): string {\n    return this.apiKey;\n  }\n\n  /**\n   * Audit #SDK-004 — Server-backed session exchange. Swaps the long-\n   * lived widget public key (`dv_live_pk_…`) for a short-lived event-\n   * stream token. The widget calls this before opening SSE so the\n   * long-lived key NEVER appears on the SSE URL (and therefore never\n   * lands in CDN / proxy / browser-history logs).\n   *\n   * Returns null when the server doesn't implement `/events/session`\n   * so the EventStream can fall back to the polling transport (which\n   * uses `X-API-Key` on a header, not the URL).\n   */\n  async exchangeEventStreamToken(): Promise<string | null> {\n    try {\n      const resp = await this.request<\n        { data?: { token?: string } } | { token?: string }\n      >(\"POST\", \"/events/session\");\n      const tok =\n        (resp as { data?: { token?: string } }).data?.token ??\n        (resp as { token?: string }).token;\n      return tok ?? null;\n    } catch {\n      return null;\n    }\n  }\n\n  /** Generic JSON request helper with a 30-second safety timeout. */\n  private async request<T>(\n    method: string,\n    path: string,\n    body?: unknown,\n  ): Promise<T> {\n    const url = `${this.baseUrl}${path}`;\n    const headers: Record<string, string> = {\n      \"X-API-Key\": this.apiKey,\n      Accept: \"application/json\",\n    };\n    if (body) headers[\"Content-Type\"] = \"application/json\";\n\n    const resp = await fetch(url, {\n      method,\n      headers,\n      body: body ? JSON.stringify(body) : undefined,\n      // Without this, a hung server keeps the widget spinner forever;\n      // 30s covers legit slow routes without leaving requests dangling.\n      signal: AbortSignal.timeout(30_000),\n    });\n\n    if (!resp.ok) {\n      const errorText = await resp.text().catch(() => \"Unknown error\");\n      // Preserve the legacy message format for backward compat, but throw a\n      // typed OrbitApiError carrying status + Retry-After so callers can\n      // branch on a 429 throttle vs a 401 expiry vs a 500.\n      throw new OrbitApiError(\n        resp.status,\n        errorText.slice(0, 200),\n        `Orbit API error ${resp.status}: ${errorText}`,\n        parseRetryAfterSeconds(resp.headers.get(\"Retry-After\")),\n      );\n    }\n\n    return resp.json() as Promise<T>;\n  }\n\n  /** Upload file via multipart form. 60-second timeout for larger uploads. */\n  private async uploadFile(\n    formData: FormData,\n  ): Promise<Record<string, unknown>> {\n    const url = `${this.baseUrl}/files`;\n    const resp = await fetch(url, {\n      method: \"POST\",\n      headers: {\n        \"X-API-Key\": this.apiKey,\n      },\n      body: formData,\n      signal: AbortSignal.timeout(60_000),\n    });\n\n    if (!resp.ok) {\n      const errorText = await resp.text().catch(() => \"Unknown error\");\n      throw new OrbitApiError(\n        resp.status,\n        errorText.slice(0, 200),\n        `File upload failed: ${resp.status}`,\n        parseRetryAfterSeconds(resp.headers.get(\"Retry-After\")),\n      );\n    }\n\n    return resp.json() as Promise<Record<string, unknown>>;\n  }\n\n  /**\n   * Generate a random visitor ID for anonymous users. Uses\n   * `crypto.getRandomValues` so the id is unguessable — a predictable\n   * `Math.random()`-derived id would let one visitor enumerate other\n   * visitor sessions for the same widget. Browser support is universal\n   * on every target we ship to (Chrome/Firefox/Safari/Edge — IE is not\n   * supported by sdk-web).\n   */\n  private generateVisitorId(): string {\n    const bytes = new Uint8Array(8);\n    crypto.getRandomValues(bytes);\n    let id = \"vis_\";\n    for (const b of bytes) {\n      id += b.toString(16).padStart(2, \"0\");\n    }\n    return id;\n  }\n}\n","/**\n * Real-time event stream via Server-Sent Events (SSE).\n * Connects to the Orbit /events/stream endpoint and dispatches\n * typed events to registered listeners.\n *\n * Falls back to polling when SSE is unavailable.\n *\n * Audit #SDK-004 — On `connect()`, the stream first POSTs to the API to\n * swap the long-lived widget API key for a short-lived stream token,\n * then opens SSE with `?token=<short-lived>`. The long-lived API key\n * never appears on the SSE URL (and therefore never lands in CDN /\n * proxy / browser-history logs). If the swap endpoint returns null\n * (older API server), the stream falls back to polling — we no longer\n * silently accept a long-lived `?token=` on the SSE URL.\n */\n\nimport type { ApiClient } from \"./api-client\";\n\ntype EventHandler = (data: Record<string, unknown>) => void;\n\nexport class EventStream {\n  private baseSseUrl: string;\n  private eventSource: EventSource | null = null;\n  private listeners = new Map<string, Set<EventHandler>>();\n  private reconnectAttempts = 0;\n  private maxReconnectAttempts = 10;\n  private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n  private connected = false;\n  private apiClient: ApiClient;\n\n  /** Polling fallback state. */\n  private pollingTimer: ReturnType<typeof setInterval> | null = null;\n  private usePolling = false;\n  private pollUrl: string;\n  private pollHeaders: Record<string, string>;\n  private lastPollTimestamp: string | null = null;\n\n  constructor(sseBaseUrl: string, pollBaseUrl: string, apiClient: ApiClient) {\n    // The SSE base URL is constructed without any `?token=` shortcut —\n    // a short-lived token is appended later via `connectSSEWithSwap()`.\n    this.baseSseUrl = `${pollBaseUrl}/events/stream`;\n    this.pollUrl = `${pollBaseUrl}/events`;\n    this.apiClient = apiClient;\n    this.pollHeaders = {\n      \"X-API-Key\": apiClient.getApiKey(),\n      Accept: \"application/json\",\n    };\n  }\n\n  /**\n   * Start the event stream. Tries SSE first (after swapping the long-\n   * lived widget key for a short-lived token), falls back to polling.\n   */\n  connect(): void {\n    if (typeof EventSource !== \"undefined\") {\n      void this.connectSSEWithSwap();\n    } else {\n      this.startPolling();\n    }\n  }\n\n  /**\n   * Audit #SDK-004 — swap the widget's long-lived API key for a short-\n   * lived stream token before opening SSE. The short-lived token is OK\n   * to put on the URL because it expires quickly + is scoped to the\n   * visitor session. If the server doesn't implement the swap, fall\n   * back to polling — the long-lived key never lands in URL logs.\n   */\n  private async connectSSEWithSwap(): Promise<void> {\n    let shortLived: string | null = null;\n    try {\n      shortLived = await this.apiClient.exchangeEventStreamToken();\n    } catch {\n      shortLived = null;\n    }\n    if (!shortLived) {\n      // Server doesn't expose the swap endpoint or it failed. Fall back\n      // to the polling transport (which uses `X-API-Key` on a header,\n      // not a URL token). We deliberately do NOT fall back to long-lived\n      // `?token=` SSE — that's the leak we're fixing.\n      this.startPolling();\n      return;\n    }\n    this.connectSSE(\n      `${this.baseSseUrl}?token=${encodeURIComponent(shortLived)}`,\n    );\n  }\n\n  /** Close the event stream and clean up. */\n  disconnect(): void {\n    this.connected = false;\n    if (this.eventSource) {\n      this.eventSource.close();\n      this.eventSource = null;\n    }\n    if (this.reconnectTimer) {\n      clearTimeout(this.reconnectTimer);\n      this.reconnectTimer = null;\n    }\n    if (this.pollingTimer) {\n      clearInterval(this.pollingTimer);\n      this.pollingTimer = null;\n    }\n  }\n\n  /** Register a handler for a specific event type. */\n  on(eventType: string, handler: EventHandler): void {\n    let handlers = this.listeners.get(eventType);\n    if (!handlers) {\n      handlers = new Set();\n      this.listeners.set(eventType, handlers);\n    }\n    handlers.add(handler);\n\n    // If SSE is already connected, add the event listener on the source\n    if (\n      this.eventSource &&\n      eventType !== \"connected\" &&\n      eventType !== \"error\"\n    ) {\n      this.eventSource.addEventListener(eventType, ((e: MessageEvent) => {\n        try {\n          const data = JSON.parse(e.data) as Record<string, unknown>;\n          handler(data);\n        } catch (_err) {\n          /* non-JSON payload */\n        }\n      }) as EventListener);\n    }\n  }\n\n  /** Remove a handler for a specific event type. */\n  off(eventType: string, handler: EventHandler): void {\n    const handlers = this.listeners.get(eventType);\n    if (handlers) {\n      handlers.delete(handler);\n    }\n  }\n\n  /** Whether the stream is currently connected. */\n  isConnected(): boolean {\n    return this.connected;\n  }\n\n  /* ── SSE implementation ── */\n\n  private connectSSE(url?: string): void {\n    try {\n      // Caller passes the freshly-swapped short-lived token URL; on\n      // reconnect (no arg) we re-swap and start over rather than\n      // re-using a possibly-expired short-lived token.\n      if (!url) {\n        void this.connectSSEWithSwap();\n        return;\n      }\n      this.eventSource = new EventSource(url);\n\n      this.eventSource.addEventListener(\"connected\", ((e: MessageEvent) => {\n        this.connected = true;\n        this.reconnectAttempts = 0;\n        this.emit(\"connected\", this.safeParse(e.data));\n      }) as EventListener);\n\n      // Listen for generic 'message' events\n      this.eventSource.addEventListener(\"message\", ((e: MessageEvent) => {\n        const data = this.safeParse(e.data);\n        const eventType = (data.type as string) ?? \"message\";\n        this.emit(eventType, data);\n      }) as EventListener);\n\n      // Register listeners for common event types\n      const commonEvents = [\n        \"message.created\",\n        \"message.updated\",\n        \"message.delivered\",\n        \"conversation.updated\",\n        \"conversation.assigned\",\n        \"typing.start\",\n        \"typing.stop\",\n      ];\n\n      for (const eventType of commonEvents) {\n        this.eventSource.addEventListener(eventType, ((e: MessageEvent) => {\n          this.emit(eventType, this.safeParse(e.data));\n        }) as EventListener);\n      }\n\n      this.eventSource.onerror = () => {\n        this.connected = false;\n        this.emit(\"error\", { message: \"SSE connection error\" });\n\n        if (this.eventSource?.readyState === EventSource.CLOSED) {\n          this.scheduleReconnect();\n        }\n      };\n    } catch (_err) {\n      // SSE failed, fall back to polling\n      this.startPolling();\n    }\n  }\n\n  private scheduleReconnect(): void {\n    if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n      // Switch to polling after too many SSE failures\n      this.startPolling();\n      return;\n    }\n\n    // Exponential backoff: 1s, 2s, 4s, 8s... capped at 30s\n    const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30_000);\n    this.reconnectAttempts++;\n\n    this.reconnectTimer = setTimeout(() => {\n      this.connectSSE();\n    }, delay);\n  }\n\n  /* ── Polling fallback ── */\n\n  private startPolling(): void {\n    this.usePolling = true;\n    this.pollingTimer = setInterval(() => {\n      void this.poll();\n    }, 3000); // Poll every 3 seconds\n    // Immediately do a first poll\n    void this.poll();\n  }\n\n  private async poll(): Promise<void> {\n    try {\n      const url = this.lastPollTimestamp\n        ? `${this.pollUrl}?since=${encodeURIComponent(this.lastPollTimestamp)}`\n        : this.pollUrl;\n\n      // Polling fallback runs every 3 s — bound the per-request wait to\n      // 10 s so a hung connection doesn't let the next tick stack up\n      // behind the previous one. AbortError is swallowed by the catch\n      // below, mirroring network-error handling.\n      const resp = await fetch(url, {\n        headers: this.pollHeaders,\n        signal: AbortSignal.timeout(10_000),\n      });\n      if (!resp.ok) return;\n\n      const json = (await resp.json()) as {\n        data?: Array<Record<string, unknown>>;\n      };\n      const events = json.data ?? [];\n\n      // First successful poll (HTTP 200) means the polling fallback is\n      // working — flip `connected` so consumers can stop showing a\n      // \"reconnecting…\" state. The previous `events.length >= 0` guard\n      // was always true, but the intent is just \"we got a successful\n      // response back\", which is what reaching this line implies.\n      if (!this.connected) {\n        this.connected = true;\n        this.emit(\"connected\", {});\n      }\n\n      for (const event of events) {\n        const eventType = (event.type as string) ?? \"message\";\n        this.emit(eventType, event);\n        // Track the latest timestamp for delta polling\n        if (event.created_at) {\n          this.lastPollTimestamp = event.created_at as string;\n        }\n      }\n    } catch (_err) {\n      // Polling errors are non-fatal; we just try again next interval\n    }\n  }\n\n  /* ── Internal helpers ── */\n\n  private emit(eventType: string, data: Record<string, unknown>): void {\n    const handlers = this.listeners.get(eventType);\n    if (handlers) {\n      for (const handler of handlers) {\n        try {\n          handler(data);\n        } catch (_err) {\n          /* handler errors should not break the stream */\n        }\n      }\n    }\n\n    // Also fire wildcard listeners\n    const wildcardHandlers = this.listeners.get(\"*\");\n    if (wildcardHandlers) {\n      for (const handler of wildcardHandlers) {\n        try {\n          handler({ ...data, _eventType: eventType });\n        } catch (_err) {\n          /* swallow handler errors */\n        }\n      }\n    }\n  }\n\n  private safeParse(raw: string): Record<string, unknown> {\n    try {\n      return JSON.parse(raw) as Record<string, unknown>;\n    } catch (_err) {\n      return { raw };\n    }\n  }\n}\n","/**\n * Audit #SDK-006 — `primaryColor` is template-interpolated raw into the\n * Shadow DOM CSS at multiple sites. A malformed value can:\n *   - break the CSS parse (token-escape into `}…{` and rewrite host\n *     selectors inside the shadow root, which still influences ::part /\n *     ::slotted)\n *   - smuggle a `url('https://attacker.example.com/track.png')` payload\n *     that fires an outbound request every time the chat icon paints,\n *     bypassing the embedding page's `img-src` CSP (it travels through\n *     `background:` which is governed by `style-src` on the host site).\n *\n * The allowed shape is intentionally narrow: hex colours with 3/4/6/8\n * digits. Anything else falls back to the widget default. `console.warn`\n * makes the rejection observable for the integrator.\n */\nconst HEX_COLOR_REGEX = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\nconst DEFAULT_PRIMARY_COLOR_FALLBACK = \"#6366f1\";\n\n/**\n * Validates a candidate primaryColor and returns either the safe input\n * or the safe default. Warns when an invalid value is rejected.\n *\n * @internal Exposed for tests and constructor-time validation.\n */\nexport function sanitizePrimaryColor(candidate: string | undefined): string {\n  if (typeof candidate !== \"string\") return DEFAULT_PRIMARY_COLOR_FALLBACK;\n  if (HEX_COLOR_REGEX.test(candidate)) return candidate;\n  if (typeof console !== \"undefined\" && console.warn) {\n    // eslint-disable-next-line no-console\n    console.warn(\n      `OrbitChat: primaryColor \"${candidate}\" is not a #RGB / #RGBA / #RRGGBB / #RRGGBBAA hex string — falling back to ${DEFAULT_PRIMARY_COLOR_FALLBACK}. See https://docs.orbit.devotel.io/widget/styling.`,\n    );\n  }\n  return DEFAULT_PRIMARY_COLOR_FALLBACK;\n}\n\n/**\n * Generates all CSS for the Orbit Chat widget.\n * Styles are injected into a Shadow DOM to avoid conflicts with host page styles.\n */\nexport function generateStyles(options: {\n  position: \"bottom-right\" | \"bottom-left\";\n  theme: \"light\" | \"dark\";\n  primaryColor: string;\n}): string {\n  const { position, theme } = options;\n  // Audit #SDK-006 — re-sanitize at the generator boundary as a\n  // defense-in-depth measure; the constructor sanitizes too, but a\n  // direct call into generateStyles from another integration point\n  // should also be protected.\n  const primaryColor = sanitizePrimaryColor(options.primaryColor);\n  // `position` and `theme` are typed unions — narrow them again at\n  // runtime so a JS caller (no TS) can't smuggle a non-matching value\n  // through. Anything off-spec falls back to the safe default.\n  const isLeft = position === \"bottom-left\";\n  const isDark = theme === \"dark\";\n\n  // Theme tokens\n  const bg = isDark ? \"#1a1b2e\" : \"#ffffff\";\n  const bgSecondary = isDark ? \"#252640\" : \"#f3f4f6\";\n  const bgTertiary = isDark ? \"#2d2e4a\" : \"#e5e7eb\";\n  const text = isDark ? \"#e4e5f1\" : \"#1a1a2e\";\n  const textSecondary = isDark ? \"#9ca3b0\" : \"#6b7280\";\n  const border = isDark ? \"#3a3b5c\" : \"#e5e7eb\";\n  const inputBg = isDark ? \"#252640\" : \"#f9fafb\";\n  const inputBorder = isDark ? \"#3a3b5c\" : \"#d1d5db\";\n  const shadowColor = isDark ? \"rgba(0,0,0,0.4)\" : \"rgba(0,0,0,0.15)\";\n\n  const positionRule = isLeft\n    ? \"left: 20px; right: auto;\"\n    : \"right: 20px; left: auto;\";\n\n  return /* css */ `\n    :host {\n      all: initial;\n      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\n      font-size: 14px;\n      line-height: 1.5;\n      color: ${text};\n      direction: ltr;\n    }\n\n    *, *::before, *::after {\n      box-sizing: border-box;\n      margin: 0;\n      padding: 0;\n    }\n\n    /* ── Toggle button ── */\n    .orbit-toggle {\n      position: fixed;\n      bottom: 20px;\n      ${positionRule}\n      width: 56px;\n      height: 56px;\n      border-radius: 50%;\n      background: ${primaryColor};\n      color: #ffffff;\n      border: none;\n      cursor: pointer;\n      font-size: 24px;\n      box-shadow: 0 4px 14px ${shadowColor};\n      z-index: 2147483647;\n      transition: transform 0.2s ease, box-shadow 0.2s ease;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      outline: none;\n    }\n    .orbit-toggle:hover {\n      transform: scale(1.08);\n      box-shadow: 0 6px 20px ${shadowColor};\n    }\n    .orbit-toggle:focus-visible {\n      outline: 2px solid ${primaryColor};\n      outline-offset: 2px;\n    }\n    .orbit-toggle svg {\n      width: 28px;\n      height: 28px;\n      fill: #ffffff;\n      transition: transform 0.2s ease;\n    }\n    .orbit-toggle.open svg {\n      transform: rotate(90deg);\n    }\n\n    /* Unread badge */\n    .orbit-badge {\n      position: absolute;\n      top: -2px;\n      right: -2px;\n      width: 20px;\n      height: 20px;\n      border-radius: 50%;\n      background: #ef4444;\n      color: #ffffff;\n      font-size: 11px;\n      font-weight: 700;\n      display: none;\n      align-items: center;\n      justify-content: center;\n      line-height: 1;\n    }\n    .orbit-badge.visible {\n      display: flex;\n    }\n\n    /* ── Chat panel ── */\n    .orbit-panel {\n      position: fixed;\n      bottom: 88px;\n      ${positionRule}\n      width: 380px;\n      height: 560px;\n      max-height: calc(100vh - 110px);\n      border-radius: 16px;\n      background: ${bg};\n      box-shadow: 0 8px 40px ${shadowColor};\n      z-index: 2147483647;\n      overflow: hidden;\n      display: none;\n      flex-direction: column;\n      opacity: 0;\n      transform: translateY(12px) scale(0.96);\n      transition: opacity 0.2s ease, transform 0.2s ease;\n    }\n    .orbit-panel.open {\n      display: flex;\n    }\n    .orbit-panel.visible {\n      opacity: 1;\n      transform: translateY(0) scale(1);\n    }\n\n    /* ── Header ── */\n    .orbit-header {\n      padding: 16px 20px;\n      background: ${primaryColor};\n      color: #ffffff;\n      display: flex;\n      align-items: center;\n      gap: 12px;\n      flex-shrink: 0;\n    }\n    .orbit-header-avatar {\n      width: 36px;\n      height: 36px;\n      border-radius: 50%;\n      background: rgba(255,255,255,0.2);\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      flex-shrink: 0;\n      overflow: hidden;\n    }\n    .orbit-header-avatar img {\n      width: 100%;\n      height: 100%;\n      object-fit: cover;\n      border-radius: 50%;\n    }\n    .orbit-header-avatar svg {\n      width: 20px;\n      height: 20px;\n      fill: #ffffff;\n    }\n    .orbit-header-info {\n      flex: 1;\n      min-width: 0;\n    }\n    .orbit-header-title {\n      font-weight: 600;\n      font-size: 15px;\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n    .orbit-header-status {\n      font-size: 12px;\n      opacity: 0.85;\n      display: flex;\n      align-items: center;\n      gap: 4px;\n    }\n    .orbit-status-dot {\n      width: 7px;\n      height: 7px;\n      border-radius: 50%;\n      background: #34d399;\n      display: inline-block;\n    }\n    .orbit-close {\n      background: none;\n      border: none;\n      color: #ffffff;\n      cursor: pointer;\n      font-size: 20px;\n      padding: 4px;\n      border-radius: 4px;\n      line-height: 1;\n      opacity: 0.8;\n      transition: opacity 0.15s;\n    }\n    .orbit-close:hover {\n      opacity: 1;\n    }\n\n    /* ── Messages area ── */\n    .orbit-messages {\n      flex: 1;\n      padding: 16px;\n      overflow-y: auto;\n      overflow-x: hidden;\n      display: flex;\n      flex-direction: column;\n      gap: 8px;\n      scroll-behavior: smooth;\n    }\n    .orbit-messages::-webkit-scrollbar {\n      width: 4px;\n    }\n    .orbit-messages::-webkit-scrollbar-track {\n      background: transparent;\n    }\n    .orbit-messages::-webkit-scrollbar-thumb {\n      background: ${bgTertiary};\n      border-radius: 2px;\n    }\n\n    /* Greeting message */\n    .orbit-greeting {\n      text-align: center;\n      padding: 24px 16px;\n      color: ${textSecondary};\n      font-size: 14px;\n      line-height: 1.6;\n    }\n\n    /* Message bubbles */\n    .orbit-msg {\n      max-width: 80%;\n      padding: 10px 14px;\n      border-radius: 16px;\n      font-size: 14px;\n      line-height: 1.5;\n      word-break: break-word;\n      animation: orbit-fade-in 0.2s ease;\n    }\n    @keyframes orbit-fade-in {\n      from { opacity: 0; transform: translateY(4px); }\n      to   { opacity: 1; transform: translateY(0); }\n    }\n\n    .orbit-msg.outbound {\n      align-self: flex-end;\n      background: ${primaryColor};\n      color: #ffffff;\n      border-bottom-right-radius: 4px;\n    }\n    .orbit-msg.inbound {\n      align-self: flex-start;\n      background: ${bgSecondary};\n      color: ${text};\n      border-bottom-left-radius: 4px;\n    }\n\n    .orbit-msg-time {\n      font-size: 11px;\n      opacity: 0.6;\n      margin-top: 4px;\n    }\n    .orbit-msg.outbound .orbit-msg-time {\n      text-align: right;\n    }\n    .orbit-msg.inbound .orbit-msg-time {\n      text-align: left;\n    }\n\n    /* Attachment bubble */\n    .orbit-msg-attachment {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      padding: 8px 12px;\n      background: rgba(255,255,255,0.15);\n      border-radius: 8px;\n      margin-top: 4px;\n      font-size: 13px;\n      text-decoration: none;\n      color: inherit;\n    }\n    .orbit-msg.inbound .orbit-msg-attachment {\n      background: ${bgTertiary};\n    }\n    .orbit-msg-attachment svg {\n      width: 16px;\n      height: 16px;\n      flex-shrink: 0;\n      fill: currentColor;\n    }\n\n    /* Attachment image preview */\n    .orbit-msg-image {\n      max-width: 100%;\n      border-radius: 8px;\n      margin-top: 4px;\n      cursor: pointer;\n    }\n\n    /* ── Typing indicator ── */\n    .orbit-typing {\n      display: none;\n      align-self: flex-start;\n      padding: 10px 16px;\n      background: ${bgSecondary};\n      border-radius: 16px;\n      border-bottom-left-radius: 4px;\n      gap: 4px;\n      align-items: center;\n    }\n    .orbit-typing.visible {\n      display: flex;\n    }\n    .orbit-typing-dot {\n      width: 7px;\n      height: 7px;\n      border-radius: 50%;\n      background: ${textSecondary};\n      animation: orbit-typing 1.4s infinite ease-in-out;\n    }\n    .orbit-typing-dot:nth-child(2) { animation-delay: 0.2s; }\n    .orbit-typing-dot:nth-child(3) { animation-delay: 0.4s; }\n    @keyframes orbit-typing {\n      0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }\n      30% { transform: translateY(-4px); opacity: 1; }\n    }\n\n    /* ── Input bar ── */\n    .orbit-input-bar {\n      padding: 12px 16px;\n      border-top: 1px solid ${border};\n      display: flex;\n      gap: 8px;\n      align-items: flex-end;\n      flex-shrink: 0;\n      background: ${bg};\n    }\n\n    .orbit-attach-btn {\n      background: none;\n      border: none;\n      cursor: pointer;\n      padding: 6px;\n      color: ${textSecondary};\n      border-radius: 6px;\n      transition: color 0.15s, background 0.15s;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      flex-shrink: 0;\n    }\n    .orbit-attach-btn:hover {\n      color: ${primaryColor};\n      background: ${bgSecondary};\n    }\n    .orbit-attach-btn svg {\n      width: 20px;\n      height: 20px;\n      fill: currentColor;\n    }\n\n    .orbit-input {\n      flex: 1;\n      border: 1px solid ${inputBorder};\n      border-radius: 20px;\n      padding: 8px 16px;\n      font-size: 14px;\n      font-family: inherit;\n      background: ${inputBg};\n      color: ${text};\n      outline: none;\n      resize: none;\n      max-height: 100px;\n      min-height: 36px;\n      line-height: 1.4;\n      transition: border-color 0.15s;\n    }\n    .orbit-input:focus {\n      border-color: ${primaryColor};\n    }\n    .orbit-input::placeholder {\n      color: ${textSecondary};\n    }\n\n    .orbit-send-btn {\n      background: ${primaryColor};\n      color: #ffffff;\n      border: none;\n      border-radius: 50%;\n      width: 36px;\n      height: 36px;\n      cursor: pointer;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n      flex-shrink: 0;\n      transition: opacity 0.15s, transform 0.15s;\n    }\n    .orbit-send-btn:hover {\n      opacity: 0.9;\n      transform: scale(1.05);\n    }\n    .orbit-send-btn:disabled {\n      opacity: 0.5;\n      cursor: not-allowed;\n      transform: none;\n    }\n    .orbit-send-btn svg {\n      width: 18px;\n      height: 18px;\n      fill: #ffffff;\n    }\n\n    /* File attachment preview bar */\n    .orbit-file-preview {\n      display: none;\n      padding: 8px 16px;\n      border-top: 1px solid ${border};\n      background: ${bgSecondary};\n      align-items: center;\n      gap: 8px;\n      font-size: 13px;\n      color: ${textSecondary};\n    }\n    .orbit-file-preview.visible {\n      display: flex;\n    }\n    .orbit-file-preview-name {\n      flex: 1;\n      overflow: hidden;\n      text-overflow: ellipsis;\n      white-space: nowrap;\n    }\n    .orbit-file-preview-remove {\n      background: none;\n      border: none;\n      color: ${textSecondary};\n      cursor: pointer;\n      font-size: 16px;\n      padding: 2px 6px;\n      border-radius: 4px;\n    }\n    .orbit-file-preview-remove:hover {\n      color: #ef4444;\n    }\n\n    /* ── Footer / branding ── */\n    .orbit-footer {\n      padding: 8px;\n      text-align: center;\n      font-size: 11px;\n      color: ${textSecondary};\n      flex-shrink: 0;\n      border-top: 1px solid ${border};\n    }\n    .orbit-footer a {\n      color: ${textSecondary};\n      text-decoration: none;\n      font-weight: 500;\n    }\n    .orbit-footer a:hover {\n      color: ${primaryColor};\n    }\n\n    /* ── Connection status ── */\n    .orbit-connection {\n      display: none;\n      padding: 6px 16px;\n      text-align: center;\n      font-size: 12px;\n      background: #fef3c7;\n      color: #92400e;\n      flex-shrink: 0;\n    }\n    .orbit-connection.disconnected {\n      display: block;\n    }\n\n    /* ── Responsive ── */\n    @media (max-width: 440px) {\n      .orbit-panel {\n        width: calc(100vw - 16px);\n        height: calc(100vh - 80px);\n        max-height: none;\n        bottom: 72px;\n        left: 8px;\n        right: 8px;\n        border-radius: 12px;\n      }\n      .orbit-toggle {\n        bottom: 12px;\n        ${isLeft ? \"left: 12px;\" : \"right: 12px;\"}\n      }\n    }\n  `;\n}\n","/**\n * Persistent storage for the Orbit Chat widget.\n * Uses localStorage with a namespaced key prefix.\n * Falls back to in-memory storage when localStorage is unavailable.\n */\n\nconst PREFIX = 'orbit_chat_'\n\n/** In-memory fallback when localStorage is blocked (e.g., incognito, iframes). */\nconst memoryStore = new Map<string, string>()\n\nfunction isLocalStorageAvailable(): boolean {\n  try {\n    const testKey = `${PREFIX}test`\n    localStorage.setItem(testKey, '1')\n    localStorage.removeItem(testKey)\n    return true\n  } catch (_err) {\n    return false\n  }\n}\n\nconst useLocal = typeof window !== 'undefined' && isLocalStorageAvailable()\n\nexport function storageGet(key: string): string | null {\n  const fullKey = `${PREFIX}${key}`\n  if (useLocal) {\n    return localStorage.getItem(fullKey)\n  }\n  return memoryStore.get(fullKey) ?? null\n}\n\nexport function storageSet(key: string, value: string): void {\n  const fullKey = `${PREFIX}${key}`\n  if (useLocal) {\n    localStorage.setItem(fullKey, value)\n  } else {\n    memoryStore.set(fullKey, value)\n  }\n}\n\nexport function storageRemove(key: string): void {\n  const fullKey = `${PREFIX}${key}`\n  if (useLocal) {\n    localStorage.removeItem(fullKey)\n  } else {\n    memoryStore.delete(fullKey)\n  }\n}\n","/** SVG icon strings used in the Orbit Chat widget. */\n\nexport const ICON_CHAT = `<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H5.17L4 17.17V4h16v12z\"/><path d=\"M7 9h10v2H7zm0-3h10v2H7z\"/></svg>`\n\nexport const ICON_CLOSE = `<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/></svg>`\n\nexport const ICON_SEND = `<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.01 21L23 12 2.01 3 2 10l15 2-15 2z\"/></svg>`\n\nexport const ICON_ATTACH = `<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z\"/></svg>`\n\nexport const ICON_FILE = `<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z\"/></svg>`\n\nexport const ICON_AGENT = `<svg viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z\"/></svg>`\n","import type { OrbitChatConfig, ChatMessage } from \"./types\";\nimport { ApiClient } from \"./api-client\";\nimport { EventStream } from \"./event-stream\";\nimport { generateStyles, sanitizePrimaryColor } from \"./styles\";\nimport { storageGet, storageSet } from \"./storage\";\nimport {\n  ICON_CHAT,\n  ICON_CLOSE,\n  ICON_SEND,\n  ICON_ATTACH,\n  ICON_FILE,\n  ICON_AGENT,\n} from \"./icons\";\n\nconst DEFAULT_BASE_URL = \"https://api.orbit.devotel.io/api/v1\";\nconst DEFAULT_PRIMARY_COLOR = \"#6366f1\";\nconst DEFAULT_GREETING = \"Hi there! How can we help you today?\";\nconst DEFAULT_AGENT_NAME = \"Orbit Support\";\n\nconst MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB\nconst ACCEPTED_FILE_TYPES =\n  \".jpg,.jpeg,.png,.gif,.webp,.pdf,.doc,.docx,.xls,.xlsx,.csv,.txt,.zip\";\nconst IMAGE_EXTENSIONS = new Set([\"jpg\", \"jpeg\", \"png\", \"gif\", \"webp\"]);\n\n/**\n * Resolves the public-key from a config object, accepting both the new\n * `publicKey` field (canonical) and the deprecated `apiKey` alias. When\n * only `apiKey` is supplied, the SDK emits a one-shot `console.warn` so\n * customers can migrate without an immediate runtime break.\n *\n * Deduplicated via a process-level Set so React strict-mode double-mount\n * or repeated `OrbitChat.init` calls don't spam the console.\n */\nconst APIKEY_DEPRECATION_SEEN = new Set<string>();\nfunction resolvePublicKey(config: {\n  publicKey?: string;\n  apiKey?: string;\n}): string | undefined {\n  if (config.publicKey && config.publicKey.length > 0) {\n    return config.publicKey;\n  }\n  if (config.apiKey && config.apiKey.length > 0) {\n    // Dedup by component key + raw value prefix so two distinct migrations\n    // (different keys, different code paths) still each warn once.\n    const dedupKey = `OrbitChat:${config.apiKey.slice(0, 16)}`;\n    if (\n      !APIKEY_DEPRECATION_SEEN.has(dedupKey) &&\n      typeof console !== \"undefined\" &&\n      console.warn\n    ) {\n      APIKEY_DEPRECATION_SEEN.add(dedupKey);\n      console.warn(\n        \"OrbitChat: `apiKey` is deprecated — rename to `publicKey`. \" +\n          \"The `apiKey` alias will be removed in the next major version. \" +\n          \"See https://docs.orbit.devotel.io/sdks/web#authentication.\",\n      );\n    }\n    return config.apiKey;\n  }\n  return undefined;\n}\n\n/**\n * Embeddable Orbit Chat widget.\n *\n * Renders a floating chat button and panel inside a Shadow DOM.\n * Communicates with the Orbit conversations API and receives\n * real-time updates via SSE.\n *\n * Usage:\n * ```html\n * <script src=\"https://cdn.devotel.io/sdk/orbit-chat.js\"></script>\n * <script>\n *   OrbitChat.init({\n *     publicKey: 'dv_live_pk_xxxx',\n *     position: 'bottom-right',\n *     theme: 'light',\n *     greeting: 'Welcome! How can we help?',\n *   })\n * </script>\n * ```\n */\nexport class OrbitChat {\n  /* ── Configuration ── */\n  private apiKey: string;\n  private baseUrl: string;\n  private position: \"bottom-right\" | \"bottom-left\";\n  private theme: \"light\" | \"dark\";\n  private greeting: string;\n  private primaryColor: string;\n  private agentAvatar: string | undefined;\n  private agentName: string;\n  private visitorId: string | undefined;\n  private metadata: Record<string, string> | undefined;\n\n  /* ── State ── */\n  private host: HTMLElement | null = null;\n  private shadow: ShadowRoot | null = null;\n  private isOpen = false;\n  private isSending = false;\n  private unreadCount = 0;\n  private messages: ChatMessage[] = [];\n  private pendingFile: File | null = null;\n  private conversationReady = false;\n\n  /* ── Services ── */\n  private api: ApiClient;\n  private eventStream: EventStream;\n\n  /* ── DOM references (inside shadow) ── */\n  private toggleBtn: HTMLButtonElement | null = null;\n  private badgeEl: HTMLElement | null = null;\n  private panelEl: HTMLElement | null = null;\n  private messagesEl: HTMLElement | null = null;\n  private inputEl: HTMLTextAreaElement | null = null;\n  private sendBtn: HTMLButtonElement | null = null;\n  private typingEl: HTMLElement | null = null;\n  private filePreviewEl: HTMLElement | null = null;\n  private filePreviewName: HTMLElement | null = null;\n  private fileInput: HTMLInputElement | null = null;\n  private connectionEl: HTMLElement | null = null;\n\n  /**\n   * Factory method -- creates, mounts, and returns a widget instance.\n   */\n  static init(config: OrbitChatConfig): OrbitChat {\n    const chat = new OrbitChat(config);\n    chat.mount();\n    return chat;\n  }\n\n  constructor(config: OrbitChatConfig) {\n    // Accept both the canonical `publicKey` field and the deprecated\n    // `apiKey` alias (audit #SDK-010). `publicKey` wins when both are\n    // set; passing only `apiKey` emits a one-shot console.warn pointing\n    // at the migration docs.\n    const resolvedKey = resolvePublicKey(config);\n    if (!resolvedKey) {\n      throw new Error(\"OrbitChat: publicKey is required\");\n    }\n\n    // Audit #SDK-004 — refuse server-side secret keys (`dv_live_sk_…` /\n    // `dv_test_sk_…`) in the browser widget. A developer who copy-pastes\n    // their dashboard secret key into `OrbitChat.init({ publicKey })` would\n    // otherwise ship it to every visitor's browser AND every proxy log\n    // between visitor and Orbit. The widget MUST receive the PUBLISHABLE\n    // key (prefix `dv_live_pk_…` or `dv_test_pk_…`).\n    if (\n      resolvedKey.startsWith(\"dv_live_sk_\") ||\n      resolvedKey.startsWith(\"dv_test_sk_\")\n    ) {\n      const msg =\n        \"OrbitChat: server-side secret keys (dv_live_sk_… / dv_test_sk_…) cannot be used in the browser. \" +\n        \"Use the public publishable key (dv_live_pk_… / dv_test_pk_…) — see https://docs.orbit.devotel.io/widget/api-keys.\";\n      if (typeof console !== \"undefined\" && console.error) {\n        // eslint-disable-next-line no-console\n        console.error(msg);\n      }\n      throw new Error(msg);\n    }\n\n    this.apiKey = resolvedKey;\n    this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n    this.position = config.position ?? \"bottom-right\";\n    this.theme = config.theme ?? \"light\";\n    this.greeting = config.greeting ?? DEFAULT_GREETING;\n    // Audit #SDK-006 — sanitize primaryColor at the constructor boundary.\n    // The value lands in Shadow DOM CSS via `background: ${primaryColor}`\n    // at multiple sites; an unvalidated payload like\n    // `red; }body{background:url(https://attacker.tld)` would parse\n    // through and trigger a covert outbound beacon on every paint. The\n    // helper rejects anything that isn't a #RGB/#RGBA/#RRGGBB/#RRGGBBAA\n    // hex string and falls back to the default with a console.warn.\n    this.primaryColor = sanitizePrimaryColor(\n      config.primaryColor ?? DEFAULT_PRIMARY_COLOR,\n    );\n    this.agentAvatar = config.agentAvatar;\n    this.agentName = config.agentName ?? DEFAULT_AGENT_NAME;\n    this.visitorId = config.visitorId;\n    this.metadata = config.metadata;\n\n    this.api = new ApiClient(this.baseUrl, this.apiKey);\n    // Audit #SDK-004 — pass the ApiClient (not the long-lived URL +\n    // raw apiKey) so EventStream can swap the long-lived widget key\n    // for a short-lived stream token before opening SSE.\n    this.eventStream = new EventStream(this.baseUrl, this.baseUrl, this.api);\n  }\n\n  /** Returns the configured API base URL. */\n  getBaseUrl(): string {\n    return this.baseUrl;\n  }\n\n  /** Returns the public API key. */\n  getApiKey(): string {\n    return this.apiKey;\n  }\n\n  /**\n   * Mounts the widget into the DOM using Shadow DOM for style isolation.\n   * Safe to call multiple times -- existing widget is removed first.\n   */\n  mount(): void {\n    this.destroy();\n\n    // Create host element\n    this.host = document.createElement(\"div\");\n    this.host.id = \"orbit-chat-widget\";\n    this.host.setAttribute(\"data-orbit-chat\", \"true\");\n\n    // Attach shadow DOM for style isolation\n    this.shadow = this.host.attachShadow({ mode: \"open\" });\n\n    // Inject styles\n    const styleEl = document.createElement(\"style\");\n    styleEl.textContent = generateStyles({\n      position: this.position,\n      theme: this.theme,\n      primaryColor: this.primaryColor,\n    });\n    this.shadow.appendChild(styleEl);\n\n    // Build the widget DOM\n    this.shadow.appendChild(this.buildToggle());\n    this.shadow.appendChild(this.buildPanel());\n\n    // Hidden file input for attachments\n    this.fileInput = document.createElement(\"input\");\n    this.fileInput.type = \"file\";\n    this.fileInput.accept = ACCEPTED_FILE_TYPES;\n    this.fileInput.style.display = \"none\";\n    this.fileInput.addEventListener(\"change\", () => this.handleFileSelected());\n    this.shadow.appendChild(this.fileInput);\n\n    // Add to page\n    document.body.appendChild(this.host);\n\n    // Cache DOM references\n    this.cacheRefs();\n\n    // Bind events\n    this.bindEvents();\n\n    // Restore conversation from storage\n    this.restoreConversation();\n\n    // Connect real-time event stream\n    this.setupEventStream();\n  }\n\n  /** Removes the widget from the DOM and cleans up. */\n  destroy(): void {\n    this.eventStream.disconnect();\n    if (this.host) {\n      this.host.remove();\n      this.host = null;\n      this.shadow = null;\n    }\n    this.isOpen = false;\n    this.pendingFile = null;\n    this.messages = [];\n    this.conversationReady = false;\n  }\n\n  /** Programmatically open the chat panel. */\n  open(): void {\n    if (!this.isOpen) {\n      this.togglePanel();\n    }\n  }\n\n  /** Programmatically close the chat panel. */\n  close(): void {\n    if (this.isOpen) {\n      this.togglePanel();\n    }\n  }\n\n  /** Send a message programmatically. */\n  async sendMessage(text: string): Promise<void> {\n    await this.ensureConversation();\n    await this.doSendMessage(text);\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     DOM Construction\n     ════════════════════════════════════════════════════════════════ */\n\n  private buildToggle(): HTMLButtonElement {\n    const btn = document.createElement(\"button\");\n    btn.className = \"orbit-toggle\";\n    btn.setAttribute(\"aria-label\", \"Open chat\");\n    btn.innerHTML = `${ICON_CHAT}<span class=\"orbit-badge\"></span>`;\n    return btn;\n  }\n\n  private buildPanel(): HTMLElement {\n    const panel = document.createElement(\"div\");\n    panel.className = \"orbit-panel\";\n    panel.setAttribute(\"role\", \"dialog\");\n    panel.setAttribute(\"aria-label\", \"Chat with support\");\n\n    const avatarContent = this.agentAvatar\n      ? `<img src=\"${this.escapeAttr(this.agentAvatar)}\" alt=\"${this.escapeAttr(this.agentName)}\" />`\n      : ICON_AGENT;\n\n    panel.innerHTML = `\n      <div class=\"orbit-header\">\n        <div class=\"orbit-header-avatar\">${avatarContent}</div>\n        <div class=\"orbit-header-info\">\n          <div class=\"orbit-header-title\">${this.escapeHtml(this.agentName)}</div>\n          <div class=\"orbit-header-status\">\n            <span class=\"orbit-status-dot\"></span>\n            Online\n          </div>\n        </div>\n        <button class=\"orbit-close\" aria-label=\"Close chat\">${ICON_CLOSE}</button>\n      </div>\n      <div class=\"orbit-connection\">Reconnecting...</div>\n      <div class=\"orbit-messages\">\n        <div class=\"orbit-greeting\">${this.escapeHtml(this.greeting)}</div>\n      </div>\n      <div class=\"orbit-typing\">\n        <span class=\"orbit-typing-dot\"></span>\n        <span class=\"orbit-typing-dot\"></span>\n        <span class=\"orbit-typing-dot\"></span>\n      </div>\n      <div class=\"orbit-file-preview\">\n        <span class=\"orbit-file-preview-name\"></span>\n        <button class=\"orbit-file-preview-remove\" aria-label=\"Remove file\">${ICON_CLOSE}</button>\n      </div>\n      <div class=\"orbit-input-bar\">\n        <button class=\"orbit-attach-btn\" aria-label=\"Attach file\">${ICON_ATTACH}</button>\n        <textarea class=\"orbit-input\" placeholder=\"Type a message...\" rows=\"1\"></textarea>\n        <button class=\"orbit-send-btn\" aria-label=\"Send message\">${ICON_SEND}</button>\n      </div>\n      <div class=\"orbit-footer\">\n        <a href=\"https://devotel.io\" target=\"_blank\" rel=\"noopener noreferrer\">Powered by Orbit</a>\n      </div>\n    `;\n\n    return panel;\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Event Binding\n     ════════════════════════════════════════════════════════════════ */\n\n  private cacheRefs(): void {\n    if (!this.shadow) return;\n    this.toggleBtn = this.shadow.querySelector(\".orbit-toggle\");\n    this.badgeEl = this.shadow.querySelector(\".orbit-badge\");\n    this.panelEl = this.shadow.querySelector(\".orbit-panel\");\n    this.messagesEl = this.shadow.querySelector(\".orbit-messages\");\n    this.inputEl = this.shadow.querySelector(\".orbit-input\");\n    this.sendBtn = this.shadow.querySelector(\".orbit-send-btn\");\n    this.typingEl = this.shadow.querySelector(\".orbit-typing\");\n    this.filePreviewEl = this.shadow.querySelector(\".orbit-file-preview\");\n    this.filePreviewName = this.shadow.querySelector(\n      \".orbit-file-preview-name\",\n    );\n    this.connectionEl = this.shadow.querySelector(\".orbit-connection\");\n  }\n\n  private bindEvents(): void {\n    this.toggleBtn?.addEventListener(\"click\", () => this.togglePanel());\n\n    const closeBtn = this.shadow?.querySelector(\".orbit-close\");\n    closeBtn?.addEventListener(\"click\", () => this.togglePanel());\n\n    this.sendBtn?.addEventListener(\"click\", () => {\n      void this.handleSend();\n    });\n\n    this.inputEl?.addEventListener(\"keydown\", (e: KeyboardEvent) => {\n      if (e.key === \"Enter\" && !e.shiftKey) {\n        e.preventDefault();\n        void this.handleSend();\n      }\n    });\n\n    // Auto-resize textarea\n    this.inputEl?.addEventListener(\"input\", () => this.autoResizeInput());\n\n    // Attach button\n    const attachBtn = this.shadow?.querySelector(\".orbit-attach-btn\");\n    attachBtn?.addEventListener(\"click\", () => this.fileInput?.click());\n\n    // Remove file preview\n    const removeFileBtn = this.shadow?.querySelector(\n      \".orbit-file-preview-remove\",\n    );\n    removeFileBtn?.addEventListener(\"click\", () => this.clearPendingFile());\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Panel Toggle & State\n     ════════════════════════════════════════════════════════════════ */\n\n  private togglePanel(): void {\n    if (!this.panelEl || !this.toggleBtn) return;\n\n    this.isOpen = !this.isOpen;\n\n    if (this.isOpen) {\n      this.panelEl.classList.add(\"open\");\n      this.toggleBtn.classList.add(\"open\");\n      this.toggleBtn.innerHTML = `${ICON_CLOSE}<span class=\"orbit-badge\"></span>`;\n      this.badgeEl = this.shadow?.querySelector(\".orbit-badge\") ?? null;\n\n      // Animate in\n      requestAnimationFrame(() => {\n        requestAnimationFrame(() => {\n          this.panelEl?.classList.add(\"visible\");\n        });\n      });\n\n      // Focus input\n      setTimeout(() => this.inputEl?.focus(), 100);\n\n      // Clear unread\n      this.unreadCount = 0;\n      this.updateBadge();\n\n      // Scroll to bottom\n      this.scrollToBottom();\n    } else {\n      this.panelEl.classList.remove(\"visible\");\n      this.toggleBtn.classList.remove(\"open\");\n      this.toggleBtn.innerHTML = `${ICON_CHAT}<span class=\"orbit-badge\"></span>`;\n      this.badgeEl = this.shadow?.querySelector(\".orbit-badge\") ?? null;\n\n      // Wait for animation then hide\n      setTimeout(() => {\n        this.panelEl?.classList.remove(\"open\");\n      }, 200);\n    }\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Conversation Management\n     ════════════════════════════════════════════════════════════════ */\n\n  private restoreConversation(): void {\n    const storedId = storageGet(`conversation_${this.apiKey}`);\n    if (storedId) {\n      this.api.setConversationId(storedId);\n      this.conversationReady = true;\n      // Load existing messages\n      void this.loadMessages();\n    }\n  }\n\n  private async ensureConversation(): Promise<void> {\n    if (this.conversationReady && this.api.getConversationId()) return;\n\n    try {\n      const conversation = await this.api.createConversation(\n        this.visitorId,\n        this.metadata,\n      );\n      storageSet(`conversation_${this.apiKey}`, conversation.id);\n      this.conversationReady = true;\n    } catch (err) {\n      // If creation fails (e.g. conversation already exists), try listing\n      try {\n        const conversations = await this.api.listConversations(\"open\");\n        if (conversations.length > 0) {\n          const conv = conversations[0]!;\n          this.api.setConversationId(conv.id);\n          storageSet(`conversation_${this.apiKey}`, conv.id);\n          this.conversationReady = true;\n          return;\n        }\n      } catch (_listErr) {\n        // Both attempts failed\n      }\n      console.error(\"[OrbitChat] Failed to create conversation:\", err);\n    }\n  }\n\n  private async loadMessages(): Promise<void> {\n    try {\n      const messages = await this.api.getMessages(50);\n      this.messages = messages;\n\n      // Clear greeting if messages exist\n      if (messages.length > 0 && this.messagesEl) {\n        const greeting = this.messagesEl.querySelector(\".orbit-greeting\");\n        if (greeting) greeting.remove();\n      }\n\n      // Render all messages\n      for (const msg of messages) {\n        this.renderMessage(msg, false);\n      }\n      this.scrollToBottom();\n    } catch (err) {\n      console.error(\"[OrbitChat] Failed to load messages:\", err);\n    }\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Message Sending\n     ════════════════════════════════════════════════════════════════ */\n\n  private async handleSend(): Promise<void> {\n    if (this.isSending) return;\n\n    const text = this.inputEl?.value.trim() ?? \"\";\n    const file = this.pendingFile;\n\n    if (!text && !file) return;\n\n    this.isSending = true;\n    if (this.sendBtn) this.sendBtn.disabled = true;\n\n    try {\n      await this.ensureConversation();\n\n      if (file) {\n        await this.doSendFile(file);\n        this.clearPendingFile();\n      }\n\n      if (text) {\n        await this.doSendMessage(text);\n      }\n    } catch (err) {\n      console.error(\"[OrbitChat] Failed to send message:\", err);\n      // Show the error as a system message\n      this.renderSystemMessage(\"Message failed to send. Please try again.\");\n    } finally {\n      this.isSending = false;\n      if (this.sendBtn) this.sendBtn.disabled = false;\n    }\n  }\n\n  private async doSendMessage(text: string): Promise<void> {\n    // Optimistically render the message\n    const optimisticMsg: ChatMessage = {\n      id: `temp_${Date.now()}`,\n      body: text,\n      direction: \"outbound\",\n      created_at: new Date().toISOString(),\n    };\n    this.messages.push(optimisticMsg);\n    this.renderMessage(optimisticMsg, true);\n\n    // Clear input\n    if (this.inputEl) {\n      this.inputEl.value = \"\";\n      this.autoResizeInput();\n    }\n\n    // Remove greeting\n    const greeting = this.messagesEl?.querySelector(\".orbit-greeting\");\n    if (greeting) greeting.remove();\n\n    try {\n      await this.api.sendMessage(text);\n    } catch (err) {\n      // Remove optimistic message on failure\n      const msgEl = this.messagesEl?.querySelector(\n        `[data-msg-id=\"${optimisticMsg.id}\"]`,\n      );\n      msgEl?.remove();\n      this.messages = this.messages.filter((m) => m.id !== optimisticMsg.id);\n      throw err;\n    }\n  }\n\n  private async doSendFile(file: File): Promise<void> {\n    // Optimistically render the file message\n    const optimisticMsg: ChatMessage = {\n      id: `temp_file_${Date.now()}`,\n      body: file.name,\n      direction: \"outbound\",\n      created_at: new Date().toISOString(),\n    };\n    this.messages.push(optimisticMsg);\n    this.renderMessage(optimisticMsg, true);\n\n    try {\n      await this.api.sendAttachment(file);\n    } catch (err) {\n      const msgEl = this.messagesEl?.querySelector(\n        `[data-msg-id=\"${optimisticMsg.id}\"]`,\n      );\n      msgEl?.remove();\n      this.messages = this.messages.filter((m) => m.id !== optimisticMsg.id);\n      throw err;\n    }\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     File Attachments\n     ════════════════════════════════════════════════════════════════ */\n\n  private handleFileSelected(): void {\n    const file = this.fileInput?.files?.[0];\n    if (!file) return;\n\n    if (file.size > MAX_FILE_SIZE) {\n      this.renderSystemMessage(\"File is too large. Maximum size is 10 MB.\");\n      return;\n    }\n\n    this.pendingFile = file;\n    if (this.filePreviewName) {\n      this.filePreviewName.textContent = file.name;\n    }\n    this.filePreviewEl?.classList.add(\"visible\");\n\n    // Reset the input so the same file can be re-selected\n    if (this.fileInput) this.fileInput.value = \"\";\n  }\n\n  private clearPendingFile(): void {\n    this.pendingFile = null;\n    this.filePreviewEl?.classList.remove(\"visible\");\n    if (this.filePreviewName) this.filePreviewName.textContent = \"\";\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Message Rendering\n     ════════════════════════════════════════════════════════════════ */\n\n  private renderMessage(msg: ChatMessage, animate: boolean): void {\n    if (!this.messagesEl) return;\n\n    // Avoid duplicates\n    if (this.messagesEl.querySelector(`[data-msg-id=\"${msg.id}\"]`)) return;\n\n    const bubble = document.createElement(\"div\");\n    bubble.className = `orbit-msg ${msg.direction === \"outbound\" ? \"outbound\" : \"inbound\"}`;\n    bubble.setAttribute(\"data-msg-id\", msg.id);\n\n    if (!animate) {\n      bubble.style.animation = \"none\";\n    }\n\n    // Message body\n    const bodyEl = document.createElement(\"div\");\n    bodyEl.textContent = msg.body;\n    bubble.appendChild(bodyEl);\n\n    // Attachment rendering\n    if (msg.media_url) {\n      const ext = msg.media_url.split(\".\").pop()?.toLowerCase() ?? \"\";\n      if (IMAGE_EXTENSIONS.has(ext)) {\n        const img = document.createElement(\"img\");\n        img.className = \"orbit-msg-image\";\n        img.src = msg.media_url;\n        img.alt = msg.body || \"Attachment\";\n        img.loading = \"lazy\";\n        img.addEventListener(\"click\", () => {\n          // `noopener,noreferrer` prevents the popup from getting a\n          // `window.opener` reference back into the host page (reverse\n          // tabnabbing) and strips the Referer header.\n          if (msg.media_url)\n            window.open(msg.media_url, \"_blank\", \"noopener,noreferrer\");\n        });\n        bubble.appendChild(img);\n      } else {\n        const link = document.createElement(\"a\");\n        link.className = \"orbit-msg-attachment\";\n        link.href = msg.media_url;\n        link.target = \"_blank\";\n        link.rel = \"noopener noreferrer\";\n        link.innerHTML = `${ICON_FILE}<span>${this.escapeHtml(msg.body || \"Download\")}</span>`;\n        bubble.appendChild(link);\n      }\n    }\n\n    // Timestamp\n    const timeEl = document.createElement(\"div\");\n    timeEl.className = \"orbit-msg-time\";\n    timeEl.textContent = this.formatTime(msg.created_at);\n    bubble.appendChild(timeEl);\n\n    this.messagesEl.appendChild(bubble);\n    this.scrollToBottom();\n  }\n\n  private renderSystemMessage(text: string): void {\n    if (!this.messagesEl) return;\n\n    const el = document.createElement(\"div\");\n    el.className = \"orbit-greeting\";\n    el.textContent = text;\n    el.style.fontSize = \"13px\";\n    el.style.padding = \"8px\";\n    this.messagesEl.appendChild(el);\n    this.scrollToBottom();\n\n    // Auto-remove after 5 seconds\n    setTimeout(() => el.remove(), 5000);\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Typing Indicators\n     ════════════════════════════════════════════════════════════════ */\n\n  private showTyping(): void {\n    this.typingEl?.classList.add(\"visible\");\n    this.scrollToBottom();\n  }\n\n  private hideTyping(): void {\n    this.typingEl?.classList.remove(\"visible\");\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Real-time Event Stream\n     ════════════════════════════════════════════════════════════════ */\n\n  private setupEventStream(): void {\n    this.eventStream.on(\"connected\", () => {\n      this.connectionEl?.classList.remove(\"disconnected\");\n    });\n\n    this.eventStream.on(\"error\", () => {\n      this.connectionEl?.classList.add(\"disconnected\");\n    });\n\n    // New message received (inbound from agent)\n    this.eventStream.on(\"message.created\", (data) => {\n      const convId = this.api.getConversationId();\n      const nested = data[\"data\"] as Record<string, unknown> | undefined;\n      const eventConvId = (data[\"conversation_id\"] ??\n        nested?.[\"conversation_id\"]) as string | undefined;\n\n      // Only process messages for our conversation\n      if (!convId || (eventConvId && eventConvId !== convId)) return;\n\n      const direction = (data[\"direction\"] ?? nested?.[\"direction\"]) as\n        | string\n        | undefined;\n      if (direction !== \"inbound\") return;\n\n      const msg: ChatMessage = {\n        id: (data[\"id\"] ?? nested?.[\"id\"] ?? `sse_${Date.now()}`) as string,\n        body: (data[\"body\"] ?? nested?.[\"body\"] ?? \"\") as string,\n        direction: \"inbound\",\n        created_at: (data[\"created_at\"] ??\n          nested?.[\"created_at\"] ??\n          new Date().toISOString()) as string,\n        media_url: (data[\"media_url\"] ?? nested?.[\"media_url\"]) as\n          | string\n          | undefined,\n      };\n\n      // Avoid rendering duplicates\n      const isDuplicate = this.messages.some((m) => m.id === msg.id);\n      if (isDuplicate) return;\n\n      this.messages.push(msg);\n      this.hideTyping();\n      this.renderMessage(msg, true);\n\n      // Increment unread if panel is closed\n      if (!this.isOpen) {\n        this.unreadCount++;\n        this.updateBadge();\n      }\n    });\n\n    // Typing indicators\n    this.eventStream.on(\"typing.start\", (data) => {\n      const convId = this.api.getConversationId();\n      const nested = data[\"data\"] as Record<string, unknown> | undefined;\n      const eventConvId = (data[\"conversation_id\"] ??\n        nested?.[\"conversation_id\"]) as string | undefined;\n      if (convId && eventConvId === convId) {\n        this.showTyping();\n      }\n    });\n\n    this.eventStream.on(\"typing.stop\", (data) => {\n      const convId = this.api.getConversationId();\n      const nested = data[\"data\"] as Record<string, unknown> | undefined;\n      const eventConvId = (data[\"conversation_id\"] ??\n        nested?.[\"conversation_id\"]) as string | undefined;\n      if (convId && eventConvId === convId) {\n        this.hideTyping();\n      }\n    });\n\n    // Connect\n    this.eventStream.connect();\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Unread Badge\n     ════════════════════════════════════════════════════════════════ */\n\n  private updateBadge(): void {\n    if (!this.badgeEl) return;\n    if (this.unreadCount > 0) {\n      this.badgeEl.classList.add(\"visible\");\n      this.badgeEl.textContent =\n        this.unreadCount > 9 ? \"9+\" : String(this.unreadCount);\n    } else {\n      this.badgeEl.classList.remove(\"visible\");\n      this.badgeEl.textContent = \"\";\n    }\n  }\n\n  /* ════════════════════════════════════════════════════════════════\n     Helpers\n     ════════════════════════════════════════════════════════════════ */\n\n  private scrollToBottom(): void {\n    if (this.messagesEl) {\n      requestAnimationFrame(() => {\n        if (this.messagesEl) {\n          this.messagesEl.scrollTop = this.messagesEl.scrollHeight;\n        }\n      });\n    }\n  }\n\n  private autoResizeInput(): void {\n    if (!this.inputEl) return;\n    this.inputEl.style.height = \"auto\";\n    const maxHeight = 100;\n    this.inputEl.style.height = `${Math.min(this.inputEl.scrollHeight, maxHeight)}px`;\n  }\n\n  private formatTime(isoString: string): string {\n    try {\n      const date = new Date(isoString);\n      return date.toLocaleTimeString(undefined, {\n        hour: \"numeric\",\n        minute: \"2-digit\",\n        hour12: true,\n      });\n    } catch (_err) {\n      return \"\";\n    }\n  }\n\n  private escapeHtml(str: string): string {\n    const div = document.createElement(\"div\");\n    div.textContent = str;\n    return div.innerHTML;\n  }\n\n  private escapeAttr(str: string): string {\n    return str\n      .replace(/&/g, \"&amp;\")\n      .replace(/\"/g, \"&quot;\")\n      .replace(/'/g, \"&#39;\")\n      .replace(/</g, \"&lt;\")\n      .replace(/>/g, \"&gt;\");\n  }\n}\n","/**\n * Orbit browser softphone — thin wrapper around LiveKit's Web SDK that\n * drives the dashboard / customer browser softphone UX.\n *\n * Lifecycle:\n *   1. Ask the API for a LiveKit token (POST /voice/softphone/token) —\n *      the token is scoped to a deterministic per-user room.\n *   2. Connect to LiveKit with the token + publish the local mic.\n *   3. To place an outbound call: POST /voice/softphone/dial; the server\n *      bridges a Telnyx leg into the same room.\n *   4. The remote SIP participant's audio auto-subscribes; the caller\n *      hears them via the WebRTC audio element the SDK attaches.\n *\n * The SDK has no opinion on UI — consumers wire onCallStateChange into\n * their own React / Vue / vanilla component tree.\n */\n\n// LiveKit Web SDK types are dynamically imported so the package has no\n// hard install-time dep when the caller only uses other sdk-web exports\n// (e.g. OrbitChat). Consumers that call OrbitSoftphone methods must have\n// `livekit-client` installed themselves.\ntype LKRoom = {\n  connect: (url: string, token: string) => Promise<void>;\n  disconnect: () => Promise<void>;\n  localParticipant: {\n    setMicrophoneEnabled: (enabled: boolean) => Promise<unknown>;\n    isMicrophoneEnabled: boolean;\n  };\n  on: (event: string, handler: (...args: unknown[]) => void) => void;\n};\n\nexport type SoftphoneCallState =\n  | \"idle\"\n  | \"connecting\"\n  | \"ringing\"\n  | \"in-call\"\n  | \"ended\";\n\nexport interface SoftphoneConfig {\n  /** Orbit API base URL (e.g. https://api.orbit.devotel.io). */\n  apiBaseUrl: string;\n  /** Tenant API key with voice scope. */\n  apiKey: string;\n  /**\n   * Optional Orbit Media WebSocket URL override (otherwise taken from the\n   * token response `media_url` field). Preferred name since the LiveKit→Orbit\n   * Media fork landed (task #796).\n   */\n  mediaUrl?: string;\n  /**\n   * @deprecated Use `mediaUrl` instead. Kept as a back-compat alias for one\n   * minor SDK version; both fields are read and `mediaUrl` takes precedence.\n   */\n  livekitUrl?: string;\n  /** Fires whenever the softphone call state transitions. */\n  onCallStateChange?: (state: SoftphoneCallState) => void;\n  /** Fires on unrecoverable errors (bad token, no mic permission, etc). */\n  onError?: (err: Error) => void;\n}\n\n/**\n * Minimal browser softphone. One instance == one softphone session for\n * the logged-in user.\n */\nexport class OrbitSoftphone {\n  private room: LKRoom | null = null;\n  private state: SoftphoneCallState = \"idle\";\n\n  constructor(private config: SoftphoneConfig) {}\n\n  /** Connect to the user's Orbit Media softphone room and publish the mic. */\n  async connect(): Promise<void> {\n    this.setState(\"connecting\");\n    try {\n      const tokenRes = await fetch(\n        `${this.config.apiBaseUrl}/api/v1/voice/softphone/token`,\n        {\n          method: \"POST\",\n          headers: {\n            \"Content-Type\": \"application/json\",\n            Authorization: `Bearer ${this.config.apiKey}`,\n          },\n          // Control-plane call before the LiveKit room is even minted —\n          // bound at 10 s so a hung API doesn't leave the user staring\n          // at a \"connecting\" spinner forever.\n          signal: AbortSignal.timeout(10_000),\n        },\n      );\n      if (!tokenRes.ok) {\n        throw new Error(`softphone token request failed (${tokenRes.status})`);\n      }\n      const body = (await tokenRes.json()) as {\n        data?: { token: string; media_url?: string | null; livekit_url?: string | null };\n      };\n      const token = body.data?.token;\n      // Prefer new `media_url` field; fall back to deprecated `livekit_url` for\n      // servers not yet upgraded. Config override: prefer `mediaUrl`, then\n      // deprecated `livekitUrl`.\n      const url =\n        this.config.mediaUrl ??\n        this.config.livekitUrl ??\n        body.data?.media_url ??\n        body.data?.livekit_url ??\n        \"\";\n      if (!token || !url) {\n        throw new Error(\n          \"softphone token response missing token or Orbit Media URL\",\n        );\n      }\n\n      // Dynamic import — no hard dep. Consumers opt in by installing\n      // `@orbit/media-client` themselves. The specifier is built from a string\n      // local so TypeScript's module resolver doesn't try to type-check the\n      // peer module at compile time (it isn't installed in this workspace).\n      const livekitModuleName = \"@orbit/media-client\";\n      const lk = (await import(/* @vite-ignore */ livekitModuleName)) as {\n        Room: new () => LKRoom;\n      };\n      this.room = new lk.Room();\n      this.room.on(\"disconnected\", () => {\n        this.setState(\"ended\");\n      });\n      await this.room.connect(url, token);\n      await this.room.localParticipant.setMicrophoneEnabled(true);\n      this.setState(\"idle\");\n    } catch (err) {\n      this.config.onError?.(err as Error);\n      this.setState(\"ended\");\n      throw err;\n    }\n  }\n\n  /** Place an outbound call through the softphone. Returns the call_id. */\n  async dial(destination: string, from?: string): Promise<string> {\n    this.setState(\"ringing\");\n    const res = await fetch(\n      `${this.config.apiBaseUrl}/api/v1/voice/softphone/dial`,\n      {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          Authorization: `Bearer ${this.config.apiKey}`,\n        },\n        body: JSON.stringify({ to: destination, from }),\n        // Same 10 s ceiling as the token call — both are control-plane\n        // operations that gate the in-call UX.\n        signal: AbortSignal.timeout(10_000),\n      },\n    );\n    if (!res.ok) {\n      this.setState(\"idle\");\n      this.config.onError?.(new Error(`softphone dial failed (${res.status})`));\n      throw new Error(`softphone dial failed (${res.status})`);\n    }\n    const body = (await res.json()) as {\n      data?: { id?: string };\n    };\n    this.setState(\"in-call\");\n    return body.data?.id ?? \"\";\n  }\n\n  /** Toggle local microphone mute. Returns the new muted state.\n   *\n   *  Audit #511 voice/sdk-web/softphone:141 — read the LK flag, await\n   *  the toggle, then re-read so the returned value reflects what\n   *  LiveKit actually applied (not what we *requested*). On slow\n   *  networks the SDK can reject the toggle and the previous code\n   *  returned the wrong-flipped value, leaving consumers with a UI\n   *  that displayed \"muted\" while the mic was still hot — privacy\n   *  bug. Throw on rejection so the consumer surfaces it.\n   */\n  async toggleMute(): Promise<boolean> {\n    if (!this.room) return false;\n    const wasEnabled = this.room.localParticipant.isMicrophoneEnabled;\n    try {\n      await this.room.localParticipant.setMicrophoneEnabled(!wasEnabled);\n    } catch (err) {\n      throw err instanceof Error ? err : new Error(String(err));\n    }\n    // muted = !enabled; re-read AFTER the await for the actual state.\n    return !this.room.localParticipant.isMicrophoneEnabled;\n  }\n\n  /** End the softphone session and drop from the LiveKit room. */\n  async disconnect(): Promise<void> {\n    if (this.room) {\n      await this.room.disconnect();\n      this.room = null;\n    }\n    this.setState(\"ended\");\n  }\n\n  private setState(next: SoftphoneCallState): void {\n    if (next === this.state) return;\n    this.state = next;\n    this.config.onCallStateChange?.(next);\n  }\n}\n","/**\n * OrbitVoiceAgent — embeddable browser WebRTC voice-agent widget\n * (PARITY-R1-010, parity-aiaas-04).\n *\n * The WEB front-door to Orbit's AI voice agent. Until this shipped, the\n * only path to the voice AI was an inbound PSTN DID — there was no web\n * widget, which Vapi (web widget), Retell (web call SDK), and Sierra all\n * ship as the primary demo / onboarding path. This class is the\n * drop-in a customer pastes into their site so a visitor can press\n * \"Talk to AI\" and hold a live WebRTC voice conversation with their\n * configured agent.\n *\n * Usage on the host page:\n *\n * ```html\n * <div id=\"orbit-voice-agent\"></div>\n * <script type=\"module\">\n *   import { OrbitVoiceAgent } from \"@devotel/orbit-web-sdk\";\n *   const agent = OrbitVoiceAgent.init({\n *     publicKey: \"org_acme\",\n *     agentId: \"agent_support\",\n *     container: document.getElementById(\"orbit-voice-agent\"),\n *   });\n *   agent.on(\"state\", (s) => console.log(\"voice-agent state:\", s));\n * </script>\n * ```\n *\n * Architecture:\n *\n *   - Control plane: `POST <apiBaseUrl>/widget/voice-agent-session` mints\n *     a short-lived orbit-media room token bound to {publicKey, agentId,\n *     visitorId}. Same anonymous, Origin-gated, public-key-keyed auth\n *     model as the native-chat `/widget/session` route — no Clerk / API\n *     key in the browser. The session carries `origin='web'` so the\n *     server-side `runAnthropicPipelineSession` path treats it as a web\n *     call (vs PSTN).\n *\n *   - Media plane: the WebRTC transport is the SAME `@orbit/media-client`\n *     (LiveKit fork) the legacy `OrbitSoftphone` already uses. We\n *     dynamic-import it via a string specifier so the package has no\n *     hard install-time dep when a consumer only uses other sdk-web\n *     exports (e.g. OrbitChat). The visitor publishes their mic; the\n *     agent runtime joins the same room server-side and its audio\n *     auto-subscribes and plays through an attached `<audio>` element.\n *\n *   - UI: deliberately minimal (M-effort scope) — a \"Talk to AI\" button\n *     and a lightweight animated waveform. Full waveform polish is a\n *     follow-on; this is the working shell the demo + onboarding path\n *     needs. No external CSS framework, no emojis (matches the repo UI\n *     register).\n */\n\nimport { TypedEmitter } from \"../softphone/emitter\";\n\n/** Lifecycle states surfaced to the host page. */\nexport type VoiceAgentState =\n  | \"idle\"\n  | \"connecting\"\n  | \"live\"\n  | \"ended\"\n  | \"error\";\n\n/**\n * Event map for `on` / `off`.\n *\n * Expressed as a type alias (not an interface) so the mapped-type\n * constraint on `TypedEmitter<M extends Record<string, ...>>` is\n * satisfied — TS does not treat plain interfaces as indexable under\n * `Record<string, ...>` without an explicit index signature (matches\n * the SoftphoneEventMap / CallSessionEventMap pattern in softphone/types.ts).\n */\nexport type OrbitVoiceAgentEventMap = {\n  state: (state: VoiceAgentState) => void;\n  error: (err: Error) => void;\n};\n\n/**\n * Page/visitor context the embed sends to power the agent screen-pop. The\n * page fields are auto-collected from the host document; `metadata` is the\n * host's free-form attribute bag.\n */\nexport interface WebCallContext {\n  page_url?: string;\n  page_title?: string;\n  referrer?: string;\n  metadata?: Record<string, string>;\n}\n\n/** Init options. */\nexport interface OrbitVoiceAgentConfig {\n  /** Public widget key (today the tenant org id) — same value as the chat widget. */\n  publicKey: string;\n  /** Which AI voice agent the browser will talk to. */\n  agentId: string;\n  /** DOM element the \"Talk to AI\" button + waveform paint into. */\n  container: HTMLElement;\n  /**\n   * Orbit API base URL. Defaults to the production host. Override for\n   * staging / self-hosted deployments. The mint route lives at\n   * `<apiBaseUrl>/widget/voice-agent-session` (NOT under /api/v1 — the\n   * widget surface is version-prefix-free by design).\n   */\n  apiBaseUrl?: string;\n  /** Button label override. */\n  buttonLabel?: string;\n  /** Stable visitor id for continuity across presses (localStorage echo). */\n  visitorId?: string;\n  /**\n   * Host-supplied attributes that ride along to the agent screen-pop (plan\n   * tier, cart value, signed-in account id, …). The page URL / title /\n   * referrer are collected automatically; this is purely the extra bag.\n   */\n  metadata?: Record<string, string>;\n  /**\n   * Auto-collect the page URL / title / referrer from the host document.\n   * Defaults to `true`. Set `false` on a privacy-sensitive embed that wants\n   * to send only an explicit `metadata` bag (or nothing).\n   */\n  collectPageContext?: boolean;\n}\n\n/** Shape of the mint route's `data` payload. */\ninterface VoiceAgentSessionResponse {\n  data?: {\n    token?: string;\n    ws_url?: string;\n    room_name?: string;\n    visitor_id?: string;\n    agent_id?: string;\n    origin?: string;\n    /** Server-sanitized echo of the context the embed sent. */\n    page_context?: WebCallContext;\n    expires_in?: number;\n  };\n}\n\n/**\n * Minimal Document surface the page-context collector reads. Kept structural\n * (not the DOM lib's `Document`) so the helper is unit-testable with a plain\n * stub and never assumes a real browser.\n */\nexport interface PageContextDocument {\n  title?: string;\n  referrer?: string;\n  defaultView?: { location?: { href?: string } } | null;\n}\n\n/**\n * Build the page/visitor context the embed posts to the mint route. Pure and\n * total — reads what the document exposes, folds in the host's `metadata`\n * bag, and omits any field the environment doesn't provide. When\n * `collectPage` is false only the explicit `metadata` is returned, so a\n * privacy-sensitive embed can opt out of URL/title/referrer collection.\n * Returns `undefined` when there is nothing to send.\n */\nexport function collectPageContext(\n  doc: PageContextDocument | null | undefined,\n  metadata: Record<string, string> | undefined,\n  collectPage: boolean,\n): WebCallContext | undefined {\n  const ctx: WebCallContext = {};\n  if (collectPage && doc) {\n    const href = doc.defaultView?.location?.href;\n    if (typeof href === \"string\" && href.length > 0) ctx.page_url = href;\n    if (typeof doc.title === \"string\" && doc.title.length > 0) {\n      ctx.page_title = doc.title;\n    }\n    if (typeof doc.referrer === \"string\" && doc.referrer.length > 0) {\n      ctx.referrer = doc.referrer;\n    }\n  }\n  if (metadata && Object.keys(metadata).length > 0) {\n    ctx.metadata = metadata;\n  }\n  return Object.keys(ctx).length > 0 ? ctx : undefined;\n}\n\n/** Minimal `@orbit/media-client` Room surface we depend on. */\ntype MediaRoom = {\n  connect: (url: string, token: string) => Promise<void>;\n  disconnect: () => Promise<void>;\n  localParticipant: {\n    setMicrophoneEnabled: (enabled: boolean) => Promise<unknown>;\n  };\n  on: (event: string, handler: (...args: unknown[]) => void) => void;\n};\n\nconst DEFAULT_BASE_URL = \"https://api.orbit.devotel.io\";\nconst DEFAULT_BUTTON_LABEL = \"Talk to AI\";\n\n/**\n * Single embeddable voice-agent session. One instance == one mount.\n */\nexport class OrbitVoiceAgent extends TypedEmitter<OrbitVoiceAgentEventMap> {\n  private state: VoiceAgentState = \"idle\";\n  private room: MediaRoom | null = null;\n  private button: HTMLButtonElement | null = null;\n  private waveform: HTMLElement | null = null;\n  private audioEl: HTMLAudioElement | null = null;\n\n  private constructor(private readonly config: OrbitVoiceAgentConfig) {\n    super();\n  }\n\n  /** Mount the widget into the configured container and return the instance. */\n  static init(config: OrbitVoiceAgentConfig): OrbitVoiceAgent {\n    if (!config.container) {\n      throw new Error(\"OrbitVoiceAgent.init: `container` is required\");\n    }\n    if (!config.publicKey || !config.agentId) {\n      throw new Error(\n        \"OrbitVoiceAgent.init: `publicKey` and `agentId` are required\",\n      );\n    }\n    const instance = new OrbitVoiceAgent(config);\n    instance.paint();\n    return instance;\n  }\n\n  /** Current lifecycle state. */\n  getState(): VoiceAgentState {\n    return this.state;\n  }\n\n  /** Tear down the session and remove the painted UI. */\n  destroy(): void {\n    void this.stop();\n    this.button?.remove();\n    this.waveform?.remove();\n    this.audioEl?.remove();\n    this.button = null;\n    this.waveform = null;\n    this.audioEl = null;\n  }\n\n  /** Paint the minimal \"Talk to AI\" button + waveform shell. */\n  private paint(): void {\n    const doc = this.config.container.ownerDocument;\n    const button = doc.createElement(\"button\");\n    button.type = \"button\";\n    button.textContent = this.config.buttonLabel ?? DEFAULT_BUTTON_LABEL;\n    button.setAttribute(\"data-orbit-voice-agent\", \"trigger\");\n    button.style.cursor = \"pointer\";\n    button.addEventListener(\"click\", () => {\n      if (this.state === \"live\" || this.state === \"connecting\") {\n        void this.stop();\n      } else {\n        void this.start();\n      }\n    });\n\n    // Lightweight waveform shell — animated bars. Full polish is a\n    // follow-on; this gives the demo path a live affordance.\n    const waveform = doc.createElement(\"div\");\n    waveform.setAttribute(\"data-orbit-voice-agent\", \"waveform\");\n    waveform.style.display = \"none\";\n    for (let i = 0; i < 5; i += 1) {\n      const bar = doc.createElement(\"span\");\n      bar.setAttribute(\"data-orbit-voice-agent\", \"bar\");\n      waveform.appendChild(bar);\n    }\n\n    // Hidden audio sink the agent's remote track plays through.\n    const audioEl = doc.createElement(\"audio\");\n    audioEl.autoplay = true;\n    audioEl.setAttribute(\"data-orbit-voice-agent\", \"audio-sink\");\n    audioEl.style.display = \"none\";\n\n    this.config.container.appendChild(button);\n    this.config.container.appendChild(waveform);\n    this.config.container.appendChild(audioEl);\n    this.button = button;\n    this.waveform = waveform;\n    this.audioEl = audioEl;\n  }\n\n  /** Begin a voice-agent session: mint a token, connect, publish mic. */\n  async start(): Promise<void> {\n    if (this.state === \"live\" || this.state === \"connecting\") return;\n    this.setState(\"connecting\");\n    try {\n      const session = await this.mintSession();\n      const token = session.data?.token;\n      const wsUrl = session.data?.ws_url;\n      if (!token || !wsUrl) {\n        throw new Error(\"voice-agent session response missing token or ws_url\");\n      }\n\n      // Dynamic import — no hard dep. Consumers opt in by installing\n      // `@orbit/media-client`. The specifier is built from a string local\n      // so TS's module resolver doesn't type-check the peer at compile\n      // time (it isn't installed in this workspace).\n      const mediaModuleName = \"@orbit/media-client\";\n      const lk = (await import(/* @vite-ignore */ mediaModuleName)) as {\n        Room: new () => MediaRoom;\n      };\n      const room = new lk.Room();\n      room.on(\"disconnected\", () => {\n        this.setState(\"ended\");\n        if (this.waveform) this.waveform.style.display = \"none\";\n      });\n      // Attach any subscribed remote track (the agent's audio) to the sink.\n      room.on(\"trackSubscribed\", (track: unknown) => {\n        const t = track as { attach?: (el?: HTMLMediaElement) => void } | null;\n        if (t?.attach && this.audioEl) t.attach(this.audioEl);\n      });\n      await room.connect(wsUrl, token);\n      await room.localParticipant.setMicrophoneEnabled(true);\n      this.room = room;\n\n      if (this.waveform) this.waveform.style.display = \"flex\";\n      this.setState(\"live\");\n    } catch (err) {\n      this.setState(\"error\");\n      this.emit(\"error\", err as Error);\n      if (this.waveform) this.waveform.style.display = \"none\";\n    }\n  }\n\n  /** End the session and release the mic. */\n  async stop(): Promise<void> {\n    const room = this.room;\n    this.room = null;\n    if (this.waveform) this.waveform.style.display = \"none\";\n    if (room) {\n      try {\n        await room.disconnect();\n      } catch {\n        /* best-effort teardown */\n      }\n    }\n    if (this.state !== \"error\") this.setState(\"ended\");\n  }\n\n  /** POST the mint route and return the parsed session. */\n  private async mintSession(): Promise<VoiceAgentSessionResponse> {\n    const baseUrl = this.config.apiBaseUrl ?? DEFAULT_BASE_URL;\n    // Collect the page/visitor context for the agent screen-pop. Defaults to\n    // on; an embed can opt out with `collectPageContext: false`.\n    const pageContext = collectPageContext(\n      this.config.container.ownerDocument as PageContextDocument | null,\n      this.config.metadata,\n      this.config.collectPageContext !== false,\n    );\n    const res = await fetch(`${baseUrl}/widget/voice-agent-session`, {\n      method: \"POST\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify({\n        public_key: this.config.publicKey,\n        agent_id: this.config.agentId,\n        visitor_id: this.config.visitorId,\n        ...(pageContext !== undefined && { page_context: pageContext }),\n      }),\n      // Bound the control-plane call — a hung API otherwise leaves the\n      // user staring at a \"connecting\" affordance forever.\n      signal: AbortSignal.timeout(10_000),\n    });\n    if (!res.ok) {\n      throw new Error(`voice-agent session request failed (${res.status})`);\n    }\n    return (await res.json()) as VoiceAgentSessionResponse;\n  }\n\n  private setState(next: VoiceAgentState): void {\n    if (this.state === next) return;\n    this.state = next;\n    this.emit(\"state\", next);\n  }\n}\n","/**\n * CCAAS-S-006 — Salesforce Open CTI adapter (2026-06-11).\n *\n * Five9, Talkdesk, Genesys, and NICE CXone all ship a Salesforce Open CTI\n * adapter so agents take contact-center calls without leaving the CRM. Orbit\n * already exposes a generic CRM bridge (`OrbitCTI` + the `/cti-embed` iframe),\n * but that speaks Orbit's own postMessage protocol — it does NOT speak\n * Salesforce's native Open CTI JS API (`sforce.opencti.*`). This adapter is\n * that missing translation layer: it runs inside the Salesforce softphone\n * panel (a Call Center adapter URL points at a page that loads Salesforce's\n * `opencti_min.js` and then calls `SalesforceOpenCtiAdapter.attach(...)`) and\n * binds Salesforce's native primitives to the Orbit CTI client:\n *\n *   - Salesforce native click-to-dial (`onClickToDial`) → `OrbitCTI.dial(...)`,\n *     so clicking a phone field on any Lead / Contact / Account record places\n *     the call through Orbit's softphone (which routes outbound via the\n *     Devotel softswitch — this adapter wires NO carrier itself).\n *   - Orbit `call:state` transitions → Salesforce `searchAndScreenPop(...)` so\n *     an agent who dials a bare number still gets the matching CRM record\n *     popped open.\n *   - Call completion → Salesforce `saveLog(...)`, writing a Task activity\n *     (Subject / CallType / CallDurationInSeconds / Status) against the record\n *     the agent dialled from — the activity-logging parity item in CCaaS RFPs.\n *\n * The adapter holds NO media transport: all WebRTC/SIP stays inside the Orbit\n * CTI iframe. It is intentionally framework-free so it can be loaded from a\n * Visualforce page, a Lightning component, or the CDN IIFE bundle.\n *\n * Why a structural `OrbitCtiClient` interface instead of the concrete\n * `OrbitCTI` class: keeps this unit-testable without a DOM/iframe and lets a\n * host wrap a custom transport. `OrbitCTI` satisfies it structurally.\n */\n\nimport type { OrbitCtiCallStateEvent } from \"./cti-embed\";\n\n/** Protocol version this adapter declares. Bump on contract change. */\nexport const SALESFORCE_OPENCTI_ADAPTER_VERSION = \"1.0.0\";\n\n/**\n * Minimal structural view of the Orbit CTI client this adapter drives. The\n * concrete `OrbitCTI` exported from `./cti-embed` satisfies this shape.\n */\nexport interface OrbitCtiClient {\n  /** Place an outbound click-to-dial. `number` must be E.164. */\n  dial(number: string, crmContext?: Record<string, string>): void;\n  /** Subscribe to backend call-state transitions. Returns an unsubscribe fn. */\n  on(\n    type: \"call:state\",\n    listener: (event: OrbitCtiCallStateEvent) => void,\n  ): () => void;\n}\n\n/** Payload Salesforce delivers on a native click-to-dial event. */\nexport interface SalesforceClickToDialPayload {\n  /** The phone number the agent clicked (CRM-formatted, e.g. \"(415) 555-0100\"). */\n  number?: string;\n  /** Salesforce record id the number belongs to (e.g. a Contact/Lead id). */\n  recordId?: string;\n  /** Display name of that record. */\n  recordName?: string;\n  /** SObject api name of the record (e.g. \"Contact\", \"Lead\", \"Account\"). */\n  objectType?: string;\n}\n\n/**\n * The subset of Salesforce's Open CTI JS API (`sforce.opencti`) this adapter\n * uses. Salesforce injects the real implementation onto `window.sforce.opencti`\n * once `opencti_min.js` has loaded inside the softphone panel.\n */\nexport interface SalesforceOpenCtiApi {\n  enableClickToDial(opts?: { callback?: (response: unknown) => void }): void;\n  disableClickToDial(opts?: { callback?: (response: unknown) => void }): void;\n  onClickToDial(opts: {\n    listener: (payload: SalesforceClickToDialPayload) => void;\n  }): void;\n  searchAndScreenPop(opts: {\n    searchParams: string;\n    queryParams?: string;\n    callType: string;\n    deferred?: boolean;\n    callback?: (response: unknown) => void;\n  }): void;\n  saveLog(opts: {\n    value: Record<string, unknown>;\n    callback?: (response: unknown) => void;\n  }): void;\n  setSoftphonePanelVisibility(opts: { visible: boolean }): void;\n  /** Salesforce's call-type enum — referenced defensively (may be absent in a mock). */\n  CALL_TYPE?: { INBOUND?: string; OUTBOUND?: string; INTERNAL?: string };\n}\n\n/** Diagnostic events the host page can observe for analytics / debugging. */\nexport type SalesforceAdapterEvent =\n  | { type: \"click-to-dial\"; number: string; recordId?: string }\n  | { type: \"screen-pop\"; number: string }\n  | { type: \"log-saved\"; recordId?: string; durationSeconds: number }\n  | { type: \"error\"; code: string; message: string };\n\n/** Config for `SalesforceOpenCtiAdapter.attach(...)`. */\nexport interface SalesforceOpenCtiAdapterConfig {\n  /** The Orbit CTI client (typically an `OrbitCTI` instance). */\n  cti: OrbitCtiClient;\n  /**\n   * The Salesforce Open CTI API. Defaults to `window.sforce.opencti`. Inject\n   * a mock in tests or a wrapper in non-Salesforce hosts.\n   */\n  opencti?: SalesforceOpenCtiApi;\n  /**\n   * E.164 country prefix applied to click-to-dial numbers that arrive without\n   * a leading \"+\". Salesforce phone fields are usually nationally formatted,\n   * so the org's home country is prepended. Defaults to \"+1\" (NANP).\n   */\n  defaultCountryCode?: string;\n  /** Write a Salesforce Task activity when a call ends. Defaults to true. */\n  logCalls?: boolean;\n  /** Subject prefix for the logged Task. Defaults to \"Orbit call\". */\n  logSubjectPrefix?: string;\n  /** Optional diagnostics hook fired on every adapter event. */\n  onEvent?: (event: SalesforceAdapterEvent) => void;\n}\n\n/** Per-call context the adapter tracks between dial and call completion. */\ninterface CallContext {\n  number: string;\n  recordId?: string;\n  recordName?: string;\n  objectType?: string;\n  startedAtMs: number;\n  answeredAtMs?: number;\n  screenPopped: boolean;\n}\n\n/**\n * Normalize a Salesforce-formatted phone number into strict E.164. Returns\n * null if the value cannot be coerced into a dialable number — the caller\n * surfaces an error rather than dialling garbage.\n *\n * A leading \"+\" is preserved (the number already carries a country code).\n * Otherwise `defaultCountryCode` is prepended, matching how Five9 / Talkdesk\n * adapters treat nationally-formatted CRM phone fields. A single leading\n * national trunk \"0\" (UK/EU-style, e.g. \"020 7946 0958\") is dropped before\n * the country code is prepended, since the trunk prefix is not part of the\n * E.164 subscriber number.\n */\nexport function normalizeSalesforceNumber(\n  raw: string,\n  defaultCountryCode: string,\n): string | null {\n  if (typeof raw !== \"string\") return null;\n  const trimmed = raw.trim();\n  if (trimmed.length === 0) return null;\n  const hasPlus = trimmed.startsWith(\"+\");\n  const digits = trimmed.replace(/\\D/g, \"\");\n  if (digits.length < 7 || digits.length > 15) return null;\n  const prefixDigits = defaultCountryCode.replace(/\\D/g, \"\");\n  // National form: strip the trunk \"0\" so it is not doubled onto the CC.\n  const nationalDigits = digits.replace(/^0/, \"\");\n  const e164 = hasPlus ? `+${digits}` : `+${prefixDigits}${nationalDigits}`;\n  if (!/^\\+[1-9]\\d{6,14}$/.test(e164)) return null;\n  return e164;\n}\n\n/** SObjects whose activities link via `WhoId` (people) rather than `WhatId`. */\nconst WHO_ID_OBJECTS = new Set([\"Contact\", \"Lead\"]);\n\n/**\n * Salesforce Open CTI adapter. Construct via the static `attach(...)` factory,\n * which wires the Salesforce <-> Orbit plumbing immediately. Call `detach()`\n * to tear it down (idempotent).\n */\nexport class SalesforceOpenCtiAdapter {\n  private readonly cti: OrbitCtiClient;\n  private readonly opencti: SalesforceOpenCtiApi;\n  private readonly defaultCountryCode: string;\n  private readonly logCalls: boolean;\n  private readonly logSubjectPrefix: string;\n  private readonly onEvent?: (event: SalesforceAdapterEvent) => void;\n\n  /** Context keyed by the backend dial id once it is known. */\n  private readonly byDialId = new Map<string, CallContext>();\n  /** Context keyed by E.164 number for the window before the first dial id. */\n  private readonly pendingByNumber = new Map<string, CallContext>();\n\n  private unsubscribe: (() => void) | null = null;\n  private detached = false;\n\n  private constructor(config: SalesforceOpenCtiAdapterConfig, api: SalesforceOpenCtiApi) {\n    this.cti = config.cti;\n    this.opencti = api;\n    this.defaultCountryCode = config.defaultCountryCode ?? \"+1\";\n    this.logCalls = config.logCalls ?? true;\n    this.logSubjectPrefix = config.logSubjectPrefix ?? \"Orbit call\";\n    this.onEvent = config.onEvent;\n  }\n\n  /**\n   * Wire the adapter to Salesforce + Orbit. Reveals the softphone panel,\n   * enables Salesforce native click-to-dial, and begins relaying call-state.\n   */\n  static attach(config: SalesforceOpenCtiAdapterConfig): SalesforceOpenCtiAdapter {\n    const api = config.opencti ?? getDefaultOpenCti();\n    if (!api) {\n      throw new Error(\n        \"SalesforceOpenCtiAdapter: window.sforce.opencti is unavailable — \" +\n          \"load Salesforce's opencti_min.js before attaching, or pass `opencti`.\",\n      );\n    }\n    const adapter = new SalesforceOpenCtiAdapter(config, api);\n    adapter.init();\n    return adapter;\n  }\n\n  private init(): void {\n    try {\n      this.opencti.setSoftphonePanelVisibility({ visible: true });\n    } catch {\n      // Panel visibility is best-effort — never block dial wiring on it.\n    }\n    this.opencti.enableClickToDial();\n    this.opencti.onClickToDial({\n      listener: (payload) => {\n        this.handleClickToDial(payload);\n      },\n    });\n    this.unsubscribe = this.cti.on(\"call:state\", (event) => {\n      this.handleCallState(event);\n    });\n  }\n\n  /** Salesforce native click-to-dial → normalize → Orbit dial. */\n  private handleClickToDial(payload: SalesforceClickToDialPayload): void {\n    if (this.detached) return;\n    const raw = typeof payload.number === \"string\" ? payload.number : \"\";\n    const number = normalizeSalesforceNumber(raw, this.defaultCountryCode);\n    if (!number) {\n      this.emit({\n        type: \"error\",\n        code: \"INVALID_NUMBER\",\n        message: `Salesforce click-to-dial number \"${raw}\" is not dialable`,\n      });\n      return;\n    }\n    const context: CallContext = {\n      number,\n      recordId: payload.recordId,\n      recordName: payload.recordName,\n      objectType: payload.objectType,\n      startedAtMs: Date.now(),\n      screenPopped: false,\n    };\n    this.pendingByNumber.set(number, context);\n\n    const crmContext: Record<string, string> = {};\n    if (payload.recordId) crmContext[\"sf_record_id\"] = payload.recordId;\n    if (payload.objectType) crmContext[\"sf_object_type\"] = payload.objectType;\n    if (payload.recordName) crmContext[\"sf_record_name\"] = payload.recordName;\n\n    this.cti.dial(number, crmContext);\n    this.emit({ type: \"click-to-dial\", number, recordId: payload.recordId });\n  }\n\n  /** Orbit call-state → Salesforce screen-pop + activity logging. */\n  private handleCallState(event: OrbitCtiCallStateEvent): void {\n    if (this.detached) return;\n    const context = this.resolveContext(event);\n    if (!context) return;\n\n    if (event.state === \"ringing\" || event.state === \"answered\") {\n      if (event.state === \"answered\" && context.answeredAtMs === undefined) {\n        context.answeredAtMs = event.at;\n      }\n      // Only search-and-pop when the dial did NOT originate from a known\n      // Salesforce record — otherwise the agent is already on that record.\n      if (!context.recordId && !context.screenPopped) {\n        context.screenPopped = true;\n        this.screenPop(context.number);\n      }\n      return;\n    }\n\n    if (event.state === \"ended\" || event.state === \"failed\") {\n      this.finishCall(event, context);\n    }\n  }\n\n  /**\n   * Locate (and migrate) the call context for an event. The first state event\n   * reveals the backend dial id, so we promote the number-keyed entry into the\n   * dial-id map and dedupe by number thereafter.\n   */\n  private resolveContext(event: OrbitCtiCallStateEvent): CallContext | null {\n    const existing = this.byDialId.get(event.dialId);\n    if (existing) return existing;\n    if (event.to) {\n      const pending = this.pendingByNumber.get(event.to);\n      if (pending) {\n        this.pendingByNumber.delete(event.to);\n        if (event.dialId) this.byDialId.set(event.dialId, pending);\n        return pending;\n      }\n    }\n    return null;\n  }\n\n  private screenPop(number: string): void {\n    const callType =\n      this.opencti.CALL_TYPE?.OUTBOUND ?? \"outbound\";\n    try {\n      this.opencti.searchAndScreenPop({\n        searchParams: number,\n        callType,\n        deferred: false,\n      });\n      this.emit({ type: \"screen-pop\", number });\n    } catch (err) {\n      this.emit({\n        type: \"error\",\n        code: \"SCREEN_POP_FAILED\",\n        message: (err as Error).message,\n      });\n    }\n  }\n\n  private finishCall(\n    event: OrbitCtiCallStateEvent,\n    context: CallContext,\n  ): void {\n    // Drop the tracked context regardless of whether we log.\n    if (event.dialId) this.byDialId.delete(event.dialId);\n\n    if (!this.logCalls) return;\n    const durationSeconds =\n      context.answeredAtMs !== undefined\n        ? Math.max(0, Math.round((event.at - context.answeredAtMs) / 1000))\n        : 0;\n    const value: Record<string, unknown> = {\n      entityApiName: \"Task\",\n      Subject: `${this.logSubjectPrefix} to ${context.number}`,\n      CallType: \"Outbound\",\n      CallDurationInSeconds: durationSeconds,\n      Status: event.state === \"failed\" ? \"Not Completed\" : \"Completed\",\n      Description:\n        event.state === \"failed\"\n          ? `Outbound call to ${context.number} did not connect.`\n          : `Outbound call to ${context.number}.`,\n    };\n    if (context.recordId) {\n      const linkField =\n        context.objectType && WHO_ID_OBJECTS.has(context.objectType)\n          ? \"WhoId\"\n          : \"WhatId\";\n      value[linkField] = context.recordId;\n    }\n    try {\n      this.opencti.saveLog({ value });\n      this.emit({\n        type: \"log-saved\",\n        recordId: context.recordId,\n        durationSeconds,\n      });\n    } catch (err) {\n      this.emit({\n        type: \"error\",\n        code: \"SAVE_LOG_FAILED\",\n        message: (err as Error).message,\n      });\n    }\n  }\n\n  /** Tear down the adapter. Idempotent. */\n  detach(): void {\n    if (this.detached) return;\n    this.detached = true;\n    if (this.unsubscribe) {\n      this.unsubscribe();\n      this.unsubscribe = null;\n    }\n    try {\n      this.opencti.disableClickToDial();\n    } catch {\n      // Best-effort — Salesforce may already have torn down the panel.\n    }\n    this.byDialId.clear();\n    this.pendingByNumber.clear();\n  }\n\n  private emit(event: SalesforceAdapterEvent): void {\n    if (!this.onEvent) return;\n    try {\n      this.onEvent(event);\n    } catch {\n      // Host listener exceptions must not crash the adapter.\n    }\n  }\n}\n\n/** Read `window.sforce.opencti` without leaking `any`. */\nfunction getDefaultOpenCti(): SalesforceOpenCtiApi | null {\n  if (typeof window === \"undefined\") return null;\n  const sforce = (\n    window as unknown as { sforce?: { opencti?: SalesforceOpenCtiApi } }\n  ).sforce;\n  return sforce?.opencti ?? null;\n}\n\nexport default SalesforceOpenCtiAdapter;\n","/**\n * Orbit CDP Analytics Web SDK (cdpaas-002, 2026-05-28).\n *\n * Segment-compatible browser analytics surface — `track`, `identify`,\n * `alias`, `page`, `screen`, `group`, `batch`. Wraps the public HMAC-\n * authed ingest endpoints (`/cdp/v1/:ingestId/*`) with timestamp + nonce\n * + signature handled INSIDE the SDK boundary so callers never touch\n * the crypto.\n *\n * Why a separate file in sdk-web (vs reusing sdk-node's `CdpAnalyticsClient`):\n *   - sdk-node imports `node:crypto` (createHmac, randomBytes) which is\n *     not available in browsers / Edge runtimes.\n *   - sdk-web is bundled with `tsup` for the browser and ships with no\n *     external dependencies — we use the WebCrypto API\n *     (`crypto.subtle.importKey` + `sign(\"HMAC\", ...)`) instead.\n *   - Both clients implement the same wire contract (canonical =\n *     `<ts>.<nonce>.<rawBody>`, signature = `v1=<hex>`), so a request\n *     signed by either client verifies against the server identically.\n *\n * Security note: this client holds an INGEST SECRET, not the dashboard\n * API key. Customers who need browser-side event ingestion MUST mint a\n * dedicated ingest secret per origin / per app and rotate on\n * compromise. For pure browser-side use, prefer the public-key surfaces\n * in `OrbitPersonalization` / `OrbitChat` which never need the ingest\n * secret. This client is intended for trusted edge workers / SSR\n * surfaces that hold the secret server-side and proxy events for the\n * client.\n */\n\n/** Shared context block — Segment v1 spec. */\nexport type CdpAnalyticsContext = Record<string, unknown>;\n\ninterface CdpIdentityBase {\n  /** Stable post-login user identifier. Preferred when known. */\n  userId?: string;\n  /** Pre-login anonymous identifier (e.g. browser cookie / localStorage). */\n  anonymousId?: string;\n  /** Optional Segment-style context block (locale, page, library, ...). */\n  context?: CdpAnalyticsContext;\n  /** Optional ISO-8601 timestamp. Defaults to server-received time. */\n  timestamp?: string;\n  /** Optional client-generated message id for server-side dedup. */\n  messageId?: string;\n}\n\nexport interface CdpTrackInput extends CdpIdentityBase {\n  /** Event name — `Order Completed`, `Page Viewed`, custom verbs. */\n  event: string;\n  properties?: Record<string, unknown>;\n}\n\nexport interface CdpIdentifyInput extends CdpIdentityBase {\n  /** Free-form traits hash. Upserts contact row keyed on `userId`. */\n  traits?: Record<string, unknown>;\n}\n\nexport interface CdpAliasInput {\n  /** Stable new identity. */\n  userId: string;\n  /** Prior identity to merge into `userId`. */\n  previousId: string;\n  context?: CdpAnalyticsContext;\n  timestamp?: string;\n  messageId?: string;\n}\n\nexport interface CdpPageInput extends CdpIdentityBase {\n  /** Optional page name (e.g. \"Pricing\"). */\n  name?: string;\n  /** Optional taxonomy category. */\n  category?: string;\n  properties?: Record<string, unknown>;\n}\n\nexport interface CdpScreenInput extends CdpIdentityBase {\n  /** Optional screen name (mobile equivalent of page.name). */\n  name?: string;\n  properties?: Record<string, unknown>;\n}\n\nexport interface CdpGroupInput extends CdpIdentityBase {\n  /** Account / org / team identifier. */\n  groupId: string;\n  traits?: Record<string, unknown>;\n}\n\n/** One element of a `batch` request — Segment v1 batch shape. */\nexport type CdpBatchEnvelope =\n  | ({ type: \"track\" } & CdpTrackInput)\n  | ({ type: \"identify\" } & CdpIdentifyInput)\n  | ({ type: \"page\" } & CdpPageInput)\n  | ({ type: \"screen\" } & CdpScreenInput)\n  | ({ type: \"group\" } & CdpGroupInput)\n  | ({ type: \"alias\" } & CdpAliasInput);\n\nexport interface CdpBatchInput {\n  /** Up to 200 events per batch (server-side cap). */\n  batch: CdpBatchEnvelope[];\n  context?: CdpAnalyticsContext;\n  sentAt?: string;\n}\n\nexport interface CdpAnalyticsAck {\n  received: boolean;\n  event_id?: string;\n  contact_id?: string | null;\n  deduped?: boolean;\n  reason?: string;\n}\n\nexport interface CdpBatchAck {\n  received: boolean;\n  count: number;\n  results: Array<{\n    type: string;\n    event_id: string | null;\n    contact_id: string | null;\n    ok: boolean;\n    received: boolean;\n    reason: string | null;\n    error: string | null;\n  }>;\n}\n\nexport interface OrbitCdpAnalyticsConfig {\n  /**\n   * Host base URL — must NOT include the `/api/v1` admin prefix. Ingest\n   * endpoints live at `<apiUrl>/cdp/v1/:ingestId/*`. Example:\n   * `https://api.orbit.devotel.io`.\n   */\n  apiUrl: string;\n  /** Per-tenant ingest id (the `cdp_ingest_secrets.id` from dashboard). */\n  ingestId: string;\n  /**\n   * Plaintext ingest secret shown ONCE at mint time. WARNING: this\n   * value MUST NOT ship to the browser as part of the page bundle —\n   * use this client only on a trusted server / edge surface, or proxy\n   * the signing through your own backend.\n   */\n  ingestSecret: string;\n  /** Per-request timeout in ms. Defaults to 30s. */\n  timeout?: number;\n}\n\ninterface ApiResponse<T> {\n  data: T;\n  meta?: { request_id?: string; [k: string]: unknown };\n}\n\n/**\n * Segment-compatible analytics client for browser / edge runtimes.\n * Mirror of {@link CdpAnalyticsClient} in `@devotel/orbit-sdk` but\n * uses the WebCrypto API for HMAC so it works in any modern browser,\n * Cloudflare Workers, Vercel Edge, Deno, and Bun.\n *\n * @example\n * ```ts\n * import { OrbitCdpAnalytics } from \"@devotel/orbit-web-sdk\";\n *\n * const analytics = new OrbitCdpAnalytics({\n *   apiUrl: \"https://api.orbit.devotel.io\",\n *   ingestId: env.ORBIT_CDP_INGEST_ID,\n *   ingestSecret: env.ORBIT_CDP_INGEST_SECRET,\n * });\n *\n * await analytics.track({\n *   userId: \"u_42\",\n *   event: \"Order Completed\",\n *   properties: { revenue: 99.99, currency: \"USD\" },\n * });\n * ```\n */\nexport class OrbitCdpAnalytics {\n  private readonly apiUrl: string;\n  private readonly ingestId: string;\n  private readonly ingestSecret: string;\n  private readonly timeoutMs: number;\n  // Lazily-imported HMAC key handle. Cached for the life of the\n  // instance because `crypto.subtle.importKey` is async + non-trivial.\n  private cryptoKeyPromise: Promise<CryptoKey> | null = null;\n\n  constructor(config: OrbitCdpAnalyticsConfig) {\n    if (!config.apiUrl) {\n      throw new Error(\"OrbitCdpAnalytics: apiUrl is required\");\n    }\n    if (!config.ingestId) {\n      throw new Error(\"OrbitCdpAnalytics: ingestId is required\");\n    }\n    if (!config.ingestSecret) {\n      throw new Error(\"OrbitCdpAnalytics: ingestSecret is required\");\n    }\n    this.apiUrl = config.apiUrl.replace(/\\/+$/, \"\");\n    this.ingestId = config.ingestId;\n    this.ingestSecret = config.ingestSecret;\n    this.timeoutMs = config.timeout ?? 30_000;\n  }\n\n  /**\n   * Tracks a user-action event.\n   *\n   * @example\n   * ```ts\n   * await analytics.track({\n   *   userId: \"u_42\",\n   *   event: \"Clicked CTA\",\n   *   properties: { cta: \"buy-now\" },\n   * });\n   * ```\n   */\n  track(input: CdpTrackInput): Promise<ApiResponse<CdpAnalyticsAck>> {\n    requireIdentity(input, \"track\");\n    return this.post(\n      \"track\",\n      buildEnvelope(\"track\", input as unknown as Record<string, unknown>),\n    );\n  }\n\n  /**\n   * Identifies a known user + upserts the contact row keyed on `userId`.\n   *\n   * @example\n   * ```ts\n   * await analytics.identify({\n   *   userId: \"u_42\",\n   *   traits: { email: \"ada@example.com\", plan: \"pro\" },\n   * });\n   * ```\n   */\n  identify(input: CdpIdentifyInput): Promise<ApiResponse<CdpAnalyticsAck>> {\n    requireIdentity(input, \"identify\");\n    return this.post(\n      \"identify\",\n      buildEnvelope(\"identify\", input as unknown as Record<string, unknown>),\n    );\n  }\n\n  /**\n   * Merges two identities — runs the server-side contact-merge flow.\n   *\n   * @example\n   * ```ts\n   * await analytics.alias({ userId: \"u_42\", previousId: \"anon_abc\" });\n   * ```\n   */\n  alias(input: CdpAliasInput): Promise<ApiResponse<CdpAnalyticsAck>> {\n    return this.post(\n      \"alias\",\n      buildEnvelope(\"alias\", input as unknown as Record<string, unknown>),\n    );\n  }\n\n  /**\n   * Records a pageview.\n   *\n   * @example\n   * ```ts\n   * await analytics.page({\n   *   userId: \"u_42\",\n   *   name: \"Pricing\",\n   *   properties: { path: \"/pricing\" },\n   * });\n   * ```\n   */\n  page(input: CdpPageInput): Promise<ApiResponse<CdpAnalyticsAck>> {\n    requireIdentity(input, \"page\");\n    return this.post(\n      \"page\",\n      buildEnvelope(\"page\", input as unknown as Record<string, unknown>),\n    );\n  }\n\n  /**\n   * Records a mobile screen view.\n   *\n   * @example\n   * ```ts\n   * await analytics.screen({ userId: \"u_42\", name: \"Checkout\" });\n   * ```\n   */\n  screen(input: CdpScreenInput): Promise<ApiResponse<CdpAnalyticsAck>> {\n    requireIdentity(input, \"screen\");\n    return this.post(\n      \"screen\",\n      buildEnvelope(\"screen\", input as unknown as Record<string, unknown>),\n    );\n  }\n\n  /**\n   * Associates a user with an account / org / team.\n   *\n   * @example\n   * ```ts\n   * await analytics.group({\n   *   userId: \"u_42\",\n   *   groupId: \"org_acme\",\n   *   traits: { plan: \"enterprise\" },\n   * });\n   * ```\n   */\n  group(input: CdpGroupInput): Promise<ApiResponse<CdpAnalyticsAck>> {\n    requireIdentity(input, \"group\");\n    return this.post(\n      \"group\",\n      buildEnvelope(\"group\", input as unknown as Record<string, unknown>),\n    );\n  }\n\n  /**\n   * Submits up to 200 events in a single signed request.\n   *\n   * @example\n   * ```ts\n   * await analytics.batch({\n   *   batch: [\n   *     { type: \"identify\", userId: \"u_42\", traits: { plan: \"pro\" } },\n   *     { type: \"track\", userId: \"u_42\", event: \"Order Completed\" },\n   *   ],\n   * });\n   * ```\n   */\n  batch(input: CdpBatchInput): Promise<ApiResponse<CdpBatchAck>> {\n    if (!Array.isArray(input.batch) || input.batch.length === 0) {\n      throw new Error(\n        \"OrbitCdpAnalytics.batch: `batch` must be a non-empty array\",\n      );\n    }\n    if (input.batch.length > 200) {\n      throw new Error(\n        \"OrbitCdpAnalytics.batch: max 200 events per batch (server-side cap)\",\n      );\n    }\n    const envelope: Record<string, unknown> = { batch: input.batch };\n    if (input.context !== undefined) envelope.context = input.context;\n    if (input.sentAt !== undefined) envelope.sentAt = input.sentAt;\n    return this.post<CdpBatchAck>(\"batch\", envelope);\n  }\n\n  /**\n   * Internal — signs the raw body and returns the headers.\n   * Exposed for test fixtures only.\n   *\n   * @internal\n   */\n  async _signRequest(rawBody: string): Promise<{\n    timestamp: number;\n    nonce: string;\n    signature: string;\n    headers: Record<string, string>;\n  }> {\n    const timestamp = Math.floor(Date.now() / 1000);\n    const nonce = generateHexNonce(16);\n    const canonical = `${timestamp}.${nonce}.${rawBody}`;\n    const signature = await hmacSha256Hex(await this.getCryptoKey(), canonical);\n    return {\n      timestamp,\n      nonce,\n      signature,\n      headers: {\n        \"X-Orbit-CDP-Timestamp\": String(timestamp),\n        \"X-Orbit-CDP-Nonce\": nonce,\n        \"X-Orbit-CDP-Signature\": `v1=${signature}`,\n      },\n    };\n  }\n\n  private getCryptoKey(): Promise<CryptoKey> {\n    if (!this.cryptoKeyPromise) {\n      const subtle = getSubtle();\n      const keyBytes = new TextEncoder().encode(this.ingestSecret);\n      this.cryptoKeyPromise = subtle.importKey(\n        \"raw\",\n        keyBytes,\n        { name: \"HMAC\", hash: \"SHA-256\" },\n        false,\n        [\"sign\"],\n      );\n    }\n    return this.cryptoKeyPromise;\n  }\n\n  private async post<T>(\n    leg: string,\n    envelope: Record<string, unknown>,\n  ): Promise<ApiResponse<T>> {\n    const rawBody = JSON.stringify(envelope);\n    const { headers: signedHeaders } = await this._signRequest(rawBody);\n    const url = `${this.apiUrl}/cdp/v1/${encodeURIComponent(this.ingestId)}/${leg}`;\n    const controller = new AbortController();\n    const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n    try {\n      const res = await fetch(url, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          ...signedHeaders,\n        },\n        body: rawBody,\n        signal: controller.signal,\n      });\n      if (!res.ok) {\n        const errBody = (await res.json().catch(() => ({}))) as {\n          error?: { message?: string; code?: string };\n        };\n        throw new Error(\n          `CDP ingest /${leg} failed: ${res.status} ${errBody.error?.code ?? \"\"} ` +\n            `${errBody.error?.message ?? res.statusText}`.trim(),\n        );\n      }\n      return (await res.json()) as ApiResponse<T>;\n    } finally {\n      clearTimeout(timer);\n    }\n  }\n}\n\n/**\n * Builds the JSON envelope sent to the server. Drops `undefined`\n * fields so the canonical HMAC body stays deterministic across SDK\n * versions (a `JSON.stringify({a: undefined})` would produce `\"{}\"`\n * inconsistently across engines if not pre-stripped).\n *\n * @internal\n */\nfunction buildEnvelope(\n  type: CdpBatchEnvelope[\"type\"],\n  input: Record<string, unknown>,\n): Record<string, unknown> {\n  const out: Record<string, unknown> = { type };\n  for (const [k, v] of Object.entries(input)) {\n    if (v !== undefined) out[k] = v;\n  }\n  return out;\n}\n\n/**\n * Throws if neither `userId` nor `anonymousId` is supplied. Matches\n * the server-side Segment v1 spec check so the SDK surfaces the\n * validation error inline rather than forcing a round-trip.\n *\n * @internal\n */\nfunction requireIdentity(\n  input: { userId?: string; anonymousId?: string },\n  leg: string,\n): void {\n  if (!input.userId && !input.anonymousId) {\n    throw new Error(\n      `OrbitCdpAnalytics.${leg}: either userId or anonymousId is required (Segment v1 spec)`,\n    );\n  }\n}\n\n/**\n * Returns a hex-encoded random nonce of `bytes` bytes. Uses WebCrypto\n * which is present in all modern browsers + Workers + Node 19+.\n *\n * @internal\n */\nfunction generateHexNonce(bytes: number): string {\n  const c = getCrypto();\n  const buf = new Uint8Array(bytes);\n  c.getRandomValues(buf);\n  return Array.from(buf, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Computes hex(HMAC-SHA256(key, message)) via WebCrypto.subtle.\n *\n * @internal\n */\nasync function hmacSha256Hex(key: CryptoKey, message: string): Promise<string> {\n  const subtle = getSubtle();\n  const sig = await subtle.sign(\"HMAC\", key, new TextEncoder().encode(message));\n  const bytes = new Uint8Array(sig);\n  return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Resolves the runtime `crypto` object. Browsers + Workers + Node 19+\n * expose `globalThis.crypto`. Older Node falls back via dynamic require\n * — but in those runtimes the customer should be using `@devotel/orbit-sdk`'s\n * `CdpAnalyticsClient` which uses native `node:crypto` directly.\n *\n * @internal\n */\nfunction getCrypto(): Crypto {\n  const c = (globalThis as { crypto?: Crypto }).crypto;\n  if (!c) {\n    throw new Error(\n      \"OrbitCdpAnalytics: globalThis.crypto is unavailable. Use @devotel/orbit-sdk (Node) instead.\",\n    );\n  }\n  return c;\n}\n\nfunction getSubtle(): SubtleCrypto {\n  const subtle = getCrypto().subtle;\n  if (!subtle) {\n    throw new Error(\n      \"OrbitCdpAnalytics: crypto.subtle is unavailable. Requires a secure context (HTTPS) or Node 19+.\",\n    );\n  }\n  return subtle;\n}\n\n/** Test-only helpers — narrow surface kept off the public export list. */\nexport const __orbitCdpAnalyticsTestables = {\n  buildEnvelope,\n  requireIdentity,\n};\n","/**\n * Orbit Web Analytics — public-key (writeKey) browser tracking snippet\n * (CDPaaS, 2026-06-12).\n *\n * The analytics.js-equivalent the CDP was missing. `OrbitCdpAnalytics`\n * (cdpaas-002) is Segment-compatible but signs every request with an\n * HMAC **ingest secret** that must never ship to the browser — it is for\n * trusted edge / SSR surfaces that hold the secret server-side. That\n * left self-serve website tracking — the front door of every CDP —\n * impossible without the customer building a server-side proxy.\n *\n * `OrbitWebAnalytics` closes that gap. It authenticates with a PUBLIC\n * write key (the tenant's public API key, `dv_live_pk_…` / `dv_test_pk_…`)\n * which is read-only and safe to paste into any webpage, and it speaks\n * the familiar Segment analytics.js surface (`identify` / `track` /\n * `page` / `group` / `reset`). Under the hood it posts to the existing\n * key-less public ingest endpoints (`/sdk/identify`, `/sdk/track`,\n * `/sdk/track/anonymous`) — the same surface `OrbitPersonalization`\n * already uses — so there is no new server auth surface and the HMAC\n * ingest secret never has to leave the customer's backend.\n *\n * Design constraints (shared with the other sdk-web public surfaces):\n *   - No external dependencies; SSR-safe (every browser-only call is\n *     gated on `typeof window !== \"undefined\"`).\n *   - Sticky per-browser `anonymousId` in localStorage so anonymous\n *     funnels reconstruct across page loads; on identify the server\n *     backfills prior anonymous events onto the resolved contact.\n *   - Events are buffered + batched (one network call per N events, not\n *     one-per-click) and flushed on `pagehide`.\n *   - Ad-blocker / unload resilient transport, mirroring the audited\n *     `#SDK-005` tiers: `fetch({ keepalive: true })` with the key on a\n *     header (never the URL), falling back to `navigator.sendBeacon` to\n *     the key-less `/sdk/track/anonymous` endpoint.\n *\n * Identity model: the public ingest surface joins on `anonymousId` plus\n * an `email` / `phone` identity (the cross-channel contact keys Orbit\n * already tracks for messaging). Segment's `userId` has no first-class\n * column on this surface, so it is preserved inside event `properties`\n * (as `userId`) for queryability while `email` / `phone` traits drive\n * the contact resolution + backfill.\n *\n * @example\n * ```ts\n * import { OrbitWebAnalytics } from \"@devotel/orbit-web-sdk\";\n *\n * const analytics = new OrbitWebAnalytics({\n *   apiUrl: \"https://api.orbit.devotel.io\",\n *   writeKey: \"dv_live_pk_xxx\", // public, safe to ship in page JS\n * });\n *\n * analytics.page();\n * analytics.track(\"Clicked CTA\", { cta: \"buy-now\" });\n * analytics.identify(\"u_42\", { email: \"ada@example.com\", plan: \"pro\" });\n * ```\n */\n\n/** Storage key — sticky per-browser anonymous id. */\nconst ANON_ID_KEY = \"orbit_analytics_anonymous_id\";\n/** Storage key — last-known identity, restored on construction. */\nconst IDENTITY_KEY = \"orbit_analytics_identity\";\n\n/**\n * Server-side caps on `/sdk/track` (apps/api/src/routes/sdk/index.ts):\n * up to 50 events per call and event names capped at 100 chars. We mirror\n * them at the SDK boundary so a flush never trips a server 422.\n */\nconst SERVER_MAX_EVENTS_PER_BATCH = 50;\nconst SERVER_MAX_EVENT_NAME_CHARS = 100;\n\n/** Free-form trait hash. `email` / `phone` drive contact resolution. */\nexport interface OrbitAnalyticsTraits {\n  /** Email — secondary cross-channel join key. */\n  email?: string;\n  /** E.164 phone — preferred cross-channel join key. */\n  phone?: string;\n  /** Display name for the dashboard contact row. */\n  name?: string;\n  /** Any other traits — stored on the contact's attribute bag. */\n  [key: string]: unknown;\n}\n\n/** Structured event properties. */\nexport type OrbitAnalyticsProperties = Record<string, unknown>;\n\n/** Constructor options. */\nexport interface OrbitWebAnalyticsConfig {\n  /**\n   * Public write key (the tenant's public API key,\n   * `dv_live_pk_…` / `dv_test_pk_…`). Read-only; safe to ship in page JS.\n   * Either `writeKey` or the `publicKey` alias must be set; `writeKey`\n   * wins when both are present.\n   */\n  writeKey?: string;\n  /** Alias for `writeKey` (parity with the other sdk-web public surfaces). */\n  publicKey?: string;\n  /** Base URL of the Orbit API (e.g. `https://api.orbit.devotel.io`). */\n  apiUrl: string;\n  /** When true, log every call to the console. Off by default. */\n  debug?: boolean;\n  /**\n   * Buffer size that triggers an immediate flush. Defaults to 20. Clamped\n   * to the inclusive range [1, 50] (50 = server-side `/sdk/track` cap).\n   */\n  flushAt?: number;\n  /**\n   * Auto-flush interval in milliseconds. Defaults to 2_000. Set to 0 to\n   * disable the timer (flush on batch-size / unload only). Negative throws.\n   */\n  flushInterval?: number;\n  /**\n   * Seed the sticky anonymousId (e.g. from SSR). When set, overrides the\n   * localStorage value. Pass `null` to FORCE-RESET to a fresh id.\n   */\n  anonymousId?: string | null;\n  /**\n   * When true, skip wiring the `pagehide` listener. Default false. Used\n   * by tests + non-browser runtimes that drive lifecycle manually.\n   */\n  skipLifecycleListeners?: boolean;\n}\n\n/** Buffered event — wire shape for one element of the `/sdk/track` batch. */\ninterface BufferedEvent {\n  name: string;\n  properties: OrbitAnalyticsProperties;\n  timestamp: string;\n}\n\n/** Resolved identity persisted across page loads. */\ninterface StoredIdentity {\n  userId?: string;\n  email?: string;\n  phone?: string;\n  name?: string;\n}\n\n/** Internal: localStorage shim that no-ops in SSR / private mode. */\nconst safeStorage = {\n  get(key: string): string | null {\n    try {\n      return typeof window !== \"undefined\" && window.localStorage\n        ? window.localStorage.getItem(key)\n        : null;\n    } catch {\n      return null;\n    }\n  },\n  set(key: string, value: string): void {\n    try {\n      if (typeof window !== \"undefined\" && window.localStorage) {\n        window.localStorage.setItem(key, value);\n      }\n    } catch {\n      /* private mode / quota — silently degrade */\n    }\n  },\n  remove(key: string): void {\n    try {\n      if (typeof window !== \"undefined\" && window.localStorage) {\n        window.localStorage.removeItem(key);\n      }\n    } catch {\n      /* ignore */\n    }\n  },\n};\n\n/**\n * Cryptographically-strong 32-hex-char id. We require\n * `crypto.getRandomValues` — every browser we target ships it. A weak\n * `Math.random()` id on a tracking SDK would let an attacker who observed\n * one anonymous id narrow the PRNG state and impersonate other visitors'\n * anonymous sessions, so we throw at init rather than ship a weak id.\n */\nfunction randomId(): string {\n  const c = (globalThis as { crypto?: Crypto }).crypto;\n  if (!c || typeof c.getRandomValues !== \"function\") {\n    throw new Error(\n      \"OrbitWebAnalytics requires crypto.getRandomValues — unsupported environment.\",\n    );\n  }\n  const bytes = new Uint8Array(16);\n  c.getRandomValues(bytes);\n  return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Resolves the canonical public write key from config, accepting the\n * `publicKey` alias. Throws when neither is set.\n *\n * @internal\n */\nfunction resolveWriteKey(config: {\n  writeKey?: string;\n  publicKey?: string;\n}): string {\n  if (config.writeKey && config.writeKey.length > 0) return config.writeKey;\n  if (config.publicKey && config.publicKey.length > 0) return config.publicKey;\n  throw new Error(\"OrbitWebAnalytics: writeKey is required\");\n}\n\n/**\n * Splits a Segment-style traits hash into the identity columns the\n * `/sdk/identify` endpoint resolves on (`email` / `phone` / `display_name`)\n * and the residual free-form traits. `name`, `display_name`, `first_name`\n * (in that order) seed the display name.\n *\n * @internal\n */\nfunction splitTraits(traits: OrbitAnalyticsTraits | undefined): {\n  email?: string;\n  phone?: string;\n  displayName?: string;\n  rest: Record<string, unknown>;\n} {\n  const rest: Record<string, unknown> = { ...(traits ?? {}) };\n  const email = typeof rest.email === \"string\" ? rest.email : undefined;\n  const phone = typeof rest.phone === \"string\" ? rest.phone : undefined;\n  const displayName =\n    (typeof rest.name === \"string\" ? rest.name : undefined) ??\n    (typeof rest.display_name === \"string\" ? rest.display_name : undefined) ??\n    (typeof rest.first_name === \"string\" ? rest.first_name : undefined);\n  delete rest.email;\n  delete rest.phone;\n  return { email, phone, displayName, rest };\n}\n\nexport class OrbitWebAnalytics {\n  private readonly apiUrl: string;\n  private readonly writeKey: string;\n  private readonly debug: boolean;\n  private readonly flushAt: number;\n  private readonly flushInterval: number;\n\n  private readonly anonId: string;\n  private identity: StoredIdentity | null;\n  private buffer: BufferedEvent[] = [];\n  private flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n  constructor(config: OrbitWebAnalyticsConfig) {\n    if (!config.apiUrl) {\n      throw new Error(\"OrbitWebAnalytics: apiUrl is required\");\n    }\n    if (config.flushInterval !== undefined && config.flushInterval < 0) {\n      throw new Error(\"OrbitWebAnalytics: flushInterval must be >= 0\");\n    }\n    if (config.flushAt !== undefined && config.flushAt < 1) {\n      throw new Error(\"OrbitWebAnalytics: flushAt must be >= 1\");\n    }\n    this.apiUrl = config.apiUrl.replace(/\\/+$/, \"\");\n    this.writeKey = resolveWriteKey(config);\n    this.debug = config.debug ?? false;\n    this.flushAt = Math.min(\n      config.flushAt ?? 20,\n      SERVER_MAX_EVENTS_PER_BATCH,\n    );\n    this.flushInterval = config.flushInterval ?? 2_000;\n\n    // Seed anonymousId: explicit config wins, else localStorage, else fresh.\n    if (typeof config.anonymousId === \"string\" && config.anonymousId) {\n      this.anonId = config.anonymousId;\n      safeStorage.set(ANON_ID_KEY, this.anonId);\n    } else if (config.anonymousId === null) {\n      this.anonId = randomId();\n      safeStorage.set(ANON_ID_KEY, this.anonId);\n    } else {\n      const existing = safeStorage.get(ANON_ID_KEY);\n      this.anonId = existing ?? randomId();\n      if (!existing) safeStorage.set(ANON_ID_KEY, this.anonId);\n    }\n\n    // Restore the last-known identity so reload-immediate events attribute\n    // correctly. Degrades gracefully on a malformed stored payload.\n    let identity: StoredIdentity | null = null;\n    try {\n      const raw = safeStorage.get(IDENTITY_KEY);\n      if (raw) identity = JSON.parse(raw) as StoredIdentity;\n    } catch {\n      identity = null;\n    }\n    this.identity = identity;\n\n    // Flush before navigation. `pagehide` is more reliable than\n    // `beforeunload` on iOS Safari + bfcache.\n    if (\n      !(config.skipLifecycleListeners ?? false) &&\n      typeof window !== \"undefined\" &&\n      typeof window.addEventListener === \"function\"\n    ) {\n      window.addEventListener(\"pagehide\", () => {\n        void this.flush(true);\n      });\n    }\n  }\n\n  /** Stable per-browser anonymous id (sticky across sessions until reset). */\n  get anonymousId(): string {\n    return this.anonId;\n  }\n\n  /** The currently-attached userId, when identified. */\n  get userId(): string | null {\n    return this.identity?.userId ?? null;\n  }\n\n  /**\n   * Attaches a user identity to subsequent events (Segment `identify`).\n   * Anonymous events captured before this call are server-merged onto the\n   * resolved contact by the `/sdk/identify` backfill.\n   *\n   * Mirrors analytics.js positional args: `identify(userId, traits)`,\n   * `identify(traits)`, or `identify(userId)`. The contact is resolved\n   * server-side from `traits.email` / `traits.phone`; when neither is\n   * present the identity is cached locally (so `userId` rides along on\n   * future events) but no contact upsert is issued.\n   */\n  async identify(\n    userIdOrTraits?: string | OrbitAnalyticsTraits,\n    maybeTraits?: OrbitAnalyticsTraits,\n  ): Promise<void> {\n    let userId: string | undefined;\n    let traits: OrbitAnalyticsTraits | undefined;\n    if (typeof userIdOrTraits === \"string\") {\n      userId = userIdOrTraits;\n      traits = maybeTraits;\n    } else {\n      traits = userIdOrTraits;\n    }\n\n    const { email, phone, displayName, rest } = splitTraits(traits);\n    this.identity = {\n      ...(userId !== undefined ? { userId } : {}),\n      ...(email !== undefined ? { email } : {}),\n      ...(phone !== undefined ? { phone } : {}),\n      ...(displayName !== undefined ? { name: displayName } : {}),\n    };\n    safeStorage.set(IDENTITY_KEY, JSON.stringify(this.identity));\n    this.log(\"identify\", userId ?? \"(anonymous traits)\");\n\n    // Without a resolvable contact key there is nothing to upsert — the\n    // identity is cached locally and rides along on subsequent events.\n    if (!email && !phone) return;\n\n    // Flush buffered anonymous events first so the server-side backfill\n    // (keyed on anonymousId) attaches them to the contact this call upserts.\n    await this.flush();\n    await this.post(\"/sdk/identify\", {\n      anonymous_id: this.anonId,\n      ...(email !== undefined ? { email } : {}),\n      ...(phone !== undefined ? { phone } : {}),\n      ...(displayName !== undefined ? { display_name: displayName } : {}),\n      ...(Object.keys(rest).length > 0 ? { traits: rest } : {}),\n    }).catch((err: unknown) => {\n      this.log(\"identify failed\", (err as Error)?.message);\n    });\n  }\n\n  /** Buffers a custom event (Segment `track`). Auto-flushes on size / timer. */\n  track(event: string, properties?: OrbitAnalyticsProperties): void {\n    if (typeof event !== \"string\" || event.length === 0) {\n      this.log(\"track called without an event name — ignoring\");\n      return;\n    }\n    this.enqueue(event, { ...(properties ?? {}) });\n  }\n\n  /**\n   * Buffers a pageview (Segment `page`). Accepts the analytics.js positional\n   * forms: `page()`, `page(name)`, `page(category, name)`, and\n   * `page(category, name, properties)`. The current URL / path / title /\n   * referrer are auto-collected.\n   */\n  page(\n    category?: string,\n    name?: string,\n    properties?: OrbitAnalyticsProperties,\n  ): void {\n    // Resolve the overloaded args: a single string is the page name.\n    let resolvedCategory: string | undefined = category;\n    let resolvedName: string | undefined = name;\n    if (name === undefined && properties === undefined) {\n      resolvedCategory = undefined;\n      resolvedName = category;\n    }\n\n    const pageProps: OrbitAnalyticsProperties = {\n      _type: \"page\",\n      ...(resolvedCategory !== undefined ? { category: resolvedCategory } : {}),\n      ...(resolvedName !== undefined ? { name: resolvedName } : {}),\n      ...(properties ?? {}),\n    };\n    if (typeof window !== \"undefined\") {\n      pageProps.url = pageProps.url ?? window.location.href;\n      pageProps.path = pageProps.path ?? window.location.pathname;\n    }\n    if (typeof document !== \"undefined\") {\n      pageProps.title = pageProps.title ?? document.title;\n      pageProps.referrer = pageProps.referrer ?? (document.referrer || null);\n    }\n    this.enqueue(resolvedName ? `Viewed ${resolvedName} Page` : \"Page Viewed\", pageProps);\n  }\n\n  /**\n   * Buffers a group association (Segment `group`). The CDP joins on\n   * contacts, not accounts, so the association is recorded as an event\n   * carrying `groupId` + traits for downstream account-level rollups.\n   */\n  group(groupId: string, traits?: OrbitAnalyticsProperties): void {\n    if (typeof groupId !== \"string\" || groupId.length === 0) {\n      this.log(\"group called without a groupId — ignoring\");\n      return;\n    }\n    this.enqueue(\"Group\", { _type: \"group\", groupId, ...(traits ?? {}) });\n  }\n\n  /**\n   * Clears the cached identity (Segment `reset` — explicit logout). The\n   * sticky anonymousId is preserved so the post-logout session stays a\n   * coherent anonymous funnel; pass `true` to also rotate it.\n   */\n  reset(rotateAnonymousId = false): void {\n    this.identity = null;\n    safeStorage.remove(IDENTITY_KEY);\n    if (rotateAnonymousId) {\n      // anonId is readonly for steady-state safety; rotation rewrites the\n      // persisted slot so the NEXT construction picks up a fresh id.\n      safeStorage.remove(ANON_ID_KEY);\n    }\n  }\n\n  /**\n   * Forces a flush of buffered events. Called on `pagehide` too.\n   *\n   * Transport tiers (audited `#SDK-005` — the key NEVER rides on the URL):\n   *   1. In-tab: a plain awaited `fetch` to `/sdk/track` with the key on\n   *      the `X-API-Key` header.\n   *   2. Unload (`beacon=true`): `fetch({ keepalive: true })` with the key\n   *      on a header — modern browsers ferry it across `pagehide`.\n   *   3. Unload fallback: `navigator.sendBeacon` to the key-less\n   *      `/sdk/track/anonymous` endpoint (no key on URL or body), which\n   *      stages events for identity-time reconciliation.\n   */\n  async flush(beacon = false): Promise<void> {\n    if (this.flushTimer) {\n      clearTimeout(this.flushTimer);\n      this.flushTimer = null;\n    }\n    if (this.buffer.length === 0) return;\n    const events = this.buffer;\n    this.buffer = [];\n\n    const body = {\n      anonymous_id: this.anonId,\n      identity: this.identityForWire(),\n      events,\n    };\n\n    if (beacon) {\n      const supportsFetchKeepalive =\n        typeof fetch === \"function\" &&\n        typeof Request === \"function\" &&\n        \"keepalive\" in Request.prototype;\n      if (supportsFetchKeepalive) {\n        try {\n          void fetch(`${this.apiUrl}/sdk/track`, {\n            method: \"POST\",\n            headers: {\n              \"Content-Type\": \"application/json\",\n              \"X-API-Key\": this.writeKey,\n            },\n            body: JSON.stringify(body),\n            keepalive: true,\n            credentials: \"omit\",\n          }).catch(() => {\n            /* unload-time failure — nothing we can do */\n          });\n          this.log(\"flushed via fetch keepalive\", events.length);\n          return;\n        } catch {\n          /* fall through to sendBeacon */\n        }\n      }\n      if (typeof navigator !== \"undefined\" && navigator.sendBeacon) {\n        try {\n          const blob = new Blob([JSON.stringify(body)], {\n            type: \"application/json\",\n          });\n          const ok = navigator.sendBeacon(\n            `${this.apiUrl}/sdk/track/anonymous`,\n            blob,\n          );\n          if (ok) {\n            this.log(\"flushed via sendBeacon (anonymous)\", events.length);\n            return;\n          }\n        } catch {\n          /* fall through to in-tab fetch */\n        }\n      }\n    }\n\n    await this.post(\"/sdk/track\", body).catch((err: unknown) => {\n      // Re-queue on transport failure so the next flush retries. Cap at\n      // 200 to bound memory under a long offline streak.\n      this.buffer = [...events.slice(-100), ...this.buffer].slice(-200);\n      this.log(\"flush failed — re-queued\", (err as Error)?.message);\n    });\n    this.log(\"flushed\", events.length);\n  }\n\n  /**\n   * Pushes an event onto the buffer (stamping the timestamp) and triggers\n   * a flush when the batch threshold is reached, else arms the timer.\n   *\n   * @internal\n   */\n  private enqueue(name: string, properties: OrbitAnalyticsProperties): void {\n    // Preserve the Segment userId inside properties for queryability — the\n    // public ingest surface has no first-class userId column.\n    if (this.identity?.userId && properties.userId === undefined) {\n      properties.userId = this.identity.userId;\n    }\n    this.buffer.push({\n      name: name.slice(0, SERVER_MAX_EVENT_NAME_CHARS),\n      properties,\n      timestamp: new Date().toISOString(),\n    });\n    this.log(\"enqueue\", name);\n\n    if (this.buffer.length >= this.flushAt) {\n      void this.flush();\n      return;\n    }\n    if (!this.flushTimer && this.flushInterval > 0) {\n      this.flushTimer = setTimeout(() => {\n        this.flushTimer = null;\n        void this.flush();\n      }, this.flushInterval);\n    }\n  }\n\n  /**\n   * The `{ email, phone }` identity the `/sdk/track` batch resolves a\n   * contact on, or `null` when the visitor is still anonymous.\n   *\n   * @internal\n   */\n  private identityForWire(): { email?: string; phone?: string } | null {\n    if (!this.identity) return null;\n    const { email, phone } = this.identity;\n    if (!email && !phone) return null;\n    return {\n      ...(email !== undefined ? { email } : {}),\n      ...(phone !== undefined ? { phone } : {}),\n    };\n  }\n\n  /**\n   * Header-authed JSON POST. Bounded by a 5s timeout so a hung request\n   * can't hold the buffer across navigations.\n   *\n   * @internal\n   */\n  private async post(path: string, body: unknown): Promise<unknown> {\n    const res = await fetch(`${this.apiUrl}${path}`, {\n      method: \"POST\",\n      headers: {\n        \"Content-Type\": \"application/json\",\n        \"X-API-Key\": this.writeKey,\n      },\n      body: JSON.stringify(body),\n      credentials: \"omit\",\n      signal: AbortSignal.timeout(5_000),\n    });\n    if (!res.ok) {\n      throw new Error(`Orbit ${path} failed (${res.status})`);\n    }\n    return res.json();\n  }\n\n  private log(...args: unknown[]): void {\n    if (this.debug && typeof console !== \"undefined\") {\n      // eslint-disable-next-line no-console\n      console.log(\"[OrbitWebAnalytics]\", ...args);\n    }\n  }\n}\n\n/** Test-only helpers — narrow surface kept off the public export list. */\nexport const __orbitWebAnalyticsTestables = {\n  resolveWriteKey,\n  splitTraits,\n  ANON_ID_KEY,\n  IDENTITY_KEY,\n  SERVER_MAX_EVENTS_PER_BATCH,\n  SERVER_MAX_EVENT_NAME_CHARS,\n};\n","/**\n * Segment-drop-in queue wrapper around `OrbitCdpAnalytics` (gap g2-deep-137,\n * 2026-05-29).\n *\n * The base `OrbitCdpAnalytics` client (cdpaas-002) ships a Segment-compatible\n * wire contract but fires each call synchronously over the network with no\n * buffering, no retry, no offline survival, and no anonymousId persistence.\n * That is fine for trusted edge / SSR surfaces that hold an ingest secret,\n * but it is NOT a usable drop-in replacement for `analytics.js` in a\n * browser — the moment the network blips or the user navigates away mid-\n * request the event is lost, and there is no sticky per-browser identifier\n * so anonymous funnels cannot be reconstructed across page loads.\n *\n * `OrbitCdpAnalyticsQueue` closes those gaps:\n *\n *   1. **Sticky `anonymousId`** — persisted to `localStorage` under\n *      `orbit_cdp_anonymous_id`. Reads on construction; lazily generates a\n *      cryptographically-strong 32-hex-char id via `crypto.getRandomValues`\n *      when the slot is empty. `getAnonymousId()` is the public accessor;\n *      `setAnonymousId(id)` overrides it (e.g. on alias / reset). Storage\n *      access is wrapped in try/catch so Safari private mode + SSR no-op.\n *\n *   2. **Batched flush** — `track / identify / page / screen / group /\n *      alias` calls enqueue an envelope onto an in-memory ring. The queue\n *      flushes via the server-side `/batch` endpoint when EITHER the\n *      buffer reaches `flushAt` events (default 20) OR `flushInterval`\n *      ms have elapsed since the last flush (default 5_000). Per-batch\n *      cap is 200 (server-side enforced; client splits at 200).\n *\n *   3. **Retry with exponential backoff** — transient failures (`fetch`\n *      reject, 5xx, 408, 429) re-enqueue the batch and retry up to\n *      `maxRetries` times (default 5) with jittered backoff\n *      (`baseRetryDelayMs * 2^attempt + rand(0..baseRetryDelayMs)`).\n *      4xx other than 408/429 are treated as terminal failures (caller\n *      bug, not network blip) and DROPPED — re-trying a 401 on a rotated\n *      secret would just thrash forever.\n *\n *   4. **Offline survival** — when `navigator.onLine === false`, flushes\n *      are deferred until the next `online` event. Events stay in the\n *      in-memory ring AND mirror to `localStorage` under\n *      `orbit_cdp_pending_events` so they survive page reload. The\n *      mirror is capped at `persistMax` events (default 500) — older\n *      events are dropped FIFO to keep storage under ~250KB.\n *\n *   5. **Best-effort unload flush** — on `pagehide` we fire a\n *      non-awaited `flush()` of the pending batch (see `flushOnUnload`,\n *      the single source of truth for this path). `navigator.sendBeacon`\n *      is deliberately NOT used: Beacon cannot set the custom\n *      `X-Orbit-CDP-Signature` header the `/batch` endpoint requires, and\n *      embedding the signature in the body would need a paired controller\n *      change that does not exist. Browsers MAY cancel the in-flight\n *      `fetch` when the tab is torn down, so this flush is opportunistic\n *      only. Durable delivery does NOT rely on it — events are mirrored to\n *      `localStorage` under `orbit_cdp_pending_events` (`PENDING_KEY`) and\n *      replayed on the NEXT construction, which is the real guarantee that\n *      no event is lost across a hard unload.\n *\n * Backward compatibility: the existing `OrbitCdpAnalytics` synchronous\n * surface is UNCHANGED. The queue is a new opt-in class that COMPOSES\n * over the base client. Existing call sites continue to work as before.\n *\n * @example\n * ```ts\n * import { OrbitCdpAnalyticsQueue } from \"@devotel/orbit-web-sdk\";\n *\n * const analytics = new OrbitCdpAnalyticsQueue({\n *   apiUrl: \"https://api.orbit.devotel.io\",\n *   ingestId: \"cdpIngestSecret_xxx\",\n *   ingestSecret: \"...\",       // ideally injected per-session by your backend\n * });\n *\n * // Drop-in for Segment's analytics.js — same surface, but resilient.\n * analytics.track({ event: \"Clicked CTA\", properties: { cta: \"buy-now\" } });\n * analytics.identify({ userId: \"u_42\", traits: { plan: \"pro\" } });\n *\n * // anonymousId is sticky across page loads.\n * console.log(analytics.getAnonymousId()); // → \"a1b2c3...\"\n * ```\n */\n\nimport {\n  OrbitCdpAnalytics,\n  type OrbitCdpAnalyticsConfig,\n  type CdpTrackInput,\n  type CdpIdentifyInput,\n  type CdpAliasInput,\n  type CdpPageInput,\n  type CdpScreenInput,\n  type CdpGroupInput,\n  type CdpBatchEnvelope,\n} from \"./cdp-analytics\";\n\n/** Storage key — persisted anonymousId (one slot per browser/origin). */\nconst ANON_ID_KEY = \"orbit_cdp_anonymous_id\";\n/** Storage key — buffered events that survive page reload + offline. */\nconst PENDING_KEY = \"orbit_cdp_pending_events\";\n\n/** Internal envelope wrapper — carries retry metadata alongside the payload. */\ninterface QueuedEnvelope {\n  /** Wire-shape envelope sent to `/batch`. */\n  payload: CdpBatchEnvelope;\n  /** Number of failed flush attempts so far. */\n  attempts: number;\n  /** Monotonic enqueue timestamp (ms epoch) — used for FIFO eviction. */\n  enqueuedAt: number;\n}\n\nexport interface OrbitCdpAnalyticsQueueConfig extends OrbitCdpAnalyticsConfig {\n  /**\n   * Batch size that triggers an immediate flush. Defaults to 20. Cap is\n   * 200 (server-side limit on `/batch`); values above 200 are clamped.\n   */\n  flushAt?: number;\n  /**\n   * Auto-flush interval in milliseconds. Defaults to 5_000. Set to 0 to\n   * disable the timer (batch-size-only flush). Negative values throw.\n   */\n  flushInterval?: number;\n  /**\n   * Max retry attempts per batch. Defaults to 5. Set to 0 to disable\n   * retry (dropped on first failure).\n   */\n  maxRetries?: number;\n  /**\n   * Base backoff delay in ms. Effective delay is\n   * `baseRetryDelayMs * 2^attempt + jitter(0..baseRetryDelayMs)`. Defaults\n   * to 500.\n   */\n  baseRetryDelayMs?: number;\n  /**\n   * Cap on the number of events mirrored to `localStorage` for offline\n   * survival. FIFO-evicted past this cap. Defaults to 500 (~250KB).\n   */\n  persistMax?: number;\n  /**\n   * Pre-existing anonymousId to seed the queue with (e.g. from\n   * server-side rendering). When set, overrides the localStorage value.\n   * Use `null` (explicit) to FORCE-RESET to a freshly-generated id.\n   */\n  anonymousId?: string | null;\n  /**\n   * When true, skip wiring `online` / `pagehide` listeners. Default\n   * false. Used by tests + non-browser runtimes that drive lifecycle\n   * events manually.\n   */\n  skipLifecycleListeners?: boolean;\n}\n\n/**\n * Queue / retry / persistence layer on top of `OrbitCdpAnalytics`.\n *\n * Wraps the base client without modifying it — composition keeps the\n * existing wire-contract tests green and keeps the secret-handling\n * surface area to one class.\n */\nexport class OrbitCdpAnalyticsQueue {\n  private readonly base: OrbitCdpAnalytics;\n  private readonly flushAt: number;\n  private readonly flushInterval: number;\n  private readonly maxRetries: number;\n  private readonly baseRetryDelayMs: number;\n  private readonly persistMax: number;\n  private readonly skipLifecycleListeners: boolean;\n\n  private anonId: string;\n  private queue: QueuedEnvelope[] = [];\n  private flushTimer: ReturnType<typeof setTimeout> | null = null;\n  private flushing = false;\n  private pendingFlush: Promise<void> | null = null;\n  /** Boolean lock — true after `pagehide` / `unload` has fired. */\n  private unloaded = false;\n  /** Bound listeners — kept to support `dispose()` cleanup. */\n  private readonly onlineListener: (() => void) | null;\n  private readonly pagehideListener: (() => void) | null;\n\n  constructor(config: OrbitCdpAnalyticsQueueConfig) {\n    this.base = new OrbitCdpAnalytics(config);\n\n    if (config.flushInterval !== undefined && config.flushInterval < 0) {\n      throw new Error(\"OrbitCdpAnalyticsQueue: flushInterval must be >= 0\");\n    }\n    if (config.flushAt !== undefined && config.flushAt < 1) {\n      throw new Error(\"OrbitCdpAnalyticsQueue: flushAt must be >= 1\");\n    }\n    this.flushAt = Math.min(config.flushAt ?? 20, 200);\n    this.flushInterval = config.flushInterval ?? 5_000;\n    this.maxRetries = config.maxRetries ?? 5;\n    this.baseRetryDelayMs = config.baseRetryDelayMs ?? 500;\n    this.persistMax = config.persistMax ?? 500;\n    this.skipLifecycleListeners = config.skipLifecycleListeners ?? false;\n\n    // Seed anonymousId: explicit config wins, else localStorage, else fresh.\n    if (typeof config.anonymousId === \"string\" && config.anonymousId) {\n      this.anonId = config.anonymousId;\n      safeStorageSet(ANON_ID_KEY, this.anonId);\n    } else if (config.anonymousId === null) {\n      this.anonId = generateAnonymousId();\n      safeStorageSet(ANON_ID_KEY, this.anonId);\n    } else {\n      const existing = safeStorageGet(ANON_ID_KEY);\n      this.anonId = existing ?? generateAnonymousId();\n      if (!existing) safeStorageSet(ANON_ID_KEY, this.anonId);\n    }\n\n    // Hydrate any events that were persisted on a prior page load.\n    const persisted = readPersistedEvents();\n    if (persisted.length > 0) {\n      this.queue.push(\n        ...persisted.map((p) => ({\n          payload: p,\n          attempts: 0,\n          enqueuedAt: Date.now(),\n        })),\n      );\n      // Don't flush synchronously — let the caller decide when. But\n      // schedule the timer so we drain in the background.\n      this.armFlushTimer();\n    }\n\n    // Wire lifecycle listeners — best-effort, no-op outside browsers.\n    if (\n      !this.skipLifecycleListeners &&\n      typeof window !== \"undefined\" &&\n      typeof window.addEventListener === \"function\"\n    ) {\n      this.onlineListener = () => {\n        // Network came back — try to drain everything queued.\n        void this.flush();\n      };\n      this.pagehideListener = () => {\n        this.unloaded = true;\n        this.flushOnUnload();\n      };\n      window.addEventListener(\"online\", this.onlineListener);\n      window.addEventListener(\"pagehide\", this.pagehideListener);\n    } else {\n      this.onlineListener = null;\n      this.pagehideListener = null;\n    }\n  }\n\n  /** Returns the sticky per-browser anonymous identifier. */\n  getAnonymousId(): string {\n    return this.anonId;\n  }\n\n  /**\n   * Overwrites the sticky anonymousId — call this on alias / reset\n   * flows so subsequent buffered events carry the new id.\n   */\n  setAnonymousId(id: string): void {\n    if (typeof id !== \"string\" || !id) {\n      throw new Error(\n        \"OrbitCdpAnalyticsQueue.setAnonymousId: id must be a non-empty string\",\n      );\n    }\n    this.anonId = id;\n    safeStorageSet(ANON_ID_KEY, id);\n  }\n\n  /** Clears the persisted anonymousId + generates a fresh one. */\n  resetAnonymousId(): string {\n    this.anonId = generateAnonymousId();\n    safeStorageSet(ANON_ID_KEY, this.anonId);\n    return this.anonId;\n  }\n\n  /** Enqueue a `track` event. Returns immediately; flushed in background. */\n  track(input: CdpTrackInput): void {\n    this.enqueue({ type: \"track\", ...this.withAnonId(input) });\n  }\n\n  /** Enqueue an `identify` event. */\n  identify(input: CdpIdentifyInput): void {\n    this.enqueue({ type: \"identify\", ...this.withAnonId(input) });\n  }\n\n  /** Enqueue a `page` event. */\n  page(input: CdpPageInput): void {\n    this.enqueue({ type: \"page\", ...this.withAnonId(input) });\n  }\n\n  /** Enqueue a `screen` event. */\n  screen(input: CdpScreenInput): void {\n    this.enqueue({ type: \"screen\", ...this.withAnonId(input) });\n  }\n\n  /** Enqueue a `group` event. */\n  group(input: CdpGroupInput): void {\n    this.enqueue({ type: \"group\", ...this.withAnonId(input) });\n  }\n\n  /** Enqueue an `alias` event. */\n  alias(input: CdpAliasInput): void {\n    this.enqueue({ type: \"alias\", ...input });\n  }\n\n  /**\n   * Force-drains the pending buffer. Returns a promise that resolves\n   * once the flush attempt completes (success or failure). Safe to call\n   * concurrently — overlapping calls share the same in-flight flush.\n   */\n  flush(): Promise<void> {\n    if (this.pendingFlush) return this.pendingFlush;\n    this.pendingFlush = this.doFlush().finally(() => {\n      this.pendingFlush = null;\n    });\n    return this.pendingFlush;\n  }\n\n  /**\n   * Returns the current queue size — useful for tests + debugging.\n   *\n   * @internal\n   */\n  _queueSize(): number {\n    return this.queue.length;\n  }\n\n  /**\n   * Releases the lifecycle listeners. Idempotent. Call this when\n   * destroying the analytics instance (SPA teardown, hot-reload).\n   */\n  dispose(): void {\n    if (this.flushTimer) {\n      clearTimeout(this.flushTimer);\n      this.flushTimer = null;\n    }\n    if (\n      typeof window !== \"undefined\" &&\n      typeof window.removeEventListener === \"function\"\n    ) {\n      if (this.onlineListener)\n        window.removeEventListener(\"online\", this.onlineListener);\n      if (this.pagehideListener)\n        window.removeEventListener(\"pagehide\", this.pagehideListener);\n    }\n  }\n\n  /**\n   * Stamps the queue-managed `anonymousId` onto inputs that didn't\n   * specify one. Explicit `userId` or explicit `anonymousId` wins —\n   * never overwrites the caller's intent.\n   *\n   * @internal\n   */\n  private withAnonId<T extends { userId?: string; anonymousId?: string }>(\n    input: T,\n  ): T {\n    if (input.userId || input.anonymousId) return input;\n    return { ...input, anonymousId: this.anonId };\n  }\n\n  /**\n   * Pushes an envelope into the in-memory ring + mirrors to localStorage,\n   * then triggers a flush if the threshold is reached.\n   *\n   * @internal\n   */\n  private enqueue(envelope: CdpBatchEnvelope): void {\n    this.queue.push({\n      payload: envelope,\n      attempts: 0,\n      enqueuedAt: Date.now(),\n    });\n    // FIFO-evict if we're past the persistence cap (in-memory + storage\n    // align with the same cap — otherwise a 10k-event flood would OOM).\n    if (this.queue.length > this.persistMax) {\n      this.queue.splice(0, this.queue.length - this.persistMax);\n    }\n    persistEvents(this.queue.map((q) => q.payload));\n\n    if (this.queue.length >= this.flushAt) {\n      void this.flush();\n      return;\n    }\n    this.armFlushTimer();\n  }\n\n  /**\n   * Arms the auto-flush timer if not already running and flushInterval\n   * is > 0. Idempotent — repeated calls during the timer window do\n   * nothing.\n   *\n   * @internal\n   */\n  private armFlushTimer(): void {\n    if (this.flushTimer || this.flushInterval <= 0) return;\n    this.flushTimer = setTimeout(() => {\n      this.flushTimer = null;\n      void this.flush();\n    }, this.flushInterval);\n  }\n\n  /**\n   * Actual flush implementation. Splits at 200-event boundary (server\n   * cap), signs via the base client, retries on transient failures.\n   *\n   * @internal\n   */\n  private async doFlush(): Promise<void> {\n    if (this.flushing) return;\n    if (this.queue.length === 0) return;\n    // Skip flush when offline — we'll get re-armed by the `online`\n    // listener once the network returns.\n    if (\n      typeof navigator !== \"undefined\" &&\n      navigator.onLine === false &&\n      !this.unloaded\n    ) {\n      return;\n    }\n\n    this.flushing = true;\n    try {\n      // Drain in 200-event chunks (server cap on /batch).\n      while (this.queue.length > 0) {\n        const chunk = this.queue.splice(0, 200);\n        persistEvents(this.queue.map((q) => q.payload));\n\n        const chunkAttempts = Math.max(...chunk.map((e) => e.attempts), 0);\n        try {\n          await this.base.batch({ batch: chunk.map((e) => e.payload) });\n          // Success — events stay drained; refresh the persisted mirror.\n          persistEvents(this.queue.map((q) => q.payload));\n        } catch (err) {\n          // Decide retry-vs-drop based on the error class.\n          const status = extractStatusCode(err);\n          const transient = isTransientStatus(status);\n\n          if (transient && chunkAttempts < this.maxRetries) {\n            // Re-enqueue the chunk at the head with bumped attempts,\n            // then wait a backoff window before the next pass.\n            const bumped = chunk.map((e) => ({\n              ...e,\n              attempts: e.attempts + 1,\n            }));\n            this.queue.unshift(...bumped);\n            persistEvents(this.queue.map((q) => q.payload));\n            const delay =\n              this.baseRetryDelayMs * Math.pow(2, chunkAttempts) +\n              Math.floor(Math.random() * this.baseRetryDelayMs);\n            await sleep(delay);\n            // Continue the while-loop — retry the chunk.\n          } else {\n            // Terminal failure — drop the chunk so the rest of the\n            // queue can drain. Don't re-throw: the queue API is\n            // fire-and-forget; surfacing here would crash the caller.\n            persistEvents(this.queue.map((q) => q.payload));\n            break;\n          }\n        }\n      }\n    } finally {\n      this.flushing = false;\n    }\n  }\n\n  /**\n   * Best-effort flush on `pagehide` / `beforeunload`. Triggers a\n   * non-awaited `flush()` — modern browsers honor in-flight fetches\n   * for a few seconds after pagehide which usually carries the batch\n   * to the server. Events that don't make it are still persisted in\n   * `localStorage` and will replay on the next page load (the queue\n   * hydrates from `PENDING_KEY` in the constructor).\n   *\n   * `navigator.sendBeacon` is NOT used here even though it's typical\n   * for unload flushes — Beacon cannot set the custom\n   * `X-Orbit-CDP-Signature` header the server requires, and embedding\n   * the signature in the body would force a paired controller change\n   * (out of scope for this atomic worker).\n   *\n   * @internal\n   */\n  private flushOnUnload(): void {\n    if (this.queue.length === 0) return;\n    // Fire-and-forget — we don't await on the unload path.\n    void this.flush();\n  }\n}\n\n/**\n * Cryptographically-strong 32-hex-char anonymous id. Requires\n * `crypto.getRandomValues` — every browser we target ships it.\n *\n * @internal\n */\nfunction generateAnonymousId(): string {\n  const c = (globalThis as { crypto?: Crypto }).crypto;\n  if (!c || typeof c.getRandomValues !== \"function\") {\n    throw new Error(\n      \"OrbitCdpAnalyticsQueue: crypto.getRandomValues unavailable — refusing to ship a weak anonymousId.\",\n    );\n  }\n  const bytes = new Uint8Array(16);\n  c.getRandomValues(bytes);\n  return Array.from(bytes, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * Reads a key from localStorage, swallowing exceptions (SSR + Safari\n * private mode + disabled-cookies all throw on access).\n *\n * @internal\n */\nfunction safeStorageGet(key: string): string | null {\n  try {\n    if (typeof window === \"undefined\" || !window.localStorage) return null;\n    return window.localStorage.getItem(key);\n  } catch {\n    return null;\n  }\n}\n\n/**\n * Writes a key to localStorage, swallowing exceptions.\n *\n * @internal\n */\nfunction safeStorageSet(key: string, value: string): void {\n  try {\n    if (typeof window === \"undefined\" || !window.localStorage) return;\n    window.localStorage.setItem(key, value);\n  } catch {\n    /* silent degrade */\n  }\n}\n\n/**\n * Hydrates the pending-events array from localStorage on startup.\n *\n * @internal\n */\nfunction readPersistedEvents(): CdpBatchEnvelope[] {\n  const raw = safeStorageGet(PENDING_KEY);\n  if (!raw) return [];\n  try {\n    const parsed = JSON.parse(raw) as unknown;\n    if (!Array.isArray(parsed)) return [];\n    // Trust the wire-shape — server-side validation will reject malformed\n    // envelopes anyway.\n    return parsed as CdpBatchEnvelope[];\n  } catch {\n    return [];\n  }\n}\n\n/**\n * Mirrors the current queue to localStorage so it survives page reload.\n * No-op on storage errors (quota exceeded, private mode, …).\n *\n * @internal\n */\nfunction persistEvents(envelopes: CdpBatchEnvelope[]): void {\n  try {\n    if (envelopes.length === 0) {\n      safeStorageRemove(PENDING_KEY);\n      return;\n    }\n    safeStorageSet(PENDING_KEY, JSON.stringify(envelopes));\n  } catch {\n    /* quota / serialization error — accept loss */\n  }\n}\n\n/**\n * Removes a key from localStorage, swallowing exceptions.\n *\n * @internal\n */\nfunction safeStorageRemove(key: string): void {\n  try {\n    if (typeof window === \"undefined\" || !window.localStorage) return;\n    window.localStorage.removeItem(key);\n  } catch {\n    /* silent degrade */\n  }\n}\n\n/**\n * Async sleep helper for backoff between retry attempts.\n *\n * @internal\n */\nfunction sleep(ms: number): Promise<void> {\n  return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Tries to pull an HTTP status code off a thrown Error. The base client\n * throws `new Error(\"CDP ingest /<leg> failed: <status> <code> <msg>\")` —\n * parse the leading status off the message. Falls back to undefined for\n * fetch-level rejections (no status at all = transient by default).\n *\n * @internal\n */\nfunction extractStatusCode(err: unknown): number | undefined {\n  if (!(err instanceof Error)) return undefined;\n  const match = err.message.match(/failed:\\s*(\\d{3})/);\n  if (!match) return undefined;\n  return Number.parseInt(match[1]!, 10);\n}\n\n/**\n * Returns true if the error class is retry-worthy (fetch reject, 5xx,\n * 408, 429). 4xx other than 408/429 is a caller bug — retrying would\n * thrash forever on a rotated secret or malformed payload.\n *\n * @internal\n */\nfunction isTransientStatus(status: number | undefined): boolean {\n  if (status === undefined) return true;\n  if (status >= 500 && status <= 599) return true;\n  if (status === 408 || status === 429) return true;\n  return false;\n}\n\n/** Test-only helpers — narrow surface kept off the public export list. */\nexport const __orbitCdpAnalyticsQueueTestables = {\n  generateAnonymousId,\n  isTransientStatus,\n  extractStatusCode,\n  readPersistedEvents,\n  persistEvents,\n  ANON_ID_KEY,\n  PENDING_KEY,\n};\n"],"mappings":"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","names":["DEFAULT_BASE_URL","ANON_ID_KEY"]}