@coventia/softphone 0.5.0 → 0.5.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-FqcvDg1t.d.cts → call-iGvr2ZnO.d.cts} +26 -2
- package/dist/{call-FqcvDg1t.d.ts → call-iGvr2ZnO.d.ts} +26 -2
- package/dist/chunk-3UTUWGVG.js +1891 -0
- package/dist/chunk-3UTUWGVG.js.map +1 -0
- package/dist/chunk-DZRSSZWB.js +39 -0
- package/dist/chunk-DZRSSZWB.js.map +1 -0
- package/dist/element/index.d.ts +25 -1
- package/dist/{engine-CzOCrfY9.d.cts → engine-BKoT_Pj7.d.cts} +10 -1
- package/dist/{engine-CsNRD7B7.d.ts → engine-CeGh5Yai.d.ts} +10 -1
- package/dist/index.cjs +1049 -130
- 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 +179 -13
- 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-By9sizlV.d.ts → softphone-D0ao5W4z.d.ts} +1 -1
- package/dist/{softphone-JZvJdzU3.d.cts → softphone-DRoS0jJR.d.cts} +1 -1
- package/dist/{sources-QL2LRZVH.js → sources-LGFJG7W2.js} +10 -38
- package/dist/sources-LGFJG7W2.js.map +1 -0
- package/dist/testing/index.d.cts +2 -2
- package/dist/testing/index.d.ts +2 -2
- package/dist/ui/index.d.cts +2 -2
- package/dist/ui/index.d.ts +2 -2
- package/dist/zadarma/index.cjs +865 -116
- package/dist/zadarma/index.cjs.map +1 -1
- package/dist/zadarma/index.d.cts +37 -10
- package/dist/zadarma/index.d.ts +37 -10
- package/dist/zadarma/index.js +2 -1
- package/dist/zadarma-engine-VYIXLBQG.js +7 -0
- package/dist/{zadarma-engine-TIUE7PZ7.js.map → zadarma-engine-VYIXLBQG.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-6RR6G35E.js +0 -1146
- package/dist/chunk-6RR6G35E.js.map +0 -1
- package/dist/sources-QL2LRZVH.js.map +0 -1
- package/dist/zadarma-engine-TIUE7PZ7.js +0 -6
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, O as OutboundNumberRefusal } 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 LineClaim, j as LineLocation, k as LineState, l as LogLevel, N as NonFatalErrorCode, m as NonFatalSoftphoneError, n as OutboundNumber, o as OutboundNumberKind, p as OutboundNumberSettings, q as OutboundNumberState, r as OutboundNumbersSource, R as RemoteParty, s as RingtoneOption, t as RingtoneSettings, u as RingtoneSource, v as SoftphoneError, w as SoftphoneErrorCode, x as SoftphoneEvent, y as SoftphoneEventMap, z as SoftphoneState, T as TokenEndpointOptions, A as TokenSource, B as TypedEmitter, V as VoiceToken, D as canTransition, G as isFatal, J as isTerminal } from './call-
|
|
1
|
+
import { S as Softphone } from './softphone-DRoS0jJR.cjs';
|
|
2
|
+
export { A as AudioDevices } from './softphone-DRoS0jJR.cjs';
|
|
3
|
+
import { S as SoftphoneConfig, O as OutboundNumberRefusal } from './call-iGvr2ZnO.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 LineClaim, j as LineLocation, k as LineState, l as LogLevel, N as NonFatalErrorCode, m as NonFatalSoftphoneError, n as OutboundNumber, o as OutboundNumberKind, p as OutboundNumberSettings, q as OutboundNumberState, r as OutboundNumbersSource, R as RemoteParty, s as RingtoneOption, t as RingtoneSettings, u as RingtoneSource, v as SoftphoneError, w as SoftphoneErrorCode, x as SoftphoneEvent, y as SoftphoneEventMap, z as SoftphoneState, T as TokenEndpointOptions, A as TokenSource, B as TypedEmitter, V as VoiceToken, D as canTransition, G as isFatal, J as isTerminal } from './call-iGvr2ZnO.cjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Construct a softphone.
|
|
@@ -67,6 +67,6 @@ declare function isWebRtcSupported(): boolean;
|
|
|
67
67
|
* - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.
|
|
68
68
|
*/
|
|
69
69
|
|
|
70
|
-
declare const VERSION = "0.5.
|
|
70
|
+
declare const VERSION = "0.5.1";
|
|
71
71
|
|
|
72
72
|
export { OutboundNumberRefusal, type OutboundNumberRefusedError, Softphone, SoftphoneConfig, VERSION, createSoftphone, isBrowser, isOutboundNumberRefused, 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, O as OutboundNumberRefusal } 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 LineClaim, j as LineLocation, k as LineState, l as LogLevel, N as NonFatalErrorCode, m as NonFatalSoftphoneError, n as OutboundNumber, o as OutboundNumberKind, p as OutboundNumberSettings, q as OutboundNumberState, r as OutboundNumbersSource, R as RemoteParty, s as RingtoneOption, t as RingtoneSettings, u as RingtoneSource, v as SoftphoneError, w as SoftphoneErrorCode, x as SoftphoneEvent, y as SoftphoneEventMap, z as SoftphoneState, T as TokenEndpointOptions, A as TokenSource, B as TypedEmitter, V as VoiceToken, D as canTransition, G as isFatal, J as isTerminal } from './call-
|
|
1
|
+
import { S as Softphone } from './softphone-D0ao5W4z.js';
|
|
2
|
+
export { A as AudioDevices } from './softphone-D0ao5W4z.js';
|
|
3
|
+
import { S as SoftphoneConfig, O as OutboundNumberRefusal } from './call-iGvr2ZnO.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 LineClaim, j as LineLocation, k as LineState, l as LogLevel, N as NonFatalErrorCode, m as NonFatalSoftphoneError, n as OutboundNumber, o as OutboundNumberKind, p as OutboundNumberSettings, q as OutboundNumberState, r as OutboundNumbersSource, R as RemoteParty, s as RingtoneOption, t as RingtoneSettings, u as RingtoneSource, v as SoftphoneError, w as SoftphoneErrorCode, x as SoftphoneEvent, y as SoftphoneEventMap, z as SoftphoneState, T as TokenEndpointOptions, A as TokenSource, B as TypedEmitter, V as VoiceToken, D as canTransition, G as isFatal, J as isTerminal } from './call-iGvr2ZnO.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Construct a softphone.
|
|
@@ -67,6 +67,6 @@ declare function isWebRtcSupported(): boolean;
|
|
|
67
67
|
* - `MockProvider` — lives at @coventia/softphone/testing, so it never lands in a production bundle.
|
|
68
68
|
*/
|
|
69
69
|
|
|
70
|
-
declare const VERSION = "0.5.
|
|
70
|
+
declare const VERSION = "0.5.1";
|
|
71
71
|
|
|
72
72
|
export { OutboundNumberRefusal, type OutboundNumberRefusedError, Softphone, SoftphoneConfig, VERSION, createSoftphone, isBrowser, isOutboundNumberRefused, isWebRtcSupported };
|
package/dist/index.js
CHANGED
|
@@ -286,7 +286,7 @@ function coventiaBackend(config, tokenSource, fallbackBaseUrl) {
|
|
|
286
286
|
}
|
|
287
287
|
let pending = null;
|
|
288
288
|
const load = () => {
|
|
289
|
-
pending ??= import('./sources-
|
|
289
|
+
pending ??= import('./sources-LGFJG7W2.js').then(
|
|
290
290
|
(m) => m.coventiaSources({ ...config, baseUrl, tokenSource })
|
|
291
291
|
);
|
|
292
292
|
return pending;
|
|
@@ -421,9 +421,55 @@ function startTransferWatch(params) {
|
|
|
421
421
|
return stop;
|
|
422
422
|
}
|
|
423
423
|
|
|
424
|
+
// src/core/start-retry.ts
|
|
425
|
+
var BACKOFF_BASE_MS = 1e3;
|
|
426
|
+
var BACKOFF_MAX_MS = 3e4;
|
|
427
|
+
function backoffDelayMs(attempt, random = Math.random) {
|
|
428
|
+
const capped = Math.min(BACKOFF_BASE_MS * 2 ** (Math.max(1, attempt) - 1), BACKOFF_MAX_MS);
|
|
429
|
+
return capped * random();
|
|
430
|
+
}
|
|
431
|
+
function isTransientStatus(status) {
|
|
432
|
+
return status === 0 || status === 408 || status === 425 || status === 429 || status >= 500;
|
|
433
|
+
}
|
|
434
|
+
function isNetworkFailure(error) {
|
|
435
|
+
const name = typeof error.name === "string" ? error.name : "";
|
|
436
|
+
return error instanceof TypeError || name === "AbortError" || name === "TimeoutError" || name === "NetworkError";
|
|
437
|
+
}
|
|
438
|
+
function transientStartFailure(error) {
|
|
439
|
+
let retryAfterMs = 0;
|
|
440
|
+
let viaTokenEndpoint = false;
|
|
441
|
+
let current = error;
|
|
442
|
+
for (let depth = 0; depth < 6 && current !== null && typeof current === "object"; depth += 1) {
|
|
443
|
+
const layer = current;
|
|
444
|
+
if (typeof layer.retryAfterMs === "number" && Number.isFinite(layer.retryAfterMs)) {
|
|
445
|
+
retryAfterMs = Math.max(retryAfterMs, layer.retryAfterMs);
|
|
446
|
+
}
|
|
447
|
+
if (typeof layer.status === "number") return isTransientStatus(layer.status) ? { retryAfterMs } : null;
|
|
448
|
+
if (isNetworkFailure(layer)) return { retryAfterMs };
|
|
449
|
+
if (isSoftphoneError(current)) {
|
|
450
|
+
if (layer.code === "auth/token-rejected") return null;
|
|
451
|
+
if (layer.code === "auth/token-fetch-failed") viaTokenEndpoint = true;
|
|
452
|
+
}
|
|
453
|
+
current = layer.cause;
|
|
454
|
+
}
|
|
455
|
+
return viaTokenEndpoint ? { retryAfterMs } : null;
|
|
456
|
+
}
|
|
457
|
+
function watchWake(wake) {
|
|
458
|
+
if (!isBrowser()) return () => {
|
|
459
|
+
};
|
|
460
|
+
const onOnline = () => wake();
|
|
461
|
+
const onVisibility = () => {
|
|
462
|
+
if (document.visibilityState === "visible") wake();
|
|
463
|
+
};
|
|
464
|
+
window.addEventListener("online", onOnline);
|
|
465
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
466
|
+
return () => {
|
|
467
|
+
window.removeEventListener("online", onOnline);
|
|
468
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
424
472
|
// src/core/presence-socket.ts
|
|
425
|
-
var RECONNECT_BASE_MS = 1e3;
|
|
426
|
-
var RECONNECT_MAX_MS = 3e4;
|
|
427
473
|
var NOOP_WATCH = { reportCallState: () => {
|
|
428
474
|
}, claimLine: () => false, stop: () => {
|
|
429
475
|
} };
|
|
@@ -435,6 +481,7 @@ function startPresenceSocket(params) {
|
|
|
435
481
|
clientId,
|
|
436
482
|
onLine,
|
|
437
483
|
onClaimResult,
|
|
484
|
+
onOffer,
|
|
438
485
|
onLineUnsupported,
|
|
439
486
|
onConnectFailed,
|
|
440
487
|
onDisconnect,
|
|
@@ -475,8 +522,7 @@ function startPresenceSocket(params) {
|
|
|
475
522
|
const scheduleReconnect = () => {
|
|
476
523
|
if (stopped) return;
|
|
477
524
|
attempt += 1;
|
|
478
|
-
const
|
|
479
|
-
const delay = capped * random();
|
|
525
|
+
const delay = backoffDelayMs(attempt, random);
|
|
480
526
|
reconnectTimer = scheduleTimeout(() => void connect(), delay);
|
|
481
527
|
};
|
|
482
528
|
const connect = async () => {
|
|
@@ -511,6 +557,9 @@ function startPresenceSocket(params) {
|
|
|
511
557
|
},
|
|
512
558
|
onClaimResult: (result) => {
|
|
513
559
|
if (!stopped) onClaimResult?.(result);
|
|
560
|
+
},
|
|
561
|
+
onOffer: (offer) => {
|
|
562
|
+
if (!stopped) onOffer?.(offer);
|
|
514
563
|
}
|
|
515
564
|
});
|
|
516
565
|
} catch {
|
|
@@ -762,6 +811,17 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
762
811
|
#claim = null;
|
|
763
812
|
/** Pending: what the server said about the line stops counting (`LINE_LAPSE_MS`). */
|
|
764
813
|
#lineLapseTimer = null;
|
|
814
|
+
// ── The start retry (incoming-call-reliability.md §4.6) ─────────────────────────────────────────────────
|
|
815
|
+
// An engine start that failed for a passing reason (a 5xx, the network, a timeout) is attempted again on its
|
|
816
|
+
// own, with backoff, instead of leaving the phone `offline` until someone presses "Reintentar".
|
|
817
|
+
/** Failed attempts in the current streak; back to 0 when the engine starts, or the softphone stops. */
|
|
818
|
+
#startRetries = 0;
|
|
819
|
+
/** The wait before the next attempt, or `null` when none is pending. */
|
|
820
|
+
#startRetryTimer = null;
|
|
821
|
+
/** No attempt before this (epoch ms): a `Retry-After` the failure carried, which a wake-up must honour too. */
|
|
822
|
+
#startRetryNotBefore = 0;
|
|
823
|
+
/** Unsubscribes the `online` / tab-visible wake-ups, armed only while an attempt is pending. */
|
|
824
|
+
#unwatchWake = null;
|
|
765
825
|
/**
|
|
766
826
|
* `tabLock` defaults to the real Web Locks implementation — not part of `SoftphoneConfig` (an
|
|
767
827
|
* internal seam, not something an integrator tunes). Tests construct this class directly with a
|
|
@@ -844,6 +904,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
844
904
|
async stop() {
|
|
845
905
|
if (!this.#startedHere) return;
|
|
846
906
|
this.#run += 1;
|
|
907
|
+
this.#cancelStartRetry();
|
|
847
908
|
this.#lineMode = "off";
|
|
848
909
|
this.#sayGoodbye();
|
|
849
910
|
this.#startDecision?.end();
|
|
@@ -887,6 +948,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
887
948
|
async call(destination, options) {
|
|
888
949
|
this.#assertUsable();
|
|
889
950
|
const run = this.#run;
|
|
951
|
+
if (this.#startRetryTimer !== null) this.#wakeStartRetry();
|
|
890
952
|
if (this.#startDecision) {
|
|
891
953
|
let timer;
|
|
892
954
|
const decided = await Promise.race([
|
|
@@ -1030,6 +1092,9 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
1030
1092
|
clientId: this.#clientId,
|
|
1031
1093
|
onLine: (update) => this.#onLine(update),
|
|
1032
1094
|
onClaimResult: (result) => this.#onClaimResult(result),
|
|
1095
|
+
// The calls offered to this line, straight to the engine: after a decline, they say when the declined
|
|
1096
|
+
// call's re-offers stop (incoming-call-reliability.md §4.4). Not public — nothing a host acts on.
|
|
1097
|
+
onOffer: (offer) => this.#engine.noteOffer?.(offer),
|
|
1033
1098
|
// A source that cannot take part will never answer, and one that cannot connect cannot answer yet:
|
|
1034
1099
|
// `start()` need not wait out its 4 s for either (R7). A later answer still applies.
|
|
1035
1100
|
onLineUnsupported: () => this.#releaseFirstLine?.(),
|
|
@@ -1076,6 +1141,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
1076
1141
|
this.#presenceAgents = [];
|
|
1077
1142
|
this.emit("presenceChanged" /* PresenceChanged */, { agents: [] });
|
|
1078
1143
|
this.#forgetLine();
|
|
1144
|
+
this.#engine.noteOffer?.(null);
|
|
1079
1145
|
}
|
|
1080
1146
|
/**
|
|
1081
1147
|
* A DELIBERATE stop (`stop()` / `destroy()`): say any call is over, then say goodbye, so the backend drops
|
|
@@ -1135,12 +1201,14 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
1135
1201
|
});
|
|
1136
1202
|
return step;
|
|
1137
1203
|
}
|
|
1204
|
+
/** Resolves when the engine started — or when its failure is being retried on its own (§4.6). */
|
|
1138
1205
|
async #startEngine(run) {
|
|
1139
1206
|
const reported = this.#lastError;
|
|
1140
1207
|
try {
|
|
1141
1208
|
await this.#engine.start();
|
|
1209
|
+
if (run === this.#run) this.#startRetries = 0;
|
|
1142
1210
|
} catch (cause) {
|
|
1143
|
-
if (run === this.#run
|
|
1211
|
+
if (run === this.#run && this.#engineStartFailed(cause, reported)) return;
|
|
1144
1212
|
throw cause;
|
|
1145
1213
|
}
|
|
1146
1214
|
}
|
|
@@ -1148,19 +1216,99 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
1148
1216
|
* An engine start the line asked for failed. Kept as it was, this softphone would stay the owner on the
|
|
1149
1217
|
* server — `starting`, its presence socket renewing the line — with no push socket anywhere: nobody would
|
|
1150
1218
|
* 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).
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1219
|
+
* phone was not switched off): the server hands the line on after its grace (R4).
|
|
1220
|
+
*
|
|
1221
|
+
* Then it depends on why (incoming-call-reliability.md §4.6, F5):
|
|
1222
|
+
* - **a passing failure** (a 5xx, the network, a timeout — `transientStartFailure`) is attempted again on its
|
|
1223
|
+
* own, after a backoff, as `reconnecting`, with no error gate. Found live: a voice token answered 500 after
|
|
1224
|
+
* 10 s left a background tab `offline` for good, and every call was lost until someone pressed "Reintentar".
|
|
1225
|
+
* Returns `true`.
|
|
1226
|
+
* - **anything else** (a refused credential, no extension) is reported like any engine failure —
|
|
1227
|
+
* `SoftphoneEvent.Error` and `lastError`, which is the widget's error gate and its "Reintentar" (`stop()`
|
|
1228
|
+
* then `start()`, which asks for the line again) — and nothing retries on its own. Returns `false`.
|
|
1154
1229
|
*/
|
|
1155
1230
|
#engineStartFailed(cause, reportedBefore) {
|
|
1156
1231
|
this.#engineOn = false;
|
|
1157
1232
|
this.#engineRunning = false;
|
|
1158
1233
|
this.#lineMode = "off";
|
|
1234
|
+
const transient = this.#destroyed ? null : transientStartFailure(cause);
|
|
1235
|
+
this.#stopPresence();
|
|
1236
|
+
if (transient) {
|
|
1237
|
+
this.#scheduleStartRetry(transient.retryAfterMs, cause);
|
|
1238
|
+
this.#setState("reconnecting");
|
|
1239
|
+
return true;
|
|
1240
|
+
}
|
|
1159
1241
|
if (this.#lastError === reportedBefore) {
|
|
1160
1242
|
this.#onError(isSoftphoneError(cause) && isFatal(cause) ? cause : createError("net/transport-failed", cause));
|
|
1161
1243
|
}
|
|
1162
|
-
this.#stopPresence();
|
|
1163
1244
|
this.#setState("offline");
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* The next attempt, after the shared backoff (1 s doubling to 30 s, jittered — the presence socket's own) and
|
|
1249
|
+
* never before a `Retry-After` the failure carried. A plain timer: in a hidden tab it can stretch to about a
|
|
1250
|
+
* minute, which is why the browser coming back `online`, or the tab becoming visible, attempts at once
|
|
1251
|
+
* (`watchWake`). One warning per streak, so a console says what is going on without filling up.
|
|
1252
|
+
*/
|
|
1253
|
+
#scheduleStartRetry(retryAfterMs, cause) {
|
|
1254
|
+
this.#startRetries += 1;
|
|
1255
|
+
const wait = Math.max(backoffDelayMs(this.#startRetries), retryAfterMs);
|
|
1256
|
+
if (this.#startRetries === 1) {
|
|
1257
|
+
console.warn("[softphone] the phone could not start; it keeps trying on its own", cause);
|
|
1258
|
+
}
|
|
1259
|
+
this.#startRetryNotBefore = Date.now() + retryAfterMs;
|
|
1260
|
+
if (this.#startRetryTimer !== null) clearTimeout(this.#startRetryTimer);
|
|
1261
|
+
const run = this.#run;
|
|
1262
|
+
this.#startRetryTimer = setTimeout(() => void this.#retryStart(run), wait);
|
|
1263
|
+
this.#unwatchWake ??= watchWake(() => this.#wakeStartRetry());
|
|
1264
|
+
}
|
|
1265
|
+
/** The pending attempt, now — unless a `Retry-After` says not yet, in which case it moves up to that moment. */
|
|
1266
|
+
#wakeStartRetry() {
|
|
1267
|
+
if (this.#startRetryTimer === null) return;
|
|
1268
|
+
const run = this.#run;
|
|
1269
|
+
clearTimeout(this.#startRetryTimer);
|
|
1270
|
+
const wait = this.#startRetryNotBefore - Date.now();
|
|
1271
|
+
if (wait > 0) {
|
|
1272
|
+
this.#startRetryTimer = setTimeout(() => void this.#retryStart(run), wait);
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
void this.#retryStart(run);
|
|
1276
|
+
}
|
|
1277
|
+
/** The wait and the wake-ups go; the streak's count stays (the next failure doubles on from it). */
|
|
1278
|
+
#clearStartRetryWait() {
|
|
1279
|
+
if (this.#startRetryTimer !== null) clearTimeout(this.#startRetryTimer);
|
|
1280
|
+
this.#startRetryTimer = null;
|
|
1281
|
+
this.#unwatchWake?.();
|
|
1282
|
+
this.#unwatchWake = null;
|
|
1283
|
+
}
|
|
1284
|
+
#cancelStartRetry() {
|
|
1285
|
+
this.#clearStartRetryWait();
|
|
1286
|
+
this.#startRetries = 0;
|
|
1287
|
+
}
|
|
1288
|
+
/**
|
|
1289
|
+
* One attempt: what "Reintentar" does after its `stop()`, minus the goodbye and the leadership (both kept) —
|
|
1290
|
+
* the presence socket opens again and its `hello` asks for the line, the line decides (`standby` if it went
|
|
1291
|
+
* elsewhere meanwhile, as at any start), and the engine starts. Without the goodbye, an attempt inside the
|
|
1292
|
+
* server's 10 s grace gets the line straight back. Never for a softphone stopped since it was scheduled.
|
|
1293
|
+
*/
|
|
1294
|
+
async #retryStart(run) {
|
|
1295
|
+
this.#clearStartRetryWait();
|
|
1296
|
+
if (this.#destroyed || run !== this.#run || !this.#startedHere || !this.#isLeader) return;
|
|
1297
|
+
const decision = this.#openStartDecision();
|
|
1298
|
+
try {
|
|
1299
|
+
this.#lineMode = "deciding";
|
|
1300
|
+
this.#setState("starting");
|
|
1301
|
+
if (this.#presenceWatch && this.#line === "unknown") await this.#waitForFirstLine();
|
|
1302
|
+
if (this.#destroyed || run !== this.#run) return;
|
|
1303
|
+
this.#lineMode = "on";
|
|
1304
|
+
const engineStart = this.#applyLine();
|
|
1305
|
+
if (!engineStart) this.#startRetries = 0;
|
|
1306
|
+
decision.end();
|
|
1307
|
+
await engineStart?.catch(() => {
|
|
1308
|
+
});
|
|
1309
|
+
} finally {
|
|
1310
|
+
decision.end();
|
|
1311
|
+
}
|
|
1164
1312
|
}
|
|
1165
1313
|
/** `start()`'s decision (`#startDecision`): ended once, by whichever comes first — the decision or its end. */
|
|
1166
1314
|
#openStartDecision() {
|
|
@@ -1214,6 +1362,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
1214
1362
|
* waiting softphone answers by listening again.
|
|
1215
1363
|
*/
|
|
1216
1364
|
#onPresenceDisconnect() {
|
|
1365
|
+
this.#engine.noteOffer?.(null);
|
|
1217
1366
|
this.#settleClaim(createError("line/unavailable"));
|
|
1218
1367
|
if (this.#line === "unknown" || this.#lineLapseTimer !== null) return;
|
|
1219
1368
|
this.#lineLapseTimer = setTimeout(() => {
|
|
@@ -1322,6 +1471,7 @@ var SoftphoneImpl = class extends Emitter {
|
|
|
1322
1471
|
};
|
|
1323
1472
|
|
|
1324
1473
|
// src/core/default-provider.ts
|
|
1474
|
+
var MAX_PENDING_OFFERS = 20;
|
|
1325
1475
|
var LazyRelay = class {
|
|
1326
1476
|
#listeners = /* @__PURE__ */ new Set();
|
|
1327
1477
|
#realOff = /* @__PURE__ */ new Map();
|
|
@@ -1348,13 +1498,16 @@ function defaultZadarmaEngine(config) {
|
|
|
1348
1498
|
const incoming = new LazyRelay();
|
|
1349
1499
|
const stateChange = new LazyRelay();
|
|
1350
1500
|
const error = new LazyRelay();
|
|
1501
|
+
let pendingOffers = [];
|
|
1351
1502
|
function load() {
|
|
1352
|
-
pending ??= import('./zadarma-engine-
|
|
1503
|
+
pending ??= import('./zadarma-engine-VYIXLBQG.js').then(({ createZadarmaEngine }) => {
|
|
1353
1504
|
const engine = createZadarmaEngine(config);
|
|
1354
1505
|
incoming.attach({ on: (l) => engine.onIncoming(l) });
|
|
1355
1506
|
stateChange.attach({ on: (l) => engine.onStateChange(l) });
|
|
1356
1507
|
error.attach({ on: (l) => engine.onError?.(l) ?? (() => {
|
|
1357
1508
|
}) });
|
|
1509
|
+
for (const offer of pendingOffers) engine.noteOffer?.(offer);
|
|
1510
|
+
pendingOffers = [];
|
|
1358
1511
|
real = engine;
|
|
1359
1512
|
return engine;
|
|
1360
1513
|
}).catch((cause) => {
|
|
@@ -1385,7 +1538,20 @@ function defaultZadarmaEngine(config) {
|
|
|
1385
1538
|
},
|
|
1386
1539
|
onIncoming: (listener) => incoming.subscribe(listener),
|
|
1387
1540
|
onStateChange: (listener) => stateChange.subscribe(listener),
|
|
1388
|
-
onError: (listener) => error.subscribe(listener)
|
|
1541
|
+
onError: (listener) => error.subscribe(listener),
|
|
1542
|
+
/*
|
|
1543
|
+
* Relayed like the listeners above, or the decline guard on this — the default — path would never hear the
|
|
1544
|
+
* backend (incoming-call-reliability.md §4.4). Before the engine loads, kept (a few) rather than loading it:
|
|
1545
|
+
* a softphone in `standby` hears offers without ever starting an engine.
|
|
1546
|
+
*/
|
|
1547
|
+
noteOffer: (offer) => {
|
|
1548
|
+
if (real) {
|
|
1549
|
+
real.noteOffer?.(offer);
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1552
|
+
pendingOffers.push(offer);
|
|
1553
|
+
if (pendingOffers.length > MAX_PENDING_OFFERS) pendingOffers.shift();
|
|
1554
|
+
}
|
|
1389
1555
|
};
|
|
1390
1556
|
}
|
|
1391
1557
|
|
|
@@ -1425,7 +1591,7 @@ function isCallEngine(value) {
|
|
|
1425
1591
|
}
|
|
1426
1592
|
|
|
1427
1593
|
// src/index.ts
|
|
1428
|
-
var VERSION = "0.5.
|
|
1594
|
+
var VERSION = "0.5.1";
|
|
1429
1595
|
|
|
1430
1596
|
export { CALL_TRANSITIONS, INITIAL_STATES, VERSION, canTransition, createSoftphone, isTerminal };
|
|
1431
1597
|
//# sourceMappingURL=index.js.map
|