@coventia/softphone 0.3.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.
- package/dist/{call-CaPDWVk3.d.cts → call-FqcvDg1t.d.cts} +168 -12
- package/dist/{call-CaPDWVk3.d.ts → call-FqcvDg1t.d.ts} +168 -12
- package/dist/chunk-5E5OEXS2.js +14 -0
- package/dist/chunk-5E5OEXS2.js.map +1 -0
- package/dist/{chunk-EYEDPD7Y.js → chunk-6RR6G35E.js} +92 -17
- package/dist/chunk-6RR6G35E.js.map +1 -0
- package/dist/{chunk-2ZSOGGPO.js → chunk-FRXHEHQA.js} +15 -2
- package/dist/chunk-FRXHEHQA.js.map +1 -0
- package/dist/{chunk-UHNLEBBO.js → chunk-I3RSC52R.js} +28 -3
- package/dist/chunk-I3RSC52R.js.map +1 -0
- package/dist/chunk-M76SAXXM.js +13 -0
- package/dist/chunk-M76SAXXM.js.map +1 -0
- package/dist/{chunk-7R6LOOAI.js → chunk-N4PLEYFJ.js} +4 -2
- package/dist/chunk-N4PLEYFJ.js.map +1 -0
- package/dist/element/index.d.ts +198 -6
- package/dist/element/index.js +1160 -489
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-BgBsIcEa.d.ts → engine-CsNRD7B7.d.ts} +1 -1
- package/dist/{engine-CU2rEo3g.d.cts → engine-CzOCrfY9.d.cts} +1 -1
- package/dist/index.cjs +675 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -6
- package/dist/index.d.ts +16 -6
- package/dist/index.js +453 -37
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +26 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +24 -5
- package/dist/react/index.d.ts +24 -5
- package/dist/react/index.js +2 -2
- package/dist/{softphone-DEt0TtQw.d.ts → softphone-By9sizlV.d.ts} +44 -3
- package/dist/{softphone-dZMkW8V1.d.cts → softphone-JZvJdzU3.d.cts} +44 -3
- package/dist/softphone.css +254 -23
- package/dist/{sources-M3GYMS6V.js → sources-QL2LRZVH.js} +93 -8
- package/dist/sources-QL2LRZVH.js.map +1 -0
- package/dist/testing/index.cjs +13 -0
- package/dist/testing/index.cjs.map +1 -1
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +1 -1
- package/dist/ui/index.cjs +670 -20
- 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 +642 -22
- package/dist/ui/index.js.map +1 -1
- package/dist/zadarma/index.cjs +111 -14
- package/dist/zadarma/index.cjs.map +1 -1
- package/dist/zadarma/index.d.cts +5 -6
- package/dist/zadarma/index.d.ts +5 -6
- package/dist/zadarma/index.js +3 -2
- package/dist/zadarma-engine-TIUE7PZ7.js +6 -0
- package/dist/{zadarma-engine-L5DEFXUS.js.map → zadarma-engine-TIUE7PZ7.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-2ZSOGGPO.js.map +0 -1
- package/dist/chunk-7R6LOOAI.js.map +0 -1
- package/dist/chunk-EYEDPD7Y.js.map +0 -1
- package/dist/chunk-UHNLEBBO.js.map +0 -1
- package/dist/sources-M3GYMS6V.js.map +0 -1
- package/dist/zadarma-engine-L5DEFXUS.js +0 -5
|
@@ -86,10 +86,10 @@ interface TokenEndpointOptions {
|
|
|
86
86
|
*/
|
|
87
87
|
interface CoventiaBackendConfig {
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* does, which is not the normal case.
|
|
89
|
+
* Coventia's domain, like `SoftphoneConfig.apiBase`: no path, the SDK builds every route.
|
|
90
|
+
* **Optional** — falls back to `SoftphoneConfig.apiBase`, which exists precisely so this and the
|
|
91
|
+
* default call engine name the same backend once instead of twice. Set it here only when
|
|
92
|
+
* contacts/history genuinely live on a DIFFERENT host than calling does, which is not the normal case.
|
|
93
93
|
*/
|
|
94
94
|
readonly baseUrl?: string | undefined;
|
|
95
95
|
/**
|
|
@@ -104,7 +104,10 @@ interface CoventiaBackendConfig {
|
|
|
104
104
|
* That is the CRM today.
|
|
105
105
|
*/
|
|
106
106
|
readonly organizationId?: string;
|
|
107
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Where the phone routes sit under the base URL. Internal: leave it unset (`/phone`). The routes are
|
|
109
|
+
* the SDK's to build, which is why `apiBase` is the domain only.
|
|
110
|
+
*/
|
|
108
111
|
readonly basePath?: string;
|
|
109
112
|
/**
|
|
110
113
|
* **The one thing a provider customises**: their own contact list, searched on their own backend.
|
|
@@ -134,8 +137,11 @@ interface CoventiaBackendConfig {
|
|
|
134
137
|
interface SoftphoneConfig {
|
|
135
138
|
readonly tokenSource: TokenSource;
|
|
136
139
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
140
|
+
* Coventia's domain — **only the domain**, e.g. `https://backend.example.com`, with no path: the SDK
|
|
141
|
+
* builds every route itself. Not the Partner API URL your backend calls: that one ends in `/api/v1`,
|
|
142
|
+
* which does not belong here (it is ignored, with a console warning). **The one field to set** if you
|
|
143
|
+
* want the default call engine (see `provider` below) and are not overriding `coventia.baseUrl`
|
|
144
|
+
* separately.
|
|
139
145
|
*
|
|
140
146
|
* Named once here rather than requiring it on both `coventia` and the engine you construct: they
|
|
141
147
|
* are two independent subsystems (contacts/history/caller ID vs. SIP credential provisioning) that
|
|
@@ -188,8 +194,44 @@ interface CallOptions {
|
|
|
188
194
|
* Zadarma registers on-demand, per call, in response to a push (SPEC §3.2); a persistent UA is
|
|
189
195
|
* ignored by its PBX. Leaking "registered" into the public API would force every future provider —
|
|
190
196
|
* including our own SIP infrastructure, which registers persistently — to fake it.
|
|
197
|
+
*
|
|
198
|
+
* `standby` means started, and this browser's leader, but ANOTHER softphone of the same extension has
|
|
199
|
+
* the line — a tab in another browser, the desktop app, a partner page (`docs/specs/line-owner.md` §5).
|
|
200
|
+
* Its push socket is closed on purpose, so nothing here registers or rings: `line` says where the calls
|
|
201
|
+
* go, and `takeLine()` — or a `call()` — brings them here. A host that reads `online` as "ready"
|
|
202
|
+
* correctly reads this as not ready.
|
|
203
|
+
*/
|
|
204
|
+
type SoftphoneState = 'stopped' | 'starting' | 'online' | 'standby' | 'offline' | 'reconnecting' | 'destroyed';
|
|
205
|
+
/**
|
|
206
|
+
* Where this extension's incoming calls ring, as far as this softphone knows. `here`: it has the line.
|
|
207
|
+
* `elsewhere`: another softphone of the same extension has it, and this one waits in `standby`. `unknown`:
|
|
208
|
+
* nobody has said — no presence source, a backend that predates the line owner, a presence socket that is
|
|
209
|
+
* down — and this softphone listens, exactly as before the line owner existed (§5.1 R7, fail-open).
|
|
210
|
+
*/
|
|
211
|
+
type LineState = 'here' | 'elsewhere' | 'unknown';
|
|
212
|
+
/** Who has the line when it is `elsewhere`: the desktop app, or any other softphone (a browser tab). */
|
|
213
|
+
type LineLocation = 'desktop' | 'browser';
|
|
214
|
+
/**
|
|
215
|
+
* The last attempt to bring the line here — `takeLine()`, or a `call()` from `standby`, whoever made it:
|
|
216
|
+
* `claiming` while it is in flight, `refused` when the user is on a call where the line is, `failed` when
|
|
217
|
+
* nothing could decide. `idle` before any attempt, once the line comes here, and after a `stop()`.
|
|
191
218
|
*/
|
|
192
|
-
type
|
|
219
|
+
type LineClaim = 'idle' | 'claiming' | 'refused' | 'failed';
|
|
220
|
+
/** The server's word on this softphone's line, as a `PresenceSource` hands it over (`PresenceCallbacks.onLine`). */
|
|
221
|
+
interface LineUpdate {
|
|
222
|
+
readonly state: 'here' | 'elsewhere';
|
|
223
|
+
/** Who has the line when it is `elsewhere`; `null` when it is here, or the server did not say. */
|
|
224
|
+
readonly where: LineLocation | null;
|
|
225
|
+
}
|
|
226
|
+
/** The answer to `PresenceConnection.claimLine()`. */
|
|
227
|
+
interface LineClaimResult {
|
|
228
|
+
readonly ok: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Why it was refused: `'on_call'` while the user is on a call (the line never moves during one). Any
|
|
231
|
+
* other value means nothing was decided. Absent when `ok`.
|
|
232
|
+
*/
|
|
233
|
+
readonly reason?: string;
|
|
234
|
+
}
|
|
193
235
|
interface ContactsPage {
|
|
194
236
|
readonly items: readonly Contact[];
|
|
195
237
|
/**
|
|
@@ -240,6 +282,11 @@ interface CallHistoryEntry {
|
|
|
240
282
|
readonly outcome?: CallOutcome;
|
|
241
283
|
/** The org's own number that was dialled — present only (and only meaningful) for an inbound call. */
|
|
242
284
|
readonly did?: string;
|
|
285
|
+
/**
|
|
286
|
+
* The org's own number an outbound call was made from (what the callee saw) — present only for outbound calls the
|
|
287
|
+
* backend recorded it for (outbound-number-per-agent.md §5.7). Digits, no `+`.
|
|
288
|
+
*/
|
|
289
|
+
readonly from?: string;
|
|
243
290
|
/** True when Coventia has at least one recording for this call. */
|
|
244
291
|
readonly recorded: boolean;
|
|
245
292
|
/**
|
|
@@ -344,6 +391,18 @@ interface PresenceConnection {
|
|
|
344
391
|
* own source that has no such message simply omits it and gets the grace. Sent just before `close()`.
|
|
345
392
|
*/
|
|
346
393
|
readonly bye?: () => void;
|
|
394
|
+
/**
|
|
395
|
+
* The line owner (`docs/specs/line-owner.md` §5.2): says which softphone this connection speaks for, so
|
|
396
|
+
* the server can tell it whether it has the line — through `PresenceCallbacks.onLine`, right after this
|
|
397
|
+
* and on every change. Sent as the FIRST message of every connection. Optional: a source without it never
|
|
398
|
+
* takes part, and its softphone listens exactly as before (fail-open), reading its line as `unknown`.
|
|
399
|
+
*/
|
|
400
|
+
readonly hello?: (clientId: string) => void;
|
|
401
|
+
/**
|
|
402
|
+
* "Recibir aquí": asks for the line. Answered through `PresenceCallbacks.onClaimResult` — and, when it
|
|
403
|
+
* succeeds, by `onLine` saying `here` first. Optional, like `hello`.
|
|
404
|
+
*/
|
|
405
|
+
readonly claimLine?: () => void;
|
|
347
406
|
/**
|
|
348
407
|
* Ends this connection deliberately. Never causes the SAME callbacks' `onClose` to fire — that is
|
|
349
408
|
* reserved for the connection ending on its OWN (a network drop, a server-side close, a rejected
|
|
@@ -363,6 +422,13 @@ interface PresenceCallbacks {
|
|
|
363
422
|
* identical from here, and both get the same treatment (spec §4.4 — quiet retry, nothing surfaced).
|
|
364
423
|
*/
|
|
365
424
|
readonly onClose: () => void;
|
|
425
|
+
/**
|
|
426
|
+
* The line, as the server says it — right after `PresenceConnection.hello`, and on every change after
|
|
427
|
+
* that. Optional, so callbacks written before the line owner existed still type-check.
|
|
428
|
+
*/
|
|
429
|
+
readonly onLine?: (update: LineUpdate) => void;
|
|
430
|
+
/** The answer to `PresenceConnection.claimLine()`. Optional, like `onLine`. */
|
|
431
|
+
readonly onClaimResult?: (result: LineClaimResult) => void;
|
|
366
432
|
}
|
|
367
433
|
/**
|
|
368
434
|
* Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
|
|
@@ -415,6 +481,64 @@ interface RingtoneSource {
|
|
|
415
481
|
*/
|
|
416
482
|
cached?(): Promise<RingtoneSettings | null>;
|
|
417
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* What kind of number this is, for the tags next to it in "Llamar desde" (outbound-number-per-agent.md §5.5).
|
|
486
|
+
* Display only — it never blocks a choice. `mobile` must not be used for customer care or unsolicited commercial
|
|
487
|
+
* calls; `commercial` (the 400 range) is for outbound commercial calls only and receives no calls.
|
|
488
|
+
*/
|
|
489
|
+
type OutboundNumberKind = 'landline' | 'mobile' | 'commercial' | 'other';
|
|
490
|
+
/** One of the organization's numbers an agent can call from. */
|
|
491
|
+
interface OutboundNumber {
|
|
492
|
+
/** Stable id — what is stored as the agent's choice. */
|
|
493
|
+
readonly id: string;
|
|
494
|
+
/** International format, digits only, no `+` — e.g. `34960000001`. */
|
|
495
|
+
readonly number: string;
|
|
496
|
+
/** The name the organization gave the number, or `null`. */
|
|
497
|
+
readonly label: string | null;
|
|
498
|
+
readonly kind: OutboundNumberKind;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Where the agent's extension stands. `pending`/`failed`: an assignment change could not update it yet, so the
|
|
502
|
+
* extension may still present an earlier number. `unassigned`: the agent has no extension in this organization.
|
|
503
|
+
*/
|
|
504
|
+
type OutboundNumberState = 'applied' | 'pending' | 'failed' | 'unassigned';
|
|
505
|
+
/** The organization's numbers and the caller's choice, as the backend resolves them. */
|
|
506
|
+
interface OutboundNumberSettings {
|
|
507
|
+
/** The organization's active numbers, ordered by number. */
|
|
508
|
+
readonly numbers: readonly OutboundNumber[];
|
|
509
|
+
/** The organization default (an `OutboundNumber.id`), or `null`. */
|
|
510
|
+
readonly orgDefault: string | null;
|
|
511
|
+
/** This agent's own choice, or `null` = "use the organization default". */
|
|
512
|
+
readonly mine: string | null;
|
|
513
|
+
/** The number this agent's calls show: `mine`, else `orgDefault`. */
|
|
514
|
+
readonly effective: string | null;
|
|
515
|
+
/** The agent's extension ("100"), or `null` when they have none. */
|
|
516
|
+
readonly extension: string | null;
|
|
517
|
+
readonly state: OutboundNumberState;
|
|
518
|
+
/** 0 when a change could go out now; otherwise the seconds until it can. */
|
|
519
|
+
readonly changeAvailableInSeconds: number;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Why a change was refused before anything was sent. `platform`: the shared limit on number changes (nothing the
|
|
523
|
+
* agent did). `user`: the agent changed it moments ago. Either way the previous number stays in use.
|
|
524
|
+
*/
|
|
525
|
+
interface OutboundNumberRefusal {
|
|
526
|
+
readonly reason: 'platform' | 'user';
|
|
527
|
+
/** Seconds until a change can go out — always at least 1. */
|
|
528
|
+
readonly retryAfterSeconds: number;
|
|
529
|
+
}
|
|
530
|
+
/** The outbound number of the agent the softphone is signed in as, in its organization. */
|
|
531
|
+
interface OutboundNumbersSource {
|
|
532
|
+
get(signal?: AbortSignal): Promise<OutboundNumberSettings>;
|
|
533
|
+
/**
|
|
534
|
+
* Changes this agent's number; `null` = back to the organization default. Resolves to the new settings once the
|
|
535
|
+
* change is in effect. When the request limits refuse it, rejects with an `Error` carrying
|
|
536
|
+
* `refusal: OutboundNumberRefusal` (check with `isOutboundNumberRefused`) — nothing was stored or sent.
|
|
537
|
+
*/
|
|
538
|
+
setMine(id: string | null): Promise<OutboundNumberSettings>;
|
|
539
|
+
/** The settings this agent last had on this device, if remembered — instant, no network. Optional. */
|
|
540
|
+
cached?(): Promise<OutboundNumberSettings | null>;
|
|
541
|
+
}
|
|
418
542
|
|
|
419
543
|
/**
|
|
420
544
|
* The error taxonomy.
|
|
@@ -535,6 +659,16 @@ declare const ERROR_CATALOGUE: {
|
|
|
535
659
|
readonly message: "The provider does not support this operation.";
|
|
536
660
|
readonly solution: "Guard the UI on the operation before offering it.";
|
|
537
661
|
};
|
|
662
|
+
readonly 'line/claim-refused': {
|
|
663
|
+
readonly fatal: false;
|
|
664
|
+
readonly message: "The line was not moved here: the user is on a call on the softphone that has it.";
|
|
665
|
+
readonly solution: "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.";
|
|
666
|
+
};
|
|
667
|
+
readonly 'line/unavailable': {
|
|
668
|
+
readonly fatal: false;
|
|
669
|
+
readonly message: "The line could not be taken: nothing decided where it is.";
|
|
670
|
+
readonly solution: "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.";
|
|
671
|
+
};
|
|
538
672
|
readonly 'provider/provision-failed': {
|
|
539
673
|
readonly fatal: true;
|
|
540
674
|
readonly message: "The provider could not issue SIP credentials.";
|
|
@@ -623,10 +757,25 @@ declare enum SoftphoneEvent {
|
|
|
623
757
|
/**
|
|
624
758
|
* `softphone.presence` changed — the live agent-presence list (Phase B,
|
|
625
759
|
* `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a
|
|
626
|
-
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being
|
|
627
|
-
* `online
|
|
760
|
+
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being a
|
|
761
|
+
* started leader — `online`, or waiting in `standby` for the line; never fires at all otherwise,
|
|
762
|
+
* same reasoning as `TransferOutcome`.
|
|
628
763
|
*/
|
|
629
|
-
PresenceChanged = "presenceChanged"
|
|
764
|
+
PresenceChanged = "presenceChanged",
|
|
765
|
+
/**
|
|
766
|
+
* `softphone.line` / `lineWhere` changed (`docs/specs/line-owner.md` §5): this softphone got the line,
|
|
767
|
+
* lost it to another softphone of the same extension, or stopped knowing (`unknown` — it then listens,
|
|
768
|
+
* as before the line owner existed). A third axis beside `StateChanged` and `LeaderChanged`: losing the
|
|
769
|
+
* line is what takes the softphone to `standby`, but not at once while a call is in progress — the line
|
|
770
|
+
* can read `elsewhere` while the state is still `online`, until that call ends.
|
|
771
|
+
*/
|
|
772
|
+
LineChanged = "lineChanged",
|
|
773
|
+
/**
|
|
774
|
+
* `softphone.lineClaim` changed: an attempt to bring the line here started, was refused, failed, or ended
|
|
775
|
+
* (the line came here, or the softphone stopped). Kept apart from `LineChanged`, which only ever means the
|
|
776
|
+
* line itself moved.
|
|
777
|
+
*/
|
|
778
|
+
LineClaimChanged = "lineClaimChanged"
|
|
630
779
|
}
|
|
631
780
|
interface SoftphoneEventMap {
|
|
632
781
|
[SoftphoneEvent.StateChanged]: {
|
|
@@ -647,6 +796,13 @@ interface SoftphoneEventMap {
|
|
|
647
796
|
[SoftphoneEvent.PresenceChanged]: {
|
|
648
797
|
readonly agents: readonly PresenceEntry[];
|
|
649
798
|
};
|
|
799
|
+
[SoftphoneEvent.LineChanged]: {
|
|
800
|
+
readonly line: LineState;
|
|
801
|
+
readonly where: LineLocation | null;
|
|
802
|
+
};
|
|
803
|
+
[SoftphoneEvent.LineClaimChanged]: {
|
|
804
|
+
readonly claim: LineClaim;
|
|
805
|
+
};
|
|
650
806
|
}
|
|
651
807
|
declare enum CallEvent {
|
|
652
808
|
/** Any transition. The catch-all; the specific events below are conveniences. */
|
|
@@ -873,4 +1029,4 @@ interface Call extends TypedEmitter<CallEventMap> {
|
|
|
873
1029
|
cancelConsult(): Promise<void>;
|
|
874
1030
|
}
|
|
875
1031
|
|
|
876
|
-
export { type
|
|
1032
|
+
export { type TokenSource as A, type TypedEmitter as B, CALL_TRANSITIONS as C, canTransition as D, ERROR_CATALOGUE as E, type FatalErrorCode as F, isFatal as G, type HangupReason as H, INITIAL_STATES as I, isTerminal as J, type ContactsSource as K, type LineClaim as L, type HistorySource as M, type NonFatalErrorCode as N, type OutboundNumberRefusal as O, type PresenceEntry as P, type AgentsDirectory as Q, type RemoteParty as R, type SoftphoneConfig as S, type TokenEndpointOptions as T, type TransferOutcomesPage as U, type VoiceToken as V, type PresenceSource as W, type CallHistoryEntry as X, type ContactsPage as Y, 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 LineLocation as j, type LineState as k, type LogLevel as l, type NonFatalSoftphoneError as m, type OutboundNumber as n, type OutboundNumberKind as o, type OutboundNumberSettings as p, type OutboundNumberState as q, type OutboundNumbersSource as r, type RingtoneOption as s, type RingtoneSettings as t, type RingtoneSource as u, type SoftphoneError as v, type SoftphoneErrorCode as w, SoftphoneEvent as x, type SoftphoneEventMap as y, type SoftphoneState as z };
|
|
@@ -86,10 +86,10 @@ interface TokenEndpointOptions {
|
|
|
86
86
|
*/
|
|
87
87
|
interface CoventiaBackendConfig {
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* does, which is not the normal case.
|
|
89
|
+
* Coventia's domain, like `SoftphoneConfig.apiBase`: no path, the SDK builds every route.
|
|
90
|
+
* **Optional** — falls back to `SoftphoneConfig.apiBase`, which exists precisely so this and the
|
|
91
|
+
* default call engine name the same backend once instead of twice. Set it here only when
|
|
92
|
+
* contacts/history genuinely live on a DIFFERENT host than calling does, which is not the normal case.
|
|
93
93
|
*/
|
|
94
94
|
readonly baseUrl?: string | undefined;
|
|
95
95
|
/**
|
|
@@ -104,7 +104,10 @@ interface CoventiaBackendConfig {
|
|
|
104
104
|
* That is the CRM today.
|
|
105
105
|
*/
|
|
106
106
|
readonly organizationId?: string;
|
|
107
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Where the phone routes sit under the base URL. Internal: leave it unset (`/phone`). The routes are
|
|
109
|
+
* the SDK's to build, which is why `apiBase` is the domain only.
|
|
110
|
+
*/
|
|
108
111
|
readonly basePath?: string;
|
|
109
112
|
/**
|
|
110
113
|
* **The one thing a provider customises**: their own contact list, searched on their own backend.
|
|
@@ -134,8 +137,11 @@ interface CoventiaBackendConfig {
|
|
|
134
137
|
interface SoftphoneConfig {
|
|
135
138
|
readonly tokenSource: TokenSource;
|
|
136
139
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
140
|
+
* Coventia's domain — **only the domain**, e.g. `https://backend.example.com`, with no path: the SDK
|
|
141
|
+
* builds every route itself. Not the Partner API URL your backend calls: that one ends in `/api/v1`,
|
|
142
|
+
* which does not belong here (it is ignored, with a console warning). **The one field to set** if you
|
|
143
|
+
* want the default call engine (see `provider` below) and are not overriding `coventia.baseUrl`
|
|
144
|
+
* separately.
|
|
139
145
|
*
|
|
140
146
|
* Named once here rather than requiring it on both `coventia` and the engine you construct: they
|
|
141
147
|
* are two independent subsystems (contacts/history/caller ID vs. SIP credential provisioning) that
|
|
@@ -188,8 +194,44 @@ interface CallOptions {
|
|
|
188
194
|
* Zadarma registers on-demand, per call, in response to a push (SPEC §3.2); a persistent UA is
|
|
189
195
|
* ignored by its PBX. Leaking "registered" into the public API would force every future provider —
|
|
190
196
|
* including our own SIP infrastructure, which registers persistently — to fake it.
|
|
197
|
+
*
|
|
198
|
+
* `standby` means started, and this browser's leader, but ANOTHER softphone of the same extension has
|
|
199
|
+
* the line — a tab in another browser, the desktop app, a partner page (`docs/specs/line-owner.md` §5).
|
|
200
|
+
* Its push socket is closed on purpose, so nothing here registers or rings: `line` says where the calls
|
|
201
|
+
* go, and `takeLine()` — or a `call()` — brings them here. A host that reads `online` as "ready"
|
|
202
|
+
* correctly reads this as not ready.
|
|
203
|
+
*/
|
|
204
|
+
type SoftphoneState = 'stopped' | 'starting' | 'online' | 'standby' | 'offline' | 'reconnecting' | 'destroyed';
|
|
205
|
+
/**
|
|
206
|
+
* Where this extension's incoming calls ring, as far as this softphone knows. `here`: it has the line.
|
|
207
|
+
* `elsewhere`: another softphone of the same extension has it, and this one waits in `standby`. `unknown`:
|
|
208
|
+
* nobody has said — no presence source, a backend that predates the line owner, a presence socket that is
|
|
209
|
+
* down — and this softphone listens, exactly as before the line owner existed (§5.1 R7, fail-open).
|
|
210
|
+
*/
|
|
211
|
+
type LineState = 'here' | 'elsewhere' | 'unknown';
|
|
212
|
+
/** Who has the line when it is `elsewhere`: the desktop app, or any other softphone (a browser tab). */
|
|
213
|
+
type LineLocation = 'desktop' | 'browser';
|
|
214
|
+
/**
|
|
215
|
+
* The last attempt to bring the line here — `takeLine()`, or a `call()` from `standby`, whoever made it:
|
|
216
|
+
* `claiming` while it is in flight, `refused` when the user is on a call where the line is, `failed` when
|
|
217
|
+
* nothing could decide. `idle` before any attempt, once the line comes here, and after a `stop()`.
|
|
191
218
|
*/
|
|
192
|
-
type
|
|
219
|
+
type LineClaim = 'idle' | 'claiming' | 'refused' | 'failed';
|
|
220
|
+
/** The server's word on this softphone's line, as a `PresenceSource` hands it over (`PresenceCallbacks.onLine`). */
|
|
221
|
+
interface LineUpdate {
|
|
222
|
+
readonly state: 'here' | 'elsewhere';
|
|
223
|
+
/** Who has the line when it is `elsewhere`; `null` when it is here, or the server did not say. */
|
|
224
|
+
readonly where: LineLocation | null;
|
|
225
|
+
}
|
|
226
|
+
/** The answer to `PresenceConnection.claimLine()`. */
|
|
227
|
+
interface LineClaimResult {
|
|
228
|
+
readonly ok: boolean;
|
|
229
|
+
/**
|
|
230
|
+
* Why it was refused: `'on_call'` while the user is on a call (the line never moves during one). Any
|
|
231
|
+
* other value means nothing was decided. Absent when `ok`.
|
|
232
|
+
*/
|
|
233
|
+
readonly reason?: string;
|
|
234
|
+
}
|
|
193
235
|
interface ContactsPage {
|
|
194
236
|
readonly items: readonly Contact[];
|
|
195
237
|
/**
|
|
@@ -240,6 +282,11 @@ interface CallHistoryEntry {
|
|
|
240
282
|
readonly outcome?: CallOutcome;
|
|
241
283
|
/** The org's own number that was dialled — present only (and only meaningful) for an inbound call. */
|
|
242
284
|
readonly did?: string;
|
|
285
|
+
/**
|
|
286
|
+
* The org's own number an outbound call was made from (what the callee saw) — present only for outbound calls the
|
|
287
|
+
* backend recorded it for (outbound-number-per-agent.md §5.7). Digits, no `+`.
|
|
288
|
+
*/
|
|
289
|
+
readonly from?: string;
|
|
243
290
|
/** True when Coventia has at least one recording for this call. */
|
|
244
291
|
readonly recorded: boolean;
|
|
245
292
|
/**
|
|
@@ -344,6 +391,18 @@ interface PresenceConnection {
|
|
|
344
391
|
* own source that has no such message simply omits it and gets the grace. Sent just before `close()`.
|
|
345
392
|
*/
|
|
346
393
|
readonly bye?: () => void;
|
|
394
|
+
/**
|
|
395
|
+
* The line owner (`docs/specs/line-owner.md` §5.2): says which softphone this connection speaks for, so
|
|
396
|
+
* the server can tell it whether it has the line — through `PresenceCallbacks.onLine`, right after this
|
|
397
|
+
* and on every change. Sent as the FIRST message of every connection. Optional: a source without it never
|
|
398
|
+
* takes part, and its softphone listens exactly as before (fail-open), reading its line as `unknown`.
|
|
399
|
+
*/
|
|
400
|
+
readonly hello?: (clientId: string) => void;
|
|
401
|
+
/**
|
|
402
|
+
* "Recibir aquí": asks for the line. Answered through `PresenceCallbacks.onClaimResult` — and, when it
|
|
403
|
+
* succeeds, by `onLine` saying `here` first. Optional, like `hello`.
|
|
404
|
+
*/
|
|
405
|
+
readonly claimLine?: () => void;
|
|
347
406
|
/**
|
|
348
407
|
* Ends this connection deliberately. Never causes the SAME callbacks' `onClose` to fire — that is
|
|
349
408
|
* reserved for the connection ending on its OWN (a network drop, a server-side close, a rejected
|
|
@@ -363,6 +422,13 @@ interface PresenceCallbacks {
|
|
|
363
422
|
* identical from here, and both get the same treatment (spec §4.4 — quiet retry, nothing surfaced).
|
|
364
423
|
*/
|
|
365
424
|
readonly onClose: () => void;
|
|
425
|
+
/**
|
|
426
|
+
* The line, as the server says it — right after `PresenceConnection.hello`, and on every change after
|
|
427
|
+
* that. Optional, so callbacks written before the line owner existed still type-check.
|
|
428
|
+
*/
|
|
429
|
+
readonly onLine?: (update: LineUpdate) => void;
|
|
430
|
+
/** The answer to `PresenceConnection.claimLine()`. Optional, like `onLine`. */
|
|
431
|
+
readonly onClaimResult?: (result: LineClaimResult) => void;
|
|
366
432
|
}
|
|
367
433
|
/**
|
|
368
434
|
* Opens one live presence connection. Coventia-backed by default (mints a one-time ticket, opens
|
|
@@ -415,6 +481,64 @@ interface RingtoneSource {
|
|
|
415
481
|
*/
|
|
416
482
|
cached?(): Promise<RingtoneSettings | null>;
|
|
417
483
|
}
|
|
484
|
+
/**
|
|
485
|
+
* What kind of number this is, for the tags next to it in "Llamar desde" (outbound-number-per-agent.md §5.5).
|
|
486
|
+
* Display only — it never blocks a choice. `mobile` must not be used for customer care or unsolicited commercial
|
|
487
|
+
* calls; `commercial` (the 400 range) is for outbound commercial calls only and receives no calls.
|
|
488
|
+
*/
|
|
489
|
+
type OutboundNumberKind = 'landline' | 'mobile' | 'commercial' | 'other';
|
|
490
|
+
/** One of the organization's numbers an agent can call from. */
|
|
491
|
+
interface OutboundNumber {
|
|
492
|
+
/** Stable id — what is stored as the agent's choice. */
|
|
493
|
+
readonly id: string;
|
|
494
|
+
/** International format, digits only, no `+` — e.g. `34960000001`. */
|
|
495
|
+
readonly number: string;
|
|
496
|
+
/** The name the organization gave the number, or `null`. */
|
|
497
|
+
readonly label: string | null;
|
|
498
|
+
readonly kind: OutboundNumberKind;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Where the agent's extension stands. `pending`/`failed`: an assignment change could not update it yet, so the
|
|
502
|
+
* extension may still present an earlier number. `unassigned`: the agent has no extension in this organization.
|
|
503
|
+
*/
|
|
504
|
+
type OutboundNumberState = 'applied' | 'pending' | 'failed' | 'unassigned';
|
|
505
|
+
/** The organization's numbers and the caller's choice, as the backend resolves them. */
|
|
506
|
+
interface OutboundNumberSettings {
|
|
507
|
+
/** The organization's active numbers, ordered by number. */
|
|
508
|
+
readonly numbers: readonly OutboundNumber[];
|
|
509
|
+
/** The organization default (an `OutboundNumber.id`), or `null`. */
|
|
510
|
+
readonly orgDefault: string | null;
|
|
511
|
+
/** This agent's own choice, or `null` = "use the organization default". */
|
|
512
|
+
readonly mine: string | null;
|
|
513
|
+
/** The number this agent's calls show: `mine`, else `orgDefault`. */
|
|
514
|
+
readonly effective: string | null;
|
|
515
|
+
/** The agent's extension ("100"), or `null` when they have none. */
|
|
516
|
+
readonly extension: string | null;
|
|
517
|
+
readonly state: OutboundNumberState;
|
|
518
|
+
/** 0 when a change could go out now; otherwise the seconds until it can. */
|
|
519
|
+
readonly changeAvailableInSeconds: number;
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Why a change was refused before anything was sent. `platform`: the shared limit on number changes (nothing the
|
|
523
|
+
* agent did). `user`: the agent changed it moments ago. Either way the previous number stays in use.
|
|
524
|
+
*/
|
|
525
|
+
interface OutboundNumberRefusal {
|
|
526
|
+
readonly reason: 'platform' | 'user';
|
|
527
|
+
/** Seconds until a change can go out — always at least 1. */
|
|
528
|
+
readonly retryAfterSeconds: number;
|
|
529
|
+
}
|
|
530
|
+
/** The outbound number of the agent the softphone is signed in as, in its organization. */
|
|
531
|
+
interface OutboundNumbersSource {
|
|
532
|
+
get(signal?: AbortSignal): Promise<OutboundNumberSettings>;
|
|
533
|
+
/**
|
|
534
|
+
* Changes this agent's number; `null` = back to the organization default. Resolves to the new settings once the
|
|
535
|
+
* change is in effect. When the request limits refuse it, rejects with an `Error` carrying
|
|
536
|
+
* `refusal: OutboundNumberRefusal` (check with `isOutboundNumberRefused`) — nothing was stored or sent.
|
|
537
|
+
*/
|
|
538
|
+
setMine(id: string | null): Promise<OutboundNumberSettings>;
|
|
539
|
+
/** The settings this agent last had on this device, if remembered — instant, no network. Optional. */
|
|
540
|
+
cached?(): Promise<OutboundNumberSettings | null>;
|
|
541
|
+
}
|
|
418
542
|
|
|
419
543
|
/**
|
|
420
544
|
* The error taxonomy.
|
|
@@ -535,6 +659,16 @@ declare const ERROR_CATALOGUE: {
|
|
|
535
659
|
readonly message: "The provider does not support this operation.";
|
|
536
660
|
readonly solution: "Guard the UI on the operation before offering it.";
|
|
537
661
|
};
|
|
662
|
+
readonly 'line/claim-refused': {
|
|
663
|
+
readonly fatal: false;
|
|
664
|
+
readonly message: "The line was not moved here: the user is on a call on the softphone that has it.";
|
|
665
|
+
readonly solution: "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.";
|
|
666
|
+
};
|
|
667
|
+
readonly 'line/unavailable': {
|
|
668
|
+
readonly fatal: false;
|
|
669
|
+
readonly message: "The line could not be taken: nothing decided where it is.";
|
|
670
|
+
readonly solution: "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.";
|
|
671
|
+
};
|
|
538
672
|
readonly 'provider/provision-failed': {
|
|
539
673
|
readonly fatal: true;
|
|
540
674
|
readonly message: "The provider could not issue SIP credentials.";
|
|
@@ -623,10 +757,25 @@ declare enum SoftphoneEvent {
|
|
|
623
757
|
/**
|
|
624
758
|
* `softphone.presence` changed — the live agent-presence list (Phase B,
|
|
625
759
|
* `docs/specs/transfer-presence-picker.md`) gained a snapshot or an updated entry. Requires a
|
|
626
|
-
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being
|
|
627
|
-
* `online
|
|
760
|
+
* `PresenceSource` (from `coventia`/`apiBase`, or a host override) AND this tab currently being a
|
|
761
|
+
* started leader — `online`, or waiting in `standby` for the line; never fires at all otherwise,
|
|
762
|
+
* same reasoning as `TransferOutcome`.
|
|
628
763
|
*/
|
|
629
|
-
PresenceChanged = "presenceChanged"
|
|
764
|
+
PresenceChanged = "presenceChanged",
|
|
765
|
+
/**
|
|
766
|
+
* `softphone.line` / `lineWhere` changed (`docs/specs/line-owner.md` §5): this softphone got the line,
|
|
767
|
+
* lost it to another softphone of the same extension, or stopped knowing (`unknown` — it then listens,
|
|
768
|
+
* as before the line owner existed). A third axis beside `StateChanged` and `LeaderChanged`: losing the
|
|
769
|
+
* line is what takes the softphone to `standby`, but not at once while a call is in progress — the line
|
|
770
|
+
* can read `elsewhere` while the state is still `online`, until that call ends.
|
|
771
|
+
*/
|
|
772
|
+
LineChanged = "lineChanged",
|
|
773
|
+
/**
|
|
774
|
+
* `softphone.lineClaim` changed: an attempt to bring the line here started, was refused, failed, or ended
|
|
775
|
+
* (the line came here, or the softphone stopped). Kept apart from `LineChanged`, which only ever means the
|
|
776
|
+
* line itself moved.
|
|
777
|
+
*/
|
|
778
|
+
LineClaimChanged = "lineClaimChanged"
|
|
630
779
|
}
|
|
631
780
|
interface SoftphoneEventMap {
|
|
632
781
|
[SoftphoneEvent.StateChanged]: {
|
|
@@ -647,6 +796,13 @@ interface SoftphoneEventMap {
|
|
|
647
796
|
[SoftphoneEvent.PresenceChanged]: {
|
|
648
797
|
readonly agents: readonly PresenceEntry[];
|
|
649
798
|
};
|
|
799
|
+
[SoftphoneEvent.LineChanged]: {
|
|
800
|
+
readonly line: LineState;
|
|
801
|
+
readonly where: LineLocation | null;
|
|
802
|
+
};
|
|
803
|
+
[SoftphoneEvent.LineClaimChanged]: {
|
|
804
|
+
readonly claim: LineClaim;
|
|
805
|
+
};
|
|
650
806
|
}
|
|
651
807
|
declare enum CallEvent {
|
|
652
808
|
/** Any transition. The catch-all; the specific events below are conveniences. */
|
|
@@ -873,4 +1029,4 @@ interface Call extends TypedEmitter<CallEventMap> {
|
|
|
873
1029
|
cancelConsult(): Promise<void>;
|
|
874
1030
|
}
|
|
875
1031
|
|
|
876
|
-
export { type
|
|
1032
|
+
export { type TokenSource as A, type TypedEmitter as B, CALL_TRANSITIONS as C, canTransition as D, ERROR_CATALOGUE as E, type FatalErrorCode as F, isFatal as G, type HangupReason as H, INITIAL_STATES as I, isTerminal as J, type ContactsSource as K, type LineClaim as L, type HistorySource as M, type NonFatalErrorCode as N, type OutboundNumberRefusal as O, type PresenceEntry as P, type AgentsDirectory as Q, type RemoteParty as R, type SoftphoneConfig as S, type TokenEndpointOptions as T, type TransferOutcomesPage as U, type VoiceToken as V, type PresenceSource as W, type CallHistoryEntry as X, type ContactsPage as Y, 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 LineLocation as j, type LineState as k, type LogLevel as l, type NonFatalSoftphoneError as m, type OutboundNumber as n, type OutboundNumberKind as o, type OutboundNumberSettings as p, type OutboundNumberState as q, type OutboundNumbersSource as r, type RingtoneOption as s, type RingtoneSettings as t, type RingtoneSource as u, type SoftphoneError as v, type SoftphoneErrorCode as w, SoftphoneEvent as x, type SoftphoneEventMap as y, type SoftphoneState as z };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// src/core/outbound-numbers.ts
|
|
2
|
+
function isOutboundNumberRefused(value) {
|
|
3
|
+
const refusal = value?.refusal;
|
|
4
|
+
return value instanceof Error && !!refusal && (refusal.reason === "platform" || refusal.reason === "user") && typeof refusal.retryAfterSeconds === "number";
|
|
5
|
+
}
|
|
6
|
+
function outboundNumberRefused(refusal) {
|
|
7
|
+
const error = new Error(`Outbound number change refused (${refusal.reason}); retry in ${refusal.retryAfterSeconds}s`);
|
|
8
|
+
error.name = "OutboundNumberRefused";
|
|
9
|
+
return Object.assign(error, { refusal });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export { isOutboundNumberRefused, outboundNumberRefused };
|
|
13
|
+
//# sourceMappingURL=chunk-5E5OEXS2.js.map
|
|
14
|
+
//# sourceMappingURL=chunk-5E5OEXS2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/outbound-numbers.ts"],"names":[],"mappings":";AAWO,SAAS,wBAAwB,KAAA,EAAqD;AAC3F,EAAA,MAAM,UAAW,KAAA,EAA+D,OAAA;AAChF,EAAA,OACE,KAAA,YAAiB,KAAA,IACjB,CAAC,CAAC,OAAA,KACD,OAAA,CAAQ,MAAA,KAAW,UAAA,IAAc,OAAA,CAAQ,MAAA,KAAW,MAAA,CAAA,IACrD,OAAO,QAAQ,iBAAA,KAAsB,QAAA;AAEzC;AAGO,SAAS,sBAAsB,OAAA,EAA4D;AAChG,EAAA,MAAM,KAAA,GAAQ,IAAI,KAAA,CAAM,CAAA,gCAAA,EAAmC,QAAQ,MAAM,CAAA,YAAA,EAAe,OAAA,CAAQ,iBAAiB,CAAA,CAAA,CAAG,CAAA;AACpH,EAAA,KAAA,CAAM,IAAA,GAAO,uBAAA;AACb,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,KAAA,EAAO,EAAE,SAAS,CAAA;AACzC","file":"chunk-5E5OEXS2.js","sourcesContent":["import type { OutboundNumberRefusal } from './types.js';\r\n\r\n/**\r\n * A change of outbound number the request limits refused (outbound-number-per-agent.md §5.3–§5.5). Structural,\r\n * like `isSoftphoneError`: the concrete error is a plain `Error` with a `refusal`, so a host — or a second copy\r\n * of the SDK on the page — can recognise it without sharing a class.\r\n */\r\nexport interface OutboundNumberRefusedError extends Error {\r\n readonly refusal: OutboundNumberRefusal;\r\n}\r\n\r\nexport function isOutboundNumberRefused(value: unknown): value is OutboundNumberRefusedError {\r\n const refusal = (value as { refusal?: Partial<OutboundNumberRefusal> } | null)?.refusal;\r\n return (\r\n value instanceof Error &&\r\n !!refusal &&\r\n (refusal.reason === 'platform' || refusal.reason === 'user') &&\r\n typeof refusal.retryAfterSeconds === 'number'\r\n );\r\n}\r\n\r\n/** @internal — raised by the Coventia source; hosts only read these. */\r\nexport function outboundNumberRefused(refusal: OutboundNumberRefusal): OutboundNumberRefusedError {\r\n const error = new Error(`Outbound number change refused (${refusal.reason}); retry in ${refusal.retryAfterSeconds}s`);\r\n error.name = 'OutboundNumberRefused';\r\n return Object.assign(error, { refusal });\r\n}\r\n"]}
|