@coventia/softphone 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{call-BTYlT2SU.d.cts → call-DUVnDxsz.d.cts} +152 -5
- package/dist/{call-BTYlT2SU.d.ts → call-DUVnDxsz.d.ts} +152 -5
- package/dist/{chunk-N2N2EQQJ.js → chunk-7R6LOOAI.js} +4 -2
- package/dist/chunk-7R6LOOAI.js.map +1 -0
- package/dist/{chunk-3V2L6UTK.js → chunk-LMJXX4YM.js} +17 -3
- package/dist/chunk-LMJXX4YM.js.map +1 -0
- package/dist/{chunk-5BGEHMPP.js → chunk-YWI6EQYS.js} +40 -7
- package/dist/chunk-YWI6EQYS.js.map +1 -0
- package/dist/element/index.d.ts +154 -5
- package/dist/element/index.js +512 -152
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-D9nbeY08.d.cts → engine-CDjkrXHY.d.cts} +1 -1
- package/dist/{engine-Doa6Brkm.d.ts → engine-EPCKf-Bl.d.ts} +1 -1
- package/dist/index.cjs +396 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +256 -8
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +15 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +14 -3
- package/dist/react/index.d.ts +14 -3
- package/dist/react/index.js +2 -2
- package/dist/{softphone-DhuhcKCy.d.cts → softphone-BtdgZWkm.d.ts} +17 -2
- package/dist/{softphone-DHmhotbf.d.ts → softphone-DHG9MTNg.d.cts} +17 -2
- package/dist/softphone.css +395 -1
- package/dist/{sources-N4BOEDRL.js → sources-5L2R4HTV.js} +104 -5
- package/dist/sources-5L2R4HTV.js.map +1 -0
- package/dist/testing/index.cjs +41 -5
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +8 -2
- package/dist/testing/index.d.ts +8 -2
- package/dist/testing/index.js +41 -5
- package/dist/testing/index.js.map +1 -1
- package/dist/ui/index.cjs +490 -139
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +2 -2
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +478 -141
- package/dist/ui/index.js.map +1 -1
- package/dist/zadarma/index.cjs +38 -5
- package/dist/zadarma/index.cjs.map +1 -1
- package/dist/zadarma/index.d.cts +2 -2
- package/dist/zadarma/index.d.ts +2 -2
- package/dist/zadarma/index.js +1 -1
- package/dist/zadarma-engine-4VOPIBXG.js +5 -0
- package/dist/{zadarma-engine-DN2SZSQ3.js.map → zadarma-engine-4VOPIBXG.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-3V2L6UTK.js.map +0 -1
- package/dist/chunk-5BGEHMPP.js.map +0 -1
- package/dist/chunk-N2N2EQQJ.js.map +0 -1
- package/dist/sources-N4BOEDRL.js.map +0 -1
- package/dist/zadarma-engine-DN2SZSQ3.js +0 -5
|
@@ -117,6 +117,19 @@ interface CoventiaBackendConfig {
|
|
|
117
117
|
* (SPEC §6.6).
|
|
118
118
|
*/
|
|
119
119
|
readonly contacts?: ContactsSource;
|
|
120
|
+
/**
|
|
121
|
+
* Transfer outcomes, overriding Coventia's own polling — for a `MockProvider`-style test double, or
|
|
122
|
+
* a future non-Zadarma provider that computes outcomes differently. Not a per-partner customisation
|
|
123
|
+
* point the way `contacts` is: unlike a contact list, a transfer outcome is not a partner's own
|
|
124
|
+
* data, so there is normally no reason to set this (`docs/specs/transfer-outcome-relay.md`, Phase A).
|
|
125
|
+
*/
|
|
126
|
+
readonly transferOutcomes?: TransferOutcomeSource;
|
|
127
|
+
/**
|
|
128
|
+
* Live agent presence, overriding Coventia's own WebSocket — for a `MockProvider`-style test
|
|
129
|
+
* double, or a host with its own presence transport. Not a per-partner customisation point the way
|
|
130
|
+
* `contacts` is (`docs/specs/transfer-presence-picker.md`, Phase B).
|
|
131
|
+
*/
|
|
132
|
+
readonly presence?: PresenceSource;
|
|
120
133
|
}
|
|
121
134
|
interface SoftphoneConfig {
|
|
122
135
|
readonly tokenSource: TokenSource;
|
|
@@ -261,6 +274,98 @@ interface AgentsDirectory {
|
|
|
261
274
|
readonly me: PhoneAgent | null;
|
|
262
275
|
readonly people: readonly PhoneAgent[];
|
|
263
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* `'connected'` | `'not-connected'` — whether an attended transfer's second ring, after `Completar`,
|
|
279
|
+
* actually reached the target. Kebab-case to match `HangupReason` (`'no-answer'`, `'transferred'`);
|
|
280
|
+
* the backend's own wire value stays `not_connected` — this is the one place that gets translated.
|
|
281
|
+
*/
|
|
282
|
+
type TransferOutcomeStatus = 'connected' | 'not-connected';
|
|
283
|
+
/** One resolved transfer, as `GET /phone/transfers/outcomes` reports it. */
|
|
284
|
+
interface TransferOutcomeEntry {
|
|
285
|
+
/** Opaque, strictly increasing per organization, never re-issued. The cursor for the NEXT poll. */
|
|
286
|
+
readonly id: string;
|
|
287
|
+
/** The extension the call was transferred to. */
|
|
288
|
+
readonly target: string;
|
|
289
|
+
readonly status: TransferOutcomeStatus;
|
|
290
|
+
readonly at: Date;
|
|
291
|
+
}
|
|
292
|
+
interface TransferOutcomesPage {
|
|
293
|
+
/** The newest `id` currently retained, or `'0'` when there are none — pass back as `since`. */
|
|
294
|
+
readonly cursor: string;
|
|
295
|
+
readonly outcomes: readonly TransferOutcomeEntry[];
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Poll for transfer outcomes. `since` omitted ⇒ everything currently retained — used for the FIRST
|
|
299
|
+
* poll after starting a watch, which exists only to learn the current cursor (spec §5.3's
|
|
300
|
+
* no-mis-attribution rule: that first poll's own outcomes are discarded, only `cursor` is kept).
|
|
301
|
+
*/
|
|
302
|
+
type TransferOutcomeSource = (params: {
|
|
303
|
+
readonly since?: string;
|
|
304
|
+
}) => Promise<TransferOutcomesPage>;
|
|
305
|
+
/**
|
|
306
|
+
* `SoftphoneEvent.TransferOutcome`'s payload. Unlike `TransferOutcomeEntry`, this never carries `id`
|
|
307
|
+
* (a polling-internal concern) and adds `caller` — the backend never stores a caller number (spec
|
|
308
|
+
* §5.3: "no caller number, nothing about other users"), so this is filled in locally, from the `Call`
|
|
309
|
+
* that was transferred, at the moment an outcome is matched to it.
|
|
310
|
+
*/
|
|
311
|
+
interface TransferOutcomeEvent {
|
|
312
|
+
readonly status: TransferOutcomeStatus;
|
|
313
|
+
readonly target: string;
|
|
314
|
+
/** The original caller's number, for "Llamar de vuelta" — null if it was never known. */
|
|
315
|
+
readonly caller: string | null;
|
|
316
|
+
readonly at: Date;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* `'disponible'` | `'en_llamada'` | `'desconectado'` — Spanish because this is what the picker shows,
|
|
320
|
+
* not an internal code (spec §4.1). `derivePresence` (the backend's own reducer, mirrored nowhere in
|
|
321
|
+
* the SDK — the SDK only ever receives an already-derived state, never `onCall`/`onlineFresh` apart).
|
|
322
|
+
*/
|
|
323
|
+
type PresenceState = 'disponible' | 'en_llamada' | 'desconectado';
|
|
324
|
+
/** One org member with a phone extension, as the picker lists them. */
|
|
325
|
+
interface PresenceEntry {
|
|
326
|
+
readonly userId: string;
|
|
327
|
+
readonly extension: string;
|
|
328
|
+
/** `null` when the backend has no name to offer — the picker falls back to the extension. */
|
|
329
|
+
readonly name: string | null;
|
|
330
|
+
readonly state: PresenceState;
|
|
331
|
+
}
|
|
332
|
+
/** What a live presence connection hands back once open. */
|
|
333
|
+
interface PresenceConnection {
|
|
334
|
+
/**
|
|
335
|
+
* Report this softphone's own on-call-ness, so a colleague transferring TO this agent sees it
|
|
336
|
+
* without a round trip through the webhook path (spec §4.1's `onCall` OR-combine). Calling this
|
|
337
|
+
* before the connection is genuinely open is never required of an implementation — a caller (see
|
|
338
|
+
* `presence-socket.ts`) that reports early is expected to wait for the connection to exist first.
|
|
339
|
+
*/
|
|
340
|
+
readonly reportCallState: (onCall: boolean) => void;
|
|
341
|
+
/**
|
|
342
|
+
* Ends this connection deliberately. Never causes the SAME callbacks' `onClose` to fire — that is
|
|
343
|
+
* reserved for the connection ending on its OWN (a network drop, a server-side close, a rejected
|
|
344
|
+
* ticket), which is the only thing a caller should ever treat as "reconnect now".
|
|
345
|
+
*/
|
|
346
|
+
readonly close: () => void;
|
|
347
|
+
}
|
|
348
|
+
interface PresenceCallbacks {
|
|
349
|
+
/** Once, right after connecting — the full current roster (spec §4.3). */
|
|
350
|
+
readonly onSnapshot: (agents: readonly PresenceEntry[]) => void;
|
|
351
|
+
/** Zero or more times after that — one agent whose state just changed. */
|
|
352
|
+
readonly onDelta: (agent: PresenceEntry) => void;
|
|
353
|
+
/**
|
|
354
|
+
* The connection ended on its own. Never called after the CALLER's own `close()` — see
|
|
355
|
+
* `PresenceConnection.close`'s own note. The only signal a watch layer needs to know it must
|
|
356
|
+
* reconnect, deliberately carrying no reason: a dropped connection and a rejected ticket look
|
|
357
|
+
* identical from here, and both get the same treatment (spec §4.4 — quiet retry, nothing surfaced).
|
|
358
|
+
*/
|
|
359
|
+
readonly onClose: () => void;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
|
|
363
|
+
* `wss://…/phone/presence`); a host may override it entirely, or omit it — omitted, or while the
|
|
364
|
+
* connection is down, the picker simply has no live data to show (spec §4.4, success criterion #5:
|
|
365
|
+
* no error surfaces). Unlike `TransferOutcomeSource` (poll, get a page, done), this is push-based and
|
|
366
|
+
* long-lived — the returned promise resolves once, when the connection opens, not once per update.
|
|
367
|
+
*/
|
|
368
|
+
type PresenceSource = (callbacks: PresenceCallbacks) => Promise<PresenceConnection>;
|
|
264
369
|
|
|
265
370
|
/**
|
|
266
371
|
* The error taxonomy.
|
|
@@ -456,7 +561,23 @@ declare enum SoftphoneEvent {
|
|
|
456
561
|
* `StateChanged`: `state` is reachability, this is whether this tab is even allowed to be
|
|
457
562
|
* reachable at all while another tab holding the same extension stays open.
|
|
458
563
|
*/
|
|
459
|
-
LeaderChanged = "leaderChanged"
|
|
564
|
+
LeaderChanged = "leaderChanged",
|
|
565
|
+
/**
|
|
566
|
+
* `Completar` completed a transfer, and the target's SECOND ring either connected or didn't —
|
|
567
|
+
* `docs/specs/transfer-outcome-relay.md` (Phase A). Fires for BOTH outcomes so a host can react to
|
|
568
|
+
* either; only `'not-connected'` is worth surfacing to the agent (spec §5.5) — roughly a minute
|
|
569
|
+
* after the transfer for a target who never answers, seconds for a decline or the caller hanging up
|
|
570
|
+
* first. Requires a `TransferOutcomeSource` (from `coventia`/`apiBase`, or a host override); never
|
|
571
|
+
* fires at all when none is configured, since there is then nothing to poll.
|
|
572
|
+
*/
|
|
573
|
+
TransferOutcome = "transferOutcome",
|
|
574
|
+
/**
|
|
575
|
+
* `softphone.presence` changed — the live agent-presence list (Phase B,
|
|
576
|
+
* `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a
|
|
577
|
+
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being
|
|
578
|
+
* `online && isLeader`; never fires at all otherwise, same reasoning as `TransferOutcome`.
|
|
579
|
+
*/
|
|
580
|
+
PresenceChanged = "presenceChanged"
|
|
460
581
|
}
|
|
461
582
|
interface SoftphoneEventMap {
|
|
462
583
|
[SoftphoneEvent.StateChanged]: {
|
|
@@ -473,6 +594,10 @@ interface SoftphoneEventMap {
|
|
|
473
594
|
[SoftphoneEvent.LeaderChanged]: {
|
|
474
595
|
readonly isLeader: boolean;
|
|
475
596
|
};
|
|
597
|
+
[SoftphoneEvent.TransferOutcome]: TransferOutcomeEvent;
|
|
598
|
+
[SoftphoneEvent.PresenceChanged]: {
|
|
599
|
+
readonly agents: readonly PresenceEntry[];
|
|
600
|
+
};
|
|
476
601
|
}
|
|
477
602
|
declare enum CallEvent {
|
|
478
603
|
/** Any transition. The catch-all; the specific events below are conveniences. */
|
|
@@ -663,18 +788,40 @@ interface Call extends TypedEmitter<CallEventMap> {
|
|
|
663
788
|
hold(): Promise<void>;
|
|
664
789
|
unhold(): Promise<void>;
|
|
665
790
|
sendDigits(digits: string): void;
|
|
666
|
-
/**
|
|
791
|
+
/**
|
|
792
|
+
* Blind transfer (SIP REFER). Ends this call — but **whether the transfer itself succeeded or
|
|
793
|
+
* failed is unknowable from here**: against Zadarma, a REFER's outcome is not reported back to the
|
|
794
|
+
* browser at all (confirmed on real calls — `docs/specs/transfer-availability.md` §2), so a REFER to
|
|
795
|
+
* an unreachable target ends this call exactly the same way a successful one does. Prefer
|
|
796
|
+
* `startConsult`/`completeTransfer` (below) when the caller must not be put at risk — that path
|
|
797
|
+
* correctly reports busy/no-answer/failed because the consult leg is an ordinary outbound call, not
|
|
798
|
+
* a REFER.
|
|
799
|
+
*/
|
|
667
800
|
transfer(target: string): Promise<void>;
|
|
668
801
|
/**
|
|
669
802
|
* Attended transfer, part 1: call the target while holding this call.
|
|
670
803
|
*
|
|
671
|
-
* Returns the consult leg — itself a Call, with its own state machine and events.
|
|
804
|
+
* Returns the consult leg — itself a Call, with its own state machine and events. Unlike blind
|
|
805
|
+
* `transfer()`, a consult leg that goes unanswered reports a real reason (busy/no-answer/rejected/
|
|
806
|
+
* failed) and never puts this call at risk — see `docs/specs/transfer-availability.md` §5.
|
|
672
807
|
*/
|
|
673
808
|
startConsult(target: string): Promise<Call>;
|
|
674
|
-
/**
|
|
809
|
+
/**
|
|
810
|
+
* Attended transfer, part 2: bridge the two, ending both. (REFER … Replaces)
|
|
811
|
+
*
|
|
812
|
+
* **Confirmed non-functional against Zadarma, on real calls, three times
|
|
813
|
+
* (`docs/specs/transfer-availability.md` §12.3).** Zadarma accepts the REFER…Replaces but never
|
|
814
|
+
* actually bridges the two legs — the consult leg is silently orphaned and the caller is lost, with
|
|
815
|
+
* no error thrown here to signal it. The pattern that DOES work (the same one `/ui`'s own
|
|
816
|
+
* `ConsultScreen` uses): hang up the consult leg, then call `transfer(target)` on THIS call, to the
|
|
817
|
+
* same already-confirmed-answering target — a blind transfer to a target already known to be there
|
|
818
|
+
* works, even though `completeTransfer()` itself does not. Left in the type for hosts against a
|
|
819
|
+
* different (non-Zadarma) SIP provider where REFER…Replaces may work as specified; do not reach for
|
|
820
|
+
* it against Zadarma.
|
|
821
|
+
*/
|
|
675
822
|
completeTransfer(): Promise<void>;
|
|
676
823
|
/** Abandon the consult and resume this call. */
|
|
677
824
|
cancelConsult(): Promise<void>;
|
|
678
825
|
}
|
|
679
826
|
|
|
680
|
-
export { type AgentsDirectory as A, CALL_TRANSITIONS as C, ERROR_CATALOGUE as E, type FatalErrorCode as F, type HangupReason as H, INITIAL_STATES as I, type LogLevel as L, type NonFatalErrorCode as N, type RemoteParty as R, type SoftphoneConfig as S, type TokenEndpointOptions as T, type VoiceToken as V, type Call as a, type CallDirection as b, CallEvent as c, type CallEventMap as d, type CallOptions as e, type CallState as f, type Contact as g, type FatalSoftphoneError as h, type HoldOriginator as i, type NonFatalSoftphoneError as j, type SoftphoneError as k, type SoftphoneErrorCode as l, SoftphoneEvent as m, type SoftphoneEventMap as n, type SoftphoneState as o, type TokenSource as p, type TypedEmitter as q, canTransition as r, isFatal as s, isTerminal as t, type ContactsSource as u, type HistorySource as v, type
|
|
827
|
+
export { type AgentsDirectory as A, CALL_TRANSITIONS as C, ERROR_CATALOGUE as E, type FatalErrorCode as F, type HangupReason as H, INITIAL_STATES as I, type LogLevel as L, type NonFatalErrorCode as N, type PresenceEntry as P, type RemoteParty as R, type SoftphoneConfig as S, type TokenEndpointOptions as T, type VoiceToken as V, type Call as a, type CallDirection as b, CallEvent as c, type CallEventMap as d, type CallOptions as e, type CallState as f, type Contact as g, type FatalSoftphoneError as h, type HoldOriginator as i, type NonFatalSoftphoneError as j, type SoftphoneError as k, type SoftphoneErrorCode as l, SoftphoneEvent as m, type SoftphoneEventMap as n, type SoftphoneState as o, type TokenSource as p, type TypedEmitter as q, canTransition as r, isFatal as s, isTerminal as t, type ContactsSource as u, type HistorySource as v, type TransferOutcomesPage as w, type PresenceSource as x, type CallHistoryEntry as y, type ContactsPage as z };
|
|
@@ -117,6 +117,19 @@ interface CoventiaBackendConfig {
|
|
|
117
117
|
* (SPEC §6.6).
|
|
118
118
|
*/
|
|
119
119
|
readonly contacts?: ContactsSource;
|
|
120
|
+
/**
|
|
121
|
+
* Transfer outcomes, overriding Coventia's own polling — for a `MockProvider`-style test double, or
|
|
122
|
+
* a future non-Zadarma provider that computes outcomes differently. Not a per-partner customisation
|
|
123
|
+
* point the way `contacts` is: unlike a contact list, a transfer outcome is not a partner's own
|
|
124
|
+
* data, so there is normally no reason to set this (`docs/specs/transfer-outcome-relay.md`, Phase A).
|
|
125
|
+
*/
|
|
126
|
+
readonly transferOutcomes?: TransferOutcomeSource;
|
|
127
|
+
/**
|
|
128
|
+
* Live agent presence, overriding Coventia's own WebSocket — for a `MockProvider`-style test
|
|
129
|
+
* double, or a host with its own presence transport. Not a per-partner customisation point the way
|
|
130
|
+
* `contacts` is (`docs/specs/transfer-presence-picker.md`, Phase B).
|
|
131
|
+
*/
|
|
132
|
+
readonly presence?: PresenceSource;
|
|
120
133
|
}
|
|
121
134
|
interface SoftphoneConfig {
|
|
122
135
|
readonly tokenSource: TokenSource;
|
|
@@ -261,6 +274,98 @@ interface AgentsDirectory {
|
|
|
261
274
|
readonly me: PhoneAgent | null;
|
|
262
275
|
readonly people: readonly PhoneAgent[];
|
|
263
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* `'connected'` | `'not-connected'` — whether an attended transfer's second ring, after `Completar`,
|
|
279
|
+
* actually reached the target. Kebab-case to match `HangupReason` (`'no-answer'`, `'transferred'`);
|
|
280
|
+
* the backend's own wire value stays `not_connected` — this is the one place that gets translated.
|
|
281
|
+
*/
|
|
282
|
+
type TransferOutcomeStatus = 'connected' | 'not-connected';
|
|
283
|
+
/** One resolved transfer, as `GET /phone/transfers/outcomes` reports it. */
|
|
284
|
+
interface TransferOutcomeEntry {
|
|
285
|
+
/** Opaque, strictly increasing per organization, never re-issued. The cursor for the NEXT poll. */
|
|
286
|
+
readonly id: string;
|
|
287
|
+
/** The extension the call was transferred to. */
|
|
288
|
+
readonly target: string;
|
|
289
|
+
readonly status: TransferOutcomeStatus;
|
|
290
|
+
readonly at: Date;
|
|
291
|
+
}
|
|
292
|
+
interface TransferOutcomesPage {
|
|
293
|
+
/** The newest `id` currently retained, or `'0'` when there are none — pass back as `since`. */
|
|
294
|
+
readonly cursor: string;
|
|
295
|
+
readonly outcomes: readonly TransferOutcomeEntry[];
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Poll for transfer outcomes. `since` omitted ⇒ everything currently retained — used for the FIRST
|
|
299
|
+
* poll after starting a watch, which exists only to learn the current cursor (spec §5.3's
|
|
300
|
+
* no-mis-attribution rule: that first poll's own outcomes are discarded, only `cursor` is kept).
|
|
301
|
+
*/
|
|
302
|
+
type TransferOutcomeSource = (params: {
|
|
303
|
+
readonly since?: string;
|
|
304
|
+
}) => Promise<TransferOutcomesPage>;
|
|
305
|
+
/**
|
|
306
|
+
* `SoftphoneEvent.TransferOutcome`'s payload. Unlike `TransferOutcomeEntry`, this never carries `id`
|
|
307
|
+
* (a polling-internal concern) and adds `caller` — the backend never stores a caller number (spec
|
|
308
|
+
* §5.3: "no caller number, nothing about other users"), so this is filled in locally, from the `Call`
|
|
309
|
+
* that was transferred, at the moment an outcome is matched to it.
|
|
310
|
+
*/
|
|
311
|
+
interface TransferOutcomeEvent {
|
|
312
|
+
readonly status: TransferOutcomeStatus;
|
|
313
|
+
readonly target: string;
|
|
314
|
+
/** The original caller's number, for "Llamar de vuelta" — null if it was never known. */
|
|
315
|
+
readonly caller: string | null;
|
|
316
|
+
readonly at: Date;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* `'disponible'` | `'en_llamada'` | `'desconectado'` — Spanish because this is what the picker shows,
|
|
320
|
+
* not an internal code (spec §4.1). `derivePresence` (the backend's own reducer, mirrored nowhere in
|
|
321
|
+
* the SDK — the SDK only ever receives an already-derived state, never `onCall`/`onlineFresh` apart).
|
|
322
|
+
*/
|
|
323
|
+
type PresenceState = 'disponible' | 'en_llamada' | 'desconectado';
|
|
324
|
+
/** One org member with a phone extension, as the picker lists them. */
|
|
325
|
+
interface PresenceEntry {
|
|
326
|
+
readonly userId: string;
|
|
327
|
+
readonly extension: string;
|
|
328
|
+
/** `null` when the backend has no name to offer — the picker falls back to the extension. */
|
|
329
|
+
readonly name: string | null;
|
|
330
|
+
readonly state: PresenceState;
|
|
331
|
+
}
|
|
332
|
+
/** What a live presence connection hands back once open. */
|
|
333
|
+
interface PresenceConnection {
|
|
334
|
+
/**
|
|
335
|
+
* Report this softphone's own on-call-ness, so a colleague transferring TO this agent sees it
|
|
336
|
+
* without a round trip through the webhook path (spec §4.1's `onCall` OR-combine). Calling this
|
|
337
|
+
* before the connection is genuinely open is never required of an implementation — a caller (see
|
|
338
|
+
* `presence-socket.ts`) that reports early is expected to wait for the connection to exist first.
|
|
339
|
+
*/
|
|
340
|
+
readonly reportCallState: (onCall: boolean) => void;
|
|
341
|
+
/**
|
|
342
|
+
* Ends this connection deliberately. Never causes the SAME callbacks' `onClose` to fire — that is
|
|
343
|
+
* reserved for the connection ending on its OWN (a network drop, a server-side close, a rejected
|
|
344
|
+
* ticket), which is the only thing a caller should ever treat as "reconnect now".
|
|
345
|
+
*/
|
|
346
|
+
readonly close: () => void;
|
|
347
|
+
}
|
|
348
|
+
interface PresenceCallbacks {
|
|
349
|
+
/** Once, right after connecting — the full current roster (spec §4.3). */
|
|
350
|
+
readonly onSnapshot: (agents: readonly PresenceEntry[]) => void;
|
|
351
|
+
/** Zero or more times after that — one agent whose state just changed. */
|
|
352
|
+
readonly onDelta: (agent: PresenceEntry) => void;
|
|
353
|
+
/**
|
|
354
|
+
* The connection ended on its own. Never called after the CALLER's own `close()` — see
|
|
355
|
+
* `PresenceConnection.close`'s own note. The only signal a watch layer needs to know it must
|
|
356
|
+
* reconnect, deliberately carrying no reason: a dropped connection and a rejected ticket look
|
|
357
|
+
* identical from here, and both get the same treatment (spec §4.4 — quiet retry, nothing surfaced).
|
|
358
|
+
*/
|
|
359
|
+
readonly onClose: () => void;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
|
|
363
|
+
* `wss://…/phone/presence`); a host may override it entirely, or omit it — omitted, or while the
|
|
364
|
+
* connection is down, the picker simply has no live data to show (spec §4.4, success criterion #5:
|
|
365
|
+
* no error surfaces). Unlike `TransferOutcomeSource` (poll, get a page, done), this is push-based and
|
|
366
|
+
* long-lived — the returned promise resolves once, when the connection opens, not once per update.
|
|
367
|
+
*/
|
|
368
|
+
type PresenceSource = (callbacks: PresenceCallbacks) => Promise<PresenceConnection>;
|
|
264
369
|
|
|
265
370
|
/**
|
|
266
371
|
* The error taxonomy.
|
|
@@ -456,7 +561,23 @@ declare enum SoftphoneEvent {
|
|
|
456
561
|
* `StateChanged`: `state` is reachability, this is whether this tab is even allowed to be
|
|
457
562
|
* reachable at all while another tab holding the same extension stays open.
|
|
458
563
|
*/
|
|
459
|
-
LeaderChanged = "leaderChanged"
|
|
564
|
+
LeaderChanged = "leaderChanged",
|
|
565
|
+
/**
|
|
566
|
+
* `Completar` completed a transfer, and the target's SECOND ring either connected or didn't —
|
|
567
|
+
* `docs/specs/transfer-outcome-relay.md` (Phase A). Fires for BOTH outcomes so a host can react to
|
|
568
|
+
* either; only `'not-connected'` is worth surfacing to the agent (spec §5.5) — roughly a minute
|
|
569
|
+
* after the transfer for a target who never answers, seconds for a decline or the caller hanging up
|
|
570
|
+
* first. Requires a `TransferOutcomeSource` (from `coventia`/`apiBase`, or a host override); never
|
|
571
|
+
* fires at all when none is configured, since there is then nothing to poll.
|
|
572
|
+
*/
|
|
573
|
+
TransferOutcome = "transferOutcome",
|
|
574
|
+
/**
|
|
575
|
+
* `softphone.presence` changed — the live agent-presence list (Phase B,
|
|
576
|
+
* `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a
|
|
577
|
+
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being
|
|
578
|
+
* `online && isLeader`; never fires at all otherwise, same reasoning as `TransferOutcome`.
|
|
579
|
+
*/
|
|
580
|
+
PresenceChanged = "presenceChanged"
|
|
460
581
|
}
|
|
461
582
|
interface SoftphoneEventMap {
|
|
462
583
|
[SoftphoneEvent.StateChanged]: {
|
|
@@ -473,6 +594,10 @@ interface SoftphoneEventMap {
|
|
|
473
594
|
[SoftphoneEvent.LeaderChanged]: {
|
|
474
595
|
readonly isLeader: boolean;
|
|
475
596
|
};
|
|
597
|
+
[SoftphoneEvent.TransferOutcome]: TransferOutcomeEvent;
|
|
598
|
+
[SoftphoneEvent.PresenceChanged]: {
|
|
599
|
+
readonly agents: readonly PresenceEntry[];
|
|
600
|
+
};
|
|
476
601
|
}
|
|
477
602
|
declare enum CallEvent {
|
|
478
603
|
/** Any transition. The catch-all; the specific events below are conveniences. */
|
|
@@ -663,18 +788,40 @@ interface Call extends TypedEmitter<CallEventMap> {
|
|
|
663
788
|
hold(): Promise<void>;
|
|
664
789
|
unhold(): Promise<void>;
|
|
665
790
|
sendDigits(digits: string): void;
|
|
666
|
-
/**
|
|
791
|
+
/**
|
|
792
|
+
* Blind transfer (SIP REFER). Ends this call — but **whether the transfer itself succeeded or
|
|
793
|
+
* failed is unknowable from here**: against Zadarma, a REFER's outcome is not reported back to the
|
|
794
|
+
* browser at all (confirmed on real calls — `docs/specs/transfer-availability.md` §2), so a REFER to
|
|
795
|
+
* an unreachable target ends this call exactly the same way a successful one does. Prefer
|
|
796
|
+
* `startConsult`/`completeTransfer` (below) when the caller must not be put at risk — that path
|
|
797
|
+
* correctly reports busy/no-answer/failed because the consult leg is an ordinary outbound call, not
|
|
798
|
+
* a REFER.
|
|
799
|
+
*/
|
|
667
800
|
transfer(target: string): Promise<void>;
|
|
668
801
|
/**
|
|
669
802
|
* Attended transfer, part 1: call the target while holding this call.
|
|
670
803
|
*
|
|
671
|
-
* Returns the consult leg — itself a Call, with its own state machine and events.
|
|
804
|
+
* Returns the consult leg — itself a Call, with its own state machine and events. Unlike blind
|
|
805
|
+
* `transfer()`, a consult leg that goes unanswered reports a real reason (busy/no-answer/rejected/
|
|
806
|
+
* failed) and never puts this call at risk — see `docs/specs/transfer-availability.md` §5.
|
|
672
807
|
*/
|
|
673
808
|
startConsult(target: string): Promise<Call>;
|
|
674
|
-
/**
|
|
809
|
+
/**
|
|
810
|
+
* Attended transfer, part 2: bridge the two, ending both. (REFER … Replaces)
|
|
811
|
+
*
|
|
812
|
+
* **Confirmed non-functional against Zadarma, on real calls, three times
|
|
813
|
+
* (`docs/specs/transfer-availability.md` §12.3).** Zadarma accepts the REFER…Replaces but never
|
|
814
|
+
* actually bridges the two legs — the consult leg is silently orphaned and the caller is lost, with
|
|
815
|
+
* no error thrown here to signal it. The pattern that DOES work (the same one `/ui`'s own
|
|
816
|
+
* `ConsultScreen` uses): hang up the consult leg, then call `transfer(target)` on THIS call, to the
|
|
817
|
+
* same already-confirmed-answering target — a blind transfer to a target already known to be there
|
|
818
|
+
* works, even though `completeTransfer()` itself does not. Left in the type for hosts against a
|
|
819
|
+
* different (non-Zadarma) SIP provider where REFER…Replaces may work as specified; do not reach for
|
|
820
|
+
* it against Zadarma.
|
|
821
|
+
*/
|
|
675
822
|
completeTransfer(): Promise<void>;
|
|
676
823
|
/** Abandon the consult and resume this call. */
|
|
677
824
|
cancelConsult(): Promise<void>;
|
|
678
825
|
}
|
|
679
826
|
|
|
680
|
-
export { type AgentsDirectory as A, CALL_TRANSITIONS as C, ERROR_CATALOGUE as E, type FatalErrorCode as F, type HangupReason as H, INITIAL_STATES as I, type LogLevel as L, type NonFatalErrorCode as N, type RemoteParty as R, type SoftphoneConfig as S, type TokenEndpointOptions as T, type VoiceToken as V, type Call as a, type CallDirection as b, CallEvent as c, type CallEventMap as d, type CallOptions as e, type CallState as f, type Contact as g, type FatalSoftphoneError as h, type HoldOriginator as i, type NonFatalSoftphoneError as j, type SoftphoneError as k, type SoftphoneErrorCode as l, SoftphoneEvent as m, type SoftphoneEventMap as n, type SoftphoneState as o, type TokenSource as p, type TypedEmitter as q, canTransition as r, isFatal as s, isTerminal as t, type ContactsSource as u, type HistorySource as v, type
|
|
827
|
+
export { type AgentsDirectory as A, CALL_TRANSITIONS as C, ERROR_CATALOGUE as E, type FatalErrorCode as F, type HangupReason as H, INITIAL_STATES as I, type LogLevel as L, type NonFatalErrorCode as N, type PresenceEntry as P, type RemoteParty as R, type SoftphoneConfig as S, type TokenEndpointOptions as T, type VoiceToken as V, type Call as a, type CallDirection as b, CallEvent as c, type CallEventMap as d, type CallOptions as e, type CallState as f, type Contact as g, type FatalSoftphoneError as h, type HoldOriginator as i, type NonFatalSoftphoneError as j, type SoftphoneError as k, type SoftphoneErrorCode as l, SoftphoneEvent as m, type SoftphoneEventMap as n, type SoftphoneState as o, type TokenSource as p, type TypedEmitter as q, canTransition as r, isFatal as s, isTerminal as t, type ContactsSource as u, type HistorySource as v, type TransferOutcomesPage as w, type PresenceSource as x, type CallHistoryEntry as y, type ContactsPage as z };
|
|
@@ -7,6 +7,8 @@ var SoftphoneEvent = /* @__PURE__ */ ((SoftphoneEvent2) => {
|
|
|
7
7
|
SoftphoneEvent2["DeviceChange"] = "deviceChange";
|
|
8
8
|
SoftphoneEvent2["Error"] = "error";
|
|
9
9
|
SoftphoneEvent2["LeaderChanged"] = "leaderChanged";
|
|
10
|
+
SoftphoneEvent2["TransferOutcome"] = "transferOutcome";
|
|
11
|
+
SoftphoneEvent2["PresenceChanged"] = "presenceChanged";
|
|
10
12
|
return SoftphoneEvent2;
|
|
11
13
|
})(SoftphoneEvent || {});
|
|
12
14
|
var CallEvent = /* @__PURE__ */ ((CallEvent2) => {
|
|
@@ -25,5 +27,5 @@ var CallEvent = /* @__PURE__ */ ((CallEvent2) => {
|
|
|
25
27
|
})(CallEvent || {});
|
|
26
28
|
|
|
27
29
|
export { CallEvent, SoftphoneEvent };
|
|
28
|
-
//# sourceMappingURL=chunk-
|
|
29
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-7R6LOOAI.js.map
|
|
31
|
+
//# sourceMappingURL=chunk-7R6LOOAI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/events.ts"],"names":["SoftphoneEvent","CallEvent"],"mappings":";AAcO,IAAK,cAAA,qBAAAA,eAAAA,KAAL;AAEL,EAAAA,gBAAA,cAAA,CAAA,GAAe,cAAA;AAEf,EAAAA,gBAAA,cAAA,CAAA,GAAe,cAAA;AAMf,EAAAA,gBAAA,mBAAA,CAAA,GAAoB,mBAAA;AAQpB,EAAAA,gBAAA,iBAAA,CAAA,GAAkB,iBAAA;AAOlB,EAAAA,gBAAA,cAAA,CAAA,GAAe,cAAA;AAEf,EAAAA,gBAAA,OAAA,CAAA,GAAQ,OAAA;AAMR,EAAAA,gBAAA,eAAA,CAAA,GAAgB,eAAA;AAShB,EAAAA,gBAAA,iBAAA,CAAA,GAAkB,iBAAA;AAOlB,EAAAA,gBAAA,iBAAA,CAAA,GAAkB,iBAAA;AAjDR,EAAA,OAAAA,eAAAA;AAAA,CAAA,EAAA,cAAA,IAAA,EAAA;AAgEL,IAAK,SAAA,qBAAAC,UAAAA,KAAL;AAEL,EAAAA,WAAA,cAAA,CAAA,GAAe,cAAA;AAEf,EAAAA,WAAA,UAAA,CAAA,GAAW,UAAA;AAEX,EAAAA,WAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,WAAA,OAAA,CAAA,GAAQ,OAAA;AAER,EAAAA,WAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,WAAA,QAAA,CAAA,GAAS,QAAA;AAET,EAAAA,WAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,WAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,WAAA,YAAA,CAAA,GAAa,YAAA;AAEb,EAAAA,WAAA,oBAAA,CAAA,GAAqB,oBAAA;AAMrB,EAAAA,WAAA,cAAA,CAAA,GAAe,cAAA;AAtBL,EAAA,OAAAA,UAAAA;AAAA,CAAA,EAAA,SAAA,IAAA,EAAA","file":"chunk-7R6LOOAI.js","sourcesContent":["import type { Call, CallState, HangupReason, HoldOriginator } from './call.js';\r\nimport type { SoftphoneError } from './errors.js';\r\nimport type { PresenceEntry, SoftphoneState, TransferOutcomeEvent } from './types.js';\r\n\r\n/**\r\n * Typed events.\r\n *\r\n * Enums rather than magic strings: autocomplete, and rename-safety. Daily's magic strings are the\r\n * counter-example — a typo in `'participant-joined'` is a silent no-op forever.\r\n *\r\n * Each map narrows the listener payload PER EVENT, which is the thing that would have been painful\r\n * to express in JSDoc and is a large part of why this package is TypeScript (SPEC §2.2).\r\n */\r\n\r\nexport enum SoftphoneEvent {\r\n /** Lifecycle. `online` = reachable, NOT SIP-registered — see SoftphoneState. */\r\n StateChanged = 'stateChanged',\r\n /** An INVITE landed. The Call is `ringing`; answer or reject it. */\r\n IncomingCall = 'incomingCall',\r\n /**\r\n * The active call changed — a call began (inbound OR outbound) or ended. Fires for both\r\n * directions, unlike IncomingCall. This is what a UI binds to in order to track \"the current call\"\r\n * without threading the Call through every component.\r\n */\r\n ActiveCallChanged = 'activeCallChanged',\r\n /**\r\n * The browser refused to play audio (autoplay policy).\r\n *\r\n * This is the single most under-documented failure in the industry and it is how calls get MISSED:\r\n * a blocked ringtone rings silently. Surface a button and call `startAudio()` from its click\r\n * handler. Only Twilio and LiveKit document it properly.\r\n */\r\n PlaybackBlocked = 'playbackBlocked',\r\n /**\r\n * `softphone.audio` changed: a device was plugged/unplugged (`inputs`/`outputs`), or the selected\r\n * input/output changed (`currentInputId`/`currentOutputId`). One event for all of it — re-read\r\n * `softphone.audio` rather than inspecting the payload, which carries nothing (`useDevices` does\r\n * exactly this).\r\n */\r\n DeviceChange = 'deviceChange',\r\n /** Fatal or non-fatal — narrow on `error.fatal`. */\r\n Error = 'error',\r\n /**\r\n * This tab's leadership changed (`multi-tab-leader-election.md`) — a DIFFERENT axis from\r\n * `StateChanged`: `state` is reachability, this is whether this tab is even allowed to be\r\n * reachable at all while another tab holding the same extension stays open.\r\n */\r\n LeaderChanged = 'leaderChanged',\r\n /**\r\n * `Completar` completed a transfer, and the target's SECOND ring either connected or didn't —\r\n * `docs/specs/transfer-outcome-relay.md` (Phase A). Fires for BOTH outcomes so a host can react to\r\n * either; only `'not-connected'` is worth surfacing to the agent (spec §5.5) — roughly a minute\r\n * after the transfer for a target who never answers, seconds for a decline or the caller hanging up\r\n * first. Requires a `TransferOutcomeSource` (from `coventia`/`apiBase`, or a host override); never\r\n * fires at all when none is configured, since there is then nothing to poll.\r\n */\r\n TransferOutcome = 'transferOutcome',\r\n /**\r\n * `softphone.presence` changed — the live agent-presence list (Phase B,\r\n * `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a\r\n * `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being\r\n * `online && isLeader`; never fires at all otherwise, same reasoning as `TransferOutcome`.\r\n */\r\n PresenceChanged = 'presenceChanged',\r\n}\r\n\r\nexport interface SoftphoneEventMap {\r\n [SoftphoneEvent.StateChanged]: { readonly state: SoftphoneState; readonly previous: SoftphoneState };\r\n [SoftphoneEvent.IncomingCall]: Call;\r\n [SoftphoneEvent.ActiveCallChanged]: { readonly call: Call | null };\r\n [SoftphoneEvent.PlaybackBlocked]: void;\r\n [SoftphoneEvent.DeviceChange]: void;\r\n [SoftphoneEvent.Error]: SoftphoneError;\r\n [SoftphoneEvent.LeaderChanged]: { readonly isLeader: boolean };\r\n [SoftphoneEvent.TransferOutcome]: TransferOutcomeEvent;\r\n [SoftphoneEvent.PresenceChanged]: { readonly agents: readonly PresenceEntry[] };\r\n}\r\n\r\nexport enum CallEvent {\r\n /** Any transition. The catch-all; the specific events below are conveniences. */\r\n StateChanged = 'stateChanged',\r\n /** Media is flowing. */\r\n Accepted = 'accepted',\r\n /** Terminal. Always fires exactly once, whatever the reason. */\r\n Ended = 'ended',\r\n Muted = 'muted',\r\n /** `originator: 'remote'` means the OTHER party held US — new capability; today's softphone is blind to it. */\r\n Held = 'held',\r\n Unheld = 'unheld',\r\n /** Media lost; an ICE restart is in flight. The call is NOT over — show a banner. */\r\n Reconnecting = 'reconnecting',\r\n Reconnected = 'reconnected',\r\n DigitsSent = 'digitsSent',\r\n /** The contact name resolved after the call was already ringing. */\r\n RemotePartyUpdated = 'remotePartyUpdated',\r\n /**\r\n * The remote audio stream is available. Attach it to an `<audio>` element and play it — this is\r\n * how the caller is actually HEARD. Core cannot do this itself (it must not touch `document` —\r\n * SPEC §10.2); the UI layer or the host owns the audio element.\r\n */\r\n RemoteStream = 'remoteStream',\r\n}\r\n\r\nexport interface CallEventMap {\r\n [CallEvent.StateChanged]: { readonly state: CallState; readonly previous: CallState };\r\n [CallEvent.Accepted]: void;\r\n [CallEvent.Ended]: { readonly reason: HangupReason; readonly error?: SoftphoneError };\r\n [CallEvent.Muted]: { readonly muted: boolean };\r\n [CallEvent.Held]: { readonly originator: HoldOriginator };\r\n [CallEvent.Unheld]: { readonly originator: HoldOriginator };\r\n [CallEvent.Reconnecting]: void;\r\n [CallEvent.Reconnected]: void;\r\n [CallEvent.DigitsSent]: { readonly digits: string };\r\n [CallEvent.RemotePartyUpdated]: void;\r\n [CallEvent.RemoteStream]: { readonly stream: MediaStream };\r\n}\r\n\r\n/**\r\n * `on()` returns an unsubscribe function.\r\n *\r\n * Deliberate: it makes the cleanup impossible to get wrong in a React effect\r\n * (`useEffect(() => call.on(...), [call])`) and it avoids Vonage's design, where `on()` returns an\r\n * opaque `symbol` you must retain in order to ever call `off()`.\r\n */\r\nexport interface TypedEmitter<M> {\r\n on<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void;\r\n once<E extends keyof M>(event: E, listener: (payload: M[E]) => void): () => void;\r\n off<E extends keyof M>(event: E, listener: (payload: M[E]) => void): void;\r\n}\r\n"]}
|
|
@@ -51,6 +51,20 @@ function useActiveCall() {
|
|
|
51
51
|
useCallback(() => phone.activeCall, [phone])
|
|
52
52
|
);
|
|
53
53
|
}
|
|
54
|
+
function usePresence(active) {
|
|
55
|
+
const phone = useSoftphone();
|
|
56
|
+
return useSyncExternalStore(
|
|
57
|
+
useCallback(
|
|
58
|
+
(cb) => {
|
|
59
|
+
if (!active) return () => {
|
|
60
|
+
};
|
|
61
|
+
return phone.on("presenceChanged" /* PresenceChanged */, () => cb());
|
|
62
|
+
},
|
|
63
|
+
[phone, active]
|
|
64
|
+
),
|
|
65
|
+
useCallback(() => phone.presence, [phone])
|
|
66
|
+
);
|
|
67
|
+
}
|
|
54
68
|
var CALL_EVENTS = [
|
|
55
69
|
"stateChanged" /* StateChanged */,
|
|
56
70
|
"muted" /* Muted */,
|
|
@@ -124,6 +138,6 @@ function useDevices() {
|
|
|
124
138
|
return useSyncExternalStore(subscribe, getSnapshot);
|
|
125
139
|
}
|
|
126
140
|
|
|
127
|
-
export { SoftphoneProvider, useActiveCall, useCall, useDevices, useIsLeader, useLastError, useSoftphone, useSoftphoneState };
|
|
128
|
-
//# sourceMappingURL=chunk-
|
|
129
|
-
//# sourceMappingURL=chunk-
|
|
141
|
+
export { SoftphoneProvider, useActiveCall, useCall, useDevices, useIsLeader, useLastError, usePresence, useSoftphone, useSoftphoneState };
|
|
142
|
+
//# sourceMappingURL=chunk-LMJXX4YM.js.map
|
|
143
|
+
//# sourceMappingURL=chunk-LMJXX4YM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/context.tsx","../src/react/hooks.ts"],"names":[],"mappings":"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","file":"chunk-LMJXX4YM.js","sourcesContent":["import { createContext, useContext, type ReactNode } from 'react';\r\nimport type { Softphone } from '../core/softphone.js';\r\n\r\n/**\r\n * The provider takes an INSTANCE the host constructed — not config props.\r\n *\r\n * This is LiveKit's hard-won lesson (SPEC §5.1): they are deprecating their config-props\r\n * `<LiveKitRoom>` in favour of a provider that accepts an instance. Holding the `Softphone` above the\r\n * React tree lets the app own the connection, survive route changes and unmounts without dropping a\r\n * call, and unit-test the engine with no React at all. Config-props would rebuild the engine on\r\n * every prop change — catastrophic for something holding a live `RTCPeerConnection`.\r\n */\r\nconst SoftphoneContext = createContext<Softphone | null>(null);\r\n\r\nexport interface SoftphoneProviderProps {\r\n readonly softphone: Softphone;\r\n readonly children: ReactNode;\r\n}\r\n\r\nexport function SoftphoneProvider({ softphone, children }: SoftphoneProviderProps) {\r\n return <SoftphoneContext.Provider value={softphone}>{children}</SoftphoneContext.Provider>;\r\n}\r\n\r\n/** The `Softphone` instance from context. Stable — does not re-render on state changes. */\r\nexport function useSoftphone(): Softphone {\r\n const softphone = useContext(SoftphoneContext);\r\n if (!softphone) {\r\n throw new Error('useSoftphone() must be used within a <SoftphoneProvider>.');\r\n }\r\n return softphone;\r\n}\r\n","// Built-in, no shim: the peer floor is React 18 (SPEC §2.7), which ships useSyncExternalStore.\r\nimport { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { useSoftphone } from './context.js';\r\nimport { SoftphoneEvent, CallEvent } from '../core/events.js';\r\nimport type { Call, CallState } from '../core/call.js';\r\nimport type { PresenceEntry, RemoteParty, SoftphoneState } from '../core/types.js';\r\nimport type { AudioDevices } from '../core/softphone.js';\r\nimport type { FatalSoftphoneError } from '../core/errors.js';\r\n\r\n/** Reactive softphone lifecycle state (`stopped` → `online` → …). */\r\n/**\r\n * The reason the phone cannot currently be used, or `null`.\r\n *\r\n * Reads `softphone.lastError`, which is STATE rather than only an event — the widget is usually\r\n * opened *after* something broke, and a listener mounted at that point would have missed the event\r\n * entirely and rendered a dialpad with no explanation.\r\n *\r\n * Re-reads on both channels because both can change it: `Error` sets it, and `StateChanged` clears it\r\n * on reaching `online`. Subscribing to only the first would leave a stale gate on screen after the\r\n * phone recovered.\r\n */\r\nexport function useLastError(): FatalSoftphoneError | null {\r\n const phone = useSoftphone();\r\n return useSyncExternalStore(\r\n useCallback(\r\n (cb: () => void) => {\r\n const offError = phone.on(SoftphoneEvent.Error, () => cb());\r\n const offState = phone.on(SoftphoneEvent.StateChanged, () => cb());\r\n return () => {\r\n offError();\r\n offState();\r\n };\r\n },\r\n [phone],\r\n ),\r\n useCallback(() => phone.lastError, [phone]),\r\n );\r\n}\r\n\r\nexport function useSoftphoneState(): SoftphoneState {\r\n const phone = useSoftphone();\r\n return useSyncExternalStore(\r\n useCallback((cb: () => void) => phone.on(SoftphoneEvent.StateChanged, () => cb()), [phone]),\r\n useCallback(() => phone.state, [phone]),\r\n );\r\n}\r\n\r\n/**\r\n * Whether THIS tab is the one allowed to be reachable (`multi-tab-leader-election.md`). `false` by\r\n * default and while waiting for another open tab to release it — show your own \"active in another\r\n * tab\" state for that duration; the `/ui` widget already does.\r\n */\r\nexport function useIsLeader(): boolean {\r\n const phone = useSoftphone();\r\n return useSyncExternalStore(\r\n useCallback((cb: () => void) => phone.on(SoftphoneEvent.LeaderChanged, () => cb()), [phone]),\r\n useCallback(() => phone.isLeader, [phone]),\r\n );\r\n}\r\n\r\n/**\r\n * The current call, or null. Re-renders when it changes — inbound, outbound, or ended.\r\n *\r\n * `phone.activeCall` returns a stable reference for a given call, so it is a safe snapshot.\r\n */\r\nexport function useActiveCall(): Call | null {\r\n const phone = useSoftphone();\r\n return useSyncExternalStore(\r\n useCallback((cb: () => void) => phone.on(SoftphoneEvent.ActiveCallChanged, () => cb()), [phone]),\r\n useCallback(() => phone.activeCall, [phone]),\r\n );\r\n}\r\n\r\n/**\r\n * The live agent-presence list (`phone.presence`, Phase B) — reactive on `SoftphoneEvent.\r\n * PresenceChanged`. `active` gates the SUBSCRIPTION only, not the underlying connection: the socket\r\n * itself stays governed entirely by `online && isLeader` inside the softphone (spec's own\r\n * always-connected decision), so passing `false` here does not close it — it only stops this\r\n * particular component from re-rendering on every update while it has no reason to care, e.g. the\r\n * transfer step being closed (spec §4.5: \"subscribe when the step opens... unsubscribe when it\r\n * closes\"). `phone.presence` is still read fresh on every call, so flipping `active` back to `true`\r\n * never shows a stale snapshot.\r\n */\r\nexport function usePresence(active: boolean): readonly PresenceEntry[] {\r\n const phone = useSoftphone();\r\n return useSyncExternalStore(\r\n useCallback(\r\n (cb: () => void) => {\r\n if (!active) return () => {};\r\n return phone.on(SoftphoneEvent.PresenceChanged, () => cb());\r\n },\r\n [phone, active],\r\n ),\r\n useCallback(() => phone.presence, [phone]),\r\n );\r\n}\r\n\r\n/** An immutable snapshot of a call's reactive fields. */\r\nexport interface CallView {\r\n readonly state: CallState;\r\n readonly isMuted: boolean;\r\n readonly isOnHold: boolean;\r\n readonly remote: RemoteParty;\r\n readonly remoteStream: MediaStream | null;\r\n readonly answeredAt: Date | null;\r\n}\r\n\r\nconst CALL_EVENTS: readonly CallEvent[] = [\r\n CallEvent.StateChanged,\r\n CallEvent.Muted,\r\n CallEvent.Held,\r\n CallEvent.Unheld,\r\n CallEvent.RemotePartyUpdated,\r\n CallEvent.RemoteStream,\r\n CallEvent.Ended,\r\n];\r\n\r\n/**\r\n * Reactive view of a specific call. Re-renders only when one of the call's own events fires — no\r\n * polling, no re-render storms.\r\n *\r\n * The snapshot is CACHED and invalidated only on an event, so `useSyncExternalStore` gets a stable\r\n * reference between changes (returning a fresh object every render would infinite-loop).\r\n */\r\nexport function useCall(call: Call | null): CallView | null {\r\n const cache = useRef<{ call: Call; view: CallView } | null>(null);\r\n\r\n const subscribe = useCallback(\r\n (cb: () => void) => {\r\n if (!call) return () => {};\r\n const unsubs = CALL_EVENTS.map((event) =>\r\n call.on(event, () => {\r\n cache.current = null; // invalidate; getSnapshot rebuilds on the next read\r\n cb();\r\n }),\r\n );\r\n return () => {\r\n for (const off of unsubs) off();\r\n };\r\n },\r\n [call],\r\n );\r\n\r\n const getSnapshot = useCallback((): CallView | null => {\r\n if (!call) return null;\r\n if (cache.current?.call === call) return cache.current.view;\r\n const view: CallView = {\r\n state: call.state,\r\n isMuted: call.isMuted,\r\n isOnHold: call.isOnHold,\r\n remote: call.remote,\r\n remoteStream: call.remoteStream,\r\n answeredAt: call.answeredAt,\r\n };\r\n cache.current = { call, view };\r\n return view;\r\n }, [call]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot);\r\n}\r\n\r\n/**\r\n * The audio device surface (`phone.audio`) — reactive. Re-renders on `SoftphoneEvent.DeviceChange`: a\r\n * device plugged/unplugged, or a selection changed (SPEC §7 Phase 2).\r\n *\r\n * The snapshot is CACHED and invalidated only on that event, same reasoning as `useCall`'s own\r\n * `CallView`: `phone.audio` is ONE stable object whose fields mutate in place, so returning it\r\n * directly would never change by reference and `useSyncExternalStore` would never re-render. A fresh\r\n * object, rebuilt only when something actually changed, is what makes this reactive without polling.\r\n */\r\nexport function useDevices(): AudioDevices {\r\n const phone = useSoftphone();\r\n const cache = useRef<{ audio: AudioDevices; view: AudioDevices } | null>(null);\r\n\r\n const subscribe = useCallback(\r\n (cb: () => void) =>\r\n phone.on(SoftphoneEvent.DeviceChange, () => {\r\n cache.current = null; // invalidate; getSnapshot rebuilds on the next read\r\n cb();\r\n }),\r\n [phone],\r\n );\r\n\r\n const getSnapshot = useCallback((): AudioDevices => {\r\n const audio = phone.audio;\r\n if (cache.current?.audio === audio) return cache.current.view;\r\n const view: AudioDevices = {\r\n inputs: audio.inputs,\r\n outputs: audio.outputs,\r\n currentInputId: audio.currentInputId,\r\n currentOutputId: audio.currentOutputId,\r\n canSelectOutput: audio.canSelectOutput,\r\n setInput: (deviceId) => audio.setInput(deviceId),\r\n setOutput: (deviceId) => audio.setOutput(deviceId),\r\n setRingtoneOutput: (deviceId) => audio.setRingtoneOutput(deviceId),\r\n refresh: () => audio.refresh(),\r\n };\r\n cache.current = { audio, view };\r\n return view;\r\n }, [phone]);\r\n\r\n return useSyncExternalStore(subscribe, getSnapshot);\r\n}\r\n"]}
|
|
@@ -259,6 +259,14 @@ var SipCallEngine = class {
|
|
|
259
259
|
#uaInFlight = null;
|
|
260
260
|
#domain = "";
|
|
261
261
|
#activeCall = null;
|
|
262
|
+
/**
|
|
263
|
+
* The consult leg from `startConsult` (SPEC §5.6), if one is live on this UA right now.
|
|
264
|
+
*
|
|
265
|
+
* Tracked separately from `#activeCall` so `#maybeTeardownUA` can tell "both legs are actually gone"
|
|
266
|
+
* apart from "the main leg ended, but a consult is still up" — see that method's own comment for the
|
|
267
|
+
* bug this exists to prevent.
|
|
268
|
+
*/
|
|
269
|
+
#consultCall = null;
|
|
262
270
|
/** The chosen microphone, or `null` for the browser default. Read fresh by every new leg's
|
|
263
271
|
* `mediaConstraints`, and pushed live to `#activeCall` by `setInputDevice`. */
|
|
264
272
|
#inputDeviceId = null;
|
|
@@ -377,9 +385,7 @@ var SipCallEngine = class {
|
|
|
377
385
|
const ms = this.provider.quirks?.noInviteTeardownMs;
|
|
378
386
|
if (!ms) return;
|
|
379
387
|
this.#clearNoInviteTimer();
|
|
380
|
-
this.#noInviteTimer = setTimeout(() =>
|
|
381
|
-
if (!this.#activeCall) this.#teardownUA();
|
|
382
|
-
}, ms);
|
|
388
|
+
this.#noInviteTimer = setTimeout(() => this.#maybeTeardownUA(), ms);
|
|
383
389
|
}
|
|
384
390
|
/**
|
|
385
391
|
* Rebuild the incoming-signal channel with fresh credentials, after a registration failure.
|
|
@@ -501,6 +507,7 @@ var SipCallEngine = class {
|
|
|
501
507
|
}
|
|
502
508
|
this.#ua = null;
|
|
503
509
|
this.#activeCall = null;
|
|
510
|
+
this.#consultCall = null;
|
|
504
511
|
}
|
|
505
512
|
/** Place a call once the UA is registered (its 200 OK for REGISTER has arrived). */
|
|
506
513
|
/**
|
|
@@ -543,7 +550,7 @@ var SipCallEngine = class {
|
|
|
543
550
|
const code = missed ? "call/missed" : CALL_END_CODES[event.reason];
|
|
544
551
|
if (code) this.#reportError(code, event.cause);
|
|
545
552
|
if (this.#activeCall === call) this.#activeCall = null;
|
|
546
|
-
|
|
553
|
+
this.#maybeTeardownUA();
|
|
547
554
|
}
|
|
548
555
|
});
|
|
549
556
|
return call;
|
|
@@ -554,12 +561,38 @@ var SipCallEngine = class {
|
|
|
554
561
|
mediaConstraints: buildAudioConstraints(this.#inputDeviceId),
|
|
555
562
|
pcConfig: PC_CONFIG
|
|
556
563
|
});
|
|
557
|
-
|
|
564
|
+
const consultCall = new SipEngineCall(consultSession, {
|
|
558
565
|
id: nextCallId(),
|
|
559
566
|
buildTarget: (x) => this.#buildTarget(x),
|
|
560
567
|
placeConsult: () => Promise.reject(new Error("cannot consult from a consult leg")),
|
|
561
568
|
iceGatheringCapMs: this.provider.quirks?.iceGatheringCapMs ?? DEFAULT_ICE_CAP_MS
|
|
562
569
|
});
|
|
570
|
+
this.#consultCall = consultCall;
|
|
571
|
+
consultCall.onEvent((event) => {
|
|
572
|
+
if (event.type === "ended") {
|
|
573
|
+
if (this.#consultCall === consultCall) this.#consultCall = null;
|
|
574
|
+
this.#maybeTeardownUA();
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
return consultCall;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* On-demand registration (SPEC §3.3): the UA is disposable once NOTHING is left on it — but "the
|
|
581
|
+
* main leg ended" (or "no INVITE followed this push") is not the same fact as "nothing is left."
|
|
582
|
+
* `completeTransfer()` (SPEC §5.6) ends the main leg via a REFER…Replaces BYE *while the consult leg
|
|
583
|
+
* is still what the PBX needs to bridge the caller onto* — tearing the UA down (and with it, EVERY
|
|
584
|
+
* session on it, via `terminateSessions`) the instant the main leg's `ended` fires killed that
|
|
585
|
+
* consult leg out from under the transfer before the bridge could use it, and the caller was simply
|
|
586
|
+
* gone. Two real call sites hit this independently (found live, 2026-08-04): the main leg's own
|
|
587
|
+
* `ended` handler, and `#armNoInviteTeardown`'s 5s timer — the latter re-arms on EVERY push,
|
|
588
|
+
* including unrelated "answered" pushes Zadarma sends around other account activity, so it can fire
|
|
589
|
+
* minutes after a transfer with no `#activeCall` and no idea a consult leg is mid-handoff. Both now
|
|
590
|
+
* route through here rather than checking `#activeCall` alone.
|
|
591
|
+
*/
|
|
592
|
+
#maybeTeardownUA() {
|
|
593
|
+
if (this.provider.registrationMode !== "on-demand") return;
|
|
594
|
+
if (this.#activeCall || this.#consultCall) return;
|
|
595
|
+
this.#teardownUA();
|
|
563
596
|
}
|
|
564
597
|
#startRejectGuard() {
|
|
565
598
|
const ms = this.provider.quirks?.rejectGuardMs;
|
|
@@ -941,5 +974,5 @@ function createZadarmaEngine(config) {
|
|
|
941
974
|
}
|
|
942
975
|
|
|
943
976
|
export { ZADARMA_QUIRKS, createZadarmaEngine };
|
|
944
|
-
//# sourceMappingURL=chunk-
|
|
945
|
-
//# sourceMappingURL=chunk-
|
|
977
|
+
//# sourceMappingURL=chunk-YWI6EQYS.js.map
|
|
978
|
+
//# sourceMappingURL=chunk-YWI6EQYS.js.map
|