@coventia/softphone 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{call-BTYlT2SU.d.cts → call-VKq1I8yP.d.cts} +158 -5
- package/dist/{call-BTYlT2SU.d.ts → call-VKq1I8yP.d.ts} +158 -5
- package/dist/{chunk-N2N2EQQJ.js → chunk-7R6LOOAI.js} +4 -2
- package/dist/chunk-7R6LOOAI.js.map +1 -0
- package/dist/{chunk-5BGEHMPP.js → chunk-EYEDPD7Y.js} +146 -20
- package/dist/chunk-EYEDPD7Y.js.map +1 -0
- package/dist/{chunk-3V2L6UTK.js → chunk-LMJXX4YM.js} +17 -3
- package/dist/chunk-LMJXX4YM.js.map +1 -0
- package/dist/element/index.d.ts +162 -7
- package/dist/element/index.js +866 -421
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-D9nbeY08.d.cts → engine-BlQBo2Ys.d.cts} +1 -1
- package/dist/{engine-Doa6Brkm.d.ts → engine-C0BOkIcW.d.ts} +1 -1
- package/dist/index.cjs +586 -28
- 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 +335 -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-Cyq3hiIM.d.ts} +17 -2
- package/dist/{softphone-DHmhotbf.d.ts → softphone-DlGV12Xp.d.cts} +17 -2
- package/dist/softphone.css +477 -35
- package/dist/{sources-N4BOEDRL.js → sources-IRKNQPWW.js} +108 -5
- package/dist/sources-IRKNQPWW.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 +846 -410
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +17 -6
- package/dist/ui/index.d.ts +17 -6
- package/dist/ui/index.js +878 -456
- package/dist/ui/index.js.map +1 -1
- package/dist/zadarma/index.cjs +144 -18
- 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-L5DEFXUS.js +5 -0
- package/dist/{zadarma-engine-DN2SZSQ3.js.map → zadarma-engine-L5DEFXUS.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
package/dist/element/index.d.ts
CHANGED
|
@@ -143,14 +143,111 @@ interface AgentsDirectory {
|
|
|
143
143
|
readonly me: PhoneAgent | null;
|
|
144
144
|
readonly people: readonly PhoneAgent[];
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* `'connected'` | `'not-connected'` — whether an attended transfer's second ring, after `Completar`,
|
|
148
|
+
* actually reached the target. Kebab-case to match `HangupReason` (`'no-answer'`, `'transferred'`);
|
|
149
|
+
* the backend's own wire value stays `not_connected` — this is the one place that gets translated.
|
|
150
|
+
*/
|
|
151
|
+
type TransferOutcomeStatus = 'connected' | 'not-connected';
|
|
152
|
+
/** One resolved transfer, as `GET /phone/transfers/outcomes` reports it. */
|
|
153
|
+
interface TransferOutcomeEntry {
|
|
154
|
+
/** Opaque, strictly increasing per organization, never re-issued. The cursor for the NEXT poll. */
|
|
155
|
+
readonly id: string;
|
|
156
|
+
/** The extension the call was transferred to. */
|
|
157
|
+
readonly target: string;
|
|
158
|
+
readonly status: TransferOutcomeStatus;
|
|
159
|
+
readonly at: Date;
|
|
160
|
+
}
|
|
161
|
+
interface TransferOutcomesPage {
|
|
162
|
+
/** The newest `id` currently retained, or `'0'` when there are none — pass back as `since`. */
|
|
163
|
+
readonly cursor: string;
|
|
164
|
+
readonly outcomes: readonly TransferOutcomeEntry[];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* `SoftphoneEvent.TransferOutcome`'s payload. Unlike `TransferOutcomeEntry`, this never carries `id`
|
|
168
|
+
* (a polling-internal concern) and adds `caller` — the backend never stores a caller number (spec
|
|
169
|
+
* §5.3: "no caller number, nothing about other users"), so this is filled in locally, from the `Call`
|
|
170
|
+
* that was transferred, at the moment an outcome is matched to it.
|
|
171
|
+
*/
|
|
172
|
+
interface TransferOutcomeEvent {
|
|
173
|
+
readonly status: TransferOutcomeStatus;
|
|
174
|
+
readonly target: string;
|
|
175
|
+
/** The original caller's number, for "Llamar de vuelta" — null if it was never known. */
|
|
176
|
+
readonly caller: string | null;
|
|
177
|
+
readonly at: Date;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* `'disponible'` | `'en_llamada'` | `'desconectado'` — Spanish because this is what the picker shows,
|
|
181
|
+
* not an internal code (spec §4.1). `derivePresence` (the backend's own reducer, mirrored nowhere in
|
|
182
|
+
* the SDK — the SDK only ever receives an already-derived state, never `onCall`/`onlineFresh` apart).
|
|
183
|
+
*/
|
|
184
|
+
type PresenceState = 'disponible' | 'en_llamada' | 'desconectado';
|
|
185
|
+
/** One org member with a phone extension, as the picker lists them. */
|
|
186
|
+
interface PresenceEntry {
|
|
187
|
+
readonly userId: string;
|
|
188
|
+
readonly extension: string;
|
|
189
|
+
/** `null` when the backend has no name to offer — the picker falls back to the extension. */
|
|
190
|
+
readonly name: string | null;
|
|
191
|
+
readonly state: PresenceState;
|
|
192
|
+
}
|
|
193
|
+
/** What a live presence connection hands back once open. */
|
|
194
|
+
interface PresenceConnection {
|
|
195
|
+
/**
|
|
196
|
+
* Report this softphone's own on-call-ness, so a colleague transferring TO this agent sees it
|
|
197
|
+
* without a round trip through the webhook path (spec §4.1's `onCall` OR-combine). Calling this
|
|
198
|
+
* before the connection is genuinely open is never required of an implementation — a caller (see
|
|
199
|
+
* `presence-socket.ts`) that reports early is expected to wait for the connection to exist first.
|
|
200
|
+
*/
|
|
201
|
+
readonly reportCallState: (onCall: boolean) => void;
|
|
202
|
+
/**
|
|
203
|
+
* Says this phone is going away ON PURPOSE (the user turned it off, the app is quitting), so the backend
|
|
204
|
+
* drops it at once instead of waiting out the close grace it gives every ordinary close. Optional: a host's
|
|
205
|
+
* own source that has no such message simply omits it and gets the grace. Sent just before `close()`.
|
|
206
|
+
*/
|
|
207
|
+
readonly bye?: () => void;
|
|
208
|
+
/**
|
|
209
|
+
* Ends this connection deliberately. Never causes the SAME callbacks' `onClose` to fire — that is
|
|
210
|
+
* reserved for the connection ending on its OWN (a network drop, a server-side close, a rejected
|
|
211
|
+
* ticket), which is the only thing a caller should ever treat as "reconnect now".
|
|
212
|
+
*/
|
|
213
|
+
readonly close: () => void;
|
|
214
|
+
}
|
|
215
|
+
interface PresenceCallbacks {
|
|
216
|
+
/** Once, right after connecting — the full current roster (spec §4.3). */
|
|
217
|
+
readonly onSnapshot: (agents: readonly PresenceEntry[]) => void;
|
|
218
|
+
/** Zero or more times after that — one agent whose state just changed. */
|
|
219
|
+
readonly onDelta: (agent: PresenceEntry) => void;
|
|
220
|
+
/**
|
|
221
|
+
* The connection ended on its own. Never called after the CALLER's own `close()` — see
|
|
222
|
+
* `PresenceConnection.close`'s own note. The only signal a watch layer needs to know it must
|
|
223
|
+
* reconnect, deliberately carrying no reason: a dropped connection and a rejected ticket look
|
|
224
|
+
* identical from here, and both get the same treatment (spec §4.4 — quiet retry, nothing surfaced).
|
|
225
|
+
*/
|
|
226
|
+
readonly onClose: () => void;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
|
|
230
|
+
* `wss://…/phone/presence`); a host may override it entirely, or omit it — omitted, or while the
|
|
231
|
+
* connection is down, the picker simply has no live data to show (spec §4.4, success criterion #5:
|
|
232
|
+
* no error surfaces). Unlike `TransferOutcomeSource` (poll, get a page, done), this is push-based and
|
|
233
|
+
* long-lived — the returned promise resolves once, when the connection opens, not once per update.
|
|
234
|
+
*/
|
|
235
|
+
type PresenceSource = (callbacks: PresenceCallbacks) => Promise<PresenceConnection>;
|
|
146
236
|
|
|
147
|
-
/** Contacts, history, caller ID
|
|
237
|
+
/** Contacts, history, caller ID, the agents directory, transfer outcomes and presence, all from one
|
|
238
|
+
* config. */
|
|
148
239
|
interface BackendSources {
|
|
149
240
|
readonly contacts: ContactsSource;
|
|
150
241
|
readonly history: HistorySource;
|
|
151
242
|
readonly resolveContact: (number: string) => Promise<Contact | null>;
|
|
152
243
|
/** The Historial tab's "filter by person" directory. Always Coventia's own org membership. */
|
|
153
244
|
readonly listAgents: () => Promise<AgentsDirectory>;
|
|
245
|
+
/** Phase A (transfer outcome relay) — polled by the transfer watch, never by UI directly. */
|
|
246
|
+
readonly transferOutcomes: (params: {
|
|
247
|
+
readonly since?: string;
|
|
248
|
+
}) => Promise<TransferOutcomesPage>;
|
|
249
|
+
/** Phase B (presence-gated transfer picker) — opened by `presence-socket.ts`, never by UI directly. */
|
|
250
|
+
readonly presence: PresenceSource;
|
|
154
251
|
}
|
|
155
252
|
|
|
156
253
|
/**
|
|
@@ -345,7 +442,23 @@ declare enum SoftphoneEvent {
|
|
|
345
442
|
* `StateChanged`: `state` is reachability, this is whether this tab is even allowed to be
|
|
346
443
|
* reachable at all while another tab holding the same extension stays open.
|
|
347
444
|
*/
|
|
348
|
-
LeaderChanged = "leaderChanged"
|
|
445
|
+
LeaderChanged = "leaderChanged",
|
|
446
|
+
/**
|
|
447
|
+
* `Completar` completed a transfer, and the target's SECOND ring either connected or didn't —
|
|
448
|
+
* `docs/specs/transfer-outcome-relay.md` (Phase A). Fires for BOTH outcomes so a host can react to
|
|
449
|
+
* either; only `'not-connected'` is worth surfacing to the agent (spec §5.5) — roughly a minute
|
|
450
|
+
* after the transfer for a target who never answers, seconds for a decline or the caller hanging up
|
|
451
|
+
* first. Requires a `TransferOutcomeSource` (from `coventia`/`apiBase`, or a host override); never
|
|
452
|
+
* fires at all when none is configured, since there is then nothing to poll.
|
|
453
|
+
*/
|
|
454
|
+
TransferOutcome = "transferOutcome",
|
|
455
|
+
/**
|
|
456
|
+
* `softphone.presence` changed — the live agent-presence list (Phase B,
|
|
457
|
+
* `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a
|
|
458
|
+
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being
|
|
459
|
+
* `online && isLeader`; never fires at all otherwise, same reasoning as `TransferOutcome`.
|
|
460
|
+
*/
|
|
461
|
+
PresenceChanged = "presenceChanged"
|
|
349
462
|
}
|
|
350
463
|
interface SoftphoneEventMap {
|
|
351
464
|
[SoftphoneEvent.StateChanged]: {
|
|
@@ -362,6 +475,10 @@ interface SoftphoneEventMap {
|
|
|
362
475
|
[SoftphoneEvent.LeaderChanged]: {
|
|
363
476
|
readonly isLeader: boolean;
|
|
364
477
|
};
|
|
478
|
+
[SoftphoneEvent.TransferOutcome]: TransferOutcomeEvent;
|
|
479
|
+
[SoftphoneEvent.PresenceChanged]: {
|
|
480
|
+
readonly agents: readonly PresenceEntry[];
|
|
481
|
+
};
|
|
365
482
|
}
|
|
366
483
|
declare enum CallEvent {
|
|
367
484
|
/** Any transition. The catch-all; the specific events below are conveniences. */
|
|
@@ -528,15 +645,37 @@ interface Call extends TypedEmitter<CallEventMap> {
|
|
|
528
645
|
hold(): Promise<void>;
|
|
529
646
|
unhold(): Promise<void>;
|
|
530
647
|
sendDigits(digits: string): void;
|
|
531
|
-
/**
|
|
648
|
+
/**
|
|
649
|
+
* Blind transfer (SIP REFER). Ends this call — but **whether the transfer itself succeeded or
|
|
650
|
+
* failed is unknowable from here**: against Zadarma, a REFER's outcome is not reported back to the
|
|
651
|
+
* browser at all (confirmed on real calls — `docs/specs/transfer-availability.md` §2), so a REFER to
|
|
652
|
+
* an unreachable target ends this call exactly the same way a successful one does. Prefer
|
|
653
|
+
* `startConsult`/`completeTransfer` (below) when the caller must not be put at risk — that path
|
|
654
|
+
* correctly reports busy/no-answer/failed because the consult leg is an ordinary outbound call, not
|
|
655
|
+
* a REFER.
|
|
656
|
+
*/
|
|
532
657
|
transfer(target: string): Promise<void>;
|
|
533
658
|
/**
|
|
534
659
|
* Attended transfer, part 1: call the target while holding this call.
|
|
535
660
|
*
|
|
536
|
-
* Returns the consult leg — itself a Call, with its own state machine and events.
|
|
661
|
+
* Returns the consult leg — itself a Call, with its own state machine and events. Unlike blind
|
|
662
|
+
* `transfer()`, a consult leg that goes unanswered reports a real reason (busy/no-answer/rejected/
|
|
663
|
+
* failed) and never puts this call at risk — see `docs/specs/transfer-availability.md` §5.
|
|
537
664
|
*/
|
|
538
665
|
startConsult(target: string): Promise<Call>;
|
|
539
|
-
/**
|
|
666
|
+
/**
|
|
667
|
+
* Attended transfer, part 2: bridge the two, ending both. (REFER … Replaces)
|
|
668
|
+
*
|
|
669
|
+
* **Confirmed non-functional against Zadarma, on real calls, three times
|
|
670
|
+
* (`docs/specs/transfer-availability.md` §12.3).** Zadarma accepts the REFER…Replaces but never
|
|
671
|
+
* actually bridges the two legs — the consult leg is silently orphaned and the caller is lost, with
|
|
672
|
+
* no error thrown here to signal it. The pattern that DOES work (the same one `/ui`'s own
|
|
673
|
+
* `ConsultScreen` uses): hang up the consult leg, then call `transfer(target)` on THIS call, to the
|
|
674
|
+
* same already-confirmed-answering target — a blind transfer to a target already known to be there
|
|
675
|
+
* works, even though `completeTransfer()` itself does not. Left in the type for hosts against a
|
|
676
|
+
* different (non-Zadarma) SIP provider where REFER…Replaces may work as specified; do not reach for
|
|
677
|
+
* it against Zadarma.
|
|
678
|
+
*/
|
|
540
679
|
completeTransfer(): Promise<void>;
|
|
541
680
|
/** Abandon the consult and resume this call. */
|
|
542
681
|
cancelConsult(): Promise<void>;
|
|
@@ -647,6 +786,14 @@ interface Softphone extends TypedEmitter<SoftphoneEventMap> {
|
|
|
647
786
|
*/
|
|
648
787
|
readonly sources: BackendSources | null;
|
|
649
788
|
readonly audio: AudioDevices;
|
|
789
|
+
/**
|
|
790
|
+
* The live agent-presence list (Phase B, `docs/specs/transfer-presence-picker.md`) — every org
|
|
791
|
+
* member with a phone extension, whatever their state. Empty when there is no `PresenceSource`
|
|
792
|
+
* (no Coventia backend, or a host that omitted one), or while this tab is not `online && isLeader`
|
|
793
|
+
* — never an error, just nothing to show (spec §4.4's own success criterion). Updates fire
|
|
794
|
+
* `SoftphoneEvent.PresenceChanged`; `usePresence()` is the reactive read.
|
|
795
|
+
*/
|
|
796
|
+
readonly presence: readonly PresenceEntry[];
|
|
650
797
|
/**
|
|
651
798
|
* False when the browser has blocked audio playback.
|
|
652
799
|
*
|
|
@@ -713,8 +860,8 @@ interface SoftphoneElementConfig {
|
|
|
713
860
|
*/
|
|
714
861
|
readonly requireMic?: boolean;
|
|
715
862
|
/**
|
|
716
|
-
* Play call audio — the remote party, the inbound ring, the outbound ringback
|
|
717
|
-
* see `SoftphoneWidgetProps.sounds`.
|
|
863
|
+
* Play call audio — the remote party, the inbound ring, the outbound ringback, the end-of-call cue
|
|
864
|
+
* and the keypad's key tones. **On by default**; see `SoftphoneWidgetProps.sounds`.
|
|
718
865
|
*
|
|
719
866
|
* Turning this off makes a connected call silent in both directions, because nothing else attaches
|
|
720
867
|
* the remote `MediaStream`. Only correct when the host attaches `call.remoteStream` itself, or for
|
|
@@ -791,6 +938,14 @@ interface SoftphoneElementEventMap {
|
|
|
791
938
|
'cv-leadership-changed': CustomEvent<{
|
|
792
939
|
readonly isLeader: boolean;
|
|
793
940
|
}>;
|
|
941
|
+
/**
|
|
942
|
+
* A transfer's final handoff step resolved — `status` says whether the caller actually reached
|
|
943
|
+
* `target`. Polled, not instant (Phase A, `docs/specs/transfer-outcome-relay.md`); only for that
|
|
944
|
+
* handoff, never for the consult leg itself, which already reports its own outcome in real time via
|
|
945
|
+
* `cv-call-ended` on that leg. **The widget already shows its own dismissable notice for this** —
|
|
946
|
+
* listen here only for your own logging, or if you are replacing the built-in UI.
|
|
947
|
+
*/
|
|
948
|
+
'cv-transfer-outcome': CustomEvent<TransferOutcomeEvent>;
|
|
794
949
|
}
|
|
795
950
|
declare global {
|
|
796
951
|
/** `document.querySelector('coventia-softphone')` returns the real type — no cast to set `.softphone`. */
|