@coventia/softphone 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/{call-C6YpT6uR.d.cts → call-FqcvDg1t.d.cts} +105 -12
  2. package/dist/{call-C6YpT6uR.d.ts → call-FqcvDg1t.d.ts} +105 -12
  3. package/dist/{chunk-EYEDPD7Y.js → chunk-6RR6G35E.js} +92 -17
  4. package/dist/chunk-6RR6G35E.js.map +1 -0
  5. package/dist/{chunk-2ZSOGGPO.js → chunk-FRXHEHQA.js} +15 -2
  6. package/dist/chunk-FRXHEHQA.js.map +1 -0
  7. package/dist/{chunk-UHNLEBBO.js → chunk-I3RSC52R.js} +28 -3
  8. package/dist/chunk-I3RSC52R.js.map +1 -0
  9. package/dist/chunk-M76SAXXM.js +13 -0
  10. package/dist/chunk-M76SAXXM.js.map +1 -0
  11. package/dist/{chunk-7R6LOOAI.js → chunk-N4PLEYFJ.js} +4 -2
  12. package/dist/chunk-N4PLEYFJ.js.map +1 -0
  13. package/dist/element/index.d.ts +142 -6
  14. package/dist/element/index.js +138 -43
  15. package/dist/element/index.js.map +1 -1
  16. package/dist/{engine-DPft99Wj.d.ts → engine-CsNRD7B7.d.ts} +1 -1
  17. package/dist/{engine-CDdaSojx.d.cts → engine-CzOCrfY9.d.cts} +1 -1
  18. package/dist/index.cjs +579 -47
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +5 -5
  21. package/dist/index.d.ts +5 -5
  22. package/dist/index.js +447 -37
  23. package/dist/index.js.map +1 -1
  24. package/dist/react/index.cjs +26 -0
  25. package/dist/react/index.cjs.map +1 -1
  26. package/dist/react/index.d.cts +24 -5
  27. package/dist/react/index.d.ts +24 -5
  28. package/dist/react/index.js +2 -2
  29. package/dist/{softphone-CY5d2WWY.d.cts → softphone-By9sizlV.d.ts} +42 -3
  30. package/dist/{softphone-DXgmTyrX.d.ts → softphone-JZvJdzU3.d.cts} +42 -3
  31. package/dist/{sources-ONYQW733.js → sources-QL2LRZVH.js} +19 -2
  32. package/dist/sources-QL2LRZVH.js.map +1 -0
  33. package/dist/testing/index.cjs +13 -0
  34. package/dist/testing/index.cjs.map +1 -1
  35. package/dist/testing/index.d.cts +2 -2
  36. package/dist/testing/index.d.ts +2 -2
  37. package/dist/testing/index.js +1 -1
  38. package/dist/ui/index.cjs +92 -2
  39. package/dist/ui/index.cjs.map +1 -1
  40. package/dist/ui/index.d.cts +2 -2
  41. package/dist/ui/index.d.ts +2 -2
  42. package/dist/ui/index.js +69 -4
  43. package/dist/ui/index.js.map +1 -1
  44. package/dist/zadarma/index.cjs +111 -14
  45. package/dist/zadarma/index.cjs.map +1 -1
  46. package/dist/zadarma/index.d.cts +5 -6
  47. package/dist/zadarma/index.d.ts +5 -6
  48. package/dist/zadarma/index.js +3 -2
  49. package/dist/zadarma-engine-TIUE7PZ7.js +6 -0
  50. package/dist/{zadarma-engine-L5DEFXUS.js.map → zadarma-engine-TIUE7PZ7.js.map} +1 -1
  51. package/package.json +1 -1
  52. package/dist/chunk-2ZSOGGPO.js.map +0 -1
  53. package/dist/chunk-7R6LOOAI.js.map +0 -1
  54. package/dist/chunk-EYEDPD7Y.js.map +0 -1
  55. package/dist/chunk-UHNLEBBO.js.map +0 -1
  56. package/dist/sources-ONYQW733.js.map +0 -1
  57. package/dist/zadarma-engine-L5DEFXUS.js +0 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/errors.ts","../../src/testing/mock.ts","../../src/testing/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * The error taxonomy.\r\n *\r\n * Two things make this worth more than `onError(Error)`:\r\n *\r\n * 1. **Fatal vs non-fatal, as a discriminated union.** A fatal error means the call (or the engine)\r\n * is over and the host must react. A non-fatal one is a warning the host may surface or ignore.\r\n * Collapsing them into one channel — which is what almost every SDK does — forces every\r\n * integrator to re-derive the distinction from string matching. Daily's split is the model.\r\n *\r\n * 2. **A coded catalogue with a `solution` per code.** Twilio and Telnyx both ship this and\r\n * integrators love it, because the support conversation becomes \"send me the code\" instead of\r\n * \"send me your logs\".\r\n *\r\n * The catalogue below is the SINGLE source of truth: `fatal` is derived from it at the type level,\r\n * so a code cannot be fatal in the table and non-fatal in a type, and adding a code without deciding\r\n * its severity is a compile error.\r\n */\r\n\r\ninterface ErrorSpec {\r\n readonly fatal: boolean;\r\n /** What went wrong, for a developer. */\r\n readonly message: string;\r\n /** What to do about it. Shown in logs and in our docs. */\r\n readonly solution: string;\r\n}\r\n\r\nexport const ERROR_CATALOGUE = {\r\n // ── Engine lifecycle ────────────────────────────────────────────────────────\r\n 'engine/duplicate-instance': {\r\n fatal: true,\r\n message: 'A Softphone instance is already running on this page.',\r\n solution:\r\n 'Construct one Softphone and share it. React StrictMode double-mounts in development — hold the instance above the tree, or in a ref, and call destroy() on unmount.',\r\n },\r\n 'engine/not-started': {\r\n fatal: false,\r\n message: 'The softphone has not been started.',\r\n solution: 'Await softphone.start() before placing or answering a call.',\r\n },\r\n 'engine/destroyed': {\r\n fatal: true,\r\n message: 'This Softphone has been destroyed and cannot be reused.',\r\n solution: 'Construct a new Softphone.',\r\n },\r\n\r\n // ── Auth ────────────────────────────────────────────────────────────────────\r\n 'auth/token-fetch-failed': {\r\n fatal: true,\r\n message: 'Could not obtain a voice token from the token endpoint.',\r\n solution:\r\n 'Check that the token endpoint is reachable and that the headers callback returns a valid host-app session token.',\r\n },\r\n 'auth/token-rejected': {\r\n fatal: true,\r\n message: 'The voice token was rejected.',\r\n solution:\r\n 'The token is expired, malformed, or scoped to a different organization. Mint a fresh one.',\r\n },\r\n\r\n // ── Media ───────────────────────────────────────────────────────────────────\r\n /*\r\n NONE of the four codes below are ever actually raised via `createError` — nothing in the SDK\r\n constructs a `SoftphoneError` with these codes today. Deleting them was the other option\r\n (SPEC discussion); they stay, commented, because the taxonomy and Spanish copy (`error-text.ts`)\r\n are worth keeping intact for whenever a real caller wants them, and a `SoftphoneErrorCode` union\r\n is a cheap thing to keep honest with a comment, expensive to keep churning as a breaking change.\r\n */\r\n 'media/permission-denied': {\r\n fatal: true,\r\n message: 'Microphone permission was denied.',\r\n solution:\r\n 'The permission prompt names the TOP-LEVEL origin. Ask for it from a user gesture, and re-request via the browser UI if previously blocked.',\r\n // NEVER RAISED. `MicPermissionGate` (ui/permission/) handles this condition today — it predates\r\n // this catalogue and has BETTER, browser-specific unblock instructions than a generic gate could\r\n // show. It does not import this catalogue at all; the two are unconnected on purpose.\r\n },\r\n 'media/no-input-device': {\r\n fatal: true,\r\n message: 'No microphone is available.',\r\n solution: 'Ask the user to connect a microphone, then retry.',\r\n // NEVER RAISED — same reason as `media/permission-denied` above: `MicPermissionGate` already\r\n // owns this.\r\n },\r\n 'media/webrtc-unsupported': {\r\n fatal: true,\r\n message: 'This browser does not expose the WebRTC APIs.',\r\n solution:\r\n 'Usually a privacy extension (uBlock Origin Extra, WebRTC Leak Prevent) disabling RTCPeerConnection — Twilio documents the same. Ask the user to disable it for this origin.',\r\n // NEVER RAISED — same reason again: `MicPermissionGate` already owns this.\r\n },\r\n 'media/playback-blocked': {\r\n fatal: false,\r\n message: 'The browser blocked audio playback (autoplay policy).',\r\n solution:\r\n 'Call softphone.startAudio() from a click handler. Until then the user cannot hear the caller, and an INCOMING RINGTONE WILL BE SILENT — this is how calls get missed.',\r\n /*\r\n NEVER RAISED — different reason from the other three. This is not superseded by an existing\r\n UI; the feature it describes (autoplay-recovery) is simply not built yet. `Softphone.canPlayAudio`\r\n / `startAudio()` (core/softphone.ts) are Phase 2 and currently no-op stubs — `canPlayAudio`\r\n always reports `true`. This is where that future work would report the block once it exists.\r\n */\r\n },\r\n\r\n // ── Network / signalling ────────────────────────────────────────────────────\r\n 'net/registration-failed': {\r\n fatal: true,\r\n message: 'SIP registration failed.',\r\n solution: 'Usually a stale credential. The engine refreshes and retries; if it persists, the extension may not exist.',\r\n },\r\n 'net/transport-failed': {\r\n fatal: true,\r\n message: 'The signalling transport could not be established.',\r\n solution: 'Check the CSP connect-src directive and any corporate proxy blocking WSS.',\r\n },\r\n 'net/ice-failed': {\r\n fatal: false,\r\n message: 'Media connectivity was lost; attempting an ICE restart.',\r\n solution:\r\n 'Usually a network handoff (wifi → cellular). The engine recovers on its own; surface a \"reconnecting\" banner rather than ending the call.',\r\n /*\r\n NEVER RAISED. The \"reconnecting\" banner this `solution` describes already exists — CallScreen\r\n shows \"Reconectando…\" directly off the call's own `reconnecting` STATE (`STATUS_LABEL` in\r\n views/CallScreen.tsx), which the SIP engine drives without going through the error channel at\r\n all. Raising this too would say the same fact twice through two different mechanisms. It would\r\n only earn its keep for host-side OBSERVABILITY — counting ICE restarts independently of the UI\r\n — which nobody has asked for yet.\r\n */\r\n },\r\n\r\n // ── Call ────────────────────────────────────────────────────────────────────\r\n 'call/not-answerable': {\r\n fatal: false,\r\n message: 'This call cannot be answered in its current state.',\r\n solution: 'Only a call in `ringing` may be answered. Check call.state first.',\r\n },\r\n 'call/busy': {\r\n fatal: false,\r\n message: 'The remote party is busy.',\r\n solution:\r\n 'Nothing to fix. Note SIP 486 is also what most endpoints send for a manual decline, so do not ' +\r\n 'label this \"busy\" alone in your UI — it is equally often \"did not answer\".',\r\n },\r\n 'call/no-answer': {\r\n fatal: false,\r\n message: 'The remote party did not answer.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/rejected': {\r\n fatal: false,\r\n message: 'The remote party rejected the call.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/missed': {\r\n fatal: false,\r\n message: 'An incoming call was cancelled before it was answered.',\r\n solution:\r\n 'Nothing to fix — the caller gave up while it rang. Surface it, and log it as a missed call.',\r\n },\r\n 'call/failed': {\r\n fatal: false,\r\n message: 'The call could not be completed.',\r\n solution:\r\n 'The catch-all for a call that never connected — a bad number, a rejected offer, a media ' +\r\n 'failure. `cause` carries the underlying SIP cause. Nothing is fatal: the phone still works.',\r\n },\r\n 'call/transfer-failed': {\r\n fatal: false,\r\n message: 'The transfer could not be completed.',\r\n solution: 'The original call is still up. Let the user retry or resume.',\r\n },\r\n 'call/unsupported': {\r\n fatal: false,\r\n message: 'The provider does not support this operation.',\r\n solution: 'Guard the UI on the operation before offering it.',\r\n },\r\n\r\n // ── Provider ────────────────────────────────────────────────────────────────\r\n 'provider/provision-failed': {\r\n fatal: true,\r\n message: 'The provider could not issue SIP credentials.',\r\n solution:\r\n 'The backend reached the carrier but got no usable credential. Check the org has a SIP extension provisioned.',\r\n },\r\n} as const satisfies Record<string, ErrorSpec>;\r\n\r\nexport type SoftphoneErrorCode = keyof typeof ERROR_CATALOGUE;\r\n\r\n/** Codes whose spec says `fatal: true`. Derived — cannot drift from the catalogue. */\r\nexport type FatalErrorCode = {\r\n [K in SoftphoneErrorCode]: (typeof ERROR_CATALOGUE)[K]['fatal'] extends true ? K : never;\r\n}[SoftphoneErrorCode];\r\n\r\nexport type NonFatalErrorCode = Exclude<SoftphoneErrorCode, FatalErrorCode>;\r\n\r\n/**\r\n * A fatal error: the call, or the engine, is over.\r\n *\r\n * `if (err.fatal)` narrows `err.code` to the fatal codes — which is the entire point of the split,\r\n * and the thing JSDoc could not have expressed (SPEC §2.2).\r\n */\r\nexport interface FatalSoftphoneError extends Error {\r\n readonly fatal: true;\r\n readonly code: FatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\n/** A non-fatal error: a warning. The call, if any, continues. */\r\nexport interface NonFatalSoftphoneError extends Error {\r\n readonly fatal: false;\r\n readonly code: NonFatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\nexport type SoftphoneError = FatalSoftphoneError | NonFatalSoftphoneError;\r\n\r\n/**\r\n * The one concrete error class. Not exported: the public contract is the `SoftphoneError` union and\r\n * the `code` on it, so nothing outside should be reaching for `instanceof`. It is a real `Error`\r\n * subclass, so `instanceof Error`, `.stack` and console formatting all behave as a host expects.\r\n */\r\nclass CatalogueError extends Error {\r\n readonly fatal: boolean;\r\n readonly code: SoftphoneErrorCode;\r\n readonly solution: string;\r\n\r\n constructor(code: SoftphoneErrorCode, cause?: unknown) {\r\n const spec: ErrorSpec = ERROR_CATALOGUE[code];\r\n // `cause` is only passed when there IS one: `{ cause: undefined }` would still define the\r\n // property, and an error carrying an explicit `undefined` cause reads as \"we looked and found\r\n // nothing\" rather than \"nothing was passed\".\r\n super(spec.message, cause === undefined ? undefined : { cause });\r\n this.name = 'SoftphoneError';\r\n this.code = code;\r\n this.fatal = spec.fatal;\r\n this.solution = spec.solution;\r\n }\r\n}\r\n\r\n/**\r\n * Is this already one of ours?\r\n *\r\n * Used where a layer catches whatever a lower one threw: a failure that already carries a catalogue\r\n * code should be reported AS that code, not flattened into the generic one for the layer that caught\r\n * it. Without this, a rejected voice token surfaces as \"the provider could not issue credentials\",\r\n * which is true and useless.\r\n *\r\n * Structural, not `instanceof` — the concrete class is not exported, and a duck-typed object with a\r\n * real code is as good as one of ours.\r\n */\r\nexport function isSoftphoneError(value: unknown): value is SoftphoneError {\r\n if (typeof value !== 'object' || value === null) return false;\r\n const candidate = value as { code?: unknown; fatal?: unknown };\r\n return typeof candidate.fatal === 'boolean' && (candidate.code as string) in ERROR_CATALOGUE;\r\n}\r\n\r\n/** Type guard, for hosts that would rather not rely on structural narrowing. */\r\nexport function isFatal(error: SoftphoneError): error is FatalSoftphoneError {\r\n return error.fatal;\r\n}\r\n\r\n/**\r\n * Construct a catalogue-backed error. **The only way the SDK raises one** — every `message` and\r\n * `solution` a host sees comes from the table above, so no call site invents error prose.\r\n *\r\n * The return type narrows on the code: `createError('call/busy')` is a `NonFatalSoftphoneError`, and\r\n * reading `.fatal` on it gives `false`, not `boolean`.\r\n */\r\nexport function createError<C extends SoftphoneErrorCode>(\r\n code: C,\r\n cause?: unknown,\r\n): C extends FatalErrorCode ? FatalSoftphoneError : NonFatalSoftphoneError {\r\n // The one cast in this module. A conditional return type cannot be verified from inside the\r\n // implementation — TypeScript has no way to prove the branch for an unresolved `C` — so the\r\n // guarantee rests on `fatal` being copied from the catalogue that DERIVES `FatalErrorCode`. The\r\n // test suite asserts that agreement for every code, which is the check the compiler cannot make.\r\n return new CatalogueError(code, cause) as C extends FatalErrorCode\r\n ? FatalSoftphoneError\r\n : NonFatalSoftphoneError;\r\n}\r\n","import type { CallEngine, EngineCall, EngineCallEvent } from '../core/engine.js';\r\nimport type { CallDirection, CallOptions, SoftphoneState } from '../core/types.js';\r\nimport type { HangupReason } from '../core/call.js';\r\nimport { createError, type SoftphoneError, type SoftphoneErrorCode } from '../core/errors.js';\r\n\r\n/**\r\n * MockProvider — a `CallEngine` that scripts a full call lifecycle with no Zadarma, no microphone, no\r\n * network and no real phone call (SPEC §5.7).\r\n *\r\n * It is DOUBLE-DUTY, and that is the point:\r\n * - the engine behind the playgrounds, so a full call (ring → answer → hold → transfer → hang up)\r\n * is drivable in a browser instantly;\r\n * - the test suite's fake.\r\n * The SAME object both ways, kept honest by the CallEngine contract suite — so what the playground\r\n * exercises is what the real engine must also satisfy.\r\n *\r\n * Everything is explicit and deterministic: nothing auto-advances on a timer. The host (a test, or a\r\n * playground button) decides when the remote answers, holds, drops the network, or hangs up. That is\r\n * what makes it usable as a test fake and as a demo without divergence.\r\n */\r\n\r\nlet seq = 0;\r\nfunction nextId(prefix: string): string {\r\n seq += 1;\r\n return `${prefix}-${seq}`;\r\n}\r\n\r\nclass MockEngineCall implements EngineCall {\r\n readonly #listeners = new Set<(event: EngineCallEvent) => void>();\r\n #ended = false;\r\n\r\n constructor(\r\n readonly id: string,\r\n readonly direction: CallDirection,\r\n readonly remoteNumber: string,\r\n /** Notified with the new leg when `startConsult` creates one — lets `MockProvider` script it. */\r\n private readonly onConsultCreated?: (consult: MockEngineCall) => void,\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 /** @internal — fire a raw engine signal, asynchronously, as a real transport would. */\r\n fire(event: EngineCallEvent): void {\r\n if (this.#ended) return;\r\n if (event.type === 'ended') this.#ended = true;\r\n queueMicrotask(() => {\r\n for (const listener of [...this.#listeners]) listener(event);\r\n });\r\n }\r\n\r\n get ended(): boolean {\r\n return this.#ended;\r\n }\r\n\r\n // ── Commands (local side) ─────────────────────────────────────────────────\r\n async answer(): Promise<void> {\r\n // Inbound: the local user accepted. Media comes up.\r\n this.fire({ type: 'accepted' });\r\n }\r\n async reject(): Promise<void> {\r\n this.fire({ type: 'ended', reason: 'rejected' });\r\n }\r\n async hangup(): Promise<void> {\r\n this.fire({ type: 'ended', reason: 'local' });\r\n }\r\n mute(_muted: boolean): void {\r\n // The engine only carries the effect; Call owns the mute state + event.\r\n }\r\n async hold(): Promise<void> {\r\n this.fire({ type: 'held', originator: 'local' });\r\n }\r\n async unhold(): Promise<void> {\r\n this.fire({ type: 'unheld', originator: 'local' });\r\n }\r\n sendDigits(_digits: string): void {\r\n // A real engine sends RFC 2833 / SIP INFO; the mock has nothing to send to.\r\n }\r\n async transfer(_target: string): Promise<void> {\r\n this.fire({ type: 'ended', reason: 'transferred' });\r\n }\r\n async startConsult(target: string): Promise<EngineCall> {\r\n const consult = new MockEngineCall(nextId('consult'), 'outbound', target);\r\n this.onConsultCreated?.(consult);\r\n return consult;\r\n }\r\n async completeTransfer(consult: EngineCall): Promise<void> {\r\n // Both legs end — the real engine's REFER…Replaces bridges caller↔consult-target and drops both\r\n // the original session and the consult session (`call-impl.ts`'s own comment on `completeTransfer`).\r\n (consult as MockEngineCall).fire({ type: 'ended', reason: 'transferred' });\r\n this.fire({ type: 'ended', reason: 'transferred' });\r\n }\r\n}\r\n\r\nexport interface SimulateIncomingOptions {\r\n readonly from: string;\r\n /**\r\n * No `displayName` here, deliberately. `EngineCall` carries only `remoteNumber` — the seam has no\r\n * display-name channel, because caller identity is resolved from CONTACTS, not from the SIP `From`\r\n * header (`CallImpl.setRemoteContact`). Until then `remote.displayName` IS the number.\r\n *\r\n * Accepting a name here would let consumers write passing tests for behaviour the real engine can\r\n * never produce. The mock must not outrun the seam it stands in for.\r\n */\r\n}\r\n\r\nexport class MockProvider implements CallEngine {\r\n #state: SoftphoneState = 'stopped';\r\n #current: MockEngineCall | null = null;\r\n /** The consult leg from the current call's own `startConsult`, if any — see `simulateConsult*`. */\r\n #currentConsult: MockEngineCall | null = null;\r\n readonly #incoming = 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\r\n get state(): SoftphoneState {\r\n return this.#state;\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.#setState('online');\r\n }\r\n async stop(): Promise<void> {\r\n this.#current = null;\r\n this.#setState('stopped');\r\n }\r\n\r\n onIncoming(listener: (call: EngineCall) => void): () => void {\r\n this.#incoming.add(listener);\r\n return () => this.#incoming.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 async placeCall(destination: string, _options?: CallOptions): Promise<EngineCall> {\r\n const call = new MockEngineCall(nextId('out'), 'outbound', destination, (c) => {\r\n this.#currentConsult = c;\r\n });\r\n this.#current = call;\r\n this.#currentConsult = null;\r\n return call;\r\n }\r\n\r\n // ── The simulation surface (SPEC §5.7) ────────────────────────────────────\r\n\r\n /** An inbound INVITE lands. Fires the engine's `onIncoming` — the Softphone raises IncomingCall. */\r\n simulateIncoming(options: SimulateIncomingOptions): void {\r\n const call = new MockEngineCall(nextId('in'), 'inbound', options.from, (c) => {\r\n this.#currentConsult = c;\r\n });\r\n this.#current = call;\r\n this.#currentConsult = null;\r\n for (const listener of [...this.#incoming]) listener(call);\r\n }\r\n\r\n /** The remote answered our outbound call (or a provisional 180/183 first, if `progress` is set). */\r\n simulateRemoteAnswer(): void {\r\n this.#requireCurrent('simulateRemoteAnswer').fire({ type: 'accepted' });\r\n }\r\n\r\n /** Outbound provisional response — remote is ringing. Optional early media. */\r\n simulateProgress(kind: 'ringing' | 'early' = 'ringing'): void {\r\n this.#requireCurrent('simulateProgress').fire({ type: 'progress', response: kind });\r\n }\r\n\r\n /**\r\n * Remote media arrives — what `ontrack` produces on a real call.\r\n *\r\n * Added after the SDK shipped with NO audio at all: `core` hands the stream out and the UI attaches\r\n * it, but nothing did, and no test could see that because the mock had no way to produce a stream.\r\n * The seam always carried one (`EngineCallEvent` has `{ type: 'stream' }`); only the driver was\r\n * missing — unlike `displayName`, which the seam genuinely cannot carry (see SimulateIncomingOptions).\r\n *\r\n * The default is a stand-in object: jsdom has no `MediaStream`, and every consumer only ever hands\r\n * this to `srcObject`.\r\n */\r\n simulateRemoteStream(stream: MediaStream = {} as MediaStream): void {\r\n this.#requireCurrent('simulateRemoteStream').fire({ type: 'stream', stream });\r\n }\r\n\r\n simulateRemoteHold(): void {\r\n this.#requireCurrent('simulateRemoteHold').fire({ type: 'held', originator: 'remote' });\r\n }\r\n simulateRemoteUnhold(): void {\r\n this.#requireCurrent('simulateRemoteUnhold').fire({ type: 'unheld', originator: 'remote' });\r\n }\r\n\r\n simulateRemoteHangup(reason: HangupReason = 'remote'): void {\r\n this.#requireCurrent('simulateRemoteHangup').fire({ type: 'ended', reason });\r\n }\r\n\r\n /**\r\n * Raise any catalogued failure, exactly as a real engine would.\r\n *\r\n * The point is the two TREATMENTS: pass a fatal code (`net/registration-failed`) and the widget\r\n * replaces the dialpad with a gate that cannot be dismissed; pass a non-fatal one (`call/missed`)\r\n * and it shows a muted line under the number that the next keypress clears. A playground wants\r\n * both on a button, and neither can be reached by driving a mock call to a happy ending.\r\n */\r\n simulateError(code: SoftphoneErrorCode, cause?: unknown): void {\r\n const error = createError(code, cause);\r\n for (const listener of [...this.#errorListeners]) listener(error);\r\n }\r\n\r\n /** Media lost — the engine begins an ICE restart. The call is NOT over. */\r\n simulateNetworkDrop(): void {\r\n this.#requireCurrent('simulateNetworkDrop').fire({ type: 'reconnecting' });\r\n }\r\n simulateNetworkRecover(): void {\r\n this.#requireCurrent('simulateNetworkRecover').fire({ type: 'reconnected' });\r\n }\r\n\r\n // ── The consult leg (attended transfer, SPEC §5.6) ─────────────────────────\r\n //\r\n // A `startConsult()` call creates a SECOND `MockEngineCall`, tracked separately from `#current` —\r\n // the methods above all target the main call, so scripting the consult leg's own outcome (it rings,\r\n // it's answered, or it fails on its own) needs its own surface rather than overloading `#current`.\r\n\r\n /** The target of the current call's `startConsult` is ringing. Optional early media. */\r\n simulateConsultProgress(kind: 'ringing' | 'early' = 'ringing'): void {\r\n this.#requireCurrentConsult('simulateConsultProgress').fire({ type: 'progress', response: kind });\r\n }\r\n /** The consult target answered — the agent can now `completeTransfer()` or `cancelConsult()`. */\r\n simulateConsultAnswer(): void {\r\n this.#requireCurrentConsult('simulateConsultAnswer').fire({ type: 'accepted' });\r\n }\r\n /** The consult leg ended on its own — busy, no answer, rejected, or a generic failure. */\r\n simulateConsultHangup(reason: HangupReason = 'no-answer'): void {\r\n this.#requireCurrentConsult('simulateConsultHangup').fire({ type: 'ended', reason });\r\n }\r\n\r\n // ── internals ─────────────────────────────────────────────────────────────\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 #requireCurrent(op: string): MockEngineCall {\r\n if (!this.#current || this.#current.ended) {\r\n throw new Error(`MockProvider.${op}() called with no active call.`);\r\n }\r\n return this.#current;\r\n }\r\n\r\n #requireCurrentConsult(op: string): MockEngineCall {\r\n if (!this.#currentConsult || this.#currentConsult.ended) {\r\n throw new Error(`MockProvider.${op}() called with no active consult leg.`);\r\n }\r\n return this.#currentConsult;\r\n }\r\n}\r\n","/**\r\n * @coventia/softphone/testing — MockProvider and test helpers.\r\n *\r\n * Its own entry point so it never lands in a consumer's production bundle. See SPEC §5.7.\r\n */\r\nimport type { TokenSource, VoiceToken } from '../core/types.js';\r\n\r\nexport { MockProvider } from './mock.js';\r\nexport type { SimulateIncomingOptions } from './mock.js';\r\n\r\n/**\r\n * A TokenSource that returns a fixed, far-future fake token.\r\n *\r\n * `SoftphoneConfig.tokenSource` is required (SPEC §6.1) because the real Zadarma path needs it — but\r\n * the mock engine never mints credentials, so tests and playgrounds need something to satisfy the\r\n * type without a backend. This is that something. Never ship it.\r\n */\r\nexport function mockTokenSource(): TokenSource {\r\n return {\r\n async getToken(): Promise<VoiceToken> {\r\n return {\r\n token: 'mock-voice-token',\r\n identity: 'mock-user',\r\n // Far future; the mock does not refresh.\r\n expiresAt: new Date(4102444800000), // 2100-01-01\r\n };\r\n },\r\n };\r\n}\r\n"]}
1
+ {"version":3,"sources":["../../src/core/errors.ts","../../src/testing/mock.ts","../../src/testing/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * The error taxonomy.\r\n *\r\n * Two things make this worth more than `onError(Error)`:\r\n *\r\n * 1. **Fatal vs non-fatal, as a discriminated union.** A fatal error means the call (or the engine)\r\n * is over and the host must react. A non-fatal one is a warning the host may surface or ignore.\r\n * Collapsing them into one channel — which is what almost every SDK does — forces every\r\n * integrator to re-derive the distinction from string matching. Daily's split is the model.\r\n *\r\n * 2. **A coded catalogue with a `solution` per code.** Twilio and Telnyx both ship this and\r\n * integrators love it, because the support conversation becomes \"send me the code\" instead of\r\n * \"send me your logs\".\r\n *\r\n * The catalogue below is the SINGLE source of truth: `fatal` is derived from it at the type level,\r\n * so a code cannot be fatal in the table and non-fatal in a type, and adding a code without deciding\r\n * its severity is a compile error.\r\n */\r\n\r\ninterface ErrorSpec {\r\n readonly fatal: boolean;\r\n /** What went wrong, for a developer. */\r\n readonly message: string;\r\n /** What to do about it. Shown in logs and in our docs. */\r\n readonly solution: string;\r\n}\r\n\r\nexport const ERROR_CATALOGUE = {\r\n // ── Engine lifecycle ────────────────────────────────────────────────────────\r\n 'engine/duplicate-instance': {\r\n fatal: true,\r\n message: 'A Softphone instance is already running on this page.',\r\n solution:\r\n 'Construct one Softphone and share it. React StrictMode double-mounts in development — hold the instance above the tree, or in a ref, and call destroy() on unmount.',\r\n },\r\n 'engine/not-started': {\r\n fatal: false,\r\n message: 'The softphone has not been started.',\r\n solution: 'Await softphone.start() before placing or answering a call.',\r\n },\r\n 'engine/destroyed': {\r\n fatal: true,\r\n message: 'This Softphone has been destroyed and cannot be reused.',\r\n solution: 'Construct a new Softphone.',\r\n },\r\n\r\n // ── Auth ────────────────────────────────────────────────────────────────────\r\n 'auth/token-fetch-failed': {\r\n fatal: true,\r\n message: 'Could not obtain a voice token from the token endpoint.',\r\n solution:\r\n 'Check that the token endpoint is reachable and that the headers callback returns a valid host-app session token.',\r\n },\r\n 'auth/token-rejected': {\r\n fatal: true,\r\n message: 'The voice token was rejected.',\r\n solution:\r\n 'The token is expired, malformed, or scoped to a different organization. Mint a fresh one.',\r\n },\r\n\r\n // ── Media ───────────────────────────────────────────────────────────────────\r\n /*\r\n NONE of the four codes below are ever actually raised via `createError` — nothing in the SDK\r\n constructs a `SoftphoneError` with these codes today. Deleting them was the other option\r\n (SPEC discussion); they stay, commented, because the taxonomy and Spanish copy (`error-text.ts`)\r\n are worth keeping intact for whenever a real caller wants them, and a `SoftphoneErrorCode` union\r\n is a cheap thing to keep honest with a comment, expensive to keep churning as a breaking change.\r\n */\r\n 'media/permission-denied': {\r\n fatal: true,\r\n message: 'Microphone permission was denied.',\r\n solution:\r\n 'The permission prompt names the TOP-LEVEL origin. Ask for it from a user gesture, and re-request via the browser UI if previously blocked.',\r\n // NEVER RAISED. `MicPermissionGate` (ui/permission/) handles this condition today — it predates\r\n // this catalogue and has BETTER, browser-specific unblock instructions than a generic gate could\r\n // show. It does not import this catalogue at all; the two are unconnected on purpose.\r\n },\r\n 'media/no-input-device': {\r\n fatal: true,\r\n message: 'No microphone is available.',\r\n solution: 'Ask the user to connect a microphone, then retry.',\r\n // NEVER RAISED — same reason as `media/permission-denied` above: `MicPermissionGate` already\r\n // owns this.\r\n },\r\n 'media/webrtc-unsupported': {\r\n fatal: true,\r\n message: 'This browser does not expose the WebRTC APIs.',\r\n solution:\r\n 'Usually a privacy extension (uBlock Origin Extra, WebRTC Leak Prevent) disabling RTCPeerConnection — Twilio documents the same. Ask the user to disable it for this origin.',\r\n // NEVER RAISED — same reason again: `MicPermissionGate` already owns this.\r\n },\r\n 'media/playback-blocked': {\r\n fatal: false,\r\n message: 'The browser blocked audio playback (autoplay policy).',\r\n solution:\r\n 'Call softphone.startAudio() from a click handler. Until then the user cannot hear the caller, and an INCOMING RINGTONE WILL BE SILENT — this is how calls get missed.',\r\n /*\r\n NEVER RAISED — different reason from the other three. This is not superseded by an existing\r\n UI; the feature it describes (autoplay-recovery) is simply not built yet. `Softphone.canPlayAudio`\r\n / `startAudio()` (core/softphone.ts) are Phase 2 and currently no-op stubs — `canPlayAudio`\r\n always reports `true`. This is where that future work would report the block once it exists.\r\n */\r\n },\r\n\r\n // ── Network / signalling ────────────────────────────────────────────────────\r\n 'net/registration-failed': {\r\n fatal: true,\r\n message: 'SIP registration failed.',\r\n solution: 'Usually a stale credential. The engine refreshes and retries; if it persists, the extension may not exist.',\r\n },\r\n 'net/transport-failed': {\r\n fatal: true,\r\n message: 'The signalling transport could not be established.',\r\n solution: 'Check the CSP connect-src directive and any corporate proxy blocking WSS.',\r\n },\r\n 'net/ice-failed': {\r\n fatal: false,\r\n message: 'Media connectivity was lost; attempting an ICE restart.',\r\n solution:\r\n 'Usually a network handoff (wifi → cellular). The engine recovers on its own; surface a \"reconnecting\" banner rather than ending the call.',\r\n /*\r\n NEVER RAISED. The \"reconnecting\" banner this `solution` describes already exists — CallScreen\r\n shows \"Reconectando…\" directly off the call's own `reconnecting` STATE (`STATUS_LABEL` in\r\n views/CallScreen.tsx), which the SIP engine drives without going through the error channel at\r\n all. Raising this too would say the same fact twice through two different mechanisms. It would\r\n only earn its keep for host-side OBSERVABILITY — counting ICE restarts independently of the UI\r\n — which nobody has asked for yet.\r\n */\r\n },\r\n\r\n // ── Call ────────────────────────────────────────────────────────────────────\r\n 'call/not-answerable': {\r\n fatal: false,\r\n message: 'This call cannot be answered in its current state.',\r\n solution: 'Only a call in `ringing` may be answered. Check call.state first.',\r\n },\r\n 'call/busy': {\r\n fatal: false,\r\n message: 'The remote party is busy.',\r\n solution:\r\n 'Nothing to fix. Note SIP 486 is also what most endpoints send for a manual decline, so do not ' +\r\n 'label this \"busy\" alone in your UI — it is equally often \"did not answer\".',\r\n },\r\n 'call/no-answer': {\r\n fatal: false,\r\n message: 'The remote party did not answer.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/rejected': {\r\n fatal: false,\r\n message: 'The remote party rejected the call.',\r\n solution: 'Nothing to fix. Surface it to the user.',\r\n },\r\n 'call/missed': {\r\n fatal: false,\r\n message: 'An incoming call was cancelled before it was answered.',\r\n solution:\r\n 'Nothing to fix — the caller gave up while it rang. Surface it, and log it as a missed call.',\r\n },\r\n 'call/failed': {\r\n fatal: false,\r\n message: 'The call could not be completed.',\r\n solution:\r\n 'The catch-all for a call that never connected — a bad number, a rejected offer, a media ' +\r\n 'failure. `cause` carries the underlying SIP cause. Nothing is fatal: the phone still works.',\r\n },\r\n 'call/transfer-failed': {\r\n fatal: false,\r\n message: 'The transfer could not be completed.',\r\n solution: 'The original call is still up. Let the user retry or resume.',\r\n },\r\n 'call/unsupported': {\r\n fatal: false,\r\n message: 'The provider does not support this operation.',\r\n solution: 'Guard the UI on the operation before offering it.',\r\n },\r\n\r\n // ── Line owner (docs/specs/line-owner.md §5) ────────────────────────────────\r\n // What `takeLine()` rejects with — and `call()` from `standby`, which takes the line first. Never\r\n // emitted on `SoftphoneEvent.Error`: the promise IS the answer, and nothing about the phone is broken.\r\n 'line/claim-refused': {\r\n fatal: false,\r\n message: 'The line was not moved here: the user is on a call on the softphone that has it.',\r\n solution:\r\n 'Nothing to fix. The line never moves during a call; take it again once that call has ended. Incoming calls keep ringing where they ring now.',\r\n },\r\n 'line/unavailable': {\r\n fatal: false,\r\n message: 'The line could not be taken: nothing decided where it is.',\r\n solution:\r\n 'The presence connection is down, the backend predates the line owner, or it did not answer in time. Retry in a moment; incoming calls keep ringing where they ring now.',\r\n },\r\n\r\n // ── Provider ────────────────────────────────────────────────────────────────\r\n 'provider/provision-failed': {\r\n fatal: true,\r\n message: 'The provider could not issue SIP credentials.',\r\n solution:\r\n 'The backend reached the carrier but got no usable credential. Check the org has a SIP extension provisioned.',\r\n },\r\n} as const satisfies Record<string, ErrorSpec>;\r\n\r\nexport type SoftphoneErrorCode = keyof typeof ERROR_CATALOGUE;\r\n\r\n/** Codes whose spec says `fatal: true`. Derived — cannot drift from the catalogue. */\r\nexport type FatalErrorCode = {\r\n [K in SoftphoneErrorCode]: (typeof ERROR_CATALOGUE)[K]['fatal'] extends true ? K : never;\r\n}[SoftphoneErrorCode];\r\n\r\nexport type NonFatalErrorCode = Exclude<SoftphoneErrorCode, FatalErrorCode>;\r\n\r\n/**\r\n * A fatal error: the call, or the engine, is over.\r\n *\r\n * `if (err.fatal)` narrows `err.code` to the fatal codes — which is the entire point of the split,\r\n * and the thing JSDoc could not have expressed (SPEC §2.2).\r\n */\r\nexport interface FatalSoftphoneError extends Error {\r\n readonly fatal: true;\r\n readonly code: FatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\n/** A non-fatal error: a warning. The call, if any, continues. */\r\nexport interface NonFatalSoftphoneError extends Error {\r\n readonly fatal: false;\r\n readonly code: NonFatalErrorCode;\r\n readonly solution: string;\r\n}\r\n\r\nexport type SoftphoneError = FatalSoftphoneError | NonFatalSoftphoneError;\r\n\r\n/**\r\n * The one concrete error class. Not exported: the public contract is the `SoftphoneError` union and\r\n * the `code` on it, so nothing outside should be reaching for `instanceof`. It is a real `Error`\r\n * subclass, so `instanceof Error`, `.stack` and console formatting all behave as a host expects.\r\n */\r\nclass CatalogueError extends Error {\r\n readonly fatal: boolean;\r\n readonly code: SoftphoneErrorCode;\r\n readonly solution: string;\r\n\r\n constructor(code: SoftphoneErrorCode, cause?: unknown) {\r\n const spec: ErrorSpec = ERROR_CATALOGUE[code];\r\n // `cause` is only passed when there IS one: `{ cause: undefined }` would still define the\r\n // property, and an error carrying an explicit `undefined` cause reads as \"we looked and found\r\n // nothing\" rather than \"nothing was passed\".\r\n super(spec.message, cause === undefined ? undefined : { cause });\r\n this.name = 'SoftphoneError';\r\n this.code = code;\r\n this.fatal = spec.fatal;\r\n this.solution = spec.solution;\r\n }\r\n}\r\n\r\n/**\r\n * Is this already one of ours?\r\n *\r\n * Used where a layer catches whatever a lower one threw: a failure that already carries a catalogue\r\n * code should be reported AS that code, not flattened into the generic one for the layer that caught\r\n * it. Without this, a rejected voice token surfaces as \"the provider could not issue credentials\",\r\n * which is true and useless.\r\n *\r\n * Structural, not `instanceof` — the concrete class is not exported, and a duck-typed object with a\r\n * real code is as good as one of ours.\r\n */\r\nexport function isSoftphoneError(value: unknown): value is SoftphoneError {\r\n if (typeof value !== 'object' || value === null) return false;\r\n const candidate = value as { code?: unknown; fatal?: unknown };\r\n return typeof candidate.fatal === 'boolean' && (candidate.code as string) in ERROR_CATALOGUE;\r\n}\r\n\r\n/** Type guard, for hosts that would rather not rely on structural narrowing. */\r\nexport function isFatal(error: SoftphoneError): error is FatalSoftphoneError {\r\n return error.fatal;\r\n}\r\n\r\n/**\r\n * Construct a catalogue-backed error. **The only way the SDK raises one** — every `message` and\r\n * `solution` a host sees comes from the table above, so no call site invents error prose.\r\n *\r\n * The return type narrows on the code: `createError('call/busy')` is a `NonFatalSoftphoneError`, and\r\n * reading `.fatal` on it gives `false`, not `boolean`.\r\n */\r\nexport function createError<C extends SoftphoneErrorCode>(\r\n code: C,\r\n cause?: unknown,\r\n): C extends FatalErrorCode ? FatalSoftphoneError : NonFatalSoftphoneError {\r\n // The one cast in this module. A conditional return type cannot be verified from inside the\r\n // implementation — TypeScript has no way to prove the branch for an unresolved `C` — so the\r\n // guarantee rests on `fatal` being copied from the catalogue that DERIVES `FatalErrorCode`. The\r\n // test suite asserts that agreement for every code, which is the check the compiler cannot make.\r\n return new CatalogueError(code, cause) as C extends FatalErrorCode\r\n ? FatalSoftphoneError\r\n : NonFatalSoftphoneError;\r\n}\r\n","import type { CallEngine, EngineCall, EngineCallEvent } from '../core/engine.js';\r\nimport type { CallDirection, CallOptions, SoftphoneState } from '../core/types.js';\r\nimport type { HangupReason } from '../core/call.js';\r\nimport { createError, type SoftphoneError, type SoftphoneErrorCode } from '../core/errors.js';\r\n\r\n/**\r\n * MockProvider — a `CallEngine` that scripts a full call lifecycle with no Zadarma, no microphone, no\r\n * network and no real phone call (SPEC §5.7).\r\n *\r\n * It is DOUBLE-DUTY, and that is the point:\r\n * - the engine behind the playgrounds, so a full call (ring → answer → hold → transfer → hang up)\r\n * is drivable in a browser instantly;\r\n * - the test suite's fake.\r\n * The SAME object both ways, kept honest by the CallEngine contract suite — so what the playground\r\n * exercises is what the real engine must also satisfy.\r\n *\r\n * Everything is explicit and deterministic: nothing auto-advances on a timer. The host (a test, or a\r\n * playground button) decides when the remote answers, holds, drops the network, or hangs up. That is\r\n * what makes it usable as a test fake and as a demo without divergence.\r\n */\r\n\r\nlet seq = 0;\r\nfunction nextId(prefix: string): string {\r\n seq += 1;\r\n return `${prefix}-${seq}`;\r\n}\r\n\r\nclass MockEngineCall implements EngineCall {\r\n readonly #listeners = new Set<(event: EngineCallEvent) => void>();\r\n #ended = false;\r\n\r\n constructor(\r\n readonly id: string,\r\n readonly direction: CallDirection,\r\n readonly remoteNumber: string,\r\n /** Notified with the new leg when `startConsult` creates one — lets `MockProvider` script it. */\r\n private readonly onConsultCreated?: (consult: MockEngineCall) => void,\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 /** @internal — fire a raw engine signal, asynchronously, as a real transport would. */\r\n fire(event: EngineCallEvent): void {\r\n if (this.#ended) return;\r\n if (event.type === 'ended') this.#ended = true;\r\n queueMicrotask(() => {\r\n for (const listener of [...this.#listeners]) listener(event);\r\n });\r\n }\r\n\r\n get ended(): boolean {\r\n return this.#ended;\r\n }\r\n\r\n // ── Commands (local side) ─────────────────────────────────────────────────\r\n async answer(): Promise<void> {\r\n // Inbound: the local user accepted. Media comes up.\r\n this.fire({ type: 'accepted' });\r\n }\r\n async reject(): Promise<void> {\r\n this.fire({ type: 'ended', reason: 'rejected' });\r\n }\r\n async hangup(): Promise<void> {\r\n this.fire({ type: 'ended', reason: 'local' });\r\n }\r\n mute(_muted: boolean): void {\r\n // The engine only carries the effect; Call owns the mute state + event.\r\n }\r\n async hold(): Promise<void> {\r\n this.fire({ type: 'held', originator: 'local' });\r\n }\r\n async unhold(): Promise<void> {\r\n this.fire({ type: 'unheld', originator: 'local' });\r\n }\r\n sendDigits(_digits: string): void {\r\n // A real engine sends RFC 2833 / SIP INFO; the mock has nothing to send to.\r\n }\r\n async transfer(_target: string): Promise<void> {\r\n this.fire({ type: 'ended', reason: 'transferred' });\r\n }\r\n async startConsult(target: string): Promise<EngineCall> {\r\n const consult = new MockEngineCall(nextId('consult'), 'outbound', target);\r\n this.onConsultCreated?.(consult);\r\n return consult;\r\n }\r\n async completeTransfer(consult: EngineCall): Promise<void> {\r\n // Both legs end — the real engine's REFER…Replaces bridges caller↔consult-target and drops both\r\n // the original session and the consult session (`call-impl.ts`'s own comment on `completeTransfer`).\r\n (consult as MockEngineCall).fire({ type: 'ended', reason: 'transferred' });\r\n this.fire({ type: 'ended', reason: 'transferred' });\r\n }\r\n}\r\n\r\nexport interface SimulateIncomingOptions {\r\n readonly from: string;\r\n /**\r\n * No `displayName` here, deliberately. `EngineCall` carries only `remoteNumber` — the seam has no\r\n * display-name channel, because caller identity is resolved from CONTACTS, not from the SIP `From`\r\n * header (`CallImpl.setRemoteContact`). Until then `remote.displayName` IS the number.\r\n *\r\n * Accepting a name here would let consumers write passing tests for behaviour the real engine can\r\n * never produce. The mock must not outrun the seam it stands in for.\r\n */\r\n}\r\n\r\nexport class MockProvider implements CallEngine {\r\n #state: SoftphoneState = 'stopped';\r\n #current: MockEngineCall | null = null;\r\n /** The consult leg from the current call's own `startConsult`, if any — see `simulateConsult*`. */\r\n #currentConsult: MockEngineCall | null = null;\r\n readonly #incoming = 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\r\n get state(): SoftphoneState {\r\n return this.#state;\r\n }\r\n\r\n async start(): Promise<void> {\r\n this.#setState('online');\r\n }\r\n async stop(): Promise<void> {\r\n this.#current = null;\r\n this.#setState('stopped');\r\n }\r\n\r\n onIncoming(listener: (call: EngineCall) => void): () => void {\r\n this.#incoming.add(listener);\r\n return () => this.#incoming.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 async placeCall(destination: string, _options?: CallOptions): Promise<EngineCall> {\r\n const call = new MockEngineCall(nextId('out'), 'outbound', destination, (c) => {\r\n this.#currentConsult = c;\r\n });\r\n this.#current = call;\r\n this.#currentConsult = null;\r\n return call;\r\n }\r\n\r\n // ── The simulation surface (SPEC §5.7) ────────────────────────────────────\r\n\r\n /** An inbound INVITE lands. Fires the engine's `onIncoming` — the Softphone raises IncomingCall. */\r\n simulateIncoming(options: SimulateIncomingOptions): void {\r\n const call = new MockEngineCall(nextId('in'), 'inbound', options.from, (c) => {\r\n this.#currentConsult = c;\r\n });\r\n this.#current = call;\r\n this.#currentConsult = null;\r\n for (const listener of [...this.#incoming]) listener(call);\r\n }\r\n\r\n /** The remote answered our outbound call (or a provisional 180/183 first, if `progress` is set). */\r\n simulateRemoteAnswer(): void {\r\n this.#requireCurrent('simulateRemoteAnswer').fire({ type: 'accepted' });\r\n }\r\n\r\n /** Outbound provisional response — remote is ringing. Optional early media. */\r\n simulateProgress(kind: 'ringing' | 'early' = 'ringing'): void {\r\n this.#requireCurrent('simulateProgress').fire({ type: 'progress', response: kind });\r\n }\r\n\r\n /**\r\n * Remote media arrives — what `ontrack` produces on a real call.\r\n *\r\n * Added after the SDK shipped with NO audio at all: `core` hands the stream out and the UI attaches\r\n * it, but nothing did, and no test could see that because the mock had no way to produce a stream.\r\n * The seam always carried one (`EngineCallEvent` has `{ type: 'stream' }`); only the driver was\r\n * missing — unlike `displayName`, which the seam genuinely cannot carry (see SimulateIncomingOptions).\r\n *\r\n * The default is a stand-in object: jsdom has no `MediaStream`, and every consumer only ever hands\r\n * this to `srcObject`.\r\n */\r\n simulateRemoteStream(stream: MediaStream = {} as MediaStream): void {\r\n this.#requireCurrent('simulateRemoteStream').fire({ type: 'stream', stream });\r\n }\r\n\r\n simulateRemoteHold(): void {\r\n this.#requireCurrent('simulateRemoteHold').fire({ type: 'held', originator: 'remote' });\r\n }\r\n simulateRemoteUnhold(): void {\r\n this.#requireCurrent('simulateRemoteUnhold').fire({ type: 'unheld', originator: 'remote' });\r\n }\r\n\r\n simulateRemoteHangup(reason: HangupReason = 'remote'): void {\r\n this.#requireCurrent('simulateRemoteHangup').fire({ type: 'ended', reason });\r\n }\r\n\r\n /**\r\n * Raise any catalogued failure, exactly as a real engine would.\r\n *\r\n * The point is the two TREATMENTS: pass a fatal code (`net/registration-failed`) and the widget\r\n * replaces the dialpad with a gate that cannot be dismissed; pass a non-fatal one (`call/missed`)\r\n * and it shows a muted line under the number that the next keypress clears. A playground wants\r\n * both on a button, and neither can be reached by driving a mock call to a happy ending.\r\n */\r\n simulateError(code: SoftphoneErrorCode, cause?: unknown): void {\r\n const error = createError(code, cause);\r\n for (const listener of [...this.#errorListeners]) listener(error);\r\n }\r\n\r\n /** Media lost — the engine begins an ICE restart. The call is NOT over. */\r\n simulateNetworkDrop(): void {\r\n this.#requireCurrent('simulateNetworkDrop').fire({ type: 'reconnecting' });\r\n }\r\n simulateNetworkRecover(): void {\r\n this.#requireCurrent('simulateNetworkRecover').fire({ type: 'reconnected' });\r\n }\r\n\r\n // ── The consult leg (attended transfer, SPEC §5.6) ─────────────────────────\r\n //\r\n // A `startConsult()` call creates a SECOND `MockEngineCall`, tracked separately from `#current` —\r\n // the methods above all target the main call, so scripting the consult leg's own outcome (it rings,\r\n // it's answered, or it fails on its own) needs its own surface rather than overloading `#current`.\r\n\r\n /** The target of the current call's `startConsult` is ringing. Optional early media. */\r\n simulateConsultProgress(kind: 'ringing' | 'early' = 'ringing'): void {\r\n this.#requireCurrentConsult('simulateConsultProgress').fire({ type: 'progress', response: kind });\r\n }\r\n /** The consult target answered — the agent can now `completeTransfer()` or `cancelConsult()`. */\r\n simulateConsultAnswer(): void {\r\n this.#requireCurrentConsult('simulateConsultAnswer').fire({ type: 'accepted' });\r\n }\r\n /** The consult leg ended on its own — busy, no answer, rejected, or a generic failure. */\r\n simulateConsultHangup(reason: HangupReason = 'no-answer'): void {\r\n this.#requireCurrentConsult('simulateConsultHangup').fire({ type: 'ended', reason });\r\n }\r\n\r\n // ── internals ─────────────────────────────────────────────────────────────\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 #requireCurrent(op: string): MockEngineCall {\r\n if (!this.#current || this.#current.ended) {\r\n throw new Error(`MockProvider.${op}() called with no active call.`);\r\n }\r\n return this.#current;\r\n }\r\n\r\n #requireCurrentConsult(op: string): MockEngineCall {\r\n if (!this.#currentConsult || this.#currentConsult.ended) {\r\n throw new Error(`MockProvider.${op}() called with no active consult leg.`);\r\n }\r\n return this.#currentConsult;\r\n }\r\n}\r\n","/**\r\n * @coventia/softphone/testing — MockProvider and test helpers.\r\n *\r\n * Its own entry point so it never lands in a consumer's production bundle. See SPEC §5.7.\r\n */\r\nimport type { TokenSource, VoiceToken } from '../core/types.js';\r\n\r\nexport { MockProvider } from './mock.js';\r\nexport type { SimulateIncomingOptions } from './mock.js';\r\n\r\n/**\r\n * A TokenSource that returns a fixed, far-future fake token.\r\n *\r\n * `SoftphoneConfig.tokenSource` is required (SPEC §6.1) because the real Zadarma path needs it — but\r\n * the mock engine never mints credentials, so tests and playgrounds need something to satisfy the\r\n * type without a backend. This is that something. Never ship it.\r\n */\r\nexport function mockTokenSource(): TokenSource {\r\n return {\r\n async getToken(): Promise<VoiceToken> {\r\n return {\r\n token: 'mock-voice-token',\r\n identity: 'mock-user',\r\n // Far future; the mock does not refresh.\r\n expiresAt: new Date(4102444800000), // 2100-01-01\r\n };\r\n },\r\n };\r\n}\r\n"]}
@@ -1,5 +1,5 @@
1
- import { w as SoftphoneState, s as SoftphoneError, e as CallOptions, H as HangupReason, t as SoftphoneErrorCode, x as TokenSource } from '../call-C6YpT6uR.cjs';
2
- import { C as CallEngine, E as EngineCall } from '../engine-CDdaSojx.cjs';
1
+ import { z as SoftphoneState, v as SoftphoneError, e as CallOptions, H as HangupReason, w as SoftphoneErrorCode, A as TokenSource } from '../call-FqcvDg1t.cjs';
2
+ import { C as CallEngine, E as EngineCall } from '../engine-CzOCrfY9.cjs';
3
3
 
4
4
  interface SimulateIncomingOptions {
5
5
  readonly from: string;
@@ -1,5 +1,5 @@
1
- import { w as SoftphoneState, s as SoftphoneError, e as CallOptions, H as HangupReason, t as SoftphoneErrorCode, x as TokenSource } from '../call-C6YpT6uR.js';
2
- import { C as CallEngine, E as EngineCall } from '../engine-DPft99Wj.js';
1
+ import { z as SoftphoneState, v as SoftphoneError, e as CallOptions, H as HangupReason, w as SoftphoneErrorCode, A as TokenSource } from '../call-FqcvDg1t.js';
2
+ import { C as CallEngine, E as EngineCall } from '../engine-CsNRD7B7.js';
3
3
 
4
4
  interface SimulateIncomingOptions {
5
5
  readonly from: string;
@@ -1,4 +1,4 @@
1
- import { createError } from '../chunk-2ZSOGGPO.js';
1
+ import { createError } from '../chunk-FRXHEHQA.js';
2
2
 
3
3
  // src/testing/mock.ts
4
4
  var seq = 0;
package/dist/ui/index.cjs CHANGED
@@ -13263,6 +13263,19 @@ var Pause = createLucideIcon("Pause", [
13263
13263
  ["rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", key: "1okwgv" }]
13264
13264
  ]);
13265
13265
 
13266
+ // ../../node_modules/lucide-react/dist/esm/icons/phone-forwarded.js
13267
+ var PhoneForwarded = createLucideIcon("PhoneForwarded", [
13268
+ ["polyline", { points: "18 2 22 6 18 10", key: "6vjanh" }],
13269
+ ["line", { x1: "14", x2: "22", y1: "6", y2: "6", key: "1jsywh" }],
13270
+ [
13271
+ "path",
13272
+ {
13273
+ d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z",
13274
+ key: "foiqr5"
13275
+ }
13276
+ ]
13277
+ ]);
13278
+
13266
13279
  // ../../node_modules/lucide-react/dist/esm/icons/phone-incoming.js
13267
13280
  var PhoneIncoming = createLucideIcon("PhoneIncoming", [
13268
13281
  ["polyline", { points: "16 2 16 8 22 8", key: "1ygljm" }],
@@ -13439,6 +13452,31 @@ function useIsLeader() {
13439
13452
  React.useCallback(() => phone.isLeader, [phone])
13440
13453
  );
13441
13454
  }
13455
+ function useLine() {
13456
+ const phone = useSoftphone();
13457
+ const cache = React.useRef(null);
13458
+ return React.useSyncExternalStore(
13459
+ React.useCallback(
13460
+ (cb) => {
13461
+ const offLine = phone.on("lineChanged" /* LineChanged */, () => cb());
13462
+ const offClaim = phone.on("lineClaimChanged" /* LineClaimChanged */, () => cb());
13463
+ return () => {
13464
+ offLine();
13465
+ offClaim();
13466
+ };
13467
+ },
13468
+ [phone]
13469
+ ),
13470
+ React.useCallback(() => {
13471
+ const { line, lineWhere: where, lineClaim: claim } = phone;
13472
+ const cached = cache.current;
13473
+ if (cached?.line === line && cached.where === where && cached.claim === claim) return cached;
13474
+ const view = { line, where, claim };
13475
+ cache.current = view;
13476
+ return view;
13477
+ }, [phone])
13478
+ );
13479
+ }
13442
13480
  function useActiveCall() {
13443
13481
  const phone = useSoftphone();
13444
13482
  return React.useSyncExternalStore(
@@ -14847,6 +14885,22 @@ var ERROR_TEXT = {
14847
14885
  // thinks they dropped it will hang up on a client to "fix" it.
14848
14886
  "call/transfer-failed": { title: "No se pudo transferir. Sigues en la llamada" },
14849
14887
  "call/unsupported": { title: "Esta funci\xF3n no est\xE1 disponible" },
14888
+ // ── Line owner ──────────────────────────────────────────────────────────────
14889
+ /*
14890
+ Not call outcomes: they answer "Recibir aquí" on `LineGate`, which has ONE line for them where the
14891
+ explanation was, so it reads the pair as two sentences — `${title}. ${body}`. Split anyway, so the
14892
+ title stands alone wherever there is only room for a headline (a host's own toast), under the same
14893
+ one-line rules as every other non-fatal title. "Allí" because the gate has just said where: the
14894
+ other softphone, the one that has the calls.
14895
+ */
14896
+ "line/claim-refused": {
14897
+ title: "Est\xE1s en una llamada all\xED",
14898
+ body: "Podr\xE1s recibirlas aqu\xED cuando cuelgues."
14899
+ },
14900
+ "line/unavailable": {
14901
+ title: "No se ha podido cambiar",
14902
+ body: "Int\xE9ntalo de nuevo."
14903
+ },
14850
14904
  // ── Provider ────────────────────────────────────────────────────────────────
14851
14905
  "provider/provision-failed": {
14852
14906
  title: "No se pudo activar el tel\xE9fono",
@@ -16939,6 +16993,40 @@ function LeaderGate() {
16939
16993
  /* @__PURE__ */ jsxRuntime.jsx("p", { part: "leader-body", children: "Solo una pesta\xF1a puede usar el tel\xE9fono a la vez. Cierra la otra para activar esta." })
16940
16994
  ] });
16941
16995
  }
16996
+ var BODY = "Las llamadas entran all\xED. Si prefieres recibirlas aqu\xED, pulsa \xABRecibir aqu\xED\xBB.";
16997
+ var sentence = (code) => `${ERROR_TEXT[code].title}. ${ERROR_TEXT[code].body ?? ""}`.trim();
16998
+ function LineGate() {
16999
+ const softphone = useSoftphone();
17000
+ const { where, claim } = useLine();
17001
+ const take = () => {
17002
+ softphone.takeLine().catch(() => {
17003
+ });
17004
+ };
17005
+ const body = claim === "refused" ? sentence("line/claim-refused") : claim === "failed" ? sentence("line/unavailable") : BODY;
17006
+ return (
17007
+ /*
17008
+ `part` on all four, like `ErrorGate`: it replaces the whole dialpad, and inside `<coventia-softphone>`
17009
+ a shadow root makes it otherwise unreachable from the host's CSS. `data-claim` is for the light-DOM
17010
+ `/ui` path and for debugging — it cannot cross the shadow boundary either.
17011
+ */
17012
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "cv-gate", "data-claim": claim, part: "line-gate", children: [
17013
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "cv-gate-mark", children: /* @__PURE__ */ jsxRuntime.jsx(PhoneForwarded, { size: 22 }) }),
17014
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "cv-gate-title", part: "line-title", children: where === "desktop" ? "Tu tel\xE9fono est\xE1 activo en la app de escritorio" : "Tu tel\xE9fono est\xE1 activo en otro sitio" }),
17015
+ /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", part: "line-body", children: body }),
17016
+ /* @__PURE__ */ jsxRuntime.jsx(
17017
+ "button",
17018
+ {
17019
+ className: "cv-btn cv-btn--accent",
17020
+ onClick: take,
17021
+ disabled: claim === "claiming",
17022
+ "aria-busy": claim === "claiming",
17023
+ part: "line-claim",
17024
+ children: claim === "claiming" ? "Cambiando\u2026" : "Recibir aqu\xED"
17025
+ }
17026
+ )
17027
+ ] })
17028
+ );
17029
+ }
16942
17030
  var BACKSTOP_MS = 6e3;
16943
17031
  function useCallOutcome() {
16944
17032
  const softphone = useSoftphone();
@@ -17170,11 +17258,12 @@ function SoftphoneWidget(props) {
17170
17258
  const isLeader = useIsLeader();
17171
17259
  const softphoneState = useSoftphoneState();
17172
17260
  const isFollower = !isLeader && softphoneState === "starting";
17261
+ const isStandby = softphoneState === "standby";
17173
17262
  const mic = useMicPermission();
17174
17263
  useCallAudio(sounds);
17175
17264
  const blocked = requireMic && mic.state !== "ready";
17176
17265
  const fatal = useLastError();
17177
- const screenIsFree = !call && !blocked && !fatal && !isFollower;
17266
+ const screenIsFree = !call && !blocked && !fatal && !isFollower && !isStandby;
17178
17267
  const { outcome, clear: clearOutcome } = useCallOutcome();
17179
17268
  const { notices: transferNotices, dismiss: dismissTransferNotice } = useTransferNotices();
17180
17269
  const retry = () => {
@@ -17201,7 +17290,7 @@ function SoftphoneWidget(props) {
17201
17290
  ...theme === "system" ? {} : { "data-cv-theme": theme },
17202
17291
  children: [
17203
17292
  screenIsFree ? /* @__PURE__ */ jsxRuntime.jsx(TransferNotices, { notices: transferNotices, dismiss: dismissTransferNotice }) : null,
17204
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cv-body", children: isFollower ? /* @__PURE__ */ jsxRuntime.jsx(LeaderGate, {}) : blocked ? /* @__PURE__ */ jsxRuntime.jsx(MicPrompt, { state: mic.state, onRequest: () => void mic.request() }) : call ? /* @__PURE__ */ jsxRuntime.jsx(CallScreen, { call, keyTones: sounds, ...incomingCallAction ? { incomingCallAction } : {} }) : fatal ? /* @__PURE__ */ jsxRuntime.jsx(ErrorGate, { error: fatal, onRetry: retry }) : tab === "contacts" && contactsSource ? /* @__PURE__ */ jsxRuntime.jsx(ContactsTab, { source: contactsSource, ...contactAction ? { contactAction } : {} }) : tab === "history" && historySource ? /* @__PURE__ */ jsxRuntime.jsx(HistoryTab, { source: historySource }) : tab === "settings" ? /* @__PURE__ */ jsxRuntime.jsx(SettingsTab, {}) : /* @__PURE__ */ jsxRuntime.jsx(Dialpad, { outcome, onIntent: clearOutcome, keyTones: sounds }) }),
17293
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cv-body", children: isFollower ? /* @__PURE__ */ jsxRuntime.jsx(LeaderGate, {}) : isStandby && !call ? /* @__PURE__ */ jsxRuntime.jsx(LineGate, {}) : blocked ? /* @__PURE__ */ jsxRuntime.jsx(MicPrompt, { state: mic.state, onRequest: () => void mic.request() }) : call ? /* @__PURE__ */ jsxRuntime.jsx(CallScreen, { call, keyTones: sounds, ...incomingCallAction ? { incomingCallAction } : {} }) : fatal ? /* @__PURE__ */ jsxRuntime.jsx(ErrorGate, { error: fatal, onRetry: retry }) : tab === "contacts" && contactsSource ? /* @__PURE__ */ jsxRuntime.jsx(ContactsTab, { source: contactsSource, ...contactAction ? { contactAction } : {} }) : tab === "history" && historySource ? /* @__PURE__ */ jsxRuntime.jsx(HistoryTab, { source: historySource }) : tab === "settings" ? /* @__PURE__ */ jsxRuntime.jsx(SettingsTab, {}) : /* @__PURE__ */ jsxRuntime.jsx(Dialpad, { outcome, onIntent: clearOutcome, keyTones: sounds }) }),
17205
17294
  screenIsFree ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "cv-tabs", role: "tablist", children: tabs.map(({ id, icon: Icon3, label }) => /* @__PURE__ */ jsxRuntime.jsxs(
17206
17295
  "button",
17207
17296
  {
@@ -17247,6 +17336,7 @@ lucide-react/dist/esm/icons/mic.js:
17247
17336
  lucide-react/dist/esm/icons/monitor-smartphone.js:
17248
17337
  lucide-react/dist/esm/icons/music.js:
17249
17338
  lucide-react/dist/esm/icons/pause.js:
17339
+ lucide-react/dist/esm/icons/phone-forwarded.js:
17250
17340
  lucide-react/dist/esm/icons/phone-incoming.js:
17251
17341
  lucide-react/dist/esm/icons/phone-missed.js:
17252
17342
  lucide-react/dist/esm/icons/phone-off.js: