@coventia/softphone 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{call-FqcvDg1t.d.cts → call-iGvr2ZnO.d.cts} +26 -2
  2. package/dist/{call-FqcvDg1t.d.ts → call-iGvr2ZnO.d.ts} +26 -2
  3. package/dist/chunk-3UTUWGVG.js +1891 -0
  4. package/dist/chunk-3UTUWGVG.js.map +1 -0
  5. package/dist/chunk-DZRSSZWB.js +39 -0
  6. package/dist/chunk-DZRSSZWB.js.map +1 -0
  7. package/dist/element/index.d.ts +25 -1
  8. package/dist/{engine-CzOCrfY9.d.cts → engine-BKoT_Pj7.d.cts} +10 -1
  9. package/dist/{engine-CsNRD7B7.d.ts → engine-CeGh5Yai.d.ts} +10 -1
  10. package/dist/index.cjs +1049 -130
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +5 -5
  13. package/dist/index.d.ts +5 -5
  14. package/dist/index.js +179 -13
  15. package/dist/index.js.map +1 -1
  16. package/dist/react/index.d.cts +2 -2
  17. package/dist/react/index.d.ts +2 -2
  18. package/dist/{softphone-By9sizlV.d.ts → softphone-D0ao5W4z.d.ts} +1 -1
  19. package/dist/{softphone-JZvJdzU3.d.cts → softphone-DRoS0jJR.d.cts} +1 -1
  20. package/dist/{sources-QL2LRZVH.js → sources-LGFJG7W2.js} +10 -38
  21. package/dist/sources-LGFJG7W2.js.map +1 -0
  22. package/dist/testing/index.d.cts +2 -2
  23. package/dist/testing/index.d.ts +2 -2
  24. package/dist/ui/index.d.cts +2 -2
  25. package/dist/ui/index.d.ts +2 -2
  26. package/dist/zadarma/index.cjs +865 -116
  27. package/dist/zadarma/index.cjs.map +1 -1
  28. package/dist/zadarma/index.d.cts +37 -10
  29. package/dist/zadarma/index.d.ts +37 -10
  30. package/dist/zadarma/index.js +2 -1
  31. package/dist/zadarma-engine-VYIXLBQG.js +7 -0
  32. package/dist/{zadarma-engine-TIUE7PZ7.js.map → zadarma-engine-VYIXLBQG.js.map} +1 -1
  33. package/package.json +1 -1
  34. package/dist/chunk-6RR6G35E.js +0 -1146
  35. package/dist/chunk-6RR6G35E.js.map +0 -1
  36. package/dist/sources-QL2LRZVH.js.map +0 -1
  37. package/dist/zadarma-engine-TIUE7PZ7.js +0 -6
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../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"],"names":["track","known","cached"],"mappings":"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","file":"chunk-3UTUWGVG.js","sourcesContent":["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\n/** The far end's number as the session names it. Exported for the engine's decline guard, which reads it before a call exists. */\r\nexport function 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 guard against the provider re-offering it\r\n * (SPEC §3.4, incoming-call-reliability.md §4.4). Tied to the reject ACTION, not to `ended` — a normal hangup\r\n * 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 { CallOffer, CallOptions, SoftphoneState } from '../../core/types.js';\r\nimport type { IncomingSignalEvent, SipCredentials, SipProvider } from '../providers/provider.js';\r\nimport type { JsSipFactory, JsSipSession, JsSipUA, JsSipUAConfig } from './jssip.js';\r\nimport { SipEngineCall, remoteNumberOf } 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\nimport { toDigits } from '../../sources.js';\r\n\r\nconst DEFAULT_ICE_CAP_MS = 3000;\r\nconst REGISTER_EXPIRES = 580;\r\n/** A retrying registrar must not become a stream of identical errors. */\r\nconst ERROR_DEBOUNCE_MS = 30_000;\r\n/**\r\n * What the backend said about an offered call (`noteOffer`) is forgotten this long after it last said anything\r\n * about it — the 2 min it keeps its own record of an offer (incoming-call-reliability.md §5). An offer older than\r\n * that is not matched to a decline.\r\n */\r\nconst OFFER_FORGOTTEN_MS = 120_000;\r\n/**\r\n * The longest a decline guard following a call (`DeclinedCall.callId`) can hold, counted from the decline —\r\n * the same 2 min as the leak guard (§4.3). An `end` can be lost (the presence socket down for minutes around it,\r\n * the backend's Redis down), and a guard waiting for it forever would keep that caller's calls out for good: a\r\n * duplicate ring beats a lost call (§11).\r\n */\r\nconst DECLINE_GUARD_CEILING_MS = 120_000;\r\n/**\r\n * How long before this call's push a `start` may have come and still be this call's. The backend relays\r\n * `NOTIFY_INTERNAL` 1.0–1.6 s after `NOTIFY_START` (production logs, 2026-10-30) — about when the push lands, on\r\n * either side of it; an earlier `start` from the same caller is an earlier call.\r\n */\r\nconst OFFER_PUSH_SLACK_MS = 10_000;\r\n/**\r\n * Timer B (64 × T1): how long JsSIP's INVITE transaction waits for any answer — so the longest a UA must stay\r\n * connected to send a CANCEL it still owes (incoming-call-reliability.md §4.7, F6).\r\n */\r\nconst INVITE_TRANSACTION_MAX_MS = 32_000;\r\n/** JsSIP's INVITE client transaction states that still wait for a final response (`Transactions.js`). */\r\nconst ICT_PROCEEDING = 2;\r\nconst ICT_CALLING = 3;\r\n\r\n/**\r\n * JsSIP's INVITE client transaction, as far as the engine reads it (§4.7). Internal to JsSIP 3.13.8 — not in its\r\n * typings — so read structurally, and only trusted when it looks like one (`inviteTransactionOf`).\r\n */\r\ninterface InviteTransaction {\r\n readonly state: number;\r\n on(event: 'stateChanged', cb: () => void): void;\r\n removeListener?(event: 'stateChanged', cb: () => void): void;\r\n}\r\n\r\n/**\r\n * The INVITE transaction behind an outbound session (`session._request.transaction`; the request is replaced when\r\n * the INVITE is re-sent with credentials, and so is this), or `null` when this JsSIP does not expose one.\r\n */\r\nfunction inviteTransactionOf(session: JsSipSession): InviteTransaction | null {\r\n const tx = (session as unknown as { _request?: { transaction?: Partial<InviteTransaction> } })._request?.transaction;\r\n return tx && typeof tx.state === 'number' && typeof tx.on === 'function' ? (tx as InviteTransaction) : null;\r\n}\r\n\r\n/** Still waiting for its final response: no 2xx, no 3xx–6xx, no Timer B. */\r\nfunction awaitsAnswer(tx: InviteTransaction): boolean {\r\n return tx.state === ICT_CALLING || tx.state === ICT_PROCEEDING;\r\n}\r\n\r\n/**\r\n * JsSIP's `_is_canceled` (internal): the leg was terminated before it could cancel — its CANCEL waits for a 1xx.\r\n * `null` when this JsSIP does not expose it.\r\n */\r\nfunction deferredCancelOf(session: JsSipSession): boolean | null {\r\n const flag = (session as unknown as { _is_canceled?: unknown })._is_canceled;\r\n return typeof flag === 'boolean' ? flag : null;\r\n}\r\n\r\n/**\r\n * A CANCEL JsSIP still owes (§4.7): the UA it has to go out on, the leg, and the INVITE transaction it is waiting on\r\n * — the latest one: a challenged INVITE is re-sent on a new transaction — or `null` when that transaction cannot be\r\n * read, which then holds the UA for the whole Timer B.\r\n */\r\ninterface OwedCancel {\r\n readonly ua: JsSipUA;\r\n readonly session: JsSipSession;\r\n tx: InviteTransaction | null;\r\n}\r\n/** Below this many digits a number must match exactly (an extension, a short code); see `sameNumber`. */\r\nconst SUFFIX_MATCH_MIN_DIGITS = 9;\r\n\r\n/**\r\n * Whether two numbers are the same line, on digits only (incoming-call-reliability.md §4.4). The caller reaches\r\n * the decline guard from three places — the push, the INVITE and the backend's offer — and not always in one\r\n * format (`+34600000001`, `34600000001`, `600000001`), so a long number also matches its own tail. Anything\r\n * unknown matches nothing: a guard that cannot tell lets the call ring.\r\n */\r\nfunction sameNumber(a: string, b: string): boolean {\r\n if (!a || !b) return false;\r\n if (a === b) return true;\r\n const [shorter, longer] = a.length < b.length ? [a, b] : [b, a];\r\n return shorter.length >= SUFFIX_MATCH_MIN_DIGITS && longer.endsWith(shorter);\r\n}\r\n\r\n/**\r\n * The registrar refused the credentials themselves (401/403) — stale, or from the wrong datacenter (SPEC §3.1) —\r\n * as opposed to a REGISTER that timed out or lost its transport. JsSIP names those two `Authentication Error`\r\n * and `Rejected`; the status code decides when there is one.\r\n */\r\nfunction isRefusal(e: { cause?: string; response?: { status_code?: number } | null } | undefined): boolean {\r\n const status = e?.response?.status_code;\r\n if (typeof status === 'number') return status === 401 || status === 403;\r\n return e?.cause === 'Authentication Error' || e?.cause === 'Rejected';\r\n}\r\n\r\n/**\r\n * Diagnostics nobody has to act on — a push that offered nothing, an offer ignored as a re-offer (§4.1).\r\n * `console.debug`, which browsers hide unless asked for.\r\n */\r\nfunction debug(message: string): void {\r\n console.debug(`[softphone] ${message}`);\r\n}\r\n\r\n/** A dial waiting for the current UA to register (`#callWhenRegistered`). */\r\ninterface RegistrationWaiter {\r\n dial(ua: JsSipUA): void;\r\n fail(): void;\r\n}\r\n\r\n/** An offer waiting for its INVITE (incoming-call-reliability.md §4.3). */\r\ninterface OfferWait {\r\n /** The caller the push named, digits only, or '' when it did not. */\r\n readonly from: string;\r\n /** When its push came: the leak guard counts from here, and so does \"this call\" for the decline guard. */\r\n readonly at: number;\r\n /** The leak guard (`offerWaitMaxMs`), from the push. */\r\n readonly leak: ReturnType<typeof setTimeout> | undefined;\r\n}\r\n\r\n/** An offer pushed while a call was in progress: registered for once the line is free (§4.3). */\r\ninterface PendingOffer {\r\n readonly from: string | null;\r\n readonly at: number;\r\n}\r\n\r\n/** What the backend last said about one offered call (`noteOffer`, §5). */\r\ninterface KnownOffer {\r\n /** The caller, digits only; '' when no `start` named one (an `end` can come alone). */\r\n readonly from: string;\r\n /** When the backend last said anything about it. */\r\n readonly at: number;\r\n /**\r\n * When it ended, or `null`. Sticky: messages about one call cross different backend instances with no order,\r\n * so a `start` that lands after its own `end` is old news — never a live call again (§4.4 decision 5).\r\n */\r\n readonly endedAt: number | null;\r\n}\r\n\r\n/** A declined call whose re-offers are ignored (incoming-call-reliability.md §4.4). */\r\ninterface DeclinedCall {\r\n /** The declined caller, digits only. */\r\n readonly caller: string;\r\n /**\r\n * The backend's id for the declined call: the guard then lasts until its `end` (or the ceiling). `null`: no\r\n * live offer from that caller was known at the decline — the fallback window, then.\r\n */\r\n readonly callId: string | null;\r\n /**\r\n * The decline came before the backend announced the call (its `start` can land after the decline): a `start`\r\n * from the same caller that comes around the call's push (`OFFER_PUSH_SLACK_MS` after it, at most) is taken as\r\n * this call's, and the guard follows it from then. `false` once adopted, for a call whose offer had ENDED (since\r\n * its push) before the decline, and once an `end` from that caller comes after the push — whatever rings next\r\n * from that caller is not the call that was declined.\r\n */\r\n readonly adopts: boolean;\r\n /** When it was declined: the fallback window and the ceiling count from here. */\r\n readonly at: number;\r\n /** When the declined call's push came (its INVITE, when no push was seen): what is older is another call. */\r\n readonly since: number;\r\n}\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 an `incomingSignal` offer (or to place a call), tear\r\n * down after. Zadarma's PBX ignores a standing registration (SPEC §3.2). Every offer\r\n * gets a REGISTER sent after it; the timers and the decline guard come from\r\n * `provider.quirks` (incoming-call-reliability.md §4).\r\n */\r\nexport class SipCallEngine implements CallEngine {\r\n #state: SoftphoneState = 'stopped';\r\n /**\r\n * Which `start()`…`stop()` cycle this is — bumped by `stop()`, which ends one. Everything that awaits\r\n * and then acts (builds a UA, subscribes to the signal, sets the state) captures it first and gives up\r\n * if a stop has happened since. Not bumped by `start()`: that would cancel work begun just before it,\r\n * such as a call placed while the softphone is still starting.\r\n *\r\n * Without it a stopped phone came back to life: a provisioning or a signal connect still in flight\r\n * when `stop()` ran finished afterwards and REGISTERed — and since the PBX routes a call to whichever\r\n * client registered last, it quietly took calls from the user's other device while its UI said off.\r\n */\r\n #run = 0;\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 /**\r\n * Bumped by every `#teardownUA`. A UA still being provisioned when its reason to exist went away — the offer\r\n * ended, the leak guard gave up — is then not built (`#createUA`): built anyway, it would REGISTER for nobody\r\n * and nothing would ever tear it down.\r\n */\r\n #uaEpoch = 0;\r\n /**\r\n * Dials waiting for the current UA to register (`#callWhenRegistered`): dialled by its `registered`, failed by\r\n * `#teardownUA` — but kept when the UA is only being rebuilt on fresh credentials (`#retryOnFreshCredentials`).\r\n */\r\n readonly #awaitingRegistration = new Set<RegistrationWaiter>();\r\n /** `placeCall`s in progress, from before their UA exists: no offer's teardown may pull it from under them. */\r\n #dialing = 0;\r\n /**\r\n * Where the current UA's registration stands: `connecting` (its REGISTER goes out once the WSS is up),\r\n * `registering` (a REGISTER is out, unanswered), `registered` (the WSS is up and no REGISTER of ours is out — a\r\n * refresh that failed under a live call leaves it here too). What decides how an offer gets a REGISTER sent\r\n * after it (`#registerAfterOffer`).\r\n */\r\n #registration: 'connecting' | 'registering' | 'registered' = 'connecting';\r\n /** An offer arrived while a REGISTER sent BEFORE it was still unanswered: another goes out once it is. */\r\n #registerAgain = false;\r\n /** A UA has already been rebuilt on fresh credentials since the last registration that worked: not again. */\r\n #freshCredentialsTried = false;\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 /** The offer waiting for its INVITE, if any (§4.3). */\r\n #offerWait: OfferWait | null = null;\r\n /**\r\n * The latest offer pushed while a call was in progress. It used to be dropped for good — the caller then waited\r\n * for the provider to offer it again. Now, when the call ends, it is registered for at once if it is still\r\n * within its wait (no `offer-ended` for it, inside the leak guard), so it rings as soon as the line is free.\r\n */\r\n #pendingOffer: PendingOffer | null = null;\r\n /** Armed at the REGISTER's 200 OK while an offer waits (`noInviteAfterRegisterMs`). */\r\n #noInviteTimer: ReturnType<typeof setTimeout> | undefined;\r\n /**\r\n * What the backend has said about the calls offered to this line (`noteOffer`, §5), by `callId` — ended ones\r\n * included, for a while. Only for telling, at a decline, which call was declined and whether it is still alive.\r\n */\r\n readonly #offers = new Map<string, KnownOffer>();\r\n /** Declined calls whose re-offers are being ignored (§4.4). Pruned as they lapse. */\r\n #declined: DeclinedCall[] = [];\r\n /** CANCELs JsSIP still owes for outbound legs that ended before any ringback (§4.7, `#watchOwedCancel`). */\r\n #owedCancels: OwedCancel[] = [];\r\n /** UAs let go of while owing one: un-REGISTERed, still connected — with the cap that stops them regardless. */\r\n readonly #lingering = new Map<JsSipUA, ReturnType<typeof setTimeout>>();\r\n readonly #errorSentAfter = new Map<SoftphoneErrorCode, number>();\r\n\r\n private readonly now: () => number;\r\n #resolvedFactory: JsSipFactory | null = null;\r\n /** The loader in flight, shared: the pre-load at `start()` and a first UA racing it fetch the module once. */\r\n #factoryLoad: Promise<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 if (typeof this.factory !== 'function') return (this.#resolvedFactory = this.factory);\r\n const loader = this.factory;\r\n this.#factoryLoad ??= Promise.resolve()\r\n .then(() => loader())\r\n .finally(() => {\r\n this.#factoryLoad = null; // a failed load is not kept: the next UA tries again, as it always did\r\n });\r\n this.#resolvedFactory = await this.#factoryLoad;\r\n return this.#resolvedFactory;\r\n }\r\n\r\n /**\r\n * Fetch the SIP stack now, not on the first call. It is loaded lazily (`import('jssip')`, SSR-safe — SPEC §10.2),\r\n * and that used to happen on the first UA: between the first push after a page load and its REGISTER, measured\r\n * 33–109 ms on localhost and more on a slow network or a throttled tab. Fire-and-forget: a failure here only\r\n * leaves the lazy path to try again when a UA is built (incoming-call-reliability.md §4.5).\r\n */\r\n #preloadFactory(): void {\r\n this.#resolveFactory().catch(() => {});\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 const run = this.#run;\r\n this.#setState('starting');\r\n if (this.provider.registrationMode === 'persistent') {\r\n try {\r\n await this.#ensureUA();\r\n } catch (cause) {\r\n if (run === this.#run) throw cause; // otherwise stop() cancelled us — not a failure\r\n }\r\n if (run !== this.#run) return; // stopped while registering: `stopped` is already the truth\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) — with the SIP stack already loaded, so no push\r\n // waits for a module fetch.\r\n this.#preloadFactory();\r\n const signal = this.provider.incomingSignal;\r\n if (signal) {\r\n try {\r\n await signal.connect();\r\n } catch (cause) {\r\n if (run !== this.#run) return; // stop() cancelled us — not a failure, and `stopped` is already the truth\r\n // Nothing half-open is left behind: a signal whose connect failed can still have its background rebuild\r\n // armed, and in a hidden tab that opened a push socket nobody listened to — kept open in `standby`, too.\r\n // The next start connects afresh (incoming-call-reliability.md §4.6).\r\n signal.disconnect();\r\n throw cause;\r\n }\r\n // stop() ran while the signal was connecting. It unsubscribed what there was — nothing yet —\r\n // so subscribing now would wire a stopped engine back to every push, and the state below\r\n // would overwrite its `stopped`.\r\n if (run !== this.#run) return;\r\n this.#signalUnsub = signal.onSignal((event) => this.#onIncomingSignal(event));\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.#run += 1; // first, so whatever is still in flight from this run sees it (see `#run`)\r\n this.#pendingOffer = null; // the call this stop hangs up must not register for it on its way out\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 // Counted from before the UA exists: an offer ending meanwhile must not tear down the UA this dial is about\r\n // to use (`#maybeTeardownUA`).\r\n this.#dialing += 1;\r\n const run = this.#run;\r\n try {\r\n await this.#ensureUA();\r\n return await this.#callWhenRegistered(this.#buildTarget(destination), run);\r\n } finally {\r\n this.#dialing -= 1;\r\n }\r\n }\r\n\r\n /**\r\n * What the backend says about calls offered to this line (incoming-call-reliability.md §5) — kept only to tell,\r\n * at a decline, WHICH call was declined, and so how long its re-offers last (§4.4).\r\n *\r\n * Per `callId`, every message is idempotent, and they come duplicated, with no order at all — not even between\r\n * the `start` and the `end` of one call, which reach different backend instances and are relayed through Redis,\r\n * and a stored offer replayed after `hello` can race either. So `ended` is sticky: a `start` after its call's\r\n * `end` never makes it live again. An `end` also reaches a softphone that is not the line's owner (a `start` only\r\n * the owner): stored all the same, whatever state this engine is in. `null`: the channel went away, so no `end`\r\n * can be counted on for the calls still live; a guard following one falls back to its window. What is known to\r\n * have ended stays known.\r\n */\r\n noteOffer(offer: CallOffer | null): void {\r\n const now = this.now();\r\n for (const [callId, known] of this.#offers) {\r\n if (now - known.at >= OFFER_FORGOTTEN_MS) this.#offers.delete(callId);\r\n }\r\n if (offer === null) {\r\n for (const [callId, known] of this.#offers) if (known.endedAt === null) this.#offers.delete(callId);\r\n return;\r\n }\r\n const known = this.#offers.get(offer.callId);\r\n this.#offers.delete(offer.callId); // re-inserted, so the most recent word is the last entry\r\n const from = toDigits(offer.from ?? '') || (known?.from ?? '');\r\n const fallbackMs = this.#declineFallbackMs();\r\n if (offer.state === 'end') {\r\n this.#offers.set(offer.callId, { from, at: now, endedAt: known?.endedAt ?? now });\r\n // The declined call is over: its caller calling back is a new call, and rings — at once, not when a\r\n // window runs out. This is CWA-81.\r\n this.#declined = this.#declined.filter((declined) => declined.callId !== offer.callId);\r\n // An `end` from a caller whose decline is still waiting to learn its call, since that call's push: whatever\r\n // that caller's next `start` is, it is not the declined call. Adoption stops; the window stays — the `end`\r\n // may be a leg of a call still being offered (a transfer back), so it does not lift the guard.\r\n if (from) {\r\n this.#declined = this.#declined.map((declined) =>\r\n declined.adopts && now >= declined.since && sameNumber(declined.caller, from)\r\n ? { ...declined, adopts: false }\r\n : declined,\r\n );\r\n }\r\n return;\r\n }\r\n const endedAt = known?.endedAt ?? null;\r\n this.#offers.set(offer.callId, { from, at: now, endedAt });\r\n if (!from) return;\r\n if (endedAt !== null) {\r\n // The `start` of a call that has already ended, arriving late. If it ended after a decline's push, it is that\r\n // declined call — over: a guard still waiting to learn which call it was has nothing left to guard.\r\n const isThatCall = (declined: DeclinedCall): boolean =>\r\n declined.adopts &&\r\n now - declined.at < fallbackMs &&\r\n endedAt >= declined.since &&\r\n sameNumber(declined.caller, from);\r\n this.#declined = this.#declined.filter((declined) => !isThatCall(declined));\r\n return;\r\n }\r\n // A decline that came before the backend announced its call: this is that call, and the guard follows it — but\r\n // only a `start` that comes around that call's push (production: 1.0–1.6 s after it). Any later one is the\r\n // NEXT call from that caller (the call-back, when the declined call's own `start` never came: lost, or held\r\n // back by the backend for a transfer back), and following it would swallow that call for its whole life.\r\n this.#declined = this.#declined.map((declined) =>\r\n declined.adopts &&\r\n now - declined.at < fallbackMs &&\r\n now <= declined.since + OFFER_PUSH_SLACK_MS &&\r\n sameNumber(declined.caller, from)\r\n ? { ...declined, callId: offer.callId, adopts: false }\r\n : declined,\r\n );\r\n }\r\n\r\n // ── incoming (on-demand: push → register → INVITE) ────────────────────────\r\n /**\r\n * The push's own word on what it is (incoming-call-reliability.md §4.1, read by the provider): only an `offer`\r\n * registers. Before this, every push did — `answered` after each hang-up included — and the UA it left\r\n * registered is where the next call's INVITE never came (F2).\r\n */\r\n #onIncomingSignal(event: IncomingSignalEvent): void {\r\n if (event.kind === 'offer') void this.#onOffer(event.from);\r\n else if (event.kind === 'offer-ended') this.#onOfferEnded(event.from);\r\n else debug(`push ignored: ${event.detail}`);\r\n }\r\n\r\n /** `pushedAt`: when its push came, for an offer kept while a call was in progress (`#pendingOffer`). */\r\n async #onOffer(from: string | null, pushedAt = this.now()): Promise<void> {\r\n // A re-offer of a call just declined (SPEC §3.4): ignored — but only that caller's, and only while that call\r\n // lives (§4.4). Persistent providers set no guard, so this is a no-op there.\r\n if (this.#isDeclinedCaller(from)) {\r\n debug('offer ignored: the caller just declined is being offered again');\r\n return;\r\n }\r\n if (this.#activeCall) {\r\n // On a call: its INVITE would only be answered busy. Kept, and registered for once the call ends (§4.3).\r\n this.#pendingOffer = { from, at: pushedAt };\r\n return;\r\n }\r\n this.#awaitOffer(from, pushedAt);\r\n // A UA kept from an earlier offer is never reused as it is: an on-demand provider sends this offer's INVITE\r\n // only to a UA that REGISTERed after its push (F2, SPEC §3.2). A refresh on the live UA is enough (Q5).\r\n const ua = this.#ua;\r\n if (ua) {\r\n this.#registerAfterOffer(ua);\r\n return;\r\n }\r\n const run = this.#run;\r\n const epoch = this.#uaEpoch;\r\n try {\r\n await this.#ensureUA(); // a UA built now REGISTERs once its WSS is up: after this push, by construction\r\n } catch {\r\n // Only if nothing has replaced it since: a UA from before a stop() refuses itself (`#createUA`), and so does\r\n // one let go of while provisioning — by then the UA here, and the in-flight guard, may be the next offer's.\r\n if (run === this.#run && epoch === this.#uaEpoch) this.#teardownUA();\r\n }\r\n }\r\n\r\n /**\r\n * That offer is over — answered elsewhere, declined, cancelled, timed out (§4.1). A UA still waiting for its\r\n * INVITE goes now instead of when a timer says so; never one carrying a call (`#maybeTeardownUA`). Only when the\r\n * push names the same caller as the offer waited for: two offers can overlap, and an end that names nobody (a\r\n * withheld number, a push with no `callerid`) could be anyone's — the timers bound that wait instead.\r\n */\r\n #onOfferEnded(from: string | null): void {\r\n const caller = toDigits(from ?? '');\r\n if (!caller) return;\r\n if (this.#pendingOffer && sameNumber(caller, toDigits(this.#pendingOffer.from ?? ''))) this.#pendingOffer = null;\r\n const wait = this.#offerWait;\r\n // Nothing waiting (the offer-ended a provider sends after every hang-up, say), or another caller's offer.\r\n if (!wait || !sameNumber(caller, wait.from)) return;\r\n this.#offerOver();\r\n }\r\n\r\n /**\r\n * Starts waiting for this offer's INVITE: a leak guard from its push (§4.3), and no no-INVITE timer yet.\r\n * `pushedAt` is now, except for an offer kept through a call (`#pendingOffer`).\r\n */\r\n #awaitOffer(from: string | null, pushedAt: number): void {\r\n this.#endOfferWait();\r\n const ms = this.provider.quirks?.offerWaitMaxMs;\r\n const leak = ms\r\n ? setTimeout(\r\n () => {\r\n if (this.#offerWait !== wait) return;\r\n // Never a real offer — those end with an INVITE or with their `offer-ended` long before — so nothing\r\n // to tell the user about a call nobody saw.\r\n debug('offer abandoned: still no INVITE long after its push');\r\n this.#offerOver();\r\n },\r\n Math.max(0, ms - (this.now() - pushedAt)),\r\n )\r\n : undefined;\r\n const wait: OfferWait = { from: toDigits(from ?? ''), at: pushedAt, leak };\r\n this.#offerWait = wait;\r\n }\r\n\r\n /**\r\n * The offer kept through the call that just ended, if it is still worth registering for: no `offer-ended` for\r\n * it, and still inside the leak guard from its push. Taken: it is registered for once, at most.\r\n */\r\n #takePendingOffer(): PendingOffer | null {\r\n const pending = this.#pendingOffer;\r\n this.#pendingOffer = null;\r\n if (!pending || !this.#signalUnsub) return null; // nothing kept, or the engine is stopped\r\n const maxMs = this.provider.quirks?.offerWaitMaxMs;\r\n if (maxMs && this.now() - pending.at >= maxMs) return null;\r\n return pending;\r\n }\r\n\r\n /** No INVITE is coming for the offer being waited for: its UA goes, unless something else is on it. */\r\n #offerOver(): void {\r\n this.#endOfferWait();\r\n this.#maybeTeardownUA();\r\n }\r\n\r\n #endOfferWait(): void {\r\n if (this.#offerWait?.leak) clearTimeout(this.#offerWait.leak);\r\n this.#offerWait = null;\r\n this.#registerAgain = false;\r\n this.#clearNoInviteTimer();\r\n }\r\n\r\n /**\r\n * A REGISTER sent AFTER this offer's push, on the UA that is already there (§4.2). Whether one is needed depends\r\n * on where that UA's registration stands:\r\n * - `connecting`: its REGISTER goes out when the WSS is up — after this push already.\r\n * - `registering`: the REGISTER in flight went out BEFORE this push; another follows its answer.\r\n * - `registered`: a refresh, now.\r\n */\r\n #registerAfterOffer(ua: JsSipUA): void {\r\n if (this.#registration === 'registering') this.#registerAgain = true;\r\n else if (this.#registration === 'registered') this.#refreshRegistration(ua);\r\n }\r\n\r\n /**\r\n * A REGISTER refresh whose 200 OK is HEARD — the no-INVITE wait counts from it (§4.3).\r\n *\r\n * JsSIP's `register()` sends one, but announces `registered` only for a UA that was not registered\r\n * (`Registrator.js`: `if (!this._registered)`), so a refresh's 200 OK would pass in silence.\r\n * `registrator().onTransportClosed()` clears that flag and sends nothing — it is what JsSIP runs itself when its\r\n * WSS drops, before registering again — so after it the refresh is announced like a first registration. On the\r\n * wire nothing changes: the same refresh (same Call-ID, next CSeq) the lab saw answered with the INVITE\r\n * (Q5). Only called with no REGISTER of ours in flight (`#registration`), so no answer is left orphaned.\r\n *\r\n * A JsSIP without that hook still sends the refresh; its 200 OK just cannot be heard, so the wait starts now.\r\n */\r\n #refreshRegistration(ua: JsSipUA): void {\r\n const registrator = ua.registrator?.();\r\n const heard = typeof registrator?.onTransportClosed === 'function';\r\n if (heard) registrator.onTransportClosed!();\r\n this.#registration = heard ? 'registering' : 'registered';\r\n ua.register();\r\n if (!heard && this.#offerWait) this.#armNoInviteTimer();\r\n }\r\n\r\n /**\r\n * Armed at the REGISTER's 200 OK, never at `ua.start()`: on a slow network the WSS alone outlasted the old 5 s,\r\n * and the UA was gone before it ever registered (F3). The INVITE follows the 200 OK by ~0.1 s (§2).\r\n */\r\n #armNoInviteTimer(): void {\r\n this.#clearNoInviteTimer();\r\n const ms = this.provider.quirks?.noInviteAfterRegisterMs;\r\n if (!ms) return;\r\n this.#noInviteTimer = setTimeout(() => {\r\n this.#noInviteTimer = undefined;\r\n this.#offerOver();\r\n }, ms);\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 // ── the decline guard (SPEC §3.4, incoming-call-reliability.md §4.4) ───────\r\n /**\r\n * Whether an offer — or an INVITE — from this caller is a re-offer of a call just declined. The provider re-offers a\r\n * declined call until its ring time ends, and nothing in a re-offer tells it from a new call. The guard used to\r\n * swallow EVERY offer and INVITE for 35 s, whoever called: a new call in that window was lost (CWA-81, F1).\r\n *\r\n * Now it holds only for the declined caller, and only while that call lives: until the backend's `end` for it\r\n * when it announced the call (`noteOffer`) — at most `DECLINE_GUARD_CEILING_MS` — else for the provider's\r\n * fallback window after the decline. Anyone else always rings; so does a caller this cannot name.\r\n */\r\n #isDeclinedCaller(number: string | null): boolean {\r\n const fallbackMs = this.#declineFallbackMs();\r\n if (!fallbackMs || this.#declined.length === 0) return false;\r\n const now = this.now();\r\n this.#declined = this.#declined.filter((declined) => {\r\n const offer = declined.callId === null ? undefined : this.#offers.get(declined.callId);\r\n // Following a call the backend still says is being offered: until its `end`, or the ceiling.\r\n if (offer && offer.endedAt === null) return now - declined.at < DECLINE_GUARD_CEILING_MS;\r\n // No call to follow (none known, or what was known went with the channel): the window.\r\n return now - declined.at < fallbackMs;\r\n });\r\n const caller = toDigits(number ?? '');\r\n return this.#declined.some((declined) => sameNumber(declined.caller, caller));\r\n }\r\n\r\n /** The decline guard's fallback window — and whether there is a guard at all: only a provider that re-offers. */\r\n #declineFallbackMs(): number {\r\n return this.provider.quirks?.declinedCallerGuardMs ?? 0;\r\n }\r\n\r\n /**\r\n * The user declined `call`. Remembers the caller as its INVITE named them, and which offer the backend says that\r\n * is (§4.4). Only what the backend said about THIS call counts: a call from the same caller that ended before\r\n * this one was pushed (`since`) is another call — taking it for this one lost the callback after a second\r\n * decline of the same caller.\r\n * - a live offer from that caller, announced around this call's push or after: the guard follows it until\r\n * its `end`;\r\n * - an offer from that caller that ENDED since this call's push (the first leg of a transfer, a ring-group\r\n * call someone else answered): the fallback window — whatever that caller sends next is not that call, so\r\n * no later `start` is taken for it;\r\n * - neither (its `start` lands after the decline): the fallback window, and a `start` from that caller that\r\n * comes around this call's push is taken as this call's (`noteOffer`).\r\n *\r\n * An INVITE that names nobody (a withheld number) guards nobody. It used to borrow the caller of the latest push,\r\n * which with two offers overlapping is someone else's — and that someone's calls were then ignored.\r\n */\r\n #onDecline(call: SipEngineCall, since: number): void {\r\n if (!this.#declineFallbackMs() || call.direction !== 'inbound') return;\r\n const caller = toDigits(call.remoteNumber);\r\n if (!caller) return; // nobody to recognise: the re-offers ring, which loses nothing\r\n const now = this.now();\r\n // The most recent word on this call — the map is in the order things were last said.\r\n let latest: { readonly callId: string; readonly ended: boolean } | null = null;\r\n for (const [callId, offer] of this.#offers) {\r\n if (!sameNumber(offer.from, caller) || now - offer.at >= OFFER_FORGOTTEN_MS) continue;\r\n if (offer.endedAt === null && offer.at >= since - OFFER_PUSH_SLACK_MS) latest = { callId, ended: false };\r\n else if (offer.endedAt !== null && offer.endedAt >= since) latest = { callId, ended: true };\r\n }\r\n this.#declined.push(\r\n latest && !latest.ended\r\n ? { caller, callId: latest.callId, adopts: false, at: now, since }\r\n : { caller, callId: null, adopts: latest === null, at: now, since },\r\n );\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\r\n if (this.#activeCall) {\r\n session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n return;\r\n }\r\n // A re-offer of the call just declined that reached us anyway — a REGISTER for someone else's offer raced it.\r\n // Declined again; the wait for that other offer's INVITE goes on.\r\n if (this.#isDeclinedCaller(remoteNumberOf(session))) {\r\n debug('INVITE declined: the caller just declined is being offered again');\r\n session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n return;\r\n }\r\n const since = this.#offerWait?.at ?? this.now();\r\n this.#endOfferWait(); // the INVITE came: the wait is over\r\n const call = this.#wrapCall(session, since);\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 offer twice in a heartbeat\r\n * (a push seen on both sockets of a rebuild, two calls at once), and `placeCall` can land on top of\r\n * 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 if (!this.#uaInFlight) {\r\n const inFlight: Promise<JsSipUA> = this.#createUA().finally(() => {\r\n // Only while it is still ours. A teardown (a stop() included) clears the guard itself, and whatever\r\n // holds it by now is a later attempt's — clearing that would let its next caller build a second UA.\r\n if (this.#uaInFlight === inFlight) this.#uaInFlight = null;\r\n });\r\n this.#uaInFlight = inFlight;\r\n }\r\n return this.#uaInFlight;\r\n }\r\n\r\n async #createUA(): Promise<JsSipUA> {\r\n const run = this.#run;\r\n const epoch = this.#uaEpoch;\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 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 // stop() ran while we were awaiting. Building the UA now would REGISTER a phone that is off — the\r\n // one thing a stop has to rule out (see `#run`) — so refuse: whoever wanted it, the phone is stopped.\r\n if (run !== this.#run) throw createError('engine/not-started');\r\n // Let go of while provisioning — its offer ended, or the leak guard gave up (see `#uaEpoch`).\r\n if (epoch !== this.#uaEpoch) throw createError('call/failed', 'the UA was let go of before it was built');\r\n this.#domain = domainOf(credentials);\r\n const config = this.#uaConfig(credentials, factory);\r\n const ua = factory.createUA(config);\r\n /** Whether this UA's WSS ever came up: a failure before that is the one that may mean new credentials. */\r\n let reached = false;\r\n\r\n /*\r\n * Every handler first checks that this is still THE UA. One that has been let go of — by stop(), or\r\n * by an on-demand teardown — keeps emitting for a while: JsSIP holds its transport open up to 2 s for\r\n * transactions in flight, and the REGISTER that dies with it reports `registrationFailed` then. Acted\r\n * on, that took a stopped engine `online`, rang it, and reopened its push socket — or tore down the\r\n * fresh UA that replaced it, the one the next INVITE is coming to. (`registrationFailed` is the one\r\n * partial exception: see its own comment.)\r\n */\r\n ua.on('newRTCSession', ({ session, originator }) => {\r\n if (ua !== this.#ua) {\r\n // Nobody is behind it to answer: decline at once rather than leave the caller ringing out.\r\n if (originator === 'remote') session.terminate({ status_code: 486, reason_phrase: 'Busy Here' });\r\n return;\r\n }\r\n this.#onNewSession(session, originator);\r\n });\r\n ua.on('disconnected', ({ error }) => {\r\n if (ua !== this.#ua) return;\r\n this.#registration = 'connecting'; // JsSIP reconnects, and REGISTERs again once it is back\r\n if (!error) return; // a clean close (our own teardown) is not a failure\r\n // Never reached at all: a geo-routed provider may have moved this browser elsewhere (SPEC §3.1). One UA\r\n // on freshly fetched credentials before this counts as a failure (incoming-call-reliability.md §4.5).\r\n if (!reached && this.#retryOnFreshCredentials('unreachable', ua, credentials)) return;\r\n this.#setState('reconnecting');\r\n this.#reportError('net/transport-failed', error);\r\n });\r\n ua.on('connected', () => {\r\n if (ua !== this.#ua) return;\r\n reached = true;\r\n this.#registration = 'registering'; // JsSIP sends the REGISTER the moment its WSS is up\r\n this.#setState('online');\r\n });\r\n ua.on('registered', () => {\r\n // The 200 OK can land after stop() has let this UA go: dialling then would ring someone from a phone that\r\n // is off, and the waiters it would dial have been failed already (`#teardownUA`).\r\n if (ua !== this.#ua) return;\r\n this.#registration = 'registered';\r\n this.#freshCredentialsTried = false;\r\n this.provider.credentialsWorked?.();\r\n for (const waiter of [...this.#awaitingRegistration]) waiter.dial(ua);\r\n if (this.#registerAgain) {\r\n // This answered a REGISTER sent before the offer now waiting: it needs one sent after it (§4.2).\r\n this.#registerAgain = false;\r\n this.#refreshRegistration(ua);\r\n return;\r\n }\r\n if (this.#offerWait) this.#armNoInviteTimer();\r\n });\r\n ua.on('registrationFailed', (e) => {\r\n /*\r\n * After a stop(), ignored: that UA's failure is no longer this phone's. Within the run it still is,\r\n * even from a UA already let go of — a slow registrar can fail one after the no-INVITE teardown, and\r\n * staying silent then left a phone that never rang, with no error and no fresh key. Only tearing\r\n * down is for the current UA alone: a stale one's replacement is the UA the next INVITE comes to.\r\n */\r\n if (run !== this.#run) return;\r\n const detail = e as { cause?: string; response?: { status_code?: number } | null } | undefined;\r\n // Refused (401/403): the credentials may be stale — and they are cached now (§4.5), so nothing else would\r\n // ever replace them. The provider forgets them; the UA is rebuilt once on fresh ones, quietly — the user\r\n // has nothing to do about a credential the SDK replaces itself.\r\n if (isRefusal(detail) && this.#retryOnFreshCredentials('rejected', ua, credentials)) return;\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 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 if (ua !== this.#ua) return;\r\n if (this.#carriesCall()) {\r\n // A REFRESH failed — one sent for an offer while a consult leg or a just-placed call is on this UA. The\r\n // call does not need the registration; tearing the UA down would end it. Only a dial still waiting for\r\n // this registration is lost: it hears so now.\r\n for (const waiter of [...this.#awaitingRegistration]) waiter.fail();\r\n // Nothing is in flight any more: the next offer sends its REGISTER (left `registering`, every later offer\r\n // only queued one behind an answer that would never come).\r\n this.#registration = 'registered';\r\n // The offer it was sent for keeps its chance: once the line is free, it is registered for (`#legEnded`).\r\n const wait = this.#offerWait;\r\n if (wait) {\r\n this.#pendingOffer = { from: wait.from || null, at: wait.at };\r\n this.#endOfferWait();\r\n }\r\n return;\r\n }\r\n this.#teardownUA();\r\n });\r\n\r\n this.#ua = ua;\r\n this.#registration = 'connecting';\r\n ua.start();\r\n return ua;\r\n }\r\n\r\n /**\r\n * The credentials behind `ua` did not work. The provider hears it either way, with the credentials in question\r\n * (it forgets them unless it has moved on from them already; a provider whose signal shares them renews that\r\n * too); then, if it has new ones to give, the current UA is rebuilt on them — once until a registration works,\r\n * and never under a live call. Whoever waited on the old UA — an offer, a dial — waits on the new one. Returns\r\n * whether it was rebuilt.\r\n *\r\n * This replaces the engine's old `#refreshSignal` (a close-then-reopen of the push socket after ANY failed\r\n * registration): only the provider knows what its credentials and its signal share.\r\n */\r\n #retryOnFreshCredentials(reason: 'rejected' | 'unreachable', ua: JsSipUA, credentials: SipCredentials): boolean {\r\n const fresh = this.provider.credentialsFailed?.(reason, credentials) ?? false;\r\n if (!fresh || ua !== this.#ua || this.#freshCredentialsTried) return false;\r\n if (this.#carriesCall()) return false;\r\n this.#freshCredentialsTried = true;\r\n const run = this.#run;\r\n this.#teardownUA({ replacing: true });\r\n const epoch = this.#uaEpoch;\r\n void this.#ensureUA().catch(() => {\r\n if (run === this.#run && epoch === this.#uaEpoch) this.#teardownUA();\r\n });\r\n return true;\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 /**\r\n * Lets the current UA go — and one still being provisioned (`#uaEpoch`). `replacing`: a successor is being\r\n * built on fresh credentials right away (`#retryOnFreshCredentials`), so the dials and the offer waiting on\r\n * this one carry over to it instead of ending here.\r\n */\r\n #teardownUA({ replacing = false }: { replacing?: boolean } = {}): void {\r\n this.#uaEpoch += 1;\r\n this.#uaInFlight = null;\r\n this.#registration = 'connecting';\r\n this.#registerAgain = false; // a successor REGISTERs after every offer so far, by construction\r\n if (!replacing) {\r\n // A UA let go of before it registered never will: whatever was waiting to dial on it hears so now.\r\n for (const waiter of [...this.#awaitingRegistration]) waiter.fail();\r\n this.#endOfferWait();\r\n }\r\n if (!this.#ua) return;\r\n const ua = this.#ua;\r\n // Detached BEFORE its sessions end: JsSIP reports a terminated leg's end synchronously, inside\r\n // `terminateSessions`, and that end must find no UA left to tear down a second time.\r\n this.#ua = null;\r\n this.#activeCall = null;\r\n this.#consultCall = null;\r\n try {\r\n ua.terminateSessions({ status_code: 487 });\r\n } catch {\r\n /* already stopped */\r\n }\r\n // A hang-up before any ringback leaves JsSIP owing a CANCEL (F6): such a UA keeps its WSS until it is sent.\r\n if (this.#owedCancels.some((owed) => owed.ua === ua)) {\r\n this.#linger(ua);\r\n return;\r\n }\r\n try {\r\n ua.stop();\r\n } catch {\r\n /* already stopped */\r\n }\r\n }\r\n\r\n // ── the CANCEL a hang-up still owes (incoming-call-reliability.md §4.7, F6) ─────────────────────────────\r\n /**\r\n * Watches an outbound leg — a call or a consult leg — from its creation, for the CANCEL JsSIP may end up owing.\r\n *\r\n * JsSIP's `terminate()` between sending the INVITE and the first provisional response cannot cancel yet (RFC 3261\r\n * §9.1): it marks the session canceled, fails it locally with `Canceled`, and sends the CANCEL when a 1xx comes —\r\n * or ACKs and BYEs a late 2xx. Live, that 1xx came 17 s late, the engine had torn the UA down on the call's end, and\r\n * the callee's phone rang on for a caller who was gone: a ghost call. Hence: the INVITE went out (`sending`), no\r\n * provisional above 100 came (`progress`), and it failed locally `Canceled` → the CANCEL is owed, on this UA.\r\n *\r\n * Registered BEFORE the leg's own listeners: its `failed` must be on record before the leg's end tears the UA down.\r\n */\r\n #watchOwedCancel(session: JsSipSession, ua: JsSipUA): void {\r\n let sent = false;\r\n let ringing = false;\r\n session.on('sending', () => (sent = true));\r\n session.on('progress', () => (ringing = true));\r\n session.on('failed', ({ originator, cause }) => {\r\n if (!sent || originator !== 'local' || cause !== 'Canceled') return;\r\n const tx = inviteTransactionOf(session);\r\n /*\r\n * Owed only when JsSIP DEFERRED it. Any 1xx it took (a 100 Trying included) lets it cancel at once, and lingering\r\n * then only waited for the 487 — or the 32 s. JsSIP's own flag says it exactly (a provisional without a To-tag\r\n * leaves the CANCEL deferred too); without it, the transaction still CALLING (no 1xx of any kind); without\r\n * either, no ringback heard.\r\n */\r\n const owes = deferredCancelOf(session) ?? (tx ? tx.state === ICT_CALLING : !ringing);\r\n if (!owes || (tx && !awaitsAnswer(tx))) return; // nothing owed, or its answer has already come\r\n const owed: OwedCancel = { ua, session, tx };\r\n this.#owedCancels.push(owed);\r\n if (tx) this.#followInvite(owed, tx); // unreadable: the UA, if let go of, waits out Timer B\r\n });\r\n }\r\n\r\n /**\r\n * Follows the INVITE an owed CANCEL is waiting on, until it is over. Not just the first one: Zadarma challenges every\r\n * INVITE (407), and JsSIP re-sends it with credentials on a NEW transaction — canceled leg or not — in the same tick\r\n * that completes the first. Releasing on that first transaction stopped the UA two seconds later, and the re-sent\r\n * INVITE's 1xx landed nowhere: the ghost call again (found driving the real JsSIP). So once one is over, the leg's\r\n * current transaction is read again a moment later, and followed while it still waits for an answer.\r\n */\r\n #followInvite(owed: OwedCancel, tx: InviteTransaction): void {\r\n const settle = (): void => {\r\n if (awaitsAnswer(tx)) return;\r\n tx.removeListener?.('stateChanged', settle);\r\n queueMicrotask(() => {\r\n const latest = inviteTransactionOf(owed.session);\r\n if (latest && latest !== tx && awaitsAnswer(latest)) {\r\n owed.tx = latest;\r\n this.#followInvite(owed, latest);\r\n return;\r\n }\r\n this.#owedCancels = this.#owedCancels.filter((other) => other !== owed);\r\n this.#releaseIfSettled(owed.ua);\r\n });\r\n };\r\n tx.on('stateChanged', settle);\r\n }\r\n\r\n /**\r\n * Lets `ua` go while it owes a CANCEL: un-REGISTERed at once, as any teardown, but connected until every INVITE it\r\n * owes is over — COMPLETED (a final non-2xx: the 487 after the late 1xx's CANCEL), ACCEPTED (a late 2xx, which\r\n * JsSIP ACKs and BYEs) or TERMINATED (Timer B, a transport error) — and never longer than Timer B. Detached: the\r\n * engine has already moved on (a push or a dial builds a fresh UA), so no timer of its own, no teardown and no\r\n * \"is a call on it\" ever sees this one, and an INVITE reaching it is declined like any let-go UA's.\r\n */\r\n #linger(ua: JsSipUA): void {\r\n if (this.#lingering.has(ua)) return;\r\n // The cap first: whatever happens below, this UA is tracked, and stopped by Timer B at the latest.\r\n this.#lingering.set(ua, setTimeout(() => this.#release(ua), INVITE_TRANSACTION_MAX_MS));\r\n /*\r\n * JsSIP's un-REGISTER is a no-op while a REGISTER of ours is still unanswered (a refresh resets its \"registered\"\r\n * flag, `#refreshRegistration`) — and that REGISTER's 200 would then leave this UA registered, where the INVITE of\r\n * the offer it was sent for would come, only to be declined. So a registration that lands here is undone at once.\r\n */\r\n ua.on('registered', () => {\r\n if (this.#lingering.has(ua)) this.#unregister(ua);\r\n });\r\n this.#unregister(ua);\r\n this.#releaseIfSettled(ua);\r\n }\r\n\r\n #unregister(ua: JsSipUA): void {\r\n try {\r\n ua.unregister();\r\n } catch {\r\n /* a UA that cannot un-REGISTER still stops at the cap, which un-REGISTERs it */\r\n }\r\n }\r\n\r\n /**\r\n * Once nothing is owed on a lingering UA, it stops — on the next tick: a late 2xx is ACKed and BYEd in the very\r\n * tick that settled it, and JsSIP's own `stop()` then keeps the transport ≤ 2 s for that BYE's answer.\r\n */\r\n #releaseIfSettled(ua: JsSipUA): void {\r\n if (!this.#lingering.has(ua) || this.#owedCancels.some((owed) => owed.ua === ua)) return;\r\n setTimeout(() => this.#release(ua), 0);\r\n }\r\n\r\n #release(ua: JsSipUA): void {\r\n const cap = this.#lingering.get(ua);\r\n if (cap === undefined) return;\r\n clearTimeout(cap);\r\n this.#lingering.delete(ua);\r\n this.#owedCancels = this.#owedCancels.filter((owed) => owed.ua !== ua);\r\n try {\r\n ua.stop();\r\n } catch {\r\n /* already stopped */\r\n }\r\n }\r\n\r\n /**\r\n * Place a call once the current 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(target: string, run: number): Promise<SipEngineCall> {\r\n const options = { mediaConstraints: buildAudioConstraints(this.#inputDeviceId), pcConfig: PC_CONFIG };\r\n // After a stop() the phone is off (`run` is the dial's, read before it awaited anything); otherwise it is\r\n // this call that could not be made.\r\n const refusal = (): SoftphoneError => createError(run === this.#run ? 'call/failed' : 'engine/not-started');\r\n // Wrapped the moment the INVITE exists: from then on this UA carries a call (`#carriesCall`).\r\n const dial = (registered: JsSipUA): SipEngineCall => {\r\n const session = registered.call(target, options);\r\n this.#watchOwedCancel(session, registered); // first: see its own comment\r\n return this.#wrapCall(session);\r\n };\r\n const ua = this.#ua;\r\n // Let go of between being built and now — a stop() among the ways: nothing will ever register for this dial.\r\n if (!ua || run !== this.#run) return Promise.reject(refusal());\r\n if (ua.isRegistered()) return Promise.resolve(dial(ua));\r\n return new Promise<SipEngineCall>((resolve, reject) => {\r\n /*\r\n * Waits for whichever UA is current when the 200 OK comes — the one built on fresh credentials when\r\n * this one's were refused, included (`#retryOnFreshCredentials`). Settles exactly once, leaving the set\r\n * as it does: the same UA registering AGAIN — its transport dropped mid-call and JsSIP recovered it, or\r\n * a refresh for an offer — must not dial the number a second time. And it must settle at all: a UA let\r\n * go of before it registered left the caller waiting forever, so `#teardownUA` fails it.\r\n */\r\n const waiter: RegistrationWaiter = {\r\n dial: (registered) => {\r\n this.#awaitingRegistration.delete(waiter);\r\n // A throw here would otherwise go up into JsSIP's emitter and leave this dial waiting forever.\r\n try {\r\n resolve(dial(registered));\r\n } catch (cause) {\r\n reject(cause);\r\n }\r\n },\r\n fail: () => {\r\n this.#awaitingRegistration.delete(waiter);\r\n reject(refusal());\r\n },\r\n };\r\n this.#awaitingRegistration.add(waiter);\r\n });\r\n }\r\n\r\n /**\r\n * `since`, for an inbound call: when its push came (its INVITE, when none was seen) — what `#onDecline` needs to\r\n * tell this call from the caller's others.\r\n */\r\n #wrapCall(session: JsSipSession, since = this.now()): SipEngineCall {\r\n const call: SipEngineCall = 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.#onDecline(call, since),\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.#legEnded();\r\n }\r\n });\r\n return call;\r\n }\r\n\r\n /**\r\n * A leg ended. The UA goes if nothing is left on it (`#maybeTeardownUA`); and once no leg at all is left, an\r\n * offer kept through the call (`#pendingOffer`, §4.3) gets its REGISTER — the line is free now.\r\n */\r\n #legEnded(): void {\r\n const pending = this.#activeCall || this.#consultCall ? null : this.#takePendingOffer();\r\n this.#maybeTeardownUA();\r\n if (pending) void this.#onOffer(pending.from, pending.at);\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 this.#watchOwedCancel(consultSession, ua); // first: a consult hung up before it rings owes a CANCEL too (F6)\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.#legEnded();\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 the no-INVITE timer — which then re-armed on EVERY push, including the\r\n * `answered` pushes Zadarma sends around other account activity, so it could fire minutes after a\r\n * transfer with no `#activeCall` and no idea a consult leg was mid-handoff. Every teardown an offer\r\n * asks for (its timers, its `offer-ended`) routes through here rather than checking `#activeCall` alone.\r\n *\r\n * A dial on its way (`#dialing`) counts too: its UA may not even exist yet, and an offer ending must not\r\n * take it. So does an offer still waiting for its INVITE: mid-transfer, with only the consult leg left, an\r\n * offer registers on this UA — and the consult leg ending used to tear it down and drop that offer, its call\r\n * ringing nowhere until the provider offered it again ~30 s later. The wait's own ending (its INVITE, its\r\n * `offer-ended`, the no-INVITE timer, the leak guard) clears `#offerWait` before it calls this, so nothing keeps\r\n * the UA past those (incoming-call-reliability.md §4.3).\r\n */\r\n #maybeTeardownUA(): void {\r\n if (this.provider.registrationMode !== 'on-demand') return;\r\n if (this.#activeCall || this.#consultCall || this.#dialing > 0 || this.#offerWait) return;\r\n this.#teardownUA();\r\n }\r\n\r\n /**\r\n * A live leg is on the current UA: the call or a consult leg. An outbound call counts from the moment its INVITE\r\n * is created — `#callWhenRegistered` wraps it there and then — so no failure in between can end it unseen.\r\n */\r\n #carriesCall(): boolean {\r\n return this.#activeCall !== null || this.#consultCall !== null;\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 IncomingSignalEvent,\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 * A cached key is treated as dead this long before its `key_expires_at`, so a clock a little ahead of the\r\n * backend's never presents one Zadarma has already let go of. The refresh normally replaced it 2 h earlier.\r\n */\r\nconst KEY_EXPIRY_MARGIN_MS = 60_000;\r\n/**\r\n * Floor between push-socket rebuilds forced by a refused registration (`ZadarmaPushSignal.rekey`), so a\r\n * registrar refusing every attempt cannot become a tight loop against our backend and Zadarma's. The value\r\n * the engine used for the same rebuild before it moved here (incoming-call-reliability.md §4.5).\r\n */\r\nconst REKEY_DEBOUNCE_MS = 30_000;\r\n\r\n/**\r\n * A rebuild is make-before-break (presence-client-reporting.md §4.1, T8): the replacement socket gets this long\r\n * to connect before it is abandoned and the old one kept. Generous — a rebuild measured ~0.5 s live — but\r\n * bounded, so one stuck handshake cannot wedge every later rebuild behind the overlap guard.\r\n */\r\nconst REBUILD_CONNECT_TIMEOUT_MS = 10_000;\r\n/** After a FAILED rebuild, when to try again. While the tab is hidden the next 20 s background tick also\r\n * retries; this is what keeps a visible tab (whose only other trigger is the 70 h key refresh) from waiting. */\r\nconst REBUILD_RETRY_MS = 30_000;\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-offers a declined call until its ring time ends (~30 s by default): the declined caller's offers\r\n // are ignored while that call lives — this long after the decline when the backend cannot say (§3.4,\r\n // incoming-call-reliability.md §4.4, Q1).\r\n declinedCallerGuardMs: 35_000,\r\n // The INVITE follows the REGISTER's 200 OK by ~0.1 s. Nothing 10 s after it: no call is coming to this UA.\r\n noInviteAfterRegisterMs: 10_000,\r\n // A leak guard only: an offer still waiting for its INVITE 2 min after its push is abandoned (§4.3).\r\n offerWaitMaxMs: 120_000,\r\n // Deprecated names of the two above, same values: this object is exported, and 0.5.1 is a patch. Never read.\r\n rejectGuardMs: 35_000,\r\n noInviteTeardownMs: 10_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 /**\r\n * `update` hands over its payload — the call it is about (`readPush`). The others carry nothing we read.\r\n */\r\n on(event: 'connect' | 'disconnect' | 'connect_error' | 'update', cb: (payload?: unknown) => 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 * Called at start and ahead of the key's expiry, not per call (`ZadarmaCredentials`).\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: kept in memory for the key's lifetime, never persisted (`ZadarmaCredentials`).\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 * What a push says, as the engine's `IncomingSignalEvent` (incoming-call-reliability.md §2, §4.1).\r\n *\r\n * The payload carries the call — `{\"action\":\"incoming\",\"callerid\":\"+34…\",\"callername\":\"…\",\"calleddid\":\"34…\",\r\n * \"sip\":\"…\"}` — and its `action` is what tells an offer from everything else the gateway sends: `answered`\r\n * (this offer to the extension is over, whichever way), and pushes about other activity on the account.\r\n * Before this, every push registered a UA, `answered` after each hang-up included (F2).\r\n *\r\n * Read defensively: Socket.IO hands the payload over as an object or as JSON text, and anything this cannot\r\n * read is `other` — logged, never a registration.\r\n */\r\nexport function readPush(payload: unknown): IncomingSignalEvent {\r\n let data: unknown = payload;\r\n if (typeof data === 'string') {\r\n try {\r\n data = JSON.parse(data);\r\n } catch {\r\n return { kind: 'other', detail: 'unreadable payload' };\r\n }\r\n }\r\n if (!data || typeof data !== 'object') return { kind: 'other', detail: 'no payload' };\r\n const { action, callerid } = data as { action?: unknown; callerid?: unknown };\r\n let from: string | null = null;\r\n if (typeof callerid === 'string' && callerid) from = callerid;\r\n else if (typeof callerid === 'number') from = String(callerid);\r\n if (action === 'incoming') return { kind: 'offer', from };\r\n if (action === 'answered') return { kind: 'offer-ended', from };\r\n // Only the action, never the payload: the rest is somebody's phone number.\r\n return { kind: 'other', detail: typeof action === 'string' ? `action \"${action}\"` : 'no action' };\r\n}\r\n\r\n/** `key_expires_at` as epoch ms, or `null` when absent or unreadable — then the key is kept until refused. */\r\nfunction expiryOf(key: ZadarmaKeyResponse): number | null {\r\n if (!key.keyExpiresAt) return null;\r\n const at = new Date(key.keyExpiresAt).getTime();\r\n return Number.isFinite(at) ? at : null;\r\n}\r\n\r\n/**\r\n * The WebRTC key and the SIP credentials it buys — fetched once, kept IN MEMORY for the key's lifetime, and\r\n * shared by the push socket and `provision()` (incoming-call-reliability.md §4.5). Before this, every push\r\n * fetched the key from our backend and the credentials from Zadarma before it could REGISTER: 0.6 s on a good\r\n * network, ~10 s on a slow one, and a call lost whenever our backend was slow or restarting (F4). Now a push\r\n * makes no request at all, and neither does a hidden-tab rebuild of the push socket.\r\n *\r\n * Fetched again only when they stop being good: the key nears expiry (`renew()`, in the background, by the push\r\n * socket's refresh), the registrar refuses them (`forget('all')`), or their WSS cannot be reached\r\n * (`forget('sip')` — Zadarma geo-routes the SIP domain to the browser's location, SPEC §3.1). Never written to\r\n * any storage: a reload starts from nothing.\r\n */\r\nexport class ZadarmaCredentials {\r\n #key: { readonly value: ZadarmaKeyResponse; readonly expiresAt: number | null } | null = null;\r\n #keyInFlight: Promise<ZadarmaKeyResponse> | null = null;\r\n #sip: { readonly forKey: string; readonly pass: string; readonly domain: string } | null = null;\r\n #sipInFlight: { readonly forKey: string; readonly promise: Promise<{ pass: string; domain: string }> } | null =\r\n null;\r\n /**\r\n * Bumped by `forget`, one per half: a fetch begun before it must not put back what was just forgotten — a\r\n * refused credential that comes back from a slow request is still refused.\r\n */\r\n #keyGeneration = 0;\r\n #sipGeneration = 0;\r\n /**\r\n * The SIP credentials were already refetched once because their WSS could not be reached: if the fresh ones\r\n * cannot reach it either, the network is the problem, and asking Zadarma again would not change it. Re-armed by\r\n * a registration that works (`worked`), and by forgetting everything (a refusal, a stop).\r\n */\r\n #unreachableRefetched = false;\r\n\r\n constructor(private readonly deps: ZadarmaDeps) {}\r\n\r\n /** What is in memory right now — both halves, for one key — or `null`. Never fetches. */\r\n current(): { readonly key: ZadarmaKeyResponse; readonly pass: string; readonly domain: string } | null {\r\n const key = this.#key?.value;\r\n const sip = this.#sip;\r\n return key && sip && sip.forKey === key.key ? { key, pass: sip.pass, domain: sip.domain } : null;\r\n }\r\n\r\n /** Their WSS could not be reached: forget the SIP half — once, until `worked()`. Whether it was forgotten. */\r\n forgetUnreachable(): boolean {\r\n if (this.#unreachableRefetched) return false;\r\n this.forget('sip');\r\n this.#unreachableRefetched = true;\r\n return true;\r\n }\r\n\r\n /** A registration worked with what is in memory. */\r\n worked(): void {\r\n this.#unreachableRefetched = false;\r\n }\r\n\r\n /** The cached key while it is alive; otherwise a fresh one (and, in the background, its SIP credentials). */\r\n key(): Promise<ZadarmaKeyResponse> {\r\n const cached = this.#key;\r\n if (cached && (cached.expiresAt === null || Date.now() < cached.expiresAt - KEY_EXPIRY_MARGIN_MS)) {\r\n return Promise.resolve(cached.value);\r\n }\r\n return this.#fetchKey();\r\n }\r\n\r\n /** A new key now, whatever is cached: the refresh ahead of expiry. The old one serves until this one lands. */\r\n renew(): Promise<ZadarmaKeyResponse> {\r\n return this.#fetchKey();\r\n }\r\n\r\n /** The key and the SIP credentials it bought, from memory when they are there. */\r\n async sip(): Promise<{ readonly key: ZadarmaKeyResponse; readonly pass: string; readonly domain: string }> {\r\n const key = await this.key();\r\n const { pass, domain } = await this.#sipFor(key);\r\n return { key, pass, domain };\r\n }\r\n\r\n /** Synchronous up to the request, so a prefetch and a `provision()` racing it share one exchange. */\r\n #sipFor(key: ZadarmaKeyResponse): Promise<{ pass: string; domain: string }> {\r\n const cached = this.#sip;\r\n if (cached && cached.forKey === key.key) return Promise.resolve({ pass: cached.pass, domain: cached.domain });\r\n const inFlight = this.#sipInFlight;\r\n if (inFlight && inFlight.forKey === key.key) return inFlight.promise;\r\n const generation = this.#sipGeneration;\r\n let request: Promise<{ pass: string; domain: string }>;\r\n try {\r\n request = this.deps.exchangeWebphoneData(key.key, key.sip);\r\n } catch (cause) {\r\n request = Promise.reject(cause instanceof Error ? cause : new Error(String(cause)));\r\n }\r\n const promise = request.then((data) => {\r\n if (generation === this.#sipGeneration) this.#sip = { forKey: key.key, pass: data.pass, domain: data.domain };\r\n return data;\r\n });\r\n const entry = { forKey: key.key, promise };\r\n this.#sipInFlight = entry;\r\n // Cleared once settled, whichever way: a failure is not cached, and the next call tries again.\r\n void promise\r\n .finally(() => {\r\n if (this.#sipInFlight === entry) this.#sipInFlight = null;\r\n })\r\n .catch(() => {});\r\n return promise;\r\n }\r\n\r\n /**\r\n * Drop what stopped working. `all`: the registrar refused the credentials, so the key behind them may be the\r\n * stale part too. `sip`: their WSS could not be reached — the key is fine, the datacenter may not be.\r\n */\r\n forget(what: 'all' | 'sip'): void {\r\n this.#sipGeneration += 1;\r\n this.#sip = null;\r\n this.#sipInFlight = null;\r\n if (what === 'sip') return;\r\n this.#keyGeneration += 1;\r\n this.#key = null;\r\n this.#keyInFlight = null;\r\n this.#unreachableRefetched = false; // everything comes fresh: an unreachable one may be refetched once again\r\n }\r\n\r\n #fetchKey(): Promise<ZadarmaKeyResponse> {\r\n if (this.#keyInFlight) return this.#keyInFlight; // one request, however many are waiting for it\r\n const generation = this.#keyGeneration;\r\n const inFlight = this.deps.fetchKey().then((value) => {\r\n if (generation === this.#keyGeneration) {\r\n this.#key = { value, expiresAt: expiryOf(value) };\r\n // Its SIP credentials straight away, in the background: no call should wait for them (§4.5). A failure\r\n // here costs nothing — the next `sip()` asks again, and reports it.\r\n void this.#sipFor(value).catch(() => {});\r\n }\r\n return value;\r\n });\r\n this.#keyInFlight = inFlight;\r\n void inFlight\r\n .finally(() => {\r\n if (this.#keyInFlight === inFlight) this.#keyInFlight = null;\r\n })\r\n .catch(() => {});\r\n return inFlight;\r\n }\r\n}\r\n\r\n/**\r\n * The push socket as an `IncomingSignal`. Its `connect()` gets the key (`ZadarmaCredentials`) and opens the\r\n * socket; a Zadarma `update` becomes an `IncomingSignalEvent` the engine reacts to (SPEC §3.2–3.3), read from\r\n * the push's payload (`readPush`).\r\n *\r\n * The engine deliberately does NOT ring on this signal — it rings on the SIP INVITE. An `offer` 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 /** The replacement socket of a rebuild in flight, not yet current. */\r\n #candidate: ZadarmaPushSocket | null = null;\r\n /** Abandons the rebuild in flight (closing its candidate). */\r\n #abortRebuild: (() => void) | null = null;\r\n #rebuilding = false;\r\n #retryTimer: ReturnType<typeof setTimeout> | undefined;\r\n #unwatchBackground: (() => void) | undefined;\r\n #closed = false;\r\n /**\r\n * Bumped by `disconnect()`. An open or a rebuild still awaiting its key when the signal is disconnected\r\n * must build nothing afterwards — and `#closed` alone cannot say so once a later `connect()` (stop, then\r\n * start again) has reset it: the late socket was built beside the new one, and nothing ever closed it.\r\n */\r\n #run = 0;\r\n /**\r\n * The key is due for its refresh (`#armRefresh`): the next rebuild fetches a new one instead of reusing the\r\n * cached one — and keeps trying, on every retry and every hidden-tab tick, until one arrives.\r\n */\r\n #renewDue = false;\r\n /** A rebuild asked for (`rekey`) while another was in flight: it runs once that one ends. */\r\n #rekeyQueued = false;\r\n #rekeyAfter = 0;\r\n readonly #listeners = new Set<(event: IncomingSignalEvent) => void>();\r\n readonly #connectionListeners = new Set<(connected: boolean) => void>();\r\n\r\n /**\r\n * `credentials` is shared with the provider's `provision()`: one key for the push socket and the SIP\r\n * credentials, fetched once (§4.5). A signal on its own gets a cache of its own.\r\n */\r\n constructor(\r\n private readonly deps: ZadarmaDeps,\r\n private readonly credentials: ZadarmaCredentials = new ZadarmaCredentials(deps),\r\n ) {}\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. The hidden-tab\r\n // rebuild every 20 s therefore reuses the cached key: no request per tick (§4.5).\r\n this.#unwatchBackground ??= this.deps.watchBackgroundReconnect?.(() => void this.#reopen());\r\n await this.#open();\r\n }\r\n\r\n /**\r\n * The registrar refused credentials bought with this socket's key, and the provider has forgotten them: the\r\n * socket is rebuilt on a fresh key, make-before-break like every rebuild (`#reopen`).\r\n *\r\n * Debounced: a registrar refusing every attempt must not become a tight loop against our backend and\r\n * Zadarma's. This rebuild used to be the engine's (`#refreshSignal`, a close-then-open); it lives here now\r\n * because only the provider knows the push socket and the SIP credentials share one key.\r\n */\r\n rekey(): void {\r\n if (this.#closed || !this.#socket) return; // nothing open to rebuild: the next connect() fetches anyway\r\n const now = Date.now();\r\n if (now < this.#rekeyAfter) return;\r\n this.#rekeyAfter = now + REKEY_DEBOUNCE_MS;\r\n if (this.#rebuilding) this.#rekeyQueued = true; // the one in flight may hold the refused key\r\n else void this.#reopen();\r\n }\r\n\r\n async #open(): Promise<void> {\r\n const run = this.#run;\r\n const { key, sip, keyExpiresAt } = await this.credentials.key();\r\n if (run !== this.#run) return; // disconnected meanwhile (see `#run`)\r\n\r\n const socket = this.deps.createPushSocket(buildPushUrl(key, sip));\r\n this.#socket = socket;\r\n this.#attach(socket);\r\n this.#armRefresh(keyExpiresAt);\r\n }\r\n\r\n /**\r\n * Wires one socket's events. `this.#socket` is read when an event FIRES, not when it is wired: a socket that\r\n * has been replaced must stop driving connectivity (our own `close()` of it emits `disconnect`, which would\r\n * otherwise flip the state to offline right after a perfectly healthy swap). `update` is the exception — it is\r\n * forwarded from any socket not yet closed, because a push seen twice is harmless (the engine ignores an offer\r\n * mid-call, and a second one only sends one more REGISTER) while one seen by neither is a missed call.\r\n *\r\n * With `hooks`, the socket is a rebuild's candidate: its first `connect` or failure goes to the rebuild instead\r\n * of changing the connection state (it is not current yet).\r\n */\r\n #attach(socket: ZadarmaPushSocket, hooks?: { onConnect(): void; onFailure(): void }): void {\r\n socket.on('connect', () => {\r\n if (hooks && socket === this.#candidate) return hooks.onConnect();\r\n if (socket !== this.#socket) return;\r\n this.#setConnected(true);\r\n this.#startPings(socket);\r\n });\r\n socket.on('update', (payload) => this.#emit(readPush(payload)));\r\n // Both mean the same thing to us: no INVITE can arrive right now.\r\n const lost = (): void => {\r\n if (hooks && socket === this.#candidate) return hooks.onFailure();\r\n if (socket !== this.#socket) return;\r\n this.#stopPings();\r\n this.#setConnected(false);\r\n };\r\n socket.on('disconnect', lost);\r\n socket.on('connect_error', lost);\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 * It is also the refresh of the shared credentials (incoming-call-reliability.md §4.5): the new key replaces\r\n * the cached one, and its SIP credentials are fetched with it, in the background — no call waits for either.\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(() => {\r\n this.#renewDue = true;\r\n void this.#reopen();\r\n }, ms);\r\n }\r\n\r\n /**\r\n * Replaces the push socket with a fresh one — MAKE-BEFORE-BREAK (presence-client-reporting.md §4.1, T8).\r\n *\r\n * Why it must happen at all: Zadarma's gateway decides the extension is alive from the raw Engine.IO ping, which\r\n * a hidden tab's throttled timers silently stop sending, so while the tab is hidden the socket is rebuilt every\r\n * 20 s (`push-socket-reliability.md` §5.3), and once before the key expires. Why it must not leave a gap: the\r\n * old close-then-reopen reported `connected = false` for the ~0.5 s in between, which took the softphone\r\n * `offline`, tore down the presence socket and left Zadarma with no socket to push an incoming call to.\r\n *\r\n * Now the OLD socket stays up and stays current until the NEW one has actually connected; only then do we swap,\r\n * move the pings and close the old one. A healthy rebuild never changes `connected`. A failed one — the key fetch\r\n * throws, the socket errors, or it has not connected within `REBUILD_CONNECT_TIMEOUT_MS` — keeps the old socket\r\n * and retries, instead of leaving the tab disconnected. Zadarma does not evict the older of two sockets for one\r\n * sip (multi-tab-leader-election.md §1), so the overlap is safe.\r\n *\r\n * The key comes from the shared cache — a hidden-tab tick makes no request — unless it is due for its refresh\r\n * (`#renewDue`) or has been forgotten after a refused registration (`rekey`).\r\n */\r\n async #reopen(): Promise<void> {\r\n if (this.#closed || this.#rebuilding) return; // one rebuild at a time: a slow one must not stack with the next tick\r\n\r\n // Nothing to be gapless about — never opened, or an earlier failure left no socket: a plain open.\r\n if (!this.#socket) {\r\n try {\r\n await this.#open();\r\n } catch {\r\n // Still no socket, and `connected` says so: the engine can surface it instead of the phone quietly never\r\n // ringing again.\r\n }\r\n return;\r\n }\r\n\r\n this.#rebuilding = true;\r\n const run = this.#run;\r\n try {\r\n const { key, sip, keyExpiresAt } = await (this.#renewDue ? this.credentials.renew() : this.credentials.key());\r\n if (run !== this.#run) return; // disconnected meanwhile (see `#run`)\r\n this.#renewDue = false; // the new key is in the cache, whatever becomes of this socket\r\n const swapped = await new Promise<boolean>((resolve) => {\r\n const socket = this.deps.createPushSocket(buildPushUrl(key, sip));\r\n this.#candidate = socket;\r\n let settled = false;\r\n const finish = (ok: boolean): void => {\r\n if (settled) return;\r\n settled = true;\r\n clearTimeout(timer);\r\n this.#abortRebuild = null;\r\n // A candidate that connected and dropped again in the same breath is not worth swapping in.\r\n if (ok && socket.connected) {\r\n this.#swapIn(socket, keyExpiresAt);\r\n } else {\r\n this.#candidate = null;\r\n socket.close();\r\n }\r\n resolve(ok && socket.connected);\r\n };\r\n const timer = setTimeout(() => finish(false), REBUILD_CONNECT_TIMEOUT_MS);\r\n this.#abortRebuild = () => finish(false);\r\n this.#attach(socket, { onConnect: () => finish(true), onFailure: () => finish(false) });\r\n });\r\n if (!swapped) this.#armRetry();\r\n } catch {\r\n this.#armRetry(); // the key fetch failed: the old socket is untouched\r\n } finally {\r\n this.#rebuilding = false;\r\n }\r\n if (this.#rekeyQueued) {\r\n this.#rekeyQueued = false;\r\n void this.#reopen(); // the rebuild that just ended may have used the key the registrar refused\r\n }\r\n }\r\n\r\n /**\r\n * The moment of the swap — synchronous, called from the candidate's own `connect` event so nothing can run\r\n * between \"the new socket is up\" and \"it is the current one\". Order matters: `#socket` moves first, so the old\r\n * socket's `disconnect` (our own `close()` of it, just below) is ignored by `#attach`.\r\n */\r\n #swapIn(socket: ZadarmaPushSocket, keyExpiresAt: string | undefined): void {\r\n const previous = this.#socket;\r\n this.#socket = socket;\r\n this.#candidate = null;\r\n clearTimeout(this.#retryTimer);\r\n this.#retryTimer = undefined;\r\n this.#stopPings(); // the old socket's pings end here...\r\n this.#startPings(socket); // ...and the new one's begin\r\n this.#setConnected(true); // a no-op when the old socket was healthy — the whole point\r\n previous?.close();\r\n this.#armRefresh(keyExpiresAt);\r\n }\r\n\r\n #armRetry(): void {\r\n if (this.#closed) return;\r\n clearTimeout(this.#retryTimer);\r\n this.#retryTimer = setTimeout(() => void this.#reopen(), REBUILD_RETRY_MS);\r\n }\r\n\r\n /**\r\n * Closes everything — and forgets the credentials: this is how the engine stops, and a phone that is off holds\r\n * no SIP password. The next `connect()` fetches them again.\r\n */\r\n disconnect(): void {\r\n this.#closed = true;\r\n this.#run += 1;\r\n this.#renewDue = false;\r\n this.#rekeyQueued = false;\r\n this.#rekeyAfter = 0;\r\n this.credentials.forget('all');\r\n this.#stopPings();\r\n clearTimeout(this.#refreshTimer);\r\n this.#refreshTimer = undefined;\r\n clearTimeout(this.#retryTimer);\r\n this.#retryTimer = undefined;\r\n this.#abortRebuild?.(); // a rebuild in flight closes its candidate and swaps nothing in\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: (event: IncomingSignalEvent) => 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(event: IncomingSignalEvent): void {\r\n for (const listener of [...this.#listeners]) listener(event);\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 * One key and one set of SIP credentials for the push socket and every registration (§4.5). Its \"unreachable\r\n * refetched once\" mark lives there too, so a stop — which forgets the credentials — clears it as well.\r\n */\r\n readonly #credentials: ZadarmaCredentials;\r\n readonly #signal: ZadarmaPushSignal;\r\n\r\n constructor(deps: ZadarmaDeps) {\r\n this.#credentials = new ZadarmaCredentials(deps);\r\n this.#signal = new ZadarmaPushSignal(deps, this.#credentials);\r\n this.incomingSignal = this.#signal;\r\n }\r\n\r\n /**\r\n * The SIP credentials, from memory (incoming-call-reliability.md §4.5). They were fetched when the push socket\r\n * opened — the key from our backend, then `{ pass, domain }` from Zadarma, in the browser for the geo-routing\r\n * (SPEC §3.1) — so a push goes straight to the WSS and the REGISTER. Fetched here only when they are not there:\r\n * the background fetch failed, or they were forgotten (`credentialsFailed`).\r\n */\r\n async provision(): Promise<SipCredentials> {\r\n return toSipCredentials(await this.#credentials.sip());\r\n }\r\n\r\n /**\r\n * `rejected` (401/403): the key may be the stale part, so both are forgotten, and the push socket — which\r\n * holds the same key — is rebuilt on a fresh one. `unreachable`: only the SIP credentials, and only once\r\n * until a registration works: Zadarma may have moved this browser to another datacenter (SPEC §3.1).\r\n *\r\n * Only if `credentials` — the ones that failed — are still what is in memory: a UA let go of earlier can report\r\n * late, when the cache has already moved on to fresh ones, and those must not be thrown away for it.\r\n */\r\n credentialsFailed(reason: 'rejected' | 'unreachable', credentials?: SipCredentials): boolean {\r\n if (credentials && !this.#holds(credentials)) return true; // already replaced (or being fetched)\r\n if (reason === 'rejected') {\r\n this.#credentials.forget('all');\r\n this.#signal.rekey();\r\n return true;\r\n }\r\n return this.#credentials.forgetUnreachable();\r\n }\r\n\r\n credentialsWorked(): void {\r\n this.#credentials.worked();\r\n }\r\n\r\n #holds(credentials: SipCredentials): boolean {\r\n const current = this.#credentials.current();\r\n if (!current) return false;\r\n const held = toSipCredentials(current);\r\n return (\r\n held.uri === credentials.uri && held.authUser === credentials.authUser && held.password === credentials.password\r\n );\r\n }\r\n}\r\n\r\n/** The key and its webphone data as the engine takes them. The registrar is the geo-routed `domain` (SPEC §3.1). */\r\nfunction toSipCredentials({ key, pass, domain }: { key: ZadarmaKeyResponse; pass: string; domain: string }): SipCredentials {\r\n return {\r\n uri: `sip:${key.sip}@${domain}`,\r\n authUser: key.sip,\r\n password: pass,\r\n wsUrl: `wss://${domain}:4443`,\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 key fetch failed with an HTTP status. `status` (and a `Retry-After`, as `retryAfterMs`) is what tells a\r\n * passing failure — a 5xx, a 429 — from a refusal, so a failed start knows whether to retry on its own\r\n * (incoming-call-reliability.md §4.6). Read structurally by `transientStartFailure`, never by type.\r\n */\r\nclass KeyFetchError extends Error {\r\n constructor(\r\n message: string,\r\n readonly status: number,\r\n readonly retryAfterMs?: number,\r\n ) {\r\n super(message);\r\n }\r\n}\r\n\r\n/** `Retry-After` as ms: delta-seconds or an HTTP date. `undefined` when absent or unreadable. */\r\nfunction retryAfterMsOf(res: Response): number | undefined {\r\n const value = res.headers?.get?.('retry-after');\r\n if (!value) return undefined;\r\n const seconds = Number(value);\r\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\r\n const at = Date.parse(value);\r\n return Number.isFinite(at) ? Math.max(0, at - Date.now()) : undefined;\r\n}\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's domain only, like `SoftphoneConfig.apiBase`: no path, the SDK builds every route. */\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 * `/phone/webrtc/key` at the root of the domain, authenticated with whatever `tokenSource` returns:\r\n * the CRM's own session, or a partner's voice token (SPEC §6.6, hop ③). The route stays at the root\r\n * for both, rather than moving under `/api/v1` as SPEC §6.6 first planned\r\n * (docs/specs/softphone-voice-token-allowlist.md).\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\r\n ? new UnauthorizedKeyError(message)\r\n : new KeyFetchError(message, res.status, retryAfterMsOf(res));\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`. It runs at start, ahead of the\r\n * key's expiry and after a refused registration (incoming-call-reliability.md §4.5), never per call — so a\r\n * token revoked mid-session must not fail the one fetch that would have recovered it.\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. Handlers\r\n * are handed to socket.io as they are, so `update`'s payload — the call it is about — reaches them.\r\n */\r\nexport function createPushSocket(url: string): ZadarmaPushSocket {\r\n const handlers = new Map<string, Array<(payload?: unknown) => void>>();\r\n let client: {\r\n on(e: string, cb: (payload?: unknown) => 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 { normalizeApiBase } from '../core/api-base.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, and what `createSoftphone` builds by\r\n * default from `apiBase` when no `provider` is given (`core/default-provider.ts`).\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 (at start, ahead of the key's expiry, after a refused\r\n // registration — incoming-call-reliability.md §4.5) would mint a token of its own each time.\r\n // Normalised here too, not only in `createSoftphone`: a host may build this engine itself and pass it\r\n // as `provider`, and that `apiBase` never goes through `createSoftphone`.\r\n const provider = new ZadarmaProvider(\r\n makeZadarmaBrowserDeps({\r\n ...config,\r\n apiBase: normalizeApiBase(config.apiBase),\r\n tokenSource: cachedTokenSource(config.tokenSource),\r\n }),\r\n );\r\n return new SipCallEngine(provider, loadJsSipFactory);\r\n}\r\n"]}
@@ -0,0 +1,39 @@
1
+ // src/sources.ts
2
+ function toDigits(number) {
3
+ return String(number ?? "").replace(/\D/g, "");
4
+ }
5
+ function normalizeResolvedName(raw, number) {
6
+ if (!raw) return null;
7
+ const trimmed = String(raw).trim();
8
+ if (!trimmed) return null;
9
+ const digits = toDigits(number);
10
+ if (!digits) return trimmed;
11
+ return toDigits(trimmed) === digits ? null : trimmed;
12
+ }
13
+ function withNameResolution(source, resolve) {
14
+ const cache = /* @__PURE__ */ new Map();
15
+ return async (params) => {
16
+ const entries = await source(params);
17
+ const wanted = [
18
+ ...new Set(entries.filter((e) => e.name === null).map((e) => toDigits(e.number)))
19
+ ].filter((d) => d !== "" && !cache.has(d));
20
+ await Promise.all(
21
+ wanted.map(async (digits) => {
22
+ try {
23
+ const contact = await resolve(digits);
24
+ cache.set(digits, normalizeResolvedName(contact?.name ?? null, digits));
25
+ } catch {
26
+ }
27
+ })
28
+ );
29
+ return entries.map((e) => {
30
+ if (e.name !== null) return e;
31
+ const resolved = cache.get(toDigits(e.number)) ?? null;
32
+ return resolved === null ? e : { ...e, name: resolved };
33
+ });
34
+ };
35
+ }
36
+
37
+ export { normalizeResolvedName, toDigits, withNameResolution };
38
+ //# sourceMappingURL=chunk-DZRSSZWB.js.map
39
+ //# sourceMappingURL=chunk-DZRSSZWB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sources.ts"],"names":[],"mappings":";AAqBO,SAAS,SAAS,MAAA,EAAwB;AAC/C,EAAA,OAAO,OAAO,MAAA,IAAU,EAAE,CAAA,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC/C;AAaO,SAAS,qBAAA,CAAsB,KAAgC,MAAA,EAA+B;AACnG,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,GAAG,CAAA,CAAE,IAAA,EAAK;AACjC,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,EAAA,MAAM,MAAA,GAAS,SAAS,MAAM,CAAA;AAC9B,EAAA,IAAI,CAAC,QAAQ,OAAO,OAAA;AACpB,EAAA,OAAO,QAAA,CAAS,OAAO,CAAA,KAAM,MAAA,GAAS,IAAA,GAAO,OAAA;AAC/C;AAeO,SAAS,kBAAA,CACd,QACA,OAAA,EACe;AACf,EAAA,MAAM,KAAA,uBAAY,GAAA,EAA2B;AAE7C,EAAA,OAAO,OAAO,MAAA,KAAW;AACvB,IAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,MAAM,CAAA;AAInC,IAAA,MAAM,MAAA,GAAS;AAAA,MACb,GAAG,IAAI,GAAA,CAAI,QAAQ,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA,CAAE,IAAI,CAAC,CAAA,KAAM,SAAS,CAAA,CAAE,MAAM,CAAC,CAAC;AAAA,KAClF,CAAE,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,KAAM,MAAM,CAAC,KAAA,CAAM,GAAA,CAAI,CAAC,CAAC,CAAA;AACzC,IAAA,MAAM,OAAA,CAAQ,GAAA;AAAA,MACZ,MAAA,CAAO,GAAA,CAAI,OAAO,MAAA,KAAW;AAC3B,QAAA,IAAI;AACF,UAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,MAAM,CAAA;AACpC,UAAA,KAAA,CAAM,IAAI,MAAA,EAAQ,qBAAA,CAAsB,SAAS,IAAA,IAAQ,IAAA,EAAM,MAAM,CAAC,CAAA;AAAA,QACxE,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,KACH;AAEA,IAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,CAAA,KAAwB;AAE1C,MAAA,IAAI,CAAA,CAAE,IAAA,KAAS,IAAA,EAAM,OAAO,CAAA;AAC5B,MAAA,MAAM,WAAW,KAAA,CAAM,GAAA,CAAI,SAAS,CAAA,CAAE,MAAM,CAAC,CAAA,IAAK,IAAA;AAClD,MAAA,OAAO,aAAa,IAAA,GAAO,CAAA,GAAI,EAAE,GAAG,CAAA,EAAG,MAAM,QAAA,EAAS;AAAA,IACxD,CAAC,CAAA;AAAA,EACH,CAAA;AACF","file":"chunk-DZRSSZWB.js","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"]}
@@ -86,6 +86,24 @@ interface LineUpdate {
86
86
  /** Who has the line when it is `elsewhere`; `null` when it is here, or the server did not say. */
87
87
  readonly where: LineLocation | null;
88
88
  }
89
+ /**
90
+ * A call offered to this user's line, as the backend announces it over the presence socket
91
+ * (incoming-call-reliability.md §5): `start` each time the call is offered to the extension (a re-offer of the
92
+ * same call repeats it, same `callId`), `end` when the call is over. `callId` is opaque; `from` is the caller,
93
+ * `null` (or, on an `end`, absent) when the backend did not say.
94
+ *
95
+ * The softphone uses it for one thing: after a decline, to know when the declined call's re-offers stop, so a
96
+ * new call from the same number rings (§4.4). A presence source that never sends it loses nothing but that.
97
+ */
98
+ type CallOffer = {
99
+ readonly state: 'start';
100
+ readonly callId: string;
101
+ readonly from: string | null;
102
+ } | {
103
+ readonly state: 'end';
104
+ readonly callId: string;
105
+ readonly from?: string | null;
106
+ };
89
107
  /** The answer to `PresenceConnection.claimLine()`. */
90
108
  interface LineClaimResult {
91
109
  readonly ok: boolean;
@@ -284,6 +302,12 @@ interface PresenceCallbacks {
284
302
  readonly onLine?: (update: LineUpdate) => void;
285
303
  /** The answer to `PresenceConnection.claimLine()`. Optional, like `onLine`. */
286
304
  readonly onClaimResult?: (result: LineClaimResult) => void;
305
+ /**
306
+ * A call offered to this line started or ended (`CallOffer`). Sent only to the line's owner. Optional, like
307
+ * `onLine`: callbacks written before it existed still type-check, and a source that never calls it leaves the
308
+ * softphone on its fallback after a decline (incoming-call-reliability.md §4.4).
309
+ */
310
+ readonly onOffer?: (offer: CallOffer) => void;
287
311
  }
288
312
  /**
289
313
  * Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
@@ -767,7 +791,7 @@ interface TypedEmitter<M> {
767
791
  *
768
792
  * - **`rejecting`.** Today's engine has one, purely to swallow Zadarma's `return_timeout` retries
769
793
  * for 35 s after a decline (SPEC §3.4). That is a PROVIDER quirk — it belongs in
770
- * `providers/zadarma.ts` as `quirks.rejectGuardMs`, not in the shared state machine. A rejected
794
+ * `providers/zadarma.ts` as `quirks.declinedCallerGuardMs`, not in the shared state machine. A rejected
771
795
  * call is simply `ended`. Our own SIP infrastructure would not need it at all (SPEC §2.3).
772
796
  *
773
797
  * - **A pre-INVITE state.** A Call exists only once an INVITE has landed. The "user answered