@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
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { isBrowser } from './chunk-KAXEMJ7E.js';
|
|
2
2
|
export { isBrowser, isWebRtcSupported } from './chunk-KAXEMJ7E.js';
|
|
3
|
-
export { CallEvent, SoftphoneEvent } from './chunk-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export { CallEvent, SoftphoneEvent } from './chunk-N4PLEYFJ.js';
|
|
4
|
+
export { isOutboundNumberRefused } from './chunk-5E5OEXS2.js';
|
|
5
|
+
import { normalizeApiBase } from './chunk-M76SAXXM.js';
|
|
6
|
+
import { createError, isSoftphoneError, isFatal } from './chunk-FRXHEHQA.js';
|
|
7
|
+
export { ERROR_CATALOGUE, isFatal } from './chunk-FRXHEHQA.js';
|
|
6
8
|
import { cachedTokenSource } from './chunk-MHOJHG5E.js';
|
|
7
9
|
|
|
8
10
|
// src/core/emitter.ts
|
|
@@ -284,7 +286,7 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
|
|
|
284
286
|
}
|
|
285
287
|
let pending = null;
|
|
286
288
|
const load = () => {
|
|
287
|
-
pending ??= import('./sources-
|
|
289
|
+
pending ??= import('./sources-QL2LRZVH.js').then(
|
|
288
290
|
(m) => m.coventiaSources({ ...config, baseUrl, tokenSource })
|
|
289
291
|
);
|
|
290
292
|
return pending;
|
|
@@ -300,6 +302,11 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
|
|
|
300
302
|
get: async (signal) => (await load()).ringtones.get(signal),
|
|
301
303
|
setMine: async (id) => (await load()).ringtones.setMine(id),
|
|
302
304
|
cached: async () => (await load()).ringtones.cached?.() ?? null
|
|
305
|
+
},
|
|
306
|
+
outboundNumbers: {
|
|
307
|
+
get: async (signal) => (await load()).outboundNumbers.get(signal),
|
|
308
|
+
setMine: async (id) => (await load()).outboundNumbers.setMine(id),
|
|
309
|
+
cached: async () => (await load()).outboundNumbers.cached?.() ?? null
|
|
303
310
|
}
|
|
304
311
|
};
|
|
305
312
|
}
|
|
@@ -310,13 +317,15 @@ function isWebLocksSupported() {
|
|
|
310
317
|
return isBrowser() && typeof navigator.locks?.request === "function";
|
|
311
318
|
}
|
|
312
319
|
function webLocksTabLock(lockName = LOCK_NAME) {
|
|
313
|
-
|
|
320
|
+
let controller;
|
|
314
321
|
let releaseHold;
|
|
315
322
|
return {
|
|
316
323
|
acquireLeadership() {
|
|
317
324
|
if (!isWebLocksSupported()) return Promise.resolve();
|
|
325
|
+
const { signal } = controller = new AbortController();
|
|
318
326
|
return new Promise((resolveAcquired) => {
|
|
319
|
-
navigator.locks.request(lockName, { signal
|
|
327
|
+
navigator.locks.request(lockName, { signal }, () => {
|
|
328
|
+
if (signal.aborted) return Promise.resolve();
|
|
320
329
|
resolveAcquired();
|
|
321
330
|
return new Promise((resolveHold) => {
|
|
322
331
|
releaseHold = resolveHold;
|
|
@@ -331,7 +340,7 @@ function webLocksTabLock(lockName = LOCK_NAME) {
|
|
|
331
340
|
releaseHold = void 0;
|
|
332
341
|
resolve();
|
|
333
342
|
} else {
|
|
334
|
-
controller
|
|
343
|
+
controller?.abort();
|
|
335
344
|
}
|
|
336
345
|
}
|
|
337
346
|
};
|
|
@@ -416,13 +425,19 @@ function startTransferWatch(params) {
|
|
|
416
425
|
var RECONNECT_BASE_MS = 1e3;
|
|
417
426
|
var RECONNECT_MAX_MS = 3e4;
|
|
418
427
|
var NOOP_WATCH = { reportCallState: () => {
|
|
419
|
-
}, stop: () => {
|
|
428
|
+
}, claimLine: () => false, stop: () => {
|
|
420
429
|
} };
|
|
421
430
|
function startPresenceSocket(params) {
|
|
422
431
|
const {
|
|
423
432
|
source,
|
|
424
433
|
getIsLeader,
|
|
425
434
|
onAgentsChanged,
|
|
435
|
+
clientId,
|
|
436
|
+
onLine,
|
|
437
|
+
onClaimResult,
|
|
438
|
+
onLineUnsupported,
|
|
439
|
+
onConnectFailed,
|
|
440
|
+
onDisconnect,
|
|
426
441
|
setTimeout: scheduleTimeout = globalThis.setTimeout,
|
|
427
442
|
clearTimeout: cancelTimeout = globalThis.clearTimeout,
|
|
428
443
|
random = Math.random
|
|
@@ -433,6 +448,8 @@ function startPresenceSocket(params) {
|
|
|
433
448
|
let reconnectTimer = null;
|
|
434
449
|
let agents = /* @__PURE__ */ new Map();
|
|
435
450
|
let lastReportedOnCall = null;
|
|
451
|
+
let sentOnCall = null;
|
|
452
|
+
let line = null;
|
|
436
453
|
let connection = null;
|
|
437
454
|
let byeOnArrival = false;
|
|
438
455
|
const stop = (options) => {
|
|
@@ -449,6 +466,12 @@ function startPresenceSocket(params) {
|
|
|
449
466
|
connection?.close();
|
|
450
467
|
connection = null;
|
|
451
468
|
};
|
|
469
|
+
const send = (onCall) => {
|
|
470
|
+
if (!connection) return;
|
|
471
|
+
connection.reportCallState(onCall);
|
|
472
|
+
sentOnCall = onCall;
|
|
473
|
+
};
|
|
474
|
+
const mayReport = (onCall) => line !== "elsewhere" || !onCall && sentOnCall === true;
|
|
452
475
|
const scheduleReconnect = () => {
|
|
453
476
|
if (stopped) return;
|
|
454
477
|
attempt += 1;
|
|
@@ -477,10 +500,23 @@ function startPresenceSocket(params) {
|
|
|
477
500
|
if (connection === null) return;
|
|
478
501
|
connection = null;
|
|
479
502
|
scheduleReconnect();
|
|
503
|
+
onDisconnect?.();
|
|
504
|
+
},
|
|
505
|
+
onLine: (update) => {
|
|
506
|
+
if (stopped) return;
|
|
507
|
+
const was = line;
|
|
508
|
+
line = update.state;
|
|
509
|
+
if (was === "elsewhere" && line === "here" && lastReportedOnCall !== null) send(lastReportedOnCall);
|
|
510
|
+
onLine?.(update);
|
|
511
|
+
},
|
|
512
|
+
onClaimResult: (result) => {
|
|
513
|
+
if (!stopped) onClaimResult?.(result);
|
|
480
514
|
}
|
|
481
515
|
});
|
|
482
516
|
} catch {
|
|
483
|
-
if (
|
|
517
|
+
if (stopped) return;
|
|
518
|
+
scheduleReconnect();
|
|
519
|
+
onConnectFailed?.();
|
|
484
520
|
return;
|
|
485
521
|
}
|
|
486
522
|
if (stopped) {
|
|
@@ -490,22 +526,63 @@ function startPresenceSocket(params) {
|
|
|
490
526
|
}
|
|
491
527
|
connection = opened;
|
|
492
528
|
attempt = 0;
|
|
493
|
-
|
|
529
|
+
sentOnCall = null;
|
|
530
|
+
line = null;
|
|
531
|
+
if (clientId !== void 0 && opened.hello) opened.hello(clientId);
|
|
532
|
+
else onLineUnsupported?.();
|
|
533
|
+
if (lastReportedOnCall !== null) send(lastReportedOnCall);
|
|
494
534
|
};
|
|
495
535
|
void connect();
|
|
496
536
|
return {
|
|
497
537
|
reportCallState: (onCall) => {
|
|
498
538
|
if (onCall === lastReportedOnCall) return;
|
|
499
539
|
lastReportedOnCall = onCall;
|
|
500
|
-
|
|
540
|
+
if (mayReport(onCall)) send(onCall);
|
|
541
|
+
},
|
|
542
|
+
claimLine: () => {
|
|
543
|
+
if (!connection?.claimLine) return false;
|
|
544
|
+
connection.claimLine();
|
|
545
|
+
return true;
|
|
501
546
|
},
|
|
502
547
|
stop
|
|
503
548
|
};
|
|
504
549
|
}
|
|
505
550
|
|
|
551
|
+
// src/core/line.ts
|
|
552
|
+
var CLIENT_ID_KEY = "coventia-softphone:client-id";
|
|
553
|
+
var CLIENT_ID_PATTERN = /^[A-Za-z0-9_.:-]{1,64}$/;
|
|
554
|
+
function lineClientId() {
|
|
555
|
+
const stored = readStoredId();
|
|
556
|
+
if (stored) return stored;
|
|
557
|
+
const id = freshClientId();
|
|
558
|
+
try {
|
|
559
|
+
if (isBrowser()) globalThis.sessionStorage?.setItem(CLIENT_ID_KEY, id);
|
|
560
|
+
} catch {
|
|
561
|
+
}
|
|
562
|
+
return id;
|
|
563
|
+
}
|
|
564
|
+
function readStoredId() {
|
|
565
|
+
if (!isBrowser()) return null;
|
|
566
|
+
try {
|
|
567
|
+
const value = globalThis.sessionStorage?.getItem(CLIENT_ID_KEY);
|
|
568
|
+
return value && CLIENT_ID_PATTERN.test(value) ? value : null;
|
|
569
|
+
} catch {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function freshClientId() {
|
|
574
|
+
const uuid = globalThis.crypto?.randomUUID?.();
|
|
575
|
+
if (uuid) return uuid;
|
|
576
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;
|
|
577
|
+
}
|
|
578
|
+
|
|
506
579
|
// src/core/softphone-impl.ts
|
|
507
580
|
var liveInstance = false;
|
|
508
581
|
var PRESENCE_TEARDOWN_GRACE_MS = 1e4;
|
|
582
|
+
var LINE_ANSWER_TIMEOUT_MS = 4e3;
|
|
583
|
+
var CALL_DECISION_TIMEOUT_MS = LINE_ANSWER_TIMEOUT_MS + 2e3;
|
|
584
|
+
var LINE_CLAIM_TIMEOUT_MS = 1e4;
|
|
585
|
+
var LINE_LAPSE_MS = 1e4;
|
|
509
586
|
var BrowserAudioDevices = class {
|
|
510
587
|
inputs = [];
|
|
511
588
|
outputs = [];
|
|
@@ -613,6 +690,8 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
613
690
|
#activeCall = null;
|
|
614
691
|
#destroyed = false;
|
|
615
692
|
#startedHere = false;
|
|
693
|
+
/** Bumped by every `stop()`, so a `start()` still waiting to lead can tell it was stopped. */
|
|
694
|
+
#run = 0;
|
|
616
695
|
#isLeader = false;
|
|
617
696
|
#engine;
|
|
618
697
|
#tabLock;
|
|
@@ -637,6 +716,52 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
637
716
|
/** A pending state-driven teardown of the watch — see `PRESENCE_TEARDOWN_GRACE_MS`. */
|
|
638
717
|
#presenceTeardownTimer = null;
|
|
639
718
|
#presenceAgents = [];
|
|
719
|
+
// ── The line owner (`docs/specs/line-owner.md` §5) ──────────────────────────────────────────────
|
|
720
|
+
// Heard over the presence watch: the server names ONE softphone per extension that listens, and this one
|
|
721
|
+
// runs its engine only while that is it (or nobody can say). `#engineOn` and `#standby` are the two halves
|
|
722
|
+
// of that decision, kept apart because neither is true while `start()` is still waiting to make it.
|
|
723
|
+
#line = "unknown";
|
|
724
|
+
#lineWhere = null;
|
|
725
|
+
/** How the last attempt to bring the line here went (`lineClaim`), whoever made it. */
|
|
726
|
+
#lineClaim = "idle";
|
|
727
|
+
/** This tab's id in `hello` (`lineClientId`), read once — one id for every reconnect of this softphone. */
|
|
728
|
+
#clientId = null;
|
|
729
|
+
/** What the line wants: the engine running. Set the moment the line decides; the engine follows (`#queueEngine`). */
|
|
730
|
+
#engineOn = false;
|
|
731
|
+
/**
|
|
732
|
+
* What the engine was last TOLD by the queue — started or stopped. Apart from `#engineOn` because the line
|
|
733
|
+
* can flip twice while a start is still running: the stop and start queued meanwhile then find nothing
|
|
734
|
+
* to do, instead of stopping the engine (and the softphone with it) for a moment.
|
|
735
|
+
*/
|
|
736
|
+
#engineRunning = false;
|
|
737
|
+
/** Waiting in `standby`: the engine is stopped ON PURPOSE, so its own `stopped` is not the softphone's. */
|
|
738
|
+
#standby = false;
|
|
739
|
+
/**
|
|
740
|
+
* Every engine start and stop the line asks for, one after another: a promotion can follow a demotion
|
|
741
|
+
* within a moment, and an engine started while its own stop is still running can end up neither.
|
|
742
|
+
*/
|
|
743
|
+
#engineSteps = Promise.resolve();
|
|
744
|
+
/** Dials in flight (`call()` awaiting `placeCall`): a call about to exist holds the line like one that does. */
|
|
745
|
+
#dialing = 0;
|
|
746
|
+
/**
|
|
747
|
+
* Open from `start()`'s first line until it has decided whether the engine runs — or given up (stopped,
|
|
748
|
+
* destroyed, failed). `call()` waits on it, so it never dials ahead of that decision: not on a phone
|
|
749
|
+
* stopped meanwhile, and not without the line.
|
|
750
|
+
*/
|
|
751
|
+
#startDecision = null;
|
|
752
|
+
/**
|
|
753
|
+
* Who decides whether the engine runs. `off`: nobody — not started, a follower, or `stop()` has begun
|
|
754
|
+
* (whatever its teardown sets off, like a call ending, must not start anything). `deciding`: `start()`
|
|
755
|
+
* has leadership and owes the FIRST decision itself, since it must await the engine start — a line
|
|
756
|
+
* message meanwhile only records what it says. `on`: the line decides (`#applyLine`).
|
|
757
|
+
*/
|
|
758
|
+
#lineMode = "off";
|
|
759
|
+
/** Ends `start()`'s wait for the server's first word on the line early (`#waitForFirstLine`). */
|
|
760
|
+
#releaseFirstLine = null;
|
|
761
|
+
/** The `takeLine()` in flight — one at a time; a second caller shares it. */
|
|
762
|
+
#claim = null;
|
|
763
|
+
/** Pending: what the server said about the line stops counting (`LINE_LAPSE_MS`). */
|
|
764
|
+
#lineLapseTimer = null;
|
|
640
765
|
/**
|
|
641
766
|
* `tabLock` defaults to the real Web Locks implementation — not part of `SoftphoneConfig` (an
|
|
642
767
|
* internal seam, not something an integrator tunes). Tests construct this class directly with a
|
|
@@ -654,7 +779,9 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
654
779
|
this.sources = config.coventia || config.apiBase ? coventiaBackend(config.coventia ?? {}, cachedTokenSource(config.tokenSource), config.apiBase) : null;
|
|
655
780
|
this.#resolveContact = config.resolveContact ?? this.sources?.resolveContact;
|
|
656
781
|
this.#engine.onIncoming((engineCall) => this.#onIncoming(engineCall));
|
|
657
|
-
this.#engine.onStateChange((state) =>
|
|
782
|
+
this.#engine.onStateChange((state) => {
|
|
783
|
+
if (!this.#standby) this.#setState(state);
|
|
784
|
+
});
|
|
658
785
|
this.#engine.onError?.((error) => this.#onError(error));
|
|
659
786
|
}
|
|
660
787
|
get state() {
|
|
@@ -672,6 +799,15 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
672
799
|
get presence() {
|
|
673
800
|
return this.#presenceAgents;
|
|
674
801
|
}
|
|
802
|
+
get line() {
|
|
803
|
+
return this.#line;
|
|
804
|
+
}
|
|
805
|
+
get lineWhere() {
|
|
806
|
+
return this.#lineWhere;
|
|
807
|
+
}
|
|
808
|
+
get lineClaim() {
|
|
809
|
+
return this.#lineClaim;
|
|
810
|
+
}
|
|
675
811
|
get canPlayAudio() {
|
|
676
812
|
return true;
|
|
677
813
|
}
|
|
@@ -687,15 +823,33 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
687
823
|
}
|
|
688
824
|
liveInstance = true;
|
|
689
825
|
this.#startedHere = true;
|
|
690
|
-
this.#
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
826
|
+
const run = this.#run;
|
|
827
|
+
const decision = this.#openStartDecision();
|
|
828
|
+
try {
|
|
829
|
+
this.#setState("starting");
|
|
830
|
+
await this.#tabLock.acquireLeadership();
|
|
831
|
+
if (this.#destroyed || run !== this.#run) return;
|
|
832
|
+
this.#lineMode = "deciding";
|
|
833
|
+
this.#setLeader(true);
|
|
834
|
+
if (this.#presenceWatch && this.#line === "unknown") await this.#waitForFirstLine();
|
|
835
|
+
if (this.#destroyed || run !== this.#run) return;
|
|
836
|
+
this.#lineMode = "on";
|
|
837
|
+
const engineStart = this.#applyLine();
|
|
838
|
+
decision.end();
|
|
839
|
+
await engineStart;
|
|
840
|
+
} finally {
|
|
841
|
+
decision.end();
|
|
842
|
+
}
|
|
695
843
|
}
|
|
696
844
|
async stop() {
|
|
697
845
|
if (!this.#startedHere) return;
|
|
846
|
+
this.#run += 1;
|
|
847
|
+
this.#lineMode = "off";
|
|
698
848
|
this.#sayGoodbye();
|
|
849
|
+
this.#startDecision?.end();
|
|
850
|
+
this.#standby = false;
|
|
851
|
+
this.#engineOn = false;
|
|
852
|
+
this.#engineRunning = false;
|
|
699
853
|
this.#tabLock.release();
|
|
700
854
|
if (this.#isLeader) {
|
|
701
855
|
await this.#engine.stop();
|
|
@@ -717,16 +871,50 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
717
871
|
this.#setState("destroyed");
|
|
718
872
|
this.clear();
|
|
719
873
|
}
|
|
874
|
+
async takeLine() {
|
|
875
|
+
this.#assertUsable();
|
|
876
|
+
if (this.#line === "here") return;
|
|
877
|
+
if (this.#claim) return this.#claim.promise;
|
|
878
|
+
const watch = this.#presenceWatch;
|
|
879
|
+
if (!watch) {
|
|
880
|
+
this.#setLineClaim("failed");
|
|
881
|
+
throw createError("line/unavailable");
|
|
882
|
+
}
|
|
883
|
+
const claim = this.#openClaim();
|
|
884
|
+
if (!watch.claimLine()) this.#settleClaim(createError("line/unavailable"));
|
|
885
|
+
return claim;
|
|
886
|
+
}
|
|
720
887
|
async call(destination, options) {
|
|
721
888
|
this.#assertUsable();
|
|
889
|
+
const run = this.#run;
|
|
890
|
+
if (this.#startDecision) {
|
|
891
|
+
let timer;
|
|
892
|
+
const decided = await Promise.race([
|
|
893
|
+
this.#startDecision.promise.then(() => true),
|
|
894
|
+
new Promise((resolve) => {
|
|
895
|
+
timer = setTimeout(() => resolve(false), CALL_DECISION_TIMEOUT_MS);
|
|
896
|
+
})
|
|
897
|
+
]);
|
|
898
|
+
clearTimeout(timer);
|
|
899
|
+
if (!decided) throw createError("engine/not-started");
|
|
900
|
+
}
|
|
901
|
+
if (run === this.#run && this.#state === "standby") await this.takeLine();
|
|
902
|
+
this.#assertUsable();
|
|
903
|
+
if (run !== this.#run) throw createError("engine/not-started");
|
|
722
904
|
if (this.#state !== "online" && this.#state !== "offline" && this.#state !== "starting") {
|
|
723
905
|
throw new Error(`Cannot place a call while softphone is '${this.#state}'.`);
|
|
724
906
|
}
|
|
725
|
-
if (this.#activeCall) {
|
|
907
|
+
if (this.#activeCall || this.#dialing > 0) {
|
|
726
908
|
throw new Error("A call is already in progress. Hang up before placing another.");
|
|
727
909
|
}
|
|
728
|
-
|
|
729
|
-
|
|
910
|
+
this.#dialing += 1;
|
|
911
|
+
try {
|
|
912
|
+
const engineCall = await this.#engine.placeCall(destination, options);
|
|
913
|
+
return this.#adopt(engineCall);
|
|
914
|
+
} finally {
|
|
915
|
+
this.#dialing -= 1;
|
|
916
|
+
this.#syncLine();
|
|
917
|
+
}
|
|
730
918
|
}
|
|
731
919
|
// ── internals ───────────────────────────────────────────────────────────────
|
|
732
920
|
#onIncoming(engineCall) {
|
|
@@ -755,6 +943,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
755
943
|
this.#activeCall = null;
|
|
756
944
|
this.emit("activeCallChanged" /* ActiveCallChanged */, { call: null });
|
|
757
945
|
this.#syncCallState();
|
|
946
|
+
this.#syncLine();
|
|
758
947
|
}
|
|
759
948
|
});
|
|
760
949
|
if (this.#resolveContact) {
|
|
@@ -784,7 +973,8 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
784
973
|
/**
|
|
785
974
|
* Reports the current on-a-call value to the backend. The watch de-duplicates (it only sends on change) and
|
|
786
975
|
* remembers the latest value to replay after a reconnect, so this can be called on every transition without
|
|
787
|
-
* ceremony
|
|
976
|
+
* ceremony — and holds it back while the line is elsewhere, since only the owner's reports count (line-owner
|
|
977
|
+
* R8). A no-op while there is no presence watch (stopped, not the leader, no backend configured).
|
|
788
978
|
*/
|
|
789
979
|
#syncCallState() {
|
|
790
980
|
this.#presenceWatch?.reportCallState(this.#isOnCall());
|
|
@@ -805,17 +995,22 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
805
995
|
* `presence-socket.ts`'s own `getIsLeader` is checked at start and before each reconnect attempt,
|
|
806
996
|
* not on a poll tick the way `startTransferWatch`'s is — see that module's own doc comment for why.
|
|
807
997
|
* This still passes `getIsLeader: () => this.#isLeader` for that narrower, defensive recheck; the
|
|
808
|
-
* BROAD
|
|
998
|
+
* BROAD started/leader gate — whether to be connected AT ALL — is owned entirely here, the same
|
|
809
999
|
* split `#onTransfer` already has with `startTransferWatch`.
|
|
810
1000
|
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
1001
|
+
* The watch runs for a leader that is `starting`, `online` or in `standby` — not only `online`, as it did
|
|
1002
|
+
* before the line owner (`docs/specs/line-owner.md` §5.4): while starting it is how this softphone learns
|
|
1003
|
+
* whether to listen at all, and in standby how it learns the line came back.
|
|
1004
|
+
*
|
|
1005
|
+
* A leader that is merely `offline` or `reconnecting` keeps its watch for `PRESENCE_TEARDOWN_GRACE_MS` — a
|
|
1006
|
+
* blip shorter than that never touches the presence socket at all; a longer outage closes it, which lets
|
|
1007
|
+
* the server hand the line to a softphone that can still take calls. Anything else (stopped, destroyed, or
|
|
1008
|
+
* no longer the leader) tears it down at once: those are not blips, and a follower must never hold a
|
|
1009
|
+
* connection.
|
|
815
1010
|
*/
|
|
816
1011
|
#syncPresenceWatch() {
|
|
817
1012
|
const eligible = Boolean(this.sources) && this.#isLeader;
|
|
818
|
-
const shouldRun = eligible && this.#state
|
|
1013
|
+
const shouldRun = eligible && this.#presenceRunsIn(this.#state);
|
|
819
1014
|
const blip = eligible && (this.#state === "offline" || this.#state === "reconnecting");
|
|
820
1015
|
if (!blip) this.#cancelPresenceTeardown();
|
|
821
1016
|
if (blip && this.#presenceWatch !== null) {
|
|
@@ -824,13 +1019,22 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
824
1019
|
}
|
|
825
1020
|
if (shouldRun === (this.#presenceWatch !== null)) return;
|
|
826
1021
|
if (shouldRun) {
|
|
1022
|
+
this.#clientId ??= lineClientId();
|
|
827
1023
|
this.#presenceWatch = startPresenceSocket({
|
|
828
1024
|
source: this.sources.presence,
|
|
829
1025
|
getIsLeader: () => this.#isLeader,
|
|
830
1026
|
onAgentsChanged: (agents) => {
|
|
831
1027
|
this.#presenceAgents = agents;
|
|
832
1028
|
this.emit("presenceChanged" /* PresenceChanged */, { agents });
|
|
833
|
-
}
|
|
1029
|
+
},
|
|
1030
|
+
clientId: this.#clientId,
|
|
1031
|
+
onLine: (update) => this.#onLine(update),
|
|
1032
|
+
onClaimResult: (result) => this.#onClaimResult(result),
|
|
1033
|
+
// A source that cannot take part will never answer, and one that cannot connect cannot answer yet:
|
|
1034
|
+
// `start()` need not wait out its 4 s for either (R7). A later answer still applies.
|
|
1035
|
+
onLineUnsupported: () => this.#releaseFirstLine?.(),
|
|
1036
|
+
onConnectFailed: () => this.#releaseFirstLine?.(),
|
|
1037
|
+
onDisconnect: () => this.#onPresenceDisconnect()
|
|
834
1038
|
});
|
|
835
1039
|
this.#syncCallState();
|
|
836
1040
|
} else {
|
|
@@ -845,19 +1049,24 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
845
1049
|
if (this.#presenceTeardownTimer !== null) return;
|
|
846
1050
|
this.#presenceTeardownTimer = setTimeout(() => {
|
|
847
1051
|
this.#presenceTeardownTimer = null;
|
|
848
|
-
if (this.#
|
|
1052
|
+
if (this.#isLeader && this.#presenceRunsIn(this.#state)) return;
|
|
849
1053
|
this.#stopPresence();
|
|
850
1054
|
}, PRESENCE_TEARDOWN_GRACE_MS);
|
|
851
1055
|
}
|
|
1056
|
+
/** The states in which a leader keeps its presence watch with no clock running (`#syncPresenceWatch`). */
|
|
1057
|
+
#presenceRunsIn(state) {
|
|
1058
|
+
return state === "online" || state === "starting" || state === "standby";
|
|
1059
|
+
}
|
|
852
1060
|
#cancelPresenceTeardown() {
|
|
853
1061
|
if (this.#presenceTeardownTimer === null) return;
|
|
854
1062
|
clearTimeout(this.#presenceTeardownTimer);
|
|
855
1063
|
this.#presenceTeardownTimer = null;
|
|
856
1064
|
}
|
|
857
1065
|
/**
|
|
858
|
-
* Stops the presence watch and forgets its roster. A STATE-DRIVEN teardown (
|
|
859
|
-
*
|
|
860
|
-
* Any pending grace-period teardown is cancelled — there is nothing
|
|
1066
|
+
* Stops the presence watch and forgets its roster, and the line. A STATE-DRIVEN teardown (an outage
|
|
1067
|
+
* outlasted the grace, the engine stopped, or leadership was lost) passes nothing and says no goodbye;
|
|
1068
|
+
* only a deliberate stop passes `bye`. Any pending grace-period teardown is cancelled — there is nothing
|
|
1069
|
+
* left for it to do.
|
|
861
1070
|
*/
|
|
862
1071
|
#stopPresence(options) {
|
|
863
1072
|
this.#cancelPresenceTeardown();
|
|
@@ -866,6 +1075,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
866
1075
|
this.#presenceWatch = null;
|
|
867
1076
|
this.#presenceAgents = [];
|
|
868
1077
|
this.emit("presenceChanged" /* PresenceChanged */, { agents: [] });
|
|
1078
|
+
this.#forgetLine();
|
|
869
1079
|
}
|
|
870
1080
|
/**
|
|
871
1081
|
* A DELIBERATE stop (`stop()` / `destroy()`): say any call is over, then say goodbye, so the backend drops
|
|
@@ -875,6 +1085,195 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
875
1085
|
this.#presenceWatch?.reportCallState(false);
|
|
876
1086
|
this.#stopPresence({ bye: true });
|
|
877
1087
|
}
|
|
1088
|
+
// ── the line owner (`docs/specs/line-owner.md` §5) ──────────────────────────────────────────────
|
|
1089
|
+
/**
|
|
1090
|
+
* THE decision: from where the line is and whether a call is in progress, whether the engine runs, and
|
|
1091
|
+
* the state that says so. Synchronous, so the state moves the instant the line does — the waiting screen
|
|
1092
|
+
* goes the moment the line comes here, and a `call()` that took the line dials from `starting` without
|
|
1093
|
+
* waiting out the engine start. Only the engine start or stop is queued (`#queueEngine`). Returns that
|
|
1094
|
+
* queued step, or `null` when nothing had to change.
|
|
1095
|
+
*/
|
|
1096
|
+
#applyLine() {
|
|
1097
|
+
if (this.#lineMode !== "on") return null;
|
|
1098
|
+
const run = this.#run;
|
|
1099
|
+
if (this.#line === "elsewhere") {
|
|
1100
|
+
if (this.#standby || this.#activeCall || this.#dialing > 0) return null;
|
|
1101
|
+
this.#standby = true;
|
|
1102
|
+
this.#setState("standby");
|
|
1103
|
+
if (!this.#engineOn) return null;
|
|
1104
|
+
this.#engineOn = false;
|
|
1105
|
+
return this.#queueEngine(run);
|
|
1106
|
+
}
|
|
1107
|
+
if (this.#engineOn) return null;
|
|
1108
|
+
this.#engineOn = true;
|
|
1109
|
+
if (this.#standby) {
|
|
1110
|
+
this.#standby = false;
|
|
1111
|
+
this.#setState("starting");
|
|
1112
|
+
}
|
|
1113
|
+
return this.#queueEngine(run);
|
|
1114
|
+
}
|
|
1115
|
+
/** `#applyLine` from an event — nobody to hand a failure to, and `#engineStartFailed` has reported it. */
|
|
1116
|
+
#syncLine() {
|
|
1117
|
+
void this.#applyLine()?.catch(() => {
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Brings the engine to what the line wants (`#engineOn`) once every step already queued has run — and does
|
|
1122
|
+
* nothing when it is already there: the line can flip twice while a start is still running, and the stop
|
|
1123
|
+
* and start queued meanwhile must not take the engine (and the softphone's state) down and up for nothing.
|
|
1124
|
+
* Nothing at all once `stop()` has run since `run` was read: a stopped softphone's engine is `stop()`'s
|
|
1125
|
+
* alone. Rejects with a failed start's cause; the queue itself never rejects, so one failure blocks nothing.
|
|
1126
|
+
*/
|
|
1127
|
+
#queueEngine(run) {
|
|
1128
|
+
const step = this.#engineSteps.then(async () => {
|
|
1129
|
+
if (run !== this.#run || this.#engineOn === this.#engineRunning) return;
|
|
1130
|
+
this.#engineRunning = this.#engineOn;
|
|
1131
|
+
if (this.#engineRunning) await this.#startEngine(run);
|
|
1132
|
+
else await this.#engine.stop();
|
|
1133
|
+
});
|
|
1134
|
+
this.#engineSteps = step.catch(() => {
|
|
1135
|
+
});
|
|
1136
|
+
return step;
|
|
1137
|
+
}
|
|
1138
|
+
async #startEngine(run) {
|
|
1139
|
+
const reported = this.#lastError;
|
|
1140
|
+
try {
|
|
1141
|
+
await this.#engine.start();
|
|
1142
|
+
} catch (cause) {
|
|
1143
|
+
if (run === this.#run) this.#engineStartFailed(cause, reported);
|
|
1144
|
+
throw cause;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* An engine start the line asked for failed. Kept as it was, this softphone would stay the owner on the
|
|
1149
|
+
* server — `starting`, its presence socket renewing the line — with no push socket anywhere: nobody would
|
|
1150
|
+
* listen. So it gives the line up the ordinary way, by closing its presence socket without a goodbye (the
|
|
1151
|
+
* phone was not switched off): the server hands the line on after its grace (R4). The failure is reported
|
|
1152
|
+
* like any engine failure — `SoftphoneEvent.Error` and `lastError`, which is the widget's error gate and
|
|
1153
|
+
* its "Reintentar" (`stop()` then `start()`, which asks for the line again). Nothing retries on its own.
|
|
1154
|
+
*/
|
|
1155
|
+
#engineStartFailed(cause, reportedBefore) {
|
|
1156
|
+
this.#engineOn = false;
|
|
1157
|
+
this.#engineRunning = false;
|
|
1158
|
+
this.#lineMode = "off";
|
|
1159
|
+
if (this.#lastError === reportedBefore) {
|
|
1160
|
+
this.#onError(isSoftphoneError(cause) && isFatal(cause) ? cause : createError("net/transport-failed", cause));
|
|
1161
|
+
}
|
|
1162
|
+
this.#stopPresence();
|
|
1163
|
+
this.#setState("offline");
|
|
1164
|
+
}
|
|
1165
|
+
/** `start()`'s decision (`#startDecision`): ended once, by whichever comes first — the decision or its end. */
|
|
1166
|
+
#openStartDecision() {
|
|
1167
|
+
let resolve;
|
|
1168
|
+
const promise = new Promise((r) => {
|
|
1169
|
+
resolve = r;
|
|
1170
|
+
});
|
|
1171
|
+
const decision = {
|
|
1172
|
+
promise,
|
|
1173
|
+
end: () => {
|
|
1174
|
+
resolve();
|
|
1175
|
+
if (this.#startDecision === decision) this.#startDecision = null;
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
this.#startDecision = decision;
|
|
1179
|
+
return decision;
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* `start()`'s wait for the server's first word on the line — released by that word, by a source that
|
|
1183
|
+
* will never say it, by the watch going away (`stop()` among them), or after `LINE_ANSWER_TIMEOUT_MS`.
|
|
1184
|
+
*/
|
|
1185
|
+
#waitForFirstLine() {
|
|
1186
|
+
return new Promise((resolve) => {
|
|
1187
|
+
const release = () => {
|
|
1188
|
+
clearTimeout(timer);
|
|
1189
|
+
if (this.#releaseFirstLine === release) this.#releaseFirstLine = null;
|
|
1190
|
+
resolve();
|
|
1191
|
+
};
|
|
1192
|
+
const timer = setTimeout(release, LINE_ANSWER_TIMEOUT_MS);
|
|
1193
|
+
this.#releaseFirstLine = release;
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
#onLine(update) {
|
|
1197
|
+
this.#cancelLineLapse();
|
|
1198
|
+
this.#setLine(update.state, update.state === "here" ? null : update.where);
|
|
1199
|
+
if (this.#releaseFirstLine) this.#releaseFirstLine();
|
|
1200
|
+
else this.#syncLine();
|
|
1201
|
+
if (update.state === "here") this.#settleClaim();
|
|
1202
|
+
}
|
|
1203
|
+
#onClaimResult(result) {
|
|
1204
|
+
if (result.ok) {
|
|
1205
|
+
if (this.#line === "here") this.#settleClaim();
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
this.#settleClaim(createError(result.reason === "on_call" ? "line/claim-refused" : "line/unavailable"));
|
|
1209
|
+
}
|
|
1210
|
+
/**
|
|
1211
|
+
* The presence socket dropped; the watch is reconnecting. A claim sent on it will never be answered, so
|
|
1212
|
+
* it fails now rather than on its timeout. What the server said about the line holds for `LINE_LAPSE_MS`
|
|
1213
|
+
* — a reconnect is normally answered well inside that — and then stops counting: `unknown`, which a
|
|
1214
|
+
* waiting softphone answers by listening again.
|
|
1215
|
+
*/
|
|
1216
|
+
#onPresenceDisconnect() {
|
|
1217
|
+
this.#settleClaim(createError("line/unavailable"));
|
|
1218
|
+
if (this.#line === "unknown" || this.#lineLapseTimer !== null) return;
|
|
1219
|
+
this.#lineLapseTimer = setTimeout(() => {
|
|
1220
|
+
this.#lineLapseTimer = null;
|
|
1221
|
+
this.#setLine("unknown", null);
|
|
1222
|
+
this.#syncLine();
|
|
1223
|
+
}, LINE_LAPSE_MS);
|
|
1224
|
+
}
|
|
1225
|
+
#cancelLineLapse() {
|
|
1226
|
+
if (this.#lineLapseTimer === null) return;
|
|
1227
|
+
clearTimeout(this.#lineLapseTimer);
|
|
1228
|
+
this.#lineLapseTimer = null;
|
|
1229
|
+
}
|
|
1230
|
+
/**
|
|
1231
|
+
* The presence watch is gone, so nothing the server said about the line holds any more: it reads
|
|
1232
|
+
* `unknown`, and every wait on it ends. Not followed by `#applyLine` — this runs inside a state change
|
|
1233
|
+
* (`#syncPresenceWatch`) or a stop, and a softphone whose watch is gone is not in `standby` anyway: that
|
|
1234
|
+
* state keeps the watch.
|
|
1235
|
+
*/
|
|
1236
|
+
#forgetLine() {
|
|
1237
|
+
this.#cancelLineLapse();
|
|
1238
|
+
this.#releaseFirstLine?.();
|
|
1239
|
+
this.#settleClaim(createError("line/unavailable"));
|
|
1240
|
+
this.#setLine("unknown", null);
|
|
1241
|
+
this.#setLineClaim("idle");
|
|
1242
|
+
}
|
|
1243
|
+
#setLine(line, where) {
|
|
1244
|
+
if (line === "here") this.#setLineClaim("idle");
|
|
1245
|
+
if (line === this.#line && where === this.#lineWhere) return;
|
|
1246
|
+
this.#line = line;
|
|
1247
|
+
this.#lineWhere = where;
|
|
1248
|
+
this.emit("lineChanged" /* LineChanged */, { line, where });
|
|
1249
|
+
}
|
|
1250
|
+
#setLineClaim(claim) {
|
|
1251
|
+
if (claim === this.#lineClaim) return;
|
|
1252
|
+
this.#lineClaim = claim;
|
|
1253
|
+
this.emit("lineClaimChanged" /* LineClaimChanged */, { claim });
|
|
1254
|
+
}
|
|
1255
|
+
#openClaim() {
|
|
1256
|
+
let settle;
|
|
1257
|
+
const promise = new Promise((resolve, reject) => {
|
|
1258
|
+
const timer = setTimeout(() => this.#settleClaim(createError("line/unavailable")), LINE_CLAIM_TIMEOUT_MS);
|
|
1259
|
+
settle = (error) => {
|
|
1260
|
+
clearTimeout(timer);
|
|
1261
|
+
if (error) reject(error);
|
|
1262
|
+
else resolve();
|
|
1263
|
+
};
|
|
1264
|
+
});
|
|
1265
|
+
this.#claim = { promise, settle };
|
|
1266
|
+
this.#setLineClaim("claiming");
|
|
1267
|
+
return promise;
|
|
1268
|
+
}
|
|
1269
|
+
/** Ends the claim in flight, if any — resolved without `error`, rejected with it. */
|
|
1270
|
+
#settleClaim(error) {
|
|
1271
|
+
const claim = this.#claim;
|
|
1272
|
+
if (!claim) return;
|
|
1273
|
+
this.#claim = null;
|
|
1274
|
+
this.#setLineClaim(!error ? "idle" : error.code === "line/claim-refused" ? "refused" : "failed");
|
|
1275
|
+
claim.settle(error);
|
|
1276
|
+
}
|
|
878
1277
|
/**
|
|
879
1278
|
* Phase A (transfer outcome relay, `docs/specs/transfer-outcome-relay.md`) — `CallImpl.transfer()`'s
|
|
880
1279
|
* internal seam calls this. No-ops entirely when no Coventia backend is configured (`sources` is
|
|
@@ -945,11 +1344,12 @@ var LazyRelay = class {
|
|
|
945
1344
|
function defaultZadarmaEngine(config) {
|
|
946
1345
|
let real = null;
|
|
947
1346
|
let pending = null;
|
|
1347
|
+
let run = 0;
|
|
948
1348
|
const incoming = new LazyRelay();
|
|
949
1349
|
const stateChange = new LazyRelay();
|
|
950
1350
|
const error = new LazyRelay();
|
|
951
1351
|
function load() {
|
|
952
|
-
pending ??= import('./zadarma-engine-
|
|
1352
|
+
pending ??= import('./zadarma-engine-TIUE7PZ7.js').then(({ createZadarmaEngine }) => {
|
|
953
1353
|
const engine = createZadarmaEngine(config);
|
|
954
1354
|
incoming.attach({ on: (l) => engine.onIncoming(l) });
|
|
955
1355
|
stateChange.attach({ on: (l) => engine.onStateChange(l) });
|
|
@@ -968,13 +1368,20 @@ function defaultZadarmaEngine(config) {
|
|
|
968
1368
|
return real?.state ?? "stopped";
|
|
969
1369
|
},
|
|
970
1370
|
async start() {
|
|
971
|
-
|
|
1371
|
+
const current = run;
|
|
1372
|
+
const engine = await load();
|
|
1373
|
+
if (current !== run) return;
|
|
1374
|
+
await engine.start();
|
|
972
1375
|
},
|
|
973
1376
|
async stop() {
|
|
1377
|
+
run += 1;
|
|
974
1378
|
if (real) await real.stop();
|
|
975
1379
|
},
|
|
976
1380
|
async placeCall(destination, options) {
|
|
977
|
-
|
|
1381
|
+
const current = run;
|
|
1382
|
+
const engine = await load();
|
|
1383
|
+
if (current !== run) throw createError("engine/not-started");
|
|
1384
|
+
return engine.placeCall(destination, options);
|
|
978
1385
|
},
|
|
979
1386
|
onIncoming: (listener) => incoming.subscribe(listener),
|
|
980
1387
|
onStateChange: (listener) => stateChange.subscribe(listener),
|
|
@@ -984,7 +1391,16 @@ function defaultZadarmaEngine(config) {
|
|
|
984
1391
|
|
|
985
1392
|
// src/core/create.ts
|
|
986
1393
|
function createSoftphone(config) {
|
|
987
|
-
|
|
1394
|
+
const resolved = withNormalizedBases(config);
|
|
1395
|
+
return new SoftphoneImpl(resolveEngine(resolved), resolved);
|
|
1396
|
+
}
|
|
1397
|
+
function withNormalizedBases(config) {
|
|
1398
|
+
const { apiBase, coventia } = config;
|
|
1399
|
+
return {
|
|
1400
|
+
...config,
|
|
1401
|
+
...apiBase ? { apiBase: normalizeApiBase(apiBase) } : {},
|
|
1402
|
+
...coventia?.baseUrl ? { coventia: { ...coventia, baseUrl: normalizeApiBase(coventia.baseUrl) } } : {}
|
|
1403
|
+
};
|
|
988
1404
|
}
|
|
989
1405
|
function resolveEngine(config) {
|
|
990
1406
|
if (config.provider !== void 0) {
|
|
@@ -1009,7 +1425,7 @@ function isCallEngine(value) {
|
|
|
1009
1425
|
}
|
|
1010
1426
|
|
|
1011
1427
|
// src/index.ts
|
|
1012
|
-
var VERSION = "0.
|
|
1428
|
+
var VERSION = "0.5.0";
|
|
1013
1429
|
|
|
1014
1430
|
export { CALL_TRANSITIONS, INITIAL_STATES, VERSION, canTransition, createSoftphone, isTerminal };
|
|
1015
1431
|
//# sourceMappingURL=index.js.map
|