@coventia/softphone 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/{call-BTYlT2SU.d.cts → call-DUVnDxsz.d.cts} +152 -5
  2. package/dist/{call-BTYlT2SU.d.ts → call-DUVnDxsz.d.ts} +152 -5
  3. package/dist/{chunk-N2N2EQQJ.js → chunk-7R6LOOAI.js} +4 -2
  4. package/dist/chunk-7R6LOOAI.js.map +1 -0
  5. package/dist/{chunk-3V2L6UTK.js → chunk-LMJXX4YM.js} +17 -3
  6. package/dist/chunk-LMJXX4YM.js.map +1 -0
  7. package/dist/{chunk-5BGEHMPP.js → chunk-YWI6EQYS.js} +40 -7
  8. package/dist/chunk-YWI6EQYS.js.map +1 -0
  9. package/dist/element/index.d.ts +154 -5
  10. package/dist/element/index.js +512 -152
  11. package/dist/element/index.js.map +1 -1
  12. package/dist/{engine-D9nbeY08.d.cts → engine-CDjkrXHY.d.cts} +1 -1
  13. package/dist/{engine-Doa6Brkm.d.ts → engine-EPCKf-Bl.d.ts} +1 -1
  14. package/dist/index.cjs +396 -14
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +5 -5
  17. package/dist/index.d.ts +5 -5
  18. package/dist/index.js +256 -8
  19. package/dist/index.js.map +1 -1
  20. package/dist/react/index.cjs +15 -0
  21. package/dist/react/index.cjs.map +1 -1
  22. package/dist/react/index.d.cts +14 -3
  23. package/dist/react/index.d.ts +14 -3
  24. package/dist/react/index.js +2 -2
  25. package/dist/{softphone-DhuhcKCy.d.cts → softphone-BtdgZWkm.d.ts} +17 -2
  26. package/dist/{softphone-DHmhotbf.d.ts → softphone-DHG9MTNg.d.cts} +17 -2
  27. package/dist/softphone.css +395 -1
  28. package/dist/{sources-N4BOEDRL.js → sources-5L2R4HTV.js} +104 -5
  29. package/dist/sources-5L2R4HTV.js.map +1 -0
  30. package/dist/testing/index.cjs +41 -5
  31. package/dist/testing/index.cjs.map +1 -1
  32. package/dist/testing/index.d.cts +8 -2
  33. package/dist/testing/index.d.ts +8 -2
  34. package/dist/testing/index.js +41 -5
  35. package/dist/testing/index.js.map +1 -1
  36. package/dist/ui/index.cjs +490 -139
  37. package/dist/ui/index.cjs.map +1 -1
  38. package/dist/ui/index.d.cts +2 -2
  39. package/dist/ui/index.d.ts +2 -2
  40. package/dist/ui/index.js +478 -141
  41. package/dist/ui/index.js.map +1 -1
  42. package/dist/zadarma/index.cjs +38 -5
  43. package/dist/zadarma/index.cjs.map +1 -1
  44. package/dist/zadarma/index.d.cts +2 -2
  45. package/dist/zadarma/index.d.ts +2 -2
  46. package/dist/zadarma/index.js +1 -1
  47. package/dist/zadarma-engine-4VOPIBXG.js +5 -0
  48. package/dist/{zadarma-engine-DN2SZSQ3.js.map → zadarma-engine-4VOPIBXG.js.map} +1 -1
  49. package/package.json +1 -1
  50. package/dist/chunk-3V2L6UTK.js.map +0 -1
  51. package/dist/chunk-5BGEHMPP.js.map +0 -1
  52. package/dist/chunk-N2N2EQQJ.js.map +0 -1
  53. package/dist/sources-N4BOEDRL.js.map +0 -1
  54. package/dist/zadarma-engine-DN2SZSQ3.js +0 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/sources.ts","../src/core/token-cache.ts","../src/coventia/api.ts","../src/coventia/sources.ts","../src/core/errors.ts","../src/transports/sip/media.ts","../src/transports/sip/ice.ts","../src/transports/sip/session.ts","../src/transports/sip/ua.ts","../src/transports/sip/real-factory.ts","../src/transports/providers/zadarma.ts","../src/transports/providers/zadarma-browser.ts","../src/transports/zadarma-engine.ts","../src/core/emitter.ts","../src/core/events.ts","../src/core/call.ts","../src/core/call-impl.ts","../src/core/backend.ts","../src/core/softphone-impl.ts","../src/core/env.ts","../src/core/tabs.ts","../src/core/default-provider.ts","../src/core/create.ts","../src/index.ts"],"names":["entries","init_sources","ERROR_CATALOGUE","track","SoftphoneEvent","CallEvent","createZadarmaEngine"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * **Internal.** Not exported from any entry point.\r\n *\r\n * The rules that are about **phone numbers and names** rather than about any backend: universal,\r\n * easy to get subtly wrong, and worth getting right once.\r\n *\r\n * These were public while a host had to build their own sources. They no longer do — a provider\r\n * supplies `coventia.contacts` and everything else (caller ID, history names) is derived from it in\r\n * `coventia/sources.ts`, the only caller left. Exported API is a contract owed forever, so it should\r\n * not outlive its consumers.\r\n */\r\nimport type { Contact } from './core/types.js';\r\nimport type { CallHistoryEntry, HistorySource } from './ui/types.js';\r\n\r\n/**\r\n * A number reduced to the digits telephony actually matches on — `'+34 600 111 222'` → `'34600111222'`.\r\n *\r\n * Comparing numbers as raw strings is the bug this exists to prevent: the same line arrives as\r\n * `+34600111222` from a SIP header, `34600111222` from a database, and `+34 600 111 222` from a human,\r\n * and all three are one contact.\r\n */\r\nexport function toDigits(number: string): string {\r\n return String(number ?? '').replace(/\\D/g, '');\r\n}\r\n\r\n/**\r\n * Decide whether a resolved string is really a **name** — the \"don't echo the bare number\" rule.\r\n *\r\n * A contact saved as its own number (\"+34 600 111 222\") resolves to a \"name\" that is just the number\r\n * reformatted. Showing it puts `+34600111222` where a name belongs, or worse, a *differently*\r\n * formatted copy of the number the widget is already displaying an inch away.\r\n *\r\n * This is the SDK deciding what it is willing to call a name, so it holds for every host — not only\r\n * for a backend that happens to be careful. Returns `null` when there is no name worth showing, which\r\n * is the signal to keep displaying the number.\r\n */\r\nexport function normalizeResolvedName(raw: string | null | undefined, number: string): string | null {\r\n if (!raw) return null;\r\n const trimmed = String(raw).trim();\r\n if (!trimmed) return null;\r\n const digits = toDigits(number);\r\n if (!digits) return trimmed;\r\n return toDigits(trimmed) === digits ? null : trimmed;\r\n}\r\n\r\n/**\r\n * Wrap a `HistorySource` so its entries get caller names, resolved once per distinct number.\r\n *\r\n * A call log is the same handful of numbers over and over, so the naive \"resolve each row\" costs a\r\n * request per row and hammers the backend with duplicates. This dedupes **by digits** (see\r\n * `toDigits` — `+34600111222` and `34600111222` are one lookup), caches across pages, and remembers\r\n * misses so an unknown number is not re-asked forever.\r\n *\r\n * A failed lookup leaves the entry's `name` null and is NOT cached, so a later page retries: the\r\n * number is still callable, and an unreachable name service must not empty the tab.\r\n *\r\n * const history = withNameResolution(myHistorySource, myResolveContact);\r\n */\r\nexport function withNameResolution(\r\n source: HistorySource,\r\n resolve: (number: string, signal?: AbortSignal) => Promise<Contact | null>,\r\n): HistorySource {\r\n const cache = new Map<string, string | null>();\r\n\r\n return async (params) => {\r\n const entries = await source(params);\r\n\r\n // Only entries that still NEED a name. A source that already knows one (its own join, its own\r\n // cache) must not cost a lookup — the numbers it named are not our business.\r\n const wanted = [\r\n ...new Set(entries.filter((e) => e.name === null).map((e) => toDigits(e.number))),\r\n ].filter((d) => d !== '' && !cache.has(d));\r\n await Promise.all(\r\n wanted.map(async (digits) => {\r\n try {\r\n const contact = await resolve(digits);\r\n cache.set(digits, normalizeResolvedName(contact?.name ?? null, digits));\r\n } catch {\r\n // Deliberately not cached — a transient failure must not become a permanent blank.\r\n }\r\n }),\r\n );\r\n\r\n return entries.map((e): CallHistoryEntry => {\r\n // Never overwrite a name the source already knew.\r\n if (e.name !== null) return e;\r\n const resolved = cache.get(toDigits(e.number)) ?? null;\r\n return resolved === null ? e : { ...e, name: resolved };\r\n });\r\n };\r\n}\r\n","import type { TokenSource, VoiceToken } from './types.js';\r\n\r\n/**\r\n * Hold a voice token until it is nearly expired, instead of minting one per request.\r\n *\r\n * **The SDK used to call `getToken()` before every single request** — the WebRTC key fetch, contacts,\r\n * history, and once per distinct number when resolving caller names. For the CRM that is nearly free,\r\n * because its `TokenSource` reads an in-memory Supabase session. For a partner it is not: every call\r\n * is a round trip to *their* backend, which mints a fresh token from ours. Opening the history tab\r\n * with ten distinct numbers cost ten tokens, each then sitting in Redis for its full 15 minutes.\r\n *\r\n * It also meant the short TTL bought nothing. A credential you never hold cannot expire, so\r\n * \"short-lived\" was a property of the design and not of anything that happened.\r\n *\r\n * Two behaviours make this safe rather than merely cheaper:\r\n *\r\n * - **Coalescing.** Concurrent callers on a cold cache share ONE `getToken()` call. Without it the\r\n * widget's first paint — key + contacts + history at once — still minted three.\r\n * - **Invalidation.** A 401 drops the entry so the next attempt re-mints. Otherwise a revoked token\r\n * would be retried until its TTL ran out, and `revokeVoiceTokensForUser` would not actually\r\n * revoke anything a running softphone does.\r\n */\r\n\r\n/** Renew this far ahead of expiry — covers clock skew and a request already in flight. */\r\nconst SKEW_MS = 60_000;\r\n\r\ninterface Entry {\r\n token: VoiceToken | null;\r\n inflight: Promise<VoiceToken> | null;\r\n}\r\n\r\n/**\r\n * Keyed by the HOST's source object, not by the wrapper.\r\n *\r\n * A host passes the same `TokenSource` to `createSoftphone` and to `createZadarmaEngine`, so both are\r\n * wrapped independently — keying on the inner object is what makes them share one token instead of\r\n * holding two. Weak, so a discarded softphone's entry goes with it.\r\n */\r\nconst entries = new WeakMap<TokenSource, Entry>();\r\nconst wrappers = new WeakMap<TokenSource, TokenSource>();\r\n\r\nconst isFresh = (token: VoiceToken | null): token is VoiceToken =>\r\n token !== null && token.expiresAt.getTime() - Date.now() > SKEW_MS;\r\n\r\n/**\r\n * Wrap a host's `TokenSource` so its token is reused until nearly expired.\r\n *\r\n * Idempotent: wrapping the same source twice returns the same wrapper, so the two entry points into\r\n * the SDK cannot end up with a cache each.\r\n */\r\nexport function cachedTokenSource(inner: TokenSource): TokenSource {\r\n const existing = wrappers.get(inner);\r\n if (existing) return existing;\r\n\r\n const entry: Entry = { token: null, inflight: null };\r\n\r\n const wrapper: TokenSource = {\r\n async getToken(signal?: AbortSignal): Promise<VoiceToken> {\r\n if (isFresh(entry.token)) return entry.token;\r\n // Someone is already fetching — wait for theirs rather than starting a second. Explicit\r\n // null-check, not truthiness: a pending promise is always truthy, and reading this as one\r\n // invites the mistake of thinking it tests the resolved value.\r\n if (entry.inflight !== null) return entry.inflight;\r\n\r\n entry.inflight = (async () => {\r\n try {\r\n const token = await inner.getToken(signal);\r\n entry.token = token;\r\n return token;\r\n } finally {\r\n // Cleared even on failure, or one rejected fetch would wedge every later caller.\r\n entry.inflight = null;\r\n }\r\n })();\r\n return entry.inflight;\r\n },\r\n };\r\n\r\n // Registered under BOTH, pointing at the same entry: callers hold the wrapper and only the SDK\r\n // holds the inner source, so invalidation has to work from either side.\r\n entries.set(inner, entry);\r\n entries.set(wrapper, entry);\r\n wrappers.set(inner, wrapper);\r\n return wrapper;\r\n}\r\n\r\n/**\r\n * Drop the cached token for a source, so the next call re-mints.\r\n *\r\n * Called when the backend rejects it — a 401 means the token is gone (expired early, or revoked),\r\n * and holding it until its nominal expiry would leave the softphone broken for up to 15 minutes.\r\n *\r\n * Safe to call with an unwrapped source, or one that was never cached.\r\n */\r\nexport function invalidateToken(source: TokenSource): void {\r\n const entry = entries.get(source);\r\n if (!entry) return;\r\n entry.token = null;\r\n}\r\n","import { invalidateToken } from '../core/token-cache.js';\r\nimport type { TokenSource } from '../core/types.js';\r\n\r\n/** A failed backend call. `status` is the HTTP status, or 0 when the request never completed. */\r\nexport class ApiError extends Error {\r\n readonly status: number;\r\n\r\n constructor(status: number, message: string) {\r\n super(message);\r\n this.name = 'ApiError';\r\n this.status = status;\r\n }\r\n}\r\n\r\nexport type QueryParams = Readonly<Record<string, string | number | undefined>>;\r\n\r\n/**\r\n * A minimal authenticated GET against Coventia's phone service.\r\n *\r\n * Lives under `coventia/` rather than `core/`: the core ships no HTTP at all, so a partner on their\r\n * own backend never loads this.\r\n */\r\nexport function createGet(config: {\r\n readonly baseUrl: string;\r\n readonly tokenSource: TokenSource;\r\n readonly fetch?: typeof globalThis.fetch;\r\n}) {\r\n // Bound, not passed by reference: an unbound `fetch` throws \"Illegal invocation\" in some browsers.\r\n const doFetch: typeof globalThis.fetch = config.fetch\r\n ? config.fetch\r\n : (...args) => globalThis.fetch(...args);\r\n const base = config.baseUrl.replace(/\\/+$/, '');\r\n\r\n /**\r\n * One attempt. Split out so a 401 can be retried exactly once with a fresh token — see `get`.\r\n */\r\n async function attempt<T>(path: string, params: QueryParams, signal?: AbortSignal): Promise<T> {\r\n const { token } = await config.tokenSource.getToken(signal);\r\n const qs = new URLSearchParams();\r\n for (const [k, v] of Object.entries(params)) {\r\n if (v !== undefined && v !== '') qs.set(k, String(v));\r\n }\r\n const query = qs.toString();\r\n\r\n const res = await doFetch(`${base}${path}${query ? `?${query}` : ''}`, {\r\n // The RAW token, no `Bearer ` prefix — see `VoiceToken.token` (SPEC §6.1).\r\n headers: { Authorization: token },\r\n ...(signal ? { signal } : {}),\r\n });\r\n\r\n const body: unknown = await res.json().catch(() => null);\r\n const message = (body as { message?: string } | null)?.message ?? `${path} — HTTP ${res.status}`;\r\n\r\n if (!res.ok) throw new ApiError(res.status, message);\r\n // This backend answers 200 with `{ success: false }` on handled failures, so the status alone is\r\n // not enough to know a call worked.\r\n if ((body as { success?: boolean } | null)?.success === false) {\r\n throw new ApiError(res.status, message);\r\n }\r\n return body as T;\r\n }\r\n\r\n /**\r\n * Retry once on 401, with a freshly minted token.\r\n *\r\n * A cached token can be rejected before its nominal expiry — revoked on logout, or invalidated when\r\n * the extension is re-provisioned. Without this the cache would keep handing back the dead one for\r\n * up to its full TTL, so `revokeVoiceTokensForUser` would not actually stop a running softphone.\r\n *\r\n * Exactly one retry: a second 401 means the credential itself is refused, not stale, and retrying\r\n * would be a loop against the host's token endpoint.\r\n */\r\n return async function get<T>(path: string, params: QueryParams = {}, signal?: AbortSignal): Promise<T> {\r\n try {\r\n return await attempt<T>(path, params, signal);\r\n } catch (error) {\r\n if (!(error instanceof ApiError) || error.status !== 401) throw error;\r\n invalidateToken(config.tokenSource);\r\n return attempt<T>(path, params, signal);\r\n }\r\n };\r\n}\r\n","import { normalizeResolvedName, toDigits, withNameResolution } from '../sources.js';\r\nimport { createGet } from './api.js';\r\nimport type { Contact, TokenSource } from '../core/types.js';\r\nimport type {\r\n AgentsDirectory,\r\n CallDetail,\r\n CallHandler,\r\n CallHistoryEntry,\r\n CallOutcome,\r\n ContactsPage,\r\n ContactsSource,\r\n HistorySource,\r\n} from '../ui/types.js';\r\n\r\nexport interface CoventiaSourcesConfig {\r\n /** Coventia backend base URL, e.g. `https://api.coventia.es`. */\r\n readonly baseUrl: string;\r\n readonly tokenSource: TokenSource;\r\n /**\r\n * Only for session-authenticated hosts. A voice token carries the organization in its claim and\r\n * the backend pins it from there, so a partner omits this and cannot point the SDK at a tenant\r\n * their token does not cover. See `CoventiaBackendConfig.organizationId`.\r\n */\r\n readonly organizationId?: string;\r\n /**\r\n * Where the phone routes live. `/phone` today; `/api/v1/phone` once the partner API ships (§6.6),\r\n * at which point only this string changes.\r\n */\r\n readonly basePath?: string;\r\n /**\r\n * **The one thing a provider implements.** Their contact list, searched on their own backend.\r\n *\r\n * Supplying this is enough for everything: it drives the Contactos tab, and caller ID is DERIVED\r\n * from it by searching for the incoming number (see `resolverFromContacts`). One source means the\r\n * Contactos tab, a live call and a history row can never disagree about who a number belongs to —\r\n * which they did when the list and the lookup were separate overrides.\r\n *\r\n * The matching happens on their backend, where the CRM is: we send the number as `search` and take\r\n * an exact digit match from the results.\r\n */\r\n readonly contacts?: ContactsSource;\r\n\r\n /**\r\n * Caller ID, only if searching the list is not how their CRM answers \"who is this number\".\r\n *\r\n * Rarely needed — `contacts` already derives it. Worth setting when a dedicated lookup is cheaper\r\n * than a search, or when the number lives on a record that search does not return.\r\n */\r\n readonly resolveContact?: (number: string) => Promise<Contact | null>;\r\n readonly fetch?: typeof globalThis.fetch;\r\n}\r\n\r\nexport interface CoventiaSources {\r\n /** The Contactos tab. A partner may replace this with their own; everything else cannot be. */\r\n readonly contacts: ContactsSource;\r\n /** The Historial tab. **Coventia's, always** — we store the calls and the recordings. */\r\n readonly history: HistorySource;\r\n /** Caller ID for an incoming number, for `SoftphoneConfig.resolveContact`. */\r\n readonly resolveContact: (number: string) => Promise<Contact | null>;\r\n /** The Historial tab's \"filter by person\" directory. Always Coventia's own org membership. */\r\n readonly listAgents: () => Promise<AgentsDirectory>;\r\n}\r\n\r\n/** What `/agents` returns. */\r\ninterface AgentRow {\r\n readonly user_id: string;\r\n readonly name: string | null;\r\n}\r\n\r\n/** What `/webrtc/contacts` returns. **The only place `wa_id` is named in the whole SDK.** */\r\ninterface WhatsAppContactRow {\r\n readonly wa_id: string;\r\n readonly name: string | null;\r\n}\r\n\r\n/**\r\n * `wa_id` → `Contact`.\r\n *\r\n * `id` IS the `wa_id`, and that leaks nothing: the backend serves callable contacts only\r\n * (`wa_id: /^[0-9]+$/`), so the value is literally the phone digits and carries nothing `phone` does\r\n * not. What the boundary removes is the SHAPE — no `custom_username`/`username` coalescing, no\r\n * WhatsApp field names past this function.\r\n */\r\nconst toContact = (row: WhatsAppContactRow): Contact => ({\r\n id: row.wa_id,\r\n name: row.name,\r\n phone: `+${row.wa_id}`,\r\n});\r\n\r\n/** One hop in `handled_by_chain`, as Mongo stores it — no `name` (see `HandledByChainRow`'s own note). */\r\ninterface HandledByChainRow {\r\n readonly ext?: string;\r\n readonly user_id?: string | null;\r\n}\r\n\r\n/** The same hop, enriched by `GET /calls/:id` with a resolved display name. */\r\ninterface EnrichedHandledByChainRow extends HandledByChainRow {\r\n readonly name?: string | null;\r\n}\r\n\r\n/** A row from `/calls`. The backend spreads raw Mongo docs, so every read below is defensive. */\r\ninterface CallRow {\r\n readonly _id?: string;\r\n /** The PBX's vocabulary: `incoming` | `outgoing`. NOT the SDK's `inbound`/`outbound`. */\r\n readonly direction?: string;\r\n readonly caller_id?: string;\r\n readonly called_did?: string;\r\n readonly destination?: string;\r\n readonly disposition?: string;\r\n readonly duration?: number;\r\n readonly created_at?: string | number;\r\n readonly recording_count?: number;\r\n readonly handled_by_chain?: readonly HandledByChainRow[];\r\n /** The transfer route as bare extensions — populated even when `handled_by_chain` isn't (older\r\n * docs, written before the chain existed). */\r\n readonly extensions?: readonly string[];\r\n readonly internal?: string;\r\n readonly last_internal?: string;\r\n}\r\n\r\nconst toDirection = (raw: string | undefined): 'inbound' | 'outbound' =>\r\n raw === 'outgoing' ? 'outbound' : 'inbound';\r\n\r\n/** Which field holds the other party depends on the direction. */\r\nconst otherParty = (row: CallRow): string =>\r\n toDirection(row.direction) === 'outbound'\r\n ? (row.called_did ?? row.destination ?? '')\r\n : (row.caller_id ?? '');\r\n\r\n/**\r\n * Who handled the call, in order. Prefers the real chain (`ext` + resolved `user_id`); falls back to\r\n * bare extensions ONLY when there is no chain at all — a doc written before `handled_by_chain` existed.\r\n * Unifies both shapes into one `CallHandler[]` so the UI never has to know which source it came from:\r\n * a bare extension is just a handler with `userId: null, name: null`.\r\n */\r\nfunction toHandledBy(row: CallRow, chain: readonly EnrichedHandledByChainRow[] = row.handled_by_chain ?? []): readonly CallHandler[] {\r\n if (chain.length > 0) {\r\n return chain\r\n .filter((h): h is EnrichedHandledByChainRow & { ext: string } => Boolean(h?.ext))\r\n .map((h) => ({ extension: h.ext, userId: h.user_id ?? null, name: h.name ?? null }));\r\n }\r\n const bare = row.extensions?.length ? row.extensions : [row.internal, row.last_internal];\r\n return [...new Set(bare.filter((e): e is string => Boolean(e)))].map((extension) => ({\r\n extension,\r\n userId: null,\r\n name: null,\r\n }));\r\n}\r\n\r\n/**\r\n * \"Not answered\" is ANY non-empty disposition other than `answered` — `no answer`, `busy`, `cancel`,\r\n * `failed`, `congestion`, and whatever Zadarma adds next.\r\n *\r\n * Open-ended rather than an allow-list, so an unrecognised value surfaces under \"Perdidas\" instead of\r\n * vanishing. **An EMPTY disposition counts as ANSWERED**, so a call of unknown status is not wrongly\r\n * flagged. Both halves are load-bearing.\r\n */\r\nconst isMissed = (row: CallRow): boolean => {\r\n const d = String(row.disposition ?? '').trim().toLowerCase();\r\n return d !== '' && d !== 'answered';\r\n};\r\n\r\n/**\r\n * The PBX's own vocabulary translated to the SDK's, for a call `isMissed`. Never returns Zadarma's\r\n * raw string — an unrecognised or future value falls into `'unavailable'` rather than leaking through\r\n * or being dropped, same open-ended reasoning `isMissed` itself documents.\r\n */\r\nconst toOutcome = (row: CallRow): CallOutcome => {\r\n switch (String(row.disposition ?? '').trim().toLowerCase()) {\r\n case 'no answer':\r\n return 'no-answer';\r\n case 'busy':\r\n return 'busy';\r\n case 'cancel':\r\n return 'cancelled';\r\n case 'failed':\r\n return 'failed';\r\n default:\r\n return 'unavailable';\r\n }\r\n};\r\n\r\n/**\r\n * Caller ID, derived from a contacts source: ask it for the number and take an exact match.\r\n *\r\n * This is why a provider only has to implement ONE function. Their search endpoint already knows how\r\n * to match a phone number — it is the same question the Contactos tab asks, with a different query —\r\n * and doing it on their backend is where the CRM actually lives.\r\n *\r\n * **Exact digit match only.** A search for a number can return partial or fuzzy hits, and naming a\r\n * caller after a near-miss is worse than showing the bare number: the user trusts the name.\r\n * `toDigits` on both sides so `+34 600 111 222` and `34600111222` compare equal.\r\n */\r\nexport function resolverFromContacts(\r\n contacts: ContactsSource,\r\n): (number: string) => Promise<Contact | null> {\r\n return async (number) => {\r\n const digits = toDigits(number);\r\n if (!digits) return null;\r\n // The number as the PBX reported it — their backend has the most to work with that way. `limit`\r\n // is small: we want one exact hit, not a page of near misses.\r\n const page = await contacts({ search: number, skip: 0, limit: 10 });\r\n return page.items.find((c) => toDigits(c.phone) === digits) ?? null;\r\n };\r\n}\r\n\r\nconst toDate = (raw: string | number | undefined): Date => {\r\n if (raw === undefined) return new Date(0);\r\n const d = new Date(raw);\r\n return Number.isNaN(d.getTime()) ? new Date(0) : d;\r\n};\r\n\r\n/**\r\n * Coventia's phone service, as the sources the widget asks for.\r\n *\r\n * **This is the anti-corruption layer.** The backend speaks WhatsApp's data model; the SDK speaks\r\n * `Contact` and `CallHistoryEntry`. The translation happens here and nowhere else — enforced by\r\n * structure, since `core/` cannot reach `coventia/` and a partner never imports it.\r\n */\r\nexport function coventiaSources(config: CoventiaSourcesConfig): CoventiaSources {\r\n const get = createGet(config);\r\n const base = config.basePath ?? '/phone';\r\n // Absent for a voice token, which already names the organization. Spread into every query below,\r\n // so omitting it removes the parameter entirely rather than sending `organization_id=undefined`.\r\n const org = config.organizationId ? { organization_id: config.organizationId } : {};\r\n\r\n const coventiaContacts: ContactsSource = async ({ search, skip, limit }): Promise<ContactsPage> => {\r\n const body = await get<{ contacts?: readonly WhatsAppContactRow[]; total?: number }>(\r\n `${base}/webrtc/contacts`,\r\n { ...org, search, skip, limit },\r\n );\r\n return { items: (body.contacts ?? []).map(toContact), total: body.total ?? 0 };\r\n };\r\n\r\n const coventiaResolve = async (number: string): Promise<Contact | null> => {\r\n const digits = toDigits(number);\r\n if (!digits) return null;\r\n const body = await get<{ name?: string | null }>(`${base}/webrtc/caller-name`, {\r\n ...org,\r\n number: digits,\r\n });\r\n // Re-applied on top of the backend's own rule: a contact saved as its own number resolves to a\r\n // \"name\" that is just the number reformatted, and showing that puts +34600111222 where a name\r\n // belongs — next to the number the widget is already displaying.\r\n const name = normalizeResolvedName(body.name ?? null, digits);\r\n return name === null ? null : { id: digits, name, phone: `+${digits}` };\r\n };\r\n\r\n const rawHistory: HistorySource = async ({\r\n skip,\r\n limit,\r\n missed,\r\n userId,\r\n }): Promise<readonly CallHistoryEntry[]> => {\r\n const body = await get<{ calls?: readonly CallRow[] }>(`${base}/calls`, {\r\n ...org,\r\n skip,\r\n limit,\r\n // Omitted rather than `'false'` — the backend only special-cases `missed=true`, so leaving it\r\n // out entirely for \"Todas\" is equivalent and one less thing for a param dump to explain.\r\n missed: missed ? 'true' : undefined,\r\n user_id: userId,\r\n });\r\n return (body.calls ?? []).map((row): CallHistoryEntry => {\r\n const id = row._id ?? '';\r\n const recorded = (row.recording_count ?? 0) > 0;\r\n const missedRow = isMissed(row);\r\n const handledBy = toHandledBy(row);\r\n return {\r\n id,\r\n direction: toDirection(row.direction),\r\n number: otherParty(row),\r\n // Filled by `withNameResolution` at the mount — deduped by digits and cached across pages.\r\n name: null,\r\n at: toDate(row.created_at),\r\n durationSec: row.duration ?? 0,\r\n missed: missedRow,\r\n ...(missedRow ? { outcome: toOutcome(row) } : {}),\r\n // The DID is only ever the ORG's own number, which only an inbound call dials — an outbound\r\n // row's `called_did` is the OTHER party (already `number` above); showing it again as \"DID\"\r\n // would just repeat the row under a wrong label.\r\n ...(toDirection(row.direction) === 'inbound' && row.called_did ? { did: row.called_did } : {}),\r\n recorded,\r\n handledBy,\r\n // The detail page's one enrichment fetch — signed recording URLs, plus this SAME chain with\r\n // names resolved. Skipped when there is nothing to enrich: minting signed URLs and resolving\r\n // names for a page of 30 calls nobody opens is pure waste.\r\n ...(recorded || handledBy.length > 0\r\n ? {\r\n loadDetail: async (): Promise<CallDetail> => {\r\n // Unlike `/calls` (rows spread flat into `calls: []`), `/calls/:id` wraps its one\r\n // result in `call` — `recordings.js`'s `res.json({ success, call: {...} })`.\r\n const detail = await get<{\r\n call?: {\r\n recording_urls?: readonly string[];\r\n handled_by_chain?: readonly EnrichedHandledByChainRow[];\r\n };\r\n }>(`${base}/calls/${encodeURIComponent(id)}`, org);\r\n return {\r\n recordingUrls: detail.call?.recording_urls ?? [],\r\n // `row`'s own bare-extension fields (read inside `toHandledBy` when the chain\r\n // passed here is empty) still work as the fallback — only the CHAIN comes from the\r\n // enriched response; the rest of `row` is unchanged.\r\n handledBy: toHandledBy(row, detail.call?.handled_by_chain ?? []),\r\n };\r\n },\r\n }\r\n : {}),\r\n };\r\n });\r\n };\r\n\r\n const coventiaListAgents = async (): Promise<AgentsDirectory> => {\r\n const body = await get<{ me?: AgentRow | null; people?: readonly AgentRow[] }>(\r\n `${base}/agents`,\r\n org,\r\n );\r\n const toAgent = (row: AgentRow) => ({ userId: row.user_id, name: row.name });\r\n return {\r\n me: body.me ? toAgent(body.me) : null,\r\n people: (body.people ?? []).map(toAgent),\r\n };\r\n };\r\n\r\n const contacts = config.contacts ?? coventiaContacts;\r\n\r\n /*\r\n * Caller ID follows the CONTACT LIST, not a separate setting.\r\n *\r\n * A provider who supplies `contacts` gets a resolver derived from it, so the tab, a live call and a\r\n * history row always name the same number the same way. Supplying only the list used to leave\r\n * caller ID on ours — the same person appearing twice under two names inside one widget.\r\n */\r\n const resolve =\r\n config.resolveContact ??\r\n (config.contacts ? resolverFromContacts(config.contacts) : coventiaResolve);\r\n\r\n // Names, resolved once per distinct number and cached across pages — a call log is the same handful\r\n // of numbers over and over. Wrapped HERE rather than left to the caller: the raw rows carry\r\n // `name: null`, so a caller who forgets this gets a history of bare numbers and no error.\r\n const history = withNameResolution(rawHistory, resolve);\r\n\r\n return { contacts, history, resolveContact: resolve, listAgents: coventiaListAgents };\r\n}\r\n","/**\r\n * The error taxonomy.\r\n *\r\n * Two things make this worth more than `onError(Error)`:\r\n *\r\n * 1. **Fatal vs non-fatal, as a discriminated union.** A fatal error means the call (or the engine)\r\n * is over and the host must react. A non-fatal one is a warning the host may surface or ignore.\r\n * Collapsing them into one channel — which is what almost every SDK does — forces every\r\n * integrator to re-derive the distinction from string matching. Daily's split is the model.\r\n *\r\n * 2. **A coded catalogue with a `solution` per code.** Twilio and Telnyx both ship this and\r\n * integrators love it, because the support conversation becomes \"send me the code\" instead of\r\n * \"send me your logs\".\r\n *\r\n * The catalogue below is the SINGLE source of truth: `fatal` is derived from it at the type level,\r\n * so a code cannot be fatal in the table and non-fatal in a type, and adding a code without deciding\r\n * its severity is a compile error.\r\n */\r\n\r\ninterface ErrorSpec {\r\n readonly fatal: boolean;\r\n /** What went wrong, for a developer. */\r\n readonly message: string;\r\n /** What to do about it. Shown in logs and in our docs. */\r\n readonly solution: string;\r\n}\r\n\r\nexport const ERROR_CATALOGUE = {\r\n // ── Engine lifecycle ────────────────────────────────────────────────────────\r\n 'engine/duplicate-instance': {\r\n fatal: true,\r\n message: 'A Softphone instance is already running on this page.',\r\n solution:\r\n 'Construct one Softphone and share it. React StrictMode double-mounts in development — hold the instance above the tree, or in a ref, and call destroy() on unmount.',\r\n },\r\n 'engine/not-started': {\r\n fatal: false,\r\n message: 'The softphone has not been started.',\r\n solution: 'Await softphone.start() before placing or answering a call.',\r\n },\r\n 'engine/destroyed': {\r\n fatal: true,\r\n message: 'This Softphone has been destroyed and cannot be reused.',\r\n solution: 'Construct a new Softphone.',\r\n },\r\n\r\n // ── Auth ────────────────────────────────────────────────────────────────────\r\n 'auth/token-fetch-failed': {\r\n fatal: true,\r\n message: 'Could not obtain a voice token from the token endpoint.',\r\n solution:\r\n 'Check that the token endpoint is reachable and that the headers callback returns a valid host-app session token.',\r\n },\r\n 'auth/token-rejected': {\r\n fatal: true,\r\n message: 'The voice token was rejected.',\r\n solution:\r\n 'The token is expired, malformed, or scoped to a different organization. Mint a fresh one.',\r\n },\r\n\r\n // ── Media ───────────────────────────────────────────────────────────────────\r\n /*\r\n NONE of the four codes below are ever actually raised via `createError` — nothing in the SDK\r\n constructs a `SoftphoneError` with these codes today. Deleting them was the other option\r\n (SPEC discussion); they stay, commented, because the taxonomy and Spanish copy (`error-text.ts`)\r\n are worth keeping intact for whenever a real caller wants them, and a `SoftphoneErrorCode` union\r\n is a cheap thing to keep honest with a comment, expensive to keep churning as a breaking change.\r\n */\r\n 'media/permission-denied': {\r\n fatal: true,\r\n message: 'Microphone permission was denied.',\r\n solution:\r\n 'The permission prompt names the TOP-LEVEL origin. Ask for it from a user gesture, and re-request via the browser UI if previously blocked.',\r\n // NEVER RAISED. `MicPermissionGate` (ui/permission/) handles this condition today — it predates\r\n // this catalogue and has BETTER, browser-specific unblock instructions than a generic gate could\r\n // show. It does not import this catalogue at all; the two are unconnected on purpose.\r\n },\r\n 'media/no-input-device': {\r\n fatal: true,\r\n message: 'No microphone is available.',\r\n solution: 'Ask the user to connect a microphone, then retry.',\r\n // NEVER RAISED — same reason as `media/permission-denied` above: `MicPermissionGate` already\r\n // owns this.\r\n },\r\n 'media/webrtc-unsupported': {\r\n fatal: true,\r\n message: 'This browser does not expose the WebRTC APIs.',\r\n solution:\r\n 'Usually a privacy extension (uBlock Origin Extra, WebRTC Leak Prevent) disabling RTCPeerConnection — Twilio documents the same. Ask the user to disable it for this origin.',\r\n // NEVER RAISED — same reason again: `MicPermissionGate` already owns this.\r\n },\r\n 'media/playback-blocked': {\r\n fatal: false,\r\n message: 'The browser blocked audio playback (autoplay policy).',\r\n solution:\r\n 'Call softphone.startAudio() from a click handler. Until then the user cannot hear the caller, and an INCOMING RINGTONE WILL BE SILENT — this is how calls get missed.',\r\n /*\r\n NEVER RAISED — different reason from the other three. This is not superseded by an existing\r\n UI; the feature it describes (autoplay-recovery) is simply not built yet. `Softphone.canPlayAudio`\r\n / `startAudio()` (core/softphone.ts) are Phase 2 and currently no-op stubs — `canPlayAudio`\r\n always reports `true`. This is where that future work would report the block once it exists.\r\n */\r\n },\r\n\r\n // ── Network / signalling ────────────────────────────────────────────────────\r\n 'net/registration-failed': {\r\n fatal: true,\r\n message: 'SIP registration failed.',\r\n solution: 'Usually a stale credential. The engine refreshes and retries; if it persists, the extension may not exist.',\r\n },\r\n 'net/transport-failed': {\r\n fatal: true,\r\n message: 'The signalling transport could not be established.',\r\n solution: 'Check the CSP connect-src directive and any corporate proxy blocking WSS.',\r\n },\r\n 'net/ice-failed': {\r\n fatal: false,\r\n message: 'Media connectivity was lost; attempting an ICE restart.',\r\n solution:\r\n 'Usually a network handoff (wifi → cellular). The engine recovers on its own; surface a \"reconnecting\" banner rather than ending the call.',\r\n /*\r\n NEVER RAISED. The \"reconnecting\" banner this `solution` describes already exists — CallScreen\r\n shows \"Reconectando…\" directly off the call's own `reconnecting` STATE (`STATUS_LABEL` in\r\n views/CallScreen.tsx), which the SIP engine drives without going through the error channel at\r\n all. Raising this too would say the same fact twice through two different mechanisms. It would\r\n only earn its keep for host-side OBSERVABILITY — counting ICE restarts independently of the UI\r\n — which nobody has asked for yet.\r\n */\r\n },\r\n\r\n // ── Call ────────────────────────────────────────────────────────────────────\r\n 'call/not-answerable': {\r\n fatal: false,\r\n message: 'This call cannot be answered in its current state.',\r\n solution: 'Only a call in `ringing` may be answered. Check call.state first.',\r\n },\r\n 'call/busy': {\r\n fatal: false,\r\n message: 'The remote party is busy.',\r\n solution:\r\n 'Nothing to fix. Note SIP 486 is also what most endpoints send for a manual decline, so do not ' +\r\n 'label this \"busy\" alone in your UI — it is equally often \"did not answer\".',\r\n },\r\n 'call/no-answer': {\r\n fatal: false,\r\n message: 'The remote party did not answer.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/rejected': {\r\n fatal: false,\r\n message: 'The remote party rejected the call.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/missed': {\r\n fatal: false,\r\n message: 'An incoming call was cancelled before it was answered.',\r\n solution:\r\n 'Nothing to fix — the caller gave up while it rang. Surface it, and log it as a missed call.',\r\n },\r\n 'call/failed': {\r\n fatal: false,\r\n message: 'The call could not be completed.',\r\n solution:\r\n 'The catch-all for a call that never connected — a bad number, a rejected offer, a media ' +\r\n 'failure. `cause` carries the underlying SIP cause. Nothing is fatal: the phone still works.',\r\n },\r\n 'call/transfer-failed': {\r\n fatal: false,\r\n message: 'The transfer could not be completed.',\r\n solution: 'The original call is still up. Let the user retry or resume.',\r\n },\r\n 'call/unsupported': {\r\n fatal: false,\r\n message: 'The provider does not support this operation.',\r\n solution: 'Guard the UI on the operation before offering it.',\r\n },\r\n\r\n // ── Provider ────────────────────────────────────────────────────────────────\r\n 'provider/provision-failed': {\r\n fatal: true,\r\n message: 'The provider could not issue SIP credentials.',\r\n solution:\r\n 'The backend reached the carrier but got no usable credential. Check the org has a SIP extension provisioned.',\r\n },\r\n} as const satisfies Record<string, ErrorSpec>;\r\n\r\nexport type SoftphoneErrorCode = keyof typeof ERROR_CATALOGUE;\r\n\r\n/** Codes whose spec says `fatal: true`. Derived — cannot drift from the catalogue. */\r\nexport type FatalErrorCode = {\r\n [K in SoftphoneErrorCode]: (typeof ERROR_CATALOGUE)[K]['fatal'] extends true ? K : never;\r\n}[SoftphoneErrorCode];\r\n\r\nexport type NonFatalErrorCode = Exclude<SoftphoneErrorCode, FatalErrorCode>;\r\n\r\n/**\r\n * A fatal error: the call, or the engine, is over.\r\n *\r\n * `if (err.fatal)` narrows `err.code` to the fatal codes — which is the entire point of the split,\r\n * and the thing JSDoc could not have expressed (SPEC §2.2).\r\n */\r\nexport interface FatalSoftphoneError extends Error {\r\n readonly fatal: true;\r\n readonly code: FatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\n/** A non-fatal error: a warning. The call, if any, continues. */\r\nexport interface NonFatalSoftphoneError extends Error {\r\n readonly fatal: false;\r\n readonly code: NonFatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\nexport type SoftphoneError = FatalSoftphoneError | NonFatalSoftphoneError;\r\n\r\n/**\r\n * The one concrete error class. Not exported: the public contract is the `SoftphoneError` union and\r\n * the `code` on it, so nothing outside should be reaching for `instanceof`. It is a real `Error`\r\n * subclass, so `instanceof Error`, `.stack` and console formatting all behave as a host expects.\r\n */\r\nclass CatalogueError extends Error {\r\n readonly fatal: boolean;\r\n readonly code: SoftphoneErrorCode;\r\n readonly solution: string;\r\n\r\n constructor(code: SoftphoneErrorCode, cause?: unknown) {\r\n const spec: ErrorSpec = ERROR_CATALOGUE[code];\r\n // `cause` is only passed when there IS one: `{ cause: undefined }` would still define the\r\n // property, and an error carrying an explicit `undefined` cause reads as \"we looked and found\r\n // nothing\" rather than \"nothing was passed\".\r\n super(spec.message, cause === undefined ? undefined : { cause });\r\n this.name = 'SoftphoneError';\r\n this.code = code;\r\n this.fatal = spec.fatal;\r\n this.solution = spec.solution;\r\n }\r\n}\r\n\r\n/**\r\n * Is this already one of ours?\r\n *\r\n * Used where a layer catches whatever a lower one threw: a failure that already carries a catalogue\r\n * code should be reported AS that code, not flattened into the generic one for the layer that caught\r\n * it. Without this, a rejected voice token surfaces as \"the provider could not issue credentials\",\r\n * which is true and useless.\r\n *\r\n * Structural, not `instanceof` — the concrete class is not exported, and a duck-typed object with a\r\n * real code is as good as one of ours.\r\n */\r\nexport function isSoftphoneError(value: unknown): value is SoftphoneError {\r\n if (typeof value !== 'object' || value === null) return false;\r\n const candidate = value as { code?: unknown; fatal?: unknown };\r\n return typeof candidate.fatal === 'boolean' && (candidate.code as string) in ERROR_CATALOGUE;\r\n}\r\n\r\n/** Type guard, for hosts that would rather not rely on structural narrowing. */\r\nexport function isFatal(error: SoftphoneError): error is FatalSoftphoneError {\r\n return error.fatal;\r\n}\r\n\r\n/**\r\n * Construct a catalogue-backed error. **The only way the SDK raises one** — every `message` and\r\n * `solution` a host sees comes from the table above, so no call site invents error prose.\r\n *\r\n * The return type narrows on the code: `createError('call/busy')` is a `NonFatalSoftphoneError`, and\r\n * reading `.fatal` on it gives `false`, not `boolean`.\r\n */\r\nexport function createError<C extends SoftphoneErrorCode>(\r\n code: C,\r\n cause?: unknown,\r\n): C extends FatalErrorCode ? FatalSoftphoneError : NonFatalSoftphoneError {\r\n // The one cast in this module. A conditional return type cannot be verified from inside the\r\n // implementation — TypeScript has no way to prove the branch for an unresolved `C` — so the\r\n // guarantee rests on `fatal` being copied from the catalogue that DERIVES `FatalErrorCode`. The\r\n // test suite asserts that agreement for every code, which is the check the compiler cannot make.\r\n return new CatalogueError(code, cause) as C extends FatalErrorCode\r\n ? FatalSoftphoneError\r\n : NonFatalSoftphoneError;\r\n}\r\n","import type { JsSipSession, JsSipIceEvent } from './jssip.js';\r\n\r\n/**\r\n * What a leg asks the browser for. Audio only — `video: false` is explicit rather than omitted,\r\n * matching the legacy hook exactly.\r\n *\r\n * A FUNCTION, not the static constant this used to be: device selection (SPEC §7 Phase 2) means the\r\n * constraint depends on whichever input device is currently selected, which can change between calls\r\n * — a static object could never reflect that. `deviceId: null` (the default, no device chosen yet)\r\n * falls back to bare `audio: true`, letting the browser pick its own default exactly as before.\r\n */\r\nexport function buildAudioConstraints(deviceId: string | null): MediaStreamConstraints {\r\n return { audio: deviceId ? { deviceId: { exact: deviceId } } : true, video: false };\r\n}\r\n\r\n/**\r\n * The two most non-obvious, most load-bearing, most deletable-LOOKING pieces of the engine\r\n * (SPEC §3.1). Everything here is generic WebRTC — nothing Zadarma-specific — but it is exactly the\r\n * code a rewrite quietly drops, so the reasoning travels with it.\r\n *\r\n * No browser global is touched at module scope (SPEC §10.2): every function reaches for\r\n * `window`/`navigator` only when called, in a browser.\r\n */\r\n\r\n/**\r\n * A **safety net** on ICE gathering: send the INVITE anyway if gathering has not finished in `capMs`.\r\n *\r\n * JsSIP already releases the INVITE the moment gathering completes — it calls its own `ready()` both\r\n * on the end-of-candidates signal and on `icegatheringstatechange === 'complete'`. So the normal path\r\n * needs nothing from us. This exists only for the pathological one: against an unreachable STUN or\r\n * TURN server, Chrome can sit in `gathering` for ~40 s, and a phone that takes 40 s to ring is broken.\r\n *\r\n * **It deliberately does NOT settle on the first `typ srflx` candidate**, which is what it used to do.\r\n * One reflexive candidate is not \"enough for NAT traversal\" — it is enough only if it happens to be\r\n * the right one, and cutting gathering short there loses:\r\n *\r\n * - **the other address family.** A first srflx over IPv6 against an IPv4-only peer is useless; the\r\n * IPv4 one arrives moments later and never makes it into the offer.\r\n * - **the other interfaces.** On a multi-homed machine (Wi-Fi + VPN + Ethernet) the first srflx may\r\n * be the one that cannot reach the media server.\r\n * - **every relay candidate.** TURN needs an allocation round-trip, so `typ relay` always arrives\r\n * AFTER srflx. Settling early meant a configured TURN server could never appear in the SDP — the\r\n * relay would be paid for and never offered, and the call would fail exactly as if it were absent.\r\n *\r\n * This is SIP, not trickle ICE: whatever is in the offer is all the peer will ever see. So the cost of\r\n * settling early is permanent for that call, while the cost of waiting is a few hundred milliseconds.\r\n *\r\n * @returns a disposer that cancels the pending timer (call it on teardown).\r\n */\r\nexport function capIceGathering(session: JsSipSession, capMs: number): () => void {\r\n let settled = false;\r\n let timer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n const settle = (ready: () => void): void => {\r\n if (settled) return;\r\n settled = true;\r\n if (timer) clearTimeout(timer);\r\n try {\r\n ready();\r\n } catch {\r\n /* offer already sent */\r\n }\r\n };\r\n\r\n session.on('icecandidate', ({ ready }: JsSipIceEvent) => {\r\n // Armed on the first candidate of any kind; JsSIP wins the race whenever gathering completes\r\n // normally, which is the overwhelmingly common case.\r\n if (!timer) timer = setTimeout(() => settle(ready), capMs);\r\n });\r\n\r\n return () => {\r\n if (timer) clearTimeout(timer);\r\n };\r\n}\r\n","/**\r\n * ICE configuration — how the browser's media finds its way out of a NAT.\r\n *\r\n * **SDK infrastructure, not host configuration.** A partner integrating a softphone should not have\r\n * to learn what a STUN server is, and every host would otherwise solve the same problem identically.\r\n * So there is a working default here and nothing to configure.\r\n *\r\n * With no ICE servers at all, a browser offers only HOST candidates — its own LAN addresses — and the\r\n * far end has to reach `192.168.x.x` directly. That works only where the NAT happens to cooperate,\r\n * and fails on symmetric NAT and most corporate networks. Firefox says `ICE failed, add a STUN\r\n * server`; Chrome's version is worse, connecting a call that carries no audio.\r\n *\r\n * **STUN only, deliberately — there is no TURN here and it could not be added here.** STUN just\r\n * reports the browser's own public address: stateless, unauthenticated, free. TURN *relays the media*,\r\n * so it costs bandwidth and therefore needs credentials — and a credential compiled into a client\r\n * bundle is a public one, usable by anyone who opens devtools. A relay would have to be issued per\r\n * user and short-lived by a backend that knows who is asking (coturn's `use-auth-secret` scheme is\r\n * the usual shape). That is real infrastructure, and until a network actually demands it we do not\r\n * run any: the legacy CRM softphone shipped exactly this one STUN server and placed real calls for\r\n * years.\r\n */\r\n\r\n/**\r\n * Google's public STUN. No registration, and the de facto default for the WebRTC ecosystem — it is\r\n * also, verbatim, what the legacy `useSipUA.js` used, which is the strongest argument available that\r\n * it is sufficient for Zadarma.\r\n *\r\n * Two caveats worth a decision rather than a shrug: it carries **no SLA**, and every user's IP is\r\n * observed by Google. Both are fixed the same way — point this at a STUN server we run.\r\n */\r\nexport const DEFAULT_ICE_SERVERS: readonly RTCIceServer[] = [\r\n { urls: 'stun:stun.l.google.com:19302' },\r\n];\r\n\r\n/** The `pcConfig` JsSIP hands to `new RTCPeerConnection`, for every leg in either direction. */\r\nexport const PC_CONFIG: RTCConfiguration = { iceServers: [...DEFAULT_ICE_SERVERS] };\r\n","import type { EngineCall, EngineCallEvent } from '../../core/engine.js';\r\nimport type { HangupReason } from '../../core/call.js';\r\nimport type { CallDirection } from '../../core/types.js';\r\nimport type { JsSipEndEvent, JsSipSession } from './jssip.js';\r\nimport { buildAudioConstraints, capIceGathering } from './media.js';\r\nimport { PC_CONFIG } from './ice.js';\r\n\r\n/** Map JsSIP end/failed causes onto the SDK's HangupReason. Generic SIP, no provider specifics. */\r\nfunction mapEndReason(e: JsSipEndEvent): HangupReason {\r\n if (e.originator === 'local') return 'local';\r\n if (e.originator === 'remote') return 'remote';\r\n return 'failed';\r\n}\r\n/**\r\n * A JsSIP cause onto the SDK's HangupReason.\r\n *\r\n * The cases below are `JsSIP.C.causes`, and the status codes in the comments are its own\r\n * `SIP_ERROR_CAUSES` table — this must be read against that list rather than guessed at, because\r\n * `JsSipEndEvent.cause` is a bare `string`, so a case that never matches costs nothing at compile\r\n * time and silently falls to `failed`. That is exactly how `Unavailable` went unhandled: it is what\r\n * a call that rings out actually returns, and it was reaching `default`.\r\n *\r\n * `failed` is a real outcome, not a fallthrough — see CALL_END_CODES in `ua.ts`. Anything unmapped\r\n * still tells the user the call did not connect.\r\n */\r\nfunction mapFailReason(e: JsSipEndEvent): HangupReason {\r\n switch (e.cause) {\r\n // 486 Busy Here, 600 Busy Everywhere. Also what most endpoints send for a MANUAL DECLINE — SIP\r\n // does not distinguish the two, and neither can we, which is why the copy covers both.\r\n case 'Busy':\r\n return 'busy';\r\n /*\r\n `Rejected` is 403 / 603 Decline — an explicit refusal, sent by the endpoints that bother to.\r\n `Redirected` is 3xx: not literally a rejection, but it ends the same way — not connected, and\r\n nothing to retry.\r\n\r\n (Both comments sit ABOVE the group rather than between the labels. A comment between two `case`\r\n lines makes the first one non-empty to `no-fallthrough`, which then demands a `break` that\r\n would be unreachable.)\r\n */\r\n case 'Rejected':\r\n case 'Redirected':\r\n return 'rejected';\r\n /*\r\n `Unavailable` is 480 Temporarily Unavailable / 410 / 408 Request Timeout / 430 — THE ring-out\r\n case, and the one that was missing. `No Answer` and `Expires` are local: our own no-answer\r\n timer, and a session that expired.\r\n */\r\n case 'Unavailable':\r\n case 'No Answer':\r\n case 'Expires':\r\n return 'no-answer';\r\n case 'Canceled':\r\n /*\r\n BOTH directions produce this cause, meaning opposite things, and only the originator tells\r\n them apart: `remote` is the caller giving up while our phone rang (a missed call — `ua.ts`\r\n turns this one into `call/missed`), `local` is the user abandoning their own outbound call\r\n before it was picked up. This used to return `remote` for both, which told the host that the\r\n far end ended a call the user had just hung up on themselves.\r\n */\r\n return e.originator === 'local' ? 'local' : 'remote';\r\n // Everything else is a genuine failure worth naming: Not Found (404/604), Address Incomplete,\r\n // Incompatible SDP, Authentication Error, WebRTC Error, RTP Timeout, Connection Error…\r\n default:\r\n return 'failed';\r\n }\r\n}\r\n\r\nfunction remoteNumberOf(session: JsSipSession): string {\r\n const id = session.remote_identity;\r\n return id?.uri?.user ?? id?.uri?.toString() ?? '';\r\n}\r\n\r\nexport interface SipEngineCallDeps {\r\n readonly id: string;\r\n /** Turn an extension or bare number into a full SIP URI (the engine knows the domain). */\r\n readonly buildTarget: (target: string) => string;\r\n /** Place a consult leg on the same UA (attended transfer). */\r\n readonly placeConsult: (target: string) => Promise<SipEngineCall>;\r\n readonly iceGatheringCapMs?: number;\r\n /** ICE servers for the answer's peer connection — same set the engine uses outbound. */\r\n readonly pcConfig?: RTCConfiguration;\r\n /**\r\n * Called when THIS call is rejected (486), so the engine can start its provider re-ring guard\r\n * (SPEC §3.4). Tied to the reject ACTION, not to `ended` — a normal hangup must not start it.\r\n */\r\n readonly onReject?: () => void;\r\n /**\r\n * The currently-selected input device, read fresh at ANSWER time — an inbound call's `getUserMedia`\r\n * happens here, not at construction, so a device chosen after the call started ringing still takes\r\n * effect the moment it is picked up. `undefined`/`null` means no device chosen — the browser default.\r\n */\r\n readonly getInputDeviceId?: () => string | null;\r\n}\r\n\r\n/**\r\n * One JsSIP call, as an `EngineCall`. It translates JsSIP's events into the SDK's raw\r\n * `EngineCallEvent`s — and `Call` (core) turns those into the public state machine. Nothing here\r\n * knows the state machine, and nothing here is Zadarma-specific.\r\n */\r\nexport class SipEngineCall implements EngineCall {\r\n readonly id: string;\r\n readonly direction: CallDirection;\r\n readonly remoteNumber: string;\r\n\r\n readonly #session: JsSipSession;\r\n readonly #deps: SipEngineCallDeps;\r\n readonly #listeners = new Set<(event: EngineCallEvent) => void>();\r\n\r\n #ended = false;\r\n #cancelIceCap: (() => void) | null = null;\r\n /**\r\n * The stream behind a LIVE device swap (`swapInputDevice`), if one has happened. Distinct from\r\n * JsSIP's own original capture: that one is `_localMediaStreamLocallyGenerated` and JsSIP stops it\r\n * itself on close (see `#end`'s comment). This one is ours — obtained with our own `getUserMedia`\r\n * call, attached via `replaceTrack`, and never known to JsSIP at all — so nothing else will ever\r\n * stop it unless we do, here.\r\n */\r\n #swappedStream: MediaStream | null = null;\r\n\r\n constructor(session: JsSipSession, deps: SipEngineCallDeps) {\r\n this.#session = session;\r\n this.#deps = deps;\r\n this.id = deps.id;\r\n this.direction = session.direction === 'incoming' ? 'inbound' : 'outbound';\r\n this.remoteNumber = remoteNumberOf(session);\r\n this.#wire();\r\n }\r\n\r\n /** @internal — the underlying session, for `completeTransfer`'s REFER…Replaces. */\r\n get session(): JsSipSession {\r\n return this.#session;\r\n }\r\n\r\n onEvent(listener: (event: EngineCallEvent) => void): () => void {\r\n this.#listeners.add(listener);\r\n return () => this.#listeners.delete(listener);\r\n }\r\n\r\n // ── JsSIP events → raw engine signals ─────────────────────────────────────\r\n #wire(): void {\r\n const s = this.#session;\r\n\r\n /*\r\n * `progress` is OUTBOUND-only.\r\n *\r\n * JsSIP fires it in both directions, but it means opposite things: on an outbound call it is the\r\n * remote's `180`/`183` — the state we call `early`, possibly with early media. On an INBOUND call\r\n * it fires when *we* send `180 Ringing`, which is not a state change at all: the call is already\r\n * `ringing`, and the machine has no `ringing → early` edge because there is no such thing.\r\n *\r\n * Wiring it for both is what produced `illegal call transition ringing → early` on every real\r\n * inbound call. It was harmless — the transition was refused and the call rang and answered fine —\r\n * but it logged an error on a healthy path, and an error that fires when nothing is wrong is how\r\n * people learn to ignore the console.\r\n */\r\n if (this.direction === 'outbound') {\r\n s.on('progress', () => this.#fire({ type: 'progress', response: 'ringing' }));\r\n }\r\n\r\n /*\r\n * The remote audio. `ontrack` has to be wired on BOTH paths, because JsSIP creates the peer\r\n * connection at a different moment in each direction:\r\n *\r\n * INBOUND — created inside `answer()`, long after this runs. The event is the only way to see it.\r\n * OUTBOUND — created SYNCHRONOUSLY inside `ua.call()`: JsSIP's `connect()` calls\r\n * `_createRTCConnection()` before it returns, so `peerconnection` has already fired\r\n * by the time this object exists. Listening alone means `ontrack` is never set and\r\n * the caller hears silence — while the callee hears them perfectly, since the\r\n * outbound media path is unaffected.\r\n *\r\n * The legacy hook did exactly this: `if (session.connection) session.connection.ontrack = …`\r\n * right after `ua.call()`, plus the event for the answer path.\r\n */\r\n const wireRemoteTrack = (pc: RTCPeerConnection): void => {\r\n pc.ontrack = ({ streams }) => {\r\n if (streams[0]) this.#fire({ type: 'stream', stream: streams[0] });\r\n };\r\n };\r\n s.on('peerconnection', ({ peerconnection }) => wireRemoteTrack(peerconnection));\r\n if (s.connection) wireRemoteTrack(s.connection);\r\n\r\n // Outbound only: release the INVITE without waiting for full ICE gathering (SPEC §3.1).\r\n if (this.direction === 'outbound' && this.#deps.iceGatheringCapMs) {\r\n this.#cancelIceCap = capIceGathering(s, this.#deps.iceGatheringCapMs);\r\n }\r\n\r\n s.on('accepted', () => this.#onAccepted());\r\n s.on('ended', (e) => this.#end(mapEndReason(e), e.cause));\r\n s.on('failed', (e) => this.#end(mapFailReason(e), e.cause));\r\n s.on('hold', ({ originator }) => this.#fire({ type: 'held', originator }));\r\n s.on('unhold', ({ originator }) => this.#fire({ type: 'unheld', originator }));\r\n }\r\n\r\n /**\r\n * The call is live. Nothing to swap: JsSIP opened the real microphone when the session was created,\r\n * so the far end has had our actual audio from the first packet.\r\n *\r\n * There used to be a silent-mic dance here — place the call with a synthetic AudioContext track,\r\n * swap the real mic in on accept — so a Bluetooth headset stayed in A2DP while the phone rang. It\r\n * cost more than it bought: with no active capture, Firefox restricts ICE gathering and the offer\r\n * goes out with no candidates at all, which no STUN or TURN server can fix.\r\n */\r\n #onAccepted(): void {\r\n this.#fire({ type: 'accepted' });\r\n }\r\n\r\n // ── Commands ───────────────────────────────────────────────────────────────\r\n async answer(): Promise<void> {\r\n const deviceId = this.#deps.getInputDeviceId?.() ?? null;\r\n this.#session.answer({\r\n mediaConstraints: buildAudioConstraints(deviceId),\r\n pcConfig: this.#deps.pcConfig ?? PC_CONFIG,\r\n });\r\n }\r\n\r\n /**\r\n * Live-swap the outgoing microphone track — `CallEngine.setInputDevice` on a call that is already\r\n * connected. Never touches the FIRST call's capture: that one is JsSIP's own `getUserMedia`, opened\r\n * inside `answer()`/`ua.call()` (see `#onAccepted`'s comment on why that stays that way). This is\r\n * only for a device chosen AFTER the call is already live.\r\n *\r\n * `RTCRtpSender.replaceTrack` rather than a fresh `answer()`/re-INVITE: it needs no SDP\r\n * renegotiation and the far end never sees an interruption — exactly the mechanism real softphones\r\n * use for \"switch mic mid-call.\"\r\n */\r\n async swapInputDevice(deviceId: string | null): Promise<void> {\r\n const pc = this.#session.connection;\r\n if (!pc || this.#ended) return; // not connected yet, or already over — nothing to swap\r\n const sender = pc.getSenders().find((s) => s.track?.kind === 'audio');\r\n if (!sender) return;\r\n\r\n let stream: MediaStream;\r\n try {\r\n stream = await navigator.mediaDevices.getUserMedia(buildAudioConstraints(deviceId));\r\n } catch {\r\n // The chosen device vanished between selection and this call (unplugged, permission revoked,\r\n // a stale id) — nothing useful to do beyond leaving the current track exactly as it was.\r\n return;\r\n }\r\n if (this.#ended) {\r\n // The call ended while getUserMedia was in flight — don't attach a track to a dead sender,\r\n // and don't leak the capture we just opened.\r\n for (const track of stream.getTracks()) track.stop();\r\n return;\r\n }\r\n const [track] = stream.getAudioTracks();\r\n if (!track) return;\r\n\r\n await sender.replaceTrack(track);\r\n // Stop whatever we swapped in LAST time — never the original JsSIP capture, which this never\r\n // touches and which JsSIP stops itself on close.\r\n for (const oldTrack of this.#swappedStream?.getTracks() ?? []) oldTrack.stop();\r\n this.#swappedStream = stream;\r\n }\r\n\r\n async reject(): Promise<void> {\r\n this.#deps.onReject?.();\r\n this.#session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n }\r\n\r\n async hangup(): Promise<void> {\r\n this.#session.terminate();\r\n }\r\n\r\n mute(muted: boolean): void {\r\n if (muted) this.#session.mute({ audio: true });\r\n else this.#session.unmute({ audio: true });\r\n }\r\n\r\n async hold(): Promise<void> {\r\n // JsSIP writes `a=sendonly` and fires 'hold' — which #wire translates to a `held` signal. One\r\n // path, so a locally-initiated hold and a remote one travel identically (SPEC §5.6).\r\n this.#session.hold();\r\n }\r\n\r\n async unhold(): Promise<void> {\r\n this.#session.unhold();\r\n }\r\n\r\n sendDigits(digits: string): void {\r\n this.#session.sendDTMF(digits);\r\n }\r\n\r\n async transfer(target: string): Promise<void> {\r\n // Blind transfer. JsSIP ends the session on a successful REFER; that surfaces as `ended`\r\n // (reason 'transferred' is set by the core when it sees the transfer intent — here it is a\r\n // normal local end).\r\n this.#session.refer(this.#deps.buildTarget(target));\r\n }\r\n\r\n async startConsult(target: string): Promise<EngineCall> {\r\n return this.#deps.placeConsult(target);\r\n }\r\n\r\n async completeTransfer(consult: EngineCall): Promise<void> {\r\n const consultSession = (consult as SipEngineCall).session;\r\n this.#session.refer(consultSession.remote_identity.uri.toString(), { replaces: consultSession });\r\n }\r\n\r\n // ── internals ──────────────────────────────────────────────────────────────\r\n #end(reason: HangupReason, cause?: string): void {\r\n if (this.#ended) return;\r\n this.#ended = true;\r\n this.#cancelIceCap?.();\r\n // The ORIGINAL microphone is NOT stopped here: JsSIP opened it (no `mediaStream` was passed), so\r\n // it owns it and stops the tracks itself on close. See `_localMediaStreamLocallyGenerated` in\r\n // JsSIP. A SWAPPED-IN one (`swapInputDevice`) is a different story — JsSIP never learned about\r\n // it, so it is ours to stop, or the capture (and the browser's mic indicator) outlives the call.\r\n for (const track of this.#swappedStream?.getTracks() ?? []) track.stop();\r\n this.#swappedStream = null;\r\n this.#fire({ type: 'ended', reason, cause });\r\n this.#listeners.clear();\r\n }\r\n\r\n #fire(event: EngineCallEvent): void {\r\n if (this.#ended && event.type !== 'ended') return;\r\n for (const listener of [...this.#listeners]) listener(event);\r\n }\r\n}\r\n","import type { CallEngine, EngineCall } from '../../core/engine.js';\r\nimport type { CallOptions, SoftphoneState } from '../../core/types.js';\r\nimport type { SipCredentials, SipProvider } from '../providers/provider.js';\r\nimport type { JsSipFactory, JsSipSession, JsSipUA, JsSipUAConfig } from './jssip.js';\r\nimport { SipEngineCall } from './session.js';\r\nimport { buildAudioConstraints } from './media.js';\r\nimport { PC_CONFIG } from './ice.js';\r\nimport { createError, isSoftphoneError } from '../../core/errors.js';\r\nimport type { NonFatalErrorCode, SoftphoneError, SoftphoneErrorCode } from '../../core/errors.js';\r\nimport type { HangupReason } from '../../core/call.js';\r\n\r\nconst DEFAULT_ICE_CAP_MS = 3000;\r\nconst REGISTER_EXPIRES = 580;\r\n/** Floor between signal rebuilds, so a persistently failing registrar cannot become a hot loop. */\r\nconst SIGNAL_REFRESH_DEBOUNCE_MS = 30_000;\r\n/** Same window, same reason: a retrying registrar must not become a stream of identical errors. */\r\nconst ERROR_DEBOUNCE_MS = 30_000;\r\n\r\n/**\r\n * Call outcomes worth telling the user about. Reasons NOT here (`local`, `remote`, `transferred`)\r\n * are ordinary endings — reporting them would turn every completed call into an error.\r\n *\r\n * `failed` MUST stay mapped. It is `mapFailReason`'s catch-all, so leaving it out means every cause\r\n * nobody thought to enumerate ends the call in silence — the user watches the dialpad come back with\r\n * no idea whether it rang, and the least-understood failures are the ones explained least.\r\n */\r\nconst CALL_END_CODES: Partial<Record<HangupReason, NonFatalErrorCode>> = {\r\n busy: 'call/busy',\r\n 'no-answer': 'call/no-answer',\r\n rejected: 'call/rejected',\r\n failed: 'call/failed',\r\n};\r\n\r\n/** `sip:100@pbx.example.com` → `pbx.example.com`. */\r\nfunction domainOf(credentials: SipCredentials): string {\r\n const at = credentials.uri.lastIndexOf('@');\r\n return at === -1 ? '' : credentials.uri.slice(at + 1);\r\n}\r\n\r\nlet callSeq = 0;\r\nconst nextCallId = (): string => `sip-${++callSeq}`;\r\n\r\n/**\r\n * The generic JsSIP call engine. A `CallEngine` driven by a `SipProvider` for credentials and\r\n * registration policy (SPEC §5.4).\r\n *\r\n * NOTHING here is Zadarma-specific — `grep -ri zadarma` over this directory must return nothing. The\r\n * push socket, the JSONP key exchange and the quirk-timer *values* all live in the provider (S4);\r\n * this engine only reads `provider.registrationMode`, `provider.incomingSignal` and\r\n * `provider.quirks` as data.\r\n *\r\n * Registration policy:\r\n * - `persistent` — provision + register once at start(); the standing UA both receives INVITEs\r\n * and places calls. The normal SIP model; our own infrastructure, later.\r\n * - `on-demand` — register only in response to `incomingSignal` (or to place a call), tear down\r\n * after. Zadarma's PBX ignores a standing registration (SPEC §3.2). The\r\n * no-INVITE teardown and reject-guard timers come from `provider.quirks`.\r\n */\r\nexport class SipCallEngine implements CallEngine {\r\n #state: SoftphoneState = 'stopped';\r\n #ua: JsSipUA | null = null;\r\n /** In-flight `#createUA()`. THE concurrency guard — see `#ensureUA`. */\r\n #uaInFlight: Promise<JsSipUA> | null = null;\r\n #domain = '';\r\n #activeCall: SipEngineCall | null = null;\r\n /** The chosen microphone, or `null` for the browser default. Read fresh by every new leg's\r\n * `mediaConstraints`, and pushed live to `#activeCall` by `setInputDevice`. */\r\n #inputDeviceId: string | null = null;\r\n\r\n readonly #incomingListeners = new Set<(call: EngineCall) => void>();\r\n readonly #stateListeners = new Set<(state: SoftphoneState) => void>();\r\n readonly #errorListeners = new Set<(error: SoftphoneError) => void>();\r\n #signalUnsub: (() => void) | null = null;\r\n #signalConnectionUnsub: (() => void) | null = null;\r\n #noInviteTimer: ReturnType<typeof setTimeout> | undefined;\r\n #rejectGuardUntil = 0;\r\n #signalRefreshAfter = 0;\r\n readonly #errorSentAfter = new Map<SoftphoneErrorCode, number>();\r\n\r\n private readonly now: () => number;\r\n #resolvedFactory: JsSipFactory | null = null;\r\n\r\n constructor(\r\n private readonly provider: SipProvider,\r\n // A factory OR an async loader. The loader form bridges sync construction with the dynamic\r\n // `import('jssip')` (SPEC §10.2) — tests still pass a plain FakeFactory.\r\n private readonly factory: JsSipFactory | (() => JsSipFactory | Promise<JsSipFactory>),\r\n options: { now?: () => number } = {},\r\n ) {\r\n this.now = options.now ?? Date.now;\r\n }\r\n\r\n async #resolveFactory(): Promise<JsSipFactory> {\r\n if (this.#resolvedFactory) return this.#resolvedFactory;\r\n this.#resolvedFactory =\r\n typeof this.factory === 'function' ? await this.factory() : this.factory;\r\n return this.#resolvedFactory;\r\n }\r\n\r\n get state(): SoftphoneState {\r\n return this.#state;\r\n }\r\n\r\n onIncoming(listener: (call: EngineCall) => void): () => void {\r\n this.#incomingListeners.add(listener);\r\n return () => this.#incomingListeners.delete(listener);\r\n }\r\n onStateChange(listener: (state: SoftphoneState) => void): () => void {\r\n this.#stateListeners.add(listener);\r\n return () => this.#stateListeners.delete(listener);\r\n }\r\n onError(listener: (error: SoftphoneError) => void): () => void {\r\n this.#errorListeners.add(listener);\r\n return () => this.#errorListeners.delete(listener);\r\n }\r\n\r\n /**\r\n * Switch the microphone: stored for every FUTURE leg's `mediaConstraints`, and — if a call is live\r\n * right now — pushed to it immediately via `SipEngineCall.swapInputDevice` (SPEC §7 Phase 2, device\r\n * management). `null` resets to the browser's own default device.\r\n */\r\n async setInputDevice(deviceId: string | null): Promise<void> {\r\n this.#inputDeviceId = deviceId;\r\n await this.#activeCall?.swapInputDevice(deviceId);\r\n }\r\n\r\n /**\r\n * Report a failure, at most once per code per `ERROR_DEBOUNCE_MS`.\r\n *\r\n * A failing registrar retries, and one emission per attempt would flood a host's logging and make\r\n * whatever the widget shows flicker. Debounced per CODE rather than globally, so a transport\r\n * failure is never swallowed because a registration failure happened to precede it.\r\n */\r\n #reportError(code: SoftphoneErrorCode, cause?: unknown): void {\r\n this.#emitError(createError(code, cause));\r\n }\r\n\r\n /**\r\n * Emit, debouncing FATAL codes only.\r\n *\r\n * A failing registrar retries, and one emission per attempt would flood a host's logging and make\r\n * whatever the widget shows flicker — so the same fatal code is reported at most once per window,\r\n * per CODE, so a transport failure is never swallowed because a registration failure preceded it.\r\n *\r\n * Non-fatal codes are never debounced: they describe one call each, and two busy tones in a row are\r\n * two facts, not a repeated one.\r\n */\r\n #emitError(error: SoftphoneError): void {\r\n if (error.fatal) {\r\n const now = this.now();\r\n if (now < (this.#errorSentAfter.get(error.code) ?? 0)) return;\r\n this.#errorSentAfter.set(error.code, now + ERROR_DEBOUNCE_MS);\r\n }\r\n for (const listener of [...this.#errorListeners]) listener(error);\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.#setState('starting');\r\n if (this.provider.registrationMode === 'persistent') {\r\n await this.#ensureUA();\r\n this.#setState('online');\r\n } else {\r\n // On-demand: the push socket keeps us \"reachable\" without a standing registration. We only\r\n // register when it signals a call (or when we place one).\r\n const signal = this.provider.incomingSignal;\r\n if (signal) {\r\n await signal.connect();\r\n this.#signalUnsub = signal.onSignal(() => void this.#onIncomingSignal());\r\n /*\r\n * The signal channel IS our reachability, so it drives the reported state.\r\n *\r\n * Without this, `online` meant \"start() was called\" and nothing could ever contradict it —\r\n * the only other state transitions come from a UA that, in on-demand mode, exists solely for\r\n * the duration of a call. A dead push socket therefore looked identical to a healthy one, and\r\n * the phone silently stopped ringing while the widget said it was online.\r\n */\r\n this.#signalConnectionUnsub =\r\n signal.onConnectionChange?.((connected) =>\r\n this.#setState(connected ? 'online' : 'offline'),\r\n ) ?? null;\r\n }\r\n // Optimistic only when the signal cannot report: an existing provider without\r\n // `onConnectionChange` keeps the old behaviour rather than being stuck offline.\r\n this.#setState(signal?.onConnectionChange ? (signal.connected ? 'online' : 'offline') : 'online');\r\n }\r\n }\r\n\r\n async stop(): Promise<void> {\r\n this.#clearNoInviteTimer();\r\n this.#signalUnsub?.();\r\n this.#signalUnsub = null;\r\n this.#signalConnectionUnsub?.();\r\n this.#signalConnectionUnsub = null;\r\n this.provider.incomingSignal?.disconnect();\r\n this.#teardownUA();\r\n this.#setState('stopped');\r\n }\r\n\r\n async placeCall(destination: string, _options?: CallOptions): Promise<EngineCall> {\r\n const ua = await this.#ensureUA();\r\n const session = await this.#callWhenRegistered(ua, this.#buildTarget(destination));\r\n return this.#wrapCall(session);\r\n }\r\n\r\n // ── incoming (on-demand: push → register → INVITE) ────────────────────────\r\n async #onIncomingSignal(): Promise<void> {\r\n // Guard: after a reject, the provider may RE-RING us (Zadarma's return_timeout). Swallow those\r\n // for the quirk window (SPEC §3.4). Persistent providers set no guard, so this is a no-op there.\r\n if (this.now() < this.#rejectGuardUntil) return;\r\n if (this.#activeCall) return; // already on a call\r\n try {\r\n await this.#ensureUA();\r\n // The push is NOT an INVITE (SPEC §3.3): it fires for transfers, retries, hangups. If no INVITE\r\n // arrives within the quirk window, tear the fresh UA down so it doesn't block the next call.\r\n this.#armNoInviteTeardown();\r\n } catch {\r\n this.#teardownUA();\r\n }\r\n }\r\n\r\n #armNoInviteTeardown(): void {\r\n const ms = this.provider.quirks?.noInviteTeardownMs;\r\n if (!ms) return;\r\n this.#clearNoInviteTimer();\r\n this.#noInviteTimer = setTimeout(() => {\r\n if (!this.#activeCall) this.#teardownUA();\r\n }, ms);\r\n }\r\n /**\r\n * Rebuild the incoming-signal channel with fresh credentials, after a registration failure.\r\n *\r\n * Registration fails when the credentials behind it have gone stale — and for Zadarma those come\r\n * from the same WebRTC key the push socket is holding. Tearing down the UA alone leaves that stale\r\n * key in place, so every subsequent attempt fails the same way and the phone is dead until the page\r\n * is reloaded. Reconnecting re-fetches the key. The legacy hook did exactly this from both of its\r\n * `registrationFailed` handlers.\r\n *\r\n * Debounced, which the legacy was not: a provider failing every attempt would otherwise become a\r\n * tight loop against our backend and theirs.\r\n */\r\n async #refreshSignal(): Promise<void> {\r\n const signal = this.provider.incomingSignal;\r\n if (!signal) return;\r\n if (this.now() < this.#signalRefreshAfter) return;\r\n this.#signalRefreshAfter = this.now() + SIGNAL_REFRESH_DEBOUNCE_MS;\r\n try {\r\n signal.disconnect();\r\n await signal.connect();\r\n } catch {\r\n // Still disconnected, and the signal reports that — the engine surfaces it as `offline`\r\n // rather than claiming a reachability it does not have.\r\n }\r\n }\r\n\r\n #clearNoInviteTimer(): void {\r\n if (this.#noInviteTimer) clearTimeout(this.#noInviteTimer);\r\n this.#noInviteTimer = undefined;\r\n }\r\n\r\n #onNewSession(session: JsSipSession, originator: 'local' | 'remote'): void {\r\n if (originator !== 'remote') return; // our own outbound legs are wired at placeCall\r\n this.#clearNoInviteTimer();\r\n\r\n if (this.#activeCall || this.now() < this.#rejectGuardUntil) {\r\n session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n return;\r\n }\r\n const call = this.#wrapCall(session);\r\n for (const listener of [...this.#incomingListeners]) listener(call);\r\n }\r\n\r\n // ── UA lifecycle ───────────────────────────────────────────────────────────\r\n /**\r\n * The single UA, created at most once — even under concurrent callers.\r\n *\r\n * **The `#ua` check alone is not a guard**, because everything after it awaits: two callers both\r\n * see `null`, both `provision()`, both build a UA, and both REGISTER the same extension. Zadarma\r\n * rejects the loser, `registrationFailed` tears down, and the survivor may be the one that was\r\n * torn down. The visible symptom is two `get_webphone_data.php` calls at startup and a phone that\r\n * never registers.\r\n *\r\n * Concurrency is the normal case here, not an edge: the push socket can signal twice in a\r\n * heartbeat (SPEC §3.3 — a push is not an INVITE; it fires for transfers, retries and hangups),\r\n * and `placeCall` can land on top of one. So the in-flight promise IS the guard.\r\n */\r\n async #ensureUA(): Promise<JsSipUA> {\r\n if (this.#ua) return this.#ua;\r\n this.#uaInFlight ??= this.#createUA().finally(() => {\r\n this.#uaInFlight = null;\r\n });\r\n return this.#uaInFlight;\r\n }\r\n\r\n async #createUA(): Promise<JsSipUA> {\r\n const factory = await this.#resolveFactory();\r\n\r\n /*\r\n * Report what actually went wrong, not the layer that caught it.\r\n *\r\n * A rejected voice token, a token endpoint that is down, and a carrier returning no credential\r\n * are three problems with three different fixes; the lower layers already raise the precise\r\n * `auth/*` code. Only supply the generic one when nothing more specific is known.\r\n *\r\n * Inline rather than a helper method: an extra async frame is an extra microtask, and the\r\n * no-INVITE teardown is armed the moment this resolves (QUIRK 1).\r\n */\r\n let credentials: SipCredentials;\r\n try {\r\n credentials = await this.provider.provision();\r\n } catch (cause) {\r\n if (isSoftphoneError(cause)) this.#emitError(cause);\r\n else this.#reportError('provider/provision-failed', cause);\r\n throw cause;\r\n }\r\n this.#domain = domainOf(credentials);\r\n const config = this.#uaConfig(credentials, factory);\r\n const ua = factory.createUA(config);\r\n\r\n ua.on('newRTCSession', ({ session, originator }) => this.#onNewSession(session, originator));\r\n ua.on('disconnected', ({ error }) => {\r\n if (!error) return; // a clean close (our own teardown) is not a failure\r\n this.#setState('reconnecting');\r\n this.#reportError('net/transport-failed', error);\r\n });\r\n ua.on('connected', () => this.#setState('online'));\r\n ua.on('registrationFailed', (e) => {\r\n /*\r\n * On Zadarma the usual cause is geo-routing (SPEC §3.1): a registrar in the wrong datacenter\r\n * answers a bare 401 with no auth challenge. The registrar `domain` is what\r\n * `get_webphone_data.php` handed back, so it is the one detail that makes this diagnosable —\r\n * it rides along as the error's `cause` rather than only reaching a console nobody is reading.\r\n */\r\n const detail = e as { cause?: string; response?: { status_code?: number } } | undefined;\r\n this.#reportError('net/registration-failed', {\r\n cause: detail?.cause,\r\n status: detail?.response?.status_code,\r\n domain: this.#domain,\r\n hint: 'A bare 401 with no auth challenge usually means the registrar is in the wrong datacenter (SPEC §3.1).',\r\n });\r\n this.#teardownUA();\r\n void this.#refreshSignal();\r\n });\r\n\r\n this.#ua = ua;\r\n ua.start();\r\n return ua;\r\n }\r\n\r\n #uaConfig(credentials: SipCredentials, factory: JsSipFactory): JsSipUAConfig {\r\n return {\r\n sockets: [factory.createWebSocket(credentials.wsUrl)],\r\n uri: credentials.uri,\r\n authorization_user: credentials.authUser,\r\n password: credentials.password,\r\n register: true,\r\n register_expires: REGISTER_EXPIRES,\r\n connection_recovery_min_interval: 2,\r\n connection_recovery_max_interval: 30,\r\n // Generic — NOT a provider brand string. If a provider ever requires a specific UA header it\r\n // belongs in the provider, not hard-coded here.\r\n // NOT ours to choose: Zadarma refuses to register any client whose User-Agent it does not\r\n // recognise (see ProviderQuirks.userAgent). The provider decides; the engine just sends it.\r\n user_agent: this.provider.quirks?.userAgent ?? 'Coventia Softphone',\r\n };\r\n }\r\n\r\n #teardownUA(): void {\r\n this.#uaInFlight = null;\r\n if (!this.#ua) return;\r\n try {\r\n this.#ua.terminateSessions({ status_code: 487 });\r\n this.#ua.stop();\r\n } catch {\r\n /* already stopped */\r\n }\r\n this.#ua = null;\r\n this.#activeCall = null;\r\n }\r\n\r\n /** Place a call once the UA is registered (its 200 OK for REGISTER has arrived). */\r\n /**\r\n * **No `mediaStream`** — JsSIP calls `getUserMedia` itself, which is what opens a real capture.\r\n *\r\n * That is load-bearing, not incidental. Firefox restricts ICE gathering on a page with no active\r\n * capture: it discards every address but the default route's (\"Ignoring addr from interface other\r\n * than …\" in about:webrtc) and can end up gathering NOTHING, so the INVITE goes out with\r\n * `c=IN IP4 0.0.0.0` and no candidates at all. No STUN or TURN server can rescue an offer that\r\n * contains no candidates. The legacy hook passed `mediaConstraints` and nothing else, for what\r\n * turns out to be a very good reason.\r\n */\r\n #callWhenRegistered(ua: JsSipUA, target: string): Promise<JsSipSession> {\r\n const options = { mediaConstraints: buildAudioConstraints(this.#inputDeviceId), pcConfig: PC_CONFIG };\r\n if (ua.isRegistered()) return Promise.resolve(ua.call(target, options));\r\n return new Promise<JsSipSession>((resolve) => {\r\n ua.on('registered', () => resolve(ua.call(target, options)));\r\n });\r\n }\r\n\r\n #wrapCall(session: JsSipSession): SipEngineCall {\r\n const call = new SipEngineCall(session, {\r\n id: nextCallId(),\r\n buildTarget: (t) => this.#buildTarget(t),\r\n placeConsult: (t) => this.#placeConsult(t),\r\n // The guard is tied to the reject ACTION, not to `ended` — a normal hangup must not arm it.\r\n onReject: () => this.#startRejectGuard(),\r\n iceGatheringCapMs: this.provider.quirks?.iceGatheringCapMs ?? DEFAULT_ICE_CAP_MS,\r\n // Inbound answers open their own peer connection, so they need the same ICE servers the\r\n // outbound leg got. Missing here, an ANSWERED call is the one that fails — the half nobody\r\n // tests until a real person picks up.\r\n pcConfig: PC_CONFIG,\r\n // Read fresh at answer() time, not captured now — a device chosen while the phone is still\r\n // ringing must still apply once it is picked up.\r\n getInputDeviceId: () => this.#inputDeviceId,\r\n });\r\n\r\n this.#activeCall = call;\r\n call.onEvent((event) => {\r\n if (event.type === 'ended') {\r\n /*\r\n The outcome of a call, as a code the widget can show: \"Sin respuesta\", \"Llamada rechazada\".\r\n A normal hangup by either side is not an error.\r\n\r\n A MISSED call is the one outcome `reason` cannot carry on its own. The caller CANCELling\r\n while our phone rings is an ordinary `remote` ending, so it would pass in silence — and it\r\n is the case the user is least able to reconstruct, because they never saw the call. JsSIP\r\n sends the same `Canceled` cause when the user abandons their OWN outbound call, so the\r\n direction is what separates a call that was missed from one that was given up on.\r\n */\r\n const missed = call.direction === 'inbound' && event.cause === 'Canceled';\r\n const code = missed ? 'call/missed' : CALL_END_CODES[event.reason];\r\n // The raw cause rides along so a `call/failed` in a host's logs says which failure it was.\r\n if (code) this.#reportError(code, event.cause);\r\n if (this.#activeCall === call) this.#activeCall = null;\r\n // On-demand: the UA is disposable — the next call registers a fresh one.\r\n if (this.provider.registrationMode === 'on-demand') this.#teardownUA();\r\n }\r\n });\r\n return call;\r\n }\r\n\r\n async #placeConsult(target: string): Promise<SipEngineCall> {\r\n const ua = await this.#ensureUA();\r\n const consultSession = ua.call(this.#buildTarget(target), {\r\n mediaConstraints: buildAudioConstraints(this.#inputDeviceId),\r\n pcConfig: PC_CONFIG,\r\n });\r\n return new SipEngineCall(consultSession, {\r\n id: nextCallId(),\r\n buildTarget: (x) => this.#buildTarget(x),\r\n placeConsult: () => Promise.reject(new Error('cannot consult from a consult leg')),\r\n iceGatheringCapMs: this.provider.quirks?.iceGatheringCapMs ?? DEFAULT_ICE_CAP_MS,\r\n });\r\n }\r\n\r\n #startRejectGuard(): void {\r\n const ms = this.provider.quirks?.rejectGuardMs;\r\n if (ms) this.#rejectGuardUntil = this.now() + ms;\r\n }\r\n\r\n #buildTarget(target: string): string {\r\n return target.includes('@') ? target : `sip:${target}@${this.#domain}`;\r\n }\r\n\r\n #setState(state: SoftphoneState): void {\r\n if (state === this.#state) return;\r\n this.#state = state;\r\n for (const listener of [...this.#stateListeners]) listener(state);\r\n }\r\n}\r\n","import type { JsSipFactory, JsSipUA, JsSipUAConfig } from './jssip.js';\r\n\r\n/**\r\n * The REAL JsSipFactory — the only place `jssip` is loaded, via a **dynamic import** so that merely\r\n * importing `@coventia/softphone` never pulls it. jssip is CJS and touches browser globals; a static\r\n * import would break `import` under SSR (SPEC §10.2) and drag ~66 KB into every bundle.\r\n *\r\n * Because the import is async but `JsSipFactory.createUA` is sync, we load jssip once up front via\r\n * `loadJsSipFactory()` and hand back a fully-synchronous factory.\r\n */\r\n\r\ninterface JsSipModule {\r\n UA: new (config: JsSipUAConfig) => JsSipUA;\r\n WebSocketInterface: new (url: string) => unknown;\r\n}\r\n\r\nlet cached: JsSipFactory | null = null;\r\n\r\nexport async function loadJsSipFactory(): Promise<JsSipFactory> {\r\n if (cached) return cached;\r\n const mod = (await import('jssip')) as unknown as { default?: JsSipModule } & Partial<JsSipModule>;\r\n const JsSIP = (mod.default ?? mod) as JsSipModule;\r\n if (!JsSIP?.UA || !JsSIP?.WebSocketInterface) {\r\n throw new Error('jssip: unexpected module shape (no UA / WebSocketInterface)');\r\n }\r\n cached = {\r\n createWebSocket: (wsUrl) => new JsSIP.WebSocketInterface(wsUrl),\r\n createUA: (config) => new JsSIP.UA(config),\r\n };\r\n return cached;\r\n}\r\n","import type {\r\n IncomingSignal,\r\n ProviderQuirks,\r\n RegistrationMode,\r\n SipCredentials,\r\n SipProvider,\r\n} from './provider.js';\r\n\r\n/**\r\n * The Zadarma provider — the FIRST (and, for now, only) `SipProvider`. Everything Zadarma-specific\r\n * that the generic SIP engine (`transports/sip/`) must not know lives here, expressed as data the\r\n * engine reads (SPEC §5.4).\r\n *\r\n * Network I/O is INJECTED (`ZadarmaDeps`) for two reasons: the JSONP exchange and the push socket\r\n * are browser-only, and the quirk tests (SPEC §3, the S4 safety net) must drive this without a\r\n * browser, a network, or a real call.\r\n */\r\n\r\n/**\r\n * The presence heartbeat Zadarma's push gateway expects, and how long before key expiry we rebuild\r\n * the socket. Both are Zadarma's numbers, carried over from the CRM hook that ran them in production.\r\n */\r\nconst PING = { localMs: 30_000, globalMs: 60_000 } as const;\r\nconst KEY_REFRESH_BUFFER_MS = 2 * 60 * 60 * 1000;\r\n\r\n/** The Zadarma quirk-timer values (SPEC §3). The engine reads these; it never hard-codes them. */\r\nexport const ZADARMA_QUIRKS: ProviderQuirks = {\r\n // Zadarma's registrar REFUSES to register a WebRTC client with any other User-Agent — a clean 401\r\n // after a valid digest challenge, indistinguishable from a wrong password (§3.1). The old CRM\r\n // hook carried this string; the port dropped it, and registration failed for exactly that reason.\r\n userAgent: 'Zadarma webRTC',\r\n // Zadarma re-rings a declined call for ~30 s (return_timeout). Swallow those (§3.4).\r\n rejectGuardMs: 35_000,\r\n // A push is not an INVITE (§3.3). If none arrives, tear the fresh UA down.\r\n noInviteTeardownMs: 5_000,\r\n // A ceiling on ICE gathering, not a target: JsSIP sends as soon as gathering completes. 3 s leaves\r\n // room for a TURN allocation (whose relay candidates arrive last) while still bounding the ~40 s\r\n // Chrome can spend against an unreachable server. See `capIceGathering`.\r\n iceGatheringCapMs: 3_000,\r\n};\r\n\r\nexport interface ZadarmaKeyResponse {\r\n readonly key: string;\r\n readonly sip: string;\r\n readonly keyExpiresAt?: string;\r\n}\r\n\r\n/** The minimal push-socket surface we consume (Zadarma's Socket.IO v2 gateway). */\r\nexport interface ZadarmaPushSocket {\r\n on(event: 'connect' | 'disconnect' | 'connect_error' | 'update', cb: () => void): void;\r\n /**\r\n * Send to the gateway. Needed for the presence pings — see `PING`.\r\n *\r\n * Its absence is why the extension went offline after a few idle minutes: the socket cannot be\r\n * kept alive by a client that has no way to say anything on it.\r\n */\r\n emit(event: string, payload?: unknown): void;\r\n close(): void;\r\n readonly connected: boolean;\r\n}\r\n\r\nexport interface ZadarmaDeps {\r\n /**\r\n * Fetch `{ key, sip }` from the Coventia backend (authenticated with the voice token upstream).\r\n * The key is a scoped, refreshable credential — NEVER a stored SIP password (SPEC §6.4, §6.5).\r\n */\r\n fetchKey(): Promise<ZadarmaKeyResponse>;\r\n\r\n /**\r\n * Exchange the key for ephemeral SIP credentials. MUST run in the browser: Zadarma geo-routes the\r\n * registrar `domain` (and the matching `pass`) to the caller's region (SPEC §3.1). The returned\r\n * password is ephemeral and must never be persisted.\r\n */\r\n exchangeWebphoneData(key: string, sip: string): Promise<{ pass: string; domain: string }>;\r\n\r\n /** Open the Zadarma push socket for a `(key, sip)`. */\r\n createPushSocket(url: string): ZadarmaPushSocket;\r\n\r\n /**\r\n * OPTIONAL: proactively rebuild the push socket, via `cb`, while the tab is hidden.\r\n *\r\n * The obvious fix — ping more often — does not work: `push-socket-reliability.md` §5.2 confirmed\r\n * empirically that Zadarma's gateway is keyed to the raw Engine.IO ping specifically, and that ping\r\n * dies under background-tab timer throttling no matter how much other traffic the socket carries.\r\n * Rather than trying to keep a fragile internal timer alive, `cb` tears the connection down and\r\n * rebuilds it on a schedule safely inside the failure window (§5.3) — a fresh socket always gets\r\n * its own fresh, reliably-successful first ping cycle.\r\n *\r\n * Returns a teardown function. Omitted (tests, SSR, no `Worker`) and `ZadarmaPushSignal` simply\r\n * does not self-heal in the background — exactly today's behaviour.\r\n */\r\n watchBackgroundReconnect?(cb: () => void): () => void;\r\n}\r\n\r\n/** `wss://ws.zadarma.com/webrtc_pushes?key=…&integrationType=site&sipId=…` */\r\nexport function buildPushUrl(key: string, sip: string): string {\r\n const params = new URLSearchParams({ key, integrationType: 'site', sipId: sip });\r\n return `wss://ws.zadarma.com/webrtc_pushes?${params.toString()}`;\r\n}\r\n\r\n/**\r\n * The push socket as an `IncomingSignal`. Its `connect()` fetches a key and opens the socket; a\r\n * Zadarma `update` becomes an out-of-band incoming signal the engine reacts to (SPEC §3.2–3.3).\r\n *\r\n * The engine deliberately does NOT ring on this signal — it rings on the SIP INVITE. This only tells\r\n * the engine to register a UA so Zadarma will *send* the INVITE.\r\n */\r\nexport class ZadarmaPushSignal implements IncomingSignal {\r\n #socket: ZadarmaPushSocket | null = null;\r\n #connected = false;\r\n #pingTimers: Array<ReturnType<typeof setInterval>> = [];\r\n #refreshTimer: ReturnType<typeof setTimeout> | undefined;\r\n #unwatchBackground: (() => void) | undefined;\r\n #closed = false;\r\n readonly #listeners = new Set<() => void>();\r\n readonly #connectionListeners = new Set<(connected: boolean) => void>();\r\n\r\n constructor(private readonly deps: ZadarmaDeps) {}\r\n\r\n async connect(): Promise<void> {\r\n if (this.#socket) return;\r\n this.#closed = false;\r\n // Armed once per signal lifetime, not per reopen — `#reopen()` itself is the callback.\r\n this.#unwatchBackground ??= this.deps.watchBackgroundReconnect?.(() => void this.#reopen());\r\n await this.#open();\r\n }\r\n\r\n async #open(): Promise<void> {\r\n const { key, sip, keyExpiresAt } = await this.deps.fetchKey();\r\n if (this.#closed) return;\r\n\r\n const socket = this.deps.createPushSocket(buildPushUrl(key, sip));\r\n this.#socket = socket;\r\n\r\n socket.on('connect', () => {\r\n this.#setConnected(true);\r\n this.#startPings(socket);\r\n });\r\n socket.on('update', () => this.#emit());\r\n // Both mean the same thing to us: no INVITE can arrive right now.\r\n socket.on('disconnect', () => {\r\n this.#stopPings();\r\n this.#setConnected(false);\r\n });\r\n socket.on('connect_error', () => {\r\n this.#stopPings();\r\n this.#setConnected(false);\r\n });\r\n\r\n this.#armRefresh(keyExpiresAt);\r\n }\r\n\r\n /**\r\n * Presence pings — **the reason an idle tab keeps receiving calls**.\r\n *\r\n * Zadarma's gateway decides whether the extension is online, and it decides from these. Without\r\n * them the socket goes quiet, Zadarma marks the extension offline within minutes, and INVITEs stop\r\n * being routed — with no error anywhere, because nothing failed. The only visible symptom is that\r\n * the phone silently stops ringing until the page is reloaded.\r\n *\r\n * The names and intervals are Zadarma's, taken from the CRM hook that ran this in production for\r\n * years. They are not ours to tune.\r\n */\r\n #startPings(socket: ZadarmaPushSocket): void {\r\n this.#stopPings();\r\n this.#pingTimers = [\r\n setInterval(() => socket.emit('localPing', {}), PING.localMs),\r\n setInterval(() => socket.emit('globalPing', {}), PING.globalMs),\r\n ];\r\n }\r\n\r\n #stopPings(): void {\r\n for (const t of this.#pingTimers) clearInterval(t);\r\n this.#pingTimers = [];\r\n }\r\n\r\n /**\r\n * Rebuild the socket before the WebRTC key expires.\r\n *\r\n * The key is baked into the socket URL, so socket.io's own reconnection re-presents whatever it was\r\n * given at construction. A tab left open past the key's 72 h lifetime would reconnect forever with\r\n * a dead credential. Absolute, from the backend's `key_expires_at`, so it targets the real expiry\r\n * rather than restarting a countdown on every reconnect.\r\n */\r\n #armRefresh(keyExpiresAt: string | undefined): void {\r\n clearTimeout(this.#refreshTimer);\r\n if (!keyExpiresAt) return;\r\n const ms = new Date(keyExpiresAt).getTime() - Date.now() - KEY_REFRESH_BUFFER_MS;\r\n if (!Number.isFinite(ms) || ms <= 0) return;\r\n this.#refreshTimer = setTimeout(() => void this.#reopen(), ms);\r\n }\r\n\r\n async #reopen(): Promise<void> {\r\n if (this.#closed) return;\r\n this.#stopPings();\r\n this.#socket?.close();\r\n this.#socket = null;\r\n this.#setConnected(false);\r\n try {\r\n await this.#open();\r\n } catch {\r\n // A failed refresh leaves us disconnected and SAYING so, which is the point: the engine can\r\n // surface it instead of the phone quietly never ringing again.\r\n }\r\n }\r\n\r\n disconnect(): void {\r\n this.#closed = true;\r\n this.#stopPings();\r\n clearTimeout(this.#refreshTimer);\r\n this.#refreshTimer = undefined;\r\n this.#unwatchBackground?.();\r\n this.#unwatchBackground = undefined;\r\n this.#socket?.close();\r\n this.#socket = null;\r\n this.#setConnected(false);\r\n }\r\n\r\n onSignal(listener: () => void): () => void {\r\n this.#listeners.add(listener);\r\n return () => this.#listeners.delete(listener);\r\n }\r\n\r\n onConnectionChange(listener: (connected: boolean) => void): () => void {\r\n this.#connectionListeners.add(listener);\r\n return () => this.#connectionListeners.delete(listener);\r\n }\r\n\r\n get connected(): boolean {\r\n return this.#connected;\r\n }\r\n\r\n #setConnected(connected: boolean): void {\r\n if (this.#connected === connected) return;\r\n this.#connected = connected;\r\n for (const listener of [...this.#connectionListeners]) listener(connected);\r\n }\r\n\r\n #emit(): void {\r\n for (const listener of [...this.#listeners]) listener();\r\n }\r\n}\r\n\r\nexport class ZadarmaProvider implements SipProvider {\r\n readonly name = 'zadarma';\r\n // Zadarma's PBX ignores a standing registration — it routes INVITEs only to a UA that registered\r\n // in response to a push (SPEC §3.2). Hence on-demand.\r\n readonly registrationMode: RegistrationMode = 'on-demand';\r\n readonly quirks: ProviderQuirks = ZADARMA_QUIRKS;\r\n readonly incomingSignal: IncomingSignal;\r\n\r\n constructor(private readonly deps: ZadarmaDeps) {\r\n this.incomingSignal = new ZadarmaPushSignal(deps);\r\n }\r\n\r\n /**\r\n * Per-registration credentialing: fetch a scoped key, then exchange it in the browser for\r\n * ephemeral SIP creds. The key fetch is cheap and backend-cached; doing it here (rather than\r\n * sharing state with the push signal) keeps the two paths independent.\r\n */\r\n async provision(): Promise<SipCredentials> {\r\n const { key, sip } = await this.deps.fetchKey();\r\n const { pass, domain } = await this.deps.exchangeWebphoneData(key, sip);\r\n return {\r\n uri: `sip:${sip}@${domain}`,\r\n authUser: sip,\r\n password: pass,\r\n wsUrl: `wss://${domain}:4443`,\r\n };\r\n }\r\n}\r\n","import type { TokenSource } from '../../core/types.js';\r\nimport { invalidateToken } from '../../core/token-cache.js';\r\nimport { createError } from '../../core/errors.js';\r\nimport type { ZadarmaDeps, ZadarmaKeyResponse, ZadarmaPushSocket } from './zadarma.js';\r\n\r\n/** The key fetch was refused. Distinct so only THIS failure triggers a token re-mint. */\r\nclass UnauthorizedKeyError extends Error {}\r\n\r\n/**\r\n * The REAL, browser-only Zadarma network deps: the backend key fetch, the JSONP credential exchange,\r\n * and the Socket.IO v2 push socket. Isolated here (not in zadarma.ts) so the provider stays\r\n * unit-testable and so these browser globals never enter the SSR import path — `socket.io-client` is\r\n * dynamically imported, and every function touches `window`/`document`/`fetch` only when called\r\n * (SPEC §10.2).\r\n *\r\n * Ported from the legacy `useSipUA.js` / `useZadarmaWebrtcLogin.js`, with the geo-routing rationale\r\n * intact (SPEC §3.1).\r\n */\r\n\r\nexport interface ZadarmaBrowserConfig {\r\n /** Coventia backend base URL, e.g. `https://api.coventia.es`. */\r\n readonly apiBase: string;\r\n /** Provides the auth token for the backend key fetch. */\r\n readonly tokenSource: TokenSource;\r\n /** Selected organization id, appended to the key request. */\r\n readonly organizationId?: string | undefined;\r\n}\r\n\r\n/**\r\n * Fetch `{ key, sip }` from the Coventia backend.\r\n *\r\n * Today this targets the existing `/phone/webrtc/key`, authenticated with the CRM's own session.\r\n * Phase 2 moves it to `/api/v1/phone/webrtc/key`, authenticated with a voice token and CORS-gated —\r\n * which is what lets a PARTNER's browser call it from their origin (SPEC §6.6, hop ③). Only the URL\r\n * and the credential change; this function's shape does not, which is the point.\r\n *\r\n * The key is a scoped, refreshable credential — never a stored SIP password (SPEC §6.4).\r\n */\r\nexport function makeFetchKey(config: ZadarmaBrowserConfig): () => Promise<ZadarmaKeyResponse> {\r\n const attempt = async (): Promise<ZadarmaKeyResponse> => {\r\n // The host's own endpoint failing is a DIFFERENT failure from us refusing their token, and the\r\n // fix differs too — \"your broker is down\" vs \"mint a fresh one\". Flattening both into one code\r\n // would send an integrator to the wrong place.\r\n let token: string;\r\n try {\r\n ({ token } = await config.tokenSource.getToken());\r\n } catch (cause) {\r\n throw createError('auth/token-fetch-failed', cause);\r\n }\r\n const qs = config.organizationId\r\n ? `?organization_id=${encodeURIComponent(config.organizationId)}`\r\n : '';\r\n const res = await fetch(`${config.apiBase}/phone/webrtc/key${qs}`, {\r\n headers: { Authorization: token },\r\n });\r\n if (!res.ok) {\r\n const body = (await res.json().catch(() => null)) as { message?: string } | null;\r\n const message = body?.message ?? `webrtc/key HTTP ${res.status}`;\r\n throw res.status === 401 ? new UnauthorizedKeyError(message) : new Error(message);\r\n }\r\n const data = (await res.json()) as {\r\n key: string;\r\n sip: string;\r\n key_expires_at?: string;\r\n };\r\n return {\r\n key: data.key,\r\n sip: data.sip,\r\n ...(data.key_expires_at ? { keyExpiresAt: data.key_expires_at } : {}),\r\n };\r\n };\r\n\r\n /*\r\n * Retry once on 401 with a fresh token — same reasoning as `createGet`, and it matters more here:\r\n * this runs per registration, so a token revoked mid-session would otherwise block every call\r\n * until its TTL elapsed.\r\n */\r\n return async () => {\r\n try {\r\n return await attempt();\r\n } catch (error) {\r\n if (!(error instanceof UnauthorizedKeyError)) throw error;\r\n invalidateToken(config.tokenSource);\r\n try {\r\n return await attempt();\r\n } catch (retryError) {\r\n // Refused twice, the second time with a freshly minted token — so the credential itself is\r\n // rejected, not merely stale.\r\n if (retryError instanceof UnauthorizedKeyError) {\r\n throw createError('auth/token-rejected', retryError);\r\n }\r\n throw retryError;\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Exchange the key for `{ pass, domain }` via Zadarma's `get_webphone_data.php`.\r\n *\r\n * **MUST run in the browser** (SPEC §3.1) — this is the load-bearing constraint and it has not\r\n * changed: Zadarma geo-routes the registrar `domain`, and the `pass` that matches it, to the\r\n * datacenter nearest the *caller's* IP. Called server-side it pins every user to the backend's\r\n * region and REGISTER is refused.\r\n *\r\n * **`fetch`, not a `<script>` tag.** The endpoint is JSONP-shaped — it always answers\r\n * `callbackName({...})`, with no raw-JSON mode — but it now sends `Access-Control-Allow-Origin: *`,\r\n * so the response is readable and the wrapper is just text to strip. Verified against the live\r\n * endpoint before switching.\r\n *\r\n * The old `<script>` injection worked, but cost three things:\r\n * - **A console warning on every call.** The response is served as `application/json`, and a\r\n * classic script with a non-JS MIME type warns in Firefox (\"loaded despite its MIME type…\").\r\n * It executes, so it was harmless — but it is noise in a console that should stay readable, and\r\n * nothing on our side could silence it.\r\n * - **A CSP demand we should not make.** A `<script src>` to a third-party origin needs\r\n * `script-src https://api.zadarma.com` — permission to EXECUTE their code. `fetch` needs\r\n * `connect-src`, which is permission to TALK to them. For a reseller embedding this SDK that is\r\n * a materially smaller ask (SPEC §7, Phase 3: we publish the CSP directives).\r\n * - **A global.** A `window[cbName]` per exchange, plus a `<script>` in `<head>` to clean up.\r\n *\r\n * What we keep is real error handling: an HTTP status, a network failure and a timeout are now\r\n * distinguishable, where a `<script>` could only ever report \"onerror\".\r\n */\r\nexport async function exchangeWebphoneData(\r\n key: string,\r\n sip: string,\r\n timeoutMs = 10_000,\r\n): Promise<{ pass: string; domain: string }> {\r\n const params = new URLSearchParams({\r\n // Still sent: the endpoint wraps its response either way, and naming the callback keeps the\r\n // wrapper predictable rather than relying on their default.\r\n jsonpCallback: 'cv',\r\n integrationType: 'site',\r\n key,\r\n sipId: sip,\r\n });\r\n\r\n let res: Response;\r\n try {\r\n res = await fetch(\r\n `https://api.zadarma.com/sys/webrtc/get_webphone_data.php?${params.toString()}`,\r\n { signal: AbortSignal.timeout(timeoutMs) },\r\n );\r\n } catch (cause) {\r\n const timedOut = cause instanceof DOMException && cause.name === 'TimeoutError';\r\n throw new Error(timedOut ? 'get_webphone_data timed out' : 'get_webphone_data network error', {\r\n cause,\r\n });\r\n }\r\n if (!res.ok) throw new Error(`get_webphone_data HTTP ${res.status}`);\r\n\r\n const data = parseJsonp(await res.text());\r\n if (!data || data.error || !data.pass || !data.domain) {\r\n throw new Error(data?.error?.content ?? 'get_webphone_data: missing SIP credentials');\r\n }\r\n return { pass: data.pass, domain: data.domain };\r\n}\r\n\r\ninterface WebphoneData {\r\n error?: { content?: string };\r\n pass?: string;\r\n domain?: string;\r\n}\r\n\r\n/**\r\n * `cv({...})` → the object.\r\n *\r\n * Sliced between the FIRST `(` and the LAST `)` rather than matched against the callback name we\r\n * sent: the endpoint ignores an empty `jsonpCallback` and falls back to its own literal\r\n * `jsonpCallback(...)`, so trusting the name we asked for would break on a response we can see it\r\n * actually returns.\r\n */\r\nfunction parseJsonp(text: string): WebphoneData | null {\r\n const open = text.indexOf('(');\r\n const close = text.lastIndexOf(')');\r\n const body = open !== -1 && close > open ? text.slice(open + 1, close) : text;\r\n try {\r\n return JSON.parse(body) as WebphoneData;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Open the Zadarma push socket (Socket.IO **v2** — their gateway is v2, hence the pinned dependency).\r\n * `socket.io-client` is dynamically imported so it never enters the SSR bundle.\r\n *\r\n * Note: because the ZadarmaPushSocket surface is synchronous but the socket.io import is async, the\r\n * returned wrapper connects lazily and buffers `on()` registrations until the client is ready.\r\n */\r\nexport function createPushSocket(url: string): ZadarmaPushSocket {\r\n const handlers = new Map<string, Array<() => void>>();\r\n let client: {\r\n on(e: string, cb: () => void): void;\r\n emit(e: string, payload?: unknown): void;\r\n close(): void;\r\n connected?: boolean;\r\n } | null = null;\r\n let closed = false;\r\n\r\n void (async () => {\r\n // A LITERAL specifier, and it must stay one. This was `const s = 'socket.io-client-v2'` behind an\r\n // indirection to dodge TS7016 on an untyped module — which no bundler can statically analyse, so\r\n // the browser got a bare specifier, the push socket never connected, `start()` rejected, and the\r\n // softphone never reached `online`. The types now come from `src/socket-io.d.ts`, which is what\r\n // should have solved it in the first place (`jssip` was always a literal and always worked).\r\n const mod = await import('socket.io-client-v2');\r\n if (closed) return;\r\n // v2's connector arrives as `default` under ESM interop and as `io` when it does not.\r\n const connect = mod.default ?? mod.io;\r\n if (!connect) throw new Error('socket.io-client-v2: no connect export');\r\n const c = connect(url, {\r\n transports: ['websocket'],\r\n reconnection: true,\r\n // The push socket drops on a schedule under background-tab timer throttling, NOT from network\r\n // flakiness — every reconnect observed succeeds on the first attempt (push-socket-reliability.md\r\n // §2.3, §5.1). There is nothing to back off from on that first attempt, so waiting the old 10s\r\n // before retrying was pure, uncontested exposure — an inbound call landing in that window is\r\n // lost, unqueued (Zadarma's gateway speaks EIO3, no offline redelivery). `reconnectionDelayMax`\r\n // stays as the ceiling for a genuinely down gateway, where backing off still matters.\r\n reconnectionDelay: 300,\r\n reconnectionDelayMax: 30_000,\r\n });\r\n client = c;\r\n for (const [event, cbs] of handlers) for (const cb of cbs) c!.on(event, cb);\r\n })();\r\n\r\n return {\r\n on(event, cb) {\r\n const list = handlers.get(event) ?? [];\r\n list.push(cb);\r\n handlers.set(event, list);\r\n client?.on(event, cb);\r\n },\r\n emit(event, payload) {\r\n // Dropped rather than queued when the client is not up yet: every caller is a periodic ping,\r\n // so the next one is seconds away and a replayed backlog would be worse than a missed beat.\r\n client?.emit(event, payload);\r\n },\r\n close() {\r\n closed = true;\r\n client?.close();\r\n },\r\n get connected() {\r\n return client?.connected ?? false;\r\n },\r\n };\r\n}\r\n\r\n/** How long the tab can stay hidden before we proactively rebuild the socket. See `watchBackgroundReconnect`. */\r\nconst BACKGROUND_RECONNECT_MS = 20_000;\r\n\r\n/**\r\n * The Worker's entire job: an alarm clock, nothing else. It owns no socket state and knows nothing\r\n * about Zadarma — Chrome does not apply background-tab timer throttling to Worker threads, only to\r\n * the page's own JS context, so ticking here stays reliable exactly where `setInterval` on the main\r\n * thread was proven not to be (`push-socket-reliability.md` §5.2, §5.3). `start`/`stop` rather than\r\n * a fire-and-forget interval so the SAME worker can be reused across every hidden/visible cycle in a\r\n * session instead of being spun up and torn down each time.\r\n */\r\nconst HEARTBEAT_WORKER_SOURCE = `\r\n let timer;\r\n self.onmessage = (e) => {\r\n if (e.data.type === 'start') timer = setInterval(() => self.postMessage('tick'), e.data.intervalMs);\r\n else if (e.data.type === 'stop') { clearInterval(timer); timer = undefined; }\r\n };\r\n`;\r\n\r\n/**\r\n * Proactively rebuilds the push socket, via `cb`, on a schedule while the tab is hidden — the real\r\n * implementation of the optional `ZadarmaDeps` seam (`zadarma.ts`).\r\n *\r\n * **Constructed lazily, only when called** (SPEC §10.3 — no browser globals at module scope): safe\r\n * to import on a server, and a host that never calls it (tests, an environment with no `Worker`)\r\n * pays nothing.\r\n */\r\nexport function watchBackgroundReconnect(cb: () => void): () => void {\r\n if (typeof document === 'undefined' || typeof Worker === 'undefined') return () => {};\r\n\r\n let worker: Worker | null = null;\r\n let blobUrl: string | null = null;\r\n\r\n const ensureWorker = (): Worker => {\r\n if (!worker) {\r\n blobUrl = URL.createObjectURL(new Blob([HEARTBEAT_WORKER_SOURCE], { type: 'application/javascript' }));\r\n worker = new Worker(blobUrl);\r\n worker.onmessage = (e: MessageEvent) => {\r\n if (e.data === 'tick') cb();\r\n };\r\n }\r\n return worker;\r\n };\r\n\r\n const onVisibilityChange = (): void => {\r\n if (document.visibilityState === 'hidden') {\r\n ensureWorker().postMessage({ type: 'start', intervalMs: BACKGROUND_RECONNECT_MS });\r\n } else {\r\n worker?.postMessage({ type: 'stop' });\r\n }\r\n };\r\n\r\n document.addEventListener('visibilitychange', onVisibilityChange);\r\n // A signal can `connect()` while the tab is already hidden (e.g. a backgrounded pre-loaded tab).\r\n if (document.visibilityState === 'hidden') onVisibilityChange();\r\n\r\n return () => {\r\n document.removeEventListener('visibilitychange', onVisibilityChange);\r\n worker?.postMessage({ type: 'stop' });\r\n worker?.terminate();\r\n if (blobUrl) URL.revokeObjectURL(blobUrl);\r\n };\r\n}\r\n\r\n/** Assemble the real browser deps for a `ZadarmaProvider`. */\r\nexport function makeZadarmaBrowserDeps(config: ZadarmaBrowserConfig): ZadarmaDeps {\r\n return {\r\n fetchKey: makeFetchKey(config),\r\n exchangeWebphoneData: (key, sip) => exchangeWebphoneData(key, sip),\r\n createPushSocket,\r\n watchBackgroundReconnect,\r\n };\r\n}\r\n","import { SipCallEngine } from './sip/ua.js';\r\nimport { loadJsSipFactory } from './sip/real-factory.js';\r\nimport { ZadarmaProvider } from './providers/zadarma.js';\r\nimport { makeZadarmaBrowserDeps, type ZadarmaBrowserConfig } from './providers/zadarma-browser.js';\r\nimport { cachedTokenSource } from '../core/token-cache.js';\r\nimport type { CallEngine } from '../core/engine.js';\r\n\r\n/**\r\n * Assemble a production Zadarma call engine: the real browser deps (backend key fetch + JSONP\r\n * exchange + push socket) → `ZadarmaProvider` → `SipCallEngine`, with jssip loaded lazily via a\r\n * dynamic import (SPEC §10.2 — importing the package stays SSR-safe).\r\n *\r\n * This is the real thing the app and the e2e smoke test use. It is intentionally NOT the automatic\r\n * default of `createSoftphone` yet — the app wires it explicitly in S9, and the voice-token endpoint\r\n * migration is Phase 2 (SPEC §6).\r\n */\r\nexport function createZadarmaEngine(config: ZadarmaBrowserConfig): CallEngine {\r\n // Cached here as well as in `createSoftphone`: a host passes the same `TokenSource` object to both,\r\n // and `cachedTokenSource` is keyed on that object — so the two share ONE token rather than minting\r\n // one each. Without this the WebRTC-key fetch, which runs per registration, would be uncached.\r\n const provider = new ZadarmaProvider(\r\n makeZadarmaBrowserDeps({ ...config, tokenSource: cachedTokenSource(config.tokenSource) }),\r\n );\r\n return new SipCallEngine(provider, loadJsSipFactory);\r\n}\r\n","import type { TypedEmitter } from './events.js';\r\n\r\n/**\r\n * A tiny typed event emitter. Framework-free — no Node `events`, no browser globals (SPEC §10.2).\r\n *\r\n * `on()` / `once()` return an unsubscribe function (SPEC §5.3 / events.ts): cleanup is impossible to\r\n * get wrong in a React effect, and there is no opaque handle to retain (Vonage's mistake).\r\n *\r\n * A listener that throws must not stop the other listeners or corrupt the emitter — a bug in the\r\n * host's UI code should never take the call engine down mid-call. Throws are routed to `onError` so\r\n * the engine can log them without swallowing them silently.\r\n */\r\n// Unconstrained, matching TypedEmitter: the event maps are keyed by enum members (CallEvent,\r\n// SoftphoneEvent), which are closed object types, not string index signatures.\r\nexport class Emitter<M> implements TypedEmitter<M> {\r\n // A copy of the listener set is iterated on emit, so a listener that unsubscribes (or subscribes)\r\n // during dispatch does not mutate the set mid-iteration.\r\n readonly #listeners = new Map<keyof M, Set<(payload: never) => void>>();\r\n\r\n constructor(private readonly onListenerError?: (event: keyof M, error: unknown) => void) {}\r\n\r\n on<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void {\r\n let set = this.#listeners.get(event);\r\n if (!set) {\r\n set = new Set();\r\n this.#listeners.set(event, set);\r\n }\r\n set.add(listener as (payload: never) => void);\r\n return () => this.off(event, listener);\r\n }\r\n\r\n once<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void {\r\n const wrapped = (payload: M[E]): void => {\r\n off();\r\n listener(payload);\r\n };\r\n const off = this.on(event, wrapped);\r\n return off;\r\n }\r\n\r\n off<E extends keyof M>(event: E, listener: (payload: M[E]) => void): void {\r\n const set = this.#listeners.get(event);\r\n if (!set) return;\r\n set.delete(listener as (payload: never) => void);\r\n if (set.size === 0) this.#listeners.delete(event);\r\n }\r\n\r\n emit<E extends keyof M>(event: E, payload: M[E]): void {\r\n const set = this.#listeners.get(event);\r\n if (!set) return;\r\n for (const listener of [...set]) {\r\n try {\r\n (listener as (p: M[E]) => void)(payload);\r\n } catch (error) {\r\n // A throwing listener must not abort the dispatch loop or bubble into the engine.\r\n this.onListenerError?.(event, error);\r\n }\r\n }\r\n }\r\n\r\n /** Drop every listener. Called on teardown so a destroyed object leaks nothing. */\r\n clear(): void {\r\n this.#listeners.clear();\r\n }\r\n}\r\n","import type { Call, CallState, HangupReason, HoldOriginator } from './call.js';\r\nimport type { SoftphoneError } from './errors.js';\r\nimport type { SoftphoneState } from './types.js';\r\n\r\n/**\r\n * Typed events.\r\n *\r\n * Enums rather than magic strings: autocomplete, and rename-safety. Daily's magic strings are the\r\n * counter-example — a typo in `'participant-joined'` is a silent no-op forever.\r\n *\r\n * Each map narrows the listener payload PER EVENT, which is the thing that would have been painful\r\n * to express in JSDoc and is a large part of why this package is TypeScript (SPEC §2.2).\r\n */\r\n\r\nexport enum SoftphoneEvent {\r\n /** Lifecycle. `online` = reachable, NOT SIP-registered — see SoftphoneState. */\r\n StateChanged = 'stateChanged',\r\n /** An INVITE landed. The Call is `ringing`; answer or reject it. */\r\n IncomingCall = 'incomingCall',\r\n /**\r\n * The active call changed — a call began (inbound OR outbound) or ended. Fires for both\r\n * directions, unlike IncomingCall. This is what a UI binds to in order to track \"the current call\"\r\n * without threading the Call through every component.\r\n */\r\n ActiveCallChanged = 'activeCallChanged',\r\n /**\r\n * The browser refused to play audio (autoplay policy).\r\n *\r\n * This is the single most under-documented failure in the industry and it is how calls get MISSED:\r\n * a blocked ringtone rings silently. Surface a button and call `startAudio()` from its click\r\n * handler. Only Twilio and LiveKit document it properly.\r\n */\r\n PlaybackBlocked = 'playbackBlocked',\r\n /**\r\n * `softphone.audio` changed: a device was plugged/unplugged (`inputs`/`outputs`), or the selected\r\n * input/output changed (`currentInputId`/`currentOutputId`). One event for all of it — re-read\r\n * `softphone.audio` rather than inspecting the payload, which carries nothing (`useDevices` does\r\n * exactly this).\r\n */\r\n DeviceChange = 'deviceChange',\r\n /** Fatal or non-fatal — narrow on `error.fatal`. */\r\n Error = 'error',\r\n /**\r\n * This tab's leadership changed (`multi-tab-leader-election.md`) — a DIFFERENT axis from\r\n * `StateChanged`: `state` is reachability, this is whether this tab is even allowed to be\r\n * reachable at all while another tab holding the same extension stays open.\r\n */\r\n LeaderChanged = 'leaderChanged',\r\n}\r\n\r\nexport interface SoftphoneEventMap {\r\n [SoftphoneEvent.StateChanged]: { readonly state: SoftphoneState; readonly previous: SoftphoneState };\r\n [SoftphoneEvent.IncomingCall]: Call;\r\n [SoftphoneEvent.ActiveCallChanged]: { readonly call: Call | null };\r\n [SoftphoneEvent.PlaybackBlocked]: void;\r\n [SoftphoneEvent.DeviceChange]: void;\r\n [SoftphoneEvent.Error]: SoftphoneError;\r\n [SoftphoneEvent.LeaderChanged]: { readonly isLeader: boolean };\r\n}\r\n\r\nexport enum CallEvent {\r\n /** Any transition. The catch-all; the specific events below are conveniences. */\r\n StateChanged = 'stateChanged',\r\n /** Media is flowing. */\r\n Accepted = 'accepted',\r\n /** Terminal. Always fires exactly once, whatever the reason. */\r\n Ended = 'ended',\r\n Muted = 'muted',\r\n /** `originator: 'remote'` means the OTHER party held US — new capability; today's softphone is blind to it. */\r\n Held = 'held',\r\n Unheld = 'unheld',\r\n /** Media lost; an ICE restart is in flight. The call is NOT over — show a banner. */\r\n Reconnecting = 'reconnecting',\r\n Reconnected = 'reconnected',\r\n DigitsSent = 'digitsSent',\r\n /** The contact name resolved after the call was already ringing. */\r\n RemotePartyUpdated = 'remotePartyUpdated',\r\n /**\r\n * The remote audio stream is available. Attach it to an `<audio>` element and play it — this is\r\n * how the caller is actually HEARD. Core cannot do this itself (it must not touch `document` —\r\n * SPEC §10.2); the UI layer or the host owns the audio element.\r\n */\r\n RemoteStream = 'remoteStream',\r\n}\r\n\r\nexport interface CallEventMap {\r\n [CallEvent.StateChanged]: { readonly state: CallState; readonly previous: CallState };\r\n [CallEvent.Accepted]: void;\r\n [CallEvent.Ended]: { readonly reason: HangupReason; readonly error?: SoftphoneError };\r\n [CallEvent.Muted]: { readonly muted: boolean };\r\n [CallEvent.Held]: { readonly originator: HoldOriginator };\r\n [CallEvent.Unheld]: { readonly originator: HoldOriginator };\r\n [CallEvent.Reconnecting]: void;\r\n [CallEvent.Reconnected]: void;\r\n [CallEvent.DigitsSent]: { readonly digits: string };\r\n [CallEvent.RemotePartyUpdated]: void;\r\n [CallEvent.RemoteStream]: { readonly stream: MediaStream };\r\n}\r\n\r\n/**\r\n * `on()` returns an unsubscribe function.\r\n *\r\n * Deliberate: it makes the cleanup impossible to get wrong in a React effect\r\n * (`useEffect(() => call.on(...), [call])`) and it avoids Vonage's design, where `on()` returns an\r\n * opaque `symbol` you must retain in order to ever call `off()`.\r\n */\r\nexport interface TypedEmitter<M> {\r\n on<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void;\r\n once<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void;\r\n off<E extends keyof M>(event: E, listener: (payload: M[E]) => void): void;\r\n}\r\n","import type { CallEventMap, TypedEmitter } from './events.js';\r\nimport type { CallDirection, RemoteParty } from './types.js';\r\n\r\n/**\r\n * THE call state machine. Singular.\r\n *\r\n * Today there are three competing representations (SPEC §5.3):\r\n * - `callStateRef` — a ref, read synchronously in guards to dodge stale closures\r\n * - `status` — a useState that can lag a render behind it\r\n * - `consultStatus` — the attended-transfer leg, tracked separately\r\n * They can disagree. Collapsing them into one is the single biggest correctness win in this\r\n * refactor, and the consult leg simply becomes its own Call.\r\n *\r\n *\r\n * INBOUND OUTBOUND\r\n * ─────── ────────\r\n * ringing ──answer()──► answering dialing ──180/183──► early\r\n * │ │ │ │\r\n * │ ▼ └────────┬───────────┘\r\n * │ reject() ┌───────┐ │ 200 OK\r\n * └──────────────┐ │ ▼ ▼\r\n * │ │ active ◄─────────────────────\r\n * │ │ ▲ │ ▲ │\r\n * │ │ │ │ │ └──hold()/remote──► held ──unhold()──┐\r\n * │ │ │ │ └───────────────────────────────────────-┘\r\n * │ │ │ └──ICE fail──► reconnecting ──recovered──┐\r\n * │ │ └────────────────────────────────────────────┘\r\n * ▼ ▼ │\r\n * ┌──────────────▼────┐\r\n * │ ended │ TERMINAL. Reachable from every state.\r\n * └────────────────────┘\r\n *\r\n * NOT modelled here, deliberately:\r\n *\r\n * - **`rejecting`.** Today's engine has one, purely to swallow Zadarma's `return_timeout` retries\r\n * for 35 s after a decline (SPEC §3.4). That is a PROVIDER quirk — it belongs in\r\n * `providers/zadarma.ts` as `quirks.rejectGuardMs`, not in the shared state machine. A rejected\r\n * call is simply `ended`. Our own SIP infrastructure would not need it at all (SPEC §2.3).\r\n *\r\n * - **A pre-INVITE state.** A Call exists only once an INVITE has landed. The \"user answered\r\n * before the INVITE arrived\" case (SPEC §3.1) is an *intent* held on `Softphone`, above the\r\n * call — because there is no call yet to hold it. Same for a pre-INVITE reject, which registers\r\n * a UA specifically so it has something to send the 486 from.\r\n */\r\nexport type CallState =\r\n /** Inbound, INVITE received, not yet answered. */\r\n | 'ringing'\r\n /** Outbound, INVITE sent, no provisional response yet. */\r\n | 'dialing'\r\n /** Outbound, remote is ringing (180/183). May carry early media. */\r\n | 'early'\r\n /** Local answer in flight — 200 OK not yet confirmed. */\r\n | 'answering'\r\n /** Media flowing. */\r\n | 'active'\r\n /** Real SIP hold (`a=sendonly`), by either party. */\r\n | 'held'\r\n /** Media lost, ICE restart in flight. The call is NOT over. */\r\n | 'reconnecting'\r\n /** Terminal. */\r\n | 'ended';\r\n\r\nexport type HoldOriginator = 'local' | 'remote';\r\n\r\nexport type HangupReason =\r\n | 'local'\r\n | 'remote'\r\n | 'rejected'\r\n | 'busy'\r\n | 'no-answer'\r\n | 'transferred'\r\n | 'failed';\r\n\r\n/**\r\n * The legal transitions, as data.\r\n *\r\n * A table rather than scattered `if`s: it is testable in isolation, it is the thing a reviewer can\r\n * actually check against the diagram above, and an illegal transition becomes a caught bug instead\r\n * of a silently wrong UI.\r\n */\r\nexport const CALL_TRANSITIONS = {\r\n ringing: ['answering', 'ended'],\r\n dialing: ['early', 'active', 'ended'],\r\n early: ['active', 'ended'],\r\n answering: ['active', 'ended'],\r\n active: ['held', 'reconnecting', 'ended'],\r\n held: ['active', 'reconnecting', 'ended'],\r\n reconnecting: ['active', 'held', 'ended'],\r\n ended: [],\r\n} as const satisfies Readonly<Record<CallState, readonly CallState[]>>;\r\n\r\n/** The states a call can start in. Everything else is reached by transition. */\r\nexport const INITIAL_STATES = {\r\n inbound: 'ringing',\r\n outbound: 'dialing',\r\n} as const satisfies Readonly<Record<CallDirection, CallState>>;\r\n\r\nexport function canTransition(from: CallState, to: CallState): boolean {\r\n return (CALL_TRANSITIONS[from] as readonly CallState[]).includes(to);\r\n}\r\n\r\nexport function isTerminal(state: CallState): boolean {\r\n return CALL_TRANSITIONS[state].length === 0;\r\n}\r\n\r\n/**\r\n * A single call. Exists only while there is one — there is no `idle`.\r\n *\r\n * The attended-transfer consult leg is *also* a Call. That is what lets one state machine cover\r\n * both, instead of today's separate `consultStatus`.\r\n *\r\n * Implementation lands in S2 (against MockProvider) and S3–S4 (against JsSIP/Zadarma).\r\n */\r\nexport interface Call extends TypedEmitter<CallEventMap> {\r\n readonly id: string;\r\n readonly direction: CallDirection;\r\n readonly state: CallState;\r\n readonly remote: RemoteParty;\r\n /** Wall-clock start of media. Null until `active`. */\r\n readonly answeredAt: Date | null;\r\n\r\n readonly isMuted: boolean;\r\n readonly isOnHold: boolean;\r\n\r\n /**\r\n * The remote party's audio stream, once media is flowing (null before). Attach it to an\r\n * `<audio autoplay>` element to hear the call. Also delivered via `CallEvent.RemoteStream`.\r\n */\r\n readonly remoteStream: MediaStream | null;\r\n\r\n /** Only legal from `ringing`. Throws `call/not-answerable` otherwise. */\r\n answer(): Promise<void>;\r\n /** Only legal from `ringing`. Sends 486 Busy Here. */\r\n reject(): Promise<void>;\r\n /** Legal from any non-terminal state. Idempotent. */\r\n hangup(): Promise<void>;\r\n\r\n mute(muted: boolean): void;\r\n\r\n /**\r\n * Real SIP hold — a re-INVITE with `a=sendonly`, so the PBX can play hold music.\r\n *\r\n * Today this is FAKED by muting (SPEC §5.6): the held caller hears dead silence and cannot tell\r\n * hold from a dropped call. JsSIP implements the real thing natively, so this is a handful of\r\n * lines rather than the SIP work it looks like.\r\n */\r\n hold(): Promise<void>;\r\n unhold(): Promise<void>;\r\n\r\n sendDigits(digits: string): void;\r\n\r\n /** Blind transfer (SIP REFER). Ends this call. */\r\n transfer(target: string): Promise<void>;\r\n\r\n /**\r\n * Attended transfer, part 1: call the target while holding this call.\r\n *\r\n * Returns the consult leg — itself a Call, with its own state machine and events.\r\n */\r\n startConsult(target: string): Promise<Call>;\r\n /** Attended transfer, part 2: bridge the two, ending both. (REFER … Replaces) */\r\n completeTransfer(): Promise<void>;\r\n /** Abandon the consult and resume this call. */\r\n cancelConsult(): Promise<void>;\r\n}\r\n","import { Emitter } from './emitter.js';\r\nimport { CallEvent, type CallEventMap } from './events.js';\r\nimport {\r\n INITIAL_STATES,\r\n canTransition,\r\n isTerminal,\r\n type Call,\r\n type CallState,\r\n type HangupReason,\r\n} from './call.js';\r\nimport type { EngineCall, EngineCallEvent } from './engine.js';\r\nimport type { Contact, RemoteParty } from './types.js';\r\n\r\n/**\r\n * The concrete call. Owns the state machine (SPEC §5.3) and the public event stream.\r\n *\r\n * It never talks to SIP or the provider — it talks to an `EngineCall`, translating that engine's\r\n * RAW signals into public state transitions in one place. The mock's scripted signals and the real\r\n * engine's provider-confirmed ones travel the identical path, which is what keeps the playground\r\n * honest against production.\r\n */\r\nexport class CallImpl extends Emitter<CallEventMap> implements Call {\r\n #state: CallState;\r\n #remote: RemoteParty;\r\n #answeredAt: Date | null = null;\r\n #muted = false;\r\n #onHold = false;\r\n #remoteStream: MediaStream | null = null;\r\n #consultEngineCall: EngineCall | null = null;\r\n #consultCall: Call | null = null;\r\n readonly #unsubscribe: () => void;\r\n\r\n readonly #onListenerError: ((event: keyof CallEventMap, error: unknown) => void) | undefined;\r\n\r\n constructor(\r\n private readonly engineCall: EngineCall,\r\n onListenerError?: (event: keyof CallEventMap, error: unknown) => void,\r\n private readonly onInternalError?: (message: string) => void,\r\n ) {\r\n super(onListenerError);\r\n this.#onListenerError = onListenerError;\r\n this.#state = INITIAL_STATES[engineCall.direction];\r\n this.#remote = {\r\n number: engineCall.remoteNumber,\r\n displayName: engineCall.remoteNumber,\r\n contact: null,\r\n };\r\n this.#unsubscribe = engineCall.onEvent((event) => this.#onEngineEvent(event));\r\n }\r\n\r\n get id(): string {\r\n return this.engineCall.id;\r\n }\r\n get direction() {\r\n return this.engineCall.direction;\r\n }\r\n get state(): CallState {\r\n return this.#state;\r\n }\r\n get remote(): RemoteParty {\r\n return this.#remote;\r\n }\r\n get answeredAt(): Date | null {\r\n return this.#answeredAt;\r\n }\r\n get isMuted(): boolean {\r\n return this.#muted;\r\n }\r\n get isOnHold(): boolean {\r\n return this.#onHold;\r\n }\r\n get remoteStream(): MediaStream | null {\r\n return this.#remoteStream;\r\n }\r\n\r\n // ── Raw engine signals → state machine ──────────────────────────────────────\r\n #onEngineEvent(event: EngineCallEvent): void {\r\n switch (event.type) {\r\n case 'progress':\r\n this.#transition('early');\r\n break;\r\n case 'accepted':\r\n if (this.#answeredAt === null) this.#answeredAt = this.#now();\r\n this.#transition('active');\r\n this.emit(CallEvent.Accepted, undefined);\r\n break;\r\n case 'held':\r\n this.#onHold = true;\r\n if (this.#transition('held')) this.emit(CallEvent.Held, { originator: event.originator });\r\n break;\r\n case 'unheld':\r\n this.#onHold = false;\r\n if (this.#transition('active')) this.emit(CallEvent.Unheld, { originator: event.originator });\r\n break;\r\n case 'reconnecting':\r\n if (this.#transition('reconnecting')) this.emit(CallEvent.Reconnecting, undefined);\r\n break;\r\n case 'reconnected':\r\n if (this.#transition(this.#onHold ? 'held' : 'active'))\r\n this.emit(CallEvent.Reconnected, undefined);\r\n break;\r\n case 'ended':\r\n this.#end(event.reason);\r\n break;\r\n case 'stream':\r\n // Expose the remote audio so the host can play it. Core must not create an <audio> element\r\n // itself (SPEC §10.2) — it hands the stream out and the UI/host attaches it.\r\n this.#remoteStream = event.stream;\r\n this.emit(CallEvent.RemoteStream, { stream: event.stream });\r\n break;\r\n }\r\n }\r\n\r\n // ── Commands ────────────────────────────────────────────────────────────────\r\n async answer(): Promise<void> {\r\n if (this.#state !== 'ringing') {\r\n throw this.#stateError('answer', 'call/not-answerable');\r\n }\r\n // Move to `answering` BEFORE awaiting the engine, so a fast provider \"answered\" signal cannot\r\n // race past the ringing state (SPEC §3.1). The engine confirms with an `accepted` event, which\r\n // is what actually reaches `active`.\r\n this.#transition('answering');\r\n await this.engineCall.answer();\r\n }\r\n\r\n async reject(): Promise<void> {\r\n if (this.#state !== 'ringing') {\r\n throw this.#stateError('reject', 'call/not-answerable');\r\n }\r\n await this.engineCall.reject();\r\n // The engine emits `ended` with reason 'rejected' — no local transition here, so there is one\r\n // path to `ended`.\r\n }\r\n\r\n async hangup(): Promise<void> {\r\n if (isTerminal(this.#state)) return; // idempotent\r\n await this.engineCall.hangup();\r\n }\r\n\r\n mute(muted: boolean): void {\r\n if (this.#muted === muted) return;\r\n this.#muted = muted;\r\n this.engineCall.mute(muted);\r\n this.emit(CallEvent.Muted, { muted });\r\n }\r\n\r\n async hold(): Promise<void> {\r\n if (this.#onHold || (this.#state !== 'active' && this.#state !== 'reconnecting')) return;\r\n await this.engineCall.hold();\r\n // The engine confirms with a `held` event (originator: 'local').\r\n }\r\n\r\n async unhold(): Promise<void> {\r\n if (!this.#onHold) return;\r\n await this.engineCall.unhold();\r\n }\r\n\r\n sendDigits(digits: string): void {\r\n this.engineCall.sendDigits(digits);\r\n this.emit(CallEvent.DigitsSent, { digits });\r\n }\r\n\r\n async transfer(target: string): Promise<void> {\r\n await this.engineCall.transfer(target);\r\n // Blind transfer ends this call; the engine emits `ended` with reason 'transferred'.\r\n }\r\n\r\n async startConsult(target: string): Promise<Call> {\r\n // Put the caller on REAL hold (a=sendonly → the PBX plays hold music) before dialling the\r\n // consult target — otherwise they hear the agent's consult conversation. The legacy softphone\r\n // FAKED this by muting, so the caller heard dead silence (SPEC §5.6); real hold is the fix.\r\n await this.hold();\r\n\r\n const consultEngineCall = await this.engineCall.startConsult(target);\r\n // The consult leg is a first-class Call with its own state machine — this is what replaces\r\n // today's separate `consultStatus` (SPEC §5.3). We keep the underlying engine handle so\r\n // completeTransfer can bridge the two legs (REFER…Replaces).\r\n this.#consultEngineCall = consultEngineCall;\r\n const consult = new CallImpl(consultEngineCall, this.#onListenerError, this.onInternalError);\r\n this.#consultCall = consult;\r\n return consult;\r\n }\r\n\r\n async completeTransfer(): Promise<void> {\r\n if (!this.#consultEngineCall) throw this.#stateError('completeTransfer', 'call/unsupported');\r\n // The bridge (REFER…Replaces) ends both legs; the caller leaves hold by being connected to the\r\n // target, so there is nothing to unhold — the engine emits `ended` on each.\r\n await this.engineCall.completeTransfer(this.#consultEngineCall);\r\n }\r\n\r\n async cancelConsult(): Promise<void> {\r\n if (!this.#consultCall) return;\r\n await this.#consultCall.hangup();\r\n this.#consultCall = null;\r\n this.#consultEngineCall = null;\r\n // Take the caller off hold — the agent is coming back to them.\r\n await this.unhold();\r\n }\r\n\r\n // ── Internals ───────────────────────────────────────────────────────────────\r\n #transition(to: CallState): boolean {\r\n const from = this.#state;\r\n if (from === to) return false;\r\n if (isTerminal(from)) return false; // never leave `ended`\r\n if (!canTransition(from, to)) {\r\n this.onInternalError?.(`illegal call transition ${from} → ${to} (SPEC §5.3)`);\r\n return false;\r\n }\r\n this.#state = to;\r\n this.emit(CallEvent.StateChanged, { state: to, previous: from });\r\n return true;\r\n }\r\n\r\n #end(reason: HangupReason): void {\r\n if (isTerminal(this.#state)) return;\r\n const from = this.#state;\r\n this.#state = 'ended';\r\n this.emit(CallEvent.StateChanged, { state: 'ended', previous: from });\r\n this.emit(CallEvent.Ended, { reason });\r\n this.#unsubscribe();\r\n this.clear();\r\n }\r\n\r\n #stateError(op: string, _code: 'call/not-answerable' | 'call/unsupported'): Error {\r\n // A real SoftphoneError is minted in S2's errors implementation; a plain Error keeps the type\r\n // surface honest until then.\r\n return new Error(`${op}() is not valid while call is '${this.#state}'`);\r\n }\r\n\r\n #now(): Date {\r\n // Isolated so tests can stub it, and so we never reach for a bare `new Date()` in logic.\r\n return new Date();\r\n }\r\n\r\n /** @internal — the Softphone updates caller identity once `resolveContact` resolves. */\r\n setRemoteContact(contact: Contact): void {\r\n this.#remote = {\r\n number: this.#remote.number,\r\n displayName: contact.name ?? this.#remote.number,\r\n contact,\r\n };\r\n this.emit(CallEvent.RemotePartyUpdated, undefined);\r\n }\r\n}\r\n","import type {\r\n AgentsDirectory,\r\n Contact,\r\n ContactsSource,\r\n CoventiaBackendConfig,\r\n HistorySource,\r\n TokenSource,\r\n} from './types.js';\r\n\r\n/** Contacts, history, caller ID and the agents directory, all from one config. */\r\nexport interface BackendSources {\r\n readonly contacts: ContactsSource;\r\n readonly history: HistorySource;\r\n readonly resolveContact: (number: string) => Promise<Contact | null>;\r\n /** The Historial tab's \"filter by person\" directory. Always Coventia's own org membership. */\r\n readonly listAgents: () => Promise<AgentsDirectory>;\r\n}\r\n\r\n/**\r\n * Coventia's phone service, wired from `SoftphoneConfig.coventia`.\r\n *\r\n * **Synchronous object, lazily-loaded module.** The widget needs to know *at first render* whether\r\n * the Contactos and Historial tabs exist, and that is answered by the config alone — so this returns\r\n * immediately, while the ~2 kB adapter is fetched on first use. Core's own entry therefore carries no\r\n * HTTP and no `wa_id`, the same trick used for `jssip`, the flag SVGs and the phone-number metadata.\r\n *\r\n * One instance per softphone: the widget reads it back off `softphone.sources`, so it never builds a\r\n * second set with its own name cache.\r\n */\r\nexport function coventiaBackend(\r\n config: CoventiaBackendConfig,\r\n tokenSource: TokenSource,\r\n /** `SoftphoneConfig.apiBase`, consulted only when `config.baseUrl` is not set. */\r\n fallbackBaseUrl?: string,\r\n): BackendSources {\r\n // Resolved and validated EAGERLY, at `createSoftphone()` time, not deferred into `load()` below —\r\n // a host who configured `coventia` clearly wants contacts/history/caller ID, so a missing base URL\r\n // is a real misconfiguration, and the worst place to surface one is the first time a user opens\r\n // the Contactos tab, long after the phone has been happily reporting itself online.\r\n const baseUrl = config.baseUrl ?? fallbackBaseUrl;\r\n if (!baseUrl) {\r\n throw new Error(\r\n 'No base URL for the Coventia backend. Pass `coventia.baseUrl`, or a top-level `apiBase` on ' +\r\n '`createSoftphone` for it to fall back to.',\r\n );\r\n }\r\n\r\n let pending: Promise<BackendSources> | null = null;\r\n const load = (): Promise<BackendSources> => {\r\n pending ??= import('../coventia/sources.js').then((m) =>\r\n m.coventiaSources({ ...config, baseUrl, tokenSource }),\r\n );\r\n return pending;\r\n };\r\n\r\n return {\r\n contacts: async (params) => (await load()).contacts(params),\r\n history: async (params) => (await load()).history(params),\r\n resolveContact: async (number) => (await load()).resolveContact(number),\r\n listAgents: async () => (await load()).listAgents(),\r\n };\r\n}\r\n","import { Emitter } from './emitter.js';\r\nimport { CallImpl } from './call-impl.js';\r\nimport { coventiaBackend, type BackendSources } from './backend.js';\r\nimport { cachedTokenSource } from './token-cache.js';\r\nimport { CallEvent, SoftphoneEvent, type SoftphoneEventMap } from './events.js';\r\nimport { isBrowser } from './env.js';\r\nimport { webLocksTabLock, type TabLock } from './tabs.js';\r\nimport type { AudioDevices, Softphone } from './softphone.js';\r\nimport type { CallEngine, EngineCall } from './engine.js';\r\nimport type { Call } from './call.js';\r\nimport { isFatal, type FatalSoftphoneError, type SoftphoneError } from './errors.js';\r\nimport type { CallOptions, SoftphoneConfig, SoftphoneState } from './types.js';\r\n\r\n/**\r\n * A page may hold ONE live softphone. A softphone owns singleton browser resources — the microphone,\r\n * a registration, an RTCPeerConnection — and a second instance means a second `getUserMedia` and two\r\n * registrations fighting over one extension. Daily throws on a second call object for the same\r\n * reason; we follow it.\r\n *\r\n * Module-scope, but not a browser global, so it is SSR-safe (SPEC §10.2): it is a plain boolean that\r\n * flips in `start()`, never at import.\r\n */\r\nlet liveInstance = false;\r\n\r\n/**\r\n * The real device surface (SPEC §7 Phase 2). Live `enumerateDevices()`, reactive to the browser's own\r\n * `devicechange`, input switching delegated to the engine (`CallEngine.setInputDevice` — the SIP\r\n * engine implements it; a mock with nothing real to swap simply omits it), output switching entirely\r\n * local (there is no engine involvement at all — see `AudioDevices.setOutput`'s own comment: routing\r\n * already-decoded audio needs no SIP/WebRTC layer underneath it).\r\n *\r\n * SSR-safe like everything else under `core/` (SPEC §10.2): every browser global is read lazily —\r\n * never at module scope — and gated behind `isBrowser()`.\r\n */\r\nclass BrowserAudioDevices implements AudioDevices {\r\n inputs: readonly MediaDeviceInfo[] = [];\r\n outputs: readonly MediaDeviceInfo[] = [];\r\n currentInputId: string | null = null;\r\n currentOutputId: string | null = null;\r\n readonly canSelectOutput: boolean;\r\n\r\n readonly #engine: CallEngine;\r\n readonly #onChange: () => void;\r\n\r\n constructor(engine: CallEngine, onChange: () => void) {\r\n this.#engine = engine;\r\n this.#onChange = onChange;\r\n this.canSelectOutput =\r\n isBrowser() &&\r\n typeof HTMLMediaElement !== 'undefined' &&\r\n 'setSinkId' in HTMLMediaElement.prototype;\r\n\r\n if (isBrowser() && navigator.mediaDevices) {\r\n void this.refresh();\r\n // No teardown: `mediaDevices` is the FRAME's own object, not a resource this instance opened,\r\n // and this instance lives exactly as long as the Softphone does — there is nothing to leak.\r\n navigator.mediaDevices.addEventListener?.('devicechange', () => void this.refresh());\r\n }\r\n }\r\n\r\n async refresh(): Promise<void> {\r\n if (!isBrowser() || typeof navigator.mediaDevices?.enumerateDevices !== 'function') return;\r\n let all: readonly MediaDeviceInfo[];\r\n try {\r\n all = await navigator.mediaDevices.enumerateDevices();\r\n } catch {\r\n // Rare, but real — a locked-down permissions policy or an odd polyfill can reject this. The\r\n // lists simply stay whatever they were; there is no better fallback than \"unchanged.\"\r\n return;\r\n }\r\n this.inputs = all.filter((d) => d.kind === 'audioinput');\r\n this.outputs = all.filter((d) => d.kind === 'audiooutput');\r\n // A selection pointing at a device that just vanished (unplugged) must not keep claiming it is\r\n // still selected — the UI would show a \"chosen\" option that no longer exists in either list.\r\n if (this.currentInputId && !this.inputs.some((d) => d.deviceId === this.currentInputId)) {\r\n this.currentInputId = null;\r\n }\r\n if (this.currentOutputId && !this.outputs.some((d) => d.deviceId === this.currentOutputId)) {\r\n this.currentOutputId = null;\r\n }\r\n this.#onChange();\r\n }\r\n\r\n async setInput(deviceId: string | null): Promise<void> {\r\n this.currentInputId = deviceId;\r\n this.#onChange();\r\n // Optional on the interface — a mock engine with no real media has nothing to swap and simply\r\n // does not implement it.\r\n await this.#engine.setInputDevice?.(deviceId);\r\n }\r\n\r\n async setOutput(deviceId: string | null): Promise<void> {\r\n this.currentOutputId = deviceId;\r\n this.#onChange();\r\n // Nothing to await here: applying it is the UI layer's job (`useCallAudio`'s `setSinkId` on the\r\n // remote-audio element), reached only through `SoftphoneEvent.DeviceChange` — core/ cannot touch\r\n // `document` itself (SPEC §10.2).\r\n }\r\n\r\n async setRingtoneOutput(): Promise<void> {\r\n /* separate ringtone output lands in a later pass — still Phase 2 groundwork */\r\n }\r\n}\r\n\r\nexport class SoftphoneImpl extends Emitter<SoftphoneEventMap> implements Softphone {\r\n #state: SoftphoneState = 'stopped';\r\n #lastError: FatalSoftphoneError | null = null;\r\n #activeCall: CallImpl | null = null;\r\n #destroyed = false;\r\n #startedHere = false;\r\n #isLeader = false;\r\n readonly #engine: CallEngine;\r\n readonly #tabLock: TabLock;\r\n readonly #resolveContact: SoftphoneConfig['resolveContact'];\r\n /** Contacts + history, whenever there is a Coventia backend to reach. The widget reads these. */\r\n readonly sources: BackendSources | null;\r\n readonly audio: AudioDevices;\r\n\r\n /**\r\n * `tabLock` defaults to the real Web Locks implementation — not part of `SoftphoneConfig` (an\r\n * internal seam, not something an integrator tunes). Tests construct this class directly with a\r\n * fake for deterministic control; `createSoftphone()` never passes a third argument, so every\r\n * existing test that goes through it is unaffected (`multi-tab-leader-election.md` §3.8).\r\n */\r\n constructor(engine: CallEngine, config: SoftphoneConfig, tabLock: TabLock = webLocksTabLock()) {\r\n super();\r\n this.#engine = engine;\r\n this.#tabLock = tabLock;\r\n // Built here, not as a field initializer — it needs `this.#engine` (set the line above) and\r\n // `this.emit` (inherited from `Emitter`, not available until after `super()`), so it cannot be a\r\n // default value evaluated before either exists.\r\n this.audio = new BrowserAudioDevices(this.#engine, () =>\r\n this.emit(SoftphoneEvent.DeviceChange, undefined),\r\n );\r\n // ONE set of sources, built here so the widget can read them back instead of constructing a\r\n // second set from identical config (two name caches, two of every request).\r\n //\r\n // `apiBase` ALONE is enough to build these — `coventia` is for OVERRIDING something (your own\r\n // contacts function, an `organizationId`, a `baseUrl` that differs from the engine's), never for\r\n // switching the feature on. It used to be the switch: omitting `coventia` meant no Contactos tab\r\n // even with `apiBase` set for calling, which is the wrong default for OUR OWN backend's own\r\n // contacts/history/caller ID — a host would have to know to add an otherwise-empty `coventia: {}`\r\n // to get something this SDK should simply give them.\r\n //\r\n // One token for every request the sources make, instead of one per request — see\r\n // `cachedTokenSource`. Idempotent, so wrapping a source the engine already wrapped is free and\r\n // they share the same entry.\r\n this.sources =\r\n config.coventia || config.apiBase\r\n ? coventiaBackend(config.coventia ?? {}, cachedTokenSource(config.tokenSource), config.apiBase)\r\n : null;\r\n this.#resolveContact = config.resolveContact ?? this.sources?.resolveContact;\r\n this.#engine.onIncoming((engineCall) => this.#onIncoming(engineCall));\r\n this.#engine.onStateChange((state) => this.#setState(state));\r\n // Optional on the interface — an engine that never reports failures (the mock) simply has none.\r\n this.#engine.onError?.((error) => this.#onError(error));\r\n }\r\n\r\n get state(): SoftphoneState {\r\n return this.#state;\r\n }\r\n get lastError(): FatalSoftphoneError | null {\r\n return this.#lastError;\r\n }\r\n get isLeader(): boolean {\r\n return this.#isLeader;\r\n }\r\n get activeCall(): Call | null {\r\n return this.#activeCall;\r\n }\r\n get canPlayAudio(): boolean {\r\n // The autoplay-recovery machinery is Phase 2; the mock has no audio to block.\r\n return true;\r\n }\r\n async startAudio(): Promise<void> {\r\n /* autoplay recovery lands in Phase 2 */\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.#assertUsable();\r\n if (this.#startedHere) return;\r\n if (liveInstance) {\r\n throw new Error(\r\n 'A Softphone is already running on this page. Construct one and share it; call destroy() before starting another. (SPEC §5.3 / error engine/duplicate-instance)',\r\n );\r\n }\r\n liveInstance = true;\r\n this.#startedHere = true;\r\n // Set BEFORE acquiring leadership, not after: `'starting'` means \"start() was called and is in\r\n // progress\", which includes waiting behind another tab as a real sub-phase of starting up — not\r\n // only the part after leadership is granted. This is what lets a host tell \"never started\" apart\r\n // from \"started, currently a follower\" (both otherwise look identical: `isLeader === false`,\r\n // and — had this stayed `'stopped'` here — `state === 'stopped'` too). The widget's `LeaderGate`\r\n // depends on this distinction (`multi-tab-leader-election.md` §3.4, §4).\r\n this.#setState('starting');\r\n\r\n // Resolves now for a single open tab (the common case — imperceptibly fast, nothing else holds\r\n // the lock), or later, whenever another tab releases it. See `multi-tab-leader-election.md` §3.4\r\n // for why this is NOT split into an immediate/deferred branch: nothing today awaits `start()`\r\n // closely enough to need one, and stop()/destroy() called while this is still pending (a\r\n // follower torn down before ever leading) simply leaves it permanently unresolved — see stop().\r\n await this.#tabLock.acquireLeadership();\r\n if (this.#destroyed) return; // torn down while waiting to lead — do not start the engine now\r\n this.#setLeader(true);\r\n await this.#engine.start();\r\n }\r\n\r\n async stop(): Promise<void> {\r\n if (!this.#startedHere) return;\r\n this.#tabLock.release();\r\n if (this.#isLeader) {\r\n await this.#engine.stop();\r\n if (this.#activeCall) await this.#activeCall.hangup().catch(() => {});\r\n }\r\n this.#setLeader(false);\r\n this.#releaseInstance();\r\n this.#setState('stopped');\r\n }\r\n\r\n async destroy(): Promise<void> {\r\n if (this.#destroyed) return;\r\n this.#destroyed = true;\r\n await this.stop().catch(() => {});\r\n this.#setState('destroyed');\r\n this.clear();\r\n }\r\n\r\n async call(destination: string, options?: CallOptions): Promise<Call> {\r\n this.#assertUsable();\r\n if (this.#state !== 'online' && this.#state !== 'offline' && this.#state !== 'starting') {\r\n throw new Error(`Cannot place a call while softphone is '${this.#state}'.`);\r\n }\r\n if (this.#activeCall) {\r\n throw new Error('A call is already in progress. Hang up before placing another.');\r\n }\r\n const engineCall = await this.#engine.placeCall(destination, options);\r\n return this.#adopt(engineCall);\r\n }\r\n\r\n // ── internals ───────────────────────────────────────────────────────────────\r\n #onIncoming(engineCall: EngineCall): void {\r\n if (this.#activeCall) {\r\n // Busy: the engine is responsible for the 486. Nothing to surface.\r\n void engineCall.reject().catch(() => {});\r\n return;\r\n }\r\n const call = this.#adopt(engineCall);\r\n this.emit(SoftphoneEvent.IncomingCall, call);\r\n }\r\n\r\n #adopt(engineCall: EngineCall): CallImpl {\r\n const call = new CallImpl(\r\n engineCall,\r\n (event, error) => this.#reportListenerError('call', event, error),\r\n (message) => this.#reportInternalError(message),\r\n );\r\n this.#activeCall = call;\r\n\r\n // Clear the slot when the call ends, so the next call can begin.\r\n const off = call.on(CallEvent.Ended, () => {\r\n off();\r\n if (this.#activeCall === call) {\r\n this.#activeCall = null;\r\n this.emit(SoftphoneEvent.ActiveCallChanged, { call: null });\r\n }\r\n });\r\n\r\n // Resolve the caller's name from the host's contacts and update the call in place. The provider's\r\n // SIP display name is the line label, not the caller (SPEC §4), so a resolved contact wins.\r\n if (this.#resolveContact) {\r\n void this.#resolveContact(engineCall.remoteNumber)\r\n .then((contact) => {\r\n if (contact && this.#activeCall === call) call.setRemoteContact(contact);\r\n })\r\n .catch(() => {});\r\n }\r\n\r\n // Fires for both inbound and outbound (both reach here) — what a UI's useActiveCall binds to.\r\n this.emit(SoftphoneEvent.ActiveCallChanged, { call });\r\n return call;\r\n }\r\n\r\n #setState(state: SoftphoneState): void {\r\n if (state === this.#state) return;\r\n const previous = this.#state;\r\n this.#state = state;\r\n /*\r\n * Reaching `online` is the only evidence we get that a fatal condition has passed, so it is what\r\n * clears it. Deliberately not a timer and not user input: neither is evidence of anything, and a\r\n * cleared error over a phone that still cannot call is worse than no error at all.\r\n *\r\n * For Zadarma `online` means the push socket is up, which is weaker than \"everything works\" — a\r\n * token could still be refused on the next key fetch. That self-corrects: the fetch fails, the\r\n * error is re-emitted, `lastError` is set again. An explanation that briefly lifts and returns\r\n * beats one that never lifts and needs a reload.\r\n */\r\n if (state === 'online') this.#lastError = null;\r\n this.emit(SoftphoneEvent.StateChanged, { state, previous });\r\n }\r\n\r\n #setLeader(isLeader: boolean): void {\r\n if (isLeader === this.#isLeader) return;\r\n this.#isLeader = isLeader;\r\n this.emit(SoftphoneEvent.LeaderChanged, { isLeader });\r\n }\r\n\r\n /**\r\n * Re-emit an engine failure to the host, and remember it if it is fatal.\r\n *\r\n * Every error is emitted; only fatal ones are retained. A non-fatal error is a fact about one call\r\n * — the remote party was busy — and keeping it as softphone state would make a working phone look\r\n * broken to anything that renders `lastError`.\r\n */\r\n #onError(error: SoftphoneError): void {\r\n if (isFatal(error)) this.#lastError = error;\r\n this.emit(SoftphoneEvent.Error, error);\r\n }\r\n\r\n #releaseInstance(): void {\r\n if (this.#startedHere) {\r\n liveInstance = false;\r\n this.#startedHere = false;\r\n }\r\n }\r\n\r\n #assertUsable(): void {\r\n if (this.#destroyed) {\r\n throw new Error('This Softphone has been destroyed. Construct a new one. (engine/destroyed)');\r\n }\r\n }\r\n\r\n #reportListenerError(source: string, event: string | number | symbol, error: unknown): void {\r\n // A throwing host listener must never take the engine down. Surface it, don't swallow it.\r\n console.error(`[softphone] a ${source} '${String(event)}' listener threw:`, error);\r\n }\r\n\r\n #reportInternalError(message: string): void {\r\n console.error(`[softphone] ${message}`);\r\n }\r\n}\r\n\r\n/** Test-only reset of the page-singleton guard, so suites don't leak the flag between cases. */\r\nexport function __resetInstanceGuardForTests(): void {\r\n liveInstance = false;\r\n}\r\n","/**\r\n * Browser-capability probes.\r\n *\r\n * SPEC §10.2: nothing under core/ or transports/ may touch `window`, `document` or `navigator` at\r\n * MODULE SCOPE. Importing the package must be safe on a server — a Next.js consumer renders on the\r\n * server first, and a bare `ReferenceError: window is not defined` at import time makes us unusable\r\n * there no matter how framework-agnostic the runtime API is.\r\n *\r\n * Every probe below is therefore a FUNCTION, evaluated lazily at call time.\r\n */\r\n\r\n/** True when running in a browser with a DOM. False under SSR, in Node, and in a worker. */\r\nexport function isBrowser(): boolean {\r\n return typeof window !== 'undefined' && typeof document !== 'undefined';\r\n}\r\n\r\n/**\r\n * True when the browser exposes the WebRTC APIs the softphone needs.\r\n *\r\n * Worth a distinct check rather than folding into isBrowser(): privacy extensions\r\n * (uBlock Origin Extra, WebRTC Leak Prevent) disable RTCPeerConnection outright, and the resulting\r\n * failure is otherwise reported as a confusing SIP error deep in a call attempt.\r\n */\r\nexport function isWebRtcSupported(): boolean {\r\n return (\r\n isBrowser() &&\r\n typeof RTCPeerConnection !== 'undefined' &&\r\n typeof navigator !== 'undefined' &&\r\n typeof navigator.mediaDevices?.getUserMedia === 'function'\r\n );\r\n}\r\n","import { isBrowser } from './env.js';\r\n\r\n/**\r\n * Cross-tab leader election (SPEC §5.2, §5.3; `multi-tab-leader-election.md`).\r\n *\r\n * Zadarma's on-demand registration means every open tab that reacts to the same push independently\r\n * registers a SIP UA for the same extension — a race the SDK never controlled, because nothing told\r\n * one tab another existed. Confirmed live: with two tabs open, an inbound call rings in exactly one,\r\n * unpredictably which. `TabLock` is the seam that lets exactly one tab (\"the leader\") ever start the\r\n * real engine; every other tab (\"a follower\") stays dormant until its turn comes.\r\n */\r\nexport interface TabLock {\r\n /**\r\n * Resolves once this tab becomes leader — immediately if no other tab currently leads (the common\r\n * case: a single open tab, or an environment without Web Locks), or later, whenever (if ever)\r\n * another tab releases it. Never rejects; a cancelled attempt (via `release()`) simply never\r\n * resolves.\r\n */\r\n acquireLeadership(): Promise<void>;\r\n\r\n /** Relinquish leadership if held, or cancel a still-pending attempt. Safe to call more than once. */\r\n release(): void;\r\n}\r\n\r\nconst LOCK_NAME = 'coventia-softphone:leader';\r\n\r\n/**\r\n * True when the browser exposes the Web Locks API this coordination depends on.\r\n *\r\n * Safari added support in 15.4 (2022); Chrome and Firefox both predate that comfortably. Where it's\r\n * missing, `webLocksTabLock()` grants leadership immediately to every tab — today's actual\r\n * (uncoordinated, but functional) behaviour, not a regression.\r\n */\r\nexport function isWebLocksSupported(): boolean {\r\n return isBrowser() && typeof navigator.locks?.request === 'function';\r\n}\r\n\r\n/**\r\n * The real implementation. Constructed lazily by a caller, never at module scope (SPEC §10.3) — safe\r\n * to import on a server, and an environment without Web Locks pays nothing beyond one property read.\r\n *\r\n * One `TabLock` is meant to be acquired at most once per `Softphone` lifetime, from `start()` — see\r\n * `multi-tab-leader-election.md` §3.4.\r\n */\r\nexport function webLocksTabLock(lockName: string = LOCK_NAME): TabLock {\r\n const controller = new AbortController();\r\n let releaseHold: (() => void) | undefined;\r\n\r\n return {\r\n acquireLeadership(): Promise<void> {\r\n if (!isWebLocksSupported()) return Promise.resolve();\r\n\r\n return new Promise<void>((resolveAcquired) => {\r\n navigator.locks\r\n .request(lockName, { signal: controller.signal }, () => {\r\n // Granted — tell the caller NOW. How long the browser keeps the lock is governed by the\r\n // SEPARATE promise below, resolved later by release(), not by this one.\r\n resolveAcquired();\r\n return new Promise<void>((resolveHold) => {\r\n releaseHold = resolveHold;\r\n });\r\n })\r\n .catch(() => {\r\n // Aborted by release() before our turn came — expected cancellation, not a failure.\r\n // `acquireLeadership()`'s promise simply never resolves in this case.\r\n });\r\n });\r\n },\r\n\r\n release(): void {\r\n if (releaseHold) {\r\n const resolve = releaseHold;\r\n releaseHold = undefined;\r\n resolve();\r\n } else {\r\n controller.abort();\r\n }\r\n },\r\n };\r\n}\r\n","import type { CallEngine, EngineCall } from './engine.js';\r\nimport type { CallOptions, SoftphoneState, TokenSource } from './types.js';\r\nimport type { SoftphoneError } from './errors.js';\r\n\r\nexport interface DefaultProviderConfig {\r\n readonly apiBase: string;\r\n readonly tokenSource: TokenSource;\r\n readonly organizationId?: string | undefined;\r\n}\r\n\r\n/**\r\n * A subscription list that may not have a real target yet.\r\n *\r\n * `defaultZadarmaEngine` below has THREE of these (`onIncoming`, `onStateChange`, `onError`), all\r\n * needing the identical, slightly tricky behaviour: a listener registered before the real engine\r\n * exists must still receive events once it does, AND unsubscribing must work correctly regardless of\r\n * whether it happens before or after that engine shows up. Three independent copies of this is three\r\n * independent chances to get the \"unsubscribe after the real engine attached\" case wrong — silently,\r\n * since nothing in this file's one current caller (`SoftphoneImpl`) ever unsubscribes these three, so\r\n * a bug here would only bite a future caller. One small class, three call sites, no reason to write\r\n * (and maintain) the replay/unsubscribe logic three times.\r\n */\r\nclass LazyRelay<L extends (...args: never[]) => void> {\r\n readonly #listeners = new Set<L>();\r\n readonly #realOff = new Map<L, () => void>();\r\n #target: { on(listener: L): () => void } | null = null;\r\n\r\n subscribe(listener: L): () => void {\r\n this.#listeners.add(listener);\r\n if (this.#target) this.#realOff.set(listener, this.#target.on(listener));\r\n return () => {\r\n this.#listeners.delete(listener);\r\n this.#realOff.get(listener)?.();\r\n this.#realOff.delete(listener);\r\n };\r\n }\r\n\r\n /** Called once the real engine exists — replays every listener registered before now onto it. */\r\n attach(target: { on(listener: L): () => void }): void {\r\n this.#target = target;\r\n for (const listener of this.#listeners) this.#realOff.set(listener, target.on(listener));\r\n }\r\n}\r\n\r\n/**\r\n * The default call engine: Zadarma, loaded lazily.\r\n *\r\n * Returns a `CallEngine` SYNCHRONOUSLY — `createSoftphone` must stay synchronous and SSR-safe even\r\n * when `provider` is omitted — and defers the real work to the first genuine use (`start()` or\r\n * `placeCall()`). Only then does it dynamically `import('../transports/zadarma-engine.js')`, which is\r\n * what actually reaches jssip and browser globals. Exactly the trick `coventiaBackend` (`backend.ts`)\r\n * already uses for contacts/history; applied here to the engine instead.\r\n *\r\n * The import specifier is a STRING LITERAL, not a variable — `check-imports.mjs` enforces this across\r\n * the whole build, after a variable specifier here once shipped a bare specifier no bundler could\r\n * resolve, which typechecked, passed 211 tests (all against fakes), and broke only in a real browser.\r\n */\r\nexport function defaultZadarmaEngine(config: DefaultProviderConfig): CallEngine {\r\n let real: CallEngine | null = null;\r\n let pending: Promise<CallEngine> | null = null;\r\n\r\n const incoming = new LazyRelay<(call: EngineCall) => void>();\r\n const stateChange = new LazyRelay<(state: SoftphoneState) => void>();\r\n const error = new LazyRelay<(error: SoftphoneError) => void>();\r\n\r\n function load(): Promise<CallEngine> {\r\n pending ??= import('../transports/zadarma-engine.js')\r\n .then(({ createZadarmaEngine }) => {\r\n const engine = createZadarmaEngine(config);\r\n incoming.attach({ on: (l) => engine.onIncoming(l) });\r\n stateChange.attach({ on: (l) => engine.onStateChange(l) });\r\n error.attach({ on: (l) => engine.onError?.(l) ?? (() => {}) });\r\n real = engine;\r\n return engine;\r\n })\r\n .catch((cause: unknown) => {\r\n // A retry (the widget's `Reintentar`: `stop()` then `start()` again) must be a genuine second\r\n // attempt, not a replay of the same cached failure — clearing `pending` is what makes that\r\n // true. Without it, one failed chunk load would fail every future retry forever.\r\n pending = null;\r\n throw cause;\r\n });\r\n return pending;\r\n }\r\n\r\n return {\r\n get state(): SoftphoneState {\r\n return real?.state ?? 'stopped';\r\n },\r\n async start(): Promise<void> {\r\n await (await load()).start();\r\n },\r\n async stop(): Promise<void> {\r\n // Never successfully started (or a failed load left `real` unset) — nothing to stop, and\r\n // calling `load()` here would wrongly turn a no-op into a first attempt.\r\n if (real) await real.stop();\r\n },\r\n async placeCall(destination: string, options?: CallOptions): Promise<EngineCall> {\r\n return (await load()).placeCall(destination, options);\r\n },\r\n onIncoming: (listener) => incoming.subscribe(listener),\r\n onStateChange: (listener) => stateChange.subscribe(listener),\r\n onError: (listener) => error.subscribe(listener),\r\n };\r\n}\r\n","import { SoftphoneImpl } from './softphone-impl.js';\r\nimport { defaultZadarmaEngine } from './default-provider.js';\r\nimport type { CallEngine } from './engine.js';\r\nimport type { Softphone } from './softphone.js';\r\nimport type { SoftphoneConfig } from './types.js';\r\n\r\n/**\r\n * Construct a softphone.\r\n *\r\n * A factory, not `new Softphone()`, for two reasons: it resolves the underlying call engine from\r\n * config without exposing that machinery, and it returns the `Softphone` INTERFACE, so the concrete\r\n * class stays an implementation detail free to change without a major version.\r\n *\r\n * @example\r\n * const phone = createSoftphone({\r\n * tokenSource: TokenSource.endpoint('/voice/token'),\r\n * resolveContact: (n) => api.lookup(n),\r\n * });\r\n * await phone.start(); // from a user gesture — see Softphone.start()\r\n */\r\nexport function createSoftphone(config: SoftphoneConfig): Softphone {\r\n return new SoftphoneImpl(resolveEngine(config), config);\r\n}\r\n\r\n/**\r\n * Pick the call engine.\r\n *\r\n * Three outcomes, in order:\r\n * 1. A valid `config.provider` — used as-is. `apiBase` is never even looked at: an explicit engine\r\n * always wins, so a host who wants a `MockProvider` or their own `organizationId` gets exactly\r\n * that, with no default quietly built alongside it.\r\n * 2. `provider` was SET but is not a valid engine — thrown on immediately, distinctly from \"none\r\n * configured\" below. Silently falling through to the default here would turn a typo'd config\r\n * into a confusing \"why is this calling Zadarma, I passed something else\" bug report.\r\n * 3. `provider` omitted, `apiBase` present — `defaultZadarmaEngine` (SPEC §5.4). It resolves\r\n * synchronously and touches nothing browser-specific until `start()`/`placeCall()` actually run,\r\n * so this stays as SSR-safe as the explicit-provider path.\r\n *\r\n * Neither present: a plain, explicit throw beats a confusing failure deep inside `start()`.\r\n */\r\nfunction resolveEngine(config: SoftphoneConfig): CallEngine {\r\n if (config.provider !== undefined) {\r\n if (isCallEngine(config.provider)) return config.provider;\r\n throw new Error(\r\n '`config.provider` was set but is not a valid CallEngine — it needs at least `placeCall` and ' +\r\n '`onIncoming`. Pass `createZadarmaEngine(...)` from \"@coventia/softphone/zadarma\", a ' +\r\n '`MockProvider` from \"@coventia/softphone/testing\", or omit it entirely to use the default.',\r\n );\r\n }\r\n\r\n if (config.apiBase) {\r\n return defaultZadarmaEngine({\r\n apiBase: config.apiBase,\r\n tokenSource: config.tokenSource,\r\n organizationId: config.coventia?.organizationId,\r\n });\r\n }\r\n\r\n throw new Error(\r\n 'No call engine configured. Pass `config.provider` explicitly — `createZadarmaEngine({ apiBase, ' +\r\n 'tokenSource })` from \"@coventia/softphone/zadarma\", or a `MockProvider` from ' +\r\n '\"@coventia/softphone/testing\" — or set `config.apiBase` so a default Zadarma engine can be built.',\r\n );\r\n}\r\n\r\nfunction isCallEngine(value: unknown): value is CallEngine {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n typeof (value as CallEngine).placeCall === 'function' &&\r\n typeof (value as CallEngine).onIncoming === 'function'\r\n );\r\n}\r\n","/**\r\n * @coventia/softphone — the framework-free engine.\r\n *\r\n * Importable from vanilla JS, Vue, Svelte, a PHP-rendered page, and a Node/SSR process. Exports NO\r\n * React and touches NO browser global at module scope (SPEC §10.1, §10.2). Those two rules — not the\r\n * authoring language, not the package layout — are what make this usable anywhere.\r\n *\r\n * THIS FILE IS THE PUBLIC API. Anything not exported here is free to change without a major version;\r\n * anything exported here is not. Adding to it is cheap and removing from it is expensive, so add\r\n * deliberately.\r\n *\r\n * Notably NOT exported, and deliberately:\r\n * - `SipProvider` and everything under transports/ — the provider seam is internal (SPEC §5.4).\r\n * Exporting it would freeze the shape of a Zadarma migration we have not designed yet.\r\n * - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.\r\n */\r\n\r\n// ── Engine ────────────────────────────────────────────────────────────────────\r\nexport { createSoftphone } from './core/create.js';\r\nexport type { Softphone, AudioDevices } from './core/softphone.js';\r\n\r\n// ── Calls ─────────────────────────────────────────────────────────────────────\r\nexport type { Call, CallState, HangupReason, HoldOriginator } from './core/call.js';\r\nexport { CALL_TRANSITIONS, INITIAL_STATES, canTransition, isTerminal } from './core/call.js';\r\n\r\n// ── Events ────────────────────────────────────────────────────────────────────\r\nexport { SoftphoneEvent, CallEvent } from './core/events.js';\r\nexport type { SoftphoneEventMap, CallEventMap, TypedEmitter } from './core/events.js';\r\n\r\n// ── Errors ────────────────────────────────────────────────────────────────────\r\n// `isFatal` is public because a host has to branch on it — `err.fatal` narrows structurally, but a\r\n// guard is friendlier from JavaScript. `createError` stays internal: the SDK raises these, hosts read\r\n// them, and a host minting our error codes would be claiming a failure we never observed.\r\nexport { ERROR_CATALOGUE, isFatal } from './core/errors.js';\r\nexport type {\r\n SoftphoneError,\r\n SoftphoneErrorCode,\r\n FatalSoftphoneError,\r\n FatalErrorCode,\r\n NonFatalSoftphoneError,\r\n NonFatalErrorCode,\r\n} from './core/errors.js';\r\n\r\n// ── Config & types ────────────────────────────────────────────────────────────\r\nexport type {\r\n SoftphoneConfig,\r\n SoftphoneState,\r\n CallDirection,\r\n CallOptions,\r\n Contact,\r\n RemoteParty,\r\n LogLevel,\r\n TokenSource,\r\n TokenEndpointOptions,\r\n VoiceToken,\r\n} from './core/types.js';\r\n\r\n// ── Capability probes ─────────────────────────────────────────────────────────\r\nexport { isBrowser, isWebRtcSupported } from './core/env.js';\r\n\r\nexport const VERSION = '0.0.0';\r\n"]}
1
+ {"version":3,"sources":["../src/sources.ts","../src/core/token-cache.ts","../src/coventia/api.ts","../src/coventia/sources.ts","../src/core/errors.ts","../src/transports/sip/media.ts","../src/transports/sip/ice.ts","../src/transports/sip/session.ts","../src/transports/sip/ua.ts","../src/transports/sip/real-factory.ts","../src/transports/providers/zadarma.ts","../src/transports/providers/zadarma-browser.ts","../src/transports/zadarma-engine.ts","../src/core/emitter.ts","../src/core/events.ts","../src/core/call.ts","../src/core/call-impl.ts","../src/core/backend.ts","../src/core/softphone-impl.ts","../src/core/env.ts","../src/core/tabs.ts","../src/core/transfer-watch.ts","../src/core/presence-socket.ts","../src/core/default-provider.ts","../src/core/create.ts","../src/index.ts"],"names":["entries","resolve","init_sources","ERROR_CATALOGUE","track","SoftphoneEvent","CallEvent","createZadarmaEngine"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * **Internal.** Not exported from any entry point.\r\n *\r\n * The rules that are about **phone numbers and names** rather than about any backend: universal,\r\n * easy to get subtly wrong, and worth getting right once.\r\n *\r\n * These were public while a host had to build their own sources. They no longer do — a provider\r\n * supplies `coventia.contacts` and everything else (caller ID, history names) is derived from it in\r\n * `coventia/sources.ts`, the only caller left. Exported API is a contract owed forever, so it should\r\n * not outlive its consumers.\r\n */\r\nimport type { Contact } from './core/types.js';\r\nimport type { CallHistoryEntry, HistorySource } from './ui/types.js';\r\n\r\n/**\r\n * A number reduced to the digits telephony actually matches on — `'+34 600 111 222'` → `'34600111222'`.\r\n *\r\n * Comparing numbers as raw strings is the bug this exists to prevent: the same line arrives as\r\n * `+34600111222` from a SIP header, `34600111222` from a database, and `+34 600 111 222` from a human,\r\n * and all three are one contact.\r\n */\r\nexport function toDigits(number: string): string {\r\n return String(number ?? '').replace(/\\D/g, '');\r\n}\r\n\r\n/**\r\n * Decide whether a resolved string is really a **name** — the \"don't echo the bare number\" rule.\r\n *\r\n * A contact saved as its own number (\"+34 600 111 222\") resolves to a \"name\" that is just the number\r\n * reformatted. Showing it puts `+34600111222` where a name belongs, or worse, a *differently*\r\n * formatted copy of the number the widget is already displaying an inch away.\r\n *\r\n * This is the SDK deciding what it is willing to call a name, so it holds for every host — not only\r\n * for a backend that happens to be careful. Returns `null` when there is no name worth showing, which\r\n * is the signal to keep displaying the number.\r\n */\r\nexport function normalizeResolvedName(raw: string | null | undefined, number: string): string | null {\r\n if (!raw) return null;\r\n const trimmed = String(raw).trim();\r\n if (!trimmed) return null;\r\n const digits = toDigits(number);\r\n if (!digits) return trimmed;\r\n return toDigits(trimmed) === digits ? null : trimmed;\r\n}\r\n\r\n/**\r\n * Wrap a `HistorySource` so its entries get caller names, resolved once per distinct number.\r\n *\r\n * A call log is the same handful of numbers over and over, so the naive \"resolve each row\" costs a\r\n * request per row and hammers the backend with duplicates. This dedupes **by digits** (see\r\n * `toDigits` — `+34600111222` and `34600111222` are one lookup), caches across pages, and remembers\r\n * misses so an unknown number is not re-asked forever.\r\n *\r\n * A failed lookup leaves the entry's `name` null and is NOT cached, so a later page retries: the\r\n * number is still callable, and an unreachable name service must not empty the tab.\r\n *\r\n * const history = withNameResolution(myHistorySource, myResolveContact);\r\n */\r\nexport function withNameResolution(\r\n source: HistorySource,\r\n resolve: (number: string, signal?: AbortSignal) => Promise<Contact | null>,\r\n): HistorySource {\r\n const cache = new Map<string, string | null>();\r\n\r\n return async (params) => {\r\n const entries = await source(params);\r\n\r\n // Only entries that still NEED a name. A source that already knows one (its own join, its own\r\n // cache) must not cost a lookup — the numbers it named are not our business.\r\n const wanted = [\r\n ...new Set(entries.filter((e) => e.name === null).map((e) => toDigits(e.number))),\r\n ].filter((d) => d !== '' && !cache.has(d));\r\n await Promise.all(\r\n wanted.map(async (digits) => {\r\n try {\r\n const contact = await resolve(digits);\r\n cache.set(digits, normalizeResolvedName(contact?.name ?? null, digits));\r\n } catch {\r\n // Deliberately not cached — a transient failure must not become a permanent blank.\r\n }\r\n }),\r\n );\r\n\r\n return entries.map((e): CallHistoryEntry => {\r\n // Never overwrite a name the source already knew.\r\n if (e.name !== null) return e;\r\n const resolved = cache.get(toDigits(e.number)) ?? null;\r\n return resolved === null ? e : { ...e, name: resolved };\r\n });\r\n };\r\n}\r\n","import type { TokenSource, VoiceToken } from './types.js';\r\n\r\n/**\r\n * Hold a voice token until it is nearly expired, instead of minting one per request.\r\n *\r\n * **The SDK used to call `getToken()` before every single request** — the WebRTC key fetch, contacts,\r\n * history, and once per distinct number when resolving caller names. For the CRM that is nearly free,\r\n * because its `TokenSource` reads an in-memory Supabase session. For a partner it is not: every call\r\n * is a round trip to *their* backend, which mints a fresh token from ours. Opening the history tab\r\n * with ten distinct numbers cost ten tokens, each then sitting in Redis for its full 15 minutes.\r\n *\r\n * It also meant the short TTL bought nothing. A credential you never hold cannot expire, so\r\n * \"short-lived\" was a property of the design and not of anything that happened.\r\n *\r\n * Two behaviours make this safe rather than merely cheaper:\r\n *\r\n * - **Coalescing.** Concurrent callers on a cold cache share ONE `getToken()` call. Without it the\r\n * widget's first paint — key + contacts + history at once — still minted three.\r\n * - **Invalidation.** A 401 drops the entry so the next attempt re-mints. Otherwise a revoked token\r\n * would be retried until its TTL ran out, and `revokeVoiceTokensForUser` would not actually\r\n * revoke anything a running softphone does.\r\n */\r\n\r\n/** Renew this far ahead of expiry — covers clock skew and a request already in flight. */\r\nconst SKEW_MS = 60_000;\r\n\r\ninterface Entry {\r\n token: VoiceToken | null;\r\n inflight: Promise<VoiceToken> | null;\r\n}\r\n\r\n/**\r\n * Keyed by the HOST's source object, not by the wrapper.\r\n *\r\n * A host passes the same `TokenSource` to `createSoftphone` and to `createZadarmaEngine`, so both are\r\n * wrapped independently — keying on the inner object is what makes them share one token instead of\r\n * holding two. Weak, so a discarded softphone's entry goes with it.\r\n */\r\nconst entries = new WeakMap<TokenSource, Entry>();\r\nconst wrappers = new WeakMap<TokenSource, TokenSource>();\r\n\r\nconst isFresh = (token: VoiceToken | null): token is VoiceToken =>\r\n token !== null && token.expiresAt.getTime() - Date.now() > SKEW_MS;\r\n\r\n/**\r\n * Wrap a host's `TokenSource` so its token is reused until nearly expired.\r\n *\r\n * Idempotent: wrapping the same source twice returns the same wrapper, so the two entry points into\r\n * the SDK cannot end up with a cache each.\r\n */\r\nexport function cachedTokenSource(inner: TokenSource): TokenSource {\r\n const existing = wrappers.get(inner);\r\n if (existing) return existing;\r\n\r\n const entry: Entry = { token: null, inflight: null };\r\n\r\n const wrapper: TokenSource = {\r\n async getToken(signal?: AbortSignal): Promise<VoiceToken> {\r\n if (isFresh(entry.token)) return entry.token;\r\n // Someone is already fetching — wait for theirs rather than starting a second. Explicit\r\n // null-check, not truthiness: a pending promise is always truthy, and reading this as one\r\n // invites the mistake of thinking it tests the resolved value.\r\n if (entry.inflight !== null) return entry.inflight;\r\n\r\n entry.inflight = (async () => {\r\n try {\r\n const token = await inner.getToken(signal);\r\n entry.token = token;\r\n return token;\r\n } finally {\r\n // Cleared even on failure, or one rejected fetch would wedge every later caller.\r\n entry.inflight = null;\r\n }\r\n })();\r\n return entry.inflight;\r\n },\r\n };\r\n\r\n // Registered under BOTH, pointing at the same entry: callers hold the wrapper and only the SDK\r\n // holds the inner source, so invalidation has to work from either side.\r\n entries.set(inner, entry);\r\n entries.set(wrapper, entry);\r\n wrappers.set(inner, wrapper);\r\n return wrapper;\r\n}\r\n\r\n/**\r\n * Drop the cached token for a source, so the next call re-mints.\r\n *\r\n * Called when the backend rejects it — a 401 means the token is gone (expired early, or revoked),\r\n * and holding it until its nominal expiry would leave the softphone broken for up to 15 minutes.\r\n *\r\n * Safe to call with an unwrapped source, or one that was never cached.\r\n */\r\nexport function invalidateToken(source: TokenSource): void {\r\n const entry = entries.get(source);\r\n if (!entry) return;\r\n entry.token = null;\r\n}\r\n","import { invalidateToken } from '../core/token-cache.js';\r\nimport type { TokenSource } from '../core/types.js';\r\n\r\n/** A failed backend call. `status` is the HTTP status, or 0 when the request never completed. */\r\nexport class ApiError extends Error {\r\n readonly status: number;\r\n\r\n constructor(status: number, message: string) {\r\n super(message);\r\n this.name = 'ApiError';\r\n this.status = status;\r\n }\r\n}\r\n\r\nexport type QueryParams = Readonly<Record<string, string | number | undefined>>;\r\n\r\n/**\r\n * A minimal authenticated GET against Coventia's phone service.\r\n *\r\n * Lives under `coventia/` rather than `core/`: the core ships no HTTP at all, so a partner on their\r\n * own backend never loads this.\r\n */\r\ninterface RequestConfig {\r\n readonly baseUrl: string;\r\n readonly tokenSource: TokenSource;\r\n readonly fetch?: typeof globalThis.fetch;\r\n}\r\n\r\n/**\r\n * Shared by `createGet` and `createPost`: one attempt against the backend, with the 401-retry-once\r\n * policy both need (see `createGet`'s own note on why). `method` is the only thing that differs\r\n * between the two — this backend's phone routes take every parameter as a query string, GET or POST\r\n * alike (the presence ticket mint is a POST with no body, `organization_id` on the query, same as any\r\n * GET here), so there is nothing else for a request body to carry.\r\n */\r\nfunction createRequest(config: RequestConfig, method: 'GET' | 'POST') {\r\n // Bound, not passed by reference: an unbound `fetch` throws \"Illegal invocation\" in some browsers.\r\n const doFetch: typeof globalThis.fetch = config.fetch\r\n ? config.fetch\r\n : (...args) => globalThis.fetch(...args);\r\n const base = config.baseUrl.replace(/\\/+$/, '');\r\n\r\n /**\r\n * One attempt. Split out so a 401 can be retried exactly once with a fresh token — see the\r\n * returned function's own note.\r\n */\r\n async function attempt<T>(path: string, params: QueryParams, signal?: AbortSignal): Promise<T> {\r\n const { token } = await config.tokenSource.getToken(signal);\r\n const qs = new URLSearchParams();\r\n for (const [k, v] of Object.entries(params)) {\r\n if (v !== undefined && v !== '') qs.set(k, String(v));\r\n }\r\n const query = qs.toString();\r\n\r\n const res = await doFetch(`${base}${path}${query ? `?${query}` : ''}`, {\r\n method,\r\n // The RAW token, no `Bearer ` prefix — see `VoiceToken.token` (SPEC §6.1).\r\n headers: { Authorization: token },\r\n ...(signal ? { signal } : {}),\r\n });\r\n\r\n const body: unknown = await res.json().catch(() => null);\r\n const message = (body as { message?: string } | null)?.message ?? `${path} — HTTP ${res.status}`;\r\n\r\n if (!res.ok) throw new ApiError(res.status, message);\r\n // This backend answers 200 with `{ success: false }` on handled failures, so the status alone is\r\n // not enough to know a call worked.\r\n if ((body as { success?: boolean } | null)?.success === false) {\r\n throw new ApiError(res.status, message);\r\n }\r\n return body as T;\r\n }\r\n\r\n /**\r\n * Retry once on 401, with a freshly minted token.\r\n *\r\n * A cached token can be rejected before its nominal expiry — revoked on logout, or invalidated when\r\n * the extension is re-provisioned. Without this the cache would keep handing back the dead one for\r\n * up to its full TTL, so `revokeVoiceTokensForUser` would not actually stop a running softphone.\r\n *\r\n * Exactly one retry: a second 401 means the credential itself is refused, not stale, and retrying\r\n * would be a loop against the host's token endpoint.\r\n */\r\n return async function request<T>(path: string, params: QueryParams = {}, signal?: AbortSignal): Promise<T> {\r\n try {\r\n return await attempt<T>(path, params, signal);\r\n } catch (error) {\r\n if (!(error instanceof ApiError) || error.status !== 401) throw error;\r\n invalidateToken(config.tokenSource);\r\n return attempt<T>(path, params, signal);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * A minimal authenticated GET against Coventia's phone service.\r\n *\r\n * Lives under `coventia/` rather than `core/`: the core ships no HTTP at all, so a partner on their\r\n * own backend never loads this.\r\n */\r\nexport function createGet(config: RequestConfig) {\r\n return createRequest(config, 'GET');\r\n}\r\n\r\n/** The POST counterpart — today, only the presence ticket mint (`POST /presence/ticket`) uses it. */\r\nexport function createPost(config: RequestConfig) {\r\n return createRequest(config, 'POST');\r\n}\r\n","import { normalizeResolvedName, toDigits, withNameResolution } from '../sources.js';\r\nimport { createGet, createPost } from './api.js';\r\nimport type {\r\n Contact,\r\n PresenceCallbacks,\r\n PresenceConnection,\r\n PresenceEntry,\r\n PresenceSource,\r\n PresenceState,\r\n TokenSource,\r\n TransferOutcomeEntry,\r\n TransferOutcomesPage,\r\n TransferOutcomeSource,\r\n} from '../core/types.js';\r\nimport type {\r\n AgentsDirectory,\r\n CallDetail,\r\n CallHandler,\r\n CallHistoryEntry,\r\n CallOutcome,\r\n ContactsPage,\r\n ContactsSource,\r\n HistorySource,\r\n} from '../ui/types.js';\r\n\r\nexport interface CoventiaSourcesConfig {\r\n /** Coventia backend base URL, e.g. `https://api.coventia.es`. */\r\n readonly baseUrl: string;\r\n readonly tokenSource: TokenSource;\r\n /**\r\n * Only for session-authenticated hosts. A voice token carries the organization in its claim and\r\n * the backend pins it from there, so a partner omits this and cannot point the SDK at a tenant\r\n * their token does not cover. See `CoventiaBackendConfig.organizationId`.\r\n */\r\n readonly organizationId?: string;\r\n /**\r\n * Where the phone routes live. `/phone` today; `/api/v1/phone` once the partner API ships (§6.6),\r\n * at which point only this string changes.\r\n */\r\n readonly basePath?: string;\r\n /**\r\n * **The one thing a provider implements.** Their contact list, searched on their own backend.\r\n *\r\n * Supplying this is enough for everything: it drives the Contactos tab, and caller ID is DERIVED\r\n * from it by searching for the incoming number (see `resolverFromContacts`). One source means the\r\n * Contactos tab, a live call and a history row can never disagree about who a number belongs to —\r\n * which they did when the list and the lookup were separate overrides.\r\n *\r\n * The matching happens on their backend, where the CRM is: we send the number as `search` and take\r\n * an exact digit match from the results.\r\n */\r\n readonly contacts?: ContactsSource;\r\n\r\n /**\r\n * Caller ID, only if searching the list is not how their CRM answers \"who is this number\".\r\n *\r\n * Rarely needed — `contacts` already derives it. Worth setting when a dedicated lookup is cheaper\r\n * than a search, or when the number lives on a record that search does not return.\r\n */\r\n readonly resolveContact?: (number: string) => Promise<Contact | null>;\r\n /** Overrides polling `/transfers/outcomes` — see `CoventiaBackendConfig.transferOutcomes`. */\r\n readonly transferOutcomes?: TransferOutcomeSource;\r\n /** Overrides the presence WebSocket — see `CoventiaBackendConfig.presence`. */\r\n readonly presence?: PresenceSource;\r\n readonly fetch?: typeof globalThis.fetch;\r\n /** Overridable for tests — a fake `WebSocket` constructor. Defaults to `globalThis.WebSocket`. */\r\n readonly WebSocket?: typeof globalThis.WebSocket;\r\n}\r\n\r\nexport interface CoventiaSources {\r\n /** The Contactos tab. A partner may replace this with their own; everything else cannot be. */\r\n readonly contacts: ContactsSource;\r\n /** The Historial tab. **Coventia's, always** — we store the calls and the recordings. */\r\n readonly history: HistorySource;\r\n /** Caller ID for an incoming number, for `SoftphoneConfig.resolveContact`. */\r\n readonly resolveContact: (number: string) => Promise<Contact | null>;\r\n /** The Historial tab's \"filter by person\" directory. Always Coventia's own org membership. */\r\n readonly listAgents: () => Promise<AgentsDirectory>;\r\n /** Phase A (transfer outcome relay) — polled by the transfer watch, never by UI directly. */\r\n readonly transferOutcomes: TransferOutcomeSource;\r\n /** Phase B (presence-gated transfer picker) — opened by `presence-socket.ts`, never by UI directly. */\r\n readonly presence: PresenceSource;\r\n}\r\n\r\n/** What `/agents` returns. */\r\ninterface AgentRow {\r\n readonly user_id: string;\r\n readonly name: string | null;\r\n}\r\n\r\n/** What `/webrtc/contacts` returns. **The only place `wa_id` is named in the whole SDK.** */\r\ninterface WhatsAppContactRow {\r\n readonly wa_id: string;\r\n readonly name: string | null;\r\n}\r\n\r\n/**\r\n * `wa_id` → `Contact`.\r\n *\r\n * `id` IS the `wa_id`, and that leaks nothing: the backend serves callable contacts only\r\n * (`wa_id: /^[0-9]+$/`), so the value is literally the phone digits and carries nothing `phone` does\r\n * not. What the boundary removes is the SHAPE — no `custom_username`/`username` coalescing, no\r\n * WhatsApp field names past this function.\r\n */\r\nconst toContact = (row: WhatsAppContactRow): Contact => ({\r\n id: row.wa_id,\r\n name: row.name,\r\n phone: `+${row.wa_id}`,\r\n});\r\n\r\n/** One hop in `handled_by_chain`, as Mongo stores it — no `name` (see `HandledByChainRow`'s own note). */\r\ninterface HandledByChainRow {\r\n readonly ext?: string;\r\n readonly user_id?: string | null;\r\n}\r\n\r\n/** The same hop, enriched by `GET /calls/:id` with a resolved display name. */\r\ninterface EnrichedHandledByChainRow extends HandledByChainRow {\r\n readonly name?: string | null;\r\n}\r\n\r\n/** A row from `/calls`. The backend spreads raw Mongo docs, so every read below is defensive. */\r\ninterface CallRow {\r\n readonly _id?: string;\r\n /** The PBX's vocabulary: `incoming` | `outgoing`. NOT the SDK's `inbound`/`outbound`. */\r\n readonly direction?: string;\r\n readonly caller_id?: string;\r\n readonly called_did?: string;\r\n readonly destination?: string;\r\n readonly disposition?: string;\r\n readonly duration?: number;\r\n readonly created_at?: string | number;\r\n readonly recording_count?: number;\r\n readonly handled_by_chain?: readonly HandledByChainRow[];\r\n /** The transfer route as bare extensions — populated even when `handled_by_chain` isn't (older\r\n * docs, written before the chain existed). */\r\n readonly extensions?: readonly string[];\r\n readonly internal?: string;\r\n readonly last_internal?: string;\r\n}\r\n\r\nconst toDirection = (raw: string | undefined): 'inbound' | 'outbound' =>\r\n raw === 'outgoing' ? 'outbound' : 'inbound';\r\n\r\n/** Which field holds the other party depends on the direction. */\r\nconst otherParty = (row: CallRow): string =>\r\n toDirection(row.direction) === 'outbound'\r\n ? (row.called_did ?? row.destination ?? '')\r\n : (row.caller_id ?? '');\r\n\r\n/**\r\n * Who handled the call, in order. Prefers the real chain (`ext` + resolved `user_id`); falls back to\r\n * bare extensions ONLY when there is no chain at all — a doc written before `handled_by_chain` existed.\r\n * Unifies both shapes into one `CallHandler[]` so the UI never has to know which source it came from:\r\n * a bare extension is just a handler with `userId: null, name: null`.\r\n */\r\nfunction toHandledBy(row: CallRow, chain: readonly EnrichedHandledByChainRow[] = row.handled_by_chain ?? []): readonly CallHandler[] {\r\n if (chain.length > 0) {\r\n return chain\r\n .filter((h): h is EnrichedHandledByChainRow & { ext: string } => Boolean(h?.ext))\r\n .map((h) => ({ extension: h.ext, userId: h.user_id ?? null, name: h.name ?? null }));\r\n }\r\n const bare = row.extensions?.length ? row.extensions : [row.internal, row.last_internal];\r\n return [...new Set(bare.filter((e): e is string => Boolean(e)))].map((extension) => ({\r\n extension,\r\n userId: null,\r\n name: null,\r\n }));\r\n}\r\n\r\n/**\r\n * \"Not answered\" is ANY non-empty disposition other than `answered` — `no answer`, `busy`, `cancel`,\r\n * `failed`, `congestion`, and whatever Zadarma adds next.\r\n *\r\n * Open-ended rather than an allow-list, so an unrecognised value surfaces under \"Perdidas\" instead of\r\n * vanishing. **An EMPTY disposition counts as ANSWERED**, so a call of unknown status is not wrongly\r\n * flagged. Both halves are load-bearing.\r\n */\r\nconst isMissed = (row: CallRow): boolean => {\r\n const d = String(row.disposition ?? '').trim().toLowerCase();\r\n return d !== '' && d !== 'answered';\r\n};\r\n\r\n/**\r\n * The PBX's own vocabulary translated to the SDK's, for a call `isMissed`. Never returns Zadarma's\r\n * raw string — an unrecognised or future value falls into `'unavailable'` rather than leaking through\r\n * or being dropped, same open-ended reasoning `isMissed` itself documents.\r\n */\r\nconst toOutcome = (row: CallRow): CallOutcome => {\r\n switch (String(row.disposition ?? '').trim().toLowerCase()) {\r\n case 'no answer':\r\n return 'no-answer';\r\n case 'busy':\r\n return 'busy';\r\n case 'cancel':\r\n return 'cancelled';\r\n case 'failed':\r\n return 'failed';\r\n default:\r\n return 'unavailable';\r\n }\r\n};\r\n\r\n/**\r\n * Caller ID, derived from a contacts source: ask it for the number and take an exact match.\r\n *\r\n * This is why a provider only has to implement ONE function. Their search endpoint already knows how\r\n * to match a phone number — it is the same question the Contactos tab asks, with a different query —\r\n * and doing it on their backend is where the CRM actually lives.\r\n *\r\n * **Exact digit match only.** A search for a number can return partial or fuzzy hits, and naming a\r\n * caller after a near-miss is worse than showing the bare number: the user trusts the name.\r\n * `toDigits` on both sides so `+34 600 111 222` and `34600111222` compare equal.\r\n */\r\nexport function resolverFromContacts(\r\n contacts: ContactsSource,\r\n): (number: string) => Promise<Contact | null> {\r\n return async (number) => {\r\n const digits = toDigits(number);\r\n if (!digits) return null;\r\n // The number as the PBX reported it — their backend has the most to work with that way. `limit`\r\n // is small: we want one exact hit, not a page of near misses.\r\n const page = await contacts({ search: number, skip: 0, limit: 10 });\r\n return page.items.find((c) => toDigits(c.phone) === digits) ?? null;\r\n };\r\n}\r\n\r\nconst toDate = (raw: string | number | undefined): Date => {\r\n if (raw === undefined) return new Date(0);\r\n const d = new Date(raw);\r\n return Number.isNaN(d.getTime()) ? new Date(0) : d;\r\n};\r\n\r\n/** What `GET /transfers/outcomes` returns per entry — `transferOutcomeStore.js`'s own wire shape. */\r\ninterface TransferOutcomeRow {\r\n readonly id?: string;\r\n readonly target?: string;\r\n /** The backend's wire value; `'connected'` or anything else (`'not_connected'`) maps below. */\r\n readonly status?: string;\r\n readonly at?: string;\r\n}\r\n\r\n/** `not_connected` → `'not-connected'` (spec §4 D2's kebab-case, matching `HangupReason`) — the ONE\r\n * place that translation happens; open-ended rather than an allow-list, so an unrecognised status\r\n * fails safe as `'not-connected'` (the agent gets told, never silently nothing) rather than throwing. */\r\nconst toTransferOutcome = (row: TransferOutcomeRow): TransferOutcomeEntry => ({\r\n id: row.id ?? '',\r\n target: row.target ?? '',\r\n status: row.status === 'connected' ? 'connected' : 'not-connected',\r\n at: toDate(row.at),\r\n});\r\n\r\n/** What the presence WebSocket's `snapshot`/`delta` messages carry per agent — `presenceSocket.js`'s\r\n * own wire shape (`docs/specs/transfer-presence-picker.md` §4.3, plan §2). */\r\ninterface PresenceAgentRow {\r\n readonly userId?: string;\r\n readonly extension?: string;\r\n readonly name?: string | null;\r\n readonly state?: string;\r\n}\r\n\r\n/** Open-ended rather than an allow-list, matching `toTransferOutcome`'s own reasoning for an\r\n * unrecognised value: fails safe as `'desconectado'` (never wrongly reachable) rather than throwing. */\r\nconst toPresenceState = (raw: string | undefined): PresenceState =>\r\n raw === 'disponible' || raw === 'en_llamada' ? raw : 'desconectado';\r\n\r\nconst toPresenceEntry = (row: PresenceAgentRow): PresenceEntry => ({\r\n userId: row.userId ?? '',\r\n extension: row.extension ?? '',\r\n name: row.name ?? null,\r\n state: toPresenceState(row.state),\r\n});\r\n\r\n/** `POST /presence/ticket`'s response (`presenceTicketStore.js`'s own wire shape). */\r\ninterface PresenceTicketResponse {\r\n readonly success?: boolean;\r\n readonly ticket?: string;\r\n readonly expires_in?: number;\r\n}\r\n\r\n/** `https://api.coventia.es` → `wss://api.coventia.es`; `http://` → `ws://`. Scheme swapped, and any\r\n * trailing slash trimmed — same normalisation `createGet`/`createPost` apply to `baseUrl` internally,\r\n * needed again here since this function works from the raw config value, not their trimmed copy. */\r\nconst toWebSocketUrl = (httpUrl: string): string => httpUrl.replace(/^http/, 'ws').replace(/\\/+$/, '');\r\n\r\n/**\r\n * Coventia's phone service, as the sources the widget asks for.\r\n *\r\n * **This is the anti-corruption layer.** The backend speaks WhatsApp's data model; the SDK speaks\r\n * `Contact` and `CallHistoryEntry`. The translation happens here and nowhere else — enforced by\r\n * structure, since `core/` cannot reach `coventia/` and a partner never imports it.\r\n */\r\nexport function coventiaSources(config: CoventiaSourcesConfig): CoventiaSources {\r\n const get = createGet(config);\r\n const post = createPost(config);\r\n const base = config.basePath ?? '/phone';\r\n // Absent for a voice token, which already names the organization. Spread into every query below,\r\n // so omitting it removes the parameter entirely rather than sending `organization_id=undefined`.\r\n const org = config.organizationId ? { organization_id: config.organizationId } : {};\r\n\r\n const coventiaContacts: ContactsSource = async ({ search, skip, limit }): Promise<ContactsPage> => {\r\n const body = await get<{ contacts?: readonly WhatsAppContactRow[]; total?: number }>(\r\n `${base}/webrtc/contacts`,\r\n { ...org, search, skip, limit },\r\n );\r\n return { items: (body.contacts ?? []).map(toContact), total: body.total ?? 0 };\r\n };\r\n\r\n const coventiaResolve = async (number: string): Promise<Contact | null> => {\r\n const digits = toDigits(number);\r\n if (!digits) return null;\r\n const body = await get<{ name?: string | null }>(`${base}/webrtc/caller-name`, {\r\n ...org,\r\n number: digits,\r\n });\r\n // Re-applied on top of the backend's own rule: a contact saved as its own number resolves to a\r\n // \"name\" that is just the number reformatted, and showing that puts +34600111222 where a name\r\n // belongs — next to the number the widget is already displaying.\r\n const name = normalizeResolvedName(body.name ?? null, digits);\r\n return name === null ? null : { id: digits, name, phone: `+${digits}` };\r\n };\r\n\r\n const rawHistory: HistorySource = async ({\r\n skip,\r\n limit,\r\n missed,\r\n userId,\r\n }): Promise<readonly CallHistoryEntry[]> => {\r\n const body = await get<{ calls?: readonly CallRow[] }>(`${base}/calls`, {\r\n ...org,\r\n skip,\r\n limit,\r\n // Omitted rather than `'false'` — the backend only special-cases `missed=true`, so leaving it\r\n // out entirely for \"Todas\" is equivalent and one less thing for a param dump to explain.\r\n missed: missed ? 'true' : undefined,\r\n user_id: userId,\r\n });\r\n return (body.calls ?? []).map((row): CallHistoryEntry => {\r\n const id = row._id ?? '';\r\n const recorded = (row.recording_count ?? 0) > 0;\r\n const missedRow = isMissed(row);\r\n const handledBy = toHandledBy(row);\r\n return {\r\n id,\r\n direction: toDirection(row.direction),\r\n number: otherParty(row),\r\n // Filled by `withNameResolution` at the mount — deduped by digits and cached across pages.\r\n name: null,\r\n at: toDate(row.created_at),\r\n durationSec: row.duration ?? 0,\r\n missed: missedRow,\r\n ...(missedRow ? { outcome: toOutcome(row) } : {}),\r\n // The DID is only ever the ORG's own number, which only an inbound call dials — an outbound\r\n // row's `called_did` is the OTHER party (already `number` above); showing it again as \"DID\"\r\n // would just repeat the row under a wrong label.\r\n ...(toDirection(row.direction) === 'inbound' && row.called_did ? { did: row.called_did } : {}),\r\n recorded,\r\n handledBy,\r\n // The detail page's one enrichment fetch — signed recording URLs, plus this SAME chain with\r\n // names resolved. Skipped when there is nothing to enrich: minting signed URLs and resolving\r\n // names for a page of 30 calls nobody opens is pure waste.\r\n ...(recorded || handledBy.length > 0\r\n ? {\r\n loadDetail: async (): Promise<CallDetail> => {\r\n // Unlike `/calls` (rows spread flat into `calls: []`), `/calls/:id` wraps its one\r\n // result in `call` — `recordings.js`'s `res.json({ success, call: {...} })`.\r\n const detail = await get<{\r\n call?: {\r\n recording_urls?: readonly string[];\r\n handled_by_chain?: readonly EnrichedHandledByChainRow[];\r\n };\r\n }>(`${base}/calls/${encodeURIComponent(id)}`, org);\r\n return {\r\n recordingUrls: detail.call?.recording_urls ?? [],\r\n // `row`'s own bare-extension fields (read inside `toHandledBy` when the chain\r\n // passed here is empty) still work as the fallback — only the CHAIN comes from the\r\n // enriched response; the rest of `row` is unchanged.\r\n handledBy: toHandledBy(row, detail.call?.handled_by_chain ?? []),\r\n };\r\n },\r\n }\r\n : {}),\r\n };\r\n });\r\n };\r\n\r\n const coventiaListAgents = async (): Promise<AgentsDirectory> => {\r\n const body = await get<{ me?: AgentRow | null; people?: readonly AgentRow[] }>(\r\n `${base}/agents`,\r\n org,\r\n );\r\n const toAgent = (row: AgentRow) => ({ userId: row.user_id, name: row.name });\r\n return {\r\n me: body.me ? toAgent(body.me) : null,\r\n people: (body.people ?? []).map(toAgent),\r\n };\r\n };\r\n\r\n /**\r\n * Phase A (transfer outcome relay). `since` omitted on the caller's very first poll — that call\r\n * exists only to learn the starting `cursor`, per spec §5.3; this source is a thin, honest wrapper\r\n * around the endpoint and does not itself implement that \"discard the first page\" rule, which\r\n * belongs to the transfer watch (plan T5) that calls it.\r\n */\r\n const coventiaTransferOutcomes: TransferOutcomeSource = async ({ since }): Promise<TransferOutcomesPage> => {\r\n const body = await get<{ cursor?: string; outcomes?: readonly TransferOutcomeRow[] }>(\r\n `${base}/transfers/outcomes`,\r\n { ...org, since },\r\n );\r\n return {\r\n cursor: body.cursor ?? '0',\r\n outcomes: (body.outcomes ?? []).map(toTransferOutcome),\r\n };\r\n };\r\n\r\n /**\r\n * Phase B (presence-gated transfer picker). Mints a fresh, one-time ticket (never the real voice\r\n * token — spec §4.2's \"decided 2026-09-24: where the token travels\") and opens the WebSocket with\r\n * it. Called fresh on every (re)connect by `presence-socket.ts`, which owns retry/backoff — this\r\n * function is a single, honest attempt, the same division of labour `coventiaTransferOutcomes` has\r\n * with the transfer watch that calls IT.\r\n */\r\n const coventiaPresence: PresenceSource = (callbacks: PresenceCallbacks): Promise<PresenceConnection> => {\r\n const WS = config.WebSocket ?? globalThis.WebSocket;\r\n return new Promise<PresenceConnection>((resolve, reject) => {\r\n void (async () => {\r\n let ticket: string;\r\n try {\r\n const body = await post<PresenceTicketResponse>(`${base}/presence/ticket`, org);\r\n if (!body.ticket) throw new Error('presence ticket mint returned no ticket');\r\n ticket = body.ticket;\r\n } catch (error) {\r\n reject(error instanceof Error ? error : new Error(String(error)));\r\n return;\r\n }\r\n\r\n const wsUrl = `${toWebSocketUrl(config.baseUrl)}${base}/presence?ticket=${encodeURIComponent(ticket)}`;\r\n const ws = new WS(wsUrl);\r\n let settled = false;\r\n\r\n ws.onopen = () => {\r\n if (settled) return;\r\n settled = true;\r\n resolve({\r\n reportCallState: (onCall) => {\r\n if (ws.readyState !== WS.OPEN) return;\r\n ws.send(JSON.stringify({ type: 'call-state', onCall }));\r\n },\r\n close: () => {\r\n // Detached first — a deliberate close must never be reported to the caller as a drop\r\n // (`PresenceConnection.close`'s own contract).\r\n ws.onclose = null;\r\n ws.onerror = null;\r\n ws.close();\r\n },\r\n });\r\n };\r\n\r\n ws.onmessage = (event: MessageEvent) => {\r\n let data: unknown;\r\n try {\r\n data = JSON.parse(String(event.data));\r\n } catch {\r\n return; // not JSON — ignore rather than tear down the connection over one stray frame\r\n }\r\n const msg = data as {\r\n type?: string;\r\n agents?: readonly PresenceAgentRow[];\r\n agent?: PresenceAgentRow;\r\n };\r\n if (msg.type === 'snapshot') {\r\n callbacks.onSnapshot((msg.agents ?? []).map(toPresenceEntry));\r\n } else if (msg.type === 'delta' && msg.agent) {\r\n callbacks.onDelta(toPresenceEntry(msg.agent));\r\n }\r\n };\r\n\r\n ws.onclose = () => {\r\n if (!settled) {\r\n // Never actually opened (ticket refused, network refusal, ...) — indistinguishable from a\r\n // drop to `presence-socket.ts`, which is exactly why this rejects rather than calling\r\n // `onClose`: there is no `PresenceConnection` yet for that callback to be about.\r\n settled = true;\r\n reject(new Error('presence socket closed before opening'));\r\n return;\r\n }\r\n callbacks.onClose();\r\n };\r\n\r\n // A real WebSocket always follows `error` with `close` — nothing extra to do here, and a\r\n // browser gives an `error` event no further detail worth reporting anyway.\r\n ws.onerror = () => {};\r\n })();\r\n });\r\n };\r\n\r\n const contacts = config.contacts ?? coventiaContacts;\r\n\r\n /*\r\n * Caller ID follows the CONTACT LIST, not a separate setting.\r\n *\r\n * A provider who supplies `contacts` gets a resolver derived from it, so the tab, a live call and a\r\n * history row always name the same number the same way. Supplying only the list used to leave\r\n * caller ID on ours — the same person appearing twice under two names inside one widget.\r\n */\r\n const resolve =\r\n config.resolveContact ??\r\n (config.contacts ? resolverFromContacts(config.contacts) : coventiaResolve);\r\n\r\n // Names, resolved once per distinct number and cached across pages — a call log is the same handful\r\n // of numbers over and over. Wrapped HERE rather than left to the caller: the raw rows carry\r\n // `name: null`, so a caller who forgets this gets a history of bare numbers and no error.\r\n const history = withNameResolution(rawHistory, resolve);\r\n\r\n const transferOutcomes = config.transferOutcomes ?? coventiaTransferOutcomes;\r\n const presence = config.presence ?? coventiaPresence;\r\n\r\n return {\r\n contacts,\r\n history,\r\n resolveContact: resolve,\r\n listAgents: coventiaListAgents,\r\n transferOutcomes,\r\n presence,\r\n };\r\n}\r\n","/**\r\n * The error taxonomy.\r\n *\r\n * Two things make this worth more than `onError(Error)`:\r\n *\r\n * 1. **Fatal vs non-fatal, as a discriminated union.** A fatal error means the call (or the engine)\r\n * is over and the host must react. A non-fatal one is a warning the host may surface or ignore.\r\n * Collapsing them into one channel — which is what almost every SDK does — forces every\r\n * integrator to re-derive the distinction from string matching. Daily's split is the model.\r\n *\r\n * 2. **A coded catalogue with a `solution` per code.** Twilio and Telnyx both ship this and\r\n * integrators love it, because the support conversation becomes \"send me the code\" instead of\r\n * \"send me your logs\".\r\n *\r\n * The catalogue below is the SINGLE source of truth: `fatal` is derived from it at the type level,\r\n * so a code cannot be fatal in the table and non-fatal in a type, and adding a code without deciding\r\n * its severity is a compile error.\r\n */\r\n\r\ninterface ErrorSpec {\r\n readonly fatal: boolean;\r\n /** What went wrong, for a developer. */\r\n readonly message: string;\r\n /** What to do about it. Shown in logs and in our docs. */\r\n readonly solution: string;\r\n}\r\n\r\nexport const ERROR_CATALOGUE = {\r\n // ── Engine lifecycle ────────────────────────────────────────────────────────\r\n 'engine/duplicate-instance': {\r\n fatal: true,\r\n message: 'A Softphone instance is already running on this page.',\r\n solution:\r\n 'Construct one Softphone and share it. React StrictMode double-mounts in development — hold the instance above the tree, or in a ref, and call destroy() on unmount.',\r\n },\r\n 'engine/not-started': {\r\n fatal: false,\r\n message: 'The softphone has not been started.',\r\n solution: 'Await softphone.start() before placing or answering a call.',\r\n },\r\n 'engine/destroyed': {\r\n fatal: true,\r\n message: 'This Softphone has been destroyed and cannot be reused.',\r\n solution: 'Construct a new Softphone.',\r\n },\r\n\r\n // ── Auth ────────────────────────────────────────────────────────────────────\r\n 'auth/token-fetch-failed': {\r\n fatal: true,\r\n message: 'Could not obtain a voice token from the token endpoint.',\r\n solution:\r\n 'Check that the token endpoint is reachable and that the headers callback returns a valid host-app session token.',\r\n },\r\n 'auth/token-rejected': {\r\n fatal: true,\r\n message: 'The voice token was rejected.',\r\n solution:\r\n 'The token is expired, malformed, or scoped to a different organization. Mint a fresh one.',\r\n },\r\n\r\n // ── Media ───────────────────────────────────────────────────────────────────\r\n /*\r\n NONE of the four codes below are ever actually raised via `createError` — nothing in the SDK\r\n constructs a `SoftphoneError` with these codes today. Deleting them was the other option\r\n (SPEC discussion); they stay, commented, because the taxonomy and Spanish copy (`error-text.ts`)\r\n are worth keeping intact for whenever a real caller wants them, and a `SoftphoneErrorCode` union\r\n is a cheap thing to keep honest with a comment, expensive to keep churning as a breaking change.\r\n */\r\n 'media/permission-denied': {\r\n fatal: true,\r\n message: 'Microphone permission was denied.',\r\n solution:\r\n 'The permission prompt names the TOP-LEVEL origin. Ask for it from a user gesture, and re-request via the browser UI if previously blocked.',\r\n // NEVER RAISED. `MicPermissionGate` (ui/permission/) handles this condition today — it predates\r\n // this catalogue and has BETTER, browser-specific unblock instructions than a generic gate could\r\n // show. It does not import this catalogue at all; the two are unconnected on purpose.\r\n },\r\n 'media/no-input-device': {\r\n fatal: true,\r\n message: 'No microphone is available.',\r\n solution: 'Ask the user to connect a microphone, then retry.',\r\n // NEVER RAISED — same reason as `media/permission-denied` above: `MicPermissionGate` already\r\n // owns this.\r\n },\r\n 'media/webrtc-unsupported': {\r\n fatal: true,\r\n message: 'This browser does not expose the WebRTC APIs.',\r\n solution:\r\n 'Usually a privacy extension (uBlock Origin Extra, WebRTC Leak Prevent) disabling RTCPeerConnection — Twilio documents the same. Ask the user to disable it for this origin.',\r\n // NEVER RAISED — same reason again: `MicPermissionGate` already owns this.\r\n },\r\n 'media/playback-blocked': {\r\n fatal: false,\r\n message: 'The browser blocked audio playback (autoplay policy).',\r\n solution:\r\n 'Call softphone.startAudio() from a click handler. Until then the user cannot hear the caller, and an INCOMING RINGTONE WILL BE SILENT — this is how calls get missed.',\r\n /*\r\n NEVER RAISED — different reason from the other three. This is not superseded by an existing\r\n UI; the feature it describes (autoplay-recovery) is simply not built yet. `Softphone.canPlayAudio`\r\n / `startAudio()` (core/softphone.ts) are Phase 2 and currently no-op stubs — `canPlayAudio`\r\n always reports `true`. This is where that future work would report the block once it exists.\r\n */\r\n },\r\n\r\n // ── Network / signalling ────────────────────────────────────────────────────\r\n 'net/registration-failed': {\r\n fatal: true,\r\n message: 'SIP registration failed.',\r\n solution: 'Usually a stale credential. The engine refreshes and retries; if it persists, the extension may not exist.',\r\n },\r\n 'net/transport-failed': {\r\n fatal: true,\r\n message: 'The signalling transport could not be established.',\r\n solution: 'Check the CSP connect-src directive and any corporate proxy blocking WSS.',\r\n },\r\n 'net/ice-failed': {\r\n fatal: false,\r\n message: 'Media connectivity was lost; attempting an ICE restart.',\r\n solution:\r\n 'Usually a network handoff (wifi → cellular). The engine recovers on its own; surface a \"reconnecting\" banner rather than ending the call.',\r\n /*\r\n NEVER RAISED. The \"reconnecting\" banner this `solution` describes already exists — CallScreen\r\n shows \"Reconectando…\" directly off the call's own `reconnecting` STATE (`STATUS_LABEL` in\r\n views/CallScreen.tsx), which the SIP engine drives without going through the error channel at\r\n all. Raising this too would say the same fact twice through two different mechanisms. It would\r\n only earn its keep for host-side OBSERVABILITY — counting ICE restarts independently of the UI\r\n — which nobody has asked for yet.\r\n */\r\n },\r\n\r\n // ── Call ────────────────────────────────────────────────────────────────────\r\n 'call/not-answerable': {\r\n fatal: false,\r\n message: 'This call cannot be answered in its current state.',\r\n solution: 'Only a call in `ringing` may be answered. Check call.state first.',\r\n },\r\n 'call/busy': {\r\n fatal: false,\r\n message: 'The remote party is busy.',\r\n solution:\r\n 'Nothing to fix. Note SIP 486 is also what most endpoints send for a manual decline, so do not ' +\r\n 'label this \"busy\" alone in your UI — it is equally often \"did not answer\".',\r\n },\r\n 'call/no-answer': {\r\n fatal: false,\r\n message: 'The remote party did not answer.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/rejected': {\r\n fatal: false,\r\n message: 'The remote party rejected the call.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/missed': {\r\n fatal: false,\r\n message: 'An incoming call was cancelled before it was answered.',\r\n solution:\r\n 'Nothing to fix — the caller gave up while it rang. Surface it, and log it as a missed call.',\r\n },\r\n 'call/failed': {\r\n fatal: false,\r\n message: 'The call could not be completed.',\r\n solution:\r\n 'The catch-all for a call that never connected — a bad number, a rejected offer, a media ' +\r\n 'failure. `cause` carries the underlying SIP cause. Nothing is fatal: the phone still works.',\r\n },\r\n 'call/transfer-failed': {\r\n fatal: false,\r\n message: 'The transfer could not be completed.',\r\n solution: 'The original call is still up. Let the user retry or resume.',\r\n },\r\n 'call/unsupported': {\r\n fatal: false,\r\n message: 'The provider does not support this operation.',\r\n solution: 'Guard the UI on the operation before offering it.',\r\n },\r\n\r\n // ── Provider ────────────────────────────────────────────────────────────────\r\n 'provider/provision-failed': {\r\n fatal: true,\r\n message: 'The provider could not issue SIP credentials.',\r\n solution:\r\n 'The backend reached the carrier but got no usable credential. Check the org has a SIP extension provisioned.',\r\n },\r\n} as const satisfies Record<string, ErrorSpec>;\r\n\r\nexport type SoftphoneErrorCode = keyof typeof ERROR_CATALOGUE;\r\n\r\n/** Codes whose spec says `fatal: true`. Derived — cannot drift from the catalogue. */\r\nexport type FatalErrorCode = {\r\n [K in SoftphoneErrorCode]: (typeof ERROR_CATALOGUE)[K]['fatal'] extends true ? K : never;\r\n}[SoftphoneErrorCode];\r\n\r\nexport type NonFatalErrorCode = Exclude<SoftphoneErrorCode, FatalErrorCode>;\r\n\r\n/**\r\n * A fatal error: the call, or the engine, is over.\r\n *\r\n * `if (err.fatal)` narrows `err.code` to the fatal codes — which is the entire point of the split,\r\n * and the thing JSDoc could not have expressed (SPEC §2.2).\r\n */\r\nexport interface FatalSoftphoneError extends Error {\r\n readonly fatal: true;\r\n readonly code: FatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\n/** A non-fatal error: a warning. The call, if any, continues. */\r\nexport interface NonFatalSoftphoneError extends Error {\r\n readonly fatal: false;\r\n readonly code: NonFatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\nexport type SoftphoneError = FatalSoftphoneError | NonFatalSoftphoneError;\r\n\r\n/**\r\n * The one concrete error class. Not exported: the public contract is the `SoftphoneError` union and\r\n * the `code` on it, so nothing outside should be reaching for `instanceof`. It is a real `Error`\r\n * subclass, so `instanceof Error`, `.stack` and console formatting all behave as a host expects.\r\n */\r\nclass CatalogueError extends Error {\r\n readonly fatal: boolean;\r\n readonly code: SoftphoneErrorCode;\r\n readonly solution: string;\r\n\r\n constructor(code: SoftphoneErrorCode, cause?: unknown) {\r\n const spec: ErrorSpec = ERROR_CATALOGUE[code];\r\n // `cause` is only passed when there IS one: `{ cause: undefined }` would still define the\r\n // property, and an error carrying an explicit `undefined` cause reads as \"we looked and found\r\n // nothing\" rather than \"nothing was passed\".\r\n super(spec.message, cause === undefined ? undefined : { cause });\r\n this.name = 'SoftphoneError';\r\n this.code = code;\r\n this.fatal = spec.fatal;\r\n this.solution = spec.solution;\r\n }\r\n}\r\n\r\n/**\r\n * Is this already one of ours?\r\n *\r\n * Used where a layer catches whatever a lower one threw: a failure that already carries a catalogue\r\n * code should be reported AS that code, not flattened into the generic one for the layer that caught\r\n * it. Without this, a rejected voice token surfaces as \"the provider could not issue credentials\",\r\n * which is true and useless.\r\n *\r\n * Structural, not `instanceof` — the concrete class is not exported, and a duck-typed object with a\r\n * real code is as good as one of ours.\r\n */\r\nexport function isSoftphoneError(value: unknown): value is SoftphoneError {\r\n if (typeof value !== 'object' || value === null) return false;\r\n const candidate = value as { code?: unknown; fatal?: unknown };\r\n return typeof candidate.fatal === 'boolean' && (candidate.code as string) in ERROR_CATALOGUE;\r\n}\r\n\r\n/** Type guard, for hosts that would rather not rely on structural narrowing. */\r\nexport function isFatal(error: SoftphoneError): error is FatalSoftphoneError {\r\n return error.fatal;\r\n}\r\n\r\n/**\r\n * Construct a catalogue-backed error. **The only way the SDK raises one** — every `message` and\r\n * `solution` a host sees comes from the table above, so no call site invents error prose.\r\n *\r\n * The return type narrows on the code: `createError('call/busy')` is a `NonFatalSoftphoneError`, and\r\n * reading `.fatal` on it gives `false`, not `boolean`.\r\n */\r\nexport function createError<C extends SoftphoneErrorCode>(\r\n code: C,\r\n cause?: unknown,\r\n): C extends FatalErrorCode ? FatalSoftphoneError : NonFatalSoftphoneError {\r\n // The one cast in this module. A conditional return type cannot be verified from inside the\r\n // implementation — TypeScript has no way to prove the branch for an unresolved `C` — so the\r\n // guarantee rests on `fatal` being copied from the catalogue that DERIVES `FatalErrorCode`. The\r\n // test suite asserts that agreement for every code, which is the check the compiler cannot make.\r\n return new CatalogueError(code, cause) as C extends FatalErrorCode\r\n ? FatalSoftphoneError\r\n : NonFatalSoftphoneError;\r\n}\r\n","import type { JsSipSession, JsSipIceEvent } from './jssip.js';\r\n\r\n/**\r\n * What a leg asks the browser for. Audio only — `video: false` is explicit rather than omitted,\r\n * matching the legacy hook exactly.\r\n *\r\n * A FUNCTION, not the static constant this used to be: device selection (SPEC §7 Phase 2) means the\r\n * constraint depends on whichever input device is currently selected, which can change between calls\r\n * — a static object could never reflect that. `deviceId: null` (the default, no device chosen yet)\r\n * falls back to bare `audio: true`, letting the browser pick its own default exactly as before.\r\n */\r\nexport function buildAudioConstraints(deviceId: string | null): MediaStreamConstraints {\r\n return { audio: deviceId ? { deviceId: { exact: deviceId } } : true, video: false };\r\n}\r\n\r\n/**\r\n * The two most non-obvious, most load-bearing, most deletable-LOOKING pieces of the engine\r\n * (SPEC §3.1). Everything here is generic WebRTC — nothing Zadarma-specific — but it is exactly the\r\n * code a rewrite quietly drops, so the reasoning travels with it.\r\n *\r\n * No browser global is touched at module scope (SPEC §10.2): every function reaches for\r\n * `window`/`navigator` only when called, in a browser.\r\n */\r\n\r\n/**\r\n * A **safety net** on ICE gathering: send the INVITE anyway if gathering has not finished in `capMs`.\r\n *\r\n * JsSIP already releases the INVITE the moment gathering completes — it calls its own `ready()` both\r\n * on the end-of-candidates signal and on `icegatheringstatechange === 'complete'`. So the normal path\r\n * needs nothing from us. This exists only for the pathological one: against an unreachable STUN or\r\n * TURN server, Chrome can sit in `gathering` for ~40 s, and a phone that takes 40 s to ring is broken.\r\n *\r\n * **It deliberately does NOT settle on the first `typ srflx` candidate**, which is what it used to do.\r\n * One reflexive candidate is not \"enough for NAT traversal\" — it is enough only if it happens to be\r\n * the right one, and cutting gathering short there loses:\r\n *\r\n * - **the other address family.** A first srflx over IPv6 against an IPv4-only peer is useless; the\r\n * IPv4 one arrives moments later and never makes it into the offer.\r\n * - **the other interfaces.** On a multi-homed machine (Wi-Fi + VPN + Ethernet) the first srflx may\r\n * be the one that cannot reach the media server.\r\n * - **every relay candidate.** TURN needs an allocation round-trip, so `typ relay` always arrives\r\n * AFTER srflx. Settling early meant a configured TURN server could never appear in the SDP — the\r\n * relay would be paid for and never offered, and the call would fail exactly as if it were absent.\r\n *\r\n * This is SIP, not trickle ICE: whatever is in the offer is all the peer will ever see. So the cost of\r\n * settling early is permanent for that call, while the cost of waiting is a few hundred milliseconds.\r\n *\r\n * @returns a disposer that cancels the pending timer (call it on teardown).\r\n */\r\nexport function capIceGathering(session: JsSipSession, capMs: number): () => void {\r\n let settled = false;\r\n let timer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n const settle = (ready: () => void): void => {\r\n if (settled) return;\r\n settled = true;\r\n if (timer) clearTimeout(timer);\r\n try {\r\n ready();\r\n } catch {\r\n /* offer already sent */\r\n }\r\n };\r\n\r\n session.on('icecandidate', ({ ready }: JsSipIceEvent) => {\r\n // Armed on the first candidate of any kind; JsSIP wins the race whenever gathering completes\r\n // normally, which is the overwhelmingly common case.\r\n if (!timer) timer = setTimeout(() => settle(ready), capMs);\r\n });\r\n\r\n return () => {\r\n if (timer) clearTimeout(timer);\r\n };\r\n}\r\n","/**\r\n * ICE configuration — how the browser's media finds its way out of a NAT.\r\n *\r\n * **SDK infrastructure, not host configuration.** A partner integrating a softphone should not have\r\n * to learn what a STUN server is, and every host would otherwise solve the same problem identically.\r\n * So there is a working default here and nothing to configure.\r\n *\r\n * With no ICE servers at all, a browser offers only HOST candidates — its own LAN addresses — and the\r\n * far end has to reach `192.168.x.x` directly. That works only where the NAT happens to cooperate,\r\n * and fails on symmetric NAT and most corporate networks. Firefox says `ICE failed, add a STUN\r\n * server`; Chrome's version is worse, connecting a call that carries no audio.\r\n *\r\n * **STUN only, deliberately — there is no TURN here and it could not be added here.** STUN just\r\n * reports the browser's own public address: stateless, unauthenticated, free. TURN *relays the media*,\r\n * so it costs bandwidth and therefore needs credentials — and a credential compiled into a client\r\n * bundle is a public one, usable by anyone who opens devtools. A relay would have to be issued per\r\n * user and short-lived by a backend that knows who is asking (coturn's `use-auth-secret` scheme is\r\n * the usual shape). That is real infrastructure, and until a network actually demands it we do not\r\n * run any: the legacy CRM softphone shipped exactly this one STUN server and placed real calls for\r\n * years.\r\n */\r\n\r\n/**\r\n * Google's public STUN. No registration, and the de facto default for the WebRTC ecosystem — it is\r\n * also, verbatim, what the legacy `useSipUA.js` used, which is the strongest argument available that\r\n * it is sufficient for Zadarma.\r\n *\r\n * Two caveats worth a decision rather than a shrug: it carries **no SLA**, and every user's IP is\r\n * observed by Google. Both are fixed the same way — point this at a STUN server we run.\r\n */\r\nexport const DEFAULT_ICE_SERVERS: readonly RTCIceServer[] = [\r\n { urls: 'stun:stun.l.google.com:19302' },\r\n];\r\n\r\n/** The `pcConfig` JsSIP hands to `new RTCPeerConnection`, for every leg in either direction. */\r\nexport const PC_CONFIG: RTCConfiguration = { iceServers: [...DEFAULT_ICE_SERVERS] };\r\n","import type { EngineCall, EngineCallEvent } from '../../core/engine.js';\r\nimport type { HangupReason } from '../../core/call.js';\r\nimport type { CallDirection } from '../../core/types.js';\r\nimport type { JsSipEndEvent, JsSipSession } from './jssip.js';\r\nimport { buildAudioConstraints, capIceGathering } from './media.js';\r\nimport { PC_CONFIG } from './ice.js';\r\n\r\n/** Map JsSIP end/failed causes onto the SDK's HangupReason. Generic SIP, no provider specifics. */\r\nfunction mapEndReason(e: JsSipEndEvent): HangupReason {\r\n if (e.originator === 'local') return 'local';\r\n if (e.originator === 'remote') return 'remote';\r\n return 'failed';\r\n}\r\n/**\r\n * A JsSIP cause onto the SDK's HangupReason.\r\n *\r\n * The cases below are `JsSIP.C.causes`, and the status codes in the comments are its own\r\n * `SIP_ERROR_CAUSES` table — this must be read against that list rather than guessed at, because\r\n * `JsSipEndEvent.cause` is a bare `string`, so a case that never matches costs nothing at compile\r\n * time and silently falls to `failed`. That is exactly how `Unavailable` went unhandled: it is what\r\n * a call that rings out actually returns, and it was reaching `default`.\r\n *\r\n * `failed` is a real outcome, not a fallthrough — see CALL_END_CODES in `ua.ts`. Anything unmapped\r\n * still tells the user the call did not connect.\r\n */\r\nfunction mapFailReason(e: JsSipEndEvent): HangupReason {\r\n switch (e.cause) {\r\n // 486 Busy Here, 600 Busy Everywhere. Also what most endpoints send for a MANUAL DECLINE — SIP\r\n // does not distinguish the two, and neither can we, which is why the copy covers both.\r\n case 'Busy':\r\n return 'busy';\r\n /*\r\n `Rejected` is 403 / 603 Decline — an explicit refusal, sent by the endpoints that bother to.\r\n `Redirected` is 3xx: not literally a rejection, but it ends the same way — not connected, and\r\n nothing to retry.\r\n\r\n (Both comments sit ABOVE the group rather than between the labels. A comment between two `case`\r\n lines makes the first one non-empty to `no-fallthrough`, which then demands a `break` that\r\n would be unreachable.)\r\n */\r\n case 'Rejected':\r\n case 'Redirected':\r\n return 'rejected';\r\n /*\r\n `Unavailable` is 480 Temporarily Unavailable / 410 / 408 Request Timeout / 430 — THE ring-out\r\n case, and the one that was missing. `No Answer` and `Expires` are local: our own no-answer\r\n timer, and a session that expired.\r\n */\r\n case 'Unavailable':\r\n case 'No Answer':\r\n case 'Expires':\r\n return 'no-answer';\r\n case 'Canceled':\r\n /*\r\n BOTH directions produce this cause, meaning opposite things, and only the originator tells\r\n them apart: `remote` is the caller giving up while our phone rang (a missed call — `ua.ts`\r\n turns this one into `call/missed`), `local` is the user abandoning their own outbound call\r\n before it was picked up. This used to return `remote` for both, which told the host that the\r\n far end ended a call the user had just hung up on themselves.\r\n */\r\n return e.originator === 'local' ? 'local' : 'remote';\r\n // Everything else is a genuine failure worth naming: Not Found (404/604), Address Incomplete,\r\n // Incompatible SDP, Authentication Error, WebRTC Error, RTP Timeout, Connection Error…\r\n default:\r\n return 'failed';\r\n }\r\n}\r\n\r\nfunction remoteNumberOf(session: JsSipSession): string {\r\n const id = session.remote_identity;\r\n return id?.uri?.user ?? id?.uri?.toString() ?? '';\r\n}\r\n\r\nexport interface SipEngineCallDeps {\r\n readonly id: string;\r\n /** Turn an extension or bare number into a full SIP URI (the engine knows the domain). */\r\n readonly buildTarget: (target: string) => string;\r\n /** Place a consult leg on the same UA (attended transfer). */\r\n readonly placeConsult: (target: string) => Promise<SipEngineCall>;\r\n readonly iceGatheringCapMs?: number;\r\n /** ICE servers for the answer's peer connection — same set the engine uses outbound. */\r\n readonly pcConfig?: RTCConfiguration;\r\n /**\r\n * Called when THIS call is rejected (486), so the engine can start its provider re-ring guard\r\n * (SPEC §3.4). Tied to the reject ACTION, not to `ended` — a normal hangup must not start it.\r\n */\r\n readonly onReject?: () => void;\r\n /**\r\n * The currently-selected input device, read fresh at ANSWER time — an inbound call's `getUserMedia`\r\n * happens here, not at construction, so a device chosen after the call started ringing still takes\r\n * effect the moment it is picked up. `undefined`/`null` means no device chosen — the browser default.\r\n */\r\n readonly getInputDeviceId?: () => string | null;\r\n}\r\n\r\n/**\r\n * One JsSIP call, as an `EngineCall`. It translates JsSIP's events into the SDK's raw\r\n * `EngineCallEvent`s — and `Call` (core) turns those into the public state machine. Nothing here\r\n * knows the state machine, and nothing here is Zadarma-specific.\r\n */\r\nexport class SipEngineCall implements EngineCall {\r\n readonly id: string;\r\n readonly direction: CallDirection;\r\n readonly remoteNumber: string;\r\n\r\n readonly #session: JsSipSession;\r\n readonly #deps: SipEngineCallDeps;\r\n readonly #listeners = new Set<(event: EngineCallEvent) => void>();\r\n\r\n #ended = false;\r\n #cancelIceCap: (() => void) | null = null;\r\n /**\r\n * The stream behind a LIVE device swap (`swapInputDevice`), if one has happened. Distinct from\r\n * JsSIP's own original capture: that one is `_localMediaStreamLocallyGenerated` and JsSIP stops it\r\n * itself on close (see `#end`'s comment). This one is ours — obtained with our own `getUserMedia`\r\n * call, attached via `replaceTrack`, and never known to JsSIP at all — so nothing else will ever\r\n * stop it unless we do, here.\r\n */\r\n #swappedStream: MediaStream | null = null;\r\n\r\n constructor(session: JsSipSession, deps: SipEngineCallDeps) {\r\n this.#session = session;\r\n this.#deps = deps;\r\n this.id = deps.id;\r\n this.direction = session.direction === 'incoming' ? 'inbound' : 'outbound';\r\n this.remoteNumber = remoteNumberOf(session);\r\n this.#wire();\r\n }\r\n\r\n /** @internal — the underlying session, for `completeTransfer`'s REFER…Replaces. */\r\n get session(): JsSipSession {\r\n return this.#session;\r\n }\r\n\r\n onEvent(listener: (event: EngineCallEvent) => void): () => void {\r\n this.#listeners.add(listener);\r\n return () => this.#listeners.delete(listener);\r\n }\r\n\r\n // ── JsSIP events → raw engine signals ─────────────────────────────────────\r\n #wire(): void {\r\n const s = this.#session;\r\n\r\n /*\r\n * `progress` is OUTBOUND-only.\r\n *\r\n * JsSIP fires it in both directions, but it means opposite things: on an outbound call it is the\r\n * remote's `180`/`183` — the state we call `early`, possibly with early media. On an INBOUND call\r\n * it fires when *we* send `180 Ringing`, which is not a state change at all: the call is already\r\n * `ringing`, and the machine has no `ringing → early` edge because there is no such thing.\r\n *\r\n * Wiring it for both is what produced `illegal call transition ringing → early` on every real\r\n * inbound call. It was harmless — the transition was refused and the call rang and answered fine —\r\n * but it logged an error on a healthy path, and an error that fires when nothing is wrong is how\r\n * people learn to ignore the console.\r\n */\r\n if (this.direction === 'outbound') {\r\n s.on('progress', () => this.#fire({ type: 'progress', response: 'ringing' }));\r\n }\r\n\r\n /*\r\n * The remote audio. `ontrack` has to be wired on BOTH paths, because JsSIP creates the peer\r\n * connection at a different moment in each direction:\r\n *\r\n * INBOUND — created inside `answer()`, long after this runs. The event is the only way to see it.\r\n * OUTBOUND — created SYNCHRONOUSLY inside `ua.call()`: JsSIP's `connect()` calls\r\n * `_createRTCConnection()` before it returns, so `peerconnection` has already fired\r\n * by the time this object exists. Listening alone means `ontrack` is never set and\r\n * the caller hears silence — while the callee hears them perfectly, since the\r\n * outbound media path is unaffected.\r\n *\r\n * The legacy hook did exactly this: `if (session.connection) session.connection.ontrack = …`\r\n * right after `ua.call()`, plus the event for the answer path.\r\n */\r\n const wireRemoteTrack = (pc: RTCPeerConnection): void => {\r\n pc.ontrack = ({ streams }) => {\r\n if (streams[0]) this.#fire({ type: 'stream', stream: streams[0] });\r\n };\r\n };\r\n s.on('peerconnection', ({ peerconnection }) => wireRemoteTrack(peerconnection));\r\n if (s.connection) wireRemoteTrack(s.connection);\r\n\r\n // Outbound only: release the INVITE without waiting for full ICE gathering (SPEC §3.1).\r\n if (this.direction === 'outbound' && this.#deps.iceGatheringCapMs) {\r\n this.#cancelIceCap = capIceGathering(s, this.#deps.iceGatheringCapMs);\r\n }\r\n\r\n s.on('accepted', () => this.#onAccepted());\r\n s.on('ended', (e) => this.#end(mapEndReason(e), e.cause));\r\n s.on('failed', (e) => this.#end(mapFailReason(e), e.cause));\r\n s.on('hold', ({ originator }) => this.#fire({ type: 'held', originator }));\r\n s.on('unhold', ({ originator }) => this.#fire({ type: 'unheld', originator }));\r\n }\r\n\r\n /**\r\n * The call is live. Nothing to swap: JsSIP opened the real microphone when the session was created,\r\n * so the far end has had our actual audio from the first packet.\r\n *\r\n * There used to be a silent-mic dance here — place the call with a synthetic AudioContext track,\r\n * swap the real mic in on accept — so a Bluetooth headset stayed in A2DP while the phone rang. It\r\n * cost more than it bought: with no active capture, Firefox restricts ICE gathering and the offer\r\n * goes out with no candidates at all, which no STUN or TURN server can fix.\r\n */\r\n #onAccepted(): void {\r\n this.#fire({ type: 'accepted' });\r\n }\r\n\r\n // ── Commands ───────────────────────────────────────────────────────────────\r\n async answer(): Promise<void> {\r\n const deviceId = this.#deps.getInputDeviceId?.() ?? null;\r\n this.#session.answer({\r\n mediaConstraints: buildAudioConstraints(deviceId),\r\n pcConfig: this.#deps.pcConfig ?? PC_CONFIG,\r\n });\r\n }\r\n\r\n /**\r\n * Live-swap the outgoing microphone track — `CallEngine.setInputDevice` on a call that is already\r\n * connected. Never touches the FIRST call's capture: that one is JsSIP's own `getUserMedia`, opened\r\n * inside `answer()`/`ua.call()` (see `#onAccepted`'s comment on why that stays that way). This is\r\n * only for a device chosen AFTER the call is already live.\r\n *\r\n * `RTCRtpSender.replaceTrack` rather than a fresh `answer()`/re-INVITE: it needs no SDP\r\n * renegotiation and the far end never sees an interruption — exactly the mechanism real softphones\r\n * use for \"switch mic mid-call.\"\r\n */\r\n async swapInputDevice(deviceId: string | null): Promise<void> {\r\n const pc = this.#session.connection;\r\n if (!pc || this.#ended) return; // not connected yet, or already over — nothing to swap\r\n const sender = pc.getSenders().find((s) => s.track?.kind === 'audio');\r\n if (!sender) return;\r\n\r\n let stream: MediaStream;\r\n try {\r\n stream = await navigator.mediaDevices.getUserMedia(buildAudioConstraints(deviceId));\r\n } catch {\r\n // The chosen device vanished between selection and this call (unplugged, permission revoked,\r\n // a stale id) — nothing useful to do beyond leaving the current track exactly as it was.\r\n return;\r\n }\r\n if (this.#ended) {\r\n // The call ended while getUserMedia was in flight — don't attach a track to a dead sender,\r\n // and don't leak the capture we just opened.\r\n for (const track of stream.getTracks()) track.stop();\r\n return;\r\n }\r\n const [track] = stream.getAudioTracks();\r\n if (!track) return;\r\n\r\n await sender.replaceTrack(track);\r\n // Stop whatever we swapped in LAST time — never the original JsSIP capture, which this never\r\n // touches and which JsSIP stops itself on close.\r\n for (const oldTrack of this.#swappedStream?.getTracks() ?? []) oldTrack.stop();\r\n this.#swappedStream = stream;\r\n }\r\n\r\n async reject(): Promise<void> {\r\n this.#deps.onReject?.();\r\n this.#session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n }\r\n\r\n async hangup(): Promise<void> {\r\n this.#session.terminate();\r\n }\r\n\r\n mute(muted: boolean): void {\r\n if (muted) this.#session.mute({ audio: true });\r\n else this.#session.unmute({ audio: true });\r\n }\r\n\r\n async hold(): Promise<void> {\r\n // JsSIP writes `a=sendonly` and fires 'hold' — which #wire translates to a `held` signal. One\r\n // path, so a locally-initiated hold and a remote one travel identically (SPEC §5.6).\r\n this.#session.hold();\r\n }\r\n\r\n async unhold(): Promise<void> {\r\n this.#session.unhold();\r\n }\r\n\r\n sendDigits(digits: string): void {\r\n this.#session.sendDTMF(digits);\r\n }\r\n\r\n async transfer(target: string): Promise<void> {\r\n // Blind transfer. JsSIP ends the session on a successful REFER; that surfaces as `ended`\r\n // (reason 'transferred' is set by the core when it sees the transfer intent — here it is a\r\n // normal local end).\r\n this.#session.refer(this.#deps.buildTarget(target));\r\n }\r\n\r\n async startConsult(target: string): Promise<EngineCall> {\r\n return this.#deps.placeConsult(target);\r\n }\r\n\r\n async completeTransfer(consult: EngineCall): Promise<void> {\r\n const consultSession = (consult as SipEngineCall).session;\r\n this.#session.refer(consultSession.remote_identity.uri.toString(), { replaces: consultSession });\r\n }\r\n\r\n // ── internals ──────────────────────────────────────────────────────────────\r\n #end(reason: HangupReason, cause?: string): void {\r\n if (this.#ended) return;\r\n this.#ended = true;\r\n this.#cancelIceCap?.();\r\n // The ORIGINAL microphone is NOT stopped here: JsSIP opened it (no `mediaStream` was passed), so\r\n // it owns it and stops the tracks itself on close. See `_localMediaStreamLocallyGenerated` in\r\n // JsSIP. A SWAPPED-IN one (`swapInputDevice`) is a different story — JsSIP never learned about\r\n // it, so it is ours to stop, or the capture (and the browser's mic indicator) outlives the call.\r\n for (const track of this.#swappedStream?.getTracks() ?? []) track.stop();\r\n this.#swappedStream = null;\r\n this.#fire({ type: 'ended', reason, cause });\r\n this.#listeners.clear();\r\n }\r\n\r\n #fire(event: EngineCallEvent): void {\r\n if (this.#ended && event.type !== 'ended') return;\r\n for (const listener of [...this.#listeners]) listener(event);\r\n }\r\n}\r\n","import type { CallEngine, EngineCall } from '../../core/engine.js';\r\nimport type { CallOptions, SoftphoneState } from '../../core/types.js';\r\nimport type { SipCredentials, SipProvider } from '../providers/provider.js';\r\nimport type { JsSipFactory, JsSipSession, JsSipUA, JsSipUAConfig } from './jssip.js';\r\nimport { SipEngineCall } from './session.js';\r\nimport { buildAudioConstraints } from './media.js';\r\nimport { PC_CONFIG } from './ice.js';\r\nimport { createError, isSoftphoneError } from '../../core/errors.js';\r\nimport type { NonFatalErrorCode, SoftphoneError, SoftphoneErrorCode } from '../../core/errors.js';\r\nimport type { HangupReason } from '../../core/call.js';\r\n\r\nconst DEFAULT_ICE_CAP_MS = 3000;\r\nconst REGISTER_EXPIRES = 580;\r\n/** Floor between signal rebuilds, so a persistently failing registrar cannot become a hot loop. */\r\nconst SIGNAL_REFRESH_DEBOUNCE_MS = 30_000;\r\n/** Same window, same reason: a retrying registrar must not become a stream of identical errors. */\r\nconst ERROR_DEBOUNCE_MS = 30_000;\r\n\r\n/**\r\n * Call outcomes worth telling the user about. Reasons NOT here (`local`, `remote`, `transferred`)\r\n * are ordinary endings — reporting them would turn every completed call into an error.\r\n *\r\n * `failed` MUST stay mapped. It is `mapFailReason`'s catch-all, so leaving it out means every cause\r\n * nobody thought to enumerate ends the call in silence — the user watches the dialpad come back with\r\n * no idea whether it rang, and the least-understood failures are the ones explained least.\r\n */\r\nconst CALL_END_CODES: Partial<Record<HangupReason, NonFatalErrorCode>> = {\r\n busy: 'call/busy',\r\n 'no-answer': 'call/no-answer',\r\n rejected: 'call/rejected',\r\n failed: 'call/failed',\r\n};\r\n\r\n/** `sip:100@pbx.example.com` → `pbx.example.com`. */\r\nfunction domainOf(credentials: SipCredentials): string {\r\n const at = credentials.uri.lastIndexOf('@');\r\n return at === -1 ? '' : credentials.uri.slice(at + 1);\r\n}\r\n\r\nlet callSeq = 0;\r\nconst nextCallId = (): string => `sip-${++callSeq}`;\r\n\r\n/**\r\n * The generic JsSIP call engine. A `CallEngine` driven by a `SipProvider` for credentials and\r\n * registration policy (SPEC §5.4).\r\n *\r\n * NOTHING here is Zadarma-specific — `grep -ri zadarma` over this directory must return nothing. The\r\n * push socket, the JSONP key exchange and the quirk-timer *values* all live in the provider (S4);\r\n * this engine only reads `provider.registrationMode`, `provider.incomingSignal` and\r\n * `provider.quirks` as data.\r\n *\r\n * Registration policy:\r\n * - `persistent` — provision + register once at start(); the standing UA both receives INVITEs\r\n * and places calls. The normal SIP model; our own infrastructure, later.\r\n * - `on-demand` — register only in response to `incomingSignal` (or to place a call), tear down\r\n * after. Zadarma's PBX ignores a standing registration (SPEC §3.2). The\r\n * no-INVITE teardown and reject-guard timers come from `provider.quirks`.\r\n */\r\nexport class SipCallEngine implements CallEngine {\r\n #state: SoftphoneState = 'stopped';\r\n #ua: JsSipUA | null = null;\r\n /** In-flight `#createUA()`. THE concurrency guard — see `#ensureUA`. */\r\n #uaInFlight: Promise<JsSipUA> | null = null;\r\n #domain = '';\r\n #activeCall: SipEngineCall | null = null;\r\n /**\r\n * The consult leg from `startConsult` (SPEC §5.6), if one is live on this UA right now.\r\n *\r\n * Tracked separately from `#activeCall` so `#maybeTeardownUA` can tell \"both legs are actually gone\"\r\n * apart from \"the main leg ended, but a consult is still up\" — see that method's own comment for the\r\n * bug this exists to prevent.\r\n */\r\n #consultCall: SipEngineCall | null = null;\r\n /** The chosen microphone, or `null` for the browser default. Read fresh by every new leg's\r\n * `mediaConstraints`, and pushed live to `#activeCall` by `setInputDevice`. */\r\n #inputDeviceId: string | null = null;\r\n\r\n readonly #incomingListeners = new Set<(call: EngineCall) => void>();\r\n readonly #stateListeners = new Set<(state: SoftphoneState) => void>();\r\n readonly #errorListeners = new Set<(error: SoftphoneError) => void>();\r\n #signalUnsub: (() => void) | null = null;\r\n #signalConnectionUnsub: (() => void) | null = null;\r\n #noInviteTimer: ReturnType<typeof setTimeout> | undefined;\r\n #rejectGuardUntil = 0;\r\n #signalRefreshAfter = 0;\r\n readonly #errorSentAfter = new Map<SoftphoneErrorCode, number>();\r\n\r\n private readonly now: () => number;\r\n #resolvedFactory: JsSipFactory | null = null;\r\n\r\n constructor(\r\n private readonly provider: SipProvider,\r\n // A factory OR an async loader. The loader form bridges sync construction with the dynamic\r\n // `import('jssip')` (SPEC §10.2) — tests still pass a plain FakeFactory.\r\n private readonly factory: JsSipFactory | (() => JsSipFactory | Promise<JsSipFactory>),\r\n options: { now?: () => number } = {},\r\n ) {\r\n this.now = options.now ?? Date.now;\r\n }\r\n\r\n async #resolveFactory(): Promise<JsSipFactory> {\r\n if (this.#resolvedFactory) return this.#resolvedFactory;\r\n this.#resolvedFactory =\r\n typeof this.factory === 'function' ? await this.factory() : this.factory;\r\n return this.#resolvedFactory;\r\n }\r\n\r\n get state(): SoftphoneState {\r\n return this.#state;\r\n }\r\n\r\n onIncoming(listener: (call: EngineCall) => void): () => void {\r\n this.#incomingListeners.add(listener);\r\n return () => this.#incomingListeners.delete(listener);\r\n }\r\n onStateChange(listener: (state: SoftphoneState) => void): () => void {\r\n this.#stateListeners.add(listener);\r\n return () => this.#stateListeners.delete(listener);\r\n }\r\n onError(listener: (error: SoftphoneError) => void): () => void {\r\n this.#errorListeners.add(listener);\r\n return () => this.#errorListeners.delete(listener);\r\n }\r\n\r\n /**\r\n * Switch the microphone: stored for every FUTURE leg's `mediaConstraints`, and — if a call is live\r\n * right now — pushed to it immediately via `SipEngineCall.swapInputDevice` (SPEC §7 Phase 2, device\r\n * management). `null` resets to the browser's own default device.\r\n */\r\n async setInputDevice(deviceId: string | null): Promise<void> {\r\n this.#inputDeviceId = deviceId;\r\n await this.#activeCall?.swapInputDevice(deviceId);\r\n }\r\n\r\n /**\r\n * Report a failure, at most once per code per `ERROR_DEBOUNCE_MS`.\r\n *\r\n * A failing registrar retries, and one emission per attempt would flood a host's logging and make\r\n * whatever the widget shows flicker. Debounced per CODE rather than globally, so a transport\r\n * failure is never swallowed because a registration failure happened to precede it.\r\n */\r\n #reportError(code: SoftphoneErrorCode, cause?: unknown): void {\r\n this.#emitError(createError(code, cause));\r\n }\r\n\r\n /**\r\n * Emit, debouncing FATAL codes only.\r\n *\r\n * A failing registrar retries, and one emission per attempt would flood a host's logging and make\r\n * whatever the widget shows flicker — so the same fatal code is reported at most once per window,\r\n * per CODE, so a transport failure is never swallowed because a registration failure preceded it.\r\n *\r\n * Non-fatal codes are never debounced: they describe one call each, and two busy tones in a row are\r\n * two facts, not a repeated one.\r\n */\r\n #emitError(error: SoftphoneError): void {\r\n if (error.fatal) {\r\n const now = this.now();\r\n if (now < (this.#errorSentAfter.get(error.code) ?? 0)) return;\r\n this.#errorSentAfter.set(error.code, now + ERROR_DEBOUNCE_MS);\r\n }\r\n for (const listener of [...this.#errorListeners]) listener(error);\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.#setState('starting');\r\n if (this.provider.registrationMode === 'persistent') {\r\n await this.#ensureUA();\r\n this.#setState('online');\r\n } else {\r\n // On-demand: the push socket keeps us \"reachable\" without a standing registration. We only\r\n // register when it signals a call (or when we place one).\r\n const signal = this.provider.incomingSignal;\r\n if (signal) {\r\n await signal.connect();\r\n this.#signalUnsub = signal.onSignal(() => void this.#onIncomingSignal());\r\n /*\r\n * The signal channel IS our reachability, so it drives the reported state.\r\n *\r\n * Without this, `online` meant \"start() was called\" and nothing could ever contradict it —\r\n * the only other state transitions come from a UA that, in on-demand mode, exists solely for\r\n * the duration of a call. A dead push socket therefore looked identical to a healthy one, and\r\n * the phone silently stopped ringing while the widget said it was online.\r\n */\r\n this.#signalConnectionUnsub =\r\n signal.onConnectionChange?.((connected) =>\r\n this.#setState(connected ? 'online' : 'offline'),\r\n ) ?? null;\r\n }\r\n // Optimistic only when the signal cannot report: an existing provider without\r\n // `onConnectionChange` keeps the old behaviour rather than being stuck offline.\r\n this.#setState(signal?.onConnectionChange ? (signal.connected ? 'online' : 'offline') : 'online');\r\n }\r\n }\r\n\r\n async stop(): Promise<void> {\r\n this.#clearNoInviteTimer();\r\n this.#signalUnsub?.();\r\n this.#signalUnsub = null;\r\n this.#signalConnectionUnsub?.();\r\n this.#signalConnectionUnsub = null;\r\n this.provider.incomingSignal?.disconnect();\r\n this.#teardownUA();\r\n this.#setState('stopped');\r\n }\r\n\r\n async placeCall(destination: string, _options?: CallOptions): Promise<EngineCall> {\r\n const ua = await this.#ensureUA();\r\n const session = await this.#callWhenRegistered(ua, this.#buildTarget(destination));\r\n return this.#wrapCall(session);\r\n }\r\n\r\n // ── incoming (on-demand: push → register → INVITE) ────────────────────────\r\n async #onIncomingSignal(): Promise<void> {\r\n // Guard: after a reject, the provider may RE-RING us (Zadarma's return_timeout). Swallow those\r\n // for the quirk window (SPEC §3.4). Persistent providers set no guard, so this is a no-op there.\r\n if (this.now() < this.#rejectGuardUntil) return;\r\n if (this.#activeCall) return; // already on a call\r\n try {\r\n await this.#ensureUA();\r\n // The push is NOT an INVITE (SPEC §3.3): it fires for transfers, retries, hangups. If no INVITE\r\n // arrives within the quirk window, tear the fresh UA down so it doesn't block the next call.\r\n this.#armNoInviteTeardown();\r\n } catch {\r\n this.#teardownUA();\r\n }\r\n }\r\n\r\n #armNoInviteTeardown(): void {\r\n const ms = this.provider.quirks?.noInviteTeardownMs;\r\n if (!ms) return;\r\n this.#clearNoInviteTimer();\r\n // `#onIncomingSignal` (SPEC §3.3) re-arms this on EVERY push, including the generic \"answered\"\r\n // pushes Zadarma sends around unrelated account activity — one landed 300ms after a real transfer\r\n // completed here (live capture, 2026-08-04) and, checking only `#activeCall`, blew away the still-\r\n // live consult leg 5s later with the exact same premature-teardown bug `#maybeTeardownUA` exists to\r\n // prevent (§ its own comment). Same guard, same reason: a consult leg is still \"something on this\r\n // UA,\" even with no `#activeCall`.\r\n this.#noInviteTimer = setTimeout(() => this.#maybeTeardownUA(), ms);\r\n }\r\n /**\r\n * Rebuild the incoming-signal channel with fresh credentials, after a registration failure.\r\n *\r\n * Registration fails when the credentials behind it have gone stale — and for Zadarma those come\r\n * from the same WebRTC key the push socket is holding. Tearing down the UA alone leaves that stale\r\n * key in place, so every subsequent attempt fails the same way and the phone is dead until the page\r\n * is reloaded. Reconnecting re-fetches the key. The legacy hook did exactly this from both of its\r\n * `registrationFailed` handlers.\r\n *\r\n * Debounced, which the legacy was not: a provider failing every attempt would otherwise become a\r\n * tight loop against our backend and theirs.\r\n */\r\n async #refreshSignal(): Promise<void> {\r\n const signal = this.provider.incomingSignal;\r\n if (!signal) return;\r\n if (this.now() < this.#signalRefreshAfter) return;\r\n this.#signalRefreshAfter = this.now() + SIGNAL_REFRESH_DEBOUNCE_MS;\r\n try {\r\n signal.disconnect();\r\n await signal.connect();\r\n } catch {\r\n // Still disconnected, and the signal reports that — the engine surfaces it as `offline`\r\n // rather than claiming a reachability it does not have.\r\n }\r\n }\r\n\r\n #clearNoInviteTimer(): void {\r\n if (this.#noInviteTimer) clearTimeout(this.#noInviteTimer);\r\n this.#noInviteTimer = undefined;\r\n }\r\n\r\n #onNewSession(session: JsSipSession, originator: 'local' | 'remote'): void {\r\n if (originator !== 'remote') return; // our own outbound legs are wired at placeCall\r\n this.#clearNoInviteTimer();\r\n\r\n if (this.#activeCall || this.now() < this.#rejectGuardUntil) {\r\n session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n return;\r\n }\r\n const call = this.#wrapCall(session);\r\n for (const listener of [...this.#incomingListeners]) listener(call);\r\n }\r\n\r\n // ── UA lifecycle ───────────────────────────────────────────────────────────\r\n /**\r\n * The single UA, created at most once — even under concurrent callers.\r\n *\r\n * **The `#ua` check alone is not a guard**, because everything after it awaits: two callers both\r\n * see `null`, both `provision()`, both build a UA, and both REGISTER the same extension. Zadarma\r\n * rejects the loser, `registrationFailed` tears down, and the survivor may be the one that was\r\n * torn down. The visible symptom is two `get_webphone_data.php` calls at startup and a phone that\r\n * never registers.\r\n *\r\n * Concurrency is the normal case here, not an edge: the push socket can signal twice in a\r\n * heartbeat (SPEC §3.3 — a push is not an INVITE; it fires for transfers, retries and hangups),\r\n * and `placeCall` can land on top of one. So the in-flight promise IS the guard.\r\n */\r\n async #ensureUA(): Promise<JsSipUA> {\r\n if (this.#ua) return this.#ua;\r\n this.#uaInFlight ??= this.#createUA().finally(() => {\r\n this.#uaInFlight = null;\r\n });\r\n return this.#uaInFlight;\r\n }\r\n\r\n async #createUA(): Promise<JsSipUA> {\r\n const factory = await this.#resolveFactory();\r\n\r\n /*\r\n * Report what actually went wrong, not the layer that caught it.\r\n *\r\n * A rejected voice token, a token endpoint that is down, and a carrier returning no credential\r\n * are three problems with three different fixes; the lower layers already raise the precise\r\n * `auth/*` code. Only supply the generic one when nothing more specific is known.\r\n *\r\n * Inline rather than a helper method: an extra async frame is an extra microtask, and the\r\n * no-INVITE teardown is armed the moment this resolves (QUIRK 1).\r\n */\r\n let credentials: SipCredentials;\r\n try {\r\n credentials = await this.provider.provision();\r\n } catch (cause) {\r\n if (isSoftphoneError(cause)) this.#emitError(cause);\r\n else this.#reportError('provider/provision-failed', cause);\r\n throw cause;\r\n }\r\n this.#domain = domainOf(credentials);\r\n const config = this.#uaConfig(credentials, factory);\r\n const ua = factory.createUA(config);\r\n\r\n ua.on('newRTCSession', ({ session, originator }) => this.#onNewSession(session, originator));\r\n ua.on('disconnected', ({ error }) => {\r\n if (!error) return; // a clean close (our own teardown) is not a failure\r\n this.#setState('reconnecting');\r\n this.#reportError('net/transport-failed', error);\r\n });\r\n ua.on('connected', () => this.#setState('online'));\r\n ua.on('registrationFailed', (e) => {\r\n /*\r\n * On Zadarma the usual cause is geo-routing (SPEC §3.1): a registrar in the wrong datacenter\r\n * answers a bare 401 with no auth challenge. The registrar `domain` is what\r\n * `get_webphone_data.php` handed back, so it is the one detail that makes this diagnosable —\r\n * it rides along as the error's `cause` rather than only reaching a console nobody is reading.\r\n */\r\n const detail = e as { cause?: string; response?: { status_code?: number } } | undefined;\r\n this.#reportError('net/registration-failed', {\r\n cause: detail?.cause,\r\n status: detail?.response?.status_code,\r\n domain: this.#domain,\r\n hint: 'A bare 401 with no auth challenge usually means the registrar is in the wrong datacenter (SPEC §3.1).',\r\n });\r\n this.#teardownUA();\r\n void this.#refreshSignal();\r\n });\r\n\r\n this.#ua = ua;\r\n ua.start();\r\n return ua;\r\n }\r\n\r\n #uaConfig(credentials: SipCredentials, factory: JsSipFactory): JsSipUAConfig {\r\n return {\r\n sockets: [factory.createWebSocket(credentials.wsUrl)],\r\n uri: credentials.uri,\r\n authorization_user: credentials.authUser,\r\n password: credentials.password,\r\n register: true,\r\n register_expires: REGISTER_EXPIRES,\r\n connection_recovery_min_interval: 2,\r\n connection_recovery_max_interval: 30,\r\n // Generic — NOT a provider brand string. If a provider ever requires a specific UA header it\r\n // belongs in the provider, not hard-coded here.\r\n // NOT ours to choose: Zadarma refuses to register any client whose User-Agent it does not\r\n // recognise (see ProviderQuirks.userAgent). The provider decides; the engine just sends it.\r\n user_agent: this.provider.quirks?.userAgent ?? 'Coventia Softphone',\r\n };\r\n }\r\n\r\n #teardownUA(): void {\r\n this.#uaInFlight = null;\r\n if (!this.#ua) return;\r\n try {\r\n this.#ua.terminateSessions({ status_code: 487 });\r\n this.#ua.stop();\r\n } catch {\r\n /* already stopped */\r\n }\r\n this.#ua = null;\r\n this.#activeCall = null;\r\n this.#consultCall = null;\r\n }\r\n\r\n /** Place a call once the UA is registered (its 200 OK for REGISTER has arrived). */\r\n /**\r\n * **No `mediaStream`** — JsSIP calls `getUserMedia` itself, which is what opens a real capture.\r\n *\r\n * That is load-bearing, not incidental. Firefox restricts ICE gathering on a page with no active\r\n * capture: it discards every address but the default route's (\"Ignoring addr from interface other\r\n * than …\" in about:webrtc) and can end up gathering NOTHING, so the INVITE goes out with\r\n * `c=IN IP4 0.0.0.0` and no candidates at all. No STUN or TURN server can rescue an offer that\r\n * contains no candidates. The legacy hook passed `mediaConstraints` and nothing else, for what\r\n * turns out to be a very good reason.\r\n */\r\n #callWhenRegistered(ua: JsSipUA, target: string): Promise<JsSipSession> {\r\n const options = { mediaConstraints: buildAudioConstraints(this.#inputDeviceId), pcConfig: PC_CONFIG };\r\n if (ua.isRegistered()) return Promise.resolve(ua.call(target, options));\r\n return new Promise<JsSipSession>((resolve) => {\r\n ua.on('registered', () => resolve(ua.call(target, options)));\r\n });\r\n }\r\n\r\n #wrapCall(session: JsSipSession): SipEngineCall {\r\n const call = new SipEngineCall(session, {\r\n id: nextCallId(),\r\n buildTarget: (t) => this.#buildTarget(t),\r\n placeConsult: (t) => this.#placeConsult(t),\r\n // The guard is tied to the reject ACTION, not to `ended` — a normal hangup must not arm it.\r\n onReject: () => this.#startRejectGuard(),\r\n iceGatheringCapMs: this.provider.quirks?.iceGatheringCapMs ?? DEFAULT_ICE_CAP_MS,\r\n // Inbound answers open their own peer connection, so they need the same ICE servers the\r\n // outbound leg got. Missing here, an ANSWERED call is the one that fails — the half nobody\r\n // tests until a real person picks up.\r\n pcConfig: PC_CONFIG,\r\n // Read fresh at answer() time, not captured now — a device chosen while the phone is still\r\n // ringing must still apply once it is picked up.\r\n getInputDeviceId: () => this.#inputDeviceId,\r\n });\r\n\r\n this.#activeCall = call;\r\n call.onEvent((event) => {\r\n if (event.type === 'ended') {\r\n /*\r\n The outcome of a call, as a code the widget can show: \"Sin respuesta\", \"Llamada rechazada\".\r\n A normal hangup by either side is not an error.\r\n\r\n A MISSED call is the one outcome `reason` cannot carry on its own. The caller CANCELling\r\n while our phone rings is an ordinary `remote` ending, so it would pass in silence — and it\r\n is the case the user is least able to reconstruct, because they never saw the call. JsSIP\r\n sends the same `Canceled` cause when the user abandons their OWN outbound call, so the\r\n direction is what separates a call that was missed from one that was given up on.\r\n */\r\n const missed = call.direction === 'inbound' && event.cause === 'Canceled';\r\n const code = missed ? 'call/missed' : CALL_END_CODES[event.reason];\r\n // The raw cause rides along so a `call/failed` in a host's logs says which failure it was.\r\n if (code) this.#reportError(code, event.cause);\r\n if (this.#activeCall === call) this.#activeCall = null;\r\n this.#maybeTeardownUA();\r\n }\r\n });\r\n return call;\r\n }\r\n\r\n async #placeConsult(target: string): Promise<SipEngineCall> {\r\n const ua = await this.#ensureUA();\r\n const consultSession = ua.call(this.#buildTarget(target), {\r\n mediaConstraints: buildAudioConstraints(this.#inputDeviceId),\r\n pcConfig: PC_CONFIG,\r\n });\r\n const consultCall = new SipEngineCall(consultSession, {\r\n id: nextCallId(),\r\n buildTarget: (x) => this.#buildTarget(x),\r\n placeConsult: () => Promise.reject(new Error('cannot consult from a consult leg')),\r\n iceGatheringCapMs: this.provider.quirks?.iceGatheringCapMs ?? DEFAULT_ICE_CAP_MS,\r\n });\r\n this.#consultCall = consultCall;\r\n consultCall.onEvent((event) => {\r\n if (event.type === 'ended') {\r\n if (this.#consultCall === consultCall) this.#consultCall = null;\r\n this.#maybeTeardownUA();\r\n }\r\n });\r\n return consultCall;\r\n }\r\n\r\n /**\r\n * On-demand registration (SPEC §3.3): the UA is disposable once NOTHING is left on it — but \"the\r\n * main leg ended\" (or \"no INVITE followed this push\") is not the same fact as \"nothing is left.\"\r\n * `completeTransfer()` (SPEC §5.6) ends the main leg via a REFER…Replaces BYE *while the consult leg\r\n * is still what the PBX needs to bridge the caller onto* — tearing the UA down (and with it, EVERY\r\n * session on it, via `terminateSessions`) the instant the main leg's `ended` fires killed that\r\n * consult leg out from under the transfer before the bridge could use it, and the caller was simply\r\n * gone. Two real call sites hit this independently (found live, 2026-08-04): the main leg's own\r\n * `ended` handler, and `#armNoInviteTeardown`'s 5s timer — the latter re-arms on EVERY push,\r\n * including unrelated \"answered\" pushes Zadarma sends around other account activity, so it can fire\r\n * minutes after a transfer with no `#activeCall` and no idea a consult leg is mid-handoff. Both now\r\n * route through here rather than checking `#activeCall` alone.\r\n */\r\n #maybeTeardownUA(): void {\r\n if (this.provider.registrationMode !== 'on-demand') return;\r\n if (this.#activeCall || this.#consultCall) return;\r\n this.#teardownUA();\r\n }\r\n\r\n #startRejectGuard(): void {\r\n const ms = this.provider.quirks?.rejectGuardMs;\r\n if (ms) this.#rejectGuardUntil = this.now() + ms;\r\n }\r\n\r\n #buildTarget(target: string): string {\r\n return target.includes('@') ? target : `sip:${target}@${this.#domain}`;\r\n }\r\n\r\n #setState(state: SoftphoneState): void {\r\n if (state === this.#state) return;\r\n this.#state = state;\r\n for (const listener of [...this.#stateListeners]) listener(state);\r\n }\r\n}\r\n","import type { JsSipFactory, JsSipUA, JsSipUAConfig } from './jssip.js';\r\n\r\n/**\r\n * The REAL JsSipFactory — the only place `jssip` is loaded, via a **dynamic import** so that merely\r\n * importing `@coventia/softphone` never pulls it. jssip is CJS and touches browser globals; a static\r\n * import would break `import` under SSR (SPEC §10.2) and drag ~66 KB into every bundle.\r\n *\r\n * Because the import is async but `JsSipFactory.createUA` is sync, we load jssip once up front via\r\n * `loadJsSipFactory()` and hand back a fully-synchronous factory.\r\n */\r\n\r\ninterface JsSipModule {\r\n UA: new (config: JsSipUAConfig) => JsSipUA;\r\n WebSocketInterface: new (url: string) => unknown;\r\n}\r\n\r\nlet cached: JsSipFactory | null = null;\r\n\r\nexport async function loadJsSipFactory(): Promise<JsSipFactory> {\r\n if (cached) return cached;\r\n const mod = (await import('jssip')) as unknown as { default?: JsSipModule } & Partial<JsSipModule>;\r\n const JsSIP = (mod.default ?? mod) as JsSipModule;\r\n if (!JsSIP?.UA || !JsSIP?.WebSocketInterface) {\r\n throw new Error('jssip: unexpected module shape (no UA / WebSocketInterface)');\r\n }\r\n cached = {\r\n createWebSocket: (wsUrl) => new JsSIP.WebSocketInterface(wsUrl),\r\n createUA: (config) => new JsSIP.UA(config),\r\n };\r\n return cached;\r\n}\r\n","import type {\r\n IncomingSignal,\r\n ProviderQuirks,\r\n RegistrationMode,\r\n SipCredentials,\r\n SipProvider,\r\n} from './provider.js';\r\n\r\n/**\r\n * The Zadarma provider — the FIRST (and, for now, only) `SipProvider`. Everything Zadarma-specific\r\n * that the generic SIP engine (`transports/sip/`) must not know lives here, expressed as data the\r\n * engine reads (SPEC §5.4).\r\n *\r\n * Network I/O is INJECTED (`ZadarmaDeps`) for two reasons: the JSONP exchange and the push socket\r\n * are browser-only, and the quirk tests (SPEC §3, the S4 safety net) must drive this without a\r\n * browser, a network, or a real call.\r\n */\r\n\r\n/**\r\n * The presence heartbeat Zadarma's push gateway expects, and how long before key expiry we rebuild\r\n * the socket. Both are Zadarma's numbers, carried over from the CRM hook that ran them in production.\r\n */\r\nconst PING = { localMs: 30_000, globalMs: 60_000 } as const;\r\nconst KEY_REFRESH_BUFFER_MS = 2 * 60 * 60 * 1000;\r\n\r\n/** The Zadarma quirk-timer values (SPEC §3). The engine reads these; it never hard-codes them. */\r\nexport const ZADARMA_QUIRKS: ProviderQuirks = {\r\n // Zadarma's registrar REFUSES to register a WebRTC client with any other User-Agent — a clean 401\r\n // after a valid digest challenge, indistinguishable from a wrong password (§3.1). The old CRM\r\n // hook carried this string; the port dropped it, and registration failed for exactly that reason.\r\n userAgent: 'Zadarma webRTC',\r\n // Zadarma re-rings a declined call for ~30 s (return_timeout). Swallow those (§3.4).\r\n rejectGuardMs: 35_000,\r\n // A push is not an INVITE (§3.3). If none arrives, tear the fresh UA down.\r\n noInviteTeardownMs: 5_000,\r\n // A ceiling on ICE gathering, not a target: JsSIP sends as soon as gathering completes. 3 s leaves\r\n // room for a TURN allocation (whose relay candidates arrive last) while still bounding the ~40 s\r\n // Chrome can spend against an unreachable server. See `capIceGathering`.\r\n iceGatheringCapMs: 3_000,\r\n};\r\n\r\nexport interface ZadarmaKeyResponse {\r\n readonly key: string;\r\n readonly sip: string;\r\n readonly keyExpiresAt?: string;\r\n}\r\n\r\n/** The minimal push-socket surface we consume (Zadarma's Socket.IO v2 gateway). */\r\nexport interface ZadarmaPushSocket {\r\n on(event: 'connect' | 'disconnect' | 'connect_error' | 'update', cb: () => void): void;\r\n /**\r\n * Send to the gateway. Needed for the presence pings — see `PING`.\r\n *\r\n * Its absence is why the extension went offline after a few idle minutes: the socket cannot be\r\n * kept alive by a client that has no way to say anything on it.\r\n */\r\n emit(event: string, payload?: unknown): void;\r\n close(): void;\r\n readonly connected: boolean;\r\n}\r\n\r\nexport interface ZadarmaDeps {\r\n /**\r\n * Fetch `{ key, sip }` from the Coventia backend (authenticated with the voice token upstream).\r\n * The key is a scoped, refreshable credential — NEVER a stored SIP password (SPEC §6.4, §6.5).\r\n */\r\n fetchKey(): Promise<ZadarmaKeyResponse>;\r\n\r\n /**\r\n * Exchange the key for ephemeral SIP credentials. MUST run in the browser: Zadarma geo-routes the\r\n * registrar `domain` (and the matching `pass`) to the caller's region (SPEC §3.1). The returned\r\n * password is ephemeral and must never be persisted.\r\n */\r\n exchangeWebphoneData(key: string, sip: string): Promise<{ pass: string; domain: string }>;\r\n\r\n /** Open the Zadarma push socket for a `(key, sip)`. */\r\n createPushSocket(url: string): ZadarmaPushSocket;\r\n\r\n /**\r\n * OPTIONAL: proactively rebuild the push socket, via `cb`, while the tab is hidden.\r\n *\r\n * The obvious fix — ping more often — does not work: `push-socket-reliability.md` §5.2 confirmed\r\n * empirically that Zadarma's gateway is keyed to the raw Engine.IO ping specifically, and that ping\r\n * dies under background-tab timer throttling no matter how much other traffic the socket carries.\r\n * Rather than trying to keep a fragile internal timer alive, `cb` tears the connection down and\r\n * rebuilds it on a schedule safely inside the failure window (§5.3) — a fresh socket always gets\r\n * its own fresh, reliably-successful first ping cycle.\r\n *\r\n * Returns a teardown function. Omitted (tests, SSR, no `Worker`) and `ZadarmaPushSignal` simply\r\n * does not self-heal in the background — exactly today's behaviour.\r\n */\r\n watchBackgroundReconnect?(cb: () => void): () => void;\r\n}\r\n\r\n/** `wss://ws.zadarma.com/webrtc_pushes?key=…&integrationType=site&sipId=…` */\r\nexport function buildPushUrl(key: string, sip: string): string {\r\n const params = new URLSearchParams({ key, integrationType: 'site', sipId: sip });\r\n return `wss://ws.zadarma.com/webrtc_pushes?${params.toString()}`;\r\n}\r\n\r\n/**\r\n * The push socket as an `IncomingSignal`. Its `connect()` fetches a key and opens the socket; a\r\n * Zadarma `update` becomes an out-of-band incoming signal the engine reacts to (SPEC §3.2–3.3).\r\n *\r\n * The engine deliberately does NOT ring on this signal — it rings on the SIP INVITE. This only tells\r\n * the engine to register a UA so Zadarma will *send* the INVITE.\r\n */\r\nexport class ZadarmaPushSignal implements IncomingSignal {\r\n #socket: ZadarmaPushSocket | null = null;\r\n #connected = false;\r\n #pingTimers: Array<ReturnType<typeof setInterval>> = [];\r\n #refreshTimer: ReturnType<typeof setTimeout> | undefined;\r\n #unwatchBackground: (() => void) | undefined;\r\n #closed = false;\r\n readonly #listeners = new Set<() => void>();\r\n readonly #connectionListeners = new Set<(connected: boolean) => void>();\r\n\r\n constructor(private readonly deps: ZadarmaDeps) {}\r\n\r\n async connect(): Promise<void> {\r\n if (this.#socket) return;\r\n this.#closed = false;\r\n // Armed once per signal lifetime, not per reopen — `#reopen()` itself is the callback.\r\n this.#unwatchBackground ??= this.deps.watchBackgroundReconnect?.(() => void this.#reopen());\r\n await this.#open();\r\n }\r\n\r\n async #open(): Promise<void> {\r\n const { key, sip, keyExpiresAt } = await this.deps.fetchKey();\r\n if (this.#closed) return;\r\n\r\n const socket = this.deps.createPushSocket(buildPushUrl(key, sip));\r\n this.#socket = socket;\r\n\r\n socket.on('connect', () => {\r\n this.#setConnected(true);\r\n this.#startPings(socket);\r\n });\r\n socket.on('update', () => this.#emit());\r\n // Both mean the same thing to us: no INVITE can arrive right now.\r\n socket.on('disconnect', () => {\r\n this.#stopPings();\r\n this.#setConnected(false);\r\n });\r\n socket.on('connect_error', () => {\r\n this.#stopPings();\r\n this.#setConnected(false);\r\n });\r\n\r\n this.#armRefresh(keyExpiresAt);\r\n }\r\n\r\n /**\r\n * Presence pings — **the reason an idle tab keeps receiving calls**.\r\n *\r\n * Zadarma's gateway decides whether the extension is online, and it decides from these. Without\r\n * them the socket goes quiet, Zadarma marks the extension offline within minutes, and INVITEs stop\r\n * being routed — with no error anywhere, because nothing failed. The only visible symptom is that\r\n * the phone silently stops ringing until the page is reloaded.\r\n *\r\n * The names and intervals are Zadarma's, taken from the CRM hook that ran this in production for\r\n * years. They are not ours to tune.\r\n */\r\n #startPings(socket: ZadarmaPushSocket): void {\r\n this.#stopPings();\r\n this.#pingTimers = [\r\n setInterval(() => socket.emit('localPing', {}), PING.localMs),\r\n setInterval(() => socket.emit('globalPing', {}), PING.globalMs),\r\n ];\r\n }\r\n\r\n #stopPings(): void {\r\n for (const t of this.#pingTimers) clearInterval(t);\r\n this.#pingTimers = [];\r\n }\r\n\r\n /**\r\n * Rebuild the socket before the WebRTC key expires.\r\n *\r\n * The key is baked into the socket URL, so socket.io's own reconnection re-presents whatever it was\r\n * given at construction. A tab left open past the key's 72 h lifetime would reconnect forever with\r\n * a dead credential. Absolute, from the backend's `key_expires_at`, so it targets the real expiry\r\n * rather than restarting a countdown on every reconnect.\r\n */\r\n #armRefresh(keyExpiresAt: string | undefined): void {\r\n clearTimeout(this.#refreshTimer);\r\n if (!keyExpiresAt) return;\r\n const ms = new Date(keyExpiresAt).getTime() - Date.now() - KEY_REFRESH_BUFFER_MS;\r\n if (!Number.isFinite(ms) || ms <= 0) return;\r\n this.#refreshTimer = setTimeout(() => void this.#reopen(), ms);\r\n }\r\n\r\n async #reopen(): Promise<void> {\r\n if (this.#closed) return;\r\n this.#stopPings();\r\n this.#socket?.close();\r\n this.#socket = null;\r\n this.#setConnected(false);\r\n try {\r\n await this.#open();\r\n } catch {\r\n // A failed refresh leaves us disconnected and SAYING so, which is the point: the engine can\r\n // surface it instead of the phone quietly never ringing again.\r\n }\r\n }\r\n\r\n disconnect(): void {\r\n this.#closed = true;\r\n this.#stopPings();\r\n clearTimeout(this.#refreshTimer);\r\n this.#refreshTimer = undefined;\r\n this.#unwatchBackground?.();\r\n this.#unwatchBackground = undefined;\r\n this.#socket?.close();\r\n this.#socket = null;\r\n this.#setConnected(false);\r\n }\r\n\r\n onSignal(listener: () => void): () => void {\r\n this.#listeners.add(listener);\r\n return () => this.#listeners.delete(listener);\r\n }\r\n\r\n onConnectionChange(listener: (connected: boolean) => void): () => void {\r\n this.#connectionListeners.add(listener);\r\n return () => this.#connectionListeners.delete(listener);\r\n }\r\n\r\n get connected(): boolean {\r\n return this.#connected;\r\n }\r\n\r\n #setConnected(connected: boolean): void {\r\n if (this.#connected === connected) return;\r\n this.#connected = connected;\r\n for (const listener of [...this.#connectionListeners]) listener(connected);\r\n }\r\n\r\n #emit(): void {\r\n for (const listener of [...this.#listeners]) listener();\r\n }\r\n}\r\n\r\nexport class ZadarmaProvider implements SipProvider {\r\n readonly name = 'zadarma';\r\n // Zadarma's PBX ignores a standing registration — it routes INVITEs only to a UA that registered\r\n // in response to a push (SPEC §3.2). Hence on-demand.\r\n readonly registrationMode: RegistrationMode = 'on-demand';\r\n readonly quirks: ProviderQuirks = ZADARMA_QUIRKS;\r\n readonly incomingSignal: IncomingSignal;\r\n\r\n constructor(private readonly deps: ZadarmaDeps) {\r\n this.incomingSignal = new ZadarmaPushSignal(deps);\r\n }\r\n\r\n /**\r\n * Per-registration credentialing: fetch a scoped key, then exchange it in the browser for\r\n * ephemeral SIP creds. The key fetch is cheap and backend-cached; doing it here (rather than\r\n * sharing state with the push signal) keeps the two paths independent.\r\n */\r\n async provision(): Promise<SipCredentials> {\r\n const { key, sip } = await this.deps.fetchKey();\r\n const { pass, domain } = await this.deps.exchangeWebphoneData(key, sip);\r\n return {\r\n uri: `sip:${sip}@${domain}`,\r\n authUser: sip,\r\n password: pass,\r\n wsUrl: `wss://${domain}:4443`,\r\n };\r\n }\r\n}\r\n","import type { TokenSource } from '../../core/types.js';\r\nimport { invalidateToken } from '../../core/token-cache.js';\r\nimport { createError } from '../../core/errors.js';\r\nimport type { ZadarmaDeps, ZadarmaKeyResponse, ZadarmaPushSocket } from './zadarma.js';\r\n\r\n/** The key fetch was refused. Distinct so only THIS failure triggers a token re-mint. */\r\nclass UnauthorizedKeyError extends Error {}\r\n\r\n/**\r\n * The REAL, browser-only Zadarma network deps: the backend key fetch, the JSONP credential exchange,\r\n * and the Socket.IO v2 push socket. Isolated here (not in zadarma.ts) so the provider stays\r\n * unit-testable and so these browser globals never enter the SSR import path — `socket.io-client` is\r\n * dynamically imported, and every function touches `window`/`document`/`fetch` only when called\r\n * (SPEC §10.2).\r\n *\r\n * Ported from the legacy `useSipUA.js` / `useZadarmaWebrtcLogin.js`, with the geo-routing rationale\r\n * intact (SPEC §3.1).\r\n */\r\n\r\nexport interface ZadarmaBrowserConfig {\r\n /** Coventia backend base URL, e.g. `https://api.coventia.es`. */\r\n readonly apiBase: string;\r\n /** Provides the auth token for the backend key fetch. */\r\n readonly tokenSource: TokenSource;\r\n /** Selected organization id, appended to the key request. */\r\n readonly organizationId?: string | undefined;\r\n}\r\n\r\n/**\r\n * Fetch `{ key, sip }` from the Coventia backend.\r\n *\r\n * Today this targets the existing `/phone/webrtc/key`, authenticated with the CRM's own session.\r\n * Phase 2 moves it to `/api/v1/phone/webrtc/key`, authenticated with a voice token and CORS-gated —\r\n * which is what lets a PARTNER's browser call it from their origin (SPEC §6.6, hop ③). Only the URL\r\n * and the credential change; this function's shape does not, which is the point.\r\n *\r\n * The key is a scoped, refreshable credential — never a stored SIP password (SPEC §6.4).\r\n */\r\nexport function makeFetchKey(config: ZadarmaBrowserConfig): () => Promise<ZadarmaKeyResponse> {\r\n const attempt = async (): Promise<ZadarmaKeyResponse> => {\r\n // The host's own endpoint failing is a DIFFERENT failure from us refusing their token, and the\r\n // fix differs too — \"your broker is down\" vs \"mint a fresh one\". Flattening both into one code\r\n // would send an integrator to the wrong place.\r\n let token: string;\r\n try {\r\n ({ token } = await config.tokenSource.getToken());\r\n } catch (cause) {\r\n throw createError('auth/token-fetch-failed', cause);\r\n }\r\n const qs = config.organizationId\r\n ? `?organization_id=${encodeURIComponent(config.organizationId)}`\r\n : '';\r\n const res = await fetch(`${config.apiBase}/phone/webrtc/key${qs}`, {\r\n headers: { Authorization: token },\r\n });\r\n if (!res.ok) {\r\n const body = (await res.json().catch(() => null)) as { message?: string } | null;\r\n const message = body?.message ?? `webrtc/key HTTP ${res.status}`;\r\n throw res.status === 401 ? new UnauthorizedKeyError(message) : new Error(message);\r\n }\r\n const data = (await res.json()) as {\r\n key: string;\r\n sip: string;\r\n key_expires_at?: string;\r\n };\r\n return {\r\n key: data.key,\r\n sip: data.sip,\r\n ...(data.key_expires_at ? { keyExpiresAt: data.key_expires_at } : {}),\r\n };\r\n };\r\n\r\n /*\r\n * Retry once on 401 with a fresh token — same reasoning as `createGet`, and it matters more here:\r\n * this runs per registration, so a token revoked mid-session would otherwise block every call\r\n * until its TTL elapsed.\r\n */\r\n return async () => {\r\n try {\r\n return await attempt();\r\n } catch (error) {\r\n if (!(error instanceof UnauthorizedKeyError)) throw error;\r\n invalidateToken(config.tokenSource);\r\n try {\r\n return await attempt();\r\n } catch (retryError) {\r\n // Refused twice, the second time with a freshly minted token — so the credential itself is\r\n // rejected, not merely stale.\r\n if (retryError instanceof UnauthorizedKeyError) {\r\n throw createError('auth/token-rejected', retryError);\r\n }\r\n throw retryError;\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Exchange the key for `{ pass, domain }` via Zadarma's `get_webphone_data.php`.\r\n *\r\n * **MUST run in the browser** (SPEC §3.1) — this is the load-bearing constraint and it has not\r\n * changed: Zadarma geo-routes the registrar `domain`, and the `pass` that matches it, to the\r\n * datacenter nearest the *caller's* IP. Called server-side it pins every user to the backend's\r\n * region and REGISTER is refused.\r\n *\r\n * **`fetch`, not a `<script>` tag.** The endpoint is JSONP-shaped — it always answers\r\n * `callbackName({...})`, with no raw-JSON mode — but it now sends `Access-Control-Allow-Origin: *`,\r\n * so the response is readable and the wrapper is just text to strip. Verified against the live\r\n * endpoint before switching.\r\n *\r\n * The old `<script>` injection worked, but cost three things:\r\n * - **A console warning on every call.** The response is served as `application/json`, and a\r\n * classic script with a non-JS MIME type warns in Firefox (\"loaded despite its MIME type…\").\r\n * It executes, so it was harmless — but it is noise in a console that should stay readable, and\r\n * nothing on our side could silence it.\r\n * - **A CSP demand we should not make.** A `<script src>` to a third-party origin needs\r\n * `script-src https://api.zadarma.com` — permission to EXECUTE their code. `fetch` needs\r\n * `connect-src`, which is permission to TALK to them. For a reseller embedding this SDK that is\r\n * a materially smaller ask (SPEC §7, Phase 3: we publish the CSP directives).\r\n * - **A global.** A `window[cbName]` per exchange, plus a `<script>` in `<head>` to clean up.\r\n *\r\n * What we keep is real error handling: an HTTP status, a network failure and a timeout are now\r\n * distinguishable, where a `<script>` could only ever report \"onerror\".\r\n */\r\nexport async function exchangeWebphoneData(\r\n key: string,\r\n sip: string,\r\n timeoutMs = 10_000,\r\n): Promise<{ pass: string; domain: string }> {\r\n const params = new URLSearchParams({\r\n // Still sent: the endpoint wraps its response either way, and naming the callback keeps the\r\n // wrapper predictable rather than relying on their default.\r\n jsonpCallback: 'cv',\r\n integrationType: 'site',\r\n key,\r\n sipId: sip,\r\n });\r\n\r\n let res: Response;\r\n try {\r\n res = await fetch(\r\n `https://api.zadarma.com/sys/webrtc/get_webphone_data.php?${params.toString()}`,\r\n { signal: AbortSignal.timeout(timeoutMs) },\r\n );\r\n } catch (cause) {\r\n const timedOut = cause instanceof DOMException && cause.name === 'TimeoutError';\r\n throw new Error(timedOut ? 'get_webphone_data timed out' : 'get_webphone_data network error', {\r\n cause,\r\n });\r\n }\r\n if (!res.ok) throw new Error(`get_webphone_data HTTP ${res.status}`);\r\n\r\n const data = parseJsonp(await res.text());\r\n if (!data || data.error || !data.pass || !data.domain) {\r\n throw new Error(data?.error?.content ?? 'get_webphone_data: missing SIP credentials');\r\n }\r\n return { pass: data.pass, domain: data.domain };\r\n}\r\n\r\ninterface WebphoneData {\r\n error?: { content?: string };\r\n pass?: string;\r\n domain?: string;\r\n}\r\n\r\n/**\r\n * `cv({...})` → the object.\r\n *\r\n * Sliced between the FIRST `(` and the LAST `)` rather than matched against the callback name we\r\n * sent: the endpoint ignores an empty `jsonpCallback` and falls back to its own literal\r\n * `jsonpCallback(...)`, so trusting the name we asked for would break on a response we can see it\r\n * actually returns.\r\n */\r\nfunction parseJsonp(text: string): WebphoneData | null {\r\n const open = text.indexOf('(');\r\n const close = text.lastIndexOf(')');\r\n const body = open !== -1 && close > open ? text.slice(open + 1, close) : text;\r\n try {\r\n return JSON.parse(body) as WebphoneData;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Open the Zadarma push socket (Socket.IO **v2** — their gateway is v2, hence the pinned dependency).\r\n * `socket.io-client` is dynamically imported so it never enters the SSR bundle.\r\n *\r\n * Note: because the ZadarmaPushSocket surface is synchronous but the socket.io import is async, the\r\n * returned wrapper connects lazily and buffers `on()` registrations until the client is ready.\r\n */\r\nexport function createPushSocket(url: string): ZadarmaPushSocket {\r\n const handlers = new Map<string, Array<() => void>>();\r\n let client: {\r\n on(e: string, cb: () => void): void;\r\n emit(e: string, payload?: unknown): void;\r\n close(): void;\r\n connected?: boolean;\r\n } | null = null;\r\n let closed = false;\r\n\r\n void (async () => {\r\n // A LITERAL specifier, and it must stay one. This was `const s = 'socket.io-client-v2'` behind an\r\n // indirection to dodge TS7016 on an untyped module — which no bundler can statically analyse, so\r\n // the browser got a bare specifier, the push socket never connected, `start()` rejected, and the\r\n // softphone never reached `online`. The types now come from `src/socket-io.d.ts`, which is what\r\n // should have solved it in the first place (`jssip` was always a literal and always worked).\r\n const mod = await import('socket.io-client-v2');\r\n if (closed) return;\r\n // v2's connector arrives as `default` under ESM interop and as `io` when it does not.\r\n const connect = mod.default ?? mod.io;\r\n if (!connect) throw new Error('socket.io-client-v2: no connect export');\r\n const c = connect(url, {\r\n transports: ['websocket'],\r\n reconnection: true,\r\n // The push socket drops on a schedule under background-tab timer throttling, NOT from network\r\n // flakiness — every reconnect observed succeeds on the first attempt (push-socket-reliability.md\r\n // §2.3, §5.1). There is nothing to back off from on that first attempt, so waiting the old 10s\r\n // before retrying was pure, uncontested exposure — an inbound call landing in that window is\r\n // lost, unqueued (Zadarma's gateway speaks EIO3, no offline redelivery). `reconnectionDelayMax`\r\n // stays as the ceiling for a genuinely down gateway, where backing off still matters.\r\n reconnectionDelay: 300,\r\n reconnectionDelayMax: 30_000,\r\n });\r\n client = c;\r\n for (const [event, cbs] of handlers) for (const cb of cbs) c!.on(event, cb);\r\n })();\r\n\r\n return {\r\n on(event, cb) {\r\n const list = handlers.get(event) ?? [];\r\n list.push(cb);\r\n handlers.set(event, list);\r\n client?.on(event, cb);\r\n },\r\n emit(event, payload) {\r\n // Dropped rather than queued when the client is not up yet: every caller is a periodic ping,\r\n // so the next one is seconds away and a replayed backlog would be worse than a missed beat.\r\n client?.emit(event, payload);\r\n },\r\n close() {\r\n closed = true;\r\n client?.close();\r\n },\r\n get connected() {\r\n return client?.connected ?? false;\r\n },\r\n };\r\n}\r\n\r\n/** How long the tab can stay hidden before we proactively rebuild the socket. See `watchBackgroundReconnect`. */\r\nconst BACKGROUND_RECONNECT_MS = 20_000;\r\n\r\n/**\r\n * The Worker's entire job: an alarm clock, nothing else. It owns no socket state and knows nothing\r\n * about Zadarma — Chrome does not apply background-tab timer throttling to Worker threads, only to\r\n * the page's own JS context, so ticking here stays reliable exactly where `setInterval` on the main\r\n * thread was proven not to be (`push-socket-reliability.md` §5.2, §5.3). `start`/`stop` rather than\r\n * a fire-and-forget interval so the SAME worker can be reused across every hidden/visible cycle in a\r\n * session instead of being spun up and torn down each time.\r\n */\r\nconst HEARTBEAT_WORKER_SOURCE = `\r\n let timer;\r\n self.onmessage = (e) => {\r\n if (e.data.type === 'start') timer = setInterval(() => self.postMessage('tick'), e.data.intervalMs);\r\n else if (e.data.type === 'stop') { clearInterval(timer); timer = undefined; }\r\n };\r\n`;\r\n\r\n/**\r\n * Proactively rebuilds the push socket, via `cb`, on a schedule while the tab is hidden — the real\r\n * implementation of the optional `ZadarmaDeps` seam (`zadarma.ts`).\r\n *\r\n * **Constructed lazily, only when called** (SPEC §10.3 — no browser globals at module scope): safe\r\n * to import on a server, and a host that never calls it (tests, an environment with no `Worker`)\r\n * pays nothing.\r\n */\r\nexport function watchBackgroundReconnect(cb: () => void): () => void {\r\n if (typeof document === 'undefined' || typeof Worker === 'undefined') return () => {};\r\n\r\n let worker: Worker | null = null;\r\n let blobUrl: string | null = null;\r\n\r\n const ensureWorker = (): Worker => {\r\n if (!worker) {\r\n blobUrl = URL.createObjectURL(new Blob([HEARTBEAT_WORKER_SOURCE], { type: 'application/javascript' }));\r\n worker = new Worker(blobUrl);\r\n worker.onmessage = (e: MessageEvent) => {\r\n if (e.data === 'tick') cb();\r\n };\r\n }\r\n return worker;\r\n };\r\n\r\n const onVisibilityChange = (): void => {\r\n if (document.visibilityState === 'hidden') {\r\n ensureWorker().postMessage({ type: 'start', intervalMs: BACKGROUND_RECONNECT_MS });\r\n } else {\r\n worker?.postMessage({ type: 'stop' });\r\n }\r\n };\r\n\r\n document.addEventListener('visibilitychange', onVisibilityChange);\r\n // A signal can `connect()` while the tab is already hidden (e.g. a backgrounded pre-loaded tab).\r\n if (document.visibilityState === 'hidden') onVisibilityChange();\r\n\r\n return () => {\r\n document.removeEventListener('visibilitychange', onVisibilityChange);\r\n worker?.postMessage({ type: 'stop' });\r\n worker?.terminate();\r\n if (blobUrl) URL.revokeObjectURL(blobUrl);\r\n };\r\n}\r\n\r\n/** Assemble the real browser deps for a `ZadarmaProvider`. */\r\nexport function makeZadarmaBrowserDeps(config: ZadarmaBrowserConfig): ZadarmaDeps {\r\n return {\r\n fetchKey: makeFetchKey(config),\r\n exchangeWebphoneData: (key, sip) => exchangeWebphoneData(key, sip),\r\n createPushSocket,\r\n watchBackgroundReconnect,\r\n };\r\n}\r\n","import { SipCallEngine } from './sip/ua.js';\r\nimport { loadJsSipFactory } from './sip/real-factory.js';\r\nimport { ZadarmaProvider } from './providers/zadarma.js';\r\nimport { makeZadarmaBrowserDeps, type ZadarmaBrowserConfig } from './providers/zadarma-browser.js';\r\nimport { cachedTokenSource } from '../core/token-cache.js';\r\nimport type { CallEngine } from '../core/engine.js';\r\n\r\n/**\r\n * Assemble a production Zadarma call engine: the real browser deps (backend key fetch + JSONP\r\n * exchange + push socket) → `ZadarmaProvider` → `SipCallEngine`, with jssip loaded lazily via a\r\n * dynamic import (SPEC §10.2 — importing the package stays SSR-safe).\r\n *\r\n * This is the real thing the app and the e2e smoke test use. It is intentionally NOT the automatic\r\n * default of `createSoftphone` yet — the app wires it explicitly in S9, and the voice-token endpoint\r\n * migration is Phase 2 (SPEC §6).\r\n */\r\nexport function createZadarmaEngine(config: ZadarmaBrowserConfig): CallEngine {\r\n // Cached here as well as in `createSoftphone`: a host passes the same `TokenSource` object to both,\r\n // and `cachedTokenSource` is keyed on that object — so the two share ONE token rather than minting\r\n // one each. Without this the WebRTC-key fetch, which runs per registration, would be uncached.\r\n const provider = new ZadarmaProvider(\r\n makeZadarmaBrowserDeps({ ...config, tokenSource: cachedTokenSource(config.tokenSource) }),\r\n );\r\n return new SipCallEngine(provider, loadJsSipFactory);\r\n}\r\n","import type { TypedEmitter } from './events.js';\r\n\r\n/**\r\n * A tiny typed event emitter. Framework-free — no Node `events`, no browser globals (SPEC §10.2).\r\n *\r\n * `on()` / `once()` return an unsubscribe function (SPEC §5.3 / events.ts): cleanup is impossible to\r\n * get wrong in a React effect, and there is no opaque handle to retain (Vonage's mistake).\r\n *\r\n * A listener that throws must not stop the other listeners or corrupt the emitter — a bug in the\r\n * host's UI code should never take the call engine down mid-call. Throws are routed to `onError` so\r\n * the engine can log them without swallowing them silently.\r\n */\r\n// Unconstrained, matching TypedEmitter: the event maps are keyed by enum members (CallEvent,\r\n// SoftphoneEvent), which are closed object types, not string index signatures.\r\nexport class Emitter<M> implements TypedEmitter<M> {\r\n // A copy of the listener set is iterated on emit, so a listener that unsubscribes (or subscribes)\r\n // during dispatch does not mutate the set mid-iteration.\r\n readonly #listeners = new Map<keyof M, Set<(payload: never) => void>>();\r\n\r\n constructor(private readonly onListenerError?: (event: keyof M, error: unknown) => void) {}\r\n\r\n on<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void {\r\n let set = this.#listeners.get(event);\r\n if (!set) {\r\n set = new Set();\r\n this.#listeners.set(event, set);\r\n }\r\n set.add(listener as (payload: never) => void);\r\n return () => this.off(event, listener);\r\n }\r\n\r\n once<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void {\r\n const wrapped = (payload: M[E]): void => {\r\n off();\r\n listener(payload);\r\n };\r\n const off = this.on(event, wrapped);\r\n return off;\r\n }\r\n\r\n off<E extends keyof M>(event: E, listener: (payload: M[E]) => void): void {\r\n const set = this.#listeners.get(event);\r\n if (!set) return;\r\n set.delete(listener as (payload: never) => void);\r\n if (set.size === 0) this.#listeners.delete(event);\r\n }\r\n\r\n emit<E extends keyof M>(event: E, payload: M[E]): void {\r\n const set = this.#listeners.get(event);\r\n if (!set) return;\r\n for (const listener of [...set]) {\r\n try {\r\n (listener as (p: M[E]) => void)(payload);\r\n } catch (error) {\r\n // A throwing listener must not abort the dispatch loop or bubble into the engine.\r\n this.onListenerError?.(event, error);\r\n }\r\n }\r\n }\r\n\r\n /** Drop every listener. Called on teardown so a destroyed object leaks nothing. */\r\n clear(): void {\r\n this.#listeners.clear();\r\n }\r\n}\r\n","import type { Call, CallState, HangupReason, HoldOriginator } from './call.js';\r\nimport type { SoftphoneError } from './errors.js';\r\nimport type { PresenceEntry, SoftphoneState, TransferOutcomeEvent } from './types.js';\r\n\r\n/**\r\n * Typed events.\r\n *\r\n * Enums rather than magic strings: autocomplete, and rename-safety. Daily's magic strings are the\r\n * counter-example — a typo in `'participant-joined'` is a silent no-op forever.\r\n *\r\n * Each map narrows the listener payload PER EVENT, which is the thing that would have been painful\r\n * to express in JSDoc and is a large part of why this package is TypeScript (SPEC §2.2).\r\n */\r\n\r\nexport enum SoftphoneEvent {\r\n /** Lifecycle. `online` = reachable, NOT SIP-registered — see SoftphoneState. */\r\n StateChanged = 'stateChanged',\r\n /** An INVITE landed. The Call is `ringing`; answer or reject it. */\r\n IncomingCall = 'incomingCall',\r\n /**\r\n * The active call changed — a call began (inbound OR outbound) or ended. Fires for both\r\n * directions, unlike IncomingCall. This is what a UI binds to in order to track \"the current call\"\r\n * without threading the Call through every component.\r\n */\r\n ActiveCallChanged = 'activeCallChanged',\r\n /**\r\n * The browser refused to play audio (autoplay policy).\r\n *\r\n * This is the single most under-documented failure in the industry and it is how calls get MISSED:\r\n * a blocked ringtone rings silently. Surface a button and call `startAudio()` from its click\r\n * handler. Only Twilio and LiveKit document it properly.\r\n */\r\n PlaybackBlocked = 'playbackBlocked',\r\n /**\r\n * `softphone.audio` changed: a device was plugged/unplugged (`inputs`/`outputs`), or the selected\r\n * input/output changed (`currentInputId`/`currentOutputId`). One event for all of it — re-read\r\n * `softphone.audio` rather than inspecting the payload, which carries nothing (`useDevices` does\r\n * exactly this).\r\n */\r\n DeviceChange = 'deviceChange',\r\n /** Fatal or non-fatal — narrow on `error.fatal`. */\r\n Error = 'error',\r\n /**\r\n * This tab's leadership changed (`multi-tab-leader-election.md`) — a DIFFERENT axis from\r\n * `StateChanged`: `state` is reachability, this is whether this tab is even allowed to be\r\n * reachable at all while another tab holding the same extension stays open.\r\n */\r\n LeaderChanged = 'leaderChanged',\r\n /**\r\n * `Completar` completed a transfer, and the target's SECOND ring either connected or didn't —\r\n * `docs/specs/transfer-outcome-relay.md` (Phase A). Fires for BOTH outcomes so a host can react to\r\n * either; only `'not-connected'` is worth surfacing to the agent (spec §5.5) — roughly a minute\r\n * after the transfer for a target who never answers, seconds for a decline or the caller hanging up\r\n * first. Requires a `TransferOutcomeSource` (from `coventia`/`apiBase`, or a host override); never\r\n * fires at all when none is configured, since there is then nothing to poll.\r\n */\r\n TransferOutcome = 'transferOutcome',\r\n /**\r\n * `softphone.presence` changed — the live agent-presence list (Phase B,\r\n * `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a\r\n * `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being\r\n * `online && isLeader`; never fires at all otherwise, same reasoning as `TransferOutcome`.\r\n */\r\n PresenceChanged = 'presenceChanged',\r\n}\r\n\r\nexport interface SoftphoneEventMap {\r\n [SoftphoneEvent.StateChanged]: { readonly state: SoftphoneState; readonly previous: SoftphoneState };\r\n [SoftphoneEvent.IncomingCall]: Call;\r\n [SoftphoneEvent.ActiveCallChanged]: { readonly call: Call | null };\r\n [SoftphoneEvent.PlaybackBlocked]: void;\r\n [SoftphoneEvent.DeviceChange]: void;\r\n [SoftphoneEvent.Error]: SoftphoneError;\r\n [SoftphoneEvent.LeaderChanged]: { readonly isLeader: boolean };\r\n [SoftphoneEvent.TransferOutcome]: TransferOutcomeEvent;\r\n [SoftphoneEvent.PresenceChanged]: { readonly agents: readonly PresenceEntry[] };\r\n}\r\n\r\nexport enum CallEvent {\r\n /** Any transition. The catch-all; the specific events below are conveniences. */\r\n StateChanged = 'stateChanged',\r\n /** Media is flowing. */\r\n Accepted = 'accepted',\r\n /** Terminal. Always fires exactly once, whatever the reason. */\r\n Ended = 'ended',\r\n Muted = 'muted',\r\n /** `originator: 'remote'` means the OTHER party held US — new capability; today's softphone is blind to it. */\r\n Held = 'held',\r\n Unheld = 'unheld',\r\n /** Media lost; an ICE restart is in flight. The call is NOT over — show a banner. */\r\n Reconnecting = 'reconnecting',\r\n Reconnected = 'reconnected',\r\n DigitsSent = 'digitsSent',\r\n /** The contact name resolved after the call was already ringing. */\r\n RemotePartyUpdated = 'remotePartyUpdated',\r\n /**\r\n * The remote audio stream is available. Attach it to an `<audio>` element and play it — this is\r\n * how the caller is actually HEARD. Core cannot do this itself (it must not touch `document` —\r\n * SPEC §10.2); the UI layer or the host owns the audio element.\r\n */\r\n RemoteStream = 'remoteStream',\r\n}\r\n\r\nexport interface CallEventMap {\r\n [CallEvent.StateChanged]: { readonly state: CallState; readonly previous: CallState };\r\n [CallEvent.Accepted]: void;\r\n [CallEvent.Ended]: { readonly reason: HangupReason; readonly error?: SoftphoneError };\r\n [CallEvent.Muted]: { readonly muted: boolean };\r\n [CallEvent.Held]: { readonly originator: HoldOriginator };\r\n [CallEvent.Unheld]: { readonly originator: HoldOriginator };\r\n [CallEvent.Reconnecting]: void;\r\n [CallEvent.Reconnected]: void;\r\n [CallEvent.DigitsSent]: { readonly digits: string };\r\n [CallEvent.RemotePartyUpdated]: void;\r\n [CallEvent.RemoteStream]: { readonly stream: MediaStream };\r\n}\r\n\r\n/**\r\n * `on()` returns an unsubscribe function.\r\n *\r\n * Deliberate: it makes the cleanup impossible to get wrong in a React effect\r\n * (`useEffect(() => call.on(...), [call])`) and it avoids Vonage's design, where `on()` returns an\r\n * opaque `symbol` you must retain in order to ever call `off()`.\r\n */\r\nexport interface TypedEmitter<M> {\r\n on<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void;\r\n once<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void;\r\n off<E extends keyof M>(event: E, listener: (payload: M[E]) => void): void;\r\n}\r\n","import type { CallEventMap, TypedEmitter } from './events.js';\r\nimport type { CallDirection, RemoteParty } from './types.js';\r\n\r\n/**\r\n * THE call state machine. Singular.\r\n *\r\n * Today there are three competing representations (SPEC §5.3):\r\n * - `callStateRef` — a ref, read synchronously in guards to dodge stale closures\r\n * - `status` — a useState that can lag a render behind it\r\n * - `consultStatus` — the attended-transfer leg, tracked separately\r\n * They can disagree. Collapsing them into one is the single biggest correctness win in this\r\n * refactor, and the consult leg simply becomes its own Call.\r\n *\r\n *\r\n * INBOUND OUTBOUND\r\n * ─────── ────────\r\n * ringing ──answer()──► answering dialing ──180/183──► early\r\n * │ │ │ │\r\n * │ ▼ └────────┬───────────┘\r\n * │ reject() ┌───────┐ │ 200 OK\r\n * └──────────────┐ │ ▼ ▼\r\n * │ │ active ◄─────────────────────\r\n * │ │ ▲ │ ▲ │\r\n * │ │ │ │ │ └──hold()/remote──► held ──unhold()──┐\r\n * │ │ │ │ └───────────────────────────────────────-┘\r\n * │ │ │ └──ICE fail──► reconnecting ──recovered──┐\r\n * │ │ └────────────────────────────────────────────┘\r\n * ▼ ▼ │\r\n * ┌──────────────▼────┐\r\n * │ ended │ TERMINAL. Reachable from every state.\r\n * └────────────────────┘\r\n *\r\n * NOT modelled here, deliberately:\r\n *\r\n * - **`rejecting`.** Today's engine has one, purely to swallow Zadarma's `return_timeout` retries\r\n * for 35 s after a decline (SPEC §3.4). That is a PROVIDER quirk — it belongs in\r\n * `providers/zadarma.ts` as `quirks.rejectGuardMs`, not in the shared state machine. A rejected\r\n * call is simply `ended`. Our own SIP infrastructure would not need it at all (SPEC §2.3).\r\n *\r\n * - **A pre-INVITE state.** A Call exists only once an INVITE has landed. The \"user answered\r\n * before the INVITE arrived\" case (SPEC §3.1) is an *intent* held on `Softphone`, above the\r\n * call — because there is no call yet to hold it. Same for a pre-INVITE reject, which registers\r\n * a UA specifically so it has something to send the 486 from.\r\n */\r\nexport type CallState =\r\n /** Inbound, INVITE received, not yet answered. */\r\n | 'ringing'\r\n /** Outbound, INVITE sent, no provisional response yet. */\r\n | 'dialing'\r\n /** Outbound, remote is ringing (180/183). May carry early media. */\r\n | 'early'\r\n /** Local answer in flight — 200 OK not yet confirmed. */\r\n | 'answering'\r\n /** Media flowing. */\r\n | 'active'\r\n /** Real SIP hold (`a=sendonly`), by either party. */\r\n | 'held'\r\n /** Media lost, ICE restart in flight. The call is NOT over. */\r\n | 'reconnecting'\r\n /** Terminal. */\r\n | 'ended';\r\n\r\nexport type HoldOriginator = 'local' | 'remote';\r\n\r\nexport type HangupReason =\r\n | 'local'\r\n | 'remote'\r\n | 'rejected'\r\n | 'busy'\r\n | 'no-answer'\r\n | 'transferred'\r\n | 'failed';\r\n\r\n/**\r\n * The legal transitions, as data.\r\n *\r\n * A table rather than scattered `if`s: it is testable in isolation, it is the thing a reviewer can\r\n * actually check against the diagram above, and an illegal transition becomes a caught bug instead\r\n * of a silently wrong UI.\r\n */\r\nexport const CALL_TRANSITIONS = {\r\n ringing: ['answering', 'ended'],\r\n dialing: ['early', 'active', 'ended'],\r\n early: ['active', 'ended'],\r\n answering: ['active', 'ended'],\r\n active: ['held', 'reconnecting', 'ended'],\r\n held: ['active', 'reconnecting', 'ended'],\r\n reconnecting: ['active', 'held', 'ended'],\r\n ended: [],\r\n} as const satisfies Readonly<Record<CallState, readonly CallState[]>>;\r\n\r\n/** The states a call can start in. Everything else is reached by transition. */\r\nexport const INITIAL_STATES = {\r\n inbound: 'ringing',\r\n outbound: 'dialing',\r\n} as const satisfies Readonly<Record<CallDirection, CallState>>;\r\n\r\nexport function canTransition(from: CallState, to: CallState): boolean {\r\n return (CALL_TRANSITIONS[from] as readonly CallState[]).includes(to);\r\n}\r\n\r\nexport function isTerminal(state: CallState): boolean {\r\n return CALL_TRANSITIONS[state].length === 0;\r\n}\r\n\r\n/**\r\n * A single call. Exists only while there is one — there is no `idle`.\r\n *\r\n * The attended-transfer consult leg is *also* a Call. That is what lets one state machine cover\r\n * both, instead of today's separate `consultStatus`.\r\n *\r\n * Implementation lands in S2 (against MockProvider) and S3–S4 (against JsSIP/Zadarma).\r\n */\r\nexport interface Call extends TypedEmitter<CallEventMap> {\r\n readonly id: string;\r\n readonly direction: CallDirection;\r\n readonly state: CallState;\r\n readonly remote: RemoteParty;\r\n /** Wall-clock start of media. Null until `active`. */\r\n readonly answeredAt: Date | null;\r\n\r\n readonly isMuted: boolean;\r\n readonly isOnHold: boolean;\r\n\r\n /**\r\n * The remote party's audio stream, once media is flowing (null before). Attach it to an\r\n * `<audio autoplay>` element to hear the call. Also delivered via `CallEvent.RemoteStream`.\r\n */\r\n readonly remoteStream: MediaStream | null;\r\n\r\n /** Only legal from `ringing`. Throws `call/not-answerable` otherwise. */\r\n answer(): Promise<void>;\r\n /** Only legal from `ringing`. Sends 486 Busy Here. */\r\n reject(): Promise<void>;\r\n /** Legal from any non-terminal state. Idempotent. */\r\n hangup(): Promise<void>;\r\n\r\n mute(muted: boolean): void;\r\n\r\n /**\r\n * Real SIP hold — a re-INVITE with `a=sendonly`, so the PBX can play hold music.\r\n *\r\n * Today this is FAKED by muting (SPEC §5.6): the held caller hears dead silence and cannot tell\r\n * hold from a dropped call. JsSIP implements the real thing natively, so this is a handful of\r\n * lines rather than the SIP work it looks like.\r\n */\r\n hold(): Promise<void>;\r\n unhold(): Promise<void>;\r\n\r\n sendDigits(digits: string): void;\r\n\r\n /**\r\n * Blind transfer (SIP REFER). Ends this call — but **whether the transfer itself succeeded or\r\n * failed is unknowable from here**: against Zadarma, a REFER's outcome is not reported back to the\r\n * browser at all (confirmed on real calls — `docs/specs/transfer-availability.md` §2), so a REFER to\r\n * an unreachable target ends this call exactly the same way a successful one does. Prefer\r\n * `startConsult`/`completeTransfer` (below) when the caller must not be put at risk — that path\r\n * correctly reports busy/no-answer/failed because the consult leg is an ordinary outbound call, not\r\n * a REFER.\r\n */\r\n transfer(target: string): Promise<void>;\r\n\r\n /**\r\n * Attended transfer, part 1: call the target while holding this call.\r\n *\r\n * Returns the consult leg — itself a Call, with its own state machine and events. Unlike blind\r\n * `transfer()`, a consult leg that goes unanswered reports a real reason (busy/no-answer/rejected/\r\n * failed) and never puts this call at risk — see `docs/specs/transfer-availability.md` §5.\r\n */\r\n startConsult(target: string): Promise<Call>;\r\n /**\r\n * Attended transfer, part 2: bridge the two, ending both. (REFER … Replaces)\r\n *\r\n * **Confirmed non-functional against Zadarma, on real calls, three times\r\n * (`docs/specs/transfer-availability.md` §12.3).** Zadarma accepts the REFER…Replaces but never\r\n * actually bridges the two legs — the consult leg is silently orphaned and the caller is lost, with\r\n * no error thrown here to signal it. The pattern that DOES work (the same one `/ui`'s own\r\n * `ConsultScreen` uses): hang up the consult leg, then call `transfer(target)` on THIS call, to the\r\n * same already-confirmed-answering target — a blind transfer to a target already known to be there\r\n * works, even though `completeTransfer()` itself does not. Left in the type for hosts against a\r\n * different (non-Zadarma) SIP provider where REFER…Replaces may work as specified; do not reach for\r\n * it against Zadarma.\r\n */\r\n completeTransfer(): Promise<void>;\r\n /** Abandon the consult and resume this call. */\r\n cancelConsult(): Promise<void>;\r\n}\r\n","import { Emitter } from './emitter.js';\r\nimport { CallEvent, type CallEventMap } from './events.js';\r\nimport {\r\n INITIAL_STATES,\r\n canTransition,\r\n isTerminal,\r\n type Call,\r\n type CallState,\r\n type HangupReason,\r\n} from './call.js';\r\nimport type { EngineCall, EngineCallEvent } from './engine.js';\r\nimport type { Contact, RemoteParty } from './types.js';\r\n\r\n/**\r\n * The concrete call. Owns the state machine (SPEC §5.3) and the public event stream.\r\n *\r\n * It never talks to SIP or the provider — it talks to an `EngineCall`, translating that engine's\r\n * RAW signals into public state transitions in one place. The mock's scripted signals and the real\r\n * engine's provider-confirmed ones travel the identical path, which is what keeps the playground\r\n * honest against production.\r\n */\r\nexport class CallImpl extends Emitter<CallEventMap> implements Call {\r\n #state: CallState;\r\n #remote: RemoteParty;\r\n #answeredAt: Date | null = null;\r\n #muted = false;\r\n #onHold = false;\r\n #remoteStream: MediaStream | null = null;\r\n #consultEngineCall: EngineCall | null = null;\r\n #consultCall: Call | null = null;\r\n readonly #unsubscribe: () => void;\r\n\r\n readonly #onListenerError: ((event: keyof CallEventMap, error: unknown) => void) | undefined;\r\n\r\n constructor(\r\n private readonly engineCall: EngineCall,\r\n onListenerError?: (event: keyof CallEventMap, error: unknown) => void,\r\n private readonly onInternalError?: (message: string) => void,\r\n /**\r\n * Fired after `transfer()` resolves, with the target and the ORIGINAL caller's number — an\r\n * internal seam for the softphone's transfer-outcome watch (Phase A,\r\n * `docs/specs/transfer-outcome-relay.md`), never part of the public `Call` interface. Not\r\n * consulted for `startConsult`/`completeTransfer`: those legs already have their own real\r\n * `HangupReason`s (SPEC §5.6) and need no external polling to know what happened to them.\r\n */\r\n private readonly onTransfer?: (target: string, callerNumber: string | null) => void,\r\n ) {\r\n super(onListenerError);\r\n this.#onListenerError = onListenerError;\r\n this.#state = INITIAL_STATES[engineCall.direction];\r\n this.#remote = {\r\n number: engineCall.remoteNumber,\r\n displayName: engineCall.remoteNumber,\r\n contact: null,\r\n };\r\n this.#unsubscribe = engineCall.onEvent((event) => this.#onEngineEvent(event));\r\n }\r\n\r\n get id(): string {\r\n return this.engineCall.id;\r\n }\r\n get direction() {\r\n return this.engineCall.direction;\r\n }\r\n get state(): CallState {\r\n return this.#state;\r\n }\r\n get remote(): RemoteParty {\r\n return this.#remote;\r\n }\r\n get answeredAt(): Date | null {\r\n return this.#answeredAt;\r\n }\r\n get isMuted(): boolean {\r\n return this.#muted;\r\n }\r\n get isOnHold(): boolean {\r\n return this.#onHold;\r\n }\r\n get remoteStream(): MediaStream | null {\r\n return this.#remoteStream;\r\n }\r\n\r\n // ── Raw engine signals → state machine ──────────────────────────────────────\r\n #onEngineEvent(event: EngineCallEvent): void {\r\n switch (event.type) {\r\n case 'progress':\r\n this.#transition('early');\r\n break;\r\n case 'accepted':\r\n if (this.#answeredAt === null) this.#answeredAt = this.#now();\r\n this.#transition('active');\r\n this.emit(CallEvent.Accepted, undefined);\r\n break;\r\n case 'held':\r\n this.#onHold = true;\r\n if (this.#transition('held')) this.emit(CallEvent.Held, { originator: event.originator });\r\n break;\r\n case 'unheld':\r\n this.#onHold = false;\r\n if (this.#transition('active')) this.emit(CallEvent.Unheld, { originator: event.originator });\r\n break;\r\n case 'reconnecting':\r\n if (this.#transition('reconnecting')) this.emit(CallEvent.Reconnecting, undefined);\r\n break;\r\n case 'reconnected':\r\n if (this.#transition(this.#onHold ? 'held' : 'active'))\r\n this.emit(CallEvent.Reconnected, undefined);\r\n break;\r\n case 'ended':\r\n this.#end(event.reason);\r\n break;\r\n case 'stream':\r\n // Expose the remote audio so the host can play it. Core must not create an <audio> element\r\n // itself (SPEC §10.2) — it hands the stream out and the UI/host attaches it.\r\n this.#remoteStream = event.stream;\r\n this.emit(CallEvent.RemoteStream, { stream: event.stream });\r\n break;\r\n }\r\n }\r\n\r\n // ── Commands ────────────────────────────────────────────────────────────────\r\n async answer(): Promise<void> {\r\n if (this.#state !== 'ringing') {\r\n throw this.#stateError('answer', 'call/not-answerable');\r\n }\r\n // Move to `answering` BEFORE awaiting the engine, so a fast provider \"answered\" signal cannot\r\n // race past the ringing state (SPEC §3.1). The engine confirms with an `accepted` event, which\r\n // is what actually reaches `active`.\r\n this.#transition('answering');\r\n await this.engineCall.answer();\r\n }\r\n\r\n async reject(): Promise<void> {\r\n if (this.#state !== 'ringing') {\r\n throw this.#stateError('reject', 'call/not-answerable');\r\n }\r\n await this.engineCall.reject();\r\n // The engine emits `ended` with reason 'rejected' — no local transition here, so there is one\r\n // path to `ended`.\r\n }\r\n\r\n async hangup(): Promise<void> {\r\n if (isTerminal(this.#state)) return; // idempotent\r\n // A live consult leg (SPEC §5.6) is a REAL second call to the transfer target — hanging up on the\r\n // caller without also ending it would leave that extension ringing forever, orphaned, with nothing\r\n // left holding a reference to it once this call reaches `ended`. Ending it HERE (rather than only\r\n // relying on `#end()`'s own safety net below) fires both hangups in parallel instead of waiting for\r\n // this call's own round trip to complete first — `#end()` still needs its copy of this for every\r\n // OTHER way the main call can end (the caller hanging up first, an engine failure, anything that\r\n // never calls this method at all).\r\n const consult = this.#consultCall;\r\n this.#consultCall = null;\r\n this.#consultEngineCall = null;\r\n await Promise.all([consult?.hangup() ?? Promise.resolve(), this.engineCall.hangup()]);\r\n }\r\n\r\n mute(muted: boolean): void {\r\n if (this.#muted === muted) return;\r\n this.#muted = muted;\r\n this.engineCall.mute(muted);\r\n this.emit(CallEvent.Muted, { muted });\r\n }\r\n\r\n async hold(): Promise<void> {\r\n if (this.#onHold || (this.#state !== 'active' && this.#state !== 'reconnecting')) return;\r\n await this.engineCall.hold();\r\n // The engine confirms with a `held` event (originator: 'local').\r\n }\r\n\r\n async unhold(): Promise<void> {\r\n if (!this.#onHold) return;\r\n await this.engineCall.unhold();\r\n }\r\n\r\n sendDigits(digits: string): void {\r\n this.engineCall.sendDigits(digits);\r\n this.emit(CallEvent.DigitsSent, { digits });\r\n }\r\n\r\n async transfer(target: string): Promise<void> {\r\n await this.engineCall.transfer(target);\r\n // Blind transfer ends this call; the engine emits `ended` with reason 'transferred'.\r\n this.onTransfer?.(target, this.#remote.number.trim() || null);\r\n }\r\n\r\n async startConsult(target: string): Promise<Call> {\r\n // Put the caller on REAL hold (a=sendonly → the PBX plays hold music) before dialling the\r\n // consult target — otherwise they hear the agent's consult conversation. The legacy softphone\r\n // FAKED this by muting, so the caller heard dead silence (SPEC §5.6); real hold is the fix.\r\n await this.hold();\r\n\r\n const consultEngineCall = await this.engineCall.startConsult(target);\r\n // The consult leg is a first-class Call with its own state machine — this is what replaces\r\n // today's separate `consultStatus` (SPEC §5.3). We keep the underlying engine handle so\r\n // completeTransfer can bridge the two legs (REFER…Replaces).\r\n this.#consultEngineCall = consultEngineCall;\r\n const consult = new CallImpl(consultEngineCall, this.#onListenerError, this.onInternalError);\r\n this.#consultCall = consult;\r\n return consult;\r\n }\r\n\r\n async completeTransfer(): Promise<void> {\r\n if (!this.#consultEngineCall) throw this.#stateError('completeTransfer', 'call/unsupported');\r\n // The bridge (REFER…Replaces) ends both legs; the caller leaves hold by being connected to the\r\n // target, so there is nothing to unhold — the engine emits `ended` on each.\r\n await this.engineCall.completeTransfer(this.#consultEngineCall);\r\n }\r\n\r\n async cancelConsult(): Promise<void> {\r\n if (!this.#consultCall) return;\r\n await this.#consultCall.hangup();\r\n this.#consultCall = null;\r\n this.#consultEngineCall = null;\r\n // Take the caller off hold — the agent is coming back to them.\r\n await this.unhold();\r\n }\r\n\r\n // ── Internals ───────────────────────────────────────────────────────────────\r\n #transition(to: CallState): boolean {\r\n const from = this.#state;\r\n if (from === to) return false;\r\n if (isTerminal(from)) return false; // never leave `ended`\r\n if (!canTransition(from, to)) {\r\n this.onInternalError?.(`illegal call transition ${from} → ${to} (SPEC §5.3)`);\r\n return false;\r\n }\r\n this.#state = to;\r\n this.emit(CallEvent.StateChanged, { state: to, previous: from });\r\n return true;\r\n }\r\n\r\n #end(reason: HangupReason): void {\r\n if (isTerminal(this.#state)) return;\r\n // Safety net for every path that ends THIS call without going through `hangup()` above — the\r\n // caller hanging up on their own while a consult is in progress, an engine failure, anything the\r\n // provider reports as `ended`. Found live (`docs/specs/transfer-availability.md`): without this,\r\n // only an agent-initiated hangup ever cleaned up the consult leg, so the caller hanging up first\r\n // left the transfer target on a real, ringing-or-answered call with nobody left holding a\r\n // reference to it — the exact \"ghost call\" this method's sibling was written to prevent, just\r\n // reachable from a different direction. `hangup()`'s own copy still runs first when it applies —\r\n // by the time its `engineCall.hangup()` round-trips back here, `#consultCall` is already `null`,\r\n // so this is a no-op in that case, not a double-hangup.\r\n if (this.#consultCall) {\r\n const consult = this.#consultCall;\r\n this.#consultCall = null;\r\n this.#consultEngineCall = null;\r\n void consult.hangup();\r\n }\r\n const from = this.#state;\r\n this.#state = 'ended';\r\n this.emit(CallEvent.StateChanged, { state: 'ended', previous: from });\r\n this.emit(CallEvent.Ended, { reason });\r\n this.#unsubscribe();\r\n this.clear();\r\n }\r\n\r\n #stateError(op: string, _code: 'call/not-answerable' | 'call/unsupported'): Error {\r\n // A real SoftphoneError is minted in S2's errors implementation; a plain Error keeps the type\r\n // surface honest until then.\r\n return new Error(`${op}() is not valid while call is '${this.#state}'`);\r\n }\r\n\r\n #now(): Date {\r\n // Isolated so tests can stub it, and so we never reach for a bare `new Date()` in logic.\r\n return new Date();\r\n }\r\n\r\n /** @internal — the Softphone updates caller identity once `resolveContact` resolves. */\r\n setRemoteContact(contact: Contact): void {\r\n this.#remote = {\r\n number: this.#remote.number,\r\n displayName: contact.name ?? this.#remote.number,\r\n contact,\r\n };\r\n this.emit(CallEvent.RemotePartyUpdated, undefined);\r\n }\r\n}\r\n","import type {\r\n AgentsDirectory,\r\n Contact,\r\n ContactsSource,\r\n CoventiaBackendConfig,\r\n HistorySource,\r\n PresenceSource,\r\n TokenSource,\r\n TransferOutcomesPage,\r\n} from './types.js';\r\n\r\n/** Contacts, history, caller ID, the agents directory, transfer outcomes and presence, all from one\r\n * config. */\r\nexport interface BackendSources {\r\n readonly contacts: ContactsSource;\r\n readonly history: HistorySource;\r\n readonly resolveContact: (number: string) => Promise<Contact | null>;\r\n /** The Historial tab's \"filter by person\" directory. Always Coventia's own org membership. */\r\n readonly listAgents: () => Promise<AgentsDirectory>;\r\n /** Phase A (transfer outcome relay) — polled by the transfer watch, never by UI directly. */\r\n readonly transferOutcomes: (params: { readonly since?: string }) => Promise<TransferOutcomesPage>;\r\n /** Phase B (presence-gated transfer picker) — opened by `presence-socket.ts`, never by UI directly. */\r\n readonly presence: PresenceSource;\r\n}\r\n\r\n/**\r\n * Coventia's phone service, wired from `SoftphoneConfig.coventia`.\r\n *\r\n * **Synchronous object, lazily-loaded module.** The widget needs to know *at first render* whether\r\n * the Contactos and Historial tabs exist, and that is answered by the config alone — so this returns\r\n * immediately, while the ~2 kB adapter is fetched on first use. Core's own entry therefore carries no\r\n * HTTP and no `wa_id`, the same trick used for `jssip`, the flag SVGs and the phone-number metadata.\r\n *\r\n * One instance per softphone: the widget reads it back off `softphone.sources`, so it never builds a\r\n * second set with its own name cache.\r\n */\r\nexport function coventiaBackend(\r\n config: CoventiaBackendConfig,\r\n tokenSource: TokenSource,\r\n /** `SoftphoneConfig.apiBase`, consulted only when `config.baseUrl` is not set. */\r\n fallbackBaseUrl?: string,\r\n): BackendSources {\r\n // Resolved and validated EAGERLY, at `createSoftphone()` time, not deferred into `load()` below —\r\n // a host who configured `coventia` clearly wants contacts/history/caller ID, so a missing base URL\r\n // is a real misconfiguration, and the worst place to surface one is the first time a user opens\r\n // the Contactos tab, long after the phone has been happily reporting itself online.\r\n const baseUrl = config.baseUrl ?? fallbackBaseUrl;\r\n if (!baseUrl) {\r\n throw new Error(\r\n 'No base URL for the Coventia backend. Pass `coventia.baseUrl`, or a top-level `apiBase` on ' +\r\n '`createSoftphone` for it to fall back to.',\r\n );\r\n }\r\n\r\n let pending: Promise<BackendSources> | null = null;\r\n const load = (): Promise<BackendSources> => {\r\n pending ??= import('../coventia/sources.js').then((m) =>\r\n m.coventiaSources({ ...config, baseUrl, tokenSource }),\r\n );\r\n return pending;\r\n };\r\n\r\n return {\r\n contacts: async (params) => (await load()).contacts(params),\r\n history: async (params) => (await load()).history(params),\r\n resolveContact: async (number) => (await load()).resolveContact(number),\r\n listAgents: async () => (await load()).listAgents(),\r\n transferOutcomes: async (params) => (await load()).transferOutcomes(params),\r\n presence: async (callbacks) => (await load()).presence(callbacks),\r\n };\r\n}\r\n","import { Emitter } from './emitter.js';\r\nimport { CallImpl } from './call-impl.js';\r\nimport { coventiaBackend, type BackendSources } from './backend.js';\r\nimport { cachedTokenSource } from './token-cache.js';\r\nimport { CallEvent, SoftphoneEvent, type SoftphoneEventMap } from './events.js';\r\nimport { isBrowser } from './env.js';\r\nimport { webLocksTabLock, type TabLock } from './tabs.js';\r\nimport { startTransferWatch } from './transfer-watch.js';\r\nimport { startPresenceSocket, type PresenceWatch } from './presence-socket.js';\r\nimport type { AudioDevices, Softphone } from './softphone.js';\r\nimport type { CallEngine, EngineCall } from './engine.js';\r\nimport type { Call } from './call.js';\r\nimport { isFatal, type FatalSoftphoneError, type SoftphoneError } from './errors.js';\r\nimport type { CallOptions, PresenceEntry, SoftphoneConfig, SoftphoneState } from './types.js';\r\n\r\n/**\r\n * A page may hold ONE live softphone. A softphone owns singleton browser resources — the microphone,\r\n * a registration, an RTCPeerConnection — and a second instance means a second `getUserMedia` and two\r\n * registrations fighting over one extension. Daily throws on a second call object for the same\r\n * reason; we follow it.\r\n *\r\n * Module-scope, but not a browser global, so it is SSR-safe (SPEC §10.2): it is a plain boolean that\r\n * flips in `start()`, never at import.\r\n */\r\nlet liveInstance = false;\r\n\r\n/**\r\n * The real device surface (SPEC §7 Phase 2). Live `enumerateDevices()`, reactive to the browser's own\r\n * `devicechange`, input switching delegated to the engine (`CallEngine.setInputDevice` — the SIP\r\n * engine implements it; a mock with nothing real to swap simply omits it), output switching entirely\r\n * local (there is no engine involvement at all — see `AudioDevices.setOutput`'s own comment: routing\r\n * already-decoded audio needs no SIP/WebRTC layer underneath it).\r\n *\r\n * SSR-safe like everything else under `core/` (SPEC §10.2): every browser global is read lazily —\r\n * never at module scope — and gated behind `isBrowser()`.\r\n */\r\nclass BrowserAudioDevices implements AudioDevices {\r\n inputs: readonly MediaDeviceInfo[] = [];\r\n outputs: readonly MediaDeviceInfo[] = [];\r\n currentInputId: string | null = null;\r\n currentOutputId: string | null = null;\r\n readonly canSelectOutput: boolean;\r\n\r\n readonly #engine: CallEngine;\r\n readonly #onChange: () => void;\r\n\r\n constructor(engine: CallEngine, onChange: () => void) {\r\n this.#engine = engine;\r\n this.#onChange = onChange;\r\n this.canSelectOutput =\r\n isBrowser() &&\r\n typeof HTMLMediaElement !== 'undefined' &&\r\n 'setSinkId' in HTMLMediaElement.prototype;\r\n\r\n if (isBrowser() && navigator.mediaDevices) {\r\n void this.refresh();\r\n // No teardown: `mediaDevices` is the FRAME's own object, not a resource this instance opened,\r\n // and this instance lives exactly as long as the Softphone does — there is nothing to leak.\r\n navigator.mediaDevices.addEventListener?.('devicechange', () => void this.refresh());\r\n }\r\n }\r\n\r\n async refresh(): Promise<void> {\r\n if (!isBrowser() || typeof navigator.mediaDevices?.enumerateDevices !== 'function') return;\r\n let all: readonly MediaDeviceInfo[];\r\n try {\r\n all = await navigator.mediaDevices.enumerateDevices();\r\n } catch {\r\n // Rare, but real — a locked-down permissions policy or an odd polyfill can reject this. The\r\n // lists simply stay whatever they were; there is no better fallback than \"unchanged.\"\r\n return;\r\n }\r\n this.inputs = all.filter((d) => d.kind === 'audioinput');\r\n this.outputs = all.filter((d) => d.kind === 'audiooutput');\r\n // A selection pointing at a device that just vanished (unplugged) must not keep claiming it is\r\n // still selected — the UI would show a \"chosen\" option that no longer exists in either list.\r\n if (this.currentInputId && !this.inputs.some((d) => d.deviceId === this.currentInputId)) {\r\n this.currentInputId = null;\r\n }\r\n if (this.currentOutputId && !this.outputs.some((d) => d.deviceId === this.currentOutputId)) {\r\n this.currentOutputId = null;\r\n }\r\n this.#onChange();\r\n }\r\n\r\n async setInput(deviceId: string | null): Promise<void> {\r\n this.currentInputId = deviceId;\r\n this.#onChange();\r\n // Optional on the interface — a mock engine with no real media has nothing to swap and simply\r\n // does not implement it.\r\n await this.#engine.setInputDevice?.(deviceId);\r\n }\r\n\r\n async setOutput(deviceId: string | null): Promise<void> {\r\n this.currentOutputId = deviceId;\r\n this.#onChange();\r\n // Nothing to await here: applying it is the UI layer's job (`useCallAudio`'s `setSinkId` on the\r\n // remote-audio element), reached only through `SoftphoneEvent.DeviceChange` — core/ cannot touch\r\n // `document` itself (SPEC §10.2).\r\n }\r\n\r\n async setRingtoneOutput(): Promise<void> {\r\n /* separate ringtone output lands in a later pass — still Phase 2 groundwork */\r\n }\r\n}\r\n\r\nexport class SoftphoneImpl extends Emitter<SoftphoneEventMap> implements Softphone {\r\n #state: SoftphoneState = 'stopped';\r\n #lastError: FatalSoftphoneError | null = null;\r\n #activeCall: CallImpl | null = null;\r\n #destroyed = false;\r\n #startedHere = false;\r\n #isLeader = false;\r\n readonly #engine: CallEngine;\r\n readonly #tabLock: TabLock;\r\n readonly #resolveContact: SoftphoneConfig['resolveContact'];\r\n /** Contacts + history, whenever there is a Coventia backend to reach. The widget reads these. */\r\n readonly sources: BackendSources | null;\r\n readonly audio: AudioDevices;\r\n /**\r\n * Every in-flight transfer-outcome watch (Phase A), so `destroy()` can stop all of them. Never\r\n * pruned as individual watches finish on their own — `stop()` is idempotent and cheap to call again,\r\n * and a session accumulating a handful of closures for however many transfers it makes is not worth\r\n * the bookkeeping a self-removing set would add.\r\n */\r\n readonly #activeWatches = new Set<() => void>();\r\n /**\r\n * Phase B (presence-gated transfer picker). ONE long-lived watch, not a member of `#activeWatches`\r\n * — that set is per-transfer instances a live call spawns and forgets; this is a single background\r\n * connection tied to the softphone's own lifecycle, started and stopped by `#syncPresenceWatch`\r\n * rather than by any one call.\r\n */\r\n #presenceWatch: PresenceWatch | null = null;\r\n #presenceAgents: readonly PresenceEntry[] = [];\r\n\r\n /**\r\n * `tabLock` defaults to the real Web Locks implementation — not part of `SoftphoneConfig` (an\r\n * internal seam, not something an integrator tunes). Tests construct this class directly with a\r\n * fake for deterministic control; `createSoftphone()` never passes a third argument, so every\r\n * existing test that goes through it is unaffected (`multi-tab-leader-election.md` §3.8).\r\n */\r\n constructor(engine: CallEngine, config: SoftphoneConfig, tabLock: TabLock = webLocksTabLock()) {\r\n super();\r\n this.#engine = engine;\r\n this.#tabLock = tabLock;\r\n // Built here, not as a field initializer — it needs `this.#engine` (set the line above) and\r\n // `this.emit` (inherited from `Emitter`, not available until after `super()`), so it cannot be a\r\n // default value evaluated before either exists.\r\n this.audio = new BrowserAudioDevices(this.#engine, () =>\r\n this.emit(SoftphoneEvent.DeviceChange, undefined),\r\n );\r\n // ONE set of sources, built here so the widget can read them back instead of constructing a\r\n // second set from identical config (two name caches, two of every request).\r\n //\r\n // `apiBase` ALONE is enough to build these — `coventia` is for OVERRIDING something (your own\r\n // contacts function, an `organizationId`, a `baseUrl` that differs from the engine's), never for\r\n // switching the feature on. It used to be the switch: omitting `coventia` meant no Contactos tab\r\n // even with `apiBase` set for calling, which is the wrong default for OUR OWN backend's own\r\n // contacts/history/caller ID — a host would have to know to add an otherwise-empty `coventia: {}`\r\n // to get something this SDK should simply give them.\r\n //\r\n // One token for every request the sources make, instead of one per request — see\r\n // `cachedTokenSource`. Idempotent, so wrapping a source the engine already wrapped is free and\r\n // they share the same entry.\r\n this.sources =\r\n config.coventia || config.apiBase\r\n ? coventiaBackend(config.coventia ?? {}, cachedTokenSource(config.tokenSource), config.apiBase)\r\n : null;\r\n this.#resolveContact = config.resolveContact ?? this.sources?.resolveContact;\r\n this.#engine.onIncoming((engineCall) => this.#onIncoming(engineCall));\r\n this.#engine.onStateChange((state) => this.#setState(state));\r\n // Optional on the interface — an engine that never reports failures (the mock) simply has none.\r\n this.#engine.onError?.((error) => this.#onError(error));\r\n }\r\n\r\n get state(): SoftphoneState {\r\n return this.#state;\r\n }\r\n get lastError(): FatalSoftphoneError | null {\r\n return this.#lastError;\r\n }\r\n get isLeader(): boolean {\r\n return this.#isLeader;\r\n }\r\n get activeCall(): Call | null {\r\n return this.#activeCall;\r\n }\r\n get presence(): readonly PresenceEntry[] {\r\n return this.#presenceAgents;\r\n }\r\n get canPlayAudio(): boolean {\r\n // The autoplay-recovery machinery is Phase 2; the mock has no audio to block.\r\n return true;\r\n }\r\n async startAudio(): Promise<void> {\r\n /* autoplay recovery lands in Phase 2 */\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.#assertUsable();\r\n if (this.#startedHere) return;\r\n if (liveInstance) {\r\n throw new Error(\r\n 'A Softphone is already running on this page. Construct one and share it; call destroy() before starting another. (SPEC §5.3 / error engine/duplicate-instance)',\r\n );\r\n }\r\n liveInstance = true;\r\n this.#startedHere = true;\r\n // Set BEFORE acquiring leadership, not after: `'starting'` means \"start() was called and is in\r\n // progress\", which includes waiting behind another tab as a real sub-phase of starting up — not\r\n // only the part after leadership is granted. This is what lets a host tell \"never started\" apart\r\n // from \"started, currently a follower\" (both otherwise look identical: `isLeader === false`,\r\n // and — had this stayed `'stopped'` here — `state === 'stopped'` too). The widget's `LeaderGate`\r\n // depends on this distinction (`multi-tab-leader-election.md` §3.4, §4).\r\n this.#setState('starting');\r\n\r\n // Resolves now for a single open tab (the common case — imperceptibly fast, nothing else holds\r\n // the lock), or later, whenever another tab releases it. See `multi-tab-leader-election.md` §3.4\r\n // for why this is NOT split into an immediate/deferred branch: nothing today awaits `start()`\r\n // closely enough to need one, and stop()/destroy() called while this is still pending (a\r\n // follower torn down before ever leading) simply leaves it permanently unresolved — see stop().\r\n await this.#tabLock.acquireLeadership();\r\n if (this.#destroyed) return; // torn down while waiting to lead — do not start the engine now\r\n this.#setLeader(true);\r\n await this.#engine.start();\r\n }\r\n\r\n async stop(): Promise<void> {\r\n if (!this.#startedHere) return;\r\n this.#tabLock.release();\r\n if (this.#isLeader) {\r\n await this.#engine.stop();\r\n if (this.#activeCall) await this.#activeCall.hangup().catch(() => {});\r\n }\r\n this.#setLeader(false);\r\n this.#releaseInstance();\r\n this.#setState('stopped');\r\n }\r\n\r\n async destroy(): Promise<void> {\r\n if (this.#destroyed) return;\r\n this.#destroyed = true;\r\n // Every timer this softphone owns beyond the engine's own — stopped first and unconditionally,\r\n // independent of #engine/#tabLock state below.\r\n for (const stopWatch of this.#activeWatches) stopWatch();\r\n this.#activeWatches.clear();\r\n this.#presenceWatch?.stop();\r\n this.#presenceWatch = null;\r\n await this.stop().catch(() => {});\r\n this.#setState('destroyed');\r\n this.clear();\r\n }\r\n\r\n async call(destination: string, options?: CallOptions): Promise<Call> {\r\n this.#assertUsable();\r\n if (this.#state !== 'online' && this.#state !== 'offline' && this.#state !== 'starting') {\r\n throw new Error(`Cannot place a call while softphone is '${this.#state}'.`);\r\n }\r\n if (this.#activeCall) {\r\n throw new Error('A call is already in progress. Hang up before placing another.');\r\n }\r\n const engineCall = await this.#engine.placeCall(destination, options);\r\n return this.#adopt(engineCall);\r\n }\r\n\r\n // ── internals ───────────────────────────────────────────────────────────────\r\n #onIncoming(engineCall: EngineCall): void {\r\n if (this.#activeCall) {\r\n // Busy: the engine is responsible for the 486. Nothing to surface.\r\n void engineCall.reject().catch(() => {});\r\n return;\r\n }\r\n const call = this.#adopt(engineCall);\r\n this.emit(SoftphoneEvent.IncomingCall, call);\r\n }\r\n\r\n #adopt(engineCall: EngineCall): CallImpl {\r\n const call = new CallImpl(\r\n engineCall,\r\n (event, error) => this.#reportListenerError('call', event, error),\r\n (message) => this.#reportInternalError(message),\r\n (target, callerNumber) => this.#onTransfer(target, callerNumber),\r\n );\r\n this.#activeCall = call;\r\n\r\n // Clear the slot when the call ends, so the next call can begin.\r\n const off = call.on(CallEvent.Ended, () => {\r\n off();\r\n if (this.#activeCall === call) {\r\n this.#activeCall = null;\r\n this.emit(SoftphoneEvent.ActiveCallChanged, { call: null });\r\n }\r\n });\r\n\r\n // Resolve the caller's name from the host's contacts and update the call in place. The provider's\r\n // SIP display name is the line label, not the caller (SPEC §4), so a resolved contact wins.\r\n if (this.#resolveContact) {\r\n void this.#resolveContact(engineCall.remoteNumber)\r\n .then((contact) => {\r\n if (contact && this.#activeCall === call) call.setRemoteContact(contact);\r\n })\r\n .catch(() => {});\r\n }\r\n\r\n // Fires for both inbound and outbound (both reach here) — what a UI's useActiveCall binds to.\r\n this.emit(SoftphoneEvent.ActiveCallChanged, { call });\r\n return call;\r\n }\r\n\r\n #setState(state: SoftphoneState): void {\r\n if (state === this.#state) return;\r\n const previous = this.#state;\r\n this.#state = state;\r\n /*\r\n * Reaching `online` is the only evidence we get that a fatal condition has passed, so it is what\r\n * clears it. Deliberately not a timer and not user input: neither is evidence of anything, and a\r\n * cleared error over a phone that still cannot call is worse than no error at all.\r\n *\r\n * For Zadarma `online` means the push socket is up, which is weaker than \"everything works\" — a\r\n * token could still be refused on the next key fetch. That self-corrects: the fetch fails, the\r\n * error is re-emitted, `lastError` is set again. An explanation that briefly lifts and returns\r\n * beats one that never lifts and needs a reload.\r\n */\r\n if (state === 'online') this.#lastError = null;\r\n this.emit(SoftphoneEvent.StateChanged, { state, previous });\r\n this.#syncPresenceWatch();\r\n }\r\n\r\n #setLeader(isLeader: boolean): void {\r\n if (isLeader === this.#isLeader) return;\r\n this.#isLeader = isLeader;\r\n this.emit(SoftphoneEvent.LeaderChanged, { isLeader });\r\n this.#syncPresenceWatch();\r\n }\r\n\r\n /**\r\n * Phase B (presence-gated transfer picker, `docs/specs/transfer-presence-picker.md` plan T6/T7).\r\n * The ONE place that decides whether the presence connection should be running right now — called\r\n * after every `#setState`/`#setLeader` transition rather than left to either alone, since either\r\n * one flipping can change the answer. No-ops entirely when no Coventia backend is configured\r\n * (`sources` is null) — same guard `#onTransfer` uses, and for the same reason: nothing to open.\r\n *\r\n * `presence-socket.ts`'s own `getIsLeader` is checked at start and before each reconnect attempt,\r\n * not on a poll tick the way `startTransferWatch`'s is — see that module's own doc comment for why.\r\n * This still passes `getIsLeader: () => this.#isLeader` for that narrower, defensive recheck; the\r\n * BROAD online/leader gate — whether to be connected AT ALL — is owned entirely here, the same\r\n * split `#onTransfer` already has with `startTransferWatch`.\r\n */\r\n #syncPresenceWatch(): void {\r\n const shouldRun = Boolean(this.sources) && this.#state === 'online' && this.#isLeader;\r\n if (shouldRun === (this.#presenceWatch !== null)) return;\r\n\r\n if (shouldRun) {\r\n this.#presenceWatch = startPresenceSocket({\r\n source: this.sources!.presence,\r\n getIsLeader: () => this.#isLeader,\r\n onAgentsChanged: (agents) => {\r\n this.#presenceAgents = agents;\r\n this.emit(SoftphoneEvent.PresenceChanged, { agents });\r\n },\r\n });\r\n } else {\r\n this.#presenceWatch?.stop();\r\n this.#presenceWatch = null;\r\n // A stale list from the connection that just closed must not keep being read as current —\r\n // mirrors `lastError`'s own \"cleared only on real evidence\" rule, just for the opposite fact:\r\n // losing the connection IS evidence there is nothing live left to show.\r\n this.#presenceAgents = [];\r\n this.emit(SoftphoneEvent.PresenceChanged, { agents: [] });\r\n }\r\n }\r\n\r\n /**\r\n * Phase A (transfer outcome relay, `docs/specs/transfer-outcome-relay.md`) — `CallImpl.transfer()`'s\r\n * internal seam calls this. No-ops entirely when no Coventia backend is configured (`sources` is\r\n * null): there is then nothing to poll, and behaviour is exactly what it was before this feature\r\n * existed (spec §5.4).\r\n */\r\n #onTransfer(target: string, callerNumber: string | null): void {\r\n if (!this.sources) return;\r\n const stopWatch = startTransferWatch({\r\n target,\r\n callerNumber,\r\n source: this.sources.transferOutcomes,\r\n getIsLeader: () => this.#isLeader,\r\n emit: (payload) => this.emit(SoftphoneEvent.TransferOutcome, payload),\r\n });\r\n this.#activeWatches.add(stopWatch);\r\n }\r\n\r\n /**\r\n * Re-emit an engine failure to the host, and remember it if it is fatal.\r\n *\r\n * Every error is emitted; only fatal ones are retained. A non-fatal error is a fact about one call\r\n * — the remote party was busy — and keeping it as softphone state would make a working phone look\r\n * broken to anything that renders `lastError`.\r\n */\r\n #onError(error: SoftphoneError): void {\r\n if (isFatal(error)) this.#lastError = error;\r\n this.emit(SoftphoneEvent.Error, error);\r\n }\r\n\r\n #releaseInstance(): void {\r\n if (this.#startedHere) {\r\n liveInstance = false;\r\n this.#startedHere = false;\r\n }\r\n }\r\n\r\n #assertUsable(): void {\r\n if (this.#destroyed) {\r\n throw new Error('This Softphone has been destroyed. Construct a new one. (engine/destroyed)');\r\n }\r\n }\r\n\r\n #reportListenerError(source: string, event: string | number | symbol, error: unknown): void {\r\n // A throwing host listener must never take the engine down. Surface it, don't swallow it.\r\n console.error(`[softphone] a ${source} '${String(event)}' listener threw:`, error);\r\n }\r\n\r\n #reportInternalError(message: string): void {\r\n console.error(`[softphone] ${message}`);\r\n }\r\n}\r\n\r\n/** Test-only reset of the page-singleton guard, so suites don't leak the flag between cases. */\r\nexport function __resetInstanceGuardForTests(): void {\r\n liveInstance = false;\r\n}\r\n","/**\r\n * Browser-capability probes.\r\n *\r\n * SPEC §10.2: nothing under core/ or transports/ may touch `window`, `document` or `navigator` at\r\n * MODULE SCOPE. Importing the package must be safe on a server — a Next.js consumer renders on the\r\n * server first, and a bare `ReferenceError: window is not defined` at import time makes us unusable\r\n * there no matter how framework-agnostic the runtime API is.\r\n *\r\n * Every probe below is therefore a FUNCTION, evaluated lazily at call time.\r\n */\r\n\r\n/** True when running in a browser with a DOM. False under SSR, in Node, and in a worker. */\r\nexport function isBrowser(): boolean {\r\n return typeof window !== 'undefined' && typeof document !== 'undefined';\r\n}\r\n\r\n/**\r\n * True when the browser exposes the WebRTC APIs the softphone needs.\r\n *\r\n * Worth a distinct check rather than folding into isBrowser(): privacy extensions\r\n * (uBlock Origin Extra, WebRTC Leak Prevent) disable RTCPeerConnection outright, and the resulting\r\n * failure is otherwise reported as a confusing SIP error deep in a call attempt.\r\n */\r\nexport function isWebRtcSupported(): boolean {\r\n return (\r\n isBrowser() &&\r\n typeof RTCPeerConnection !== 'undefined' &&\r\n typeof navigator !== 'undefined' &&\r\n typeof navigator.mediaDevices?.getUserMedia === 'function'\r\n );\r\n}\r\n","import { isBrowser } from './env.js';\r\n\r\n/**\r\n * Cross-tab leader election (SPEC §5.2, §5.3; `multi-tab-leader-election.md`).\r\n *\r\n * Zadarma's on-demand registration means every open tab that reacts to the same push independently\r\n * registers a SIP UA for the same extension — a race the SDK never controlled, because nothing told\r\n * one tab another existed. Confirmed live: with two tabs open, an inbound call rings in exactly one,\r\n * unpredictably which. `TabLock` is the seam that lets exactly one tab (\"the leader\") ever start the\r\n * real engine; every other tab (\"a follower\") stays dormant until its turn comes.\r\n */\r\nexport interface TabLock {\r\n /**\r\n * Resolves once this tab becomes leader — immediately if no other tab currently leads (the common\r\n * case: a single open tab, or an environment without Web Locks), or later, whenever (if ever)\r\n * another tab releases it. Never rejects; a cancelled attempt (via `release()`) simply never\r\n * resolves.\r\n */\r\n acquireLeadership(): Promise<void>;\r\n\r\n /** Relinquish leadership if held, or cancel a still-pending attempt. Safe to call more than once. */\r\n release(): void;\r\n}\r\n\r\nconst LOCK_NAME = 'coventia-softphone:leader';\r\n\r\n/**\r\n * True when the browser exposes the Web Locks API this coordination depends on.\r\n *\r\n * Safari added support in 15.4 (2022); Chrome and Firefox both predate that comfortably. Where it's\r\n * missing, `webLocksTabLock()` grants leadership immediately to every tab — today's actual\r\n * (uncoordinated, but functional) behaviour, not a regression.\r\n */\r\nexport function isWebLocksSupported(): boolean {\r\n return isBrowser() && typeof navigator.locks?.request === 'function';\r\n}\r\n\r\n/**\r\n * The real implementation. Constructed lazily by a caller, never at module scope (SPEC §10.3) — safe\r\n * to import on a server, and an environment without Web Locks pays nothing beyond one property read.\r\n *\r\n * One `TabLock` is meant to be acquired at most once per `Softphone` lifetime, from `start()` — see\r\n * `multi-tab-leader-election.md` §3.4.\r\n */\r\nexport function webLocksTabLock(lockName: string = LOCK_NAME): TabLock {\r\n const controller = new AbortController();\r\n let releaseHold: (() => void) | undefined;\r\n\r\n return {\r\n acquireLeadership(): Promise<void> {\r\n if (!isWebLocksSupported()) return Promise.resolve();\r\n\r\n return new Promise<void>((resolveAcquired) => {\r\n navigator.locks\r\n .request(lockName, { signal: controller.signal }, () => {\r\n // Granted — tell the caller NOW. How long the browser keeps the lock is governed by the\r\n // SEPARATE promise below, resolved later by release(), not by this one.\r\n resolveAcquired();\r\n return new Promise<void>((resolveHold) => {\r\n releaseHold = resolveHold;\r\n });\r\n })\r\n .catch(() => {\r\n // Aborted by release() before our turn came — expected cancellation, not a failure.\r\n // `acquireLeadership()`'s promise simply never resolves in this case.\r\n });\r\n });\r\n },\r\n\r\n release(): void {\r\n if (releaseHold) {\r\n const resolve = releaseHold;\r\n releaseHold = undefined;\r\n resolve();\r\n } else {\r\n controller.abort();\r\n }\r\n },\r\n };\r\n}\r\n","import type { TransferOutcomeEvent, TransferOutcomeSource, TransferOutcomesPage } from './types.js';\r\n\r\n/**\r\n * Poll cadence (spec §5.4, plan T5): 2 s for the first 10 s — a real success answers within ~4 s\r\n * (spec §8, F5) — then 5 s until the deadline. The observed no-answer ring is ~61 s (spec §8, F4); 90 s\r\n * leaves headroom without polling indefinitely.\r\n */\r\nconst FAST_INTERVAL_MS = 2_000;\r\nconst FAST_WINDOW_MS = 10_000;\r\nconst SLOW_INTERVAL_MS = 5_000;\r\nconst DEADLINE_MS = 90_000;\r\n\r\n/**\r\n * Digits only, at most six — deliberately generous next to Zadarma's own 3-digit extensions (spec §7,\r\n * plan §2). A `+`-prefixed or longer number is an outside line: no `NOTIFY_INTERNAL` will ever name it,\r\n * so there is nothing to poll for and starting a watch would just waste requests until the deadline.\r\n */\r\nconst EXTENSION_LIKE = /^\\d{1,6}$/;\r\n\r\nexport interface TransferWatchParams {\r\n readonly target: string;\r\n /** The ORIGINAL caller's number, for the eventual `TransferOutcomeEvent.caller` — null if unknown. */\r\n readonly callerNumber: string | null;\r\n readonly source: TransferOutcomeSource;\r\n /** Re-checked on every poll, not just at the start — see the leadership-handover note below. */\r\n readonly getIsLeader: () => boolean;\r\n readonly emit: (payload: TransferOutcomeEvent) => void;\r\n /** Overridable for tests; default to the real timers/clock. */\r\n readonly setTimeout?: typeof globalThis.setTimeout;\r\n readonly clearTimeout?: typeof globalThis.clearTimeout;\r\n readonly now?: () => number;\r\n}\r\n\r\n/**\r\n * Starts polling for one transfer's outcome (spec §5.4, plan T5). Returns a `stop()` — called from\r\n * `destroy()`, and safe to call again or after the watch already finished on its own (idempotent).\r\n *\r\n * **Each call owns its own private polling loop.** Deliberately not a single shared, mutable watch the\r\n * softphone reuses: `Call.transfer()` ends the call it was called on, so an agent is free to place and\r\n * transfer a SECOND call before the first watch's 90 s window elapses — multiple watches must be able\r\n * to run at once without one clobbering another's cursor or state. `no cross-attribution` is what this\r\n * buys, for the case the plan's acceptance actually describes: CONSECUTIVE transfers to the same\r\n * target, one fully resolved before the next begins. (Two watches racing for the exact same target at\r\n * the exact same time is a narrower case this design does not additionally solve — see the matching\r\n * step below.)\r\n */\r\nexport function startTransferWatch(params: TransferWatchParams): () => void {\r\n const {\r\n target,\r\n callerNumber,\r\n source,\r\n getIsLeader,\r\n emit,\r\n setTimeout: scheduleTimeout = globalThis.setTimeout,\r\n clearTimeout: cancelTimeout = globalThis.clearTimeout,\r\n now = () => Date.now(),\r\n } = params;\r\n\r\n // Outside line — nothing will ever be reported for it (spec §7). Checked before anything else so a\r\n // typed phone number never starts a single request.\r\n if (!EXTENSION_LIKE.test(target)) return () => {};\r\n // A follower tab never actually holds the call that just transferred (LeaderGate never lets one\r\n // place or receive a call at all) — this should be unreachable in practice, but costs nothing to\r\n // state explicitly rather than rely on that being true by construction elsewhere.\r\n if (!getIsLeader()) return () => {};\r\n\r\n let stopped = false;\r\n let timer: ReturnType<typeof globalThis.setTimeout> | null = null;\r\n let cursor: string | null = null;\r\n const startedAt = now();\r\n\r\n const stop = (): void => {\r\n stopped = true;\r\n if (timer !== null) {\r\n cancelTimeout(timer);\r\n timer = null;\r\n }\r\n };\r\n\r\n const finish = (status: 'connected' | 'not-connected'): void => {\r\n stop();\r\n emit({ status, target, caller: callerNumber, at: new Date(now()) });\r\n };\r\n\r\n const scheduleNext = (): void => {\r\n if (stopped) return;\r\n const elapsed = now() - startedAt;\r\n if (elapsed >= DEADLINE_MS) {\r\n // Deadline — stop silently. Spec §5.4: \"a timeout is never reported as success or failure.\"\r\n stop();\r\n return;\r\n }\r\n timer = scheduleTimeout(() => void poll(), elapsed < FAST_WINDOW_MS ? FAST_INTERVAL_MS : SLOW_INTERVAL_MS);\r\n };\r\n\r\n const poll = async (): Promise<void> => {\r\n if (stopped) return;\r\n if (!getIsLeader()) {\r\n // Leadership changed mid-watch (a tab handover) — this tab no longer holds the call that\r\n // transferred. Stop rather than keep polling and risk surfacing a notice nowhere it belongs.\r\n stop();\r\n return;\r\n }\r\n\r\n let page: TransferOutcomesPage;\r\n try {\r\n page = await source(cursor === null ? {} : { since: cursor });\r\n } catch {\r\n // A failed poll never ends the watch and never reaches the user or the host (plan T5's own\r\n // acceptance) — the transfer itself didn't fail, only this one attempt to check on it. Retry on\r\n // the normal cadence rather than inventing a separate backoff the spec doesn't call for.\r\n if (!stopped) scheduleNext();\r\n return;\r\n }\r\n if (stopped) return; // stop() may have run while the request was in flight\r\n\r\n if (cursor === null) {\r\n // The FIRST poll exists only to learn the starting cursor (spec §5.3's no-mis-attribution rule)\r\n // — anything already in the inbox predates this transfer and is deliberately never inspected.\r\n cursor = page.cursor;\r\n } else {\r\n cursor = page.cursor;\r\n const match = page.outcomes.find((o) => o.target === target);\r\n if (match) {\r\n finish(match.status);\r\n return;\r\n }\r\n }\r\n\r\n scheduleNext();\r\n };\r\n\r\n void poll(); // fires immediately — no reason to wait 2 s just to learn the starting cursor\r\n return stop;\r\n}\r\n","import type { PresenceEntry, PresenceSource } from './types.js';\r\n\r\n/**\r\n * Reconnect backoff (plan T6, spec §4.4/§4.7): doubling from 1 s, capped at 30 s, jittered so a\r\n * Cloud Run-wide cut (spec §4.2 — every connection capped at 60 min) doesn't send every agent's\r\n * softphone back in at the same instant. Not configurable — nothing about a specific value is part\r\n * of any contract, only \"backoff and jitter\" is (spec §4.4).\r\n */\r\nconst RECONNECT_BASE_MS = 1_000;\r\nconst RECONNECT_MAX_MS = 30_000;\r\n\r\nexport interface StartPresenceSocketParams {\r\n readonly source: PresenceSource;\r\n /**\r\n * Checked before the FIRST connection attempt, and again before every reconnect attempt after a\r\n * drop — the same two decision points `transfer-watch.ts`'s own `getIsLeader` is checked at (\"at\r\n * the start\" and \"on every poll\"). A persistent connection has no poll tick to hang a recheck off,\r\n * so those two moments — first attempt, each retry — are this module's equivalent: the places it is\r\n * already about to do something, rather than an invented timer whose only job would be to ask.\r\n *\r\n * Today's `SoftphoneImpl` grants leadership once, in `start()`, and only ever revokes it in\r\n * `stop()`/`destroy()` (`tabs.ts` — \"acquired at most once per Softphone lifetime\") — so in\r\n * practice `startPresenceSocket` is only ever called once leadership already holds, and this never\r\n * flips back to false out from under a running connection. Checked anyway, for the same reason\r\n * `transfer-watch.ts` does: cheap to state explicitly, and the caller who wires this up (plan T7)\r\n * is expected to call `stop()` the moment leadership or online-ness is lost, same as `#onTransfer`\r\n * already does for `#activeWatches` — this is a second line of defence, not the primary one.\r\n */\r\n readonly getIsLeader: () => boolean;\r\n /**\r\n * Fires after `snapshot` (the list, wholesale) and after every applied `delta` (the list, already\r\n * merged) — never the raw wire message. A caller wanting \"just this one agent\" over-reads a single\r\n * field; wanting \"everyone right now\" is the only shape a picker (T7) ever actually needs.\r\n */\r\n readonly onAgentsChanged: (agents: readonly PresenceEntry[]) => void;\r\n /** Overridable for tests; default to the real timers. */\r\n readonly setTimeout?: typeof globalThis.setTimeout;\r\n readonly clearTimeout?: typeof globalThis.clearTimeout;\r\n /** Overridable for tests — deterministic jitter. Defaults to `Math.random`. */\r\n readonly random?: () => number;\r\n}\r\n\r\nexport interface PresenceWatch {\r\n /**\r\n * Report this softphone's own on-call-ness. Safe to call as often as the caller likes — a no-op\r\n * unless the value actually changed since the last report (spec §4.4: \"never on an interval\").\r\n * While disconnected, the latest value is remembered and replayed on the next successful connect —\r\n * a call that started while the socket happened to be reconnecting must not be lost.\r\n */\r\n readonly reportCallState: (onCall: boolean) => void;\r\n /** Stops for good — no further reconnect attempts. Idempotent, matching `startTransferWatch`'s own\r\n * `stop()` and `#activeWatches`'s cleanup convention. */\r\n readonly stop: () => void;\r\n}\r\n\r\nconst NOOP_WATCH: PresenceWatch = { reportCallState: () => {}, stop: () => {} };\r\n\r\n/**\r\n * Starts (or immediately declines to start, for a follower tab) the phone-presence connection (plan\r\n * T6). Framework-free and transport-free: `source` is the only thing that knows how to actually open\r\n * a connection — Coventia's by default (`coventia/sources.ts`: mints a ticket, opens the WebSocket),\r\n * a host's own for a test double. This module owns only what is true of ANY such source: leader-\r\n * gating, reconnect-with-backoff, folding `snapshot`/`delta` into one live list, and de-duplicating\r\n * outgoing call-state reports.\r\n *\r\n * When `source` never resolves (a follower that should never have been given a source at all, or a\r\n * backend that is simply down) this produces no data and raises no error — spec §4.4's own success\r\n * criterion: the picker falls back to free text, nothing more.\r\n */\r\nexport function startPresenceSocket(params: StartPresenceSocketParams): PresenceWatch {\r\n const {\r\n source,\r\n getIsLeader,\r\n onAgentsChanged,\r\n setTimeout: scheduleTimeout = globalThis.setTimeout,\r\n clearTimeout: cancelTimeout = globalThis.clearTimeout,\r\n random = Math.random,\r\n } = params;\r\n\r\n // A follower never opens a connection at all — mirrors `startTransferWatch`'s own\r\n // `if (!getIsLeader()) return () => {}` bail (this file's own doc comment above explains why this\r\n // is the current design's normal, expected path for every tab but one, not an edge case).\r\n if (!getIsLeader()) return NOOP_WATCH;\r\n\r\n let stopped = false;\r\n let attempt = 0;\r\n let reconnectTimer: ReturnType<typeof globalThis.setTimeout> | null = null;\r\n let agents = new Map<string, PresenceEntry>();\r\n let lastReportedOnCall: boolean | null = null;\r\n let connection: Awaited<ReturnType<PresenceSource>> | null = null;\r\n\r\n const stop = (): void => {\r\n if (stopped) return;\r\n stopped = true;\r\n if (reconnectTimer !== null) {\r\n cancelTimeout(reconnectTimer);\r\n reconnectTimer = null;\r\n }\r\n connection?.close();\r\n connection = null;\r\n };\r\n\r\n const scheduleReconnect = (): void => {\r\n if (stopped) return;\r\n attempt += 1;\r\n const capped = Math.min(RECONNECT_BASE_MS * 2 ** (attempt - 1), RECONNECT_MAX_MS);\r\n // Full jitter: anywhere from 0 up to the capped delay, so a mass reconnect (a Cloud Run-wide cut)\r\n // spreads out instead of every socket retrying on the exact same clock tick.\r\n const delay = capped * random();\r\n reconnectTimer = scheduleTimeout(() => void connect(), delay);\r\n };\r\n\r\n const connect = async (): Promise<void> => {\r\n if (stopped) return;\r\n // Re-checked here too (see the param doc above) — the one moment after the very first attempt\r\n // where this module is about to do something and can cheaply ask first.\r\n if (!getIsLeader()) {\r\n stop();\r\n return;\r\n }\r\n\r\n let opened: Awaited<ReturnType<PresenceSource>>;\r\n try {\r\n opened = await source({\r\n onSnapshot: (snapshot) => {\r\n agents = new Map(snapshot.map((agent) => [agent.userId, agent]));\r\n onAgentsChanged([...agents.values()]);\r\n },\r\n onDelta: (agent) => {\r\n agents.set(agent.userId, agent);\r\n onAgentsChanged([...agents.values()]);\r\n },\r\n onClose: () => {\r\n // `connection === null` also covers \"not opened yet\" — safe because `source` is contracted\r\n // to resolve (open) BEFORE it ever calls `onClose` (a real WebSocket's own `open`/`close`\r\n // are always separate ticks; there is no such thing as a connection that closes before it\r\n // opens). A close reported before `connection` is set is therefore always OUR OWN — `stop()`\r\n // discarding this very attempt while it was still in flight — never a genuine drop.\r\n if (connection === null) return;\r\n connection = null;\r\n scheduleReconnect();\r\n },\r\n });\r\n } catch {\r\n // Ticket mint failed, the socket never opened, whatever — indistinguishable from a drop, so it\r\n // gets the same treatment: quiet retry, never surfaced (spec §4.4, success criterion #5).\r\n if (!stopped) scheduleReconnect();\r\n return;\r\n }\r\n\r\n if (stopped) {\r\n // `stop()` ran while the connection was still being opened — close it now that it exists rather\r\n // than leave it live with nothing left to receive its callbacks.\r\n opened.close();\r\n return;\r\n }\r\n\r\n connection = opened;\r\n attempt = 0; // a successful connect resets the backoff for the NEXT drop, not this one\r\n if (lastReportedOnCall !== null) connection.reportCallState(lastReportedOnCall);\r\n };\r\n\r\n void connect();\r\n\r\n return {\r\n reportCallState: (onCall) => {\r\n if (onCall === lastReportedOnCall) return;\r\n lastReportedOnCall = onCall;\r\n connection?.reportCallState(onCall);\r\n },\r\n stop,\r\n };\r\n}\r\n","import type { CallEngine, EngineCall } from './engine.js';\r\nimport type { CallOptions, SoftphoneState, TokenSource } from './types.js';\r\nimport type { SoftphoneError } from './errors.js';\r\n\r\nexport interface DefaultProviderConfig {\r\n readonly apiBase: string;\r\n readonly tokenSource: TokenSource;\r\n readonly organizationId?: string | undefined;\r\n}\r\n\r\n/**\r\n * A subscription list that may not have a real target yet.\r\n *\r\n * `defaultZadarmaEngine` below has THREE of these (`onIncoming`, `onStateChange`, `onError`), all\r\n * needing the identical, slightly tricky behaviour: a listener registered before the real engine\r\n * exists must still receive events once it does, AND unsubscribing must work correctly regardless of\r\n * whether it happens before or after that engine shows up. Three independent copies of this is three\r\n * independent chances to get the \"unsubscribe after the real engine attached\" case wrong — silently,\r\n * since nothing in this file's one current caller (`SoftphoneImpl`) ever unsubscribes these three, so\r\n * a bug here would only bite a future caller. One small class, three call sites, no reason to write\r\n * (and maintain) the replay/unsubscribe logic three times.\r\n */\r\nclass LazyRelay<L extends (...args: never[]) => void> {\r\n readonly #listeners = new Set<L>();\r\n readonly #realOff = new Map<L, () => void>();\r\n #target: { on(listener: L): () => void } | null = null;\r\n\r\n subscribe(listener: L): () => void {\r\n this.#listeners.add(listener);\r\n if (this.#target) this.#realOff.set(listener, this.#target.on(listener));\r\n return () => {\r\n this.#listeners.delete(listener);\r\n this.#realOff.get(listener)?.();\r\n this.#realOff.delete(listener);\r\n };\r\n }\r\n\r\n /** Called once the real engine exists — replays every listener registered before now onto it. */\r\n attach(target: { on(listener: L): () => void }): void {\r\n this.#target = target;\r\n for (const listener of this.#listeners) this.#realOff.set(listener, target.on(listener));\r\n }\r\n}\r\n\r\n/**\r\n * The default call engine: Zadarma, loaded lazily.\r\n *\r\n * Returns a `CallEngine` SYNCHRONOUSLY — `createSoftphone` must stay synchronous and SSR-safe even\r\n * when `provider` is omitted — and defers the real work to the first genuine use (`start()` or\r\n * `placeCall()`). Only then does it dynamically `import('../transports/zadarma-engine.js')`, which is\r\n * what actually reaches jssip and browser globals. Exactly the trick `coventiaBackend` (`backend.ts`)\r\n * already uses for contacts/history; applied here to the engine instead.\r\n *\r\n * The import specifier is a STRING LITERAL, not a variable — `check-imports.mjs` enforces this across\r\n * the whole build, after a variable specifier here once shipped a bare specifier no bundler could\r\n * resolve, which typechecked, passed 211 tests (all against fakes), and broke only in a real browser.\r\n */\r\nexport function defaultZadarmaEngine(config: DefaultProviderConfig): CallEngine {\r\n let real: CallEngine | null = null;\r\n let pending: Promise<CallEngine> | null = null;\r\n\r\n const incoming = new LazyRelay<(call: EngineCall) => void>();\r\n const stateChange = new LazyRelay<(state: SoftphoneState) => void>();\r\n const error = new LazyRelay<(error: SoftphoneError) => void>();\r\n\r\n function load(): Promise<CallEngine> {\r\n pending ??= import('../transports/zadarma-engine.js')\r\n .then(({ createZadarmaEngine }) => {\r\n const engine = createZadarmaEngine(config);\r\n incoming.attach({ on: (l) => engine.onIncoming(l) });\r\n stateChange.attach({ on: (l) => engine.onStateChange(l) });\r\n error.attach({ on: (l) => engine.onError?.(l) ?? (() => {}) });\r\n real = engine;\r\n return engine;\r\n })\r\n .catch((cause: unknown) => {\r\n // A retry (the widget's `Reintentar`: `stop()` then `start()` again) must be a genuine second\r\n // attempt, not a replay of the same cached failure — clearing `pending` is what makes that\r\n // true. Without it, one failed chunk load would fail every future retry forever.\r\n pending = null;\r\n throw cause;\r\n });\r\n return pending;\r\n }\r\n\r\n return {\r\n get state(): SoftphoneState {\r\n return real?.state ?? 'stopped';\r\n },\r\n async start(): Promise<void> {\r\n await (await load()).start();\r\n },\r\n async stop(): Promise<void> {\r\n // Never successfully started (or a failed load left `real` unset) — nothing to stop, and\r\n // calling `load()` here would wrongly turn a no-op into a first attempt.\r\n if (real) await real.stop();\r\n },\r\n async placeCall(destination: string, options?: CallOptions): Promise<EngineCall> {\r\n return (await load()).placeCall(destination, options);\r\n },\r\n onIncoming: (listener) => incoming.subscribe(listener),\r\n onStateChange: (listener) => stateChange.subscribe(listener),\r\n onError: (listener) => error.subscribe(listener),\r\n };\r\n}\r\n","import { SoftphoneImpl } from './softphone-impl.js';\r\nimport { defaultZadarmaEngine } from './default-provider.js';\r\nimport type { CallEngine } from './engine.js';\r\nimport type { Softphone } from './softphone.js';\r\nimport type { SoftphoneConfig } from './types.js';\r\n\r\n/**\r\n * Construct a softphone.\r\n *\r\n * A factory, not `new Softphone()`, for two reasons: it resolves the underlying call engine from\r\n * config without exposing that machinery, and it returns the `Softphone` INTERFACE, so the concrete\r\n * class stays an implementation detail free to change without a major version.\r\n *\r\n * @example\r\n * const phone = createSoftphone({\r\n * tokenSource: TokenSource.endpoint('/voice/token'),\r\n * resolveContact: (n) => api.lookup(n),\r\n * });\r\n * await phone.start(); // from a user gesture — see Softphone.start()\r\n */\r\nexport function createSoftphone(config: SoftphoneConfig): Softphone {\r\n return new SoftphoneImpl(resolveEngine(config), config);\r\n}\r\n\r\n/**\r\n * Pick the call engine.\r\n *\r\n * Three outcomes, in order:\r\n * 1. A valid `config.provider` — used as-is. `apiBase` is never even looked at: an explicit engine\r\n * always wins, so a host who wants a `MockProvider` or their own `organizationId` gets exactly\r\n * that, with no default quietly built alongside it.\r\n * 2. `provider` was SET but is not a valid engine — thrown on immediately, distinctly from \"none\r\n * configured\" below. Silently falling through to the default here would turn a typo'd config\r\n * into a confusing \"why is this calling Zadarma, I passed something else\" bug report.\r\n * 3. `provider` omitted, `apiBase` present — `defaultZadarmaEngine` (SPEC §5.4). It resolves\r\n * synchronously and touches nothing browser-specific until `start()`/`placeCall()` actually run,\r\n * so this stays as SSR-safe as the explicit-provider path.\r\n *\r\n * Neither present: a plain, explicit throw beats a confusing failure deep inside `start()`.\r\n */\r\nfunction resolveEngine(config: SoftphoneConfig): CallEngine {\r\n if (config.provider !== undefined) {\r\n if (isCallEngine(config.provider)) return config.provider;\r\n throw new Error(\r\n '`config.provider` was set but is not a valid CallEngine — it needs at least `placeCall` and ' +\r\n '`onIncoming`. Pass `createZadarmaEngine(...)` from \"@coventia/softphone/zadarma\", a ' +\r\n '`MockProvider` from \"@coventia/softphone/testing\", or omit it entirely to use the default.',\r\n );\r\n }\r\n\r\n if (config.apiBase) {\r\n return defaultZadarmaEngine({\r\n apiBase: config.apiBase,\r\n tokenSource: config.tokenSource,\r\n organizationId: config.coventia?.organizationId,\r\n });\r\n }\r\n\r\n throw new Error(\r\n 'No call engine configured. Pass `config.provider` explicitly — `createZadarmaEngine({ apiBase, ' +\r\n 'tokenSource })` from \"@coventia/softphone/zadarma\", or a `MockProvider` from ' +\r\n '\"@coventia/softphone/testing\" — or set `config.apiBase` so a default Zadarma engine can be built.',\r\n );\r\n}\r\n\r\nfunction isCallEngine(value: unknown): value is CallEngine {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n typeof (value as CallEngine).placeCall === 'function' &&\r\n typeof (value as CallEngine).onIncoming === 'function'\r\n );\r\n}\r\n","/**\r\n * @coventia/softphone — the framework-free engine.\r\n *\r\n * Importable from vanilla JS, Vue, Svelte, a PHP-rendered page, and a Node/SSR process. Exports NO\r\n * React and touches NO browser global at module scope (SPEC §10.1, §10.2). Those two rules — not the\r\n * authoring language, not the package layout — are what make this usable anywhere.\r\n *\r\n * THIS FILE IS THE PUBLIC API. Anything not exported here is free to change without a major version;\r\n * anything exported here is not. Adding to it is cheap and removing from it is expensive, so add\r\n * deliberately.\r\n *\r\n * Notably NOT exported, and deliberately:\r\n * - `SipProvider` and everything under transports/ — the provider seam is internal (SPEC §5.4).\r\n * Exporting it would freeze the shape of a Zadarma migration we have not designed yet.\r\n * - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.\r\n */\r\n\r\n// ── Engine ────────────────────────────────────────────────────────────────────\r\nexport { createSoftphone } from './core/create.js';\r\nexport type { Softphone, AudioDevices } from './core/softphone.js';\r\n\r\n// ── Calls ─────────────────────────────────────────────────────────────────────\r\nexport type { Call, CallState, HangupReason, HoldOriginator } from './core/call.js';\r\nexport { CALL_TRANSITIONS, INITIAL_STATES, canTransition, isTerminal } from './core/call.js';\r\n\r\n// ── Events ────────────────────────────────────────────────────────────────────\r\nexport { SoftphoneEvent, CallEvent } from './core/events.js';\r\nexport type { SoftphoneEventMap, CallEventMap, TypedEmitter } from './core/events.js';\r\n\r\n// ── Errors ────────────────────────────────────────────────────────────────────\r\n// `isFatal` is public because a host has to branch on it — `err.fatal` narrows structurally, but a\r\n// guard is friendlier from JavaScript. `createError` stays internal: the SDK raises these, hosts read\r\n// them, and a host minting our error codes would be claiming a failure we never observed.\r\nexport { ERROR_CATALOGUE, isFatal } from './core/errors.js';\r\nexport type {\r\n SoftphoneError,\r\n SoftphoneErrorCode,\r\n FatalSoftphoneError,\r\n FatalErrorCode,\r\n NonFatalSoftphoneError,\r\n NonFatalErrorCode,\r\n} from './core/errors.js';\r\n\r\n// ── Config & types ────────────────────────────────────────────────────────────\r\nexport type {\r\n SoftphoneConfig,\r\n SoftphoneState,\r\n CallDirection,\r\n CallOptions,\r\n Contact,\r\n RemoteParty,\r\n LogLevel,\r\n TokenSource,\r\n TokenEndpointOptions,\r\n VoiceToken,\r\n} from './core/types.js';\r\n\r\n// ── Capability probes ─────────────────────────────────────────────────────────\r\nexport { isBrowser, isWebRtcSupported } from './core/env.js';\r\n\r\nexport const VERSION = '0.2.0';\r\n"]}