@coventia/softphone 0.2.0 → 0.3.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-DUVnDxsz.d.cts → call-CaPDWVk3.d.cts} +50 -1
- package/dist/{call-DUVnDxsz.d.ts → call-CaPDWVk3.d.ts} +50 -1
- package/dist/{chunk-YWI6EQYS.js → chunk-EYEDPD7Y.js} +108 -15
- package/dist/chunk-EYEDPD7Y.js.map +1 -0
- package/dist/{chunk-LMJXX4YM.js → chunk-UHNLEBBO.js} +4 -2
- package/dist/chunk-UHNLEBBO.js.map +1 -0
- package/dist/element/index.d.ts +76 -11
- package/dist/element/index.js +1411 -478
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-EPCKf-Bl.d.ts → engine-BgBsIcEa.d.ts} +1 -1
- package/dist/{engine-CDjkrXHY.d.cts → engine-CU2rEo3g.d.cts} +1 -1
- package/dist/index.cjs +318 -32
- 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 +157 -19
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +2 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +1 -1
- package/dist/{softphone-BtdgZWkm.d.ts → softphone-DEt0TtQw.d.ts} +26 -10
- package/dist/{softphone-DHG9MTNg.d.cts → softphone-dZMkW8V1.d.cts} +26 -10
- package/dist/softphone.css +192 -46
- package/dist/{sources-5L2R4HTV.js → sources-M3GYMS6V.js} +58 -3
- package/dist/sources-M3GYMS6V.js.map +1 -0
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/ui/index.cjs +1308 -366
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +40 -7
- package/dist/ui/index.d.ts +40 -7
- package/dist/ui/index.js +1308 -369
- package/dist/ui/index.js.map +1 -1
- package/dist/zadarma/index.cjs +106 -13
- 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-4VOPIBXG.js.map → zadarma-engine-L5DEFXUS.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-LMJXX4YM.js.map +0 -1
- package/dist/chunk-YWI6EQYS.js.map +0 -1
- package/dist/sources-5L2R4HTV.js.map +0 -1
- package/dist/zadarma-engine-4VOPIBXG.js +0 -5
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { S as Softphone } from './softphone-
|
|
2
|
-
export { A as AudioDevices } from './softphone-
|
|
3
|
-
import { S as SoftphoneConfig } from './call-
|
|
4
|
-
export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as
|
|
1
|
+
import { S as Softphone } from './softphone-dZMkW8V1.cjs';
|
|
2
|
+
export { A as AudioDevices } from './softphone-dZMkW8V1.cjs';
|
|
3
|
+
import { S as SoftphoneConfig } from './call-CaPDWVk3.cjs';
|
|
4
|
+
export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as RingtoneOption, l as RingtoneSettings, m as RingtoneSource, n as SoftphoneError, o as SoftphoneErrorCode, p as SoftphoneEvent, q as SoftphoneEventMap, r as SoftphoneState, T as TokenEndpointOptions, s as TokenSource, t as TypedEmitter, V as VoiceToken, u as canTransition, v as isFatal, w as isTerminal } from './call-CaPDWVk3.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Construct a softphone.
|
|
@@ -57,6 +57,6 @@ declare function isWebRtcSupported(): boolean;
|
|
|
57
57
|
* - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.
|
|
58
58
|
*/
|
|
59
59
|
|
|
60
|
-
declare const VERSION = "0.
|
|
60
|
+
declare const VERSION = "0.3.0";
|
|
61
61
|
|
|
62
62
|
export { Softphone, SoftphoneConfig, VERSION, createSoftphone, isBrowser, isWebRtcSupported };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { S as Softphone } from './softphone-
|
|
2
|
-
export { A as AudioDevices } from './softphone-
|
|
3
|
-
import { S as SoftphoneConfig } from './call-
|
|
4
|
-
export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as
|
|
1
|
+
import { S as Softphone } from './softphone-DEt0TtQw.js';
|
|
2
|
+
export { A as AudioDevices } from './softphone-DEt0TtQw.js';
|
|
3
|
+
import { S as SoftphoneConfig } from './call-CaPDWVk3.js';
|
|
4
|
+
export { C as CALL_TRANSITIONS, a as Call, b as CallDirection, c as CallEvent, d as CallEventMap, e as CallOptions, f as CallState, g as Contact, E as ERROR_CATALOGUE, F as FatalErrorCode, h as FatalSoftphoneError, H as HangupReason, i as HoldOriginator, I as INITIAL_STATES, L as LogLevel, N as NonFatalErrorCode, j as NonFatalSoftphoneError, R as RemoteParty, k as RingtoneOption, l as RingtoneSettings, m as RingtoneSource, n as SoftphoneError, o as SoftphoneErrorCode, p as SoftphoneEvent, q as SoftphoneEventMap, r as SoftphoneState, T as TokenEndpointOptions, s as TokenSource, t as TypedEmitter, V as VoiceToken, u as canTransition, v as isFatal, w as isTerminal } from './call-CaPDWVk3.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Construct a softphone.
|
|
@@ -57,6 +57,6 @@ declare function isWebRtcSupported(): boolean;
|
|
|
57
57
|
* - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.
|
|
58
58
|
*/
|
|
59
59
|
|
|
60
|
-
declare const VERSION = "0.
|
|
60
|
+
declare const VERSION = "0.3.0";
|
|
61
61
|
|
|
62
62
|
export { Softphone, SoftphoneConfig, VERSION, createSoftphone, isBrowser, isWebRtcSupported };
|
package/dist/index.js
CHANGED
|
@@ -75,6 +75,9 @@ function canTransition(from, to) {
|
|
|
75
75
|
function isTerminal(state) {
|
|
76
76
|
return CALL_TRANSITIONS[state].length === 0;
|
|
77
77
|
}
|
|
78
|
+
function isOnCallState(state) {
|
|
79
|
+
return state !== "ringing" && !isTerminal(state);
|
|
80
|
+
}
|
|
78
81
|
|
|
79
82
|
// src/core/call-impl.ts
|
|
80
83
|
var CallImpl = class _CallImpl extends Emitter {
|
|
@@ -281,7 +284,7 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
|
|
|
281
284
|
}
|
|
282
285
|
let pending = null;
|
|
283
286
|
const load = () => {
|
|
284
|
-
pending ??= import('./sources-
|
|
287
|
+
pending ??= import('./sources-M3GYMS6V.js').then(
|
|
285
288
|
(m) => m.coventiaSources({ ...config, baseUrl, tokenSource })
|
|
286
289
|
);
|
|
287
290
|
return pending;
|
|
@@ -292,7 +295,12 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
|
|
|
292
295
|
resolveContact: async (number) => (await load()).resolveContact(number),
|
|
293
296
|
listAgents: async () => (await load()).listAgents(),
|
|
294
297
|
transferOutcomes: async (params) => (await load()).transferOutcomes(params),
|
|
295
|
-
presence: async (callbacks) => (await load()).presence(callbacks)
|
|
298
|
+
presence: async (callbacks) => (await load()).presence(callbacks),
|
|
299
|
+
ringtones: {
|
|
300
|
+
get: async (signal) => (await load()).ringtones.get(signal),
|
|
301
|
+
setMine: async (id) => (await load()).ringtones.setMine(id),
|
|
302
|
+
cached: async () => (await load()).ringtones.cached?.() ?? null
|
|
303
|
+
}
|
|
296
304
|
};
|
|
297
305
|
}
|
|
298
306
|
|
|
@@ -426,13 +434,18 @@ function startPresenceSocket(params) {
|
|
|
426
434
|
let agents = /* @__PURE__ */ new Map();
|
|
427
435
|
let lastReportedOnCall = null;
|
|
428
436
|
let connection = null;
|
|
429
|
-
|
|
437
|
+
let byeOnArrival = false;
|
|
438
|
+
const stop = (options) => {
|
|
430
439
|
if (stopped) return;
|
|
431
440
|
stopped = true;
|
|
432
441
|
if (reconnectTimer !== null) {
|
|
433
442
|
cancelTimeout(reconnectTimer);
|
|
434
443
|
reconnectTimer = null;
|
|
435
444
|
}
|
|
445
|
+
if (options?.bye) {
|
|
446
|
+
byeOnArrival = true;
|
|
447
|
+
connection?.bye?.();
|
|
448
|
+
}
|
|
436
449
|
connection?.close();
|
|
437
450
|
connection = null;
|
|
438
451
|
};
|
|
@@ -471,6 +484,7 @@ function startPresenceSocket(params) {
|
|
|
471
484
|
return;
|
|
472
485
|
}
|
|
473
486
|
if (stopped) {
|
|
487
|
+
if (byeOnArrival) opened.bye?.();
|
|
474
488
|
opened.close();
|
|
475
489
|
return;
|
|
476
490
|
}
|
|
@@ -491,18 +505,36 @@ function startPresenceSocket(params) {
|
|
|
491
505
|
|
|
492
506
|
// src/core/softphone-impl.ts
|
|
493
507
|
var liveInstance = false;
|
|
508
|
+
var PRESENCE_TEARDOWN_GRACE_MS = 1e4;
|
|
494
509
|
var BrowserAudioDevices = class {
|
|
495
510
|
inputs = [];
|
|
496
511
|
outputs = [];
|
|
497
512
|
currentInputId = null;
|
|
498
513
|
currentOutputId = null;
|
|
514
|
+
currentRingtoneOutputId = null;
|
|
499
515
|
canSelectOutput;
|
|
516
|
+
canSelectRingtoneOutput;
|
|
500
517
|
#engine;
|
|
501
518
|
#onChange;
|
|
519
|
+
/**
|
|
520
|
+
* What the user CHOSE, remembered across reloads — distinct from the `current*` fields, which are
|
|
521
|
+
* what is usable right now. Keeping them apart is what stops a fresh page load from wiping a saved
|
|
522
|
+
* choice: until the microphone has been granted, Chrome enumerates every device with a blank id, so
|
|
523
|
+
* "is the saved device in the list?" is no on every first enumeration. The preference survives
|
|
524
|
+
* that; `current*` simply reads `null` until the device is really there.
|
|
525
|
+
*/
|
|
526
|
+
#preferred;
|
|
527
|
+
/** The input the engine was last told about — it starts on the system default (`null`). */
|
|
528
|
+
#engineInputId = null;
|
|
502
529
|
constructor(engine, onChange) {
|
|
503
530
|
this.#engine = engine;
|
|
504
531
|
this.#onChange = onChange;
|
|
505
532
|
this.canSelectOutput = isBrowser() && typeof HTMLMediaElement !== "undefined" && "setSinkId" in HTMLMediaElement.prototype;
|
|
533
|
+
this.canSelectRingtoneOutput = this.canSelectOutput;
|
|
534
|
+
this.#preferred = readStoredDevices();
|
|
535
|
+
this.currentInputId = this.#preferred.input;
|
|
536
|
+
this.currentOutputId = this.#preferred.output;
|
|
537
|
+
this.currentRingtoneOutputId = this.#preferred.ringtone;
|
|
506
538
|
if (isBrowser() && navigator.mediaDevices) {
|
|
507
539
|
void this.refresh();
|
|
508
540
|
navigator.mediaDevices.addEventListener?.("devicechange", () => void this.refresh());
|
|
@@ -518,26 +550,63 @@ var BrowserAudioDevices = class {
|
|
|
518
550
|
}
|
|
519
551
|
this.inputs = all.filter((d) => d.kind === "audioinput");
|
|
520
552
|
this.outputs = all.filter((d) => d.kind === "audiooutput");
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
if (this.currentOutputId && !this.outputs.some((d) => d.deviceId === this.currentOutputId)) {
|
|
525
|
-
this.currentOutputId = null;
|
|
526
|
-
}
|
|
553
|
+
this.currentInputId = usable(this.#preferred.input, this.inputs);
|
|
554
|
+
this.currentOutputId = usable(this.#preferred.output, this.outputs);
|
|
555
|
+
this.currentRingtoneOutputId = usable(this.#preferred.ringtone, this.outputs);
|
|
527
556
|
this.#onChange();
|
|
557
|
+
if (this.currentInputId && this.currentInputId !== this.#engineInputId) {
|
|
558
|
+
await this.#tellEngine(this.currentInputId);
|
|
559
|
+
}
|
|
528
560
|
}
|
|
529
561
|
async setInput(deviceId) {
|
|
530
562
|
this.currentInputId = deviceId;
|
|
563
|
+
this.#remember({ input: deviceId });
|
|
531
564
|
this.#onChange();
|
|
565
|
+
await this.#tellEngine(deviceId);
|
|
566
|
+
}
|
|
567
|
+
async #tellEngine(deviceId) {
|
|
568
|
+
this.#engineInputId = deviceId;
|
|
532
569
|
await this.#engine.setInputDevice?.(deviceId);
|
|
533
570
|
}
|
|
534
571
|
async setOutput(deviceId) {
|
|
535
572
|
this.currentOutputId = deviceId;
|
|
573
|
+
this.#remember({ output: deviceId });
|
|
536
574
|
this.#onChange();
|
|
537
575
|
}
|
|
538
|
-
async setRingtoneOutput() {
|
|
576
|
+
async setRingtoneOutput(deviceId) {
|
|
577
|
+
this.currentRingtoneOutputId = deviceId;
|
|
578
|
+
this.#remember({ ringtone: deviceId });
|
|
579
|
+
this.#onChange();
|
|
580
|
+
}
|
|
581
|
+
#remember(patch) {
|
|
582
|
+
this.#preferred = { ...this.#preferred, ...patch };
|
|
583
|
+
writeStoredDevices(this.#preferred);
|
|
539
584
|
}
|
|
540
585
|
};
|
|
586
|
+
var DEVICES_KEY = "coventia-softphone:devices";
|
|
587
|
+
var NO_DEVICES = { input: null, output: null, ringtone: null };
|
|
588
|
+
function usable(id, list) {
|
|
589
|
+
return id && list.some((d) => d.deviceId === id) ? id : null;
|
|
590
|
+
}
|
|
591
|
+
function readStoredDevices() {
|
|
592
|
+
if (!isBrowser()) return NO_DEVICES;
|
|
593
|
+
try {
|
|
594
|
+
const raw = JSON.parse(globalThis.localStorage?.getItem(DEVICES_KEY) ?? "null");
|
|
595
|
+
if (!raw || typeof raw !== "object") return NO_DEVICES;
|
|
596
|
+
const pick = (v) => typeof v === "string" && v ? v : null;
|
|
597
|
+
const r = raw;
|
|
598
|
+
return { input: pick(r.input), output: pick(r.output), ringtone: pick(r.ringtone) };
|
|
599
|
+
} catch {
|
|
600
|
+
return NO_DEVICES;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
function writeStoredDevices(devices) {
|
|
604
|
+
if (!isBrowser()) return;
|
|
605
|
+
try {
|
|
606
|
+
globalThis.localStorage?.setItem(DEVICES_KEY, JSON.stringify(devices));
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
}
|
|
541
610
|
var SoftphoneImpl = class extends Emitter {
|
|
542
611
|
#state = "stopped";
|
|
543
612
|
#lastError = null;
|
|
@@ -565,6 +634,8 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
565
634
|
* rather than by any one call.
|
|
566
635
|
*/
|
|
567
636
|
#presenceWatch = null;
|
|
637
|
+
/** A pending state-driven teardown of the watch — see `PRESENCE_TEARDOWN_GRACE_MS`. */
|
|
638
|
+
#presenceTeardownTimer = null;
|
|
568
639
|
#presenceAgents = [];
|
|
569
640
|
/**
|
|
570
641
|
* `tabLock` defaults to the real Web Locks implementation — not part of `SoftphoneConfig` (an
|
|
@@ -624,6 +695,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
624
695
|
}
|
|
625
696
|
async stop() {
|
|
626
697
|
if (!this.#startedHere) return;
|
|
698
|
+
this.#sayGoodbye();
|
|
627
699
|
this.#tabLock.release();
|
|
628
700
|
if (this.#isLeader) {
|
|
629
701
|
await this.#engine.stop();
|
|
@@ -639,8 +711,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
639
711
|
this.#destroyed = true;
|
|
640
712
|
for (const stopWatch of this.#activeWatches) stopWatch();
|
|
641
713
|
this.#activeWatches.clear();
|
|
642
|
-
this.#
|
|
643
|
-
this.#presenceWatch = null;
|
|
714
|
+
this.#sayGoodbye();
|
|
644
715
|
await this.stop().catch(() => {
|
|
645
716
|
});
|
|
646
717
|
this.#setState("destroyed");
|
|
@@ -675,11 +746,15 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
675
746
|
(target, callerNumber) => this.#onTransfer(target, callerNumber)
|
|
676
747
|
);
|
|
677
748
|
this.#activeCall = call;
|
|
749
|
+
const offState = call.on("stateChanged" /* StateChanged */, () => this.#syncCallState());
|
|
750
|
+
this.#syncCallState();
|
|
678
751
|
const off = call.on("ended" /* Ended */, () => {
|
|
679
752
|
off();
|
|
753
|
+
offState();
|
|
680
754
|
if (this.#activeCall === call) {
|
|
681
755
|
this.#activeCall = null;
|
|
682
756
|
this.emit("activeCallChanged" /* ActiveCallChanged */, { call: null });
|
|
757
|
+
this.#syncCallState();
|
|
683
758
|
}
|
|
684
759
|
});
|
|
685
760
|
if (this.#resolveContact) {
|
|
@@ -699,6 +774,21 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
699
774
|
this.emit("stateChanged" /* StateChanged */, { state, previous });
|
|
700
775
|
this.#syncPresenceWatch();
|
|
701
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* Whether this softphone is on a call right now, as presence means it (`isOnCallState`). There is a single
|
|
779
|
+
* active-call slot, and a consult leg is a child of that call, so the parent staying alive covers a consult.
|
|
780
|
+
*/
|
|
781
|
+
#isOnCall() {
|
|
782
|
+
return this.#activeCall !== null && isOnCallState(this.#activeCall.state);
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Reports the current on-a-call value to the backend. The watch de-duplicates (it only sends on change) and
|
|
786
|
+
* remembers the latest value to replay after a reconnect, so this can be called on every transition without
|
|
787
|
+
* ceremony. A no-op while there is no presence watch (not online, not the leader, no backend configured).
|
|
788
|
+
*/
|
|
789
|
+
#syncCallState() {
|
|
790
|
+
this.#presenceWatch?.reportCallState(this.#isOnCall());
|
|
791
|
+
}
|
|
702
792
|
#setLeader(isLeader) {
|
|
703
793
|
if (isLeader === this.#isLeader) return;
|
|
704
794
|
this.#isLeader = isLeader;
|
|
@@ -717,9 +807,21 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
717
807
|
* This still passes `getIsLeader: () => this.#isLeader` for that narrower, defensive recheck; the
|
|
718
808
|
* BROAD online/leader gate — whether to be connected AT ALL — is owned entirely here, the same
|
|
719
809
|
* split `#onTransfer` already has with `startTransferWatch`.
|
|
810
|
+
*
|
|
811
|
+
* One exception to "tear down the moment `online` is lost": a leader that is merely `offline` or
|
|
812
|
+
* `reconnecting` keeps its watch for `PRESENCE_TEARDOWN_GRACE_MS` — a blip shorter than that never touches
|
|
813
|
+
* the presence socket at all. Anything else (stopped, starting, destroyed, or no longer the leader) still
|
|
814
|
+
* tears it down at once: those are not blips, and a follower must never hold a connection.
|
|
720
815
|
*/
|
|
721
816
|
#syncPresenceWatch() {
|
|
722
|
-
const
|
|
817
|
+
const eligible = Boolean(this.sources) && this.#isLeader;
|
|
818
|
+
const shouldRun = eligible && this.#state === "online";
|
|
819
|
+
const blip = eligible && (this.#state === "offline" || this.#state === "reconnecting");
|
|
820
|
+
if (!blip) this.#cancelPresenceTeardown();
|
|
821
|
+
if (blip && this.#presenceWatch !== null) {
|
|
822
|
+
this.#schedulePresenceTeardown();
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
723
825
|
if (shouldRun === (this.#presenceWatch !== null)) return;
|
|
724
826
|
if (shouldRun) {
|
|
725
827
|
this.#presenceWatch = startPresenceSocket({
|
|
@@ -730,13 +832,49 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
730
832
|
this.emit("presenceChanged" /* PresenceChanged */, { agents });
|
|
731
833
|
}
|
|
732
834
|
});
|
|
835
|
+
this.#syncCallState();
|
|
733
836
|
} else {
|
|
734
|
-
this.#
|
|
735
|
-
this.#presenceWatch = null;
|
|
736
|
-
this.#presenceAgents = [];
|
|
737
|
-
this.emit("presenceChanged" /* PresenceChanged */, { agents: [] });
|
|
837
|
+
this.#stopPresence();
|
|
738
838
|
}
|
|
739
839
|
}
|
|
840
|
+
/**
|
|
841
|
+
* Starts the grace period before an `offline` / `reconnecting` leader loses its presence socket. Idempotent
|
|
842
|
+
* on purpose: a second notification inside the same blip must NOT push the deadline back.
|
|
843
|
+
*/
|
|
844
|
+
#schedulePresenceTeardown() {
|
|
845
|
+
if (this.#presenceTeardownTimer !== null) return;
|
|
846
|
+
this.#presenceTeardownTimer = setTimeout(() => {
|
|
847
|
+
this.#presenceTeardownTimer = null;
|
|
848
|
+
if (this.#state === "online" && this.#isLeader) return;
|
|
849
|
+
this.#stopPresence();
|
|
850
|
+
}, PRESENCE_TEARDOWN_GRACE_MS);
|
|
851
|
+
}
|
|
852
|
+
#cancelPresenceTeardown() {
|
|
853
|
+
if (this.#presenceTeardownTimer === null) return;
|
|
854
|
+
clearTimeout(this.#presenceTeardownTimer);
|
|
855
|
+
this.#presenceTeardownTimer = null;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Stops the presence watch and forgets its roster. A STATE-DRIVEN teardown (the softphone left `online`
|
|
859
|
+
* for good, or lost leadership) passes nothing and says no goodbye; only a deliberate stop passes `bye`.
|
|
860
|
+
* Any pending grace-period teardown is cancelled — there is nothing left for it to do.
|
|
861
|
+
*/
|
|
862
|
+
#stopPresence(options) {
|
|
863
|
+
this.#cancelPresenceTeardown();
|
|
864
|
+
if (!this.#presenceWatch) return;
|
|
865
|
+
this.#presenceWatch.stop(options);
|
|
866
|
+
this.#presenceWatch = null;
|
|
867
|
+
this.#presenceAgents = [];
|
|
868
|
+
this.emit("presenceChanged" /* PresenceChanged */, { agents: [] });
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* A DELIBERATE stop (`stop()` / `destroy()`): say any call is over, then say goodbye, so the backend drops
|
|
872
|
+
* this phone at once instead of waiting out the close grace. Idempotent — a second call finds no watch.
|
|
873
|
+
*/
|
|
874
|
+
#sayGoodbye() {
|
|
875
|
+
this.#presenceWatch?.reportCallState(false);
|
|
876
|
+
this.#stopPresence({ bye: true });
|
|
877
|
+
}
|
|
740
878
|
/**
|
|
741
879
|
* Phase A (transfer outcome relay, `docs/specs/transfer-outcome-relay.md`) — `CallImpl.transfer()`'s
|
|
742
880
|
* internal seam calls this. No-ops entirely when no Coventia backend is configured (`sources` is
|
|
@@ -811,7 +949,7 @@ function defaultZadarmaEngine(config) {
|
|
|
811
949
|
const stateChange = new LazyRelay();
|
|
812
950
|
const error = new LazyRelay();
|
|
813
951
|
function load() {
|
|
814
|
-
pending ??= import('./zadarma-engine-
|
|
952
|
+
pending ??= import('./zadarma-engine-L5DEFXUS.js').then(({ createZadarmaEngine }) => {
|
|
815
953
|
const engine = createZadarmaEngine(config);
|
|
816
954
|
incoming.attach({ on: (l) => engine.onIncoming(l) });
|
|
817
955
|
stateChange.attach({ on: (l) => engine.onStateChange(l) });
|
|
@@ -871,7 +1009,7 @@ function isCallEngine(value) {
|
|
|
871
1009
|
}
|
|
872
1010
|
|
|
873
1011
|
// src/index.ts
|
|
874
|
-
var VERSION = "0.
|
|
1012
|
+
var VERSION = "0.3.0";
|
|
875
1013
|
|
|
876
1014
|
export { CALL_TRANSITIONS, INITIAL_STATES, VERSION, canTransition, createSoftphone, isTerminal };
|
|
877
1015
|
//# sourceMappingURL=index.js.map
|