@signalwire/js 4.0.0-dev-20260410164129 → 4.0.0-dev-20260413184400
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/browser.mjs +906 -599
- package/dist/browser.mjs.map +1 -1
- package/dist/browser.umd.js +906 -599
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +670 -364
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +6 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +660 -354
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -139,7 +139,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
|
|
|
139
139
|
|
|
140
140
|
//#endregion
|
|
141
141
|
//#region src/controllers/HTTPRequestController.ts
|
|
142
|
-
const logger$
|
|
142
|
+
const logger$28 = getLogger();
|
|
143
143
|
const GET_PARAMS = {
|
|
144
144
|
method: "GET",
|
|
145
145
|
headers: { Accept: "application/json" }
|
|
@@ -202,7 +202,7 @@ var HTTPRequestController = class HTTPRequestController {
|
|
|
202
202
|
this._responses$.next(response);
|
|
203
203
|
return response;
|
|
204
204
|
} catch (error) {
|
|
205
|
-
logger$
|
|
205
|
+
logger$28.error("[HTTPRequestController] Request error:", error);
|
|
206
206
|
this._status$.next("error");
|
|
207
207
|
const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
|
|
208
208
|
this._errors$.next(err);
|
|
@@ -229,7 +229,7 @@ var HTTPRequestController = class HTTPRequestController {
|
|
|
229
229
|
const url = this.buildURL(request.url);
|
|
230
230
|
const headers = this.buildHeaders(request.headers);
|
|
231
231
|
const timeout$1 = request.timeout ?? this.requestTimeout;
|
|
232
|
-
logger$
|
|
232
|
+
logger$28.debug("[HTTPRequestController] Executing request:", {
|
|
233
233
|
method: request.method,
|
|
234
234
|
url,
|
|
235
235
|
headers: Object.keys(headers).reduce((acc, key) => {
|
|
@@ -249,7 +249,7 @@ var HTTPRequestController = class HTTPRequestController {
|
|
|
249
249
|
});
|
|
250
250
|
clearTimeout(timeoutId);
|
|
251
251
|
const httpResponse = await this.convertResponse(response);
|
|
252
|
-
logger$
|
|
252
|
+
logger$28.debug("[HTTPRequestController] Response received:", {
|
|
253
253
|
status: response.status,
|
|
254
254
|
statusText: response.statusText,
|
|
255
255
|
headers: [...response.headers.entries()],
|
|
@@ -259,7 +259,7 @@ var HTTPRequestController = class HTTPRequestController {
|
|
|
259
259
|
} catch (error) {
|
|
260
260
|
clearTimeout(timeoutId);
|
|
261
261
|
if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$1}ms`, { cause: error });
|
|
262
|
-
logger$
|
|
262
|
+
logger$28.error("[HTTPRequestController] Request failed:", error);
|
|
263
263
|
throw error;
|
|
264
264
|
}
|
|
265
265
|
}
|
|
@@ -273,8 +273,8 @@ var HTTPRequestController = class HTTPRequestController {
|
|
|
273
273
|
const credential = this.getCredential();
|
|
274
274
|
if (credential.token) {
|
|
275
275
|
headers.Authorization = `Bearer ${credential.token}`;
|
|
276
|
-
logger$
|
|
277
|
-
} else logger$
|
|
276
|
+
logger$28.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
|
|
277
|
+
} else logger$28.warn("[HTTPRequestController] No credentials available for authentication");
|
|
278
278
|
return headers;
|
|
279
279
|
}
|
|
280
280
|
/**
|
|
@@ -423,6 +423,19 @@ const DEFAULT_RECONNECT_DELAY_MAX_MS = 3e3;
|
|
|
423
423
|
const DEFAULT_DEVICE_DEBOUNCE_TIME_MS = 1500;
|
|
424
424
|
const DEFAULT_DEVICE_POLLING_INTERVAL_MS = 0;
|
|
425
425
|
const PREFERENCES_STORAGE_KEY = "sw:preferences";
|
|
426
|
+
/** Scope value that enables automatic token refresh. */
|
|
427
|
+
const SAT_REFRESH_SCOPE = "sat:refresh";
|
|
428
|
+
/** API endpoints for device token operations. */
|
|
429
|
+
const DEVICE_TOKEN_ENDPOINT = "/api/fabric/subscriber/devices/token";
|
|
430
|
+
const DEVICE_REFRESH_ENDPOINT = "/api/fabric/subscriber/devices/refresh";
|
|
431
|
+
/** Default device token TTL in seconds (15 minutes). */
|
|
432
|
+
const DEVICE_TOKEN_DEFAULT_EXPIRE_IN = 900;
|
|
433
|
+
/** Buffer time in milliseconds before expiry to trigger refresh. */
|
|
434
|
+
const DEVICE_TOKEN_REFRESH_BUFFER_MS = 3e4;
|
|
435
|
+
/** Maximum retry attempts for device token refresh on transient failure. */
|
|
436
|
+
const DEVICE_TOKEN_REFRESH_MAX_RETRIES = 3;
|
|
437
|
+
/** Base delay in milliseconds for exponential backoff on refresh retry. */
|
|
438
|
+
const DEVICE_TOKEN_REFRESH_RETRY_BASE_MS = 1e3;
|
|
426
439
|
/** JSON-RPC error code for requester validation failure (corrupted auth state). */
|
|
427
440
|
const RPC_ERROR_REQUESTER_VALIDATION_FAILED = -32003;
|
|
428
441
|
/** JSON-RPC error code for invalid params (e.g., missing authentication block). */
|
|
@@ -513,7 +526,7 @@ function fromMsToSec(milliseconds) {
|
|
|
513
526
|
|
|
514
527
|
//#endregion
|
|
515
528
|
//#region src/containers/PreferencesContainer.ts
|
|
516
|
-
const logger$
|
|
529
|
+
const logger$27 = getLogger();
|
|
517
530
|
var PreferencesContainer = class PreferencesContainer {
|
|
518
531
|
static get instance() {
|
|
519
532
|
this._instance ??= new PreferencesContainer();
|
|
@@ -1175,7 +1188,7 @@ var ClientPreferences = class {
|
|
|
1175
1188
|
if (!this._storage) return;
|
|
1176
1189
|
const data = collectStoredPreferences();
|
|
1177
1190
|
this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
|
|
1178
|
-
logger$
|
|
1191
|
+
logger$27.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
|
|
1179
1192
|
});
|
|
1180
1193
|
}
|
|
1181
1194
|
/** Loads preferences from storage and applies them to the container. */
|
|
@@ -1184,14 +1197,14 @@ var ClientPreferences = class {
|
|
|
1184
1197
|
this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
|
|
1185
1198
|
if (stored) applyStoredPreferences(stored);
|
|
1186
1199
|
}).catch((error) => {
|
|
1187
|
-
logger$
|
|
1200
|
+
logger$27.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
|
|
1188
1201
|
});
|
|
1189
1202
|
}
|
|
1190
1203
|
};
|
|
1191
1204
|
|
|
1192
1205
|
//#endregion
|
|
1193
1206
|
//#region src/controllers/NavigatorDeviceController.ts
|
|
1194
|
-
const logger$
|
|
1207
|
+
const logger$26 = getLogger();
|
|
1195
1208
|
/** Maps a device kind to its storage key. */
|
|
1196
1209
|
const DEVICE_STORAGE_KEYS = {
|
|
1197
1210
|
audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
|
|
@@ -1213,7 +1226,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1213
1226
|
super();
|
|
1214
1227
|
this.webRTCApiProvider = webRTCApiProvider;
|
|
1215
1228
|
this.deviceChangeHandler = () => {
|
|
1216
|
-
logger$
|
|
1229
|
+
logger$26.debug("[DeviceController] Device change detected");
|
|
1217
1230
|
this.enumerateDevices();
|
|
1218
1231
|
};
|
|
1219
1232
|
this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
|
|
@@ -1278,13 +1291,13 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1278
1291
|
return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe(map((state) => state.videoinput), distinctUntilChanged(), takeUntil(this.destroyed$)));
|
|
1279
1292
|
}
|
|
1280
1293
|
get selectedAudioInputDevice$() {
|
|
1281
|
-
return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe(map((state) => state.audioinput), distinctUntilChanged(), takeUntil(this.destroyed$), tap((info) => logger$
|
|
1294
|
+
return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe(map((state) => state.audioinput), distinctUntilChanged(), takeUntil(this.destroyed$), tap((info) => logger$26.debug("[DeviceController] Selected audio input device changed:", info))));
|
|
1282
1295
|
}
|
|
1283
1296
|
get selectedAudioOutputDevice$() {
|
|
1284
|
-
return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe(map((state) => state.audiooutput), distinctUntilChanged(), takeUntil(this.destroyed$), tap((info) => logger$
|
|
1297
|
+
return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe(map((state) => state.audiooutput), distinctUntilChanged(), takeUntil(this.destroyed$), tap((info) => logger$26.debug("[DeviceController] Selected audio output device changed:", info))));
|
|
1285
1298
|
}
|
|
1286
1299
|
get selectedVideoInputDevice$() {
|
|
1287
|
-
return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe(map((state) => state.videoinput), distinctUntilChanged(), takeUntil(this.destroyed$), tap((info) => logger$
|
|
1300
|
+
return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe(map((state) => state.videoinput), distinctUntilChanged(), takeUntil(this.destroyed$), tap((info) => logger$26.debug("[DeviceController] Selected video input device changed:", info))));
|
|
1288
1301
|
}
|
|
1289
1302
|
get selectedAudioInputDevice() {
|
|
1290
1303
|
if (this._audioInputDisabled$.value) return null;
|
|
@@ -1359,7 +1372,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1359
1372
|
if (device) this.persistDeviceSelection("audioinput", device);
|
|
1360
1373
|
}
|
|
1361
1374
|
selectVideoInputDevice(device) {
|
|
1362
|
-
logger$
|
|
1375
|
+
logger$26.debug("[DeviceController] Setting selected video input device:", device);
|
|
1363
1376
|
if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
|
|
1364
1377
|
const previous = this._selectedDevicesState$.value.videoinput;
|
|
1365
1378
|
if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
|
|
@@ -1416,7 +1429,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1416
1429
|
}
|
|
1417
1430
|
const fromHistory = this._deviceHistory.findInHistory(kind, devices);
|
|
1418
1431
|
if (fromHistory) {
|
|
1419
|
-
logger$
|
|
1432
|
+
logger$26.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
|
|
1420
1433
|
this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
|
|
1421
1434
|
return fromHistory;
|
|
1422
1435
|
}
|
|
@@ -1469,7 +1482,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1469
1482
|
try {
|
|
1470
1483
|
await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
|
|
1471
1484
|
} catch (error) {
|
|
1472
|
-
logger$
|
|
1485
|
+
logger$26.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
|
|
1473
1486
|
}
|
|
1474
1487
|
}
|
|
1475
1488
|
async loadPersistedDevices() {
|
|
@@ -1485,7 +1498,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1485
1498
|
[kind]: stored
|
|
1486
1499
|
};
|
|
1487
1500
|
} catch (error) {
|
|
1488
|
-
logger$
|
|
1501
|
+
logger$26.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
|
|
1489
1502
|
}
|
|
1490
1503
|
}
|
|
1491
1504
|
/** Clears device history, persisted selections, and re-enumerates devices. */
|
|
@@ -1503,7 +1516,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1503
1516
|
this.disableDeviceMonitoring();
|
|
1504
1517
|
this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
|
|
1505
1518
|
if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = interval(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
|
|
1506
|
-
logger$
|
|
1519
|
+
logger$26.debug("[DeviceController] Polling devices due to interval");
|
|
1507
1520
|
this.enumerateDevices();
|
|
1508
1521
|
});
|
|
1509
1522
|
this.enumerateDevices();
|
|
@@ -1529,13 +1542,13 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1529
1542
|
videoinput: []
|
|
1530
1543
|
});
|
|
1531
1544
|
this._devicesState$.next(devicesByKind);
|
|
1532
|
-
logger$
|
|
1545
|
+
logger$26.debug("[DeviceController] Devices enumerated:", {
|
|
1533
1546
|
audioInputs: devicesByKind.audioinput.length,
|
|
1534
1547
|
audioOutputs: devicesByKind.audiooutput.length,
|
|
1535
1548
|
videoInputs: devicesByKind.videoinput.length
|
|
1536
1549
|
});
|
|
1537
1550
|
} catch (error) {
|
|
1538
|
-
logger$
|
|
1551
|
+
logger$26.error("[DeviceController] Failed to enumerate devices:", error);
|
|
1539
1552
|
this._errors$.next(error);
|
|
1540
1553
|
}
|
|
1541
1554
|
}
|
|
@@ -1551,7 +1564,7 @@ var NavigatorDeviceController = class extends Destroyable {
|
|
|
1551
1564
|
stream.getTracks().forEach((t) => t.stop());
|
|
1552
1565
|
return capabilities;
|
|
1553
1566
|
} catch (error) {
|
|
1554
|
-
logger$
|
|
1567
|
+
logger$26.error("[DeviceController] Failed to get device capabilities:", error);
|
|
1555
1568
|
this._errors$.next(error);
|
|
1556
1569
|
throw error;
|
|
1557
1570
|
}
|
|
@@ -1802,7 +1815,7 @@ var DependencyContainer = class {
|
|
|
1802
1815
|
|
|
1803
1816
|
//#endregion
|
|
1804
1817
|
//#region src/controllers/CryptoController.ts
|
|
1805
|
-
const logger$
|
|
1818
|
+
const logger$25 = getLogger();
|
|
1806
1819
|
const DPOP_DB_NAME = "sw-dpop";
|
|
1807
1820
|
const DPOP_DB_VERSION = 1;
|
|
1808
1821
|
const DPOP_STORE_NAME = "keys";
|
|
@@ -1861,7 +1874,7 @@ async function loadKeyPairFromDB() {
|
|
|
1861
1874
|
tx.oncomplete = () => db.close();
|
|
1862
1875
|
});
|
|
1863
1876
|
} catch (error) {
|
|
1864
|
-
logger$
|
|
1877
|
+
logger$25.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
|
|
1865
1878
|
return null;
|
|
1866
1879
|
}
|
|
1867
1880
|
}
|
|
@@ -1881,7 +1894,7 @@ async function saveKeyPairToDB(keyPair) {
|
|
|
1881
1894
|
};
|
|
1882
1895
|
});
|
|
1883
1896
|
} catch (error) {
|
|
1884
|
-
logger$
|
|
1897
|
+
logger$25.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
|
|
1885
1898
|
}
|
|
1886
1899
|
}
|
|
1887
1900
|
async function deleteKeyPairFromDB() {
|
|
@@ -1900,7 +1913,7 @@ async function deleteKeyPairFromDB() {
|
|
|
1900
1913
|
};
|
|
1901
1914
|
});
|
|
1902
1915
|
} catch (error) {
|
|
1903
|
-
logger$
|
|
1916
|
+
logger$25.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
|
|
1904
1917
|
}
|
|
1905
1918
|
}
|
|
1906
1919
|
/**
|
|
@@ -1960,13 +1973,13 @@ var CryptoController = class {
|
|
|
1960
1973
|
this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
|
|
1961
1974
|
this._fingerprint = await computeJwkThumbprint(this._publicJwk);
|
|
1962
1975
|
this._initialized = true;
|
|
1963
|
-
logger$
|
|
1976
|
+
logger$25.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
|
|
1964
1977
|
return this._fingerprint;
|
|
1965
1978
|
} catch (error) {
|
|
1966
|
-
logger$
|
|
1979
|
+
logger$25.warn("[DPoP] Stored key pair unusable, generating new one:", error);
|
|
1967
1980
|
await deleteKeyPairFromDB();
|
|
1968
1981
|
}
|
|
1969
|
-
logger$
|
|
1982
|
+
logger$25.debug("[DPoP] Generating RSA key pair");
|
|
1970
1983
|
this._keyPair = await crypto.subtle.generateKey({
|
|
1971
1984
|
name: "RSASSA-PKCS1-v1_5",
|
|
1972
1985
|
modulusLength: 2048,
|
|
@@ -1981,7 +1994,7 @@ var CryptoController = class {
|
|
|
1981
1994
|
this._fingerprint = await computeJwkThumbprint(this._publicJwk);
|
|
1982
1995
|
this._initialized = true;
|
|
1983
1996
|
await saveKeyPairToDB(this._keyPair);
|
|
1984
|
-
logger$
|
|
1997
|
+
logger$25.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
|
|
1985
1998
|
return this._fingerprint;
|
|
1986
1999
|
}
|
|
1987
2000
|
/**
|
|
@@ -2047,7 +2060,7 @@ var CryptoController = class {
|
|
|
2047
2060
|
this._fingerprint = null;
|
|
2048
2061
|
this._initialized = false;
|
|
2049
2062
|
deleteKeyPairFromDB();
|
|
2050
|
-
logger$
|
|
2063
|
+
logger$25.debug("[DPoP] Controller destroyed");
|
|
2051
2064
|
}
|
|
2052
2065
|
get publicJwk() {
|
|
2053
2066
|
if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
|
|
@@ -2070,7 +2083,7 @@ var CryptoController = class {
|
|
|
2070
2083
|
|
|
2071
2084
|
//#endregion
|
|
2072
2085
|
//#region src/controllers/NetworkMonitor.ts
|
|
2073
|
-
const logger$
|
|
2086
|
+
const logger$24 = getLogger();
|
|
2074
2087
|
/**
|
|
2075
2088
|
* Safely check whether we are running in a browser environment
|
|
2076
2089
|
* with `window` and the relevant event targets.
|
|
@@ -2127,7 +2140,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
2127
2140
|
}
|
|
2128
2141
|
attachListeners() {
|
|
2129
2142
|
if (!hasBrowserNetworkEvents()) {
|
|
2130
|
-
logger$
|
|
2143
|
+
logger$24.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
|
|
2131
2144
|
return;
|
|
2132
2145
|
}
|
|
2133
2146
|
window.addEventListener("online", this._onOnline);
|
|
@@ -2135,7 +2148,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
2135
2148
|
const connection = getNetworkConnection();
|
|
2136
2149
|
if (connection) connection.addEventListener("change", this._onConnectionChange);
|
|
2137
2150
|
this._listenersAttached = true;
|
|
2138
|
-
logger$
|
|
2151
|
+
logger$24.debug("NetworkMonitor: event listeners attached");
|
|
2139
2152
|
}
|
|
2140
2153
|
removeListeners() {
|
|
2141
2154
|
if (!this._listenersAttached) return;
|
|
@@ -2146,10 +2159,10 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
2146
2159
|
if (connection) connection.removeEventListener("change", this._onConnectionChange);
|
|
2147
2160
|
}
|
|
2148
2161
|
this._listenersAttached = false;
|
|
2149
|
-
logger$
|
|
2162
|
+
logger$24.debug("NetworkMonitor: event listeners removed");
|
|
2150
2163
|
}
|
|
2151
2164
|
handleOnline() {
|
|
2152
|
-
logger$
|
|
2165
|
+
logger$24.info("NetworkMonitor: browser went online");
|
|
2153
2166
|
this._isOnline$.next(true);
|
|
2154
2167
|
this._networkChange$.next({
|
|
2155
2168
|
type: "online",
|
|
@@ -2158,7 +2171,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
2158
2171
|
});
|
|
2159
2172
|
}
|
|
2160
2173
|
handleOffline() {
|
|
2161
|
-
logger$
|
|
2174
|
+
logger$24.info("NetworkMonitor: browser went offline");
|
|
2162
2175
|
this._isOnline$.next(false);
|
|
2163
2176
|
this._networkChange$.next({
|
|
2164
2177
|
type: "offline",
|
|
@@ -2167,7 +2180,7 @@ var NetworkMonitor = class extends Destroyable {
|
|
|
2167
2180
|
}
|
|
2168
2181
|
handleConnectionChange() {
|
|
2169
2182
|
const networkType = getNetworkType();
|
|
2170
|
-
logger$
|
|
2183
|
+
logger$24.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
|
|
2171
2184
|
this._networkChange$.next({
|
|
2172
2185
|
type: "connection_change",
|
|
2173
2186
|
timestamp: Date.now(),
|
|
@@ -2282,7 +2295,7 @@ function getNavigatorMediaDevices() {
|
|
|
2282
2295
|
|
|
2283
2296
|
//#endregion
|
|
2284
2297
|
//#region src/controllers/PreflightRunner.ts
|
|
2285
|
-
const logger$
|
|
2298
|
+
const logger$23 = getLogger();
|
|
2286
2299
|
const DEFAULT_MEDIA_TEST_DURATION_S = 10;
|
|
2287
2300
|
const ICE_GATHERING_TIMEOUT_MS = 1e4;
|
|
2288
2301
|
const SIGNALING_RTT_TIMEOUT_MS = 5e3;
|
|
@@ -2331,7 +2344,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
2331
2344
|
if (!this._options.skipMediaTest) try {
|
|
2332
2345
|
bandwidth = await this.testMediaBandwidth(destination);
|
|
2333
2346
|
} catch (error) {
|
|
2334
|
-
logger$
|
|
2347
|
+
logger$23.warn("[PreflightRunner] Media bandwidth test failed:", error);
|
|
2335
2348
|
warnings.push("Media bandwidth test failed");
|
|
2336
2349
|
}
|
|
2337
2350
|
return {
|
|
@@ -2343,7 +2356,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
2343
2356
|
warnings
|
|
2344
2357
|
};
|
|
2345
2358
|
} catch (error) {
|
|
2346
|
-
logger$
|
|
2359
|
+
logger$23.error("[PreflightRunner] Preflight test failed:", error);
|
|
2347
2360
|
throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
|
|
2348
2361
|
} finally {
|
|
2349
2362
|
this.destroy();
|
|
@@ -2374,7 +2387,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
2374
2387
|
if (track.kind === "video" && track.readyState === "live") videoWorking = true;
|
|
2375
2388
|
}
|
|
2376
2389
|
} catch (error) {
|
|
2377
|
-
logger$
|
|
2390
|
+
logger$23.warn("[PreflightRunner] Device test failed:", error);
|
|
2378
2391
|
} finally {
|
|
2379
2392
|
if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
|
|
2380
2393
|
}
|
|
@@ -2432,7 +2445,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
2432
2445
|
rttMs
|
|
2433
2446
|
};
|
|
2434
2447
|
} catch (error) {
|
|
2435
|
-
logger$
|
|
2448
|
+
logger$23.warn("[PreflightRunner] ICE connectivity test failed:", error);
|
|
2436
2449
|
return {
|
|
2437
2450
|
type: "failed",
|
|
2438
2451
|
turnReachable: false,
|
|
@@ -2479,7 +2492,7 @@ var PreflightRunner = class extends Destroyable {
|
|
|
2479
2492
|
|
|
2480
2493
|
//#endregion
|
|
2481
2494
|
//#region src/controllers/VisibilityController.ts
|
|
2482
|
-
const logger$
|
|
2495
|
+
const logger$22 = getLogger();
|
|
2483
2496
|
/**
|
|
2484
2497
|
* Checks whether the document visibility API is available.
|
|
2485
2498
|
*/
|
|
@@ -2516,8 +2529,8 @@ var VisibilityController = class extends Destroyable {
|
|
|
2516
2529
|
this._boundHandler = this._handleVisibilityChange.bind(this);
|
|
2517
2530
|
if (this._hasVisibilityApi) {
|
|
2518
2531
|
document.addEventListener("visibilitychange", this._boundHandler);
|
|
2519
|
-
logger$
|
|
2520
|
-
} else logger$
|
|
2532
|
+
logger$22.debug("VisibilityController: listening for visibilitychange events");
|
|
2533
|
+
} else logger$22.debug("VisibilityController: document visibility API not available, defaulting to visible");
|
|
2521
2534
|
}
|
|
2522
2535
|
/**
|
|
2523
2536
|
* Observable of the current visibility state.
|
|
@@ -2542,7 +2555,7 @@ var VisibilityController = class extends Destroyable {
|
|
|
2542
2555
|
destroy() {
|
|
2543
2556
|
if (this._hasVisibilityApi) {
|
|
2544
2557
|
document.removeEventListener("visibilitychange", this._boundHandler);
|
|
2545
|
-
logger$
|
|
2558
|
+
logger$22.debug("VisibilityController: removed visibilitychange listener");
|
|
2546
2559
|
}
|
|
2547
2560
|
super.destroy();
|
|
2548
2561
|
}
|
|
@@ -2560,7 +2573,7 @@ var VisibilityController = class extends Destroyable {
|
|
|
2560
2573
|
timestamp: Date.now()
|
|
2561
2574
|
};
|
|
2562
2575
|
this._visibilityChange$.next(changeEvent);
|
|
2563
|
-
logger$
|
|
2576
|
+
logger$22.debug("VisibilityController: visibility changed", {
|
|
2564
2577
|
from: previousState,
|
|
2565
2578
|
to: newState
|
|
2566
2579
|
});
|
|
@@ -2763,7 +2776,7 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
|
|
|
2763
2776
|
|
|
2764
2777
|
//#endregion
|
|
2765
2778
|
//#region src/managers/AttachManager.ts
|
|
2766
|
-
const logger$
|
|
2779
|
+
const logger$21 = getLogger();
|
|
2767
2780
|
var AttachManager = class {
|
|
2768
2781
|
constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
|
|
2769
2782
|
this.storage = storage;
|
|
@@ -2775,6 +2788,7 @@ var AttachManager = class {
|
|
|
2775
2788
|
const attached = await this.readAttached();
|
|
2776
2789
|
for (const callId of Object.keys(attached)) await this.detach({
|
|
2777
2790
|
id: callId,
|
|
2791
|
+
nodeId: attached[callId].nodeId,
|
|
2778
2792
|
mediaDirections: attached[callId].mediaDirections
|
|
2779
2793
|
});
|
|
2780
2794
|
}
|
|
@@ -2785,7 +2799,7 @@ var AttachManager = class {
|
|
|
2785
2799
|
try {
|
|
2786
2800
|
return await this.storage.getItem(this.attachKey) ?? {};
|
|
2787
2801
|
} catch (error) {
|
|
2788
|
-
logger$
|
|
2802
|
+
logger$21.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
|
|
2789
2803
|
return {};
|
|
2790
2804
|
}
|
|
2791
2805
|
}
|
|
@@ -2793,20 +2807,20 @@ var AttachManager = class {
|
|
|
2793
2807
|
try {
|
|
2794
2808
|
await this.storage.setItem(this.attachKey, attached);
|
|
2795
2809
|
} catch (error) {
|
|
2796
|
-
logger$
|
|
2810
|
+
logger$21.warn("[AttachManager] Failed to write attached calls to storage", error);
|
|
2797
2811
|
}
|
|
2798
2812
|
}
|
|
2799
2813
|
async attach(call) {
|
|
2800
2814
|
if (!call.to) {
|
|
2801
|
-
logger$
|
|
2815
|
+
logger$21.warn("[AttachManager] Skip attach for calls with no destination");
|
|
2802
2816
|
return;
|
|
2803
2817
|
}
|
|
2804
2818
|
const attachment = {
|
|
2819
|
+
nodeId: call.nodeId,
|
|
2805
2820
|
destination: call.to,
|
|
2806
2821
|
mediaDirections: call.mediaDirections,
|
|
2807
2822
|
audioInputDevice: call.mediaDirections.audio !== "inactive" ? this.deviceController.selectedAudioInputDevice : null,
|
|
2808
2823
|
videoInputDevice: call.mediaDirections.video !== "inactive" ? this.deviceController.selectedVideoInputDevice : null,
|
|
2809
|
-
nodeId: call.nodeId,
|
|
2810
2824
|
attachedAt: Date.now()
|
|
2811
2825
|
};
|
|
2812
2826
|
const updated = {
|
|
@@ -2849,15 +2863,15 @@ var AttachManager = class {
|
|
|
2849
2863
|
callId,
|
|
2850
2864
|
...options
|
|
2851
2865
|
});
|
|
2852
|
-
logger$
|
|
2866
|
+
logger$21.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
|
|
2853
2867
|
succeeded = true;
|
|
2854
2868
|
break;
|
|
2855
2869
|
} catch (error) {
|
|
2856
|
-
logger$
|
|
2870
|
+
logger$21.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
|
|
2857
2871
|
if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
|
|
2858
2872
|
}
|
|
2859
2873
|
if (!succeeded) {
|
|
2860
|
-
logger$
|
|
2874
|
+
logger$21.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
|
|
2861
2875
|
await this.detach({
|
|
2862
2876
|
id: callId,
|
|
2863
2877
|
mediaDirections: attachment.mediaDirections
|
|
@@ -2871,12 +2885,13 @@ var AttachManager = class {
|
|
|
2871
2885
|
*/
|
|
2872
2886
|
buildCallOptions(attachment) {
|
|
2873
2887
|
const { audio: audioDirection, video: videoDirection } = attachment.mediaDirections;
|
|
2874
|
-
const { audioInputDevice, videoInputDevice } = attachment;
|
|
2888
|
+
const { audioInputDevice, videoInputDevice, nodeId } = attachment;
|
|
2875
2889
|
const receiveAudio = audioDirection.includes("recv");
|
|
2876
2890
|
const receiveVideo = videoDirection.includes("recv");
|
|
2877
2891
|
const sendAudio = audioDirection.includes("send");
|
|
2878
2892
|
const sendVideo = videoDirection.includes("send");
|
|
2879
2893
|
return {
|
|
2894
|
+
nodeId,
|
|
2880
2895
|
receiveAudio,
|
|
2881
2896
|
receiveVideo,
|
|
2882
2897
|
inputAudioDeviceConstraints: sendAudio ? {
|
|
@@ -2887,7 +2902,6 @@ var AttachManager = class {
|
|
|
2887
2902
|
video: true,
|
|
2888
2903
|
...this.deviceController.deviceInfoToConstraints(videoInputDevice)
|
|
2889
2904
|
} : void 0,
|
|
2890
|
-
nodeId: attachment.nodeId ?? void 0,
|
|
2891
2905
|
reattach: true
|
|
2892
2906
|
};
|
|
2893
2907
|
}
|
|
@@ -3160,7 +3174,7 @@ function toggleHandraiseMethod(is) {
|
|
|
3160
3174
|
|
|
3161
3175
|
//#endregion
|
|
3162
3176
|
//#region src/core/entities/Participant.ts
|
|
3163
|
-
const logger$
|
|
3177
|
+
const logger$20 = getLogger();
|
|
3164
3178
|
const initialState = {};
|
|
3165
3179
|
/**
|
|
3166
3180
|
* Represents a participant in a call.
|
|
@@ -3538,7 +3552,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3538
3552
|
try {
|
|
3539
3553
|
await this.vertoManager.addScreenMedia();
|
|
3540
3554
|
} catch (error) {
|
|
3541
|
-
logger$
|
|
3555
|
+
logger$20.error("[Participant.startScreenShare] Screen share error:", error);
|
|
3542
3556
|
}
|
|
3543
3557
|
}
|
|
3544
3558
|
/** Observable of the current screen share status. */
|
|
@@ -3558,7 +3572,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3558
3572
|
try {
|
|
3559
3573
|
await this.vertoManager.addInputDevice(options);
|
|
3560
3574
|
} catch (error) {
|
|
3561
|
-
logger$
|
|
3575
|
+
logger$20.error("[Participant.startScreenShare] Screen share error:", error);
|
|
3562
3576
|
}
|
|
3563
3577
|
}
|
|
3564
3578
|
/** Removes an additional media input device by ID. */
|
|
@@ -3620,7 +3634,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3620
3634
|
*/
|
|
3621
3635
|
exitStudioModeIfActive() {
|
|
3622
3636
|
if (this._studioAudio$.value) {
|
|
3623
|
-
logger$
|
|
3637
|
+
logger$20.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
|
|
3624
3638
|
this._studioAudio$.next(false);
|
|
3625
3639
|
}
|
|
3626
3640
|
}
|
|
@@ -3644,7 +3658,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3644
3658
|
try {
|
|
3645
3659
|
await super.mute();
|
|
3646
3660
|
} catch (error) {
|
|
3647
|
-
logger$
|
|
3661
|
+
logger$20.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
|
|
3648
3662
|
} finally {
|
|
3649
3663
|
this.vertoManager.muteMainAudioInputDevice();
|
|
3650
3664
|
}
|
|
@@ -3654,7 +3668,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3654
3668
|
try {
|
|
3655
3669
|
await super.unmute();
|
|
3656
3670
|
} catch (error) {
|
|
3657
|
-
logger$
|
|
3671
|
+
logger$20.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
|
|
3658
3672
|
} finally {
|
|
3659
3673
|
await this.vertoManager.unmuteMainAudioInputDevice();
|
|
3660
3674
|
}
|
|
@@ -3664,7 +3678,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3664
3678
|
try {
|
|
3665
3679
|
await super.muteVideo();
|
|
3666
3680
|
} catch (error) {
|
|
3667
|
-
logger$
|
|
3681
|
+
logger$20.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
|
|
3668
3682
|
} finally {
|
|
3669
3683
|
this.vertoManager.muteMainVideoInputDevice();
|
|
3670
3684
|
}
|
|
@@ -3674,7 +3688,7 @@ var SelfParticipant = class extends Participant {
|
|
|
3674
3688
|
try {
|
|
3675
3689
|
await super.unmuteVideo();
|
|
3676
3690
|
} catch (error) {
|
|
3677
|
-
logger$
|
|
3691
|
+
logger$20.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
|
|
3678
3692
|
} finally {
|
|
3679
3693
|
await this.vertoManager.unmuteMainVideoInputDevice();
|
|
3680
3694
|
}
|
|
@@ -3768,7 +3782,7 @@ function isLayoutChangedPayload(value) {
|
|
|
3768
3782
|
|
|
3769
3783
|
//#endregion
|
|
3770
3784
|
//#region src/managers/CallEventsManager.ts
|
|
3771
|
-
const logger$
|
|
3785
|
+
const logger$19 = getLogger();
|
|
3772
3786
|
const initialSessionState = {};
|
|
3773
3787
|
/** @internal */
|
|
3774
3788
|
var CallEventsManager = class extends Destroyable {
|
|
@@ -3872,7 +3886,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3872
3886
|
}
|
|
3873
3887
|
initSubscriptions() {
|
|
3874
3888
|
this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
|
|
3875
|
-
logger$
|
|
3889
|
+
logger$19.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
|
|
3876
3890
|
callId: callJoinedEvent.call_id,
|
|
3877
3891
|
roomSessionId: callJoinedEvent.room_session_id
|
|
3878
3892
|
});
|
|
@@ -3899,19 +3913,19 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3899
3913
|
if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
|
|
3900
3914
|
});
|
|
3901
3915
|
this.subscribeTo(this.memberUpdates$, (member) => {
|
|
3902
|
-
logger$
|
|
3916
|
+
logger$19.debug("[CallEventsManager] Handling member update event for member ID:", member);
|
|
3903
3917
|
this.upsertParticipant(member);
|
|
3904
3918
|
});
|
|
3905
3919
|
this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
|
|
3906
|
-
logger$
|
|
3920
|
+
logger$19.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
|
|
3907
3921
|
const participants = { ...this._participants$.value };
|
|
3908
3922
|
if (memberLeftEvent.member.member_id in participants) {
|
|
3909
3923
|
delete participants[memberLeftEvent.member.member_id];
|
|
3910
3924
|
this._participants$.next(participants);
|
|
3911
|
-
} else logger$
|
|
3925
|
+
} else logger$19.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
|
|
3912
3926
|
});
|
|
3913
3927
|
this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
|
|
3914
|
-
logger$
|
|
3928
|
+
logger$19.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
|
|
3915
3929
|
const roomSession = callUpdatedEvent.room_session;
|
|
3916
3930
|
this._sessionState$.next({
|
|
3917
3931
|
...this._sessionState$.value,
|
|
@@ -3926,7 +3940,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3926
3940
|
});
|
|
3927
3941
|
});
|
|
3928
3942
|
this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
|
|
3929
|
-
logger$
|
|
3943
|
+
logger$19.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
|
|
3930
3944
|
this._sessionState$.next({
|
|
3931
3945
|
...this._sessionState$.value,
|
|
3932
3946
|
layout_name: layoutChangedEvent.id,
|
|
@@ -3936,10 +3950,10 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3936
3950
|
});
|
|
3937
3951
|
}
|
|
3938
3952
|
updateParticipantPositions(layoutChangedEvent) {
|
|
3939
|
-
if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$
|
|
3953
|
+
if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$19.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
|
|
3940
3954
|
layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
|
|
3941
3955
|
if (!(layer.member_id in this._participants$.value)) {
|
|
3942
|
-
logger$
|
|
3956
|
+
logger$19.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
|
|
3943
3957
|
return false;
|
|
3944
3958
|
}
|
|
3945
3959
|
return true;
|
|
@@ -3962,7 +3976,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3962
3976
|
layouts: response.result.layouts
|
|
3963
3977
|
});
|
|
3964
3978
|
}).catch((error) => {
|
|
3965
|
-
logger$
|
|
3979
|
+
logger$19.error("[CallEventsManager] Error fetching layouts:", error);
|
|
3966
3980
|
});
|
|
3967
3981
|
}
|
|
3968
3982
|
updateParticipants(members) {
|
|
@@ -3978,7 +3992,7 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3978
3992
|
}
|
|
3979
3993
|
const participant = this._participants$.value[member.member_id];
|
|
3980
3994
|
const oldValue = participant.value;
|
|
3981
|
-
logger$
|
|
3995
|
+
logger$19.debug("[CallEventsManager] Updating participant:", member.member_id, {
|
|
3982
3996
|
oldValue,
|
|
3983
3997
|
newValue: member
|
|
3984
3998
|
});
|
|
@@ -3991,17 +4005,17 @@ var CallEventsManager = class extends Destroyable {
|
|
|
3991
4005
|
}
|
|
3992
4006
|
get callJoinedEvent$() {
|
|
3993
4007
|
return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filter(isCallJoinedPayload), tap((event) => {
|
|
3994
|
-
logger$
|
|
4008
|
+
logger$19.debug("[CallEventsManager] Call joined event:", event);
|
|
3995
4009
|
})));
|
|
3996
4010
|
}
|
|
3997
4011
|
get layoutChangedEvent$() {
|
|
3998
4012
|
return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), tap((event) => {
|
|
3999
|
-
logger$
|
|
4013
|
+
logger$19.debug("[CallEventsManager] Layout changed event:", event);
|
|
4000
4014
|
})));
|
|
4001
4015
|
}
|
|
4002
4016
|
get memberUpdates$() {
|
|
4003
4017
|
return this.cachedObservable("memberUpdates$", () => merge(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe(map((event) => event.member), tap((event) => {
|
|
4004
|
-
logger$
|
|
4018
|
+
logger$19.debug("[CallEventsManager] Member update event:", event);
|
|
4005
4019
|
})));
|
|
4006
4020
|
}
|
|
4007
4021
|
destroy() {
|
|
@@ -4257,7 +4271,7 @@ function appendStereoParams(fmtpLine, maxBitrate) {
|
|
|
4257
4271
|
|
|
4258
4272
|
//#endregion
|
|
4259
4273
|
//#region src/controllers/ICEGatheringController.ts
|
|
4260
|
-
const logger$
|
|
4274
|
+
const logger$18 = getLogger();
|
|
4261
4275
|
var ICEGatheringController = class extends Destroyable {
|
|
4262
4276
|
constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
|
|
4263
4277
|
super();
|
|
@@ -4265,23 +4279,23 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4265
4279
|
this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
|
|
4266
4280
|
this.onicegatheringstatechangeHandler = () => {
|
|
4267
4281
|
const { iceGatheringState } = this.peerConnection;
|
|
4268
|
-
logger$
|
|
4282
|
+
logger$18.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
|
|
4269
4283
|
if (iceGatheringState === "gathering") this._iceCandidatesState.next({
|
|
4270
4284
|
state: "gathering",
|
|
4271
4285
|
validSDP: false
|
|
4272
4286
|
});
|
|
4273
4287
|
};
|
|
4274
4288
|
this.onicecandidateHandler = (event) => {
|
|
4275
|
-
logger$
|
|
4289
|
+
logger$18.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
|
|
4276
4290
|
this.removeTimer("iceCandidateTimer");
|
|
4277
4291
|
if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
|
|
4278
4292
|
if (this.peerConnection.iceGatheringState !== "complete") {
|
|
4279
|
-
logger$
|
|
4293
|
+
logger$18.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
|
|
4280
4294
|
this.handleICECandidateTimeout();
|
|
4281
4295
|
}
|
|
4282
4296
|
}, this.iceCandidateTimeout);
|
|
4283
4297
|
else {
|
|
4284
|
-
logger$
|
|
4298
|
+
logger$18.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
|
|
4285
4299
|
this.removeTimer("iceGatheringTimer");
|
|
4286
4300
|
this.handleICEGatheringComplete();
|
|
4287
4301
|
}
|
|
@@ -4299,7 +4313,7 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4299
4313
|
this.setupEventListeners();
|
|
4300
4314
|
this.iceGatheringTimer = setTimeout(() => {
|
|
4301
4315
|
if (this.peerConnection.iceGatheringState !== "complete") {
|
|
4302
|
-
logger$
|
|
4316
|
+
logger$18.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
|
|
4303
4317
|
this.handleICEGatheringTimeout();
|
|
4304
4318
|
}
|
|
4305
4319
|
}, this.iceGatheringTimeout);
|
|
@@ -4326,9 +4340,9 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4326
4340
|
this.relayOnly = value;
|
|
4327
4341
|
}
|
|
4328
4342
|
handleICEGatheringComplete() {
|
|
4329
|
-
logger$
|
|
4330
|
-
logger$
|
|
4331
|
-
logger$
|
|
4343
|
+
logger$18.debug("[ICEGatheringController] Handling ICE gathering complete");
|
|
4344
|
+
logger$18.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
|
|
4345
|
+
logger$18.debug("[ICEGatheringController] ICE gathering complete");
|
|
4332
4346
|
this._iceCandidatesState.next({
|
|
4333
4347
|
state: "complete",
|
|
4334
4348
|
validSDP: this.hasValidLocalDescriptionSDP
|
|
@@ -4344,21 +4358,21 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4344
4358
|
this.removeTimer("iceGatheringTimer");
|
|
4345
4359
|
const validSDP = this.hasValidLocalDescriptionSDP;
|
|
4346
4360
|
if (validSDP) {
|
|
4347
|
-
logger$
|
|
4361
|
+
logger$18.debug("[ICEGatheringController] Local SDP is valid");
|
|
4348
4362
|
this._iceCandidatesState.next({
|
|
4349
4363
|
state: "timeout",
|
|
4350
4364
|
validSDP
|
|
4351
4365
|
});
|
|
4352
4366
|
this.stopGathering();
|
|
4353
|
-
} else logger$
|
|
4367
|
+
} else logger$18.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
|
|
4354
4368
|
}
|
|
4355
4369
|
handleICECandidateTimeout() {
|
|
4356
4370
|
if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
|
|
4357
|
-
logger$
|
|
4371
|
+
logger$18.warn("[ICEGatheringController] ICE candidate timeout");
|
|
4358
4372
|
const validSDP = this.hasValidLocalDescriptionSDP;
|
|
4359
4373
|
if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
|
|
4360
4374
|
else {
|
|
4361
|
-
logger$
|
|
4375
|
+
logger$18.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
|
|
4362
4376
|
this._iceCandidatesState.next({
|
|
4363
4377
|
state: "timeout",
|
|
4364
4378
|
validSDP
|
|
@@ -4367,7 +4381,7 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4367
4381
|
}
|
|
4368
4382
|
}
|
|
4369
4383
|
restartICEGatheringWithRelayOnly() {
|
|
4370
|
-
logger$
|
|
4384
|
+
logger$18.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
|
|
4371
4385
|
this.relayOnly = true;
|
|
4372
4386
|
this.peerConnection.setConfiguration({
|
|
4373
4387
|
...this.peerConnection.getConfiguration(),
|
|
@@ -4382,7 +4396,7 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4382
4396
|
}
|
|
4383
4397
|
}
|
|
4384
4398
|
clearAllTimers() {
|
|
4385
|
-
logger$
|
|
4399
|
+
logger$18.debug("[ICEGatheringController] Clearing all timers");
|
|
4386
4400
|
this.removeTimer("iceGatheringTimer");
|
|
4387
4401
|
this.removeTimer("iceCandidateTimer");
|
|
4388
4402
|
}
|
|
@@ -4391,7 +4405,7 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4391
4405
|
this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
|
|
4392
4406
|
}
|
|
4393
4407
|
destroy() {
|
|
4394
|
-
logger$
|
|
4408
|
+
logger$18.debug("[ICEGatheringController] Destroying ICEGatheringController");
|
|
4395
4409
|
this.clearAllTimers();
|
|
4396
4410
|
this.removeEventListeners();
|
|
4397
4411
|
super.destroy();
|
|
@@ -4400,7 +4414,7 @@ var ICEGatheringController = class extends Destroyable {
|
|
|
4400
4414
|
|
|
4401
4415
|
//#endregion
|
|
4402
4416
|
//#region src/controllers/LocalStreamController.ts
|
|
4403
|
-
const logger$
|
|
4417
|
+
const logger$17 = getLogger();
|
|
4404
4418
|
var LocalStreamController = class extends Destroyable {
|
|
4405
4419
|
constructor(options) {
|
|
4406
4420
|
super();
|
|
@@ -4438,26 +4452,26 @@ var LocalStreamController = class extends Destroyable {
|
|
|
4438
4452
|
* Build the local media stream based on the provided options.
|
|
4439
4453
|
*/
|
|
4440
4454
|
async buildLocalStream() {
|
|
4441
|
-
logger$
|
|
4455
|
+
logger$17.debug("[LocalStreamController] Building local media stream.");
|
|
4442
4456
|
let stream;
|
|
4443
4457
|
if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
|
|
4444
4458
|
const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
|
|
4445
4459
|
stream = new MediaStream(tracks);
|
|
4446
4460
|
} else if (this.options.propose === "screenshare") {
|
|
4447
|
-
logger$
|
|
4461
|
+
logger$17.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
|
|
4448
4462
|
stream = await this.options.getDisplayMedia({
|
|
4449
4463
|
video: true,
|
|
4450
4464
|
audio: Boolean(this.options.inputAudioDeviceConstraints)
|
|
4451
4465
|
});
|
|
4452
|
-
logger$
|
|
4466
|
+
logger$17.debug("[LocalStreamController] Screen share media obtained:", stream);
|
|
4453
4467
|
} else {
|
|
4454
4468
|
const constraints = {
|
|
4455
4469
|
audio: this.options.inputAudioDeviceConstraints,
|
|
4456
4470
|
video: this.options.inputVideoDeviceConstraints
|
|
4457
4471
|
};
|
|
4458
|
-
logger$
|
|
4472
|
+
logger$17.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
|
|
4459
4473
|
stream = await this.options.getUserMedia(constraints);
|
|
4460
|
-
logger$
|
|
4474
|
+
logger$17.debug("[LocalStreamController] User media obtained:", stream);
|
|
4461
4475
|
}
|
|
4462
4476
|
this._localStream$.next(stream);
|
|
4463
4477
|
return stream;
|
|
@@ -4474,7 +4488,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
4474
4488
|
this._localStream$.next(localStream);
|
|
4475
4489
|
if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
|
|
4476
4490
|
else this._localAudioTracks$.next(localStream.getAudioTracks());
|
|
4477
|
-
logger$
|
|
4491
|
+
logger$17.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
|
|
4478
4492
|
return localStream;
|
|
4479
4493
|
}
|
|
4480
4494
|
/**
|
|
@@ -4486,7 +4500,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
4486
4500
|
const stream = this._localStream$.value;
|
|
4487
4501
|
const track = stream?.getTracks().find((t) => t.id === trackId);
|
|
4488
4502
|
if (!track) {
|
|
4489
|
-
logger$
|
|
4503
|
+
logger$17.debug(`[LocalStreamController] track not found: ${trackId}`);
|
|
4490
4504
|
return;
|
|
4491
4505
|
}
|
|
4492
4506
|
track.removeEventListener("ended", this.mediaTrackEndedHandler);
|
|
@@ -4495,7 +4509,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
4495
4509
|
this._localStream$.next(stream);
|
|
4496
4510
|
if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
|
|
4497
4511
|
else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
|
|
4498
|
-
logger$
|
|
4512
|
+
logger$17.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
|
|
4499
4513
|
return track;
|
|
4500
4514
|
}
|
|
4501
4515
|
/**
|
|
@@ -4530,7 +4544,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
4530
4544
|
*/
|
|
4531
4545
|
stopAllTracks() {
|
|
4532
4546
|
this._localStream$.value?.getTracks().forEach((track) => {
|
|
4533
|
-
logger$
|
|
4547
|
+
logger$17.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
|
|
4534
4548
|
track.removeEventListener("ended", this.mediaTrackEndedHandler);
|
|
4535
4549
|
track.stop();
|
|
4536
4550
|
});
|
|
@@ -4546,7 +4560,7 @@ var LocalStreamController = class extends Destroyable {
|
|
|
4546
4560
|
|
|
4547
4561
|
//#endregion
|
|
4548
4562
|
//#region src/controllers/TransceiverController.ts
|
|
4549
|
-
const logger$
|
|
4563
|
+
const logger$16 = getLogger();
|
|
4550
4564
|
const getDirection = (send, recv) => {
|
|
4551
4565
|
if (send && recv) return "sendrecv";
|
|
4552
4566
|
else if (send && !recv) return "sendonly";
|
|
@@ -4648,7 +4662,7 @@ var TransceiverController = class extends Destroyable {
|
|
|
4648
4662
|
sendEncodings: isAudio ? void 0 : this.sendEncodings,
|
|
4649
4663
|
streams: direction === "recvonly" ? void 0 : [localStream]
|
|
4650
4664
|
};
|
|
4651
|
-
logger$
|
|
4665
|
+
logger$16.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
|
|
4652
4666
|
transceiver,
|
|
4653
4667
|
transceiverParams
|
|
4654
4668
|
});
|
|
@@ -4656,11 +4670,11 @@ var TransceiverController = class extends Destroyable {
|
|
|
4656
4670
|
await transceiver.sender.replaceTrack(track);
|
|
4657
4671
|
transceiver.direction = transceiverParams.direction;
|
|
4658
4672
|
if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
|
|
4659
|
-
logger$
|
|
4673
|
+
logger$16.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
|
|
4660
4674
|
transceiver.sender.setStreams(...transceiverParams.streams);
|
|
4661
4675
|
}
|
|
4662
4676
|
} else {
|
|
4663
|
-
logger$
|
|
4677
|
+
logger$16.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
|
|
4664
4678
|
this.peerConnection.addTransceiver(track, transceiverParams);
|
|
4665
4679
|
}
|
|
4666
4680
|
}
|
|
@@ -4674,13 +4688,13 @@ var TransceiverController = class extends Destroyable {
|
|
|
4674
4688
|
if (options.updateTransceiverDirection) transceiver.direction = "inactive";
|
|
4675
4689
|
}
|
|
4676
4690
|
} catch (error) {
|
|
4677
|
-
logger$
|
|
4691
|
+
logger$16.error("[TransceiverController] stopTrackSender error", kind, error);
|
|
4678
4692
|
this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
|
|
4679
4693
|
}
|
|
4680
4694
|
}
|
|
4681
4695
|
async restoreTrackSender(kind) {
|
|
4682
4696
|
try {
|
|
4683
|
-
logger$
|
|
4697
|
+
logger$16.debug("[TransceiverController] restoreTrackSender called", kind);
|
|
4684
4698
|
const constraints = {};
|
|
4685
4699
|
const transceivers = this.transceiverByKind(kind);
|
|
4686
4700
|
for (const transceiver of transceivers) {
|
|
@@ -4690,23 +4704,23 @@ var TransceiverController = class extends Destroyable {
|
|
|
4690
4704
|
if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
|
|
4691
4705
|
}
|
|
4692
4706
|
}
|
|
4693
|
-
logger$
|
|
4707
|
+
logger$16.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
|
|
4694
4708
|
if (Object.keys(constraints).length === 0) {
|
|
4695
|
-
logger$
|
|
4709
|
+
logger$16.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
|
|
4696
4710
|
return;
|
|
4697
4711
|
}
|
|
4698
4712
|
const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
|
|
4699
|
-
logger$
|
|
4713
|
+
logger$16.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
|
|
4700
4714
|
for (const newTrack of newTracks) {
|
|
4701
4715
|
this.options.localStreamController.addTrack(newTrack);
|
|
4702
4716
|
const trackKind = newTrack.kind;
|
|
4703
4717
|
const transceiverOfKind = this.transceiverByKind(trackKind)[0];
|
|
4704
4718
|
transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
|
|
4705
|
-
logger$
|
|
4719
|
+
logger$16.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
|
|
4706
4720
|
await transceiverOfKind.sender.replaceTrack(newTrack);
|
|
4707
4721
|
}
|
|
4708
4722
|
} catch (error) {
|
|
4709
|
-
logger$
|
|
4723
|
+
logger$16.error("[TransceiverController] restoreTrackSender error", kind, error);
|
|
4710
4724
|
this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
|
|
4711
4725
|
}
|
|
4712
4726
|
}
|
|
@@ -4747,14 +4761,14 @@ var TransceiverController = class extends Destroyable {
|
|
|
4747
4761
|
};
|
|
4748
4762
|
try {
|
|
4749
4763
|
await track.applyConstraints(constraintsToApply);
|
|
4750
|
-
logger$
|
|
4751
|
-
logger$
|
|
4764
|
+
logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
|
|
4765
|
+
logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
|
|
4752
4766
|
} catch (error) {
|
|
4753
|
-
logger$
|
|
4767
|
+
logger$16.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
|
|
4754
4768
|
try {
|
|
4755
4769
|
await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
|
|
4756
4770
|
} catch (fallbackError) {
|
|
4757
|
-
logger$
|
|
4771
|
+
logger$16.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
|
|
4758
4772
|
this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
|
|
4759
4773
|
}
|
|
4760
4774
|
}
|
|
@@ -4782,7 +4796,7 @@ var TransceiverController = class extends Destroyable {
|
|
|
4782
4796
|
if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
|
|
4783
4797
|
await sender.replaceTrack(newTrack);
|
|
4784
4798
|
this.options.localStreamController.addTrack(newTrack);
|
|
4785
|
-
logger$
|
|
4799
|
+
logger$16.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
|
|
4786
4800
|
}
|
|
4787
4801
|
getMediaDirections() {
|
|
4788
4802
|
if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
|
|
@@ -4812,7 +4826,7 @@ var TransceiverController = class extends Destroyable {
|
|
|
4812
4826
|
|
|
4813
4827
|
//#endregion
|
|
4814
4828
|
//#region src/controllers/RTCPeerConnectionController.ts
|
|
4815
|
-
const logger$
|
|
4829
|
+
const logger$15 = getLogger();
|
|
4816
4830
|
var RTCPeerConnectionController = class extends Destroyable {
|
|
4817
4831
|
constructor(options = {}, remoteSessionDescription, deviceController) {
|
|
4818
4832
|
super();
|
|
@@ -4828,43 +4842,43 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
4828
4842
|
this.oniceconnectionstatechangeHandler = () => {
|
|
4829
4843
|
if (this.peerConnection) {
|
|
4830
4844
|
const { iceConnectionState } = this.peerConnection;
|
|
4831
|
-
logger$
|
|
4845
|
+
logger$15.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
|
|
4832
4846
|
this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
|
|
4833
4847
|
}
|
|
4834
4848
|
};
|
|
4835
4849
|
this.onconnectionstatechangeHandler = () => {
|
|
4836
4850
|
if (this.peerConnection) {
|
|
4837
4851
|
const { connectionState } = this.peerConnection;
|
|
4838
|
-
logger$
|
|
4852
|
+
logger$15.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
|
|
4839
4853
|
if (connectionState === "connected") this.removeConnectionTimer();
|
|
4840
4854
|
this._connectionState$.next(this.peerConnection.connectionState);
|
|
4841
4855
|
}
|
|
4842
4856
|
};
|
|
4843
4857
|
this.onsignalingstatechangeHandler = () => {
|
|
4844
|
-
logger$
|
|
4858
|
+
logger$15.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
|
|
4845
4859
|
};
|
|
4846
4860
|
this.onicegatheringstatechangeHandler = () => {
|
|
4847
4861
|
if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
|
|
4848
4862
|
};
|
|
4849
4863
|
this.onnegotiationneededHandler = (event) => {
|
|
4850
|
-
logger$
|
|
4864
|
+
logger$15.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
|
|
4851
4865
|
this.negotiationNeeded$.next();
|
|
4852
4866
|
};
|
|
4853
4867
|
this.updateSelectedInputDevice = async (kind, deviceInfo) => {
|
|
4854
4868
|
try {
|
|
4855
4869
|
const { localStream } = this;
|
|
4856
4870
|
if (!localStream) {
|
|
4857
|
-
logger$
|
|
4871
|
+
logger$15.warn("[RTCPeerConnectionController] No local stream available to update input device.");
|
|
4858
4872
|
return;
|
|
4859
4873
|
}
|
|
4860
|
-
logger$
|
|
4874
|
+
logger$15.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
|
|
4861
4875
|
const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
|
|
4862
4876
|
if (track) {
|
|
4863
4877
|
this.transceiverController?.stopTrackSender(kind);
|
|
4864
4878
|
this.localStream?.removeTrack(track);
|
|
4865
|
-
logger$
|
|
4879
|
+
logger$15.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
|
|
4866
4880
|
if (!deviceInfo) {
|
|
4867
|
-
logger$
|
|
4881
|
+
logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
|
|
4868
4882
|
return;
|
|
4869
4883
|
}
|
|
4870
4884
|
const streamTrack = (await this.getUserMedia({ [kind]: {
|
|
@@ -4872,15 +4886,15 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
4872
4886
|
...this.deviceController.deviceInfoToConstraints(deviceInfo)
|
|
4873
4887
|
} })).getTracks().find((t) => t.kind === kind);
|
|
4874
4888
|
if (streamTrack) {
|
|
4875
|
-
logger$
|
|
4889
|
+
logger$15.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
|
|
4876
4890
|
this.localStream?.addTrack(streamTrack);
|
|
4877
4891
|
await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
|
|
4878
|
-
logger$
|
|
4892
|
+
logger$15.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
|
|
4879
4893
|
}
|
|
4880
4894
|
}
|
|
4881
|
-
logger$
|
|
4895
|
+
logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
|
|
4882
4896
|
} catch (error) {
|
|
4883
|
-
logger$
|
|
4897
|
+
logger$15.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
|
|
4884
4898
|
this._errors$.next(error);
|
|
4885
4899
|
throw error;
|
|
4886
4900
|
}
|
|
@@ -5117,15 +5131,15 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5117
5131
|
this.setupPeerConnection();
|
|
5118
5132
|
this.subscribeTo(this.negotiationNeeded$.pipe(auditTime(0), exhaustMap(async () => this.startNegotiation())), {
|
|
5119
5133
|
next: () => {
|
|
5120
|
-
logger$
|
|
5134
|
+
logger$15.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
|
|
5121
5135
|
},
|
|
5122
5136
|
error: (error) => {
|
|
5123
|
-
logger$
|
|
5137
|
+
logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
|
|
5124
5138
|
this._errors$.next(error);
|
|
5125
5139
|
}
|
|
5126
5140
|
});
|
|
5127
5141
|
this.subscribeTo(merge(this.deviceController.selectedAudioInputDevice$.pipe(map((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe(map((deviceInfo) => ["video", deviceInfo]))).pipe(skipWhile(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
|
|
5128
|
-
logger$
|
|
5142
|
+
logger$15.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
|
|
5129
5143
|
kind,
|
|
5130
5144
|
deviceInfo
|
|
5131
5145
|
});
|
|
@@ -5142,7 +5156,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5142
5156
|
this._initialized$.next(true);
|
|
5143
5157
|
}
|
|
5144
5158
|
} catch (error) {
|
|
5145
|
-
logger$
|
|
5159
|
+
logger$15.error("[RTCPeerConnectionController] Initialization error:", error);
|
|
5146
5160
|
this._errors$.next(error);
|
|
5147
5161
|
this.destroy();
|
|
5148
5162
|
}
|
|
@@ -5174,22 +5188,22 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5174
5188
|
}
|
|
5175
5189
|
async startNegotiation() {
|
|
5176
5190
|
if (this.isNegotiating) {
|
|
5177
|
-
logger$
|
|
5191
|
+
logger$15.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
|
|
5178
5192
|
return;
|
|
5179
5193
|
}
|
|
5180
5194
|
this.setupEventListeners();
|
|
5181
5195
|
if (this.type === "answer") {
|
|
5182
|
-
logger$
|
|
5196
|
+
logger$15.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
|
|
5183
5197
|
return;
|
|
5184
5198
|
}
|
|
5185
5199
|
this._isNegotiating$.next(true);
|
|
5186
|
-
logger$
|
|
5200
|
+
logger$15.debug("[RTCPeerConnectionController] Starting negotiation.");
|
|
5187
5201
|
try {
|
|
5188
5202
|
const { offerOptions } = this;
|
|
5189
|
-
logger$
|
|
5203
|
+
logger$15.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
|
|
5190
5204
|
await this.createOffer(offerOptions);
|
|
5191
5205
|
} catch (error) {
|
|
5192
|
-
logger$
|
|
5206
|
+
logger$15.error("[RTCPeerConnectionController] Error during negotiation:", error);
|
|
5193
5207
|
this._errors$.next(error);
|
|
5194
5208
|
}
|
|
5195
5209
|
}
|
|
@@ -5205,14 +5219,14 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5205
5219
|
let readyToConnect = status !== "failed";
|
|
5206
5220
|
try {
|
|
5207
5221
|
if (status === "received" && sdp) {
|
|
5208
|
-
logger$
|
|
5222
|
+
logger$15.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
|
|
5209
5223
|
await this._setRemoteDescription({
|
|
5210
5224
|
type: "answer",
|
|
5211
5225
|
sdp
|
|
5212
5226
|
});
|
|
5213
5227
|
}
|
|
5214
5228
|
} catch (error) {
|
|
5215
|
-
logger$
|
|
5229
|
+
logger$15.error("[RTCPeerConnectionController] Error updating answer status:", error);
|
|
5216
5230
|
this._errors$.next(error);
|
|
5217
5231
|
readyToConnect = false;
|
|
5218
5232
|
} finally {
|
|
@@ -5231,7 +5245,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5231
5245
|
await this.handleOfferReceived();
|
|
5232
5246
|
break;
|
|
5233
5247
|
case "failed":
|
|
5234
|
-
logger$
|
|
5248
|
+
logger$15.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
|
|
5235
5249
|
break;
|
|
5236
5250
|
case "sent":
|
|
5237
5251
|
default:
|
|
@@ -5263,7 +5277,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5263
5277
|
}
|
|
5264
5278
|
await this.setupLocalTracks();
|
|
5265
5279
|
const { answerOptions } = this;
|
|
5266
|
-
logger$
|
|
5280
|
+
logger$15.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
|
|
5267
5281
|
await this.createAnswer(answerOptions);
|
|
5268
5282
|
}
|
|
5269
5283
|
async handleOfferReceived() {
|
|
@@ -5271,7 +5285,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5271
5285
|
this._isNegotiating$.next(true);
|
|
5272
5286
|
await this._setRemoteDescription(this.sdpInit);
|
|
5273
5287
|
const { answerOptions } = this;
|
|
5274
|
-
logger$
|
|
5288
|
+
logger$15.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
|
|
5275
5289
|
await this.createAnswer(answerOptions);
|
|
5276
5290
|
}
|
|
5277
5291
|
readyToConnect() {
|
|
@@ -5279,7 +5293,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5279
5293
|
this.connectionTimer = setTimeout(() => {
|
|
5280
5294
|
this.removeConnectionTimer();
|
|
5281
5295
|
if (this.peerConnection?.connectionState !== "connected") {
|
|
5282
|
-
logger$
|
|
5296
|
+
logger$15.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
|
|
5283
5297
|
this.iceGatheringController.restartICEGatheringWithRelayOnly();
|
|
5284
5298
|
}
|
|
5285
5299
|
}, this.connectionTimeout);
|
|
@@ -5301,14 +5315,14 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5301
5315
|
const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
|
|
5302
5316
|
if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
|
|
5303
5317
|
result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
|
|
5304
|
-
logger$
|
|
5318
|
+
logger$15.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
|
|
5305
5319
|
preferredAudioCodecs,
|
|
5306
5320
|
preferredVideoCodecs
|
|
5307
5321
|
});
|
|
5308
5322
|
}
|
|
5309
5323
|
if (stereo) {
|
|
5310
5324
|
result = enableStereoOpus(result);
|
|
5311
|
-
logger$
|
|
5325
|
+
logger$15.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
|
|
5312
5326
|
}
|
|
5313
5327
|
return Promise.resolve(result);
|
|
5314
5328
|
}
|
|
@@ -5367,18 +5381,18 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5367
5381
|
...this.peerConnection.getConfiguration(),
|
|
5368
5382
|
iceTransportPolicy: "relay"
|
|
5369
5383
|
});
|
|
5370
|
-
logger$
|
|
5384
|
+
logger$15.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
|
|
5371
5385
|
} catch (error) {
|
|
5372
|
-
logger$
|
|
5386
|
+
logger$15.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
|
|
5373
5387
|
}
|
|
5374
5388
|
this.setupEventListeners();
|
|
5375
5389
|
this._isNegotiating$.next(true);
|
|
5376
|
-
logger$
|
|
5390
|
+
logger$15.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
|
|
5377
5391
|
try {
|
|
5378
5392
|
const offer = await this.peerConnection.createOffer({ iceRestart: true });
|
|
5379
5393
|
await this.setLocalDescription(offer);
|
|
5380
5394
|
} catch (error) {
|
|
5381
|
-
logger$
|
|
5395
|
+
logger$15.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
|
|
5382
5396
|
this._errors$.next(error);
|
|
5383
5397
|
this.negotiationEnded();
|
|
5384
5398
|
if (policyChanged) this.restoreIceTransportPolicy();
|
|
@@ -5392,9 +5406,9 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5392
5406
|
...this.peerConnection.getConfiguration(),
|
|
5393
5407
|
iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
|
|
5394
5408
|
});
|
|
5395
|
-
logger$
|
|
5409
|
+
logger$15.debug("[RTCPeerConnectionController] ICE transport policy restored");
|
|
5396
5410
|
} catch (error) {
|
|
5397
|
-
logger$
|
|
5411
|
+
logger$15.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
|
|
5398
5412
|
}
|
|
5399
5413
|
}
|
|
5400
5414
|
/**
|
|
@@ -5406,13 +5420,13 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5406
5420
|
await this.setupRemoteTracks();
|
|
5407
5421
|
}
|
|
5408
5422
|
async setupLocalTracks() {
|
|
5409
|
-
logger$
|
|
5423
|
+
logger$15.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
|
|
5410
5424
|
const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
|
|
5411
5425
|
if (this.transceiverController?.useAddStream ?? false) {
|
|
5412
|
-
logger$
|
|
5426
|
+
logger$15.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
|
|
5413
5427
|
this.peerConnection?.addStream(localStream);
|
|
5414
5428
|
if (!this.isNegotiating) {
|
|
5415
|
-
logger$
|
|
5429
|
+
logger$15.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
|
|
5416
5430
|
this.negotiationNeeded$.next();
|
|
5417
5431
|
}
|
|
5418
5432
|
return;
|
|
@@ -5428,7 +5442,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5428
5442
|
const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
|
|
5429
5443
|
await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
|
|
5430
5444
|
} else {
|
|
5431
|
-
logger$
|
|
5445
|
+
logger$15.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
|
|
5432
5446
|
this.peerConnection?.addTrack(track, localStream);
|
|
5433
5447
|
}
|
|
5434
5448
|
}
|
|
@@ -5445,7 +5459,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5445
5459
|
async setupRemoteTracks() {
|
|
5446
5460
|
if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
|
|
5447
5461
|
this.peerConnection.ontrack = (event) => {
|
|
5448
|
-
logger$
|
|
5462
|
+
logger$15.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
|
|
5449
5463
|
if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
|
|
5450
5464
|
else {
|
|
5451
5465
|
const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
|
|
@@ -5471,9 +5485,9 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5471
5485
|
try {
|
|
5472
5486
|
const localStream = this.localStreamController.addTrack(track);
|
|
5473
5487
|
this.peerConnection.addTrack(track, localStream);
|
|
5474
|
-
logger$
|
|
5488
|
+
logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
|
|
5475
5489
|
} catch (error) {
|
|
5476
|
-
logger$
|
|
5490
|
+
logger$15.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
|
|
5477
5491
|
this._errors$.next(error);
|
|
5478
5492
|
throw error;
|
|
5479
5493
|
}
|
|
@@ -5490,15 +5504,15 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5490
5504
|
}
|
|
5491
5505
|
const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
|
|
5492
5506
|
if (!sender) {
|
|
5493
|
-
logger$
|
|
5507
|
+
logger$15.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
|
|
5494
5508
|
return;
|
|
5495
5509
|
}
|
|
5496
5510
|
try {
|
|
5497
5511
|
this.peerConnection.removeTrack(sender);
|
|
5498
5512
|
this.localStreamController.removeTrack(trackId);
|
|
5499
|
-
logger$
|
|
5513
|
+
logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
|
|
5500
5514
|
} catch (error) {
|
|
5501
|
-
logger$
|
|
5515
|
+
logger$15.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
|
|
5502
5516
|
this._errors$.next(error);
|
|
5503
5517
|
throw error;
|
|
5504
5518
|
}
|
|
@@ -5525,7 +5539,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5525
5539
|
async replaceAudioTrackWithConstraints(constraints) {
|
|
5526
5540
|
const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
|
|
5527
5541
|
if (!senders || senders.length === 0) {
|
|
5528
|
-
logger$
|
|
5542
|
+
logger$15.warn("[RTCPeerConnectionController] No live audio sender to replace");
|
|
5529
5543
|
return;
|
|
5530
5544
|
}
|
|
5531
5545
|
for (const sender of senders) {
|
|
@@ -5543,7 +5557,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5543
5557
|
const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
|
|
5544
5558
|
await sender.replaceTrack(newTrack);
|
|
5545
5559
|
this.localStreamController.addTrack(newTrack);
|
|
5546
|
-
logger$
|
|
5560
|
+
logger$15.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
|
|
5547
5561
|
}
|
|
5548
5562
|
}
|
|
5549
5563
|
/**
|
|
@@ -5551,7 +5565,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5551
5565
|
* Completes all observables to prevent memory leaks.
|
|
5552
5566
|
*/
|
|
5553
5567
|
destroy() {
|
|
5554
|
-
logger$
|
|
5568
|
+
logger$15.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
|
|
5555
5569
|
this.removeConnectionTimer();
|
|
5556
5570
|
this._iceGatheringController?.destroy();
|
|
5557
5571
|
this.localStreamController.destroy();
|
|
@@ -5575,7 +5589,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5575
5589
|
}
|
|
5576
5590
|
stopRemoteTracks() {
|
|
5577
5591
|
this._remoteStream$.value?.getTracks().forEach((track) => {
|
|
5578
|
-
logger$
|
|
5592
|
+
logger$15.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
|
|
5579
5593
|
track.stop();
|
|
5580
5594
|
});
|
|
5581
5595
|
}
|
|
@@ -5592,7 +5606,7 @@ var RTCPeerConnectionController = class extends Destroyable {
|
|
|
5592
5606
|
...params,
|
|
5593
5607
|
sdp: finalRemote
|
|
5594
5608
|
};
|
|
5595
|
-
logger$
|
|
5609
|
+
logger$15.debug("[RTCPeerConnectionController] Setting remote description:", answer);
|
|
5596
5610
|
return this.peerConnection.setRemoteDescription(answer);
|
|
5597
5611
|
}
|
|
5598
5612
|
};
|
|
@@ -5630,7 +5644,7 @@ function isVertoPingInnerParams(value) {
|
|
|
5630
5644
|
|
|
5631
5645
|
//#endregion
|
|
5632
5646
|
//#region src/managers/VertoManager.ts
|
|
5633
|
-
const logger$
|
|
5647
|
+
const logger$14 = getLogger();
|
|
5634
5648
|
var VertoManager = class extends Destroyable {
|
|
5635
5649
|
constructor(callSession) {
|
|
5636
5650
|
super();
|
|
@@ -5656,6 +5670,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5656
5670
|
this._screenShareTimeoutMs = 5e4;
|
|
5657
5671
|
this._nodeId$ = this.createBehaviorSubject(options.nodeId ?? null);
|
|
5658
5672
|
this.onError = options.onError;
|
|
5673
|
+
this.onModifyFailed = options.onModifyFailed;
|
|
5659
5674
|
this.initSubscriptions();
|
|
5660
5675
|
this.initMainPeerConnection();
|
|
5661
5676
|
}
|
|
@@ -5668,7 +5683,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5668
5683
|
try {
|
|
5669
5684
|
await this.executeVerto(vertoModifyMessage);
|
|
5670
5685
|
} catch (error) {
|
|
5671
|
-
logger$
|
|
5686
|
+
logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
|
|
5672
5687
|
throw error;
|
|
5673
5688
|
}
|
|
5674
5689
|
}
|
|
@@ -5681,7 +5696,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5681
5696
|
try {
|
|
5682
5697
|
await this.executeVerto(vertoModifyMessage);
|
|
5683
5698
|
} catch (error) {
|
|
5684
|
-
logger$
|
|
5699
|
+
logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
|
|
5685
5700
|
throw error;
|
|
5686
5701
|
}
|
|
5687
5702
|
}
|
|
@@ -5734,7 +5749,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5734
5749
|
if (event.member_id) this.setSelfIdIfNull(event.member_id);
|
|
5735
5750
|
});
|
|
5736
5751
|
this.subscribeTo(this.vertoMedia$, (event) => {
|
|
5737
|
-
logger$
|
|
5752
|
+
logger$14.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
|
|
5738
5753
|
this._signalingStatus$.next("ringing");
|
|
5739
5754
|
const { sdp, callID } = event;
|
|
5740
5755
|
this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
|
|
@@ -5743,7 +5758,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5743
5758
|
});
|
|
5744
5759
|
});
|
|
5745
5760
|
this.subscribeTo(this.vertoAnswer$, (event) => {
|
|
5746
|
-
logger$
|
|
5761
|
+
logger$14.debug("[WebRTCManager] Received Verto answer event:", event);
|
|
5747
5762
|
this._signalingStatus$.next("connecting");
|
|
5748
5763
|
const { sdp, callID } = event;
|
|
5749
5764
|
this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
|
|
@@ -5752,7 +5767,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5752
5767
|
});
|
|
5753
5768
|
});
|
|
5754
5769
|
this.subscribeTo(this.vertoMediaParams$, (event) => {
|
|
5755
|
-
logger$
|
|
5770
|
+
logger$14.debug("[WebRTCManager] Received Verto mediaParams event:", event);
|
|
5756
5771
|
const { mediaParams, callID } = event;
|
|
5757
5772
|
const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
|
|
5758
5773
|
const { audio, video } = mediaParams;
|
|
@@ -5766,13 +5781,13 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5766
5781
|
timestamp: Date.now()
|
|
5767
5782
|
});
|
|
5768
5783
|
} catch (error) {
|
|
5769
|
-
logger$
|
|
5784
|
+
logger$14.warn("[WebRTCManager] Error applying server-pushed media params:", error);
|
|
5770
5785
|
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
5771
5786
|
}
|
|
5772
5787
|
})();
|
|
5773
5788
|
});
|
|
5774
5789
|
this.subscribeTo(this.vertoPing$, (vertoPing) => {
|
|
5775
|
-
this.attachManager.attach(this.
|
|
5790
|
+
this.attachManager.attach(this.buildAttachableCall());
|
|
5776
5791
|
this.sendVertoPong(vertoPing);
|
|
5777
5792
|
});
|
|
5778
5793
|
}
|
|
@@ -5788,13 +5803,13 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5788
5803
|
*/
|
|
5789
5804
|
setNodeIdIfNull(nodeId) {
|
|
5790
5805
|
if (!this._nodeId$.value && nodeId) {
|
|
5791
|
-
logger$
|
|
5806
|
+
logger$14.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
|
|
5792
5807
|
this._nodeId$.next(nodeId);
|
|
5793
5808
|
}
|
|
5794
5809
|
}
|
|
5795
5810
|
setSelfIdIfNull(selfId) {
|
|
5796
5811
|
if (!this._selfId$.value && selfId) {
|
|
5797
|
-
logger$
|
|
5812
|
+
logger$14.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
|
|
5798
5813
|
this._selfId$.next(selfId);
|
|
5799
5814
|
}
|
|
5800
5815
|
}
|
|
@@ -5803,7 +5818,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5803
5818
|
const vertoPongMessage = VertoPong({ ...vertoPing });
|
|
5804
5819
|
await this.executeVerto(vertoPongMessage);
|
|
5805
5820
|
} catch (error) {
|
|
5806
|
-
logger$
|
|
5821
|
+
logger$14.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
|
|
5807
5822
|
this.onError?.(new VertoPongError(error));
|
|
5808
5823
|
}
|
|
5809
5824
|
}
|
|
@@ -5813,7 +5828,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5813
5828
|
if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
|
|
5814
5829
|
if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
|
|
5815
5830
|
} catch (error) {
|
|
5816
|
-
logger$
|
|
5831
|
+
logger$14.warn("[WebRTCManager] Error updating media constraints:", error);
|
|
5817
5832
|
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
5818
5833
|
throw error;
|
|
5819
5834
|
}
|
|
@@ -5821,6 +5836,15 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5821
5836
|
get selfId() {
|
|
5822
5837
|
return this._selfId$.value;
|
|
5823
5838
|
}
|
|
5839
|
+
/** Build an AttachableCall from the current call state. */
|
|
5840
|
+
buildAttachableCall(idOverride) {
|
|
5841
|
+
return {
|
|
5842
|
+
nodeId: this.nodeId ?? void 0,
|
|
5843
|
+
id: idOverride ?? this.webRtcCallSession.id,
|
|
5844
|
+
to: this.webRtcCallSession.to,
|
|
5845
|
+
mediaDirections: this.webRtcCallSession.mediaDirections
|
|
5846
|
+
};
|
|
5847
|
+
}
|
|
5824
5848
|
/**
|
|
5825
5849
|
* Request a video keyframe via RTCP PLI/FIR.
|
|
5826
5850
|
*
|
|
@@ -5834,20 +5858,20 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5834
5858
|
try {
|
|
5835
5859
|
const pc = this.mainPeerConnection.peerConnection;
|
|
5836
5860
|
if (!pc) {
|
|
5837
|
-
logger$
|
|
5861
|
+
logger$14.warn("[WebRTCManager] No peer connection for keyframe request");
|
|
5838
5862
|
return;
|
|
5839
5863
|
}
|
|
5840
5864
|
const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
|
|
5841
5865
|
if (!videoReceiver) {
|
|
5842
|
-
logger$
|
|
5866
|
+
logger$14.warn("[WebRTCManager] No video receiver for keyframe request");
|
|
5843
5867
|
return;
|
|
5844
5868
|
}
|
|
5845
5869
|
if (typeof videoReceiver.requestKeyFrame === "function") {
|
|
5846
5870
|
videoReceiver.requestKeyFrame();
|
|
5847
|
-
logger$
|
|
5848
|
-
} else logger$
|
|
5871
|
+
logger$14.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
|
|
5872
|
+
} else logger$14.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
|
|
5849
5873
|
} catch (error) {
|
|
5850
|
-
logger$
|
|
5874
|
+
logger$14.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
|
|
5851
5875
|
}
|
|
5852
5876
|
}
|
|
5853
5877
|
/**
|
|
@@ -5865,13 +5889,13 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5865
5889
|
try {
|
|
5866
5890
|
const controller = this.mainPeerConnection;
|
|
5867
5891
|
if (!controller.peerConnection) {
|
|
5868
|
-
logger$
|
|
5892
|
+
logger$14.warn("[WebRTCManager] No peer connection for ICE restart");
|
|
5869
5893
|
return;
|
|
5870
5894
|
}
|
|
5871
5895
|
await controller.triggerIceRestart(relayOnly);
|
|
5872
|
-
logger$
|
|
5896
|
+
logger$14.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
|
|
5873
5897
|
} catch (error) {
|
|
5874
|
-
logger$
|
|
5898
|
+
logger$14.error("[WebRTCManager] ICE restart failed:", error);
|
|
5875
5899
|
throw error;
|
|
5876
5900
|
}
|
|
5877
5901
|
}
|
|
@@ -5889,13 +5913,13 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5889
5913
|
const entries = Array.from(this._rtcPeerConnectionsMap.entries());
|
|
5890
5914
|
for (const [id, controller] of entries) try {
|
|
5891
5915
|
if (!controller.peerConnection) {
|
|
5892
|
-
logger$
|
|
5916
|
+
logger$14.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
|
|
5893
5917
|
continue;
|
|
5894
5918
|
}
|
|
5895
5919
|
await controller.triggerIceRestart(relayOnly);
|
|
5896
|
-
logger$
|
|
5920
|
+
logger$14.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
|
|
5897
5921
|
} catch (error) {
|
|
5898
|
-
logger$
|
|
5922
|
+
logger$14.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
|
|
5899
5923
|
}
|
|
5900
5924
|
}
|
|
5901
5925
|
/**
|
|
@@ -5907,7 +5931,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5907
5931
|
requestKeyframeAll() {
|
|
5908
5932
|
for (const [id, controller] of this._rtcPeerConnectionsMap) {
|
|
5909
5933
|
if (controller.isScreenShare) {
|
|
5910
|
-
logger$
|
|
5934
|
+
logger$14.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
|
|
5911
5935
|
continue;
|
|
5912
5936
|
}
|
|
5913
5937
|
try {
|
|
@@ -5917,10 +5941,10 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5917
5941
|
if (!videoReceiver) continue;
|
|
5918
5942
|
if (typeof videoReceiver.requestKeyFrame === "function") {
|
|
5919
5943
|
videoReceiver.requestKeyFrame();
|
|
5920
|
-
logger$
|
|
5944
|
+
logger$14.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
|
|
5921
5945
|
}
|
|
5922
5946
|
} catch (error) {
|
|
5923
|
-
logger$
|
|
5947
|
+
logger$14.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
|
|
5924
5948
|
}
|
|
5925
5949
|
}
|
|
5926
5950
|
}
|
|
@@ -5968,7 +5992,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5968
5992
|
}
|
|
5969
5993
|
async sendLocalDescription(message, rtcPeerConnController) {
|
|
5970
5994
|
const vertoMethod = message.method;
|
|
5971
|
-
const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController,
|
|
5995
|
+
const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController, message);
|
|
5972
5996
|
try {
|
|
5973
5997
|
const response = await this.executeVerto(message, optionalsParams);
|
|
5974
5998
|
switch (vertoMethod) {
|
|
@@ -5981,8 +6005,9 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5981
6005
|
default:
|
|
5982
6006
|
}
|
|
5983
6007
|
} catch (error) {
|
|
5984
|
-
logger$
|
|
6008
|
+
logger$14.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
|
|
5985
6009
|
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
6010
|
+
if (vertoMethod === "verto.modify") this.onModifyFailed?.();
|
|
5986
6011
|
}
|
|
5987
6012
|
}
|
|
5988
6013
|
async processModifyResponse(response, rtcPeerConnController) {
|
|
@@ -5995,7 +6020,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
5995
6020
|
sdp
|
|
5996
6021
|
});
|
|
5997
6022
|
} catch (error) {
|
|
5998
|
-
logger$
|
|
6023
|
+
logger$14.warn("[WebRTCManager] Error processing modify response:", error);
|
|
5999
6024
|
const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
6000
6025
|
this.onError?.(modifyError);
|
|
6001
6026
|
}
|
|
@@ -6007,19 +6032,24 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6007
6032
|
this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
|
|
6008
6033
|
const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
|
|
6009
6034
|
const callId = getValueFrom(response, "result.result.result.callID") ?? null;
|
|
6010
|
-
logger$
|
|
6035
|
+
logger$14.debug("[WebRTCManager] Verto invite response:", {
|
|
6011
6036
|
callId,
|
|
6012
6037
|
memberId,
|
|
6013
6038
|
response
|
|
6014
6039
|
});
|
|
6015
6040
|
this._selfId$.next(memberId);
|
|
6016
6041
|
rtcPeerConnController.setMemberId(memberId);
|
|
6017
|
-
if (callId)
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
logger$
|
|
6042
|
+
if (callId) {
|
|
6043
|
+
this.webRtcCallSession.addCallId(callId);
|
|
6044
|
+
this.attachManager.attach(this.buildAttachableCall(callId));
|
|
6045
|
+
} else logger$14.warn("[WebRTCManager] Cannot attach call, missing callId:", {
|
|
6046
|
+
nodeId: this.nodeId,
|
|
6047
|
+
callId
|
|
6048
|
+
});
|
|
6049
|
+
logger$14.info("[WebRTCManager] Verto invite successful");
|
|
6050
|
+
logger$14.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
|
|
6021
6051
|
} else {
|
|
6022
|
-
logger$
|
|
6052
|
+
logger$14.error("[WebRTCManager] Verto invite failed:", response);
|
|
6023
6053
|
const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
|
|
6024
6054
|
this.onError?.(inviteError);
|
|
6025
6055
|
}
|
|
@@ -6064,17 +6094,17 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6064
6094
|
if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
|
|
6065
6095
|
}
|
|
6066
6096
|
async handleInboundAnswer(rtcPeerConnController) {
|
|
6067
|
-
logger$
|
|
6097
|
+
logger$14.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
|
|
6068
6098
|
const vertoByeOrAccepted = await firstValueFrom(race(this.vertoBye$, this.webRtcCallSession.answered$).pipe(takeUntil(this.destroyed$))).catch(() => null);
|
|
6069
6099
|
if (vertoByeOrAccepted === null) {
|
|
6070
|
-
logger$
|
|
6100
|
+
logger$14.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
|
|
6071
6101
|
return;
|
|
6072
6102
|
}
|
|
6073
6103
|
if (isVertoByeMessage(vertoByeOrAccepted)) {
|
|
6074
|
-
logger$
|
|
6104
|
+
logger$14.info("[WebRTCManager] Inbound call ended by remote before answer.");
|
|
6075
6105
|
this.callSession?.destroy();
|
|
6076
6106
|
} else if (!vertoByeOrAccepted) {
|
|
6077
|
-
logger$
|
|
6107
|
+
logger$14.info("[WebRTCManager] Inbound call rejected by user.");
|
|
6078
6108
|
try {
|
|
6079
6109
|
await this.bye("USER_BUSY");
|
|
6080
6110
|
} finally {
|
|
@@ -6082,21 +6112,22 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6082
6112
|
this.callSession?.destroy();
|
|
6083
6113
|
}
|
|
6084
6114
|
} else {
|
|
6085
|
-
logger$
|
|
6115
|
+
logger$14.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
|
|
6086
6116
|
const answerOptions = this.webRtcCallSession.answerMediaOptions;
|
|
6087
6117
|
try {
|
|
6088
6118
|
await rtcPeerConnController.acceptInbound(answerOptions);
|
|
6089
6119
|
} catch (error) {
|
|
6090
|
-
logger$
|
|
6120
|
+
logger$14.error("[WebRTCManager] Error creating inbound answer:", error);
|
|
6091
6121
|
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
6092
6122
|
}
|
|
6093
6123
|
}
|
|
6094
6124
|
}
|
|
6095
6125
|
setupVertoAttachHandler() {
|
|
6096
6126
|
this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
|
|
6097
|
-
logger$
|
|
6127
|
+
logger$14.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
|
|
6098
6128
|
const { callID } = vertoAttach;
|
|
6099
6129
|
await this.attachManager.attach({
|
|
6130
|
+
nodeId: this.nodeId ?? void 0,
|
|
6100
6131
|
id: callID,
|
|
6101
6132
|
to: vertoAttach.callee_id_number,
|
|
6102
6133
|
mediaDirections: {
|
|
@@ -6141,11 +6172,11 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6141
6172
|
setupVertoByeHandler() {
|
|
6142
6173
|
this.subscribeTo(this.vertoBye$, () => {
|
|
6143
6174
|
this._signalingStatus$.next("disconnected");
|
|
6144
|
-
this.attachManager.detach(this.
|
|
6175
|
+
this.attachManager.detach(this.buildAttachableCall());
|
|
6145
6176
|
this.callSession?.destroy();
|
|
6146
6177
|
});
|
|
6147
6178
|
}
|
|
6148
|
-
getSendLocalSDPOptionalParams(rtcPeerConnController,
|
|
6179
|
+
getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMessage) {
|
|
6149
6180
|
let subscribe = void 0;
|
|
6150
6181
|
if (!rtcPeerConnController.firstSDPExchangeCompleted) {
|
|
6151
6182
|
subscribe = [];
|
|
@@ -6153,7 +6184,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6153
6184
|
else if (rtcPeerConnController.isAdditionalDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);
|
|
6154
6185
|
else if (rtcPeerConnController.isScreenShare) subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);
|
|
6155
6186
|
}
|
|
6156
|
-
const isInvite =
|
|
6187
|
+
const isInvite = isVertoInviteMessage(vertoMessage);
|
|
6157
6188
|
const isReattach = isInvite && this.webRtcCallSession.options.reattach;
|
|
6158
6189
|
return {
|
|
6159
6190
|
callID: rtcPeerConnController.id,
|
|
@@ -6162,13 +6193,13 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6162
6193
|
};
|
|
6163
6194
|
}
|
|
6164
6195
|
async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
|
|
6165
|
-
logger$
|
|
6196
|
+
logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
|
|
6166
6197
|
const vertoByeOrAccepted = await firstValueFrom(race(this.vertoBye$, this.webRtcCallSession.answered$));
|
|
6167
6198
|
if (isVertoByeMessage(vertoByeOrAccepted)) {
|
|
6168
|
-
logger$
|
|
6199
|
+
logger$14.info("[WebRTCManager] Call ended before answer was sent.");
|
|
6169
6200
|
this.callSession?.destroy();
|
|
6170
6201
|
} else if (!vertoByeOrAccepted) {
|
|
6171
|
-
logger$
|
|
6202
|
+
logger$14.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
|
|
6172
6203
|
try {
|
|
6173
6204
|
await this.bye("USER_BUSY");
|
|
6174
6205
|
} finally {
|
|
@@ -6176,14 +6207,14 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6176
6207
|
this.callSession?.destroy();
|
|
6177
6208
|
}
|
|
6178
6209
|
} else {
|
|
6179
|
-
logger$
|
|
6210
|
+
logger$14.debug("[WebRTCManager] Call accepted, sending answer");
|
|
6180
6211
|
try {
|
|
6181
6212
|
this._signalingStatus$.next("connecting");
|
|
6182
6213
|
await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
|
|
6183
6214
|
await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
|
|
6184
|
-
await this.attachManager.attach(this.
|
|
6215
|
+
await this.attachManager.attach(this.buildAttachableCall());
|
|
6185
6216
|
} catch (error) {
|
|
6186
|
-
logger$
|
|
6217
|
+
logger$14.error("[WebRTCManager] Error sending Verto answer:", error);
|
|
6187
6218
|
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
6188
6219
|
await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
|
|
6189
6220
|
}
|
|
@@ -6274,10 +6305,10 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6274
6305
|
});
|
|
6275
6306
|
await firstValueFrom(rtcPeerConnController.connectionState$.pipe(filter((state) => state === "connected"), take(1), timeout(this._screenShareTimeoutMs)));
|
|
6276
6307
|
this._screenShareStatus$.next("started");
|
|
6277
|
-
logger$
|
|
6308
|
+
logger$14.info("[WebRTCManager] Screen share started successfully.");
|
|
6278
6309
|
return rtcPeerConnController.id;
|
|
6279
6310
|
} catch (error) {
|
|
6280
|
-
logger$
|
|
6311
|
+
logger$14.warn("[WebRTCManager] Error initializing additional peer connection:", error);
|
|
6281
6312
|
this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
6282
6313
|
if (rtcPeerConnController) rtcPeerConnController.destroy();
|
|
6283
6314
|
this._screenShareStatus$.next("none");
|
|
@@ -6296,9 +6327,9 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6296
6327
|
if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
|
|
6297
6328
|
}
|
|
6298
6329
|
async removeScreenMedia() {
|
|
6299
|
-
if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$
|
|
6330
|
+
if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$14.warn("[WebRTCManager] No active screen share to stop.");
|
|
6300
6331
|
if (!this._screenShareId) {
|
|
6301
|
-
logger$
|
|
6332
|
+
logger$14.debug("[WebRTCManager] No screen share peer connection found.");
|
|
6302
6333
|
return;
|
|
6303
6334
|
}
|
|
6304
6335
|
this._screenShareStatus$.next("stopping");
|
|
@@ -6327,12 +6358,12 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6327
6358
|
dialogParams: this.dialogParams(rtcPeerConnController)
|
|
6328
6359
|
}));
|
|
6329
6360
|
} catch (error) {
|
|
6330
|
-
logger$
|
|
6361
|
+
logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
|
|
6331
6362
|
throw error;
|
|
6332
6363
|
}
|
|
6333
6364
|
}
|
|
6334
6365
|
async bye(cause) {
|
|
6335
|
-
this.attachManager.detach(this.
|
|
6366
|
+
this.attachManager.detach(this.buildAttachableCall());
|
|
6336
6367
|
const rtcPeerConnController = this._rtcPeerConnectionsMap.get(this.webRtcCallSession.id);
|
|
6337
6368
|
if (rtcPeerConnController) await this.executeVertoBye(rtcPeerConnController, cause);
|
|
6338
6369
|
}
|
|
@@ -6345,7 +6376,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6345
6376
|
try {
|
|
6346
6377
|
await this.executeVerto(vertoInfoMessage);
|
|
6347
6378
|
} catch (error) {
|
|
6348
|
-
logger$
|
|
6379
|
+
logger$14.warn("[WebRTCManager] Error sending DTMF digits:", error);
|
|
6349
6380
|
throw error;
|
|
6350
6381
|
}
|
|
6351
6382
|
}
|
|
@@ -6356,10 +6387,10 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6356
6387
|
action: "transfer"
|
|
6357
6388
|
});
|
|
6358
6389
|
try {
|
|
6359
|
-
logger$
|
|
6390
|
+
logger$14.debug("[WebRTCManager] Transferring call with options:", options);
|
|
6360
6391
|
await this.executeVerto(message);
|
|
6361
6392
|
} catch (error) {
|
|
6362
|
-
logger$
|
|
6393
|
+
logger$14.error("[WebRTCManager] Error transferring call:", error);
|
|
6363
6394
|
throw error;
|
|
6364
6395
|
}
|
|
6365
6396
|
}
|
|
@@ -6381,7 +6412,7 @@ var WebRTCVertoManager = class extends VertoManager {
|
|
|
6381
6412
|
*
|
|
6382
6413
|
* Extends Destroyable for automatic RxJS cleanup.
|
|
6383
6414
|
*/
|
|
6384
|
-
const logger$
|
|
6415
|
+
const logger$13 = getLogger();
|
|
6385
6416
|
const DEFAULT_POLLING_INTERVAL_MS = 1e3;
|
|
6386
6417
|
const DEFAULT_BASELINE_SAMPLES = 10;
|
|
6387
6418
|
const DEFAULT_NO_AUDIO_PACKET_THRESHOLD_MS = 2e3;
|
|
@@ -6471,9 +6502,9 @@ var RTCStatsMonitor = class extends Destroyable {
|
|
|
6471
6502
|
const now = Date.now();
|
|
6472
6503
|
this.lastAudioPacketChangeTime = now;
|
|
6473
6504
|
this.lastVideoPacketChangeTime = now;
|
|
6474
|
-
logger$
|
|
6505
|
+
logger$13.debug("[RTCStatsMonitor] Starting stats monitoring");
|
|
6475
6506
|
this.subscribeTo(interval(this.pollingIntervalMs).pipe(filter(() => this.running), switchMap(() => from(this.peerConnection.getStats()).pipe(catchError((err) => {
|
|
6476
|
-
logger$
|
|
6507
|
+
logger$13.warn("[RTCStatsMonitor] Failed to get stats:", err);
|
|
6477
6508
|
return EMPTY;
|
|
6478
6509
|
}))), filter(() => this.running), map((report) => this.extractSample(report))), (sample) => this._sample$.next(sample));
|
|
6479
6510
|
this.subscribeTo(this._sample$.pipe(take(this.baselineSampleCount), toArray(), map((samples) => ({
|
|
@@ -6481,7 +6512,7 @@ var RTCStatsMonitor = class extends Destroyable {
|
|
|
6481
6512
|
jitter: samples.reduce((s, b) => s + b.audioJitter, 0) / samples.length,
|
|
6482
6513
|
ready: true
|
|
6483
6514
|
}))), (baseline) => {
|
|
6484
|
-
logger$
|
|
6515
|
+
logger$13.debug(`[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`);
|
|
6485
6516
|
this._baseline$.next(baseline);
|
|
6486
6517
|
});
|
|
6487
6518
|
this.subscribeTo(this._sample$.pipe(scan((acc, sample) => ({
|
|
@@ -6518,10 +6549,10 @@ var RTCStatsMonitor = class extends Destroyable {
|
|
|
6518
6549
|
stop() {
|
|
6519
6550
|
if (!this.running) return;
|
|
6520
6551
|
this.running = false;
|
|
6521
|
-
logger$
|
|
6552
|
+
logger$13.debug("[RTCStatsMonitor] Stopping stats monitoring");
|
|
6522
6553
|
}
|
|
6523
6554
|
destroy() {
|
|
6524
|
-
logger$
|
|
6555
|
+
logger$13.debug("[RTCStatsMonitor] Destroying RTCStatsMonitor");
|
|
6525
6556
|
this.stop();
|
|
6526
6557
|
super.destroy();
|
|
6527
6558
|
}
|
|
@@ -6649,7 +6680,7 @@ var RTCStatsMonitor = class extends Destroyable {
|
|
|
6649
6680
|
|
|
6650
6681
|
//#endregion
|
|
6651
6682
|
//#region src/managers/CallRecoveryManager.ts
|
|
6652
|
-
const logger$
|
|
6683
|
+
const logger$12 = getLogger();
|
|
6653
6684
|
const DEFAULT_DEBOUNCE_TIME_MS = 2e3;
|
|
6654
6685
|
const DEFAULT_COOLDOWN_MS = 1e4;
|
|
6655
6686
|
const DEFAULT_ICE_GRACE_PERIOD_MS = 3e3;
|
|
@@ -6685,7 +6716,7 @@ const DEGRADATION_ONLY_ISSUES = new Set([
|
|
|
6685
6716
|
* A state machine tracks the pipeline: IDLE -> DEBOUNCING -> RECOVERING -> COOLDOWN.
|
|
6686
6717
|
*/
|
|
6687
6718
|
var CallRecoveryManager = class extends Destroyable {
|
|
6688
|
-
constructor(callbacks, config = {}) {
|
|
6719
|
+
constructor(callbacks, inputs, config = {}) {
|
|
6689
6720
|
super();
|
|
6690
6721
|
this._recoveryState$ = this.createBehaviorSubject("idle");
|
|
6691
6722
|
this._recoveryEvent$ = this.createSubject();
|
|
@@ -6699,6 +6730,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6699
6730
|
this._cooldownUntil = 0;
|
|
6700
6731
|
this._pipelineStop$ = this.createSubject();
|
|
6701
6732
|
this._callbacks = callbacks;
|
|
6733
|
+
this._inputs = inputs;
|
|
6702
6734
|
this._config = {
|
|
6703
6735
|
debounceTimeMs: config.debounceTimeMs ?? DEFAULT_DEBOUNCE_TIME_MS,
|
|
6704
6736
|
cooldownMs: config.cooldownMs ?? DEFAULT_COOLDOWN_MS,
|
|
@@ -6745,10 +6777,10 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6745
6777
|
*/
|
|
6746
6778
|
async requestIceRestart() {
|
|
6747
6779
|
if (this._recoveryState$.value === "recovering") {
|
|
6748
|
-
logger$
|
|
6780
|
+
logger$12.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
|
|
6749
6781
|
return;
|
|
6750
6782
|
}
|
|
6751
|
-
logger$
|
|
6783
|
+
logger$12.info("CallRecoveryManager: manual ICE restart requested");
|
|
6752
6784
|
this.transitionTo("recovering");
|
|
6753
6785
|
await this.executeIceRestart(false);
|
|
6754
6786
|
this.startCooldown();
|
|
@@ -6764,7 +6796,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6764
6796
|
* WebSocket reconnect or call state recovers to 'connected'.
|
|
6765
6797
|
*/
|
|
6766
6798
|
reset() {
|
|
6767
|
-
logger$
|
|
6799
|
+
logger$12.info("CallRecoveryManager: resetting counters");
|
|
6768
6800
|
this._attemptCount = 0;
|
|
6769
6801
|
this._keyframeBurstCount = 0;
|
|
6770
6802
|
this._keyframeBurstStart = 0;
|
|
@@ -6773,6 +6805,22 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6773
6805
|
this.transitionTo("idle");
|
|
6774
6806
|
}
|
|
6775
6807
|
/**
|
|
6808
|
+
* Notify the recovery manager that a verto.modify (ICE restart SDP exchange)
|
|
6809
|
+
* failed at the signaling layer. Resets cooldown and pushes a new trigger
|
|
6810
|
+
* so recovery can re-attempt.
|
|
6811
|
+
*/
|
|
6812
|
+
notifyModifyFailed() {
|
|
6813
|
+
if (this._recoveryState$.value === "cooldown" || this._recoveryState$.value === "idle") {
|
|
6814
|
+
logger$12.info("CallRecoveryManager: verto.modify failed — re-entering recovery");
|
|
6815
|
+
this._cooldownUntil = 0;
|
|
6816
|
+
this.transitionTo("idle");
|
|
6817
|
+
this.pushTrigger({
|
|
6818
|
+
source: "network",
|
|
6819
|
+
detail: "modify_failed_during_recovery"
|
|
6820
|
+
});
|
|
6821
|
+
}
|
|
6822
|
+
}
|
|
6823
|
+
/**
|
|
6776
6824
|
* Feed bandwidth information for graceful degradation (Section 22).
|
|
6777
6825
|
* Call this from the stats monitor with current available outgoing bitrate.
|
|
6778
6826
|
*/
|
|
@@ -6787,7 +6835,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6787
6835
|
reason: `bandwidth ${bitrateKbps}kbps below threshold ${this._config.degradationBitrateThreshold}kbps`,
|
|
6788
6836
|
timestamp: Date.now()
|
|
6789
6837
|
});
|
|
6790
|
-
logger$
|
|
6838
|
+
logger$12.warn(`CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`);
|
|
6791
6839
|
} else if (wasConstrained && bitrateKbps >= this._config.degradationRecoveryThreshold) {
|
|
6792
6840
|
this._bandwidthConstrained$.next(false);
|
|
6793
6841
|
this._callbacks.enableVideo();
|
|
@@ -6796,7 +6844,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6796
6844
|
reason: `bandwidth ${bitrateKbps}kbps recovered above ${this._config.degradationRecoveryThreshold}kbps`,
|
|
6797
6845
|
timestamp: Date.now()
|
|
6798
6846
|
});
|
|
6799
|
-
logger$
|
|
6847
|
+
logger$12.info(`CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`);
|
|
6800
6848
|
}
|
|
6801
6849
|
}
|
|
6802
6850
|
/**
|
|
@@ -6815,14 +6863,14 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6815
6863
|
handleWebSocketReconnect() {
|
|
6816
6864
|
const pcState = this._callbacks.getPeerConnectionState();
|
|
6817
6865
|
if (pcState === "connected" || pcState === "completed") {
|
|
6818
|
-
logger$
|
|
6866
|
+
logger$12.info("CallRecoveryManager: signal-only reconnect — peer connection still alive");
|
|
6819
6867
|
this.emitEvent({
|
|
6820
6868
|
action: "signal_reconnect",
|
|
6821
6869
|
reason: "WebSocket reconnected, peer connection still connected",
|
|
6822
6870
|
timestamp: Date.now()
|
|
6823
6871
|
});
|
|
6824
6872
|
} else {
|
|
6825
|
-
logger$
|
|
6873
|
+
logger$12.info("CallRecoveryManager: full reconnect — peer connection also down");
|
|
6826
6874
|
this.emitEvent({
|
|
6827
6875
|
action: "full_reconnect",
|
|
6828
6876
|
reason: "WebSocket reconnected, peer connection not connected — ICE restart needed",
|
|
@@ -6843,10 +6891,10 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6843
6891
|
initPipeline() {
|
|
6844
6892
|
this.subscribeTo(this._trigger$.pipe(tap(() => {
|
|
6845
6893
|
if (this._recoveryState$.value === "idle") this.transitionTo("debouncing");
|
|
6846
|
-
}), debounceTime(this._config.debounceTimeMs), filter(() => this.passGateChecks()), exhaustMap((trigger) => this.executeTieredRecovery(trigger)), takeUntil(merge(this._destroyed$, this._pipelineStop$))), {
|
|
6894
|
+
}), debounceTime(this._config.debounceTimeMs), withLatestFrom(this._inputs.signalingReady$), filter(([, signalingReady]) => this.passGateChecks(signalingReady)), map(([trigger]) => trigger), exhaustMap((trigger) => this.executeTieredRecovery(trigger)), takeUntil(merge(this._destroyed$, this._pipelineStop$))), {
|
|
6847
6895
|
next: () => {},
|
|
6848
6896
|
error: (err) => {
|
|
6849
|
-
logger$
|
|
6897
|
+
logger$12.error("CallRecoveryManager: pipeline error", err);
|
|
6850
6898
|
this.transitionTo("idle");
|
|
6851
6899
|
}
|
|
6852
6900
|
});
|
|
@@ -6873,27 +6921,27 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6873
6921
|
reason: `no packet loss for ${this._config.packetLossRecoveryDelaySec}s — restoring video`,
|
|
6874
6922
|
timestamp: Date.now()
|
|
6875
6923
|
});
|
|
6876
|
-
logger$
|
|
6924
|
+
logger$12.info(`CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`);
|
|
6877
6925
|
});
|
|
6878
6926
|
}
|
|
6879
|
-
passGateChecks() {
|
|
6927
|
+
passGateChecks(signalingReady) {
|
|
6880
6928
|
if (this._callbacks.isNegotiating()) {
|
|
6881
|
-
logger$
|
|
6929
|
+
logger$12.debug("CallRecoveryManager: gate blocked — negotiation in progress");
|
|
6882
6930
|
this.transitionTo("idle");
|
|
6883
6931
|
return false;
|
|
6884
6932
|
}
|
|
6885
|
-
if (!
|
|
6886
|
-
logger$
|
|
6933
|
+
if (!signalingReady) {
|
|
6934
|
+
logger$12.debug("CallRecoveryManager: gate blocked — signaling not ready");
|
|
6887
6935
|
this.transitionTo("idle");
|
|
6888
6936
|
return false;
|
|
6889
6937
|
}
|
|
6890
6938
|
if (!this._callbacks.isCallConnected()) {
|
|
6891
|
-
logger$
|
|
6939
|
+
logger$12.debug("CallRecoveryManager: gate blocked — call not connected");
|
|
6892
6940
|
this.transitionTo("idle");
|
|
6893
6941
|
return false;
|
|
6894
6942
|
}
|
|
6895
6943
|
if (this.isCooldownActive()) {
|
|
6896
|
-
logger$
|
|
6944
|
+
logger$12.debug("CallRecoveryManager: gate blocked — cooldown active");
|
|
6897
6945
|
this.transitionTo("cooldown");
|
|
6898
6946
|
return false;
|
|
6899
6947
|
}
|
|
@@ -6904,9 +6952,9 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6904
6952
|
}
|
|
6905
6953
|
executeTieredRecovery(trigger) {
|
|
6906
6954
|
this.transitionTo("recovering");
|
|
6907
|
-
logger$
|
|
6955
|
+
logger$12.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
|
|
6908
6956
|
return from(this.runTiers(trigger)).pipe(tap(() => this.startCooldown()), catchError((err) => {
|
|
6909
|
-
logger$
|
|
6957
|
+
logger$12.error("CallRecoveryManager: tiered recovery failed", err);
|
|
6910
6958
|
this.startCooldown();
|
|
6911
6959
|
return EMPTY;
|
|
6912
6960
|
}));
|
|
@@ -6914,7 +6962,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6914
6962
|
async runTiers(trigger) {
|
|
6915
6963
|
this.executeKeyframe(trigger.detail);
|
|
6916
6964
|
if (trigger.issueType && DEGRADATION_ONLY_ISSUES.has(trigger.issueType)) {
|
|
6917
|
-
logger$
|
|
6965
|
+
logger$12.debug(`CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`);
|
|
6918
6966
|
return;
|
|
6919
6967
|
}
|
|
6920
6968
|
if (this._attemptCount < this._config.maxAttempts) {
|
|
@@ -6931,13 +6979,13 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6931
6979
|
maxAttempts: this._config.maxAttempts,
|
|
6932
6980
|
timestamp: Date.now()
|
|
6933
6981
|
});
|
|
6934
|
-
logger$
|
|
6982
|
+
logger$12.warn("CallRecoveryManager: max recovery attempts reached");
|
|
6935
6983
|
}
|
|
6936
6984
|
}
|
|
6937
6985
|
executeKeyframe(reason) {
|
|
6938
6986
|
const now = Date.now();
|
|
6939
6987
|
if (now < this._keyframeCooldownUntil) {
|
|
6940
|
-
logger$
|
|
6988
|
+
logger$12.debug("CallRecoveryManager: keyframe request skipped — cooldown active");
|
|
6941
6989
|
return;
|
|
6942
6990
|
}
|
|
6943
6991
|
if (now - this._keyframeBurstStart > this._config.keyframeBurstWindowMs) {
|
|
@@ -6946,7 +6994,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6946
6994
|
}
|
|
6947
6995
|
if (this._keyframeBurstCount >= this._config.keyframeMaxBurst) {
|
|
6948
6996
|
this._keyframeCooldownUntil = now + this._config.keyframeCooldownMs;
|
|
6949
|
-
logger$
|
|
6997
|
+
logger$12.debug(`CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`);
|
|
6950
6998
|
return;
|
|
6951
6999
|
}
|
|
6952
7000
|
this._keyframeBurstCount += 1;
|
|
@@ -6956,12 +7004,12 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6956
7004
|
reason,
|
|
6957
7005
|
timestamp: now
|
|
6958
7006
|
});
|
|
6959
|
-
logger$
|
|
7007
|
+
logger$12.debug(`CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`);
|
|
6960
7008
|
}
|
|
6961
7009
|
async executeIceRestart(relayOnly) {
|
|
6962
7010
|
this._attemptCount += 1;
|
|
6963
7011
|
const tier = relayOnly ? "Tier 3 (relay-only)" : "Tier 2 (standard)";
|
|
6964
|
-
logger$
|
|
7012
|
+
logger$12.info(`CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`);
|
|
6965
7013
|
this.emitEvent({
|
|
6966
7014
|
action: "reinvite_started",
|
|
6967
7015
|
reason: `${tier} ICE restart`,
|
|
@@ -6978,7 +7026,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6978
7026
|
maxAttempts: this._config.maxAttempts,
|
|
6979
7027
|
timestamp: Date.now()
|
|
6980
7028
|
});
|
|
6981
|
-
logger$
|
|
7029
|
+
logger$12.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);
|
|
6982
7030
|
this._attemptCount = 0;
|
|
6983
7031
|
return true;
|
|
6984
7032
|
}
|
|
@@ -6989,7 +7037,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6989
7037
|
maxAttempts: this._config.maxAttempts,
|
|
6990
7038
|
timestamp: Date.now()
|
|
6991
7039
|
});
|
|
6992
|
-
logger$
|
|
7040
|
+
logger$12.warn(`CallRecoveryManager: ${tier} ICE restart failed`);
|
|
6993
7041
|
return false;
|
|
6994
7042
|
} catch {
|
|
6995
7043
|
this.emitEvent({
|
|
@@ -6999,7 +7047,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
6999
7047
|
maxAttempts: this._config.maxAttempts,
|
|
7000
7048
|
timestamp: Date.now()
|
|
7001
7049
|
});
|
|
7002
|
-
logger$
|
|
7050
|
+
logger$12.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);
|
|
7003
7051
|
return false;
|
|
7004
7052
|
}
|
|
7005
7053
|
}
|
|
@@ -7020,7 +7068,7 @@ var CallRecoveryManager = class extends Destroyable {
|
|
|
7020
7068
|
transitionTo(state) {
|
|
7021
7069
|
const prev = this._recoveryState$.value;
|
|
7022
7070
|
if (prev !== state) {
|
|
7023
|
-
logger$
|
|
7071
|
+
logger$12.debug(`CallRecoveryManager: state ${prev} -> ${state}`);
|
|
7024
7072
|
this._recoveryState$.next(state);
|
|
7025
7073
|
}
|
|
7026
7074
|
}
|
|
@@ -7122,7 +7170,7 @@ function mosToQualityLevel(mos) {
|
|
|
7122
7170
|
|
|
7123
7171
|
//#endregion
|
|
7124
7172
|
//#region src/core/entities/Call.ts
|
|
7125
|
-
const logger$
|
|
7173
|
+
const logger$11 = getLogger();
|
|
7126
7174
|
/**
|
|
7127
7175
|
* Ratio between the critical and warning RTT spike multipliers.
|
|
7128
7176
|
* Warning threshold = baseline * warningMultiplier (default 3x)
|
|
@@ -7140,7 +7188,7 @@ const fromDestinationParams = (destination) => {
|
|
|
7140
7188
|
});
|
|
7141
7189
|
return params;
|
|
7142
7190
|
} catch (error) {
|
|
7143
|
-
logger$
|
|
7191
|
+
logger$11.warn(`Failed to parse destination URI: ${destination}`, error);
|
|
7144
7192
|
return {};
|
|
7145
7193
|
}
|
|
7146
7194
|
};
|
|
@@ -7199,7 +7247,10 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7199
7247
|
this.subscribeTo(merge(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe(distinctUntilChanged(), takeUntil(this._destroyed$)), (status) => {
|
|
7200
7248
|
this._lastMergedStatus = status;
|
|
7201
7249
|
if (status === "connected" && !this._statsMonitor) this.initResilienceSubsystems();
|
|
7202
|
-
else if (status === "disconnected"
|
|
7250
|
+
else if (status === "disconnected") {
|
|
7251
|
+
this._statsMonitor?.destroy();
|
|
7252
|
+
this._statsMonitor = void 0;
|
|
7253
|
+
} else if (status === "destroyed" || status === "failed") this.stopResilienceSubsystems();
|
|
7203
7254
|
});
|
|
7204
7255
|
}
|
|
7205
7256
|
/** Observable stream of errors from media, signaling, and peer connection layers. */
|
|
@@ -7218,6 +7269,10 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7218
7269
|
this.destroy();
|
|
7219
7270
|
}
|
|
7220
7271
|
}
|
|
7272
|
+
/** Notify the recovery manager that a verto.modify signaling exchange failed. */
|
|
7273
|
+
notifyModifyFailed() {
|
|
7274
|
+
this._recoveryManager?.notifyModifyFailed();
|
|
7275
|
+
}
|
|
7221
7276
|
/** Whether this call is `'inbound'` or `'outbound'`. */
|
|
7222
7277
|
get direction() {
|
|
7223
7278
|
return this.options.initOffer ? "inbound" : "outbound";
|
|
@@ -7331,7 +7386,7 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7331
7386
|
if (isJSONRPCErrorResponse(response)) throw new JSONRPCError(parseInt(response.result?.code ?? "0"), `Error response from method ${method}: ${response.result?.code} ${response.result?.message}`, void 0, void 0, request.id);
|
|
7332
7387
|
return response;
|
|
7333
7388
|
} catch (error) {
|
|
7334
|
-
logger$
|
|
7389
|
+
logger$11.error(`[Call] Error executing method ${method} with params`, params, error);
|
|
7335
7390
|
throw error;
|
|
7336
7391
|
}
|
|
7337
7392
|
}
|
|
@@ -7534,9 +7589,9 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7534
7589
|
*/
|
|
7535
7590
|
initResilienceSubsystems() {
|
|
7536
7591
|
const pc = this.rtcPeerConnection;
|
|
7537
|
-
logger$
|
|
7592
|
+
logger$11.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
|
|
7538
7593
|
if (!pc) {
|
|
7539
|
-
logger$
|
|
7594
|
+
logger$11.warn("[Call] No peer connection available, skipping resilience init");
|
|
7540
7595
|
return;
|
|
7541
7596
|
}
|
|
7542
7597
|
try {
|
|
@@ -7571,21 +7626,20 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7571
7626
|
disableVideo: () => {
|
|
7572
7627
|
try {
|
|
7573
7628
|
this.vertoManager.muteMainVideoInputDevice();
|
|
7574
|
-
logger$
|
|
7629
|
+
logger$11.debug("[Call] Recovery manager disabled video");
|
|
7575
7630
|
} catch {
|
|
7576
|
-
logger$
|
|
7631
|
+
logger$11.debug("[Call] Recovery manager failed to disable video");
|
|
7577
7632
|
}
|
|
7578
7633
|
},
|
|
7579
7634
|
enableVideo: () => {
|
|
7580
7635
|
this.vertoManager.unmuteMainVideoInputDevice().catch(() => {
|
|
7581
|
-
logger$
|
|
7636
|
+
logger$11.debug("[Call] Recovery manager failed to enable video");
|
|
7582
7637
|
});
|
|
7583
7638
|
},
|
|
7584
7639
|
isNegotiating: () => this.vertoManager.mainPeerConnection.isNegotiating,
|
|
7585
|
-
isWebSocketConnected: () => this._lastMergedStatus !== "disconnected",
|
|
7586
7640
|
isCallConnected: () => this._lastMergedStatus === "connected",
|
|
7587
7641
|
getPeerConnectionState: () => pc.connectionState
|
|
7588
|
-
}, {
|
|
7642
|
+
}, { signalingReady$: this.clientSession.authenticated$ }, {
|
|
7589
7643
|
debounceTimeMs: prefs.recoveryDebounceTime,
|
|
7590
7644
|
cooldownMs: prefs.recoveryCooldown,
|
|
7591
7645
|
iceGracePeriodMs: prefs.iceDisconnectedGracePeriod,
|
|
@@ -7628,6 +7682,15 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7628
7682
|
});
|
|
7629
7683
|
this.subscribeTo(this._recoveryManager.recoveryEvent$, (event) => {
|
|
7630
7684
|
this._recoveryEvent$.next(event);
|
|
7685
|
+
if (event.action === "max_attempts_reached") {
|
|
7686
|
+
logger$11.warn("[Call] All recovery attempts exhausted, terminating call");
|
|
7687
|
+
this.emitError({
|
|
7688
|
+
kind: "network",
|
|
7689
|
+
fatal: true,
|
|
7690
|
+
error: /* @__PURE__ */ new Error("Call recovery failed: all attempts exhausted"),
|
|
7691
|
+
callId: this.id
|
|
7692
|
+
});
|
|
7693
|
+
}
|
|
7631
7694
|
});
|
|
7632
7695
|
this.subscribeTo(this._recoveryManager.bandwidthConstrained$, (constrained) => {
|
|
7633
7696
|
this._bandwidthConstrained$.next(constrained);
|
|
@@ -7640,13 +7703,13 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7640
7703
|
else if (event.type === "online") this._recoveryManager?.handleWebSocketReconnect();
|
|
7641
7704
|
});
|
|
7642
7705
|
this.subscribeTo(this.clientSession.authenticated$.pipe(skip(1), filter(Boolean)), () => {
|
|
7643
|
-
logger$
|
|
7706
|
+
logger$11.debug("[Call] WebSocket reconnected — notifying recovery manager");
|
|
7644
7707
|
this._recoveryManager?.handleWebSocketReconnect();
|
|
7645
7708
|
});
|
|
7646
7709
|
this._statsMonitor.start();
|
|
7647
|
-
logger$
|
|
7710
|
+
logger$11.debug("[Call] Resilience subsystems initialized for call", this.id);
|
|
7648
7711
|
} catch (error) {
|
|
7649
|
-
logger$
|
|
7712
|
+
logger$11.warn("[Call] Failed to initialize resilience subsystems:", error);
|
|
7650
7713
|
}
|
|
7651
7714
|
}
|
|
7652
7715
|
/**
|
|
@@ -7708,19 +7771,19 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7708
7771
|
}
|
|
7709
7772
|
isCallSessionEvent(event) {
|
|
7710
7773
|
try {
|
|
7711
|
-
logger$
|
|
7774
|
+
logger$11.debug("[Call] Checking if event is for this call session:", event);
|
|
7712
7775
|
const callId = getValueFrom(event, "params.params.callID") ?? getValueFrom(event, "params.call_id");
|
|
7713
7776
|
const roomSessionId = getValueFrom(event, "params.room_session_id");
|
|
7714
|
-
logger$
|
|
7777
|
+
logger$11.debug(`[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`);
|
|
7715
7778
|
return callId === this.id || !!callId && this.callEventsManager.isCallIdValid(callId) || !!roomSessionId && this.callEventsManager.isRoomSessionIdValid(roomSessionId);
|
|
7716
7779
|
} catch (error) {
|
|
7717
|
-
logger$
|
|
7780
|
+
logger$11.error("[Call] Error checking if event is for this call session:", error);
|
|
7718
7781
|
return false;
|
|
7719
7782
|
}
|
|
7720
7783
|
}
|
|
7721
7784
|
get callSessionEvents$() {
|
|
7722
7785
|
return this.cachedObservable("callSessionEvents$", () => this.clientSession.signalingEvent$.pipe(filter((event) => this.isCallSessionEvent(event)), tap((event) => {
|
|
7723
|
-
logger$
|
|
7786
|
+
logger$11.debug("[Call] Received call session event:", event);
|
|
7724
7787
|
}), takeUntil(this.destroyed$), share()));
|
|
7725
7788
|
}
|
|
7726
7789
|
/** Observable of call-updated events. */
|
|
@@ -7791,10 +7854,10 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7791
7854
|
return filtered$;
|
|
7792
7855
|
}
|
|
7793
7856
|
get webrtcMessages$() {
|
|
7794
|
-
return this.cachedObservable("webrtcMessages$", () => this.callSessionEvents$.pipe(filterAs(isWebrtcMessageMetadata, "params"), tap((event) => logger$
|
|
7857
|
+
return this.cachedObservable("webrtcMessages$", () => this.callSessionEvents$.pipe(filterAs(isWebrtcMessageMetadata, "params"), tap((event) => logger$11.debug("[Call] Event is a WebRTC message event:", event)), takeUntil(this.destroyed$), share()));
|
|
7795
7858
|
}
|
|
7796
7859
|
get callEvent$() {
|
|
7797
|
-
return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(filterAs(isSignalwireCallMetadata, "params"), tap((event) => logger$
|
|
7860
|
+
return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(filterAs(isSignalwireCallMetadata, "params"), tap((event) => logger$11.debug("[Call] Event is a call event:", event)), takeUntil(this.destroyed$), share()));
|
|
7798
7861
|
}
|
|
7799
7862
|
get layoutEvent$() {
|
|
7800
7863
|
return this.cachedObservable("layoutEvent$", () => this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params")));
|
|
@@ -7928,10 +7991,10 @@ var WebRTCCall = class extends Destroyable {
|
|
|
7928
7991
|
message
|
|
7929
7992
|
};
|
|
7930
7993
|
this.clientSession.execute(WebrtcVerto(params)).catch((error) => {
|
|
7931
|
-
logger$
|
|
7994
|
+
logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed (non-fatal):`, error);
|
|
7932
7995
|
});
|
|
7933
7996
|
} catch (error) {
|
|
7934
|
-
logger$
|
|
7997
|
+
logger$11.warn(`[Call] Failed to send verto.subscribe for '${eventType}':`, error);
|
|
7935
7998
|
}
|
|
7936
7999
|
}
|
|
7937
8000
|
};
|
|
@@ -7953,6 +8016,7 @@ function inferCallErrorKind(error) {
|
|
|
7953
8016
|
function isFatalError(error) {
|
|
7954
8017
|
if (error instanceof VertoPongError) return false;
|
|
7955
8018
|
if (error instanceof MediaTrackError) return false;
|
|
8019
|
+
if (error instanceof RPCTimeoutError) return false;
|
|
7956
8020
|
return true;
|
|
7957
8021
|
}
|
|
7958
8022
|
/**
|
|
@@ -7984,6 +8048,9 @@ var CallFactory = class {
|
|
|
7984
8048
|
callId: callInstance.id
|
|
7985
8049
|
};
|
|
7986
8050
|
callInstance.emitError(callError);
|
|
8051
|
+
},
|
|
8052
|
+
onModifyFailed: () => {
|
|
8053
|
+
callInstance.notifyModifyFailed();
|
|
7987
8054
|
}
|
|
7988
8055
|
}),
|
|
7989
8056
|
callEventsManager: new CallEventsManager(callInstance)
|
|
@@ -7997,7 +8064,7 @@ var CallFactory = class {
|
|
|
7997
8064
|
|
|
7998
8065
|
//#endregion
|
|
7999
8066
|
//#region src/behaviors/Collection.ts
|
|
8000
|
-
const logger$
|
|
8067
|
+
const logger$10 = getLogger();
|
|
8001
8068
|
var Fetcher = class {
|
|
8002
8069
|
constructor(endpoint, params, http) {
|
|
8003
8070
|
this.endpoint = endpoint;
|
|
@@ -8021,7 +8088,7 @@ var Fetcher = class {
|
|
|
8021
8088
|
this.hasMore = !!this.nextUrl;
|
|
8022
8089
|
return result.data.filter(this.filter).map(this.mapper);
|
|
8023
8090
|
}
|
|
8024
|
-
logger$
|
|
8091
|
+
logger$10.error("Failed to fetch entity");
|
|
8025
8092
|
return [];
|
|
8026
8093
|
}
|
|
8027
8094
|
async id(v) {
|
|
@@ -8093,7 +8160,7 @@ var EntityCollection = class extends Destroyable {
|
|
|
8093
8160
|
this._hasMore$.next(this.fetchController.hasMore ?? false);
|
|
8094
8161
|
this.loading$.next(false);
|
|
8095
8162
|
} catch (error) {
|
|
8096
|
-
logger$
|
|
8163
|
+
logger$10.error(`Failed to fetch initial collection data`, error);
|
|
8097
8164
|
this._hasMore$.next(this.fetchController.hasMore ?? false);
|
|
8098
8165
|
this.loading$.next(false);
|
|
8099
8166
|
this.onError?.(new CollectionFetchError("fetchMore", error));
|
|
@@ -8107,7 +8174,7 @@ var EntityCollection = class extends Destroyable {
|
|
|
8107
8174
|
if (data) this.upsertData(data);
|
|
8108
8175
|
return data;
|
|
8109
8176
|
} catch (error) {
|
|
8110
|
-
logger$
|
|
8177
|
+
logger$10.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
|
|
8111
8178
|
this.loading$.next(false);
|
|
8112
8179
|
this.onError?.(new CollectionFetchError(`tryFetch(${String(key)})`, error));
|
|
8113
8180
|
}
|
|
@@ -8357,13 +8424,13 @@ var Address = class extends Destroyable {
|
|
|
8357
8424
|
|
|
8358
8425
|
//#endregion
|
|
8359
8426
|
//#region src/core/utils.ts
|
|
8360
|
-
const logger$
|
|
8427
|
+
const logger$9 = getLogger();
|
|
8361
8428
|
const isRPCConnectResult = (e) => {
|
|
8362
|
-
logger$
|
|
8429
|
+
logger$9.debug("isRPCConnectResult check:", e);
|
|
8363
8430
|
if (!e || typeof e !== "object") return false;
|
|
8364
8431
|
const result = e;
|
|
8365
8432
|
const is = typeof result.identity === "string" && typeof result.protocol === "string" && typeof result.authorization === "object" && typeof result.authorization.jti === "string" && typeof result.authorization.project_id === "string" && typeof result.authorization.fabric_subscriber === "object";
|
|
8366
|
-
logger$
|
|
8433
|
+
logger$9.debug("isRPCConnectResult check result:", is);
|
|
8367
8434
|
return is;
|
|
8368
8435
|
};
|
|
8369
8436
|
var PendingRPC = class PendingRPC {
|
|
@@ -8372,7 +8439,7 @@ var PendingRPC = class PendingRPC {
|
|
|
8372
8439
|
}
|
|
8373
8440
|
constructor(request, responses$, options) {
|
|
8374
8441
|
this.id = v4();
|
|
8375
|
-
logger$
|
|
8442
|
+
logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`);
|
|
8376
8443
|
this.request = request;
|
|
8377
8444
|
const timeoutMs = options?.timeoutMs ?? PendingRPC.defaultTimeoutMs;
|
|
8378
8445
|
const signal = options?.signal;
|
|
@@ -8398,22 +8465,22 @@ var PendingRPC = class PendingRPC {
|
|
|
8398
8465
|
isSettled = true;
|
|
8399
8466
|
if (response.error) {
|
|
8400
8467
|
const rpcError = new JSONRPCError(response.error.code, response.error.message, response.error.data, void 0, request.id);
|
|
8401
|
-
logger$
|
|
8468
|
+
logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
|
|
8402
8469
|
reject(rpcError);
|
|
8403
8470
|
} else {
|
|
8404
|
-
logger$
|
|
8471
|
+
logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
|
|
8405
8472
|
resolve(response);
|
|
8406
8473
|
}
|
|
8407
8474
|
subscription.unsubscribe();
|
|
8408
8475
|
},
|
|
8409
8476
|
error: (error) => {
|
|
8410
|
-
logger$
|
|
8477
|
+
logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`, error);
|
|
8411
8478
|
isSettled = true;
|
|
8412
8479
|
reject(error);
|
|
8413
8480
|
subscription.unsubscribe();
|
|
8414
8481
|
},
|
|
8415
8482
|
complete: () => {
|
|
8416
|
-
logger$
|
|
8483
|
+
logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
|
|
8417
8484
|
if (!isSettled) reject(new RPCTimeoutError(request.id, timeoutMs));
|
|
8418
8485
|
subscription.unsubscribe();
|
|
8419
8486
|
}
|
|
@@ -8433,7 +8500,7 @@ var PendingRPC = class PendingRPC {
|
|
|
8433
8500
|
|
|
8434
8501
|
//#endregion
|
|
8435
8502
|
//#region src/managers/ClientSessionManager.ts
|
|
8436
|
-
const logger$
|
|
8503
|
+
const logger$8 = getLogger();
|
|
8437
8504
|
const getAddressSearchURI = (options) => {
|
|
8438
8505
|
const to = options.to?.split("?")[0];
|
|
8439
8506
|
const from$1 = options.from?.startsWith("subscriber://") ? options.from.replace("subscriber://", "") : options.from;
|
|
@@ -8527,7 +8594,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8527
8594
|
try {
|
|
8528
8595
|
return await this.transport.execute(request, options);
|
|
8529
8596
|
} catch (error) {
|
|
8530
|
-
logger$
|
|
8597
|
+
logger$8.debug("[Session] Execute Error", error);
|
|
8531
8598
|
this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
8532
8599
|
throw error;
|
|
8533
8600
|
}
|
|
@@ -8541,40 +8608,40 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8541
8608
|
return true;
|
|
8542
8609
|
}
|
|
8543
8610
|
setupMessageHandlers() {
|
|
8544
|
-
logger$
|
|
8611
|
+
logger$8.debug("[Session] Setting up message handlers");
|
|
8545
8612
|
this.subscribeTo(this.authStateEvent$, async (authStateEvent) => {
|
|
8546
|
-
logger$
|
|
8613
|
+
logger$8.debug("[Session] Authorization state event received:", authStateEvent);
|
|
8547
8614
|
try {
|
|
8548
8615
|
await this.updateAuthorizationStateInStorage(authStateEvent.authorization_state);
|
|
8549
8616
|
} catch (error) {
|
|
8550
|
-
logger$
|
|
8617
|
+
logger$8.error("[Session] Failed to handle authorization state update:", error);
|
|
8551
8618
|
this._errors$.next(new AuthStateHandlerError(error));
|
|
8552
8619
|
}
|
|
8553
8620
|
});
|
|
8554
8621
|
this.subscribeTo(this.transport.connectionStatus$.pipe(filter((status) => status === "connected"), exhaustMap(() => {
|
|
8555
|
-
logger$
|
|
8622
|
+
logger$8.debug("[Session] Connection established, initiating authentication");
|
|
8556
8623
|
return from(this.authenticate()).pipe(catchError((error) => {
|
|
8557
8624
|
this.handleAuthenticationError(error).catch((err) => {
|
|
8558
|
-
logger$
|
|
8625
|
+
logger$8.error("[Session] Error handling authentication failure:", err);
|
|
8559
8626
|
});
|
|
8560
8627
|
return EMPTY;
|
|
8561
8628
|
}));
|
|
8562
8629
|
})), void 0);
|
|
8563
8630
|
this.subscribeTo(this.vertoInvite$, async (invite) => {
|
|
8564
|
-
logger$
|
|
8631
|
+
logger$8.debug("[Session] Verto invite received:", invite);
|
|
8565
8632
|
try {
|
|
8566
8633
|
await this.createInboundCall(invite);
|
|
8567
8634
|
} catch (error) {
|
|
8568
|
-
logger$
|
|
8635
|
+
logger$8.error("[Session] Error handling Verto invite:", error);
|
|
8569
8636
|
this._errors$.next(new VertoInviteHandlerError(error));
|
|
8570
8637
|
}
|
|
8571
8638
|
});
|
|
8572
8639
|
this.subscribeTo(this.vertoAttach$, async (attach) => {
|
|
8573
|
-
logger$
|
|
8640
|
+
logger$8.debug("[Session] Verto attach received:", attach);
|
|
8574
8641
|
try {
|
|
8575
8642
|
await this.handleVertoAttach(attach);
|
|
8576
8643
|
} catch (error) {
|
|
8577
|
-
logger$
|
|
8644
|
+
logger$8.error("[Session] Error handling Verto attach:", error);
|
|
8578
8645
|
this._errors$.next(new VertoAttachHandlerError(error));
|
|
8579
8646
|
}
|
|
8580
8647
|
});
|
|
@@ -8584,36 +8651,36 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8584
8651
|
const storedState = await this.storage.getItem(this.authorizationStateKey);
|
|
8585
8652
|
this.authorizationState$.next(storedState ?? void 0);
|
|
8586
8653
|
} catch (error) {
|
|
8587
|
-
logger$
|
|
8654
|
+
logger$8.error("Failed to retrieve authorization state from storage:", error);
|
|
8588
8655
|
this.authorizationState$.next(void 0);
|
|
8589
8656
|
}
|
|
8590
8657
|
}
|
|
8591
8658
|
async updateAuthorizationStateInStorage(authorizationState) {
|
|
8592
8659
|
if (!authorizationState) {
|
|
8593
|
-
logger$
|
|
8660
|
+
logger$8.debug("[Session] Removing authorization state from storage");
|
|
8594
8661
|
try {
|
|
8595
8662
|
await this.storage.removeItem(this.authorizationStateKey);
|
|
8596
8663
|
this.authorizationState$.next(void 0);
|
|
8597
8664
|
} catch (error) {
|
|
8598
|
-
logger$
|
|
8665
|
+
logger$8.error("Failed to remove authorization state from storage:", error);
|
|
8599
8666
|
throw error;
|
|
8600
8667
|
}
|
|
8601
8668
|
return;
|
|
8602
8669
|
}
|
|
8603
8670
|
try {
|
|
8604
|
-
logger$
|
|
8671
|
+
logger$8.debug("[Session] Updating authorization state in storage");
|
|
8605
8672
|
await this.storage.setItem(this.authorizationStateKey, authorizationState);
|
|
8606
8673
|
this.authorizationState$.next(authorizationState);
|
|
8607
8674
|
} catch (error) {
|
|
8608
|
-
logger$
|
|
8675
|
+
logger$8.error("Failed to retrieve authorization state from storage:", error);
|
|
8609
8676
|
throw error;
|
|
8610
8677
|
}
|
|
8611
8678
|
}
|
|
8612
8679
|
get authStateEvent$() {
|
|
8613
8680
|
return this.cachedObservable("authStateEvent$", () => this.signalingEvent$.pipe(tap((msg) => {
|
|
8614
|
-
logger$
|
|
8681
|
+
logger$8.debug("[Session] Received incoming message:", msg);
|
|
8615
8682
|
}), filterAs(isSignalwireAuthorizationStateMetadata, "params"), tap((event) => {
|
|
8616
|
-
logger$
|
|
8683
|
+
logger$8.debug("[Session] Authorization state event received:", event.authorization_state);
|
|
8617
8684
|
})));
|
|
8618
8685
|
}
|
|
8619
8686
|
get signalingEvent$() {
|
|
@@ -8651,15 +8718,15 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8651
8718
|
await firstValueFrom(this.authenticated$.pipe(takeUntil(this.destroyed$), filter(Boolean), take(1), timeout({ first: 15e3 })));
|
|
8652
8719
|
}
|
|
8653
8720
|
async handleAuthenticationError(error) {
|
|
8654
|
-
logger$
|
|
8721
|
+
logger$8.error("Authentication error:", error);
|
|
8655
8722
|
const isRecoverableAuthError = error instanceof JSONRPCError && (error.code === RPC_ERROR_REQUESTER_VALIDATION_FAILED || error.code === RPC_ERROR_INVALID_PARAMS || error.code === RPC_ERROR_AUTHENTICATION_FAILED);
|
|
8656
8723
|
const hasStoredState = await firstValueFrom(this.authorizationState$.pipe(take(1))) !== void 0;
|
|
8657
8724
|
if (isRecoverableAuthError && hasStoredState) {
|
|
8658
|
-
logger$
|
|
8725
|
+
logger$8.debug("[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh");
|
|
8659
8726
|
try {
|
|
8660
8727
|
await this.cleanupStoredConnectionParams();
|
|
8661
8728
|
} catch (cleanupError) {
|
|
8662
|
-
logger$
|
|
8729
|
+
logger$8.error("Failed to cleanup stored connection params:", cleanupError);
|
|
8663
8730
|
} finally {
|
|
8664
8731
|
this.transport.reconnect();
|
|
8665
8732
|
}
|
|
@@ -8674,19 +8741,19 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8674
8741
|
try {
|
|
8675
8742
|
await this.storage.setItem(this.authorizationStateKey, authorization_state);
|
|
8676
8743
|
} catch (error) {
|
|
8677
|
-
logger$
|
|
8744
|
+
logger$8.error("Failed to update authorization state in storage:", error);
|
|
8678
8745
|
this._errors$.next(new AuthStateHandlerError(error));
|
|
8679
8746
|
}
|
|
8680
8747
|
}
|
|
8681
8748
|
async reauthenticate(token, dpopToken, options) {
|
|
8682
|
-
logger$
|
|
8749
|
+
logger$8.debug("[Session] Re-authenticating session");
|
|
8683
8750
|
try {
|
|
8684
8751
|
let resolvedDpopToken = dpopToken;
|
|
8685
8752
|
if (!resolvedDpopToken && this.dpopManager?.initialized) try {
|
|
8686
8753
|
resolvedDpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
|
|
8687
8754
|
} catch (error) {
|
|
8688
8755
|
if (this._clientBound) throw error;
|
|
8689
|
-
logger$
|
|
8756
|
+
logger$8.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
|
|
8690
8757
|
}
|
|
8691
8758
|
const request = RPCReauthenticate({
|
|
8692
8759
|
project: this._authorization$.value?.project_id ?? "",
|
|
@@ -8694,24 +8761,24 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8694
8761
|
...resolvedDpopToken ? { dpop_token: resolvedDpopToken } : {}
|
|
8695
8762
|
});
|
|
8696
8763
|
await lastValueFrom(from(this.transport.execute(request)).pipe(throwOnRPCError(), take(1), catchError((err) => {
|
|
8697
|
-
logger$
|
|
8764
|
+
logger$8.error("[Session] Re-authentication RPC failed:", err);
|
|
8698
8765
|
throw err;
|
|
8699
8766
|
})));
|
|
8700
8767
|
if (options?.clientBound) this._clientBound = true;
|
|
8701
|
-
logger$
|
|
8768
|
+
logger$8.debug("[Session] Re-authentication successful, updating stored auth state");
|
|
8702
8769
|
} catch (error) {
|
|
8703
|
-
logger$
|
|
8770
|
+
logger$8.error("[Session] Re-authentication failed:", error);
|
|
8704
8771
|
this._errors$.next(new AuthStateHandlerError(error));
|
|
8705
8772
|
throw error;
|
|
8706
8773
|
}
|
|
8707
8774
|
}
|
|
8708
8775
|
async authenticate() {
|
|
8709
|
-
logger$
|
|
8776
|
+
logger$8.debug("[Session] Starting authentication process");
|
|
8710
8777
|
const persistedParams = await firstValueFrom(combineLatest({
|
|
8711
8778
|
protocol: this.transport.protocol$,
|
|
8712
8779
|
authorization_state: this.authorizationState$
|
|
8713
8780
|
}).pipe(take(1)));
|
|
8714
|
-
logger$
|
|
8781
|
+
logger$8.debug("[Session] Persisted params:\n", {
|
|
8715
8782
|
protocol: persistedParams.protocol,
|
|
8716
8783
|
authStateLength: persistedParams.authorization_state?.length
|
|
8717
8784
|
});
|
|
@@ -8719,16 +8786,16 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8719
8786
|
const storedToken = this.getCredential().token;
|
|
8720
8787
|
const isReconnect = hasReconnectState && storedToken;
|
|
8721
8788
|
let dpopToken;
|
|
8722
|
-
if (isReconnect) logger$
|
|
8789
|
+
if (isReconnect) logger$8.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
|
|
8723
8790
|
else if (this.onBeforeReconnect && this._clientBound) {
|
|
8724
|
-
logger$
|
|
8791
|
+
logger$8.debug("[Session] Refreshing credentials before fresh connect");
|
|
8725
8792
|
await this.onBeforeReconnect();
|
|
8726
8793
|
}
|
|
8727
8794
|
if ((!isReconnect || this._clientBound) && this.dpopManager?.initialized) try {
|
|
8728
8795
|
dpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.connect" });
|
|
8729
8796
|
} catch (error) {
|
|
8730
8797
|
if (this._clientBound) throw error;
|
|
8731
|
-
logger$
|
|
8798
|
+
logger$8.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
|
|
8732
8799
|
}
|
|
8733
8800
|
const rpcConnectRequest = RPCConnect({
|
|
8734
8801
|
authentication: isReconnect ? { jwt_token: storedToken } : this.authentication,
|
|
@@ -8745,12 +8812,12 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8745
8812
|
} : {}
|
|
8746
8813
|
});
|
|
8747
8814
|
const response = await lastValueFrom(from(this.transport.execute(rpcConnectRequest)).pipe(throwOnRPCError(), map((res) => res.result), filter(isRPCConnectResult), tap(() => {
|
|
8748
|
-
logger$
|
|
8815
|
+
logger$8.debug("[Session] Response passed filter, processing authentication result");
|
|
8749
8816
|
}), take(1), catchError((err) => {
|
|
8750
|
-
logger$
|
|
8817
|
+
logger$8.error("[Session] Authentication RPC failed:", err);
|
|
8751
8818
|
throw err;
|
|
8752
8819
|
})));
|
|
8753
|
-
logger$
|
|
8820
|
+
logger$8.debug("[Session] Processing authentication result:", {
|
|
8754
8821
|
hasProtocol: !!response.protocol,
|
|
8755
8822
|
hasAuthorization: !!response.authorization,
|
|
8756
8823
|
hasIceServers: !!response.ice_servers
|
|
@@ -8759,7 +8826,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8759
8826
|
this._authorization$.next(response.authorization);
|
|
8760
8827
|
this._iceServers$.next(response.ice_servers ?? []);
|
|
8761
8828
|
this._authenticated$.next(true);
|
|
8762
|
-
logger$
|
|
8829
|
+
logger$8.debug("[Session] Authentication completed successfully");
|
|
8763
8830
|
}
|
|
8764
8831
|
async disconnect() {
|
|
8765
8832
|
this.transport.disconnect();
|
|
@@ -8796,11 +8863,11 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8796
8863
|
async handleVertoAttach(attach) {
|
|
8797
8864
|
const { callID } = attach;
|
|
8798
8865
|
if (callID in this._calls$.value) {
|
|
8799
|
-
logger$
|
|
8866
|
+
logger$8.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
|
|
8800
8867
|
return;
|
|
8801
8868
|
}
|
|
8802
8869
|
const storedOptions = this.attachManager.consumePendingAttachment(callID);
|
|
8803
|
-
logger$
|
|
8870
|
+
logger$8.debug(`[Session] Creating reattached call for callID: ${callID}`);
|
|
8804
8871
|
const callSession = await this.createCall({
|
|
8805
8872
|
nodeId: attach.node_id,
|
|
8806
8873
|
callId: callID,
|
|
@@ -8832,7 +8899,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8832
8899
|
});
|
|
8833
8900
|
return callSession;
|
|
8834
8901
|
} catch (error) {
|
|
8835
|
-
logger$
|
|
8902
|
+
logger$8.error("[Session] Error creating outbound call:", error);
|
|
8836
8903
|
callSession?.destroy();
|
|
8837
8904
|
const callError = new CallCreateError(error instanceof TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
|
|
8838
8905
|
this._errors$.next(callError);
|
|
@@ -8850,7 +8917,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8850
8917
|
address = this._directory.get(addressId);
|
|
8851
8918
|
if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
|
|
8852
8919
|
} catch {
|
|
8853
|
-
logger$
|
|
8920
|
+
logger$8.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
|
|
8854
8921
|
}
|
|
8855
8922
|
const callSession = this.callFactory.createCall(address, { ...options });
|
|
8856
8923
|
this.subscribeTo(callSession.status$.pipe(filter((status) => status === "destroyed"), take(1)), () => {
|
|
@@ -8859,7 +8926,7 @@ var ClientSessionManager = class extends Destroyable {
|
|
|
8859
8926
|
});
|
|
8860
8927
|
return callSession;
|
|
8861
8928
|
} catch (error) {
|
|
8862
|
-
logger$
|
|
8929
|
+
logger$8.error("[Session] Error creating call session:", error);
|
|
8863
8930
|
throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
|
|
8864
8931
|
}
|
|
8865
8932
|
}
|
|
@@ -8907,7 +8974,7 @@ const isString = (obj) => typeof obj === "string";
|
|
|
8907
8974
|
|
|
8908
8975
|
//#endregion
|
|
8909
8976
|
//#region src/managers/ConversationsManager.ts
|
|
8910
|
-
const logger$
|
|
8977
|
+
const logger$7 = getLogger();
|
|
8911
8978
|
var ConversationMessagesFetcher = class extends Fetcher {
|
|
8912
8979
|
constructor(groupId, http) {
|
|
8913
8980
|
super(`/api/fabric/conversations/${groupId}/messages`, "page_size=100", http);
|
|
@@ -8947,13 +9014,13 @@ var ConversationsManager = class {
|
|
|
8947
9014
|
}
|
|
8948
9015
|
throw new ConversationError("Join Failed - Unexpected response");
|
|
8949
9016
|
} catch (error) {
|
|
8950
|
-
logger$
|
|
9017
|
+
logger$7.error("[ConversationsManager] Failed to join conversation:", error);
|
|
8951
9018
|
throw error;
|
|
8952
9019
|
}
|
|
8953
9020
|
}
|
|
8954
9021
|
async getConversationMessageCollection(addressId) {
|
|
8955
9022
|
const groupId = this.groupIds.get(addressId) ?? await this.join(addressId);
|
|
8956
|
-
return Promise.resolve(new ConversationMessageCollection(groupId, this.clientSession.signalingEvent$.pipe(filterAs(isConversationMessageMetadata, "params"), tap((event) => logger$
|
|
9023
|
+
return Promise.resolve(new ConversationMessageCollection(groupId, this.clientSession.signalingEvent$.pipe(filterAs(isConversationMessageMetadata, "params"), tap((event) => logger$7.debug("[ConversationsManager ] Conversation Event:", event)), map((params) => ({ ...params }))), this.http, this.onError));
|
|
8957
9024
|
}
|
|
8958
9025
|
async sendText(text, destinationAddressId) {
|
|
8959
9026
|
const groupId = this.groupIds.get(destinationAddressId) ?? await this.join(destinationAddressId);
|
|
@@ -8970,12 +9037,221 @@ var ConversationsManager = class {
|
|
|
8970
9037
|
})).ok) return;
|
|
8971
9038
|
throw new ConversationError("Send Text Failed - Unexpected response");
|
|
8972
9039
|
} catch (error) {
|
|
8973
|
-
logger$
|
|
9040
|
+
logger$7.error("[ConversationsManager] Failed to send text message:", error);
|
|
8974
9041
|
throw error;
|
|
8975
9042
|
}
|
|
8976
9043
|
}
|
|
8977
9044
|
};
|
|
8978
9045
|
|
|
9046
|
+
//#endregion
|
|
9047
|
+
//#region src/managers/DeviceTokenManager.ts
|
|
9048
|
+
const logger$6 = getLogger();
|
|
9049
|
+
/**
|
|
9050
|
+
* Resolves the token expiry timestamp (epoch seconds) using a 3-tier priority chain:
|
|
9051
|
+
* 1. `data.expires_at` — server-provided absolute timestamp
|
|
9052
|
+
* 2. `data.expires_in` — server-provided relative lifetime
|
|
9053
|
+
* 3. Fallback to `DEVICE_TOKEN_DEFAULT_EXPIRE_IN` with a warning
|
|
9054
|
+
*/
|
|
9055
|
+
function resolveExpiresAt(data) {
|
|
9056
|
+
if (data.expires_at) return data.expires_at;
|
|
9057
|
+
if (data.expires_in) return Math.floor(Date.now() / 1e3) + data.expires_in;
|
|
9058
|
+
logger$6.warn("[DeviceToken] Could not determine token expiry, using default");
|
|
9059
|
+
return Math.floor(Date.now() / 1e3) + DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
|
|
9060
|
+
}
|
|
9061
|
+
/**
|
|
9062
|
+
* Resolves the token TTL in seconds from a fresh response.
|
|
9063
|
+
* Called at token receive time so `expires_at - now` reflects the original lifetime.
|
|
9064
|
+
*
|
|
9065
|
+
* 1. `data.expires_in` — server-provided TTL directly
|
|
9066
|
+
* 2. `data.expires_at - now` — derive TTL from absolute timestamp
|
|
9067
|
+
* 3. Fallback to `DEVICE_TOKEN_DEFAULT_EXPIRE_IN`
|
|
9068
|
+
*/
|
|
9069
|
+
function resolveExpireIn(data) {
|
|
9070
|
+
if (data.expires_in) return data.expires_in;
|
|
9071
|
+
if (data.expires_at) return Math.max(data.expires_at - Math.floor(Date.now() / 1e3), 1);
|
|
9072
|
+
return DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
|
|
9073
|
+
}
|
|
9074
|
+
/**
|
|
9075
|
+
* Manages the Client Bound SAT lifecycle: activation, token exchange,
|
|
9076
|
+
* reauthentication, and automatic refresh scheduling.
|
|
9077
|
+
*
|
|
9078
|
+
* Extends {@link Destroyable} for automatic RxJS subscription and subject cleanup.
|
|
9079
|
+
* Uses a reactive pipeline (`BehaviorSubject` + `switchMap(timer())`) instead of
|
|
9080
|
+
* raw `setTimeout` for refresh scheduling.
|
|
9081
|
+
*/
|
|
9082
|
+
var DeviceTokenManager = class extends Destroyable {
|
|
9083
|
+
constructor(dpopManager, http, errorHandler, getCredential) {
|
|
9084
|
+
super();
|
|
9085
|
+
this.dpopManager = dpopManager;
|
|
9086
|
+
this.http = http;
|
|
9087
|
+
this.errorHandler = errorHandler;
|
|
9088
|
+
this.getCredential = getCredential;
|
|
9089
|
+
this._currentToken$ = this.createBehaviorSubject(null);
|
|
9090
|
+
this._refreshInProgress = false;
|
|
9091
|
+
this._effectiveExpireIn = DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
|
|
9092
|
+
this.subscribeTo(this._currentToken$.pipe(filter(Boolean), switchMap((tokenData) => {
|
|
9093
|
+
const expiresAt = resolveExpiresAt(tokenData);
|
|
9094
|
+
const refreshIn = Math.max(expiresAt * 1e3 - Date.now() - DEVICE_TOKEN_REFRESH_BUFFER_MS, 1e3);
|
|
9095
|
+
logger$6.debug(`[DeviceToken] Scheduling Client Bound SAT refresh in ${refreshIn}ms`);
|
|
9096
|
+
return timer(refreshIn);
|
|
9097
|
+
})), () => {
|
|
9098
|
+
this.executeRefresh();
|
|
9099
|
+
});
|
|
9100
|
+
}
|
|
9101
|
+
/** Current token TTL in milliseconds. Used to extend cached credential expiry on refresh. */
|
|
9102
|
+
get effectiveExpireIn() {
|
|
9103
|
+
return this._effectiveExpireIn;
|
|
9104
|
+
}
|
|
9105
|
+
/**
|
|
9106
|
+
* Activates the Client Bound SAT flow when the subscriber's token has
|
|
9107
|
+
* `sat:refresh` scope.
|
|
9108
|
+
*
|
|
9109
|
+
* Steps:
|
|
9110
|
+
* 1. Check subscriber's `sat_claims` for `sat:refresh` scope
|
|
9111
|
+
* 2. Call `/api/fabric/subscriber/devices/token` with a DPoP proof
|
|
9112
|
+
* 3. Reauthenticate the session with the Client Bound SAT + DPoP proof
|
|
9113
|
+
* 4. Emit token to trigger the reactive refresh pipeline
|
|
9114
|
+
*/
|
|
9115
|
+
async activate(subscriber, session, updateCredential) {
|
|
9116
|
+
const { satClaims } = subscriber;
|
|
9117
|
+
if (!satClaims?.scope?.includes(SAT_REFRESH_SCOPE)) {
|
|
9118
|
+
logger$6.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
|
|
9119
|
+
return;
|
|
9120
|
+
}
|
|
9121
|
+
this._session = session;
|
|
9122
|
+
this._updateCredential = updateCredential;
|
|
9123
|
+
try {
|
|
9124
|
+
const tokenData = await this.obtainToken();
|
|
9125
|
+
if (!tokenData.expires_at && !tokenData.expires_in && satClaims.expires_at) tokenData.expires_at = satClaims.expires_at;
|
|
9126
|
+
this._effectiveExpireIn = resolveExpireIn(tokenData);
|
|
9127
|
+
const rpcProof = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
|
|
9128
|
+
await session.reauthenticate(tokenData.token, rpcProof, { clientBound: true });
|
|
9129
|
+
updateCredential({ token: tokenData.token });
|
|
9130
|
+
logger$6.info("[DeviceToken] Client Bound SAT activated successfully");
|
|
9131
|
+
this._currentToken$.next(tokenData);
|
|
9132
|
+
} catch (error) {
|
|
9133
|
+
logger$6.error("[DeviceToken] Failed to activate Client Bound SAT:", error);
|
|
9134
|
+
this.errorHandler(new DPoPInitError(error, "Failed to activate Client Bound SAT"));
|
|
9135
|
+
const credential = this.getCredential();
|
|
9136
|
+
const expiresAt = satClaims.expires_at ?? (credential.expiry_at ? credential.expiry_at / 1e3 : Date.now() / 1e3 + DEVICE_TOKEN_DEFAULT_EXPIRE_IN / 1e3);
|
|
9137
|
+
this._currentToken$.next({
|
|
9138
|
+
token: credential.token ?? "",
|
|
9139
|
+
expires_at: expiresAt
|
|
9140
|
+
});
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
/**
|
|
9144
|
+
* Obtains a Client Bound SAT from `/api/fabric/subscriber/devices/token`.
|
|
9145
|
+
* Returns the full {@link DeviceTokenResponse} including expiry metadata.
|
|
9146
|
+
*/
|
|
9147
|
+
async obtainToken() {
|
|
9148
|
+
const dpopProof = await this.dpopManager.createHttpProof({
|
|
9149
|
+
method: "POST",
|
|
9150
|
+
uri: DEVICE_TOKEN_ENDPOINT
|
|
9151
|
+
});
|
|
9152
|
+
const response = await this.http.request({
|
|
9153
|
+
url: DEVICE_TOKEN_ENDPOINT,
|
|
9154
|
+
...POST_PARAMS,
|
|
9155
|
+
body: JSON.stringify({
|
|
9156
|
+
dpop_token: dpopProof,
|
|
9157
|
+
expire_in: DEVICE_TOKEN_DEFAULT_EXPIRE_IN
|
|
9158
|
+
})
|
|
9159
|
+
});
|
|
9160
|
+
if (!response.ok || !response.body) throw new DeviceTokenError(`Failed to obtain device token: ${response.status} ${response.statusText}`);
|
|
9161
|
+
const data = JSON.parse(response.body);
|
|
9162
|
+
if (!data.token) throw new DeviceTokenError("Device token response missing token field");
|
|
9163
|
+
return data;
|
|
9164
|
+
}
|
|
9165
|
+
/**
|
|
9166
|
+
* Refreshes the Client Bound SAT via `/api/fabric/subscriber/devices/refresh`.
|
|
9167
|
+
*
|
|
9168
|
+
* Creates a fresh DPoP proof, calls the refresh endpoint, reauthenticates
|
|
9169
|
+
* the WebSocket session, and returns the new token data (scheduling is
|
|
9170
|
+
* handled by the reactive pipeline).
|
|
9171
|
+
*/
|
|
9172
|
+
async refreshToken(session, currentToken, updateCredential) {
|
|
9173
|
+
logger$6.debug("[DeviceToken] Refreshing Client Bound SAT");
|
|
9174
|
+
const dpopProof = await this.dpopManager.createHttpProof({
|
|
9175
|
+
method: "POST",
|
|
9176
|
+
uri: DEVICE_REFRESH_ENDPOINT,
|
|
9177
|
+
accessToken: currentToken
|
|
9178
|
+
});
|
|
9179
|
+
const response = await this.http.request({
|
|
9180
|
+
url: DEVICE_REFRESH_ENDPOINT,
|
|
9181
|
+
...POST_PARAMS,
|
|
9182
|
+
body: JSON.stringify({
|
|
9183
|
+
dpop_token: dpopProof,
|
|
9184
|
+
expire_in: this._effectiveExpireIn
|
|
9185
|
+
})
|
|
9186
|
+
});
|
|
9187
|
+
if (!response.ok || !response.body) throw new TokenRefreshError(`Failed to refresh device token: ${response.status} ${response.statusText}`);
|
|
9188
|
+
const data = JSON.parse(response.body);
|
|
9189
|
+
if (!data.token) throw new TokenRefreshError("Device token refresh response missing token field");
|
|
9190
|
+
if (!data.expires_at && !data.expires_in) data.expires_in = this._effectiveExpireIn;
|
|
9191
|
+
this._effectiveExpireIn = resolveExpireIn(data);
|
|
9192
|
+
const rpcProof = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
|
|
9193
|
+
await session.reauthenticate(data.token, rpcProof);
|
|
9194
|
+
updateCredential({ token: data.token });
|
|
9195
|
+
logger$6.info("[DeviceToken] Client Bound SAT refreshed successfully");
|
|
9196
|
+
return data;
|
|
9197
|
+
}
|
|
9198
|
+
/**
|
|
9199
|
+
* Executes a refresh with retry and exponential backoff.
|
|
9200
|
+
* On success, emits to `_currentToken$` to schedule the next refresh.
|
|
9201
|
+
* On all retries exhausted, emits to `errorHandler`.
|
|
9202
|
+
*/
|
|
9203
|
+
async executeRefresh() {
|
|
9204
|
+
if (this._refreshInProgress) {
|
|
9205
|
+
logger$6.debug("[DeviceToken] Refresh already in progress, skipping");
|
|
9206
|
+
return;
|
|
9207
|
+
}
|
|
9208
|
+
const session = this._session;
|
|
9209
|
+
const updateCredential = this._updateCredential;
|
|
9210
|
+
if (!session || !updateCredential) {
|
|
9211
|
+
logger$6.warn("[DeviceToken] Cannot refresh: session or updateCredential not set");
|
|
9212
|
+
return;
|
|
9213
|
+
}
|
|
9214
|
+
if (!session.authenticated) {
|
|
9215
|
+
logger$6.debug("[DeviceToken] Session not authenticated, deferring refresh");
|
|
9216
|
+
return;
|
|
9217
|
+
}
|
|
9218
|
+
this._refreshInProgress = true;
|
|
9219
|
+
try {
|
|
9220
|
+
const currentToken = this.getCredential().token;
|
|
9221
|
+
if (!currentToken) throw new TokenRefreshError("No current token available for refresh");
|
|
9222
|
+
const newTokenData = await this.retryRefresh(session, currentToken, updateCredential);
|
|
9223
|
+
this._currentToken$.next(newTokenData);
|
|
9224
|
+
} catch (error) {
|
|
9225
|
+
logger$6.error("[DeviceToken] Automatic Client Bound SAT refresh failed:", error);
|
|
9226
|
+
this.errorHandler(error instanceof TokenRefreshError ? error : new TokenRefreshError("Automatic token refresh failed", error));
|
|
9227
|
+
} finally {
|
|
9228
|
+
this._refreshInProgress = false;
|
|
9229
|
+
}
|
|
9230
|
+
}
|
|
9231
|
+
/**
|
|
9232
|
+
* Retries `refreshToken()` up to `DEVICE_TOKEN_REFRESH_MAX_RETRIES` times
|
|
9233
|
+
* with exponential backoff (1s, 2s, 4s).
|
|
9234
|
+
*/
|
|
9235
|
+
async retryRefresh(session, currentToken, updateCredential) {
|
|
9236
|
+
let lastError;
|
|
9237
|
+
for (let attempt = 0; attempt < DEVICE_TOKEN_REFRESH_MAX_RETRIES; attempt++) try {
|
|
9238
|
+
return await this.refreshToken(session, currentToken, updateCredential);
|
|
9239
|
+
} catch (error) {
|
|
9240
|
+
lastError = error;
|
|
9241
|
+
if (attempt < DEVICE_TOKEN_REFRESH_MAX_RETRIES - 1) {
|
|
9242
|
+
const delay = DEVICE_TOKEN_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt);
|
|
9243
|
+
logger$6.warn(`[DeviceToken] Refresh attempt ${attempt + 1} failed, retrying in ${delay}ms`);
|
|
9244
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
9247
|
+
throw lastError instanceof Error ? lastError : new TokenRefreshError("All refresh retries exhausted", lastError);
|
|
9248
|
+
}
|
|
9249
|
+
/** Cleans up the manager, cancelling the reactive pipeline and all subscriptions. */
|
|
9250
|
+
destroy() {
|
|
9251
|
+
super.destroy();
|
|
9252
|
+
}
|
|
9253
|
+
};
|
|
9254
|
+
|
|
8979
9255
|
//#endregion
|
|
8980
9256
|
//#region src/managers/DiagnosticsCollector.ts
|
|
8981
9257
|
const logger$5 = getLogger();
|
|
@@ -9905,13 +10181,43 @@ var SignalWire = class extends Destroyable {
|
|
|
9905
10181
|
this._errors$.next(error);
|
|
9906
10182
|
});
|
|
9907
10183
|
await this._clientSession.connect();
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
10184
|
+
if (this._dpopManager?.initialized) {
|
|
10185
|
+
if (this._refreshTimerId) {
|
|
10186
|
+
clearTimeout(this._refreshTimerId);
|
|
10187
|
+
this._refreshTimerId = void 0;
|
|
10188
|
+
logger$1.debug("[SignalWire] Developer refresh disabled — Client Bound SAT activation starting");
|
|
10189
|
+
}
|
|
10190
|
+
this._deviceTokenManager = new DeviceTokenManager(this._dpopManager, this._deps.http, (error) => this._errors$.next(error), () => this._deps.credential);
|
|
10191
|
+
await this._deviceTokenManager.activate(this._deps.subscriber, this._clientSession, (cred) => {
|
|
10192
|
+
this._deps.credential = {
|
|
10193
|
+
...this._deps.credential,
|
|
10194
|
+
...cred
|
|
10195
|
+
};
|
|
10196
|
+
});
|
|
10197
|
+
}
|
|
10198
|
+
this.subscribeTo(this._clientSession.authenticated$.pipe(skip(1), filter(Boolean)), async () => {
|
|
10199
|
+
try {
|
|
10200
|
+
if (this._deviceTokenManager) {
|
|
10201
|
+
await this._deviceTokenManager.activate(this._deps.subscriber, this._clientSession, (cred) => {
|
|
10202
|
+
this._deps.credential = {
|
|
10203
|
+
...this._deps.credential,
|
|
10204
|
+
...cred
|
|
10205
|
+
};
|
|
10206
|
+
});
|
|
10207
|
+
logger$1.debug("[SignalWire] Client Bound SAT re-activated after reconnect");
|
|
10208
|
+
}
|
|
10209
|
+
} catch (error) {
|
|
10210
|
+
logger$1.error("[SignalWire] Client Bound SAT re-activation failed (non-fatal):", error);
|
|
10211
|
+
this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
10212
|
+
}
|
|
10213
|
+
try {
|
|
10214
|
+
logger$1.debug("[SignalWire] Re-registering subscriber after reconnect");
|
|
10215
|
+
await this.register();
|
|
9911
10216
|
logger$1.debug("[SignalWire] Subscriber re-registered successfully after reconnect");
|
|
9912
|
-
}
|
|
10217
|
+
} catch (error) {
|
|
9913
10218
|
logger$1.error("[SignalWire] Re-registration failed after reconnect:", error);
|
|
9914
|
-
|
|
10219
|
+
this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
|
|
10220
|
+
}
|
|
9915
10221
|
});
|
|
9916
10222
|
const conversationManager = new ConversationsManager(this._clientSession, this._deps.http, () => this._deps.getSubscriberFromAddressId(), errorHandler);
|
|
9917
10223
|
const directory = new DirectoryManager(this._deps.http, this._clientSession, conversationManager, errorHandler);
|