@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/index.cjs 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$27 = require_operators.getLogger();
142
+ const logger$28 = require_operators.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$27.error("[HTTPRequestController] Request error:", error);
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$4 = request.timeout ?? this.requestTimeout;
232
- logger$27.debug("[HTTPRequestController] Executing request:", {
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$27.debug("[HTTPRequestController] Response received:", {
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 require_operators.RequestTimeoutError(`Request timeout after ${timeout$4}ms`, { cause: error });
262
- logger$27.error("[HTTPRequestController] Request failed:", error);
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$27.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
277
- } else logger$27.warn("[HTTPRequestController] No credentials available for authentication");
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$26 = require_operators.getLogger();
529
+ const logger$27 = require_operators.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$26.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
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$26.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
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$25 = require_operators.getLogger();
1207
+ const logger$26 = require_operators.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$25.debug("[DeviceController] Device change detected");
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((0, rxjs.map)((state) => state.videoinput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$)));
1279
1292
  }
1280
1293
  get selectedAudioInputDevice$() {
1281
- return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, rxjs.map)((state) => state.audioinput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.tap)((info) => logger$25.debug("[DeviceController] Selected audio input device changed:", info))));
1294
+ return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, rxjs.map)((state) => state.audioinput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.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((0, rxjs.map)((state) => state.audiooutput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.tap)((info) => logger$25.debug("[DeviceController] Selected audio output device changed:", info))));
1297
+ return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, rxjs.map)((state) => state.audiooutput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.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((0, rxjs.map)((state) => state.videoinput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.tap)((info) => logger$25.debug("[DeviceController] Selected video input device changed:", info))));
1300
+ return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, rxjs.map)((state) => state.videoinput), (0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.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$25.debug("[DeviceController] Setting selected video input device:", device);
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$25.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
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$25.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
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$25.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
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 = (0, rxjs.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
1506
- logger$25.debug("[DeviceController] Polling devices due to interval");
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$25.debug("[DeviceController] Devices enumerated:", {
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$25.error("[DeviceController] Failed to enumerate devices:", error);
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$25.error("[DeviceController] Failed to get device capabilities:", error);
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$24 = require_operators.getLogger();
1818
+ const logger$25 = require_operators.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$24.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
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$24.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
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$24.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
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$24.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
1976
+ logger$25.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
1964
1977
  return this._fingerprint;
1965
1978
  } catch (error) {
1966
- logger$24.warn("[DPoP] Stored key pair unusable, generating new one:", error);
1979
+ logger$25.warn("[DPoP] Stored key pair unusable, generating new one:", error);
1967
1980
  await deleteKeyPairFromDB();
1968
1981
  }
1969
- logger$24.debug("[DPoP] Generating RSA key pair");
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$24.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
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$24.debug("[DPoP] Controller destroyed");
2063
+ logger$25.debug("[DPoP] Controller destroyed");
2051
2064
  }
2052
2065
  get publicJwk() {
2053
2066
  if (!this._publicJwk) throw new require_operators.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$23 = require_operators.getLogger();
2086
+ const logger$24 = require_operators.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$23.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
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$23.debug("NetworkMonitor: event listeners attached");
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$23.debug("NetworkMonitor: event listeners removed");
2162
+ logger$24.debug("NetworkMonitor: event listeners removed");
2150
2163
  }
2151
2164
  handleOnline() {
2152
- logger$23.info("NetworkMonitor: browser went online");
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$23.info("NetworkMonitor: browser went offline");
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$23.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
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$22 = require_operators.getLogger();
2298
+ const logger$23 = require_operators.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$22.warn("[PreflightRunner] Media bandwidth test failed:", error);
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$22.error("[PreflightRunner] Preflight test failed:", error);
2359
+ logger$23.error("[PreflightRunner] Preflight test failed:", error);
2347
2360
  throw new require_operators.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$22.warn("[PreflightRunner] Device test failed:", error);
2390
+ logger$23.warn("[PreflightRunner] Device test failed:", error);
2378
2391
  } finally {
2379
2392
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
2380
2393
  }
@@ -2401,7 +2414,7 @@ var PreflightRunner = class extends Destroyable {
2401
2414
  const candidateTypes = /* @__PURE__ */ new Set();
2402
2415
  const startTime = Date.now();
2403
2416
  const gatheringComplete = new Promise((resolve) => {
2404
- const timer$1 = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);
2417
+ const timer$2 = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);
2405
2418
  peerConnection.onicecandidate = (event) => {
2406
2419
  if (event.candidate) {
2407
2420
  const candidateStr = event.candidate.candidate;
@@ -2409,7 +2422,7 @@ var PreflightRunner = class extends Destroyable {
2409
2422
  if (candidateStr.includes("typ srflx")) candidateTypes.add("srflx");
2410
2423
  if (candidateStr.includes("typ relay")) candidateTypes.add("relay");
2411
2424
  } else {
2412
- clearTimeout(timer$1);
2425
+ clearTimeout(timer$2);
2413
2426
  resolve();
2414
2427
  }
2415
2428
  };
@@ -2432,7 +2445,7 @@ var PreflightRunner = class extends Destroyable {
2432
2445
  rttMs
2433
2446
  };
2434
2447
  } catch (error) {
2435
- logger$22.warn("[PreflightRunner] ICE connectivity test failed:", error);
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$21 = require_operators.getLogger();
2495
+ const logger$22 = require_operators.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$21.debug("VisibilityController: listening for visibilitychange events");
2520
- } else logger$21.debug("VisibilityController: document visibility API not available, defaulting to visible");
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$21.debug("VisibilityController: removed visibilitychange listener");
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$21.debug("VisibilityController: visibility changed", {
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$20 = require_operators.getLogger();
2779
+ const logger$21 = require_operators.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$20.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
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$20.warn("[AttachManager] Failed to write attached calls to storage", error);
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$20.warn("[AttachManager] Skip attach for calls with no destination");
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$20.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
2866
+ logger$21.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
2853
2867
  succeeded = true;
2854
2868
  break;
2855
2869
  } catch (error) {
2856
- logger$20.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
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$20.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
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$19 = require_operators.getLogger();
3177
+ const logger$20 = require_operators.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$19.error("[Participant.startScreenShare] Screen share error:", error);
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$19.error("[Participant.startScreenShare] Screen share error:", error);
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$19.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
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$19.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
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$19.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
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$19.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
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$19.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
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$18 = require_operators.getLogger();
3785
+ const logger$19 = require_operators.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$18.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
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$18.debug("[CallEventsManager] Handling member update event for member ID:", member);
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$18.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
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$18.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
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$18.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
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$18.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
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$18.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
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$18.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
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$18.error("[CallEventsManager] Error fetching layouts:", error);
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$18.debug("[CallEventsManager] Updating participant:", member.member_id, {
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((0, rxjs.filter)(isCallJoinedPayload), (0, rxjs.tap)((event) => {
3994
- logger$18.debug("[CallEventsManager] Call joined event:", event);
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(require_operators.filterAs(isLayoutChangedPayload, "layout"), (0, rxjs.tap)((event) => {
3999
- logger$18.debug("[CallEventsManager] Layout changed event:", event);
4013
+ logger$19.debug("[CallEventsManager] Layout changed event:", event);
4000
4014
  })));
4001
4015
  }
4002
4016
  get memberUpdates$() {
4003
4017
  return this.cachedObservable("memberUpdates$", () => (0, rxjs.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, rxjs.map)((event) => event.member), (0, rxjs.tap)((event) => {
4004
- logger$18.debug("[CallEventsManager] Member update event:", event);
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$17 = require_operators.getLogger();
4274
+ const logger$18 = require_operators.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$17.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
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$17.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
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$17.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
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$17.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
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$17.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
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$17.debug("[ICEGatheringController] Handling ICE gathering complete");
4330
- logger$17.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
4331
- logger$17.debug("[ICEGatheringController] ICE gathering complete");
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$17.debug("[ICEGatheringController] Local SDP is valid");
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$17.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
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$17.warn("[ICEGatheringController] ICE candidate timeout");
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$17.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
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$17.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
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(),
@@ -4375,14 +4389,14 @@ var ICEGatheringController = class extends Destroyable {
4375
4389
  });
4376
4390
  if (!(this.peerConnection.connectionState === "connected")) this.peerConnection.restartIce();
4377
4391
  }
4378
- removeTimer(timer$1) {
4379
- if (this[timer$1]) {
4380
- clearTimeout(this[timer$1]);
4381
- this[timer$1] = void 0;
4392
+ removeTimer(timer$2) {
4393
+ if (this[timer$2]) {
4394
+ clearTimeout(this[timer$2]);
4395
+ this[timer$2] = void 0;
4382
4396
  }
4383
4397
  }
4384
4398
  clearAllTimers() {
4385
- logger$17.debug("[ICEGatheringController] Clearing all timers");
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$17.debug("[ICEGatheringController] Destroying ICEGatheringController");
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$16 = require_operators.getLogger();
4417
+ const logger$17 = require_operators.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$16.debug("[LocalStreamController] Building local media stream.");
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$16.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
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$16.debug("[LocalStreamController] Screen share media obtained:", stream);
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$16.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
4472
+ logger$17.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
4459
4473
  stream = await this.options.getUserMedia(constraints);
4460
- logger$16.debug("[LocalStreamController] User media obtained:", stream);
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$16.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
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$16.debug(`[LocalStreamController] track not found: ${trackId}`);
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$16.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
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$16.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
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$15 = require_operators.getLogger();
4563
+ const logger$16 = require_operators.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$15.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
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$15.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
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$15.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
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$15.error("[TransceiverController] stopTrackSender error", kind, error);
4691
+ logger$16.error("[TransceiverController] stopTrackSender error", kind, error);
4678
4692
  this.options.onError?.(new require_operators.MediaTrackError("stopTrackSender", kind, error));
4679
4693
  }
4680
4694
  }
4681
4695
  async restoreTrackSender(kind) {
4682
4696
  try {
4683
- logger$15.debug("[TransceiverController] restoreTrackSender called", kind);
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$15.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
4707
+ logger$16.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
4694
4708
  if (Object.keys(constraints).length === 0) {
4695
- logger$15.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
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$15.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
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$15.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
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$15.error("[TransceiverController] restoreTrackSender error", kind, error);
4723
+ logger$16.error("[TransceiverController] restoreTrackSender error", kind, error);
4710
4724
  this.options.onError?.(new require_operators.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$15.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
4751
- logger$15.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
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$15.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
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$15.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
4771
+ logger$16.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
4758
4772
  this.options.onError?.(new require_operators.MediaTrackError("updateSendersConstraints", kind, fallbackError));
4759
4773
  }
4760
4774
  }
@@ -4782,7 +4796,7 @@ var TransceiverController = class extends Destroyable {
4782
4796
  if (!newTrack) throw new require_operators.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$15.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
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$14 = require_operators.getLogger();
4829
+ const logger$15 = require_operators.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$14.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
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$14.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
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$14.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
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$14.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
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$14.warn("[RTCPeerConnectionController] No local stream available to update input device.");
4871
+ logger$15.warn("[RTCPeerConnectionController] No local stream available to update input device.");
4858
4872
  return;
4859
4873
  }
4860
- logger$14.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
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$14.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
4879
+ logger$15.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
4866
4880
  if (!deviceInfo) {
4867
- logger$14.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
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$14.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
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$14.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
4892
+ logger$15.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
4879
4893
  }
4880
4894
  }
4881
- logger$14.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
4895
+ logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
4882
4896
  } catch (error) {
4883
- logger$14.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
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((0, rxjs.auditTime)(0), (0, rxjs.exhaustMap)(async () => this.startNegotiation())), {
5119
5133
  next: () => {
5120
- logger$14.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
5134
+ logger$15.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
5121
5135
  },
5122
5136
  error: (error) => {
5123
- logger$14.error("[RTCPeerConnectionController] Start Negotiation error:", error);
5137
+ logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
5124
5138
  this._errors$.next(error);
5125
5139
  }
5126
5140
  });
5127
5141
  this.subscribeTo((0, rxjs.merge)(this.deviceController.selectedAudioInputDevice$.pipe((0, rxjs.map)((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe((0, rxjs.map)((deviceInfo) => ["video", deviceInfo]))).pipe((0, rxjs.skipWhile)(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
5128
- logger$14.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
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$14.error("[RTCPeerConnectionController] Initialization error:", error);
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$14.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
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$14.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
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$14.debug("[RTCPeerConnectionController] Starting negotiation.");
5200
+ logger$15.debug("[RTCPeerConnectionController] Starting negotiation.");
5187
5201
  try {
5188
5202
  const { offerOptions } = this;
5189
- logger$14.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
5203
+ logger$15.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
5190
5204
  await this.createOffer(offerOptions);
5191
5205
  } catch (error) {
5192
- logger$14.error("[RTCPeerConnectionController] Error during negotiation:", error);
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$14.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
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$14.error("[RTCPeerConnectionController] Error updating answer status:", error);
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$14.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
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$14.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
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$14.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
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$14.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
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$14.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
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$14.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
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$14.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
5384
+ logger$15.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
5371
5385
  } catch (error) {
5372
- logger$14.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
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$14.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
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$14.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
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$14.debug("[RTCPeerConnectionController] ICE transport policy restored");
5409
+ logger$15.debug("[RTCPeerConnectionController] ICE transport policy restored");
5396
5410
  } catch (error) {
5397
- logger$14.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
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$14.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
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$14.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
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$14.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
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$14.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
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 require_operators.DependencyError("RTCPeerConnection is not initialized");
5447
5461
  this.peerConnection.ontrack = (event) => {
5448
- logger$14.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
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$14.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
5488
+ logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
5475
5489
  } catch (error) {
5476
- logger$14.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
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$14.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
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$14.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
5513
+ logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
5500
5514
  } catch (error) {
5501
- logger$14.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
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$14.warn("[RTCPeerConnectionController] No live audio sender to replace");
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$14.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
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$14.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
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$14.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
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$14.debug("[RTCPeerConnectionController] Setting remote description:", answer);
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$13 = require_operators.getLogger();
5647
+ const logger$14 = require_operators.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$13.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
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$13.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
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$13.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
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$13.debug("[WebRTCManager] Received Verto answer event:", event);
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$13.debug("[WebRTCManager] Received Verto mediaParams event:", event);
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$13.warn("[WebRTCManager] Error applying server-pushed media params:", error);
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.webRtcCallSession);
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$13.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
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$13.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
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$13.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
5821
+ logger$14.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
5807
5822
  this.onError?.(new require_operators.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$13.warn("[WebRTCManager] Error updating media constraints:", error);
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$13.warn("[WebRTCManager] No peer connection for keyframe request");
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$13.warn("[WebRTCManager] No video receiver for keyframe request");
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$13.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
5848
- } else logger$13.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
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$13.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
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$13.warn("[WebRTCManager] No peer connection for ICE restart");
5892
+ logger$14.warn("[WebRTCManager] No peer connection for ICE restart");
5869
5893
  return;
5870
5894
  }
5871
5895
  await controller.triggerIceRestart(relayOnly);
5872
- logger$13.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
5896
+ logger$14.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
5873
5897
  } catch (error) {
5874
- logger$13.error("[WebRTCManager] ICE restart failed:", error);
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$13.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
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$13.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
5920
+ logger$14.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
5897
5921
  } catch (error) {
5898
- logger$13.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
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$13.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
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$13.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
5944
+ logger$14.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
5921
5945
  }
5922
5946
  } catch (error) {
5923
- logger$13.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
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, vertoMethod);
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$13.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
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$13.warn("[WebRTCManager] Error processing modify response:", error);
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(require_operators.getValueFrom(response, "result.node_id") ?? null);
6008
6033
  const memberId = require_operators.getValueFrom(response, "result.result.result.memberID") ?? null;
6009
6034
  const callId = require_operators.getValueFrom(response, "result.result.result.callID") ?? null;
6010
- logger$13.debug("[WebRTCManager] Verto invite response:", {
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) this.webRtcCallSession.addCallId(callId);
6018
- this.attachManager.attach(this.webRtcCallSession);
6019
- logger$13.info("[WebRTCManager] Verto invite successful");
6020
- logger$13.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
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$13.error("[WebRTCManager] Verto invite failed:", response);
6052
+ logger$14.error("[WebRTCManager] Verto invite failed:", response);
6023
6053
  const inviteError = response.error ? new require_operators.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$13.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
6097
+ logger$14.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
6068
6098
  const vertoByeOrAccepted = await (0, rxjs.firstValueFrom)((0, rxjs.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, rxjs.takeUntil)(this.destroyed$))).catch(() => null);
6069
6099
  if (vertoByeOrAccepted === null) {
6070
- logger$13.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
6100
+ logger$14.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
6071
6101
  return;
6072
6102
  }
6073
6103
  if (isVertoByeMessage(vertoByeOrAccepted)) {
6074
- logger$13.info("[WebRTCManager] Inbound call ended by remote before answer.");
6104
+ logger$14.info("[WebRTCManager] Inbound call ended by remote before answer.");
6075
6105
  this.callSession?.destroy();
6076
6106
  } else if (!vertoByeOrAccepted) {
6077
- logger$13.info("[WebRTCManager] Inbound call rejected by user.");
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$13.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
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$13.error("[WebRTCManager] Error creating inbound answer:", error);
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$13.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
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.webRtcCallSession);
6175
+ this.attachManager.detach(this.buildAttachableCall());
6145
6176
  this.callSession?.destroy();
6146
6177
  });
6147
6178
  }
6148
- getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod) {
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 = vertoMethod === "verto.invite";
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$13.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
6196
+ logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
6166
6197
  const vertoByeOrAccepted = await (0, rxjs.firstValueFrom)((0, rxjs.race)(this.vertoBye$, this.webRtcCallSession.answered$));
6167
6198
  if (isVertoByeMessage(vertoByeOrAccepted)) {
6168
- logger$13.info("[WebRTCManager] Call ended before answer was sent.");
6199
+ logger$14.info("[WebRTCManager] Call ended before answer was sent.");
6169
6200
  this.callSession?.destroy();
6170
6201
  } else if (!vertoByeOrAccepted) {
6171
- logger$13.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
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$13.debug("[WebRTCManager] Call accepted, sending answer");
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.webRtcCallSession);
6215
+ await this.attachManager.attach(this.buildAttachableCall());
6185
6216
  } catch (error) {
6186
- logger$13.error("[WebRTCManager] Error sending Verto answer:", error);
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 (0, rxjs.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, rxjs.filter)((state) => state === "connected"), (0, rxjs.take)(1), (0, rxjs.timeout)(this._screenShareTimeoutMs)));
6276
6307
  this._screenShareStatus$.next("started");
6277
- logger$13.info("[WebRTCManager] Screen share started successfully.");
6308
+ logger$14.info("[WebRTCManager] Screen share started successfully.");
6278
6309
  return rtcPeerConnController.id;
6279
6310
  } catch (error) {
6280
- logger$13.warn("[WebRTCManager] Error initializing additional peer connection:", error);
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$13.warn("[WebRTCManager] No active screen share to stop.");
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$13.debug("[WebRTCManager] No screen share peer connection found.");
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$13.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
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.webRtcCallSession);
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$13.warn("[WebRTCManager] Error sending DTMF digits:", error);
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$13.debug("[WebRTCManager] Transferring call with options:", options);
6390
+ logger$14.debug("[WebRTCManager] Transferring call with options:", options);
6360
6391
  await this.executeVerto(message);
6361
6392
  } catch (error) {
6362
- logger$13.error("[WebRTCManager] Error transferring call:", error);
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$12 = require_operators.getLogger();
6415
+ const logger$13 = require_operators.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$12.debug("[RTCStatsMonitor] Starting stats monitoring");
6505
+ logger$13.debug("[RTCStatsMonitor] Starting stats monitoring");
6475
6506
  this.subscribeTo((0, rxjs.interval)(this.pollingIntervalMs).pipe((0, rxjs.filter)(() => this.running), (0, rxjs.switchMap)(() => (0, rxjs.from)(this.peerConnection.getStats()).pipe((0, rxjs.catchError)((err) => {
6476
- logger$12.warn("[RTCStatsMonitor] Failed to get stats:", err);
6507
+ logger$13.warn("[RTCStatsMonitor] Failed to get stats:", err);
6477
6508
  return rxjs.EMPTY;
6478
6509
  }))), (0, rxjs.filter)(() => this.running), (0, rxjs.map)((report) => this.extractSample(report))), (sample) => this._sample$.next(sample));
6479
6510
  this.subscribeTo(this._sample$.pipe((0, rxjs.take)(this.baselineSampleCount), (0, rxjs.toArray)(), (0, rxjs.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$12.debug(`[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`);
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((0, rxjs.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$12.debug("[RTCStatsMonitor] Stopping stats monitoring");
6552
+ logger$13.debug("[RTCStatsMonitor] Stopping stats monitoring");
6522
6553
  }
6523
6554
  destroy() {
6524
- logger$12.debug("[RTCStatsMonitor] Destroying RTCStatsMonitor");
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$11 = require_operators.getLogger();
6683
+ const logger$12 = require_operators.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$11.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
6780
+ logger$12.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
6749
6781
  return;
6750
6782
  }
6751
- logger$11.info("CallRecoveryManager: manual ICE restart requested");
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$11.info("CallRecoveryManager: resetting counters");
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$11.warn(`CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`);
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$11.info(`CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`);
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$11.info("CallRecoveryManager: signal-only reconnect — peer connection still alive");
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$11.info("CallRecoveryManager: full reconnect — peer connection also down");
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((0, rxjs.tap)(() => {
6845
6893
  if (this._recoveryState$.value === "idle") this.transitionTo("debouncing");
6846
- }), (0, rxjs.debounceTime)(this._config.debounceTimeMs), (0, rxjs.filter)(() => this.passGateChecks()), (0, rxjs.exhaustMap)((trigger) => this.executeTieredRecovery(trigger)), (0, rxjs.takeUntil)((0, rxjs.merge)(this._destroyed$, this._pipelineStop$))), {
6894
+ }), (0, rxjs.debounceTime)(this._config.debounceTimeMs), (0, rxjs.withLatestFrom)(this._inputs.signalingReady$), (0, rxjs.filter)(([, signalingReady]) => this.passGateChecks(signalingReady)), (0, rxjs.map)(([trigger]) => trigger), (0, rxjs.exhaustMap)((trigger) => this.executeTieredRecovery(trigger)), (0, rxjs.takeUntil)((0, rxjs.merge)(this._destroyed$, this._pipelineStop$))), {
6847
6895
  next: () => {},
6848
6896
  error: (err) => {
6849
- logger$11.error("CallRecoveryManager: pipeline error", err);
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$11.info(`CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`);
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$11.debug("CallRecoveryManager: gate blocked — negotiation in progress");
6929
+ logger$12.debug("CallRecoveryManager: gate blocked — negotiation in progress");
6882
6930
  this.transitionTo("idle");
6883
6931
  return false;
6884
6932
  }
6885
- if (!this._callbacks.isWebSocketConnected()) {
6886
- logger$11.debug("CallRecoveryManager: gate blocked — WebSocket not connected");
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$11.debug("CallRecoveryManager: gate blocked — call not connected");
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$11.debug("CallRecoveryManager: gate blocked — cooldown active");
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$11.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
6955
+ logger$12.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
6908
6956
  return (0, rxjs.from)(this.runTiers(trigger)).pipe((0, rxjs.tap)(() => this.startCooldown()), (0, rxjs.catchError)((err) => {
6909
- logger$11.error("CallRecoveryManager: tiered recovery failed", err);
6957
+ logger$12.error("CallRecoveryManager: tiered recovery failed", err);
6910
6958
  this.startCooldown();
6911
6959
  return rxjs.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$11.debug(`CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`);
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$11.warn("CallRecoveryManager: max recovery attempts reached");
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$11.debug("CallRecoveryManager: keyframe request skipped — cooldown active");
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$11.debug(`CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`);
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$11.debug(`CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`);
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$11.info(`CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`);
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$11.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);
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$11.warn(`CallRecoveryManager: ${tier} ICE restart failed`);
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$11.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);
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$11.debug(`CallRecoveryManager: state ${prev} -> ${state}`);
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$10 = require_operators.getLogger();
7173
+ const logger$11 = require_operators.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$10.warn(`Failed to parse destination URI: ${destination}`, error);
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((0, rxjs.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe((0, rxjs.distinctUntilChanged)(), (0, rxjs.takeUntil)(this._destroyed$)), (status) => {
7200
7248
  this._lastMergedStatus = status;
7201
7249
  if (status === "connected" && !this._statsMonitor) this.initResilienceSubsystems();
7202
- else if (status === "disconnected" || status === "destroyed" || status === "failed") this.stopResilienceSubsystems();
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 require_operators.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$10.error(`[Call] Error executing method ${method} with params`, params, error);
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$10.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
7592
+ logger$11.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
7538
7593
  if (!pc) {
7539
- logger$10.warn("[Call] No peer connection available, skipping resilience init");
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$10.debug("[Call] Recovery manager disabled video");
7629
+ logger$11.debug("[Call] Recovery manager disabled video");
7575
7630
  } catch {
7576
- logger$10.debug("[Call] Recovery manager failed to disable video");
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$10.debug("[Call] Recovery manager failed to enable video");
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((0, rxjs.skip)(1), (0, rxjs.filter)(Boolean)), () => {
7643
- logger$10.debug("[Call] WebSocket reconnected — notifying recovery manager");
7706
+ logger$11.debug("[Call] WebSocket reconnected — notifying recovery manager");
7644
7707
  this._recoveryManager?.handleWebSocketReconnect();
7645
7708
  });
7646
7709
  this._statsMonitor.start();
7647
- logger$10.debug("[Call] Resilience subsystems initialized for call", this.id);
7710
+ logger$11.debug("[Call] Resilience subsystems initialized for call", this.id);
7648
7711
  } catch (error) {
7649
- logger$10.warn("[Call] Failed to initialize resilience subsystems:", error);
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$10.debug("[Call] Checking if event is for this call session:", event);
7774
+ logger$11.debug("[Call] Checking if event is for this call session:", event);
7712
7775
  const callId = require_operators.getValueFrom(event, "params.params.callID") ?? require_operators.getValueFrom(event, "params.call_id");
7713
7776
  const roomSessionId = require_operators.getValueFrom(event, "params.room_session_id");
7714
- logger$10.debug(`[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`);
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$10.error("[Call] Error checking if event is for this call session:", error);
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((0, rxjs.filter)((event) => this.isCallSessionEvent(event)), (0, rxjs.tap)((event) => {
7723
- logger$10.debug("[Call] Received call session event:", event);
7786
+ logger$11.debug("[Call] Received call session event:", event);
7724
7787
  }), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.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(require_operators.filterAs(isWebrtcMessageMetadata, "params"), (0, rxjs.tap)((event) => logger$10.debug("[Call] Event is a WebRTC message event:", event)), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.share)()));
7857
+ return this.cachedObservable("webrtcMessages$", () => this.callSessionEvents$.pipe(require_operators.filterAs(isWebrtcMessageMetadata, "params"), (0, rxjs.tap)((event) => logger$11.debug("[Call] Event is a WebRTC message event:", event)), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.share)()));
7795
7858
  }
7796
7859
  get callEvent$() {
7797
- return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(require_operators.filterAs(isSignalwireCallMetadata, "params"), (0, rxjs.tap)((event) => logger$10.debug("[Call] Event is a call event:", event)), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.share)()));
7860
+ return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(require_operators.filterAs(isSignalwireCallMetadata, "params"), (0, rxjs.tap)((event) => logger$11.debug("[Call] Event is a call event:", event)), (0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.share)()));
7798
7861
  }
7799
7862
  get layoutEvent$() {
7800
7863
  return this.cachedObservable("layoutEvent$", () => this.callEvent$.pipe(require_operators.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$10.warn(`[Call] verto.subscribe for '${eventType}' failed (non-fatal):`, error);
7994
+ logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed (non-fatal):`, error);
7932
7995
  });
7933
7996
  } catch (error) {
7934
- logger$10.warn(`[Call] Failed to send verto.subscribe for '${eventType}':`, error);
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 require_operators.VertoPongError) return false;
7955
8018
  if (error instanceof require_operators.MediaTrackError) return false;
8019
+ if (error instanceof require_operators.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$9 = require_operators.getLogger();
8067
+ const logger$10 = require_operators.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$9.error("Failed to fetch entity");
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$9.error(`Failed to fetch initial collection data`, error);
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 require_operators.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$9.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
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 require_operators.CollectionFetchError(`tryFetch(${String(key)})`, error));
8113
8180
  }
@@ -8138,9 +8205,9 @@ var EntityCollection = class extends Destroyable {
8138
8205
  }
8139
8206
  };
8140
8207
  var EntityCollectionTransformed = class {
8141
- constructor(originalCollection, filter$13 = (i) => !!i, mapper = (item) => item) {
8208
+ constructor(originalCollection, filter$14 = (i) => !!i, mapper = (item) => item) {
8142
8209
  this.originalCollection = originalCollection;
8143
- this.filter = filter$13;
8210
+ this.filter = filter$14;
8144
8211
  this.mapper = mapper;
8145
8212
  }
8146
8213
  get loading$() {
@@ -8357,13 +8424,13 @@ var Address = class extends Destroyable {
8357
8424
 
8358
8425
  //#endregion
8359
8426
  //#region src/core/utils.ts
8360
- const logger$8 = require_operators.getLogger();
8427
+ const logger$9 = require_operators.getLogger();
8361
8428
  const isRPCConnectResult = (e) => {
8362
- logger$8.debug("isRPCConnectResult check:", e);
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$8.debug("isRPCConnectResult check result:", is);
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 = (0, uuid.v4)();
8375
- logger$8.debug(`[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`);
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;
@@ -8383,10 +8450,10 @@ var PendingRPC = class PendingRPC {
8383
8450
  }
8384
8451
  let isSettled = false;
8385
8452
  const subscription = (0, rxjs.race)(responses$.pipe((0, rxjs.filter)((result) => result.id === request.id), (0, rxjs.take)(1)), new rxjs.Observable((subscriber) => {
8386
- const timer$1 = setTimeout(() => {
8453
+ const timer$2 = setTimeout(() => {
8387
8454
  subscriber.error(new require_operators.RPCTimeoutError(request.id, timeoutMs));
8388
8455
  }, timeoutMs);
8389
- return () => clearTimeout(timer$1);
8456
+ return () => clearTimeout(timer$2);
8390
8457
  }), signal ? new rxjs.Observable((subscriber) => {
8391
8458
  const abortHandler = () => {
8392
8459
  subscriber.error(new DOMException("The operation was aborted", "AbortError"));
@@ -8398,22 +8465,22 @@ var PendingRPC = class PendingRPC {
8398
8465
  isSettled = true;
8399
8466
  if (response.error) {
8400
8467
  const rpcError = new require_operators.JSONRPCError(response.error.code, response.error.message, response.error.data, void 0, request.id);
8401
- logger$8.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
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$8.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
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$8.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`, error);
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$8.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
8483
+ logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
8417
8484
  if (!isSettled) reject(new require_operators.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$7 = require_operators.getLogger();
8503
+ const logger$8 = require_operators.getLogger();
8437
8504
  const getAddressSearchURI = (options) => {
8438
8505
  const to = options.to?.split("?")[0];
8439
8506
  const from$8 = 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$7.debug("[Session] Execute Error", error);
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$7.debug("[Session] Setting up message handlers");
8611
+ logger$8.debug("[Session] Setting up message handlers");
8545
8612
  this.subscribeTo(this.authStateEvent$, async (authStateEvent) => {
8546
- logger$7.debug("[Session] Authorization state event received:", authStateEvent);
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$7.error("[Session] Failed to handle authorization state update:", error);
8617
+ logger$8.error("[Session] Failed to handle authorization state update:", error);
8551
8618
  this._errors$.next(new require_operators.AuthStateHandlerError(error));
8552
8619
  }
8553
8620
  });
8554
8621
  this.subscribeTo(this.transport.connectionStatus$.pipe((0, rxjs.filter)((status) => status === "connected"), (0, rxjs.exhaustMap)(() => {
8555
- logger$7.debug("[Session] Connection established, initiating authentication");
8622
+ logger$8.debug("[Session] Connection established, initiating authentication");
8556
8623
  return (0, rxjs.from)(this.authenticate()).pipe((0, rxjs.catchError)((error) => {
8557
8624
  this.handleAuthenticationError(error).catch((err) => {
8558
- logger$7.error("[Session] Error handling authentication failure:", err);
8625
+ logger$8.error("[Session] Error handling authentication failure:", err);
8559
8626
  });
8560
8627
  return rxjs.EMPTY;
8561
8628
  }));
8562
8629
  })), void 0);
8563
8630
  this.subscribeTo(this.vertoInvite$, async (invite) => {
8564
- logger$7.debug("[Session] Verto invite received:", invite);
8631
+ logger$8.debug("[Session] Verto invite received:", invite);
8565
8632
  try {
8566
8633
  await this.createInboundCall(invite);
8567
8634
  } catch (error) {
8568
- logger$7.error("[Session] Error handling Verto invite:", error);
8635
+ logger$8.error("[Session] Error handling Verto invite:", error);
8569
8636
  this._errors$.next(new require_operators.VertoInviteHandlerError(error));
8570
8637
  }
8571
8638
  });
8572
8639
  this.subscribeTo(this.vertoAttach$, async (attach) => {
8573
- logger$7.debug("[Session] Verto attach received:", attach);
8640
+ logger$8.debug("[Session] Verto attach received:", attach);
8574
8641
  try {
8575
8642
  await this.handleVertoAttach(attach);
8576
8643
  } catch (error) {
8577
- logger$7.error("[Session] Error handling Verto attach:", error);
8644
+ logger$8.error("[Session] Error handling Verto attach:", error);
8578
8645
  this._errors$.next(new require_operators.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$7.error("Failed to retrieve authorization state from storage:", error);
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$7.debug("[Session] Removing authorization state from storage");
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$7.error("Failed to remove authorization state from storage:", error);
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$7.debug("[Session] Updating authorization state in storage");
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$7.error("Failed to retrieve authorization state from storage:", error);
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((0, rxjs.tap)((msg) => {
8614
- logger$7.debug("[Session] Received incoming message:", msg);
8681
+ logger$8.debug("[Session] Received incoming message:", msg);
8615
8682
  }), require_operators.filterAs(isSignalwireAuthorizationStateMetadata, "params"), (0, rxjs.tap)((event) => {
8616
- logger$7.debug("[Session] Authorization state event received:", event.authorization_state);
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 (0, rxjs.firstValueFrom)(this.authenticated$.pipe((0, rxjs.takeUntil)(this.destroyed$), (0, rxjs.filter)(Boolean), (0, rxjs.take)(1), (0, rxjs.timeout)({ first: 15e3 })));
8652
8719
  }
8653
8720
  async handleAuthenticationError(error) {
8654
- logger$7.error("Authentication error:", error);
8721
+ logger$8.error("Authentication error:", error);
8655
8722
  const isRecoverableAuthError = error instanceof require_operators.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 (0, rxjs.firstValueFrom)(this.authorizationState$.pipe((0, rxjs.take)(1))) !== void 0;
8657
8724
  if (isRecoverableAuthError && hasStoredState) {
8658
- logger$7.debug("[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh");
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$7.error("Failed to cleanup stored connection params:", cleanupError);
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$7.error("Failed to update authorization state in storage:", error);
8744
+ logger$8.error("Failed to update authorization state in storage:", error);
8678
8745
  this._errors$.next(new require_operators.AuthStateHandlerError(error));
8679
8746
  }
8680
8747
  }
8681
8748
  async reauthenticate(token, dpopToken, options) {
8682
- logger$7.debug("[Session] Re-authenticating session");
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$7.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
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 (0, rxjs.lastValueFrom)((0, rxjs.from)(this.transport.execute(request)).pipe(require_operators.throwOnRPCError(), (0, rxjs.take)(1), (0, rxjs.catchError)((err) => {
8697
- logger$7.error("[Session] Re-authentication RPC failed:", err);
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$7.debug("[Session] Re-authentication successful, updating stored auth state");
8768
+ logger$8.debug("[Session] Re-authentication successful, updating stored auth state");
8702
8769
  } catch (error) {
8703
- logger$7.error("[Session] Re-authentication failed:", error);
8770
+ logger$8.error("[Session] Re-authentication failed:", error);
8704
8771
  this._errors$.next(new require_operators.AuthStateHandlerError(error));
8705
8772
  throw error;
8706
8773
  }
8707
8774
  }
8708
8775
  async authenticate() {
8709
- logger$7.debug("[Session] Starting authentication process");
8776
+ logger$8.debug("[Session] Starting authentication process");
8710
8777
  const persistedParams = await (0, rxjs.firstValueFrom)((0, rxjs.combineLatest)({
8711
8778
  protocol: this.transport.protocol$,
8712
8779
  authorization_state: this.authorizationState$
8713
8780
  }).pipe((0, rxjs.take)(1)));
8714
- logger$7.debug("[Session] Persisted params:\n", {
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$7.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
8789
+ if (isReconnect) logger$8.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
8723
8790
  else if (this.onBeforeReconnect && this._clientBound) {
8724
- logger$7.debug("[Session] Refreshing credentials before fresh connect");
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$7.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
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 (0, rxjs.lastValueFrom)((0, rxjs.from)(this.transport.execute(rpcConnectRequest)).pipe(require_operators.throwOnRPCError(), (0, rxjs.map)((res) => res.result), (0, rxjs.filter)(isRPCConnectResult), (0, rxjs.tap)(() => {
8748
- logger$7.debug("[Session] Response passed filter, processing authentication result");
8815
+ logger$8.debug("[Session] Response passed filter, processing authentication result");
8749
8816
  }), (0, rxjs.take)(1), (0, rxjs.catchError)((err) => {
8750
- logger$7.error("[Session] Authentication RPC failed:", err);
8817
+ logger$8.error("[Session] Authentication RPC failed:", err);
8751
8818
  throw err;
8752
8819
  })));
8753
- logger$7.debug("[Session] Processing authentication result:", {
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$7.debug("[Session] Authentication completed successfully");
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$7.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
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$7.debug(`[Session] Creating reattached call for callID: ${callID}`);
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$7.error("[Session] Error creating outbound call:", error);
8902
+ logger$8.error("[Session] Error creating outbound call:", error);
8836
8903
  callSession?.destroy();
8837
8904
  const callError = new require_operators.CallCreateError(error instanceof rxjs.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 require_operators.DependencyError(`Address ID: ${addressId} not found`);
8852
8919
  } catch {
8853
- logger$7.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
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((0, rxjs.filter)((status) => status === "destroyed"), (0, rxjs.take)(1)), () => {
@@ -8859,7 +8926,7 @@ var ClientSessionManager = class extends Destroyable {
8859
8926
  });
8860
8927
  return callSession;
8861
8928
  } catch (error) {
8862
- logger$7.error("[Session] Error creating call session:", error);
8929
+ logger$8.error("[Session] Error creating call session:", error);
8863
8930
  throw new require_operators.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$6 = require_operators.getLogger();
8977
+ const logger$7 = require_operators.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 require_operators.ConversationError("Join Failed - Unexpected response");
8949
9016
  } catch (error) {
8950
- logger$6.error("[ConversationsManager] Failed to join conversation:", error);
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(require_operators.filterAs(isConversationMessageMetadata, "params"), (0, rxjs.tap)((event) => logger$6.debug("[ConversationsManager ] Conversation Event:", event)), (0, rxjs.map)((params) => ({ ...params }))), this.http, this.onError));
9023
+ return Promise.resolve(new ConversationMessageCollection(groupId, this.clientSession.signalingEvent$.pipe(require_operators.filterAs(isConversationMessageMetadata, "params"), (0, rxjs.tap)((event) => logger$7.debug("[ConversationsManager ] Conversation Event:", event)), (0, rxjs.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 require_operators.ConversationError("Send Text Failed - Unexpected response");
8972
9039
  } catch (error) {
8973
- logger$6.error("[ConversationsManager] Failed to send text message:", error);
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 = require_operators.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((0, rxjs.filter)(Boolean), (0, rxjs.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 (0, rxjs.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 require_operators.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 require_operators.DeviceTokenError(`Failed to obtain device token: ${response.status} ${response.statusText}`);
9161
+ const data = JSON.parse(response.body);
9162
+ if (!data.token) throw new require_operators.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 require_operators.TokenRefreshError(`Failed to refresh device token: ${response.status} ${response.statusText}`);
9188
+ const data = JSON.parse(response.body);
9189
+ if (!data.token) throw new require_operators.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 require_operators.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 require_operators.TokenRefreshError ? error : new require_operators.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 require_operators.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 = require_operators.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
- this.subscribeTo(this._clientSession.authenticated$.pipe((0, rxjs.skip)(1), (0, rxjs.filter)(Boolean)), () => {
9909
- logger$1.debug("[SignalWire] Re-registering subscriber after reconnect");
9910
- this.register().then(() => {
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((0, rxjs.skip)(1), (0, rxjs.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
- }).catch((error) => {
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);