@coventia/softphone 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{call-DUVnDxsz.d.cts → call-VKq1I8yP.d.cts} +6 -0
- package/dist/{call-DUVnDxsz.d.ts → call-VKq1I8yP.d.ts} +6 -0
- package/dist/{chunk-YWI6EQYS.js → chunk-EYEDPD7Y.js} +108 -15
- package/dist/chunk-EYEDPD7Y.js.map +1 -0
- package/dist/element/index.d.ts +8 -2
- package/dist/element/index.js +334 -249
- package/dist/element/index.js.map +1 -1
- package/dist/{engine-CDjkrXHY.d.cts → engine-BlQBo2Ys.d.cts} +1 -1
- package/dist/{engine-EPCKf-Bl.d.ts → engine-C0BOkIcW.d.ts} +1 -1
- package/dist/index.cjs +199 -23
- 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 +90 -11
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.cts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/{softphone-BtdgZWkm.d.ts → softphone-Cyq3hiIM.d.ts} +1 -1
- package/dist/{softphone-DHG9MTNg.d.cts → softphone-DlGV12Xp.d.cts} +1 -1
- package/dist/softphone.css +82 -34
- package/dist/{sources-5L2R4HTV.js → sources-IRKNQPWW.js} +6 -2
- package/dist/sources-IRKNQPWW.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 +332 -247
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.cts +17 -6
- package/dist/ui/index.d.ts +17 -6
- package/dist/ui/index.js +332 -247
- 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-YWI6EQYS.js.map +0 -1
- package/dist/sources-5L2R4HTV.js.map +0 -1
- package/dist/zadarma-engine-4VOPIBXG.js +0 -5
package/dist/zadarma/index.cjs
CHANGED
|
@@ -789,6 +789,8 @@ async function loadJsSipFactory() {
|
|
|
789
789
|
// src/transports/providers/zadarma.ts
|
|
790
790
|
var PING = { localMs: 3e4, globalMs: 6e4 };
|
|
791
791
|
var KEY_REFRESH_BUFFER_MS = 2 * 60 * 60 * 1e3;
|
|
792
|
+
var REBUILD_CONNECT_TIMEOUT_MS = 1e4;
|
|
793
|
+
var REBUILD_RETRY_MS = 3e4;
|
|
792
794
|
var ZADARMA_QUIRKS = {
|
|
793
795
|
// Zadarma's registrar REFUSES to register a WebRTC client with any other User-Agent — a clean 401
|
|
794
796
|
// after a valid digest challenge, indistinguishable from a wrong password (§3.1). The old CRM
|
|
@@ -816,6 +818,12 @@ var ZadarmaPushSignal = class {
|
|
|
816
818
|
#connected = false;
|
|
817
819
|
#pingTimers = [];
|
|
818
820
|
#refreshTimer;
|
|
821
|
+
/** The replacement socket of a rebuild in flight, not yet current. */
|
|
822
|
+
#candidate = null;
|
|
823
|
+
/** Abandons the rebuild in flight (closing its candidate). */
|
|
824
|
+
#abortRebuild = null;
|
|
825
|
+
#rebuilding = false;
|
|
826
|
+
#retryTimer;
|
|
819
827
|
#unwatchBackground;
|
|
820
828
|
#closed = false;
|
|
821
829
|
#listeners = /* @__PURE__ */ new Set();
|
|
@@ -831,20 +839,35 @@ var ZadarmaPushSignal = class {
|
|
|
831
839
|
if (this.#closed) return;
|
|
832
840
|
const socket = this.deps.createPushSocket(buildPushUrl(key, sip));
|
|
833
841
|
this.#socket = socket;
|
|
842
|
+
this.#attach(socket);
|
|
843
|
+
this.#armRefresh(keyExpiresAt);
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Wires one socket's events. `this.#socket` is read when an event FIRES, not when it is wired: a socket that
|
|
847
|
+
* has been replaced must stop driving connectivity (our own `close()` of it emits `disconnect`, which would
|
|
848
|
+
* otherwise flip the state to offline right after a perfectly healthy swap). `update` is the exception — it is
|
|
849
|
+
* forwarded from any socket not yet closed, because a push seen twice is harmless (the engine ignores a
|
|
850
|
+
* repeated signal mid-call and reuses a UA that already exists) while one seen by neither is a missed call.
|
|
851
|
+
*
|
|
852
|
+
* With `hooks`, the socket is a rebuild's candidate: its first `connect` or failure goes to the rebuild instead
|
|
853
|
+
* of changing the connection state (it is not current yet).
|
|
854
|
+
*/
|
|
855
|
+
#attach(socket, hooks) {
|
|
834
856
|
socket.on("connect", () => {
|
|
857
|
+
if (hooks && socket === this.#candidate) return hooks.onConnect();
|
|
858
|
+
if (socket !== this.#socket) return;
|
|
835
859
|
this.#setConnected(true);
|
|
836
860
|
this.#startPings(socket);
|
|
837
861
|
});
|
|
838
862
|
socket.on("update", () => this.#emit());
|
|
839
|
-
|
|
863
|
+
const lost = () => {
|
|
864
|
+
if (hooks && socket === this.#candidate) return hooks.onFailure();
|
|
865
|
+
if (socket !== this.#socket) return;
|
|
840
866
|
this.#stopPings();
|
|
841
867
|
this.#setConnected(false);
|
|
842
|
-
}
|
|
843
|
-
socket.on("
|
|
844
|
-
|
|
845
|
-
this.#setConnected(false);
|
|
846
|
-
});
|
|
847
|
-
this.#armRefresh(keyExpiresAt);
|
|
868
|
+
};
|
|
869
|
+
socket.on("disconnect", lost);
|
|
870
|
+
socket.on("connect_error", lost);
|
|
848
871
|
}
|
|
849
872
|
/**
|
|
850
873
|
* Presence pings — **the reason an idle tab keeps receiving calls**.
|
|
@@ -883,22 +906,92 @@ var ZadarmaPushSignal = class {
|
|
|
883
906
|
if (!Number.isFinite(ms) || ms <= 0) return;
|
|
884
907
|
this.#refreshTimer = setTimeout(() => void this.#reopen(), ms);
|
|
885
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* Replaces the push socket with a fresh one — MAKE-BEFORE-BREAK (presence-client-reporting.md §4.1, T8).
|
|
911
|
+
*
|
|
912
|
+
* Why it must happen at all: Zadarma's gateway decides the extension is alive from the raw Engine.IO ping, which
|
|
913
|
+
* a hidden tab's throttled timers silently stop sending, so while the tab is hidden the socket is rebuilt every
|
|
914
|
+
* 20 s (`push-socket-reliability.md` §5.3), and once before the key expires. Why it must not leave a gap: the
|
|
915
|
+
* old close-then-reopen reported `connected = false` for the ~0.5 s in between, which took the softphone
|
|
916
|
+
* `offline`, tore down the presence socket and left Zadarma with no socket to push an incoming call to.
|
|
917
|
+
*
|
|
918
|
+
* Now the OLD socket stays up and stays current until the NEW one has actually connected; only then do we swap,
|
|
919
|
+
* move the pings and close the old one. A healthy rebuild never changes `connected`. A failed one — the key fetch
|
|
920
|
+
* throws, the socket errors, or it has not connected within `REBUILD_CONNECT_TIMEOUT_MS` — keeps the old socket
|
|
921
|
+
* and retries, instead of leaving the tab disconnected. Zadarma does not evict the older of two sockets for one
|
|
922
|
+
* sip (multi-tab-leader-election.md §1), so the overlap is safe.
|
|
923
|
+
*/
|
|
886
924
|
async #reopen() {
|
|
887
|
-
if (this.#closed) return;
|
|
888
|
-
this.#
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
925
|
+
if (this.#closed || this.#rebuilding) return;
|
|
926
|
+
if (!this.#socket) {
|
|
927
|
+
try {
|
|
928
|
+
await this.#open();
|
|
929
|
+
} catch {
|
|
930
|
+
}
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
this.#rebuilding = true;
|
|
892
934
|
try {
|
|
893
|
-
await this
|
|
935
|
+
const { key, sip, keyExpiresAt } = await this.deps.fetchKey();
|
|
936
|
+
if (this.#closed) return;
|
|
937
|
+
const swapped = await new Promise((resolve) => {
|
|
938
|
+
const socket = this.deps.createPushSocket(buildPushUrl(key, sip));
|
|
939
|
+
this.#candidate = socket;
|
|
940
|
+
let settled = false;
|
|
941
|
+
const finish = (ok) => {
|
|
942
|
+
if (settled) return;
|
|
943
|
+
settled = true;
|
|
944
|
+
clearTimeout(timer);
|
|
945
|
+
this.#abortRebuild = null;
|
|
946
|
+
if (ok && socket.connected) {
|
|
947
|
+
this.#swapIn(socket, keyExpiresAt);
|
|
948
|
+
} else {
|
|
949
|
+
this.#candidate = null;
|
|
950
|
+
socket.close();
|
|
951
|
+
}
|
|
952
|
+
resolve(ok && socket.connected);
|
|
953
|
+
};
|
|
954
|
+
const timer = setTimeout(() => finish(false), REBUILD_CONNECT_TIMEOUT_MS);
|
|
955
|
+
this.#abortRebuild = () => finish(false);
|
|
956
|
+
this.#attach(socket, { onConnect: () => finish(true), onFailure: () => finish(false) });
|
|
957
|
+
});
|
|
958
|
+
if (!swapped) this.#armRetry();
|
|
894
959
|
} catch {
|
|
960
|
+
this.#armRetry();
|
|
961
|
+
} finally {
|
|
962
|
+
this.#rebuilding = false;
|
|
895
963
|
}
|
|
896
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* The moment of the swap — synchronous, called from the candidate's own `connect` event so nothing can run
|
|
967
|
+
* between "the new socket is up" and "it is the current one". Order matters: `#socket` moves first, so the old
|
|
968
|
+
* socket's `disconnect` (our own `close()` of it, just below) is ignored by `#attach`.
|
|
969
|
+
*/
|
|
970
|
+
#swapIn(socket, keyExpiresAt) {
|
|
971
|
+
const previous = this.#socket;
|
|
972
|
+
this.#socket = socket;
|
|
973
|
+
this.#candidate = null;
|
|
974
|
+
clearTimeout(this.#retryTimer);
|
|
975
|
+
this.#retryTimer = void 0;
|
|
976
|
+
this.#stopPings();
|
|
977
|
+
this.#startPings(socket);
|
|
978
|
+
this.#setConnected(true);
|
|
979
|
+
previous?.close();
|
|
980
|
+
this.#armRefresh(keyExpiresAt);
|
|
981
|
+
}
|
|
982
|
+
#armRetry() {
|
|
983
|
+
if (this.#closed) return;
|
|
984
|
+
clearTimeout(this.#retryTimer);
|
|
985
|
+
this.#retryTimer = setTimeout(() => void this.#reopen(), REBUILD_RETRY_MS);
|
|
986
|
+
}
|
|
897
987
|
disconnect() {
|
|
898
988
|
this.#closed = true;
|
|
899
989
|
this.#stopPings();
|
|
900
990
|
clearTimeout(this.#refreshTimer);
|
|
901
991
|
this.#refreshTimer = void 0;
|
|
992
|
+
clearTimeout(this.#retryTimer);
|
|
993
|
+
this.#retryTimer = void 0;
|
|
994
|
+
this.#abortRebuild?.();
|
|
902
995
|
this.#unwatchBackground?.();
|
|
903
996
|
this.#unwatchBackground = void 0;
|
|
904
997
|
this.#socket?.close();
|