@signalwire/js 4.0.0-dev-20260515133934 → 4.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9470,9 +9470,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9470
9470
  defaultLogger$1 = new Logger();
9471
9471
  defaultLogger$1.getLogger = function getLogger$1(name) {
9472
9472
  if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
9473
- var logger$32 = _loggersByName[name];
9474
- if (!logger$32) logger$32 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9475
- return logger$32;
9473
+ var logger$33 = _loggersByName[name];
9474
+ if (!logger$33) logger$33 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9475
+ return logger$33;
9476
9476
  };
9477
9477
  var _log = typeof window !== undefinedType ? window.log : void 0;
9478
9478
  defaultLogger$1.noConflict = function() {
@@ -9508,8 +9508,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
9508
9508
  defaultLogger.setLevel(defaultLoggerLevel);
9509
9509
  let userLogger = null;
9510
9510
  /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
9511
- const setLogger = (logger$32) => {
9512
- userLogger = logger$32;
9511
+ const setLogger = (logger$33) => {
9512
+ userLogger = logger$33;
9513
9513
  };
9514
9514
  let debugOptions = {};
9515
9515
  /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
@@ -9553,8 +9553,8 @@ const wsTraffic = (options) => {
9553
9553
  loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
9554
9554
  };
9555
9555
  const getLogger = () => {
9556
- const logger$32 = getLoggerInstance();
9557
- return new Proxy(logger$32, { get(_target, prop, _receiver) {
9556
+ const logger$33 = getLoggerInstance();
9557
+ return new Proxy(logger$33, { get(_target, prop, _receiver) {
9558
9558
  if (prop === "wsTraffic") return wsTraffic;
9559
9559
  const instance = getLoggerInstance();
9560
9560
  const value = Reflect.get(instance, prop);
@@ -9606,7 +9606,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
9606
9606
 
9607
9607
  //#endregion
9608
9608
  //#region src/controllers/HTTPRequestController.ts
9609
- const logger$31 = getLogger();
9609
+ const logger$32 = getLogger();
9610
9610
  const GET_PARAMS = {
9611
9611
  method: "GET",
9612
9612
  headers: { Accept: "application/json" }
@@ -9670,7 +9670,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9670
9670
  this._responses$.next(response);
9671
9671
  return response;
9672
9672
  } catch (error) {
9673
- logger$31.error("[HTTPRequestController] Request error:", error);
9673
+ logger$32.error("[HTTPRequestController] Request error:", error);
9674
9674
  this._status$.next("error");
9675
9675
  const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
9676
9676
  this._errors$.next(err);
@@ -9697,7 +9697,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9697
9697
  const url = this.buildURL(request.url);
9698
9698
  const headers = this.buildHeaders(request.headers);
9699
9699
  const timeout$5 = request.timeout ?? this.requestTimeout;
9700
- logger$31.debug("[HTTPRequestController] Executing request:", {
9700
+ logger$32.debug("[HTTPRequestController] Executing request:", {
9701
9701
  method: request.method,
9702
9702
  url,
9703
9703
  headers: Object.keys(headers).reduce((acc, key) => {
@@ -9717,7 +9717,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9717
9717
  });
9718
9718
  clearTimeout(timeoutId);
9719
9719
  const httpResponse = await this.convertResponse(response);
9720
- logger$31.debug("[HTTPRequestController] Response received:", {
9720
+ logger$32.debug("[HTTPRequestController] Response received:", {
9721
9721
  status: response.status,
9722
9722
  statusText: response.statusText,
9723
9723
  headers: [...response.headers.entries()],
@@ -9727,7 +9727,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9727
9727
  } catch (error) {
9728
9728
  clearTimeout(timeoutId);
9729
9729
  if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
9730
- logger$31.error("[HTTPRequestController] Request failed:", error);
9730
+ logger$32.error("[HTTPRequestController] Request failed:", error);
9731
9731
  throw error;
9732
9732
  }
9733
9733
  }
@@ -9741,8 +9741,8 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9741
9741
  const credential = this.getCredential();
9742
9742
  if (credential.token) {
9743
9743
  headers.Authorization = `Bearer ${credential.token}`;
9744
- logger$31.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9745
- } else logger$31.warn("[HTTPRequestController] No credentials available for authentication");
9744
+ logger$32.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9745
+ } else logger$32.warn("[HTTPRequestController] No credentials available for authentication");
9746
9746
  return headers;
9747
9747
  }
9748
9748
  /**
@@ -9912,6 +9912,13 @@ const CREDENTIAL_REFRESH_RETRY_BASE_MS = 1e3;
9912
9912
  const CREDENTIAL_REFRESH_MAX_DELAY_MS = 3e4;
9913
9913
  /** Buffer in milliseconds before token expiry to trigger refresh. */
9914
9914
  const CREDENTIAL_REFRESH_BUFFER_MS = 5e3;
9915
+ /**
9916
+ * Maximum time the coordinator will wait for `DeviceTokenManager.activate()`
9917
+ * to resolve before treating the activation as failed and falling back to
9918
+ * the developer-provided refresh path. Prevents a wedged HTTP layer from
9919
+ * leaving the session with no active refresh mechanism.
9920
+ */
9921
+ const CREDENTIAL_ACTIVATE_TIMEOUT_MS = 3e4;
9915
9922
  /** JSON-RPC error code for requester validation failure (corrupted auth state). */
9916
9923
  const RPC_ERROR_REQUESTER_VALIDATION_FAILED = -32003;
9917
9924
  /** JSON-RPC error code for invalid params (e.g., missing authentication block). */
@@ -10014,7 +10021,7 @@ function fromMsToSec(milliseconds) {
10014
10021
 
10015
10022
  //#endregion
10016
10023
  //#region src/containers/PreferencesContainer.ts
10017
- const logger$30 = getLogger();
10024
+ const logger$31 = getLogger();
10018
10025
  var PreferencesContainer = class PreferencesContainer {
10019
10026
  static get instance() {
10020
10027
  this._instance ??= new PreferencesContainer();
@@ -10676,7 +10683,7 @@ var ClientPreferences = class {
10676
10683
  if (!this._storage) return;
10677
10684
  const data = collectStoredPreferences();
10678
10685
  this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
10679
- logger$30.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10686
+ logger$31.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10680
10687
  });
10681
10688
  }
10682
10689
  /** Loads preferences from storage and applies them to the container. */
@@ -10685,7 +10692,7 @@ var ClientPreferences = class {
10685
10692
  this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
10686
10693
  if (stored) applyStoredPreferences(stored);
10687
10694
  }).catch((error) => {
10688
- logger$30.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10695
+ logger$31.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10689
10696
  });
10690
10697
  }
10691
10698
  };
@@ -10707,7 +10714,7 @@ function toError(value) {
10707
10714
  //#endregion
10708
10715
  //#region src/controllers/NavigatorDeviceController.ts
10709
10716
  var import_cjs$29 = require_cjs();
10710
- const logger$29 = getLogger();
10717
+ const logger$30 = getLogger();
10711
10718
  /** Maps a device kind to its storage key. */
10712
10719
  const DEVICE_STORAGE_KEYS = {
10713
10720
  audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
@@ -10729,7 +10736,7 @@ var NavigatorDeviceController = class extends Destroyable {
10729
10736
  super();
10730
10737
  this.webRTCApiProvider = webRTCApiProvider;
10731
10738
  this.deviceChangeHandler = () => {
10732
- logger$29.debug("[DeviceController] Device change detected");
10739
+ logger$30.debug("[DeviceController] Device change detected");
10733
10740
  this.enumerateDevices();
10734
10741
  };
10735
10742
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
@@ -10794,13 +10801,13 @@ var NavigatorDeviceController = class extends Destroyable {
10794
10801
  return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10795
10802
  }
10796
10803
  get selectedAudioInputDevice$() {
10797
- return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected audio input device changed:", info))));
10804
+ return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$30.debug("[DeviceController] Selected audio input device changed:", info))));
10798
10805
  }
10799
10806
  get selectedAudioOutputDevice$() {
10800
- return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected audio output device changed:", info))));
10807
+ return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$30.debug("[DeviceController] Selected audio output device changed:", info))));
10801
10808
  }
10802
10809
  get selectedVideoInputDevice$() {
10803
- return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected video input device changed:", info))));
10810
+ return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$30.debug("[DeviceController] Selected video input device changed:", info))));
10804
10811
  }
10805
10812
  get selectedAudioInputDevice() {
10806
10813
  if (this._audioInputDisabled$.value) return null;
@@ -10875,7 +10882,7 @@ var NavigatorDeviceController = class extends Destroyable {
10875
10882
  if (device) this.persistDeviceSelection("audioinput", device);
10876
10883
  }
10877
10884
  selectVideoInputDevice(device) {
10878
- logger$29.debug("[DeviceController] Setting selected video input device:", device);
10885
+ logger$30.debug("[DeviceController] Setting selected video input device:", device);
10879
10886
  if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
10880
10887
  const previous = this._selectedDevicesState$.value.videoinput;
10881
10888
  if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
@@ -10932,7 +10939,7 @@ var NavigatorDeviceController = class extends Destroyable {
10932
10939
  }
10933
10940
  const fromHistory = this._deviceHistory.findInHistory(kind, devices);
10934
10941
  if (fromHistory) {
10935
- logger$29.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10942
+ logger$30.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10936
10943
  this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
10937
10944
  return fromHistory;
10938
10945
  }
@@ -10985,7 +10992,7 @@ var NavigatorDeviceController = class extends Destroyable {
10985
10992
  try {
10986
10993
  await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
10987
10994
  } catch (error) {
10988
- logger$29.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10995
+ logger$30.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10989
10996
  }
10990
10997
  }
10991
10998
  async loadPersistedDevices() {
@@ -11001,7 +11008,7 @@ var NavigatorDeviceController = class extends Destroyable {
11001
11008
  [kind]: stored
11002
11009
  };
11003
11010
  } catch (error) {
11004
- logger$29.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11011
+ logger$30.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11005
11012
  }
11006
11013
  }
11007
11014
  /** Clears device history, persisted selections, and re-enumerates devices. */
@@ -11019,7 +11026,7 @@ var NavigatorDeviceController = class extends Destroyable {
11019
11026
  this.disableDeviceMonitoring();
11020
11027
  this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
11021
11028
  if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11022
- logger$29.debug("[DeviceController] Polling devices due to interval");
11029
+ logger$30.debug("[DeviceController] Polling devices due to interval");
11023
11030
  this.enumerateDevices();
11024
11031
  });
11025
11032
  this.enumerateDevices();
@@ -11045,13 +11052,13 @@ var NavigatorDeviceController = class extends Destroyable {
11045
11052
  videoinput: []
11046
11053
  });
11047
11054
  this._devicesState$.next(devicesByKind);
11048
- logger$29.debug("[DeviceController] Devices enumerated:", {
11055
+ logger$30.debug("[DeviceController] Devices enumerated:", {
11049
11056
  audioInputs: devicesByKind.audioinput.length,
11050
11057
  audioOutputs: devicesByKind.audiooutput.length,
11051
11058
  videoInputs: devicesByKind.videoinput.length
11052
11059
  });
11053
11060
  } catch (error) {
11054
- logger$29.error("[DeviceController] Failed to enumerate devices:", error);
11061
+ logger$30.error("[DeviceController] Failed to enumerate devices:", error);
11055
11062
  this._errors$.next(toError(error));
11056
11063
  }
11057
11064
  }
@@ -11067,7 +11074,7 @@ var NavigatorDeviceController = class extends Destroyable {
11067
11074
  stream.getTracks().forEach((t) => t.stop());
11068
11075
  return capabilities;
11069
11076
  } catch (error) {
11070
- logger$29.error("[DeviceController] Failed to get device capabilities:", error);
11077
+ logger$30.error("[DeviceController] Failed to get device capabilities:", error);
11071
11078
  this._errors$.next(toError(error));
11072
11079
  throw error;
11073
11080
  }
@@ -11318,7 +11325,7 @@ var DependencyContainer = class {
11318
11325
 
11319
11326
  //#endregion
11320
11327
  //#region src/controllers/CryptoController.ts
11321
- const logger$28 = getLogger();
11328
+ const logger$29 = getLogger();
11322
11329
  const DPOP_DB_NAME = "sw-dpop";
11323
11330
  const DPOP_DB_VERSION = 1;
11324
11331
  const DPOP_STORE_NAME = "keys";
@@ -11377,7 +11384,7 @@ async function loadKeyPairFromDB() {
11377
11384
  tx.oncomplete = () => db.close();
11378
11385
  });
11379
11386
  } catch (error) {
11380
- logger$28.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11387
+ logger$29.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11381
11388
  return null;
11382
11389
  }
11383
11390
  }
@@ -11397,7 +11404,7 @@ async function saveKeyPairToDB(keyPair) {
11397
11404
  };
11398
11405
  });
11399
11406
  } catch (error) {
11400
- logger$28.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11407
+ logger$29.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11401
11408
  }
11402
11409
  }
11403
11410
  async function deleteKeyPairFromDB() {
@@ -11416,7 +11423,7 @@ async function deleteKeyPairFromDB() {
11416
11423
  };
11417
11424
  });
11418
11425
  } catch (error) {
11419
- logger$28.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11426
+ logger$29.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11420
11427
  }
11421
11428
  }
11422
11429
  /**
@@ -11476,13 +11483,13 @@ var CryptoController = class {
11476
11483
  this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
11477
11484
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11478
11485
  this._initialized = true;
11479
- logger$28.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11486
+ logger$29.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11480
11487
  return this._fingerprint;
11481
11488
  } catch (error) {
11482
- logger$28.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11489
+ logger$29.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11483
11490
  await deleteKeyPairFromDB();
11484
11491
  }
11485
- logger$28.debug("[DPoP] Generating RSA key pair");
11492
+ logger$29.debug("[DPoP] Generating RSA key pair");
11486
11493
  this._keyPair = await crypto.subtle.generateKey({
11487
11494
  name: "RSASSA-PKCS1-v1_5",
11488
11495
  modulusLength: 2048,
@@ -11497,7 +11504,7 @@ var CryptoController = class {
11497
11504
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11498
11505
  this._initialized = true;
11499
11506
  await saveKeyPairToDB(this._keyPair);
11500
- logger$28.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11507
+ logger$29.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11501
11508
  return this._fingerprint;
11502
11509
  }
11503
11510
  /**
@@ -11563,7 +11570,7 @@ var CryptoController = class {
11563
11570
  this._fingerprint = null;
11564
11571
  this._initialized = false;
11565
11572
  deleteKeyPairFromDB();
11566
- logger$28.debug("[DPoP] Controller destroyed");
11573
+ logger$29.debug("[DPoP] Controller destroyed");
11567
11574
  }
11568
11575
  get publicJwk() {
11569
11576
  if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
@@ -11587,7 +11594,7 @@ var CryptoController = class {
11587
11594
  //#endregion
11588
11595
  //#region src/controllers/NetworkMonitor.ts
11589
11596
  var import_cjs$28 = require_cjs();
11590
- const logger$27 = getLogger();
11597
+ const logger$28 = getLogger();
11591
11598
  /**
11592
11599
  * Safely check whether we are running in a browser environment
11593
11600
  * with `window` and the relevant event targets.
@@ -11644,7 +11651,7 @@ var NetworkMonitor = class extends Destroyable {
11644
11651
  }
11645
11652
  attachListeners() {
11646
11653
  if (!hasBrowserNetworkEvents()) {
11647
- logger$27.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11654
+ logger$28.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11648
11655
  return;
11649
11656
  }
11650
11657
  window.addEventListener("online", this._onOnline);
@@ -11652,7 +11659,7 @@ var NetworkMonitor = class extends Destroyable {
11652
11659
  const connection = getNetworkConnection();
11653
11660
  if (connection) connection.addEventListener("change", this._onConnectionChange);
11654
11661
  this._listenersAttached = true;
11655
- logger$27.debug("NetworkMonitor: event listeners attached");
11662
+ logger$28.debug("NetworkMonitor: event listeners attached");
11656
11663
  }
11657
11664
  removeListeners() {
11658
11665
  if (!this._listenersAttached) return;
@@ -11663,10 +11670,10 @@ var NetworkMonitor = class extends Destroyable {
11663
11670
  if (connection) connection.removeEventListener("change", this._onConnectionChange);
11664
11671
  }
11665
11672
  this._listenersAttached = false;
11666
- logger$27.debug("NetworkMonitor: event listeners removed");
11673
+ logger$28.debug("NetworkMonitor: event listeners removed");
11667
11674
  }
11668
11675
  handleOnline() {
11669
- logger$27.info("NetworkMonitor: browser went online");
11676
+ logger$28.info("NetworkMonitor: browser went online");
11670
11677
  this._isOnline$.next(true);
11671
11678
  this._networkChange$.next({
11672
11679
  type: "online",
@@ -11675,7 +11682,7 @@ var NetworkMonitor = class extends Destroyable {
11675
11682
  });
11676
11683
  }
11677
11684
  handleOffline() {
11678
- logger$27.info("NetworkMonitor: browser went offline");
11685
+ logger$28.info("NetworkMonitor: browser went offline");
11679
11686
  this._isOnline$.next(false);
11680
11687
  this._networkChange$.next({
11681
11688
  type: "offline",
@@ -11684,7 +11691,7 @@ var NetworkMonitor = class extends Destroyable {
11684
11691
  }
11685
11692
  handleConnectionChange() {
11686
11693
  const networkType = getNetworkType();
11687
- logger$27.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11694
+ logger$28.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11688
11695
  this._networkChange$.next({
11689
11696
  type: "connection_change",
11690
11697
  timestamp: Date.now(),
@@ -11800,7 +11807,7 @@ function getNavigatorMediaDevices() {
11800
11807
  //#endregion
11801
11808
  //#region src/controllers/PreflightRunner.ts
11802
11809
  var import_cjs$27 = require_cjs();
11803
- const logger$26 = getLogger();
11810
+ const logger$27 = getLogger();
11804
11811
  const DEFAULT_MEDIA_TEST_DURATION_S = 10;
11805
11812
  const ICE_GATHERING_TIMEOUT_MS = 1e4;
11806
11813
  const SIGNALING_RTT_TIMEOUT_MS = 5e3;
@@ -11849,7 +11856,7 @@ var PreflightRunner = class extends Destroyable {
11849
11856
  if (!this._options.skipMediaTest) try {
11850
11857
  bandwidth = await this.testMediaBandwidth(destination);
11851
11858
  } catch (error) {
11852
- logger$26.warn("[PreflightRunner] Media bandwidth test failed:", error);
11859
+ logger$27.warn("[PreflightRunner] Media bandwidth test failed:", error);
11853
11860
  warnings.push("Media bandwidth test failed");
11854
11861
  }
11855
11862
  return {
@@ -11861,7 +11868,7 @@ var PreflightRunner = class extends Destroyable {
11861
11868
  warnings
11862
11869
  };
11863
11870
  } catch (error) {
11864
- logger$26.error("[PreflightRunner] Preflight test failed:", error);
11871
+ logger$27.error("[PreflightRunner] Preflight test failed:", error);
11865
11872
  throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
11866
11873
  } finally {
11867
11874
  this.destroy();
@@ -11892,7 +11899,7 @@ var PreflightRunner = class extends Destroyable {
11892
11899
  if (track.kind === "video" && track.readyState === "live") videoWorking = true;
11893
11900
  }
11894
11901
  } catch (error) {
11895
- logger$26.warn("[PreflightRunner] Device test failed:", error);
11902
+ logger$27.warn("[PreflightRunner] Device test failed:", error);
11896
11903
  } finally {
11897
11904
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
11898
11905
  }
@@ -11950,7 +11957,7 @@ var PreflightRunner = class extends Destroyable {
11950
11957
  rttMs
11951
11958
  };
11952
11959
  } catch (error) {
11953
- logger$26.warn("[PreflightRunner] ICE connectivity test failed:", error);
11960
+ logger$27.warn("[PreflightRunner] ICE connectivity test failed:", error);
11954
11961
  return {
11955
11962
  type: "failed",
11956
11963
  turnReachable: false,
@@ -11998,7 +12005,7 @@ var PreflightRunner = class extends Destroyable {
11998
12005
  //#endregion
11999
12006
  //#region src/controllers/VisibilityController.ts
12000
12007
  var import_cjs$26 = require_cjs();
12001
- const logger$25 = getLogger();
12008
+ const logger$26 = getLogger();
12002
12009
  /**
12003
12010
  * Checks whether the document visibility API is available.
12004
12011
  */
@@ -12035,8 +12042,8 @@ var VisibilityController = class extends Destroyable {
12035
12042
  this._boundHandler = this._handleVisibilityChange.bind(this);
12036
12043
  if (this._hasVisibilityApi) {
12037
12044
  document.addEventListener("visibilitychange", this._boundHandler);
12038
- logger$25.debug("VisibilityController: listening for visibilitychange events");
12039
- } else logger$25.debug("VisibilityController: document visibility API not available, defaulting to visible");
12045
+ logger$26.debug("VisibilityController: listening for visibilitychange events");
12046
+ } else logger$26.debug("VisibilityController: document visibility API not available, defaulting to visible");
12040
12047
  }
12041
12048
  /**
12042
12049
  * Observable of the current visibility state.
@@ -12061,7 +12068,7 @@ var VisibilityController = class extends Destroyable {
12061
12068
  destroy() {
12062
12069
  if (this._hasVisibilityApi) {
12063
12070
  document.removeEventListener("visibilitychange", this._boundHandler);
12064
- logger$25.debug("VisibilityController: removed visibilitychange listener");
12071
+ logger$26.debug("VisibilityController: removed visibilitychange listener");
12065
12072
  }
12066
12073
  super.destroy();
12067
12074
  }
@@ -12079,7 +12086,7 @@ var VisibilityController = class extends Destroyable {
12079
12086
  timestamp: Date.now()
12080
12087
  };
12081
12088
  this._visibilityChange$.next(changeEvent);
12082
- logger$25.debug("VisibilityController: visibility changed", {
12089
+ logger$26.debug("VisibilityController: visibility changed", {
12083
12090
  from: previousState,
12084
12091
  to: newState
12085
12092
  });
@@ -12320,7 +12327,7 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
12320
12327
 
12321
12328
  //#endregion
12322
12329
  //#region src/managers/AttachManager.ts
12323
- const logger$24 = getLogger();
12330
+ const logger$25 = getLogger();
12324
12331
  var AttachManager = class {
12325
12332
  constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
12326
12333
  this.storage = storage;
@@ -12341,7 +12348,7 @@ var AttachManager = class {
12341
12348
  try {
12342
12349
  return await this.storage.getItem(this.attachKey) ?? {};
12343
12350
  } catch (error) {
12344
- logger$24.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12351
+ logger$25.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12345
12352
  return {};
12346
12353
  }
12347
12354
  }
@@ -12349,7 +12356,7 @@ var AttachManager = class {
12349
12356
  try {
12350
12357
  await this.storage.setItem(this.attachKey, attached);
12351
12358
  } catch (error) {
12352
- logger$24.warn("[AttachManager] Failed to write attached calls to storage", error);
12359
+ logger$25.warn("[AttachManager] Failed to write attached calls to storage", error);
12353
12360
  }
12354
12361
  }
12355
12362
  /**
@@ -12368,7 +12375,7 @@ var AttachManager = class {
12368
12375
  }
12369
12376
  async attach(call) {
12370
12377
  if (!call.to) {
12371
- logger$24.warn("[AttachManager] Skip attach for calls with no destination");
12378
+ logger$25.warn("[AttachManager] Skip attach for calls with no destination");
12372
12379
  return;
12373
12380
  }
12374
12381
  const destination = call.to;
@@ -12421,15 +12428,15 @@ var AttachManager = class {
12421
12428
  callId,
12422
12429
  ...options
12423
12430
  });
12424
- logger$24.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12431
+ logger$25.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12425
12432
  succeeded = true;
12426
12433
  break;
12427
12434
  } catch (error) {
12428
- logger$24.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12435
+ logger$25.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12429
12436
  if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
12430
12437
  }
12431
12438
  if (!succeeded) {
12432
- logger$24.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12439
+ logger$25.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12433
12440
  await this.detach({
12434
12441
  id: callId,
12435
12442
  mediaDirections: attachment.mediaDirections
@@ -13366,7 +13373,9 @@ const DEFAULT_MEMBER_CAPABILITIES = {
13366
13373
  position: false,
13367
13374
  meta: false,
13368
13375
  remove: false,
13369
- audioFlags: false
13376
+ audioFlags: false,
13377
+ denoise: false,
13378
+ lowbitrate: false
13370
13379
  };
13371
13380
  /**
13372
13381
  * Default call capabilities with no permissions
@@ -13439,7 +13448,9 @@ function computeMemberCapabilities(flags, memberType) {
13439
13448
  position: hasBooleanCapability(flags, memberType, null, "position"),
13440
13449
  meta: hasBooleanCapability(flags, memberType, null, "meta"),
13441
13450
  remove: hasBooleanCapability(flags, memberType, null, "remove"),
13442
- audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags")
13451
+ audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags"),
13452
+ denoise: hasBooleanCapability(flags, memberType, null, "denoise"),
13453
+ lowbitrate: hasBooleanCapability(flags, memberType, null, "lowbitrate")
13443
13454
  };
13444
13455
  }
13445
13456
  /**
@@ -13596,7 +13607,7 @@ function toggleHandraiseMethod(is) {
13596
13607
 
13597
13608
  //#endregion
13598
13609
  //#region src/core/entities/Participant.ts
13599
- const logger$23 = getLogger();
13610
+ const logger$24 = getLogger();
13600
13611
  const initialState = {};
13601
13612
  /**
13602
13613
  * Represents a participant in a call.
@@ -13822,6 +13833,10 @@ var Participant = class extends Destroyable {
13822
13833
  get nodeId() {
13823
13834
  return this._state$.value.node_id;
13824
13835
  }
13836
+ /** Call ID for this participant's leg, or `undefined` if not available. */
13837
+ get callId() {
13838
+ return this._state$.value.call_id;
13839
+ }
13825
13840
  /** @internal */
13826
13841
  get value() {
13827
13842
  return this._state$.value;
@@ -13883,8 +13898,9 @@ var Participant = class extends Destroyable {
13883
13898
  noise_suppression: !this.noiseSuppression
13884
13899
  });
13885
13900
  }
13901
+ /** Toggles low-bitrate mode for this participant's media. */
13886
13902
  async toggleLowbitrate() {
13887
- throw new UnimplementedError();
13903
+ await this.executeMethod(this.id, "call.lowbitrate.set", { lowbitrate: !this.lowbitrate });
13888
13904
  }
13889
13905
  /**
13890
13906
  * Adjusts the **conference-only** microphone energy gate / sensitivity level
@@ -13931,10 +13947,27 @@ var Participant = class extends Destroyable {
13931
13947
  }
13932
13948
  /**
13933
13949
  * Sets the participant's position in the video layout.
13950
+ *
13951
+ * Requires the `member.position` capability. The gateway keys positions by the
13952
+ * **target member's own** `call_id`/`node_id` (see issue #19400 and the legacy
13953
+ * `setPositions` implementation), so this sends the participant's own call
13954
+ * context — matching {@link Participant.remove}. A resolved promise does not
13955
+ * guarantee a visible change: the backend silently returns `200` (no-op) for
13956
+ * non-conference targets.
13957
+ *
13934
13958
  * @param value - The {@link VideoPosition} to assign (e.g. `'auto'`, `'reserved-0'`).
13935
13959
  */
13936
13960
  async setPosition(value) {
13937
- await this.executeMethod(this.id, "call.member.position.set", { position: value });
13961
+ const state = this._state$.value;
13962
+ const target = {
13963
+ member_id: this.id,
13964
+ call_id: state.call_id ?? "",
13965
+ node_id: state.node_id ?? ""
13966
+ };
13967
+ await this.executeMethod(target, "call.member.position.set", { targets: [{
13968
+ target,
13969
+ position: value
13970
+ }] });
13938
13971
  }
13939
13972
  /** Removes this participant from the call. */
13940
13973
  async remove() {
@@ -14029,7 +14062,7 @@ var SelfParticipant = class extends Participant {
14029
14062
  try {
14030
14063
  await this.vertoManager.addScreenMedia();
14031
14064
  } catch (error) {
14032
- logger$23.error("[Participant.startScreenShare] Screen share error:", error);
14065
+ logger$24.error("[Participant.startScreenShare] Screen share error:", error);
14033
14066
  }
14034
14067
  }
14035
14068
  /** Observable of the current screen share status. */
@@ -14049,7 +14082,7 @@ var SelfParticipant = class extends Participant {
14049
14082
  try {
14050
14083
  await this.vertoManager.addInputDevice(options);
14051
14084
  } catch (error) {
14052
- logger$23.error("[Participant.startScreenShare] Screen share error:", error);
14085
+ logger$24.error("[Participant.startScreenShare] Screen share error:", error);
14053
14086
  }
14054
14087
  }
14055
14088
  /** Removes an additional media input device by ID. */
@@ -14111,7 +14144,7 @@ var SelfParticipant = class extends Participant {
14111
14144
  */
14112
14145
  exitStudioModeIfActive() {
14113
14146
  if (this._studioAudio$.value) {
14114
- logger$23.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14147
+ logger$24.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14115
14148
  this._studioAudio$.next(false);
14116
14149
  }
14117
14150
  }
@@ -14135,7 +14168,7 @@ var SelfParticipant = class extends Participant {
14135
14168
  try {
14136
14169
  await super.mute();
14137
14170
  } catch (error) {
14138
- logger$23.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14171
+ logger$24.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14139
14172
  } finally {
14140
14173
  this.vertoManager.muteMainAudioInputDevice();
14141
14174
  }
@@ -14145,7 +14178,7 @@ var SelfParticipant = class extends Participant {
14145
14178
  try {
14146
14179
  await super.unmute();
14147
14180
  } catch (error) {
14148
- logger$23.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14181
+ logger$24.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14149
14182
  } finally {
14150
14183
  await this.vertoManager.unmuteMainAudioInputDevice();
14151
14184
  }
@@ -14155,7 +14188,7 @@ var SelfParticipant = class extends Participant {
14155
14188
  try {
14156
14189
  await super.muteVideo();
14157
14190
  } catch (error) {
14158
- logger$23.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14191
+ logger$24.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14159
14192
  } finally {
14160
14193
  this.vertoManager.muteMainVideoInputDevice();
14161
14194
  }
@@ -14165,7 +14198,7 @@ var SelfParticipant = class extends Participant {
14165
14198
  try {
14166
14199
  await super.unmuteVideo();
14167
14200
  } catch (error) {
14168
- logger$23.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14201
+ logger$24.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14169
14202
  } finally {
14170
14203
  await this.vertoManager.unmuteMainVideoInputDevice();
14171
14204
  }
@@ -14370,7 +14403,7 @@ function filterAs(predicate, resultPath) {
14370
14403
  //#endregion
14371
14404
  //#region src/operators/throwOnRPCError.ts
14372
14405
  var import_cjs$21 = require_cjs();
14373
- const logger$22 = getLogger();
14406
+ const logger$23 = getLogger();
14374
14407
  /**
14375
14408
  * RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
14376
14409
  * Passes successful responses through unchanged.
@@ -14378,14 +14411,14 @@ const logger$22 = getLogger();
14378
14411
  function throwOnRPCError() {
14379
14412
  return (0, import_cjs$21.map)((response) => {
14380
14413
  if (response.error) {
14381
- logger$22.error("[throwOnRPCError] RPC error response:", {
14414
+ logger$23.error("[throwOnRPCError] RPC error response:", {
14382
14415
  code: response.error.code,
14383
14416
  message: response.error.message,
14384
14417
  data: response.error.data
14385
14418
  });
14386
14419
  throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
14387
14420
  }
14388
- logger$22.debug("[throwOnRPCError] RPC successful response:", response);
14421
+ logger$23.debug("[throwOnRPCError] RPC successful response:", response);
14389
14422
  return response;
14390
14423
  });
14391
14424
  }
@@ -14393,7 +14426,7 @@ function throwOnRPCError() {
14393
14426
  //#endregion
14394
14427
  //#region src/managers/CallEventsManager.ts
14395
14428
  var import_cjs$20 = require_cjs();
14396
- const logger$21 = getLogger();
14429
+ const logger$22 = getLogger();
14397
14430
  const initialSessionState = {};
14398
14431
  /** @internal */
14399
14432
  var CallEventsManager = class extends Destroyable {
@@ -14497,7 +14530,7 @@ var CallEventsManager = class extends Destroyable {
14497
14530
  }
14498
14531
  initSubscriptions() {
14499
14532
  this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
14500
- logger$21.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14533
+ logger$22.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14501
14534
  callId: callJoinedEvent.call_id,
14502
14535
  roomSessionId: callJoinedEvent.room_session_id
14503
14536
  });
@@ -14524,19 +14557,19 @@ var CallEventsManager = class extends Destroyable {
14524
14557
  if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
14525
14558
  });
14526
14559
  this.subscribeTo(this.memberUpdates$, (member) => {
14527
- logger$21.debug("[CallEventsManager] Handling member update event for member ID:", member);
14560
+ logger$22.debug("[CallEventsManager] Handling member update event for member ID:", member);
14528
14561
  this.upsertParticipant(member);
14529
14562
  });
14530
14563
  this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
14531
- logger$21.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14564
+ logger$22.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14532
14565
  const participants = { ...this._participants$.value };
14533
14566
  if (memberLeftEvent.member.member_id in participants) {
14534
14567
  delete participants[memberLeftEvent.member.member_id];
14535
14568
  this._participants$.next(participants);
14536
- } else logger$21.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14569
+ } else logger$22.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14537
14570
  });
14538
14571
  this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
14539
- logger$21.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14572
+ logger$22.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14540
14573
  const roomSession = callUpdatedEvent.room_session;
14541
14574
  this._sessionState$.next({
14542
14575
  ...this._sessionState$.value,
@@ -14551,7 +14584,7 @@ var CallEventsManager = class extends Destroyable {
14551
14584
  });
14552
14585
  });
14553
14586
  this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
14554
- logger$21.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14587
+ logger$22.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14555
14588
  this._sessionState$.next({
14556
14589
  ...this._sessionState$.value,
14557
14590
  layout_name: layoutChangedEvent.id,
@@ -14561,10 +14594,10 @@ var CallEventsManager = class extends Destroyable {
14561
14594
  });
14562
14595
  }
14563
14596
  updateParticipantPositions(layoutChangedEvent) {
14564
- if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$21.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
14597
+ if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$22.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
14565
14598
  layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
14566
14599
  if (!(layer.member_id in this._participants$.value)) {
14567
- logger$21.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14600
+ logger$22.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14568
14601
  return false;
14569
14602
  }
14570
14603
  return true;
@@ -14587,7 +14620,7 @@ var CallEventsManager = class extends Destroyable {
14587
14620
  layouts: response.result.layouts
14588
14621
  });
14589
14622
  }).catch((error) => {
14590
- logger$21.error("[CallEventsManager] Error fetching layouts:", error);
14623
+ logger$22.error("[CallEventsManager] Error fetching layouts:", error);
14591
14624
  });
14592
14625
  }
14593
14626
  updateParticipants(members) {
@@ -14603,7 +14636,7 @@ var CallEventsManager = class extends Destroyable {
14603
14636
  }
14604
14637
  const participant = this._participants$.value[member.member_id];
14605
14638
  const oldValue = participant.value;
14606
- logger$21.debug("[CallEventsManager] Updating participant:", member.member_id, {
14639
+ logger$22.debug("[CallEventsManager] Updating participant:", member.member_id, {
14607
14640
  oldValue,
14608
14641
  newValue: member
14609
14642
  });
@@ -14616,17 +14649,17 @@ var CallEventsManager = class extends Destroyable {
14616
14649
  }
14617
14650
  get callJoinedEvent$() {
14618
14651
  return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
14619
- logger$21.debug("[CallEventsManager] Call joined event:", event);
14652
+ logger$22.debug("[CallEventsManager] Call joined event:", event);
14620
14653
  })));
14621
14654
  }
14622
14655
  get layoutChangedEvent$() {
14623
14656
  return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
14624
- logger$21.debug("[CallEventsManager] Layout changed event:", event);
14657
+ logger$22.debug("[CallEventsManager] Layout changed event:", event);
14625
14658
  })));
14626
14659
  }
14627
14660
  get memberUpdates$() {
14628
14661
  return this.cachedObservable("memberUpdates$", () => (0, import_cjs$20.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$20.map)((event) => event.member), (0, import_cjs$20.tap)((event) => {
14629
- logger$21.debug("[CallEventsManager] Member update event:", event);
14662
+ logger$22.debug("[CallEventsManager] Member update event:", event);
14630
14663
  })));
14631
14664
  }
14632
14665
  destroy() {
@@ -14883,7 +14916,7 @@ function appendStereoParams(fmtpLine, maxBitrate) {
14883
14916
  //#endregion
14884
14917
  //#region src/controllers/ICEGatheringController.ts
14885
14918
  var import_cjs$19 = require_cjs();
14886
- const logger$20 = getLogger();
14919
+ const logger$21 = getLogger();
14887
14920
  var ICEGatheringController = class extends Destroyable {
14888
14921
  constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
14889
14922
  super();
@@ -14891,23 +14924,23 @@ var ICEGatheringController = class extends Destroyable {
14891
14924
  this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
14892
14925
  this.onicegatheringstatechangeHandler = () => {
14893
14926
  const { iceGatheringState } = this.peerConnection;
14894
- logger$20.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14927
+ logger$21.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14895
14928
  if (iceGatheringState === "gathering") this._iceCandidatesState.next({
14896
14929
  state: "gathering",
14897
14930
  validSDP: false
14898
14931
  });
14899
14932
  };
14900
14933
  this.onicecandidateHandler = (event) => {
14901
- logger$20.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14934
+ logger$21.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14902
14935
  this.removeTimer("iceCandidateTimer");
14903
14936
  if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
14904
14937
  if (this.peerConnection.iceGatheringState !== "complete") {
14905
- logger$20.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14938
+ logger$21.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14906
14939
  this.handleICECandidateTimeout();
14907
14940
  }
14908
14941
  }, this.iceCandidateTimeout);
14909
14942
  else {
14910
- logger$20.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14943
+ logger$21.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14911
14944
  this.removeTimer("iceGatheringTimer");
14912
14945
  this.handleICEGatheringComplete();
14913
14946
  }
@@ -14925,7 +14958,7 @@ var ICEGatheringController = class extends Destroyable {
14925
14958
  this.setupEventListeners();
14926
14959
  this.iceGatheringTimer = setTimeout(() => {
14927
14960
  if (this.peerConnection.iceGatheringState !== "complete") {
14928
- logger$20.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14961
+ logger$21.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14929
14962
  this.handleICEGatheringTimeout();
14930
14963
  }
14931
14964
  }, this.iceGatheringTimeout);
@@ -14952,9 +14985,9 @@ var ICEGatheringController = class extends Destroyable {
14952
14985
  this.relayOnly = value;
14953
14986
  }
14954
14987
  handleICEGatheringComplete() {
14955
- logger$20.debug("[ICEGatheringController] Handling ICE gathering complete");
14956
- logger$20.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14957
- logger$20.debug("[ICEGatheringController] ICE gathering complete");
14988
+ logger$21.debug("[ICEGatheringController] Handling ICE gathering complete");
14989
+ logger$21.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14990
+ logger$21.debug("[ICEGatheringController] ICE gathering complete");
14958
14991
  this._iceCandidatesState.next({
14959
14992
  state: "complete",
14960
14993
  validSDP: this.hasValidLocalDescriptionSDP
@@ -14970,21 +15003,21 @@ var ICEGatheringController = class extends Destroyable {
14970
15003
  this.removeTimer("iceGatheringTimer");
14971
15004
  const validSDP = this.hasValidLocalDescriptionSDP;
14972
15005
  if (validSDP) {
14973
- logger$20.debug("[ICEGatheringController] Local SDP is valid");
15006
+ logger$21.debug("[ICEGatheringController] Local SDP is valid");
14974
15007
  this._iceCandidatesState.next({
14975
15008
  state: "timeout",
14976
15009
  validSDP
14977
15010
  });
14978
15011
  this.stopGathering();
14979
- } else logger$20.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
15012
+ } else logger$21.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14980
15013
  }
14981
15014
  handleICECandidateTimeout() {
14982
15015
  if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
14983
- logger$20.warn("[ICEGatheringController] ICE candidate timeout");
15016
+ logger$21.warn("[ICEGatheringController] ICE candidate timeout");
14984
15017
  const validSDP = this.hasValidLocalDescriptionSDP;
14985
15018
  if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
14986
15019
  else {
14987
- logger$20.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
15020
+ logger$21.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14988
15021
  this._iceCandidatesState.next({
14989
15022
  state: "timeout",
14990
15023
  validSDP
@@ -14993,7 +15026,7 @@ var ICEGatheringController = class extends Destroyable {
14993
15026
  }
14994
15027
  }
14995
15028
  restartICEGatheringWithRelayOnly() {
14996
- logger$20.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
15029
+ logger$21.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
14997
15030
  this.relayOnly = true;
14998
15031
  this.peerConnection.setConfiguration({
14999
15032
  ...this.peerConnection.getConfiguration(),
@@ -15008,7 +15041,7 @@ var ICEGatheringController = class extends Destroyable {
15008
15041
  }
15009
15042
  }
15010
15043
  clearAllTimers() {
15011
- logger$20.debug("[ICEGatheringController] Clearing all timers");
15044
+ logger$21.debug("[ICEGatheringController] Clearing all timers");
15012
15045
  this.removeTimer("iceGatheringTimer");
15013
15046
  this.removeTimer("iceCandidateTimer");
15014
15047
  }
@@ -15017,7 +15050,7 @@ var ICEGatheringController = class extends Destroyable {
15017
15050
  this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
15018
15051
  }
15019
15052
  destroy() {
15020
- logger$20.debug("[ICEGatheringController] Destroying ICEGatheringController");
15053
+ logger$21.debug("[ICEGatheringController] Destroying ICEGatheringController");
15021
15054
  this.clearAllTimers();
15022
15055
  this.removeEventListeners();
15023
15056
  super.destroy();
@@ -15027,7 +15060,7 @@ var ICEGatheringController = class extends Destroyable {
15027
15060
  //#endregion
15028
15061
  //#region src/controllers/LocalAudioPipeline.ts
15029
15062
  var import_cjs$18 = require_cjs();
15030
- const logger$19 = getLogger();
15063
+ const logger$20 = getLogger();
15031
15064
  /**
15032
15065
  * Web Audio pipeline for the local microphone stream.
15033
15066
  *
@@ -15139,7 +15172,7 @@ var LocalAudioPipeline = class extends Destroyable {
15139
15172
  try {
15140
15173
  this._inputSource.disconnect();
15141
15174
  } catch (error) {
15142
- logger$19.debug("[LocalAudioPipeline] input disconnect warning:", error);
15175
+ logger$20.debug("[LocalAudioPipeline] input disconnect warning:", error);
15143
15176
  }
15144
15177
  this._inputSource = null;
15145
15178
  }
@@ -15149,7 +15182,7 @@ var LocalAudioPipeline = class extends Destroyable {
15149
15182
  this._inputSource = this._audioContext.createMediaStreamSource(this._inputStream);
15150
15183
  this._inputSource.connect(this._gainNode);
15151
15184
  if (this._audioContext.state === "suspended") this._audioContext.resume().catch((error) => {
15152
- logger$19.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15185
+ logger$20.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15153
15186
  });
15154
15187
  }
15155
15188
  destroy() {
@@ -15164,7 +15197,7 @@ var LocalAudioPipeline = class extends Destroyable {
15164
15197
  this._analyser.disconnect();
15165
15198
  } catch {}
15166
15199
  this._audioContext.close().catch((error) => {
15167
- logger$19.debug("[LocalAudioPipeline] audio context close warning:", error);
15200
+ logger$20.debug("[LocalAudioPipeline] audio context close warning:", error);
15168
15201
  });
15169
15202
  super.destroy();
15170
15203
  }
@@ -15191,7 +15224,7 @@ var LocalAudioPipeline = class extends Destroyable {
15191
15224
  //#endregion
15192
15225
  //#region src/controllers/LocalStreamController.ts
15193
15226
  var import_cjs$17 = require_cjs();
15194
- const logger$18 = getLogger();
15227
+ const logger$19 = getLogger();
15195
15228
  var LocalStreamController = class extends Destroyable {
15196
15229
  constructor(options) {
15197
15230
  super();
@@ -15229,26 +15262,26 @@ var LocalStreamController = class extends Destroyable {
15229
15262
  * Build the local media stream based on the provided options.
15230
15263
  */
15231
15264
  async buildLocalStream() {
15232
- logger$18.debug("[LocalStreamController] Building local media stream.");
15265
+ logger$19.debug("[LocalStreamController] Building local media stream.");
15233
15266
  let stream;
15234
15267
  if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
15235
15268
  const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
15236
15269
  stream = new MediaStream(tracks);
15237
15270
  } else if (this.options.propose === "screenshare") {
15238
- logger$18.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15271
+ logger$19.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15239
15272
  stream = await this.options.getDisplayMedia({
15240
15273
  video: true,
15241
15274
  audio: Boolean(this.options.inputAudioDeviceConstraints)
15242
15275
  });
15243
- logger$18.debug("[LocalStreamController] Screen share media obtained:", stream);
15276
+ logger$19.debug("[LocalStreamController] Screen share media obtained:", stream);
15244
15277
  } else {
15245
15278
  const constraints = {
15246
15279
  audio: this.options.inputAudioDeviceConstraints,
15247
15280
  video: this.options.inputVideoDeviceConstraints
15248
15281
  };
15249
- logger$18.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15282
+ logger$19.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15250
15283
  stream = await this.options.getUserMedia(constraints);
15251
- logger$18.debug("[LocalStreamController] User media obtained:", stream);
15284
+ logger$19.debug("[LocalStreamController] User media obtained:", stream);
15252
15285
  }
15253
15286
  this._localStream$.next(stream);
15254
15287
  this._localAudioTracks$.next(stream.getAudioTracks());
@@ -15267,7 +15300,7 @@ var LocalStreamController = class extends Destroyable {
15267
15300
  this._localStream$.next(localStream);
15268
15301
  if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
15269
15302
  else this._localAudioTracks$.next(localStream.getAudioTracks());
15270
- logger$18.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15303
+ logger$19.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15271
15304
  return localStream;
15272
15305
  }
15273
15306
  /**
@@ -15279,7 +15312,7 @@ var LocalStreamController = class extends Destroyable {
15279
15312
  const stream = this._localStream$.value;
15280
15313
  const track = stream?.getTracks().find((t) => t.id === trackId);
15281
15314
  if (!track) {
15282
- logger$18.debug(`[LocalStreamController] track not found: ${trackId}`);
15315
+ logger$19.debug(`[LocalStreamController] track not found: ${trackId}`);
15283
15316
  return;
15284
15317
  }
15285
15318
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
@@ -15288,7 +15321,7 @@ var LocalStreamController = class extends Destroyable {
15288
15321
  this._localStream$.next(stream);
15289
15322
  if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
15290
15323
  else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
15291
- logger$18.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15324
+ logger$19.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15292
15325
  return track;
15293
15326
  }
15294
15327
  /**
@@ -15323,7 +15356,7 @@ var LocalStreamController = class extends Destroyable {
15323
15356
  */
15324
15357
  stopAllTracks() {
15325
15358
  this._localStream$.value?.getTracks().forEach((track) => {
15326
- logger$18.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15359
+ logger$19.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15327
15360
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
15328
15361
  track.stop();
15329
15362
  });
@@ -15339,7 +15372,7 @@ var LocalStreamController = class extends Destroyable {
15339
15372
 
15340
15373
  //#endregion
15341
15374
  //#region src/controllers/TransceiverController.ts
15342
- const logger$17 = getLogger();
15375
+ const logger$18 = getLogger();
15343
15376
  const getDirection = (send, recv) => {
15344
15377
  if (send && recv) return "sendrecv";
15345
15378
  else if (send && !recv) return "sendonly";
@@ -15447,7 +15480,7 @@ var TransceiverController = class extends Destroyable {
15447
15480
  sendEncodings: isAudio ? void 0 : this.sendEncodings,
15448
15481
  streams: direction === "recvonly" ? void 0 : [localStream]
15449
15482
  };
15450
- logger$17.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15483
+ logger$18.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15451
15484
  transceiver,
15452
15485
  transceiverParams
15453
15486
  });
@@ -15455,11 +15488,11 @@ var TransceiverController = class extends Destroyable {
15455
15488
  await transceiver.sender.replaceTrack(track);
15456
15489
  transceiver.direction = transceiverParams.direction;
15457
15490
  if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
15458
- logger$17.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15491
+ logger$18.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15459
15492
  transceiver.sender.setStreams(...transceiverParams.streams);
15460
15493
  }
15461
15494
  } else {
15462
- logger$17.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15495
+ logger$18.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15463
15496
  this.peerConnection.addTransceiver(track, transceiverParams);
15464
15497
  }
15465
15498
  }
@@ -15473,13 +15506,13 @@ var TransceiverController = class extends Destroyable {
15473
15506
  if (options.updateTransceiverDirection) transceiver.direction = "inactive";
15474
15507
  }
15475
15508
  } catch (error) {
15476
- logger$17.error("[TransceiverController] stopTrackSender error", kind, error);
15509
+ logger$18.error("[TransceiverController] stopTrackSender error", kind, error);
15477
15510
  this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
15478
15511
  }
15479
15512
  }
15480
15513
  async restoreTrackSender(kind) {
15481
15514
  try {
15482
- logger$17.debug("[TransceiverController] restoreTrackSender called", kind);
15515
+ logger$18.debug("[TransceiverController] restoreTrackSender called", kind);
15483
15516
  const constraints = {};
15484
15517
  const transceivers = this.transceiverByKind(kind);
15485
15518
  for (const transceiver of transceivers) {
@@ -15489,23 +15522,23 @@ var TransceiverController = class extends Destroyable {
15489
15522
  if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
15490
15523
  }
15491
15524
  }
15492
- logger$17.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15525
+ logger$18.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15493
15526
  if (Object.keys(constraints).length === 0) {
15494
- logger$17.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15527
+ logger$18.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15495
15528
  return;
15496
15529
  }
15497
15530
  const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
15498
- logger$17.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15531
+ logger$18.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15499
15532
  for (const newTrack of newTracks) {
15500
15533
  this.options.localStreamController.addTrack(newTrack);
15501
15534
  const trackKind = newTrack.kind;
15502
15535
  const transceiverOfKind = this.transceiverByKind(trackKind)[0];
15503
15536
  transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
15504
- logger$17.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15537
+ logger$18.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15505
15538
  await transceiverOfKind.sender.replaceTrack(newTrack);
15506
15539
  }
15507
15540
  } catch (error) {
15508
- logger$17.error("[TransceiverController] restoreTrackSender error", kind, error);
15541
+ logger$18.error("[TransceiverController] restoreTrackSender error", kind, error);
15509
15542
  this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
15510
15543
  }
15511
15544
  }
@@ -15546,14 +15579,14 @@ var TransceiverController = class extends Destroyable {
15546
15579
  };
15547
15580
  try {
15548
15581
  await track.applyConstraints(constraintsToApply);
15549
- logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15550
- logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15582
+ logger$18.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15583
+ logger$18.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15551
15584
  } catch (error) {
15552
- logger$17.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15585
+ logger$18.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15553
15586
  try {
15554
15587
  await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
15555
15588
  } catch (fallbackError) {
15556
- logger$17.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15589
+ logger$18.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15557
15590
  this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
15558
15591
  }
15559
15592
  }
@@ -15581,7 +15614,7 @@ var TransceiverController = class extends Destroyable {
15581
15614
  if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
15582
15615
  await sender.replaceTrack(newTrack);
15583
15616
  this.options.localStreamController.addTrack(newTrack);
15584
- logger$17.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15617
+ logger$18.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15585
15618
  }
15586
15619
  getMediaDirections() {
15587
15620
  if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
@@ -15612,7 +15645,7 @@ var TransceiverController = class extends Destroyable {
15612
15645
  //#endregion
15613
15646
  //#region src/controllers/RTCPeerConnectionController.ts
15614
15647
  var import_cjs$16 = require_cjs();
15615
- const logger$16 = getLogger();
15648
+ const logger$17 = getLogger();
15616
15649
  var RTCPeerConnectionController = class extends Destroyable {
15617
15650
  constructor(options = {}, remoteSessionDescription, deviceController) {
15618
15651
  super();
@@ -15628,43 +15661,43 @@ var RTCPeerConnectionController = class extends Destroyable {
15628
15661
  this.oniceconnectionstatechangeHandler = () => {
15629
15662
  if (this.peerConnection) {
15630
15663
  const { iceConnectionState } = this.peerConnection;
15631
- logger$16.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15664
+ logger$17.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15632
15665
  this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
15633
15666
  }
15634
15667
  };
15635
15668
  this.onconnectionstatechangeHandler = () => {
15636
15669
  if (this.peerConnection) {
15637
15670
  const { connectionState } = this.peerConnection;
15638
- logger$16.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15671
+ logger$17.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15639
15672
  if (connectionState === "connected") this.removeConnectionTimer();
15640
15673
  this._connectionState$.next(this.peerConnection.connectionState);
15641
15674
  }
15642
15675
  };
15643
15676
  this.onsignalingstatechangeHandler = () => {
15644
- logger$16.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15677
+ logger$17.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15645
15678
  };
15646
15679
  this.onicegatheringstatechangeHandler = () => {
15647
15680
  if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
15648
15681
  };
15649
15682
  this.onnegotiationneededHandler = (event) => {
15650
- logger$16.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15683
+ logger$17.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15651
15684
  this.negotiationNeeded$.next();
15652
15685
  };
15653
15686
  this.updateSelectedInputDevice = async (kind, deviceInfo) => {
15654
15687
  try {
15655
15688
  const { localStream } = this;
15656
15689
  if (!localStream) {
15657
- logger$16.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15690
+ logger$17.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15658
15691
  return;
15659
15692
  }
15660
- logger$16.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15693
+ logger$17.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15661
15694
  const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
15662
15695
  if (track) {
15663
15696
  this.transceiverController?.stopTrackSender(kind);
15664
15697
  this.localStreamController.removeTrack(track.id);
15665
- logger$16.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15698
+ logger$17.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15666
15699
  if (!deviceInfo) {
15667
- logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15700
+ logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15668
15701
  return;
15669
15702
  }
15670
15703
  const streamTrack = (await this.getUserMedia({ [kind]: {
@@ -15672,15 +15705,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15672
15705
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
15673
15706
  } })).getTracks().find((t) => t.kind === kind);
15674
15707
  if (streamTrack) {
15675
- logger$16.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15708
+ logger$17.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15676
15709
  this.localStreamController.addTrack(streamTrack);
15677
15710
  await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
15678
- logger$16.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15711
+ logger$17.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15679
15712
  }
15680
15713
  }
15681
- logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15714
+ logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15682
15715
  } catch (error) {
15683
- logger$16.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15716
+ logger$17.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15684
15717
  this._errors$.next(toError(error));
15685
15718
  throw error;
15686
15719
  }
@@ -15930,15 +15963,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15930
15963
  this.setupPeerConnection();
15931
15964
  this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$16.auditTime)(0), (0, import_cjs$16.exhaustMap)(async () => this.startNegotiation())), {
15932
15965
  next: () => {
15933
- logger$16.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15966
+ logger$17.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15934
15967
  },
15935
15968
  error: (error) => {
15936
- logger$16.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15969
+ logger$17.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15937
15970
  this._errors$.next(toError(error));
15938
15971
  }
15939
15972
  });
15940
15973
  this.subscribeTo((0, import_cjs$16.merge)(this.deviceController.selectedAudioInputDevice$.pipe((0, import_cjs$16.map)((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe((0, import_cjs$16.map)((deviceInfo) => ["video", deviceInfo]))).pipe((0, import_cjs$16.skipWhile)(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
15941
- logger$16.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15974
+ logger$17.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15942
15975
  kind,
15943
15976
  deviceInfo
15944
15977
  });
@@ -15955,7 +15988,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15955
15988
  this._initialized$.next(true);
15956
15989
  }
15957
15990
  } catch (error) {
15958
- logger$16.error("[RTCPeerConnectionController] Initialization error:", error);
15991
+ logger$17.error("[RTCPeerConnectionController] Initialization error:", error);
15959
15992
  this._errors$.next(toError(error));
15960
15993
  this.destroy();
15961
15994
  }
@@ -15987,22 +16020,22 @@ var RTCPeerConnectionController = class extends Destroyable {
15987
16020
  }
15988
16021
  async startNegotiation() {
15989
16022
  if (this.isNegotiating) {
15990
- logger$16.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
16023
+ logger$17.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15991
16024
  return;
15992
16025
  }
15993
16026
  this.setupEventListeners();
15994
16027
  if (this.type === "answer") {
15995
- logger$16.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
16028
+ logger$17.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15996
16029
  return;
15997
16030
  }
15998
16031
  this._isNegotiating$.next(true);
15999
- logger$16.debug("[RTCPeerConnectionController] Starting negotiation.");
16032
+ logger$17.debug("[RTCPeerConnectionController] Starting negotiation.");
16000
16033
  try {
16001
16034
  const { offerOptions } = this;
16002
- logger$16.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
16035
+ logger$17.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
16003
16036
  await this.createOffer(offerOptions);
16004
16037
  } catch (error) {
16005
- logger$16.error("[RTCPeerConnectionController] Error during negotiation:", error);
16038
+ logger$17.error("[RTCPeerConnectionController] Error during negotiation:", error);
16006
16039
  this._errors$.next(toError(error));
16007
16040
  }
16008
16041
  }
@@ -16018,14 +16051,14 @@ var RTCPeerConnectionController = class extends Destroyable {
16018
16051
  let readyToConnect = status !== "failed";
16019
16052
  try {
16020
16053
  if (status === "received" && sdp) {
16021
- logger$16.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16054
+ logger$17.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16022
16055
  await this._setRemoteDescription({
16023
16056
  type: "answer",
16024
16057
  sdp
16025
16058
  });
16026
16059
  }
16027
16060
  } catch (error) {
16028
- logger$16.error("[RTCPeerConnectionController] Error updating answer status:", error);
16061
+ logger$17.error("[RTCPeerConnectionController] Error updating answer status:", error);
16029
16062
  this._errors$.next(toError(error));
16030
16063
  readyToConnect = false;
16031
16064
  } finally {
@@ -16044,7 +16077,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16044
16077
  await this.handleOfferReceived();
16045
16078
  break;
16046
16079
  case "failed":
16047
- logger$16.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16080
+ logger$17.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16048
16081
  break;
16049
16082
  case "sent":
16050
16083
  default:
@@ -16076,7 +16109,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16076
16109
  }
16077
16110
  await this.setupLocalTracks();
16078
16111
  const { answerOptions } = this;
16079
- logger$16.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16112
+ logger$17.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16080
16113
  await this.createAnswer(answerOptions);
16081
16114
  }
16082
16115
  async handleOfferReceived() {
@@ -16084,7 +16117,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16084
16117
  this._isNegotiating$.next(true);
16085
16118
  await this._setRemoteDescription(this.sdpInit);
16086
16119
  const { answerOptions } = this;
16087
- logger$16.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16120
+ logger$17.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16088
16121
  await this.createAnswer(answerOptions);
16089
16122
  }
16090
16123
  readyToConnect() {
@@ -16092,7 +16125,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16092
16125
  this.connectionTimer = setTimeout(() => {
16093
16126
  this.removeConnectionTimer();
16094
16127
  if (this.peerConnection?.connectionState !== "connected") {
16095
- logger$16.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16128
+ logger$17.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16096
16129
  this.iceGatheringController.restartICEGatheringWithRelayOnly();
16097
16130
  }
16098
16131
  }, this.connectionTimeout);
@@ -16114,14 +16147,14 @@ var RTCPeerConnectionController = class extends Destroyable {
16114
16147
  const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
16115
16148
  if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
16116
16149
  result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
16117
- logger$16.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16150
+ logger$17.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16118
16151
  preferredAudioCodecs,
16119
16152
  preferredVideoCodecs
16120
16153
  });
16121
16154
  }
16122
16155
  if (stereo) {
16123
16156
  result = enableStereoOpus(result);
16124
- logger$16.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16157
+ logger$17.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16125
16158
  }
16126
16159
  return Promise.resolve(result);
16127
16160
  }
@@ -16177,25 +16210,25 @@ var RTCPeerConnectionController = class extends Destroyable {
16177
16210
  ...this.peerConnection.getConfiguration(),
16178
16211
  iceTransportPolicy: "relay"
16179
16212
  });
16180
- logger$16.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16213
+ logger$17.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16181
16214
  } catch (error) {
16182
- logger$16.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16215
+ logger$17.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16183
16216
  }
16184
16217
  this.setupEventListeners();
16185
16218
  this._isNegotiating$.next(true);
16186
- logger$16.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16219
+ logger$17.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16187
16220
  try {
16188
16221
  const offer = await this.peerConnection.createOffer({ iceRestart: true });
16189
16222
  await this.setLocalDescription(offer);
16190
16223
  } catch (error) {
16191
- logger$16.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16224
+ logger$17.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16192
16225
  this._errors$.next(toError(error));
16193
16226
  this.negotiationEnded();
16194
16227
  if (policyChanged) this.restoreIceTransportPolicy();
16195
16228
  throw error;
16196
16229
  }
16197
16230
  if (policyChanged) (0, import_cjs$16.firstValueFrom)((0, import_cjs$16.race)(this._iceGatheringState$.pipe((0, import_cjs$16.filter)((state) => state === "complete"), (0, import_cjs$16.take)(1)), (0, import_cjs$16.timer)(ICE_GATHERING_COMPLETE_TIMEOUT_MS).pipe((0, import_cjs$16.map)(() => "timeout")))).then(() => this.restoreIceTransportPolicy()).catch((error) => {
16198
- logger$16.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16231
+ logger$17.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16199
16232
  this.restoreIceTransportPolicy();
16200
16233
  });
16201
16234
  }
@@ -16205,9 +16238,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16205
16238
  ...this.peerConnection.getConfiguration(),
16206
16239
  iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
16207
16240
  });
16208
- logger$16.debug("[RTCPeerConnectionController] ICE transport policy restored");
16241
+ logger$17.debug("[RTCPeerConnectionController] ICE transport policy restored");
16209
16242
  } catch (error) {
16210
- logger$16.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16243
+ logger$17.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16211
16244
  }
16212
16245
  }
16213
16246
  /**
@@ -16219,13 +16252,13 @@ var RTCPeerConnectionController = class extends Destroyable {
16219
16252
  await this.setupRemoteTracks();
16220
16253
  }
16221
16254
  async setupLocalTracks() {
16222
- logger$16.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16255
+ logger$17.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16223
16256
  const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
16224
16257
  if (this.transceiverController?.useAddStream ?? false) {
16225
- logger$16.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16258
+ logger$17.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16226
16259
  this.peerConnection?.addStream(localStream);
16227
16260
  if (!this.isNegotiating) {
16228
- logger$16.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16261
+ logger$17.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16229
16262
  this.negotiationNeeded$.next();
16230
16263
  }
16231
16264
  return;
@@ -16241,7 +16274,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16241
16274
  const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
16242
16275
  await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
16243
16276
  } else {
16244
- logger$16.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16277
+ logger$17.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16245
16278
  this.peerConnection?.addTrack(track, localStream);
16246
16279
  }
16247
16280
  }
@@ -16258,7 +16291,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16258
16291
  async setupRemoteTracks() {
16259
16292
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
16260
16293
  this.peerConnection.ontrack = (event) => {
16261
- logger$16.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16294
+ logger$17.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16262
16295
  if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
16263
16296
  else {
16264
16297
  const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
@@ -16282,7 +16315,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16282
16315
  try {
16283
16316
  stream = await this.getUserMedia({ audio: constraints });
16284
16317
  } catch (error) {
16285
- logger$16.error("[RTCPeerConnectionController] Failed to re-acquire mic for pipeline restore:", error);
16318
+ logger$17.error("[RTCPeerConnectionController] Failed to re-acquire mic for pipeline restore:", error);
16286
16319
  this._errors$.next(toError(error));
16287
16320
  return;
16288
16321
  }
@@ -16304,7 +16337,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16304
16337
  try {
16305
16338
  this._localAudioPipeline = new LocalAudioPipeline();
16306
16339
  } catch (error) {
16307
- logger$16.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16340
+ logger$17.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16308
16341
  return null;
16309
16342
  }
16310
16343
  this.subscribeTo(this.localStreamController.localAudioTracks$, () => {
@@ -16326,7 +16359,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16326
16359
  try {
16327
16360
  await sender.replaceTrack(this._localAudioPipeline.outputTrack);
16328
16361
  } catch (error) {
16329
- logger$16.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16362
+ logger$17.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16330
16363
  }
16331
16364
  }
16332
16365
  /**
@@ -16342,9 +16375,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16342
16375
  try {
16343
16376
  const localStream = this.localStreamController.addTrack(track);
16344
16377
  this.peerConnection.addTrack(track, localStream);
16345
- logger$16.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16378
+ logger$17.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16346
16379
  } catch (error) {
16347
- logger$16.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16380
+ logger$17.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16348
16381
  this._errors$.next(toError(error));
16349
16382
  throw error;
16350
16383
  }
@@ -16361,15 +16394,15 @@ var RTCPeerConnectionController = class extends Destroyable {
16361
16394
  }
16362
16395
  const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
16363
16396
  if (!sender) {
16364
- logger$16.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16397
+ logger$17.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16365
16398
  return;
16366
16399
  }
16367
16400
  try {
16368
16401
  this.peerConnection.removeTrack(sender);
16369
16402
  this.localStreamController.removeTrack(trackId);
16370
- logger$16.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16403
+ logger$17.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16371
16404
  } catch (error) {
16372
- logger$16.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16405
+ logger$17.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16373
16406
  this._errors$.next(toError(error));
16374
16407
  throw error;
16375
16408
  }
@@ -16396,7 +16429,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16396
16429
  async replaceAudioTrackWithConstraints(constraints) {
16397
16430
  const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
16398
16431
  if (!senders || senders.length === 0) {
16399
- logger$16.warn("[RTCPeerConnectionController] No live audio sender to replace");
16432
+ logger$17.warn("[RTCPeerConnectionController] No live audio sender to replace");
16400
16433
  return;
16401
16434
  }
16402
16435
  for (const sender of senders) {
@@ -16414,7 +16447,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16414
16447
  const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
16415
16448
  await sender.replaceTrack(newTrack);
16416
16449
  this.localStreamController.addTrack(newTrack);
16417
- logger$16.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16450
+ logger$17.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16418
16451
  }
16419
16452
  }
16420
16453
  /**
@@ -16422,7 +16455,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16422
16455
  * Completes all observables to prevent memory leaks.
16423
16456
  */
16424
16457
  destroy() {
16425
- logger$16.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16458
+ logger$17.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16426
16459
  this.removeConnectionTimer();
16427
16460
  this._iceGatheringController?.destroy();
16428
16461
  this._localAudioPipeline?.destroy();
@@ -16448,7 +16481,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16448
16481
  }
16449
16482
  stopRemoteTracks() {
16450
16483
  this._remoteStream$.value?.getTracks().forEach((track) => {
16451
- logger$16.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16484
+ logger$17.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16452
16485
  track.stop();
16453
16486
  });
16454
16487
  }
@@ -16465,7 +16498,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16465
16498
  ...params,
16466
16499
  sdp: finalRemote
16467
16500
  };
16468
- logger$16.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16501
+ logger$17.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16469
16502
  return this.peerConnection.setRemoteDescription(answer);
16470
16503
  }
16471
16504
  };
@@ -16480,7 +16513,11 @@ function isVertoInviteMessage(value) {
16480
16513
  const msg = value;
16481
16514
  return msg.method === "verto.invite" && isObject(msg.params) && hasProperty(msg.params, "sdp") && hasProperty(msg.params, "callID");
16482
16515
  }
16483
- function isVertoByeMessage(value) {
16516
+ /**
16517
+ * Guards inbound `verto.bye` frames (server → client). Outbound bye messages are
16518
+ * built locally and never type-guarded, so only the inbound shape is asserted.
16519
+ */
16520
+ function isVertoByeInboundMessage(value) {
16484
16521
  if (!isVertoMethodMessage(value)) return false;
16485
16522
  return value.method === "verto.bye";
16486
16523
  }
@@ -16500,11 +16537,19 @@ function isVertoMediaParamsInnerParams(value) {
16500
16537
  function isVertoPingInnerParams(value) {
16501
16538
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.ping";
16502
16539
  }
16540
+ /**
16541
+ * Guards the params of an inbound `verto.bye` frame (top-level `callID`). Use
16542
+ * this to recognise the bye payload extracted by `vertoBye$`, e.g. when racing
16543
+ * it against a boolean answer/reject signal.
16544
+ */
16545
+ function isVertoByeInboundParamsGuard(value) {
16546
+ return isObject(value) && hasProperty(value, "callID") && typeof value.callID === "string";
16547
+ }
16503
16548
 
16504
16549
  //#endregion
16505
16550
  //#region src/managers/VertoManager.ts
16506
16551
  var import_cjs$15 = require_cjs();
16507
- const logger$15 = getLogger();
16552
+ const logger$16 = getLogger();
16508
16553
  /**
16509
16554
  * Decide what value goes on the `node_id` field of a `webrtc.verto` envelope.
16510
16555
  *
@@ -16560,7 +16605,7 @@ var WebRTCVertoManager = class extends VertoManager {
16560
16605
  try {
16561
16606
  await this.executeVerto(vertoModifyMessage);
16562
16607
  } catch (error) {
16563
- logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16608
+ logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16564
16609
  throw error;
16565
16610
  }
16566
16611
  }
@@ -16573,7 +16618,7 @@ var WebRTCVertoManager = class extends VertoManager {
16573
16618
  try {
16574
16619
  await this.executeVerto(vertoModifyMessage);
16575
16620
  } catch (error) {
16576
- logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16621
+ logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16577
16622
  throw error;
16578
16623
  }
16579
16624
  }
@@ -16626,7 +16671,7 @@ var WebRTCVertoManager = class extends VertoManager {
16626
16671
  if (event.member_id) this.setSelfIdIfNull(event.member_id);
16627
16672
  });
16628
16673
  this.subscribeTo(this.vertoMedia$, (event) => {
16629
- logger$15.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16674
+ logger$16.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16630
16675
  const { sdp, callID } = event;
16631
16676
  this.emitMainSignalingStatus(callID, "ringing");
16632
16677
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16635,7 +16680,7 @@ var WebRTCVertoManager = class extends VertoManager {
16635
16680
  });
16636
16681
  });
16637
16682
  this.subscribeTo(this.vertoAnswer$, (event) => {
16638
- logger$15.debug("[WebRTCManager] Received Verto answer event:", event);
16683
+ logger$16.debug("[WebRTCManager] Received Verto answer event:", event);
16639
16684
  const { sdp, callID } = event;
16640
16685
  this.emitMainSignalingStatus(callID, "connecting");
16641
16686
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16644,7 +16689,7 @@ var WebRTCVertoManager = class extends VertoManager {
16644
16689
  });
16645
16690
  });
16646
16691
  this.subscribeTo(this.vertoMediaParams$, (event) => {
16647
- logger$15.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16692
+ logger$16.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16648
16693
  const { mediaParams, callID } = event;
16649
16694
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
16650
16695
  const { audio, video } = mediaParams;
@@ -16658,7 +16703,7 @@ var WebRTCVertoManager = class extends VertoManager {
16658
16703
  timestamp: Date.now()
16659
16704
  });
16660
16705
  } catch (error) {
16661
- logger$15.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16706
+ logger$16.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16662
16707
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16663
16708
  }
16664
16709
  })();
@@ -16680,13 +16725,13 @@ var WebRTCVertoManager = class extends VertoManager {
16680
16725
  */
16681
16726
  setNodeIdIfNull(nodeId) {
16682
16727
  if (!this._nodeId$.value && nodeId) {
16683
- logger$15.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16728
+ logger$16.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16684
16729
  this._nodeId$.next(nodeId);
16685
16730
  }
16686
16731
  }
16687
16732
  setSelfIdIfNull(selfId) {
16688
16733
  if (!this._selfId$.value && selfId) {
16689
- logger$15.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16734
+ logger$16.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16690
16735
  this._selfId$.next(selfId);
16691
16736
  }
16692
16737
  }
@@ -16695,7 +16740,7 @@ var WebRTCVertoManager = class extends VertoManager {
16695
16740
  const vertoPongMessage = VertoPong({ ...vertoPing });
16696
16741
  await this.executeVerto(vertoPongMessage);
16697
16742
  } catch (error) {
16698
- logger$15.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16743
+ logger$16.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16699
16744
  this.onError?.(new VertoPongError(error));
16700
16745
  }
16701
16746
  }
@@ -16705,7 +16750,7 @@ var WebRTCVertoManager = class extends VertoManager {
16705
16750
  if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
16706
16751
  if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
16707
16752
  } catch (error) {
16708
- logger$15.warn("[WebRTCManager] Error updating media constraints:", error);
16753
+ logger$16.warn("[WebRTCManager] Error updating media constraints:", error);
16709
16754
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16710
16755
  throw error;
16711
16756
  }
@@ -16735,20 +16780,20 @@ var WebRTCVertoManager = class extends VertoManager {
16735
16780
  try {
16736
16781
  const pc = this.mainPeerConnection.peerConnection;
16737
16782
  if (!pc) {
16738
- logger$15.warn("[WebRTCManager] No peer connection for keyframe request");
16783
+ logger$16.warn("[WebRTCManager] No peer connection for keyframe request");
16739
16784
  return;
16740
16785
  }
16741
16786
  const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
16742
16787
  if (!videoReceiver) {
16743
- logger$15.warn("[WebRTCManager] No video receiver for keyframe request");
16788
+ logger$16.warn("[WebRTCManager] No video receiver for keyframe request");
16744
16789
  return;
16745
16790
  }
16746
16791
  if (typeof videoReceiver.requestKeyFrame === "function") {
16747
16792
  videoReceiver.requestKeyFrame();
16748
- logger$15.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16749
- } else logger$15.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16793
+ logger$16.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16794
+ } else logger$16.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16750
16795
  } catch (error) {
16751
- logger$15.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16796
+ logger$16.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16752
16797
  }
16753
16798
  }
16754
16799
  /**
@@ -16766,13 +16811,13 @@ var WebRTCVertoManager = class extends VertoManager {
16766
16811
  try {
16767
16812
  const controller = this.mainPeerConnection;
16768
16813
  if (!controller.peerConnection) {
16769
- logger$15.warn("[WebRTCManager] No peer connection for ICE restart");
16814
+ logger$16.warn("[WebRTCManager] No peer connection for ICE restart");
16770
16815
  return;
16771
16816
  }
16772
16817
  await controller.triggerIceRestart(relayOnly);
16773
- logger$15.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16818
+ logger$16.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16774
16819
  } catch (error) {
16775
- logger$15.error("[WebRTCManager] ICE restart failed:", error);
16820
+ logger$16.error("[WebRTCManager] ICE restart failed:", error);
16776
16821
  throw error;
16777
16822
  }
16778
16823
  }
@@ -16790,13 +16835,13 @@ var WebRTCVertoManager = class extends VertoManager {
16790
16835
  const entries = Array.from(this._rtcPeerConnectionsMap.entries());
16791
16836
  for (const [id, controller] of entries) try {
16792
16837
  if (!controller.peerConnection) {
16793
- logger$15.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16838
+ logger$16.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16794
16839
  continue;
16795
16840
  }
16796
16841
  await controller.triggerIceRestart(relayOnly);
16797
- logger$15.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16842
+ logger$16.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16798
16843
  } catch (error) {
16799
- logger$15.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16844
+ logger$16.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16800
16845
  }
16801
16846
  }
16802
16847
  /**
@@ -16808,7 +16853,7 @@ var WebRTCVertoManager = class extends VertoManager {
16808
16853
  requestKeyframeAll() {
16809
16854
  for (const [id, controller] of this._rtcPeerConnectionsMap) {
16810
16855
  if (controller.isScreenShare) {
16811
- logger$15.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16856
+ logger$16.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16812
16857
  continue;
16813
16858
  }
16814
16859
  try {
@@ -16818,10 +16863,10 @@ var WebRTCVertoManager = class extends VertoManager {
16818
16863
  if (!videoReceiver) continue;
16819
16864
  if (typeof videoReceiver.requestKeyFrame === "function") {
16820
16865
  videoReceiver.requestKeyFrame();
16821
- logger$15.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16866
+ logger$16.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16822
16867
  }
16823
16868
  } catch (error) {
16824
- logger$15.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16869
+ logger$16.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16825
16870
  }
16826
16871
  }
16827
16872
  }
@@ -16838,7 +16883,7 @@ var WebRTCVertoManager = class extends VertoManager {
16838
16883
  return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16839
16884
  }
16840
16885
  get vertoBye$() {
16841
- return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16886
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeInboundMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16842
16887
  }
16843
16888
  get vertoAttach$() {
16844
16889
  return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
@@ -16882,7 +16927,7 @@ var WebRTCVertoManager = class extends VertoManager {
16882
16927
  default:
16883
16928
  }
16884
16929
  } catch (error) {
16885
- logger$15.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16930
+ logger$16.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16886
16931
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16887
16932
  if (vertoMethod === "verto.modify") this.onModifyFailed?.();
16888
16933
  }
@@ -16897,7 +16942,7 @@ var WebRTCVertoManager = class extends VertoManager {
16897
16942
  sdp
16898
16943
  });
16899
16944
  } catch (error) {
16900
- logger$15.warn("[WebRTCManager] Error processing modify response:", error);
16945
+ logger$16.warn("[WebRTCManager] Error processing modify response:", error);
16901
16946
  const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
16902
16947
  this.onError?.(modifyError);
16903
16948
  }
@@ -16907,7 +16952,7 @@ var WebRTCVertoManager = class extends VertoManager {
16907
16952
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callId);
16908
16953
  if (!rtcPeerConnController) {
16909
16954
  const signalingError = new DependencyError(`Cannot emit signaling status, RTCPeerConnectionController not found for callID: ${callId}`);
16910
- logger$15.error("[WebRTCManager] Failed to emit signaling status:", {
16955
+ logger$16.error("[WebRTCManager] Failed to emit signaling status:", {
16911
16956
  callId,
16912
16957
  status,
16913
16958
  signalingError
@@ -16923,7 +16968,7 @@ var WebRTCVertoManager = class extends VertoManager {
16923
16968
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
16924
16969
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
16925
16970
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
16926
- logger$15.debug("[WebRTCManager] Verto invite response:", {
16971
+ logger$16.debug("[WebRTCManager] Verto invite response:", {
16927
16972
  callId,
16928
16973
  memberId,
16929
16974
  response
@@ -16933,14 +16978,14 @@ var WebRTCVertoManager = class extends VertoManager {
16933
16978
  if (callId) {
16934
16979
  this.webRtcCallSession.addCallId(callId);
16935
16980
  this.attachManager.attach(this.buildAttachableCall(callId));
16936
- } else logger$15.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16981
+ } else logger$16.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16937
16982
  nodeId: this.nodeId,
16938
16983
  callId
16939
16984
  });
16940
- logger$15.info("[WebRTCManager] Verto invite successful");
16941
- logger$15.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16985
+ logger$16.info("[WebRTCManager] Verto invite successful");
16986
+ logger$16.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16942
16987
  } else {
16943
- logger$15.error("[WebRTCManager] Verto invite failed:", response);
16988
+ logger$16.error("[WebRTCManager] Verto invite failed:", response);
16944
16989
  const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
16945
16990
  this.onError?.(inviteError);
16946
16991
  }
@@ -16985,17 +17030,17 @@ var WebRTCVertoManager = class extends VertoManager {
16985
17030
  if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
16986
17031
  }
16987
17032
  async handleInboundAnswer(rtcPeerConnController) {
16988
- logger$15.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
17033
+ logger$16.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16989
17034
  const vertoByeOrAccepted = await (0, import_cjs$15.firstValueFrom)((0, import_cjs$15.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$15.takeUntil)(this.destroyed$))).catch(() => null);
16990
17035
  if (vertoByeOrAccepted === null) {
16991
- logger$15.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
17036
+ logger$16.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16992
17037
  return;
16993
17038
  }
16994
- if (isVertoByeMessage(vertoByeOrAccepted)) {
16995
- logger$15.info("[WebRTCManager] Inbound call ended by remote before answer.");
17039
+ if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
17040
+ logger$16.info("[WebRTCManager] Inbound call ended by remote before answer.");
16996
17041
  this.callSession?.destroy();
16997
17042
  } else if (!vertoByeOrAccepted) {
16998
- logger$15.info("[WebRTCManager] Inbound call rejected by user.");
17043
+ logger$16.info("[WebRTCManager] Inbound call rejected by user.");
16999
17044
  try {
17000
17045
  await this.bye("USER_BUSY");
17001
17046
  } finally {
@@ -17003,19 +17048,19 @@ var WebRTCVertoManager = class extends VertoManager {
17003
17048
  this.callSession?.destroy();
17004
17049
  }
17005
17050
  } else {
17006
- logger$15.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
17051
+ logger$16.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
17007
17052
  const answerOptions = this.webRtcCallSession.answerMediaOptions;
17008
17053
  try {
17009
17054
  await rtcPeerConnController.acceptInbound(answerOptions);
17010
17055
  } catch (error) {
17011
- logger$15.error("[WebRTCManager] Error creating inbound answer:", error);
17056
+ logger$16.error("[WebRTCManager] Error creating inbound answer:", error);
17012
17057
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
17013
17058
  }
17014
17059
  }
17015
17060
  }
17016
17061
  setupVertoAttachHandler() {
17017
17062
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
17018
- logger$15.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
17063
+ logger$16.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
17019
17064
  const { callID } = vertoAttach;
17020
17065
  await this.attachManager.attach({
17021
17066
  nodeId: this.nodeId ?? void 0,
@@ -17087,17 +17132,17 @@ var WebRTCVertoManager = class extends VertoManager {
17087
17132
  };
17088
17133
  }
17089
17134
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
17090
- logger$15.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17135
+ logger$16.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17091
17136
  const vertoByeOrAccepted = await (0, import_cjs$15.firstValueFrom)((0, import_cjs$15.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$15.takeUntil)(this.destroyed$))).catch(() => null);
17092
17137
  if (vertoByeOrAccepted === null) {
17093
- logger$15.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17138
+ logger$16.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17094
17139
  return;
17095
17140
  }
17096
- if (isVertoByeMessage(vertoByeOrAccepted)) {
17097
- logger$15.info("[WebRTCManager] Call ended before answer was sent.");
17141
+ if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
17142
+ logger$16.info("[WebRTCManager] Call ended before answer was sent.");
17098
17143
  this.callSession?.destroy();
17099
17144
  } else if (!vertoByeOrAccepted) {
17100
- logger$15.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17145
+ logger$16.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17101
17146
  try {
17102
17147
  await this.bye("USER_BUSY");
17103
17148
  } finally {
@@ -17105,14 +17150,14 @@ var WebRTCVertoManager = class extends VertoManager {
17105
17150
  this.callSession?.destroy();
17106
17151
  }
17107
17152
  } else {
17108
- logger$15.debug("[WebRTCManager] Call accepted, sending answer");
17153
+ logger$16.debug("[WebRTCManager] Call accepted, sending answer");
17109
17154
  try {
17110
17155
  this.emitMainSignalingStatus(rtcPeerConnectionController.id, "connecting");
17111
17156
  await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
17112
17157
  await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
17113
17158
  await this.attachManager.attach(this.buildAttachableCall());
17114
17159
  } catch (error) {
17115
- logger$15.error("[WebRTCManager] Error sending Verto answer:", error);
17160
+ logger$16.error("[WebRTCManager] Error sending Verto answer:", error);
17116
17161
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
17117
17162
  await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
17118
17163
  }
@@ -17211,10 +17256,10 @@ var WebRTCVertoManager = class extends VertoManager {
17211
17256
  });
17212
17257
  await (0, import_cjs$15.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, import_cjs$15.filter)((state) => state === "connected"), (0, import_cjs$15.take)(1), (0, import_cjs$15.timeout)(this._screenShareTimeoutMs), (0, import_cjs$15.takeUntil)(this.destroyed$)));
17213
17258
  this._screenShareStatus$.next("started");
17214
- logger$15.info("[WebRTCManager] Screen share started successfully.");
17259
+ logger$16.info("[WebRTCManager] Screen share started successfully.");
17215
17260
  return rtcPeerConnController.id;
17216
17261
  } catch (error) {
17217
- logger$15.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17262
+ logger$16.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17218
17263
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
17219
17264
  if (rtcPeerConnController) rtcPeerConnController.destroy();
17220
17265
  this._screenShareStatus$.next("none");
@@ -17233,9 +17278,9 @@ var WebRTCVertoManager = class extends VertoManager {
17233
17278
  if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
17234
17279
  }
17235
17280
  async removeScreenMedia() {
17236
- if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$15.warn("[WebRTCManager] No active screen share to stop.");
17281
+ if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$16.warn("[WebRTCManager] No active screen share to stop.");
17237
17282
  if (!this._screenShareId) {
17238
- logger$15.debug("[WebRTCManager] No screen share peer connection found.");
17283
+ logger$16.debug("[WebRTCManager] No screen share peer connection found.");
17239
17284
  return;
17240
17285
  }
17241
17286
  this._screenShareStatus$.next("stopping");
@@ -17264,7 +17309,7 @@ var WebRTCVertoManager = class extends VertoManager {
17264
17309
  dialogParams: this.dialogParams(rtcPeerConnController)
17265
17310
  }));
17266
17311
  } catch (error) {
17267
- logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17312
+ logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17268
17313
  throw error;
17269
17314
  }
17270
17315
  }
@@ -17282,7 +17327,7 @@ var WebRTCVertoManager = class extends VertoManager {
17282
17327
  try {
17283
17328
  await this.executeVerto(vertoInfoMessage);
17284
17329
  } catch (error) {
17285
- logger$15.warn("[WebRTCManager] Error sending DTMF digits:", error);
17330
+ logger$16.warn("[WebRTCManager] Error sending DTMF digits:", error);
17286
17331
  throw error;
17287
17332
  }
17288
17333
  }
@@ -17293,10 +17338,10 @@ var WebRTCVertoManager = class extends VertoManager {
17293
17338
  action: "transfer"
17294
17339
  });
17295
17340
  try {
17296
- logger$15.debug("[WebRTCManager] Transferring call with options:", options);
17341
+ logger$16.debug("[WebRTCManager] Transferring call with options:", options);
17297
17342
  await this.executeVerto(message);
17298
17343
  } catch (error) {
17299
- logger$15.error("[WebRTCManager] Error transferring call:", error);
17344
+ logger$16.error("[WebRTCManager] Error transferring call:", error);
17300
17345
  throw error;
17301
17346
  }
17302
17347
  }
@@ -17313,7 +17358,7 @@ var WebRTCVertoManager = class extends VertoManager {
17313
17358
  //#endregion
17314
17359
  //#region src/controllers/RemoteAudioMeter.ts
17315
17360
  var import_cjs$14 = require_cjs();
17316
- const logger$14 = getLogger();
17361
+ const logger$15 = getLogger();
17317
17362
  /**
17318
17363
  * Read-only audio level meter for a remote MediaStream. Attaches an
17319
17364
  * AnalyserNode to a MediaStreamAudioSourceNode so it observes the stream
@@ -17348,7 +17393,7 @@ var RemoteAudioMeter = class extends Destroyable {
17348
17393
  try {
17349
17394
  this._source.disconnect();
17350
17395
  } catch (error) {
17351
- logger$14.debug("[RemoteAudioMeter] source disconnect warning:", error);
17396
+ logger$15.debug("[RemoteAudioMeter] source disconnect warning:", error);
17352
17397
  }
17353
17398
  this._source = null;
17354
17399
  this._stream = null;
@@ -17365,7 +17410,7 @@ var RemoteAudioMeter = class extends Destroyable {
17365
17410
  this._source = null;
17366
17411
  }
17367
17412
  this._audioContext.close().catch((error) => {
17368
- logger$14.debug("[RemoteAudioMeter] audio context close warning:", error);
17413
+ logger$15.debug("[RemoteAudioMeter] audio context close warning:", error);
17369
17414
  });
17370
17415
  super.destroy();
17371
17416
  }
@@ -17384,7 +17429,7 @@ var RemoteAudioMeter = class extends Destroyable {
17384
17429
  //#endregion
17385
17430
  //#region src/controllers/RTCStatsMonitor.ts
17386
17431
  var import_cjs$13 = require_cjs();
17387
- const logger$13 = getLogger();
17432
+ const logger$14 = getLogger();
17388
17433
  const DEFAULT_POLLING_INTERVAL_MS = 1e3;
17389
17434
  const DEFAULT_BASELINE_SAMPLES = 10;
17390
17435
  const DEFAULT_NO_AUDIO_PACKET_THRESHOLD_MS = 2e3;
@@ -17474,9 +17519,9 @@ var RTCStatsMonitor = class extends Destroyable {
17474
17519
  const now = Date.now();
17475
17520
  this.lastAudioPacketChangeTime = now;
17476
17521
  this.lastVideoPacketChangeTime = now;
17477
- logger$13.debug("[RTCStatsMonitor] Starting stats monitoring");
17522
+ logger$14.debug("[RTCStatsMonitor] Starting stats monitoring");
17478
17523
  this.subscribeTo((0, import_cjs$13.interval)(this.pollingIntervalMs).pipe((0, import_cjs$13.filter)(() => this.running), (0, import_cjs$13.switchMap)(() => (0, import_cjs$13.from)(this.peerConnection.getStats()).pipe((0, import_cjs$13.catchError)((err) => {
17479
- logger$13.warn("[RTCStatsMonitor] Failed to get stats:", err);
17524
+ logger$14.warn("[RTCStatsMonitor] Failed to get stats:", err);
17480
17525
  return import_cjs$13.EMPTY;
17481
17526
  }))), (0, import_cjs$13.filter)(() => this.running), (0, import_cjs$13.map)((report) => this.extractSample(report))), (sample$1) => this._sample$.next(sample$1));
17482
17527
  this.subscribeTo(this._sample$.pipe((0, import_cjs$13.take)(this.baselineSampleCount), (0, import_cjs$13.toArray)(), (0, import_cjs$13.map)((samples) => ({
@@ -17484,7 +17529,7 @@ var RTCStatsMonitor = class extends Destroyable {
17484
17529
  jitter: samples.reduce((s, b) => s + b.audioJitter, 0) / samples.length,
17485
17530
  ready: true
17486
17531
  }))), (baseline) => {
17487
- logger$13.debug(`[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`);
17532
+ logger$14.debug(`[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`);
17488
17533
  this._baseline$.next(baseline);
17489
17534
  });
17490
17535
  this.subscribeTo(this._sample$.pipe((0, import_cjs$13.scan)((acc, sample$1) => ({
@@ -17521,10 +17566,10 @@ var RTCStatsMonitor = class extends Destroyable {
17521
17566
  stop() {
17522
17567
  if (!this.running) return;
17523
17568
  this.running = false;
17524
- logger$13.debug("[RTCStatsMonitor] Stopping stats monitoring");
17569
+ logger$14.debug("[RTCStatsMonitor] Stopping stats monitoring");
17525
17570
  }
17526
17571
  destroy() {
17527
- logger$13.debug("[RTCStatsMonitor] Destroying RTCStatsMonitor");
17572
+ logger$14.debug("[RTCStatsMonitor] Destroying RTCStatsMonitor");
17528
17573
  this.stop();
17529
17574
  super.destroy();
17530
17575
  }
@@ -17653,7 +17698,7 @@ var RTCStatsMonitor = class extends Destroyable {
17653
17698
  //#endregion
17654
17699
  //#region src/managers/CallRecoveryManager.ts
17655
17700
  var import_cjs$12 = require_cjs();
17656
- const logger$12 = getLogger();
17701
+ const logger$13 = getLogger();
17657
17702
  const DEFAULT_DEBOUNCE_TIME_MS = 2e3;
17658
17703
  const DEFAULT_COOLDOWN_MS = 1e4;
17659
17704
  const DEFAULT_ICE_GRACE_PERIOD_MS = 3e3;
@@ -17750,10 +17795,10 @@ var CallRecoveryManager = class extends Destroyable {
17750
17795
  */
17751
17796
  async requestIceRestart() {
17752
17797
  if (this._recoveryState$.value === "recovering") {
17753
- logger$12.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
17798
+ logger$13.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
17754
17799
  return;
17755
17800
  }
17756
- logger$12.info("CallRecoveryManager: manual ICE restart requested");
17801
+ logger$13.info("CallRecoveryManager: manual ICE restart requested");
17757
17802
  this.transitionTo("recovering");
17758
17803
  await this.executeIceRestart(false);
17759
17804
  this.startCooldown();
@@ -17769,7 +17814,7 @@ var CallRecoveryManager = class extends Destroyable {
17769
17814
  * WebSocket reconnect or call state recovers to 'connected'.
17770
17815
  */
17771
17816
  reset() {
17772
- logger$12.info("CallRecoveryManager: resetting counters");
17817
+ logger$13.info("CallRecoveryManager: resetting counters");
17773
17818
  this._attemptCount = 0;
17774
17819
  this._keyframeBurstCount = 0;
17775
17820
  this._keyframeBurstStart = 0;
@@ -17784,7 +17829,7 @@ var CallRecoveryManager = class extends Destroyable {
17784
17829
  */
17785
17830
  notifyModifyFailed() {
17786
17831
  if (this._recoveryState$.value === "cooldown" || this._recoveryState$.value === "idle") {
17787
- logger$12.info("CallRecoveryManager: verto.modify failed — re-entering recovery");
17832
+ logger$13.info("CallRecoveryManager: verto.modify failed — re-entering recovery");
17788
17833
  this._cooldownUntil = 0;
17789
17834
  this.transitionTo("idle");
17790
17835
  this.pushTrigger({
@@ -17808,7 +17853,7 @@ var CallRecoveryManager = class extends Destroyable {
17808
17853
  reason: `bandwidth ${bitrateKbps}kbps below threshold ${this._config.degradationBitrateThreshold}kbps`,
17809
17854
  timestamp: Date.now()
17810
17855
  });
17811
- logger$12.warn(`CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`);
17856
+ logger$13.warn(`CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`);
17812
17857
  } else if (wasConstrained && bitrateKbps >= this._config.degradationRecoveryThreshold) {
17813
17858
  this._bandwidthConstrained$.next(false);
17814
17859
  this._callbacks.enableVideo();
@@ -17817,7 +17862,7 @@ var CallRecoveryManager = class extends Destroyable {
17817
17862
  reason: `bandwidth ${bitrateKbps}kbps recovered above ${this._config.degradationRecoveryThreshold}kbps`,
17818
17863
  timestamp: Date.now()
17819
17864
  });
17820
- logger$12.info(`CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`);
17865
+ logger$13.info(`CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`);
17821
17866
  }
17822
17867
  }
17823
17868
  /**
@@ -17836,14 +17881,14 @@ var CallRecoveryManager = class extends Destroyable {
17836
17881
  handleWebSocketReconnect() {
17837
17882
  const pcState = this._callbacks.getPeerConnectionState();
17838
17883
  if (pcState === "connected" || pcState === "completed") {
17839
- logger$12.info("CallRecoveryManager: signal-only reconnect — peer connection still alive");
17884
+ logger$13.info("CallRecoveryManager: signal-only reconnect — peer connection still alive");
17840
17885
  this.emitEvent({
17841
17886
  action: "signal_reconnect",
17842
17887
  reason: "WebSocket reconnected, peer connection still connected",
17843
17888
  timestamp: Date.now()
17844
17889
  });
17845
17890
  } else {
17846
- logger$12.info("CallRecoveryManager: full reconnect — peer connection also down");
17891
+ logger$13.info("CallRecoveryManager: full reconnect — peer connection also down");
17847
17892
  this.emitEvent({
17848
17893
  action: "full_reconnect",
17849
17894
  reason: "WebSocket reconnected, peer connection not connected — ICE restart needed",
@@ -17867,7 +17912,7 @@ var CallRecoveryManager = class extends Destroyable {
17867
17912
  }), (0, import_cjs$12.debounceTime)(this._config.debounceTimeMs), (0, import_cjs$12.withLatestFrom)(this._inputs.signalingReady$), (0, import_cjs$12.filter)(([, signalingReady]) => this.passGateChecks(signalingReady)), (0, import_cjs$12.map)(([trigger]) => trigger), (0, import_cjs$12.exhaustMap)((trigger) => this.executeTieredRecovery(trigger)), (0, import_cjs$12.takeUntil)((0, import_cjs$12.merge)(this._destroyed$, this._pipelineStop$))), {
17868
17913
  next: () => {},
17869
17914
  error: (err) => {
17870
- logger$12.error("CallRecoveryManager: pipeline error", err);
17915
+ logger$13.error("CallRecoveryManager: pipeline error", err);
17871
17916
  this.transitionTo("idle");
17872
17917
  }
17873
17918
  });
@@ -17894,27 +17939,27 @@ var CallRecoveryManager = class extends Destroyable {
17894
17939
  reason: `no packet loss for ${this._config.packetLossRecoveryDelaySec}s — restoring video`,
17895
17940
  timestamp: Date.now()
17896
17941
  });
17897
- logger$12.info(`CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`);
17942
+ logger$13.info(`CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`);
17898
17943
  });
17899
17944
  }
17900
17945
  passGateChecks(signalingReady) {
17901
17946
  if (this._callbacks.isNegotiating()) {
17902
- logger$12.debug("CallRecoveryManager: gate blocked — negotiation in progress");
17947
+ logger$13.debug("CallRecoveryManager: gate blocked — negotiation in progress");
17903
17948
  this.transitionTo("idle");
17904
17949
  return false;
17905
17950
  }
17906
17951
  if (!signalingReady) {
17907
- logger$12.debug("CallRecoveryManager: gate blocked — signaling not ready");
17952
+ logger$13.debug("CallRecoveryManager: gate blocked — signaling not ready");
17908
17953
  this.transitionTo("idle");
17909
17954
  return false;
17910
17955
  }
17911
17956
  if (!this._callbacks.isCallConnected()) {
17912
- logger$12.debug("CallRecoveryManager: gate blocked — call not connected");
17957
+ logger$13.debug("CallRecoveryManager: gate blocked — call not connected");
17913
17958
  this.transitionTo("idle");
17914
17959
  return false;
17915
17960
  }
17916
17961
  if (this.isCooldownActive()) {
17917
- logger$12.debug("CallRecoveryManager: gate blocked — cooldown active");
17962
+ logger$13.debug("CallRecoveryManager: gate blocked — cooldown active");
17918
17963
  this.transitionTo("cooldown");
17919
17964
  return false;
17920
17965
  }
@@ -17925,9 +17970,9 @@ var CallRecoveryManager = class extends Destroyable {
17925
17970
  }
17926
17971
  executeTieredRecovery(trigger) {
17927
17972
  this.transitionTo("recovering");
17928
- logger$12.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
17973
+ logger$13.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
17929
17974
  return (0, import_cjs$12.from)(this.runTiers(trigger)).pipe((0, import_cjs$12.tap)(() => this.startCooldown()), (0, import_cjs$12.catchError)((err) => {
17930
- logger$12.error("CallRecoveryManager: tiered recovery failed", err);
17975
+ logger$13.error("CallRecoveryManager: tiered recovery failed", err);
17931
17976
  this.startCooldown();
17932
17977
  return import_cjs$12.EMPTY;
17933
17978
  }));
@@ -17935,7 +17980,7 @@ var CallRecoveryManager = class extends Destroyable {
17935
17980
  async runTiers(trigger) {
17936
17981
  this.executeKeyframe(trigger.detail);
17937
17982
  if (trigger.issueType && DEGRADATION_ONLY_ISSUES.has(trigger.issueType)) {
17938
- logger$12.debug(`CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`);
17983
+ logger$13.debug(`CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`);
17939
17984
  return;
17940
17985
  }
17941
17986
  if (this._attemptCount < this._config.maxAttempts) {
@@ -17952,13 +17997,13 @@ var CallRecoveryManager = class extends Destroyable {
17952
17997
  maxAttempts: this._config.maxAttempts,
17953
17998
  timestamp: Date.now()
17954
17999
  });
17955
- logger$12.warn("CallRecoveryManager: max recovery attempts reached");
18000
+ logger$13.warn("CallRecoveryManager: max recovery attempts reached");
17956
18001
  }
17957
18002
  }
17958
18003
  executeKeyframe(reason) {
17959
18004
  const now = Date.now();
17960
18005
  if (now < this._keyframeCooldownUntil) {
17961
- logger$12.debug("CallRecoveryManager: keyframe request skipped — cooldown active");
18006
+ logger$13.debug("CallRecoveryManager: keyframe request skipped — cooldown active");
17962
18007
  return;
17963
18008
  }
17964
18009
  if (now - this._keyframeBurstStart > this._config.keyframeBurstWindowMs) {
@@ -17967,7 +18012,7 @@ var CallRecoveryManager = class extends Destroyable {
17967
18012
  }
17968
18013
  if (this._keyframeBurstCount >= this._config.keyframeMaxBurst) {
17969
18014
  this._keyframeCooldownUntil = now + this._config.keyframeCooldownMs;
17970
- logger$12.debug(`CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`);
18015
+ logger$13.debug(`CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`);
17971
18016
  return;
17972
18017
  }
17973
18018
  this._keyframeBurstCount += 1;
@@ -17977,12 +18022,12 @@ var CallRecoveryManager = class extends Destroyable {
17977
18022
  reason,
17978
18023
  timestamp: now
17979
18024
  });
17980
- logger$12.debug(`CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`);
18025
+ logger$13.debug(`CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`);
17981
18026
  }
17982
18027
  async executeIceRestart(relayOnly) {
17983
18028
  this._attemptCount += 1;
17984
18029
  const tier = relayOnly ? "Tier 3 (relay-only)" : "Tier 2 (standard)";
17985
- logger$12.info(`CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`);
18030
+ logger$13.info(`CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`);
17986
18031
  this.emitEvent({
17987
18032
  action: "reinvite_started",
17988
18033
  reason: `${tier} ICE restart`,
@@ -17999,7 +18044,7 @@ var CallRecoveryManager = class extends Destroyable {
17999
18044
  maxAttempts: this._config.maxAttempts,
18000
18045
  timestamp: Date.now()
18001
18046
  });
18002
- logger$12.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);
18047
+ logger$13.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);
18003
18048
  this._attemptCount = 0;
18004
18049
  return true;
18005
18050
  }
@@ -18010,7 +18055,7 @@ var CallRecoveryManager = class extends Destroyable {
18010
18055
  maxAttempts: this._config.maxAttempts,
18011
18056
  timestamp: Date.now()
18012
18057
  });
18013
- logger$12.warn(`CallRecoveryManager: ${tier} ICE restart failed`);
18058
+ logger$13.warn(`CallRecoveryManager: ${tier} ICE restart failed`);
18014
18059
  return false;
18015
18060
  } catch {
18016
18061
  this.emitEvent({
@@ -18020,7 +18065,7 @@ var CallRecoveryManager = class extends Destroyable {
18020
18065
  maxAttempts: this._config.maxAttempts,
18021
18066
  timestamp: Date.now()
18022
18067
  });
18023
- logger$12.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);
18068
+ logger$13.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);
18024
18069
  return false;
18025
18070
  }
18026
18071
  }
@@ -18041,7 +18086,7 @@ var CallRecoveryManager = class extends Destroyable {
18041
18086
  transitionTo(state) {
18042
18087
  const prev = this._recoveryState$.value;
18043
18088
  if (prev !== state) {
18044
- logger$12.debug(`CallRecoveryManager: state ${prev} -> ${state}`);
18089
+ logger$13.debug(`CallRecoveryManager: state ${prev} -> ${state}`);
18045
18090
  this._recoveryState$.next(state);
18046
18091
  }
18047
18092
  }
@@ -18144,7 +18189,13 @@ function mosToQualityLevel(mos) {
18144
18189
  //#endregion
18145
18190
  //#region src/core/entities/Call.ts
18146
18191
  var import_cjs$11 = require_cjs();
18147
- const logger$11 = getLogger();
18192
+ const logger$12 = getLogger();
18193
+ /**
18194
+ * Verto method for setting member layout positions. Its gateway DTO requires a
18195
+ * `targets` array whose entries are `{ target, position }` (NOT bare targets),
18196
+ * so {@link WebRTCCall.buildMethodParams} special-cases it. See issue #19400.
18197
+ */
18198
+ const POSITION_SET_METHOD = "call.member.position.set";
18148
18199
  /**
18149
18200
  * Ratio between the critical and warning RTT spike multipliers.
18150
18201
  * Warning threshold = baseline * warningMultiplier (default 3x)
@@ -18162,7 +18213,7 @@ const fromDestinationParams = (destination) => {
18162
18213
  });
18163
18214
  return params;
18164
18215
  } catch (error) {
18165
- logger$11.warn(`Failed to parse destination URI: ${destination}`, error);
18216
+ logger$12.warn(`Failed to parse destination URI: ${destination}`, error);
18166
18217
  return {};
18167
18218
  }
18168
18219
  };
@@ -18352,7 +18403,7 @@ var WebRTCCall = class extends Destroyable {
18352
18403
  * @throws {JSONRPCError} If the RPC call returns an error.
18353
18404
  */
18354
18405
  async executeMethod(target, method, args) {
18355
- const params = this.buildMethodParams(target, args);
18406
+ const params = this.buildMethodParams(target, args, method);
18356
18407
  const request = buildRPCRequest({
18357
18408
  method,
18358
18409
  params
@@ -18362,16 +18413,20 @@ var WebRTCCall = class extends Destroyable {
18362
18413
  if (isJSONRPCErrorResponse(response)) throw new JSONRPCError(parseInt(response.result?.code ?? "0"), `Error response from method ${method}: ${response.result?.code} ${response.result?.message}`, void 0, void 0, request.id);
18363
18414
  return response;
18364
18415
  } catch (error) {
18365
- logger$11.error(`[Call] Error executing method ${method} with params`, params, error);
18416
+ logger$12.error(`[Call] Error executing method ${method} with params`, params, error);
18366
18417
  throw error;
18367
18418
  }
18368
18419
  }
18369
- buildMethodParams(target, args) {
18420
+ buildMethodParams(target, args, method) {
18370
18421
  const self = {
18371
18422
  node_id: this.nodeId ?? "",
18372
18423
  call_id: this.id,
18373
18424
  member_id: this.vertoManager.selfId ?? ""
18374
18425
  };
18426
+ if (method === POSITION_SET_METHOD) return {
18427
+ ...args,
18428
+ self
18429
+ };
18375
18430
  if (typeof target === "object") return {
18376
18431
  ...args,
18377
18432
  self,
@@ -18565,9 +18620,9 @@ var WebRTCCall = class extends Destroyable {
18565
18620
  */
18566
18621
  initResilienceSubsystems() {
18567
18622
  const pc = this.rtcPeerConnection;
18568
- logger$11.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
18623
+ logger$12.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
18569
18624
  if (!pc) {
18570
- logger$11.warn("[Call] No peer connection available, skipping resilience init");
18625
+ logger$12.warn("[Call] No peer connection available, skipping resilience init");
18571
18626
  return;
18572
18627
  }
18573
18628
  try {
@@ -18602,14 +18657,14 @@ var WebRTCCall = class extends Destroyable {
18602
18657
  disableVideo: () => {
18603
18658
  try {
18604
18659
  this.vertoManager.muteMainVideoInputDevice();
18605
- logger$11.debug("[Call] Recovery manager disabled video");
18660
+ logger$12.debug("[Call] Recovery manager disabled video");
18606
18661
  } catch {
18607
- logger$11.debug("[Call] Recovery manager failed to disable video");
18662
+ logger$12.debug("[Call] Recovery manager failed to disable video");
18608
18663
  }
18609
18664
  },
18610
18665
  enableVideo: () => {
18611
18666
  this.vertoManager.unmuteMainVideoInputDevice().catch(() => {
18612
- logger$11.debug("[Call] Recovery manager failed to enable video");
18667
+ logger$12.debug("[Call] Recovery manager failed to enable video");
18613
18668
  });
18614
18669
  },
18615
18670
  isNegotiating: () => this.vertoManager.mainPeerConnection.isNegotiating,
@@ -18659,7 +18714,7 @@ var WebRTCCall = class extends Destroyable {
18659
18714
  this.subscribeTo(this._recoveryManager.recoveryEvent$, (event) => {
18660
18715
  this._recoveryEvent$.next(event);
18661
18716
  if (event.action === "max_attempts_reached") {
18662
- logger$11.warn("[Call] All recovery attempts exhausted, terminating call");
18717
+ logger$12.warn("[Call] All recovery attempts exhausted, terminating call");
18663
18718
  this.emitError({
18664
18719
  kind: "network",
18665
18720
  fatal: true,
@@ -18679,13 +18734,13 @@ var WebRTCCall = class extends Destroyable {
18679
18734
  else if (event.type === "online") this._recoveryManager?.handleWebSocketReconnect();
18680
18735
  });
18681
18736
  this.subscribeTo(this.clientSession.authenticated$.pipe((0, import_cjs$11.skip)(1), (0, import_cjs$11.filter)(Boolean)), () => {
18682
- logger$11.debug("[Call] WebSocket reconnected — notifying recovery manager");
18737
+ logger$12.debug("[Call] WebSocket reconnected — notifying recovery manager");
18683
18738
  this._recoveryManager?.handleWebSocketReconnect();
18684
18739
  });
18685
18740
  this._statsMonitor.start();
18686
- logger$11.debug("[Call] Resilience subsystems initialized for call", this.id);
18741
+ logger$12.debug("[Call] Resilience subsystems initialized for call", this.id);
18687
18742
  } catch (error) {
18688
- logger$11.warn("[Call] Failed to initialize resilience subsystems:", error);
18743
+ logger$12.warn("[Call] Failed to initialize resilience subsystems:", error);
18689
18744
  }
18690
18745
  }
18691
18746
  /**
@@ -18768,19 +18823,19 @@ var WebRTCCall = class extends Destroyable {
18768
18823
  }
18769
18824
  isCallSessionEvent(event) {
18770
18825
  try {
18771
- logger$11.debug("[Call] Checking if event is for this call session:", event);
18826
+ logger$12.debug("[Call] Checking if event is for this call session:", event);
18772
18827
  const callId = getValueFrom(event, "params.params.callID") ?? getValueFrom(event, "params.call_id");
18773
18828
  const roomSessionId = getValueFrom(event, "params.room_session_id");
18774
- logger$11.debug(`[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`);
18829
+ logger$12.debug(`[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`);
18775
18830
  return callId === this.id || !!callId && this.callEventsManager.isCallIdValid(callId) || !!roomSessionId && this.callEventsManager.isRoomSessionIdValid(roomSessionId);
18776
18831
  } catch (error) {
18777
- logger$11.error("[Call] Error checking if event is for this call session:", error);
18832
+ logger$12.error("[Call] Error checking if event is for this call session:", error);
18778
18833
  return false;
18779
18834
  }
18780
18835
  }
18781
18836
  get callSessionEvents$() {
18782
18837
  return this.cachedObservable("callSessionEvents$", () => this.clientSession.signalingEvent$.pipe((0, import_cjs$11.filter)((event) => this.isCallSessionEvent(event)), (0, import_cjs$11.tap)((event) => {
18783
- logger$11.debug("[Call] Received call session event:", event);
18838
+ logger$12.debug("[Call] Received call session event:", event);
18784
18839
  }), (0, import_cjs$11.takeUntil)(this.destroyed$), (0, import_cjs$11.share)()));
18785
18840
  }
18786
18841
  /** Observable of call-updated events. */
@@ -18850,16 +18905,16 @@ var WebRTCCall = class extends Destroyable {
18850
18905
  this._customSubscriptions.set(eventType, filtered$);
18851
18906
  }, (error) => {
18852
18907
  this._customSubscriptions.delete(eventType);
18853
- logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed, not caching:`, error);
18908
+ logger$12.warn(`[Call] verto.subscribe for '${eventType}' failed, not caching:`, error);
18854
18909
  });
18855
18910
  this._customSubscriptions.set(eventType, filtered$);
18856
18911
  return filtered$;
18857
18912
  }
18858
18913
  get webrtcMessages$() {
18859
- return this.cachedObservable("webrtcMessages$", () => this.callSessionEvents$.pipe(filterAs(isWebrtcMessageMetadata, "params"), (0, import_cjs$11.tap)((event) => logger$11.debug("[Call] Event is a WebRTC message event:", event)), (0, import_cjs$11.takeUntil)(this.destroyed$), (0, import_cjs$11.share)()));
18914
+ return this.cachedObservable("webrtcMessages$", () => this.callSessionEvents$.pipe(filterAs(isWebrtcMessageMetadata, "params"), (0, import_cjs$11.tap)((event) => logger$12.debug("[Call] Event is a WebRTC message event:", event)), (0, import_cjs$11.takeUntil)(this.destroyed$), (0, import_cjs$11.share)()));
18860
18915
  }
18861
18916
  get callEvent$() {
18862
- return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(filterAs(isSignalwireCallMetadata, "params"), (0, import_cjs$11.tap)((event) => logger$11.debug("[Call] Event is a call event:", event)), (0, import_cjs$11.takeUntil)(this.destroyed$), (0, import_cjs$11.share)()));
18917
+ return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(filterAs(isSignalwireCallMetadata, "params"), (0, import_cjs$11.tap)((event) => logger$12.debug("[Call] Event is a call event:", event)), (0, import_cjs$11.takeUntil)(this.destroyed$), (0, import_cjs$11.share)()));
18863
18918
  }
18864
18919
  get layoutEvent$() {
18865
18920
  return this.cachedObservable("layoutEvent$", () => this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params")));
@@ -18937,10 +18992,21 @@ var WebRTCCall = class extends Destroyable {
18937
18992
  return this.deferEmission(this._answered$.asObservable());
18938
18993
  }
18939
18994
  /**
18940
- * Sets the call layout and participant positions.
18995
+ * Sets the call layout and, optionally, individual participant positions.
18996
+ *
18997
+ * The gateway `call.layout.set` DTO has **no** `positions` member, so when
18998
+ * `positions` is provided this method issues a `call.member.position.set`
18999
+ * request per member (via {@link Participant.setPosition}, which keys each
19000
+ * position by that member's own call context) alongside `call.layout.set`
19001
+ * (issue #19400, Flag #6).
19002
+ *
19003
+ * **These operations are NOT atomic.** The layout is applied first, then each
19004
+ * member position sequentially, so members may briefly flash into their
19005
+ * default slots before being moved to the requested positions.
18941
19006
  *
18942
19007
  * @param layout - Layout name (must be one of {@link layouts}).
18943
- * @param positions - Map of member IDs to {@link VideoPosition} values.
19008
+ * @param positions - Optional map of member IDs to {@link VideoPosition} values.
19009
+ * When omitted or empty, only the layout is changed.
18944
19010
  * @throws {InvalidParams} If the layout is not in the available {@link layouts}.
18945
19011
  *
18946
19012
  * @example
@@ -18953,10 +19019,17 @@ var WebRTCCall = class extends Destroyable {
18953
19019
  async setLayout(layout, positions) {
18954
19020
  if (!this.layouts.includes(layout)) throw new InvalidParams(`Layout ${layout} is not available in the current call layouts: ${this.layouts.join(", ")}`);
18955
19021
  const selfId = await (0, import_cjs$11.firstValueFrom)(this.selfId$.pipe((0, import_cjs$11.filter)((id) => id !== null)));
18956
- await this.executeMethod(selfId, "call.layout.set", {
18957
- layout,
18958
- positions
18959
- });
19022
+ await this.executeMethod(selfId, "call.layout.set", { layout });
19023
+ const positionEntries = Object.entries(positions ?? {});
19024
+ if (positionEntries.length === 0) return;
19025
+ for (const [memberId, position] of positionEntries) {
19026
+ const participant = this.participants.find((p) => p.id === memberId);
19027
+ if (!participant) {
19028
+ logger$12.warn(`[Call] setLayout: member ${memberId} not found in participants; skipping position ${position}`);
19029
+ continue;
19030
+ }
19031
+ await participant.setPosition(position);
19032
+ }
18960
19033
  }
18961
19034
  /**
18962
19035
  * Transfers the call to another destination.
@@ -18988,7 +19061,7 @@ var WebRTCCall = class extends Destroyable {
18988
19061
  setLocalMicrophoneGain(value) {
18989
19062
  const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18990
19063
  if (!pipeline) {
18991
- logger$11.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
19064
+ logger$12.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
18992
19065
  return;
18993
19066
  }
18994
19067
  const percent = Math.max(0, Math.min(200, value));
@@ -19033,7 +19106,7 @@ var WebRTCCall = class extends Destroyable {
19033
19106
  enablePushToTalk() {
19034
19107
  const pipeline = this.vertoManager.ensureLocalAudioPipeline();
19035
19108
  if (!pipeline) {
19036
- logger$11.warn("[Call] enablePushToTalk: audio pipeline unavailable");
19109
+ logger$12.warn("[Call] enablePushToTalk: audio pipeline unavailable");
19037
19110
  return;
19038
19111
  }
19039
19112
  pipeline.setPTTActive(false);
@@ -19196,7 +19269,7 @@ var CallFactory = class {
19196
19269
  //#endregion
19197
19270
  //#region src/behaviors/Collection.ts
19198
19271
  var import_cjs$10 = require_cjs();
19199
- const logger$10 = getLogger();
19272
+ const logger$11 = getLogger();
19200
19273
  var Fetcher = class {
19201
19274
  constructor(endpoint, params, http) {
19202
19275
  this.endpoint = endpoint;
@@ -19220,7 +19293,7 @@ var Fetcher = class {
19220
19293
  this.hasMore = !!this.nextUrl;
19221
19294
  return result.data.filter(this.filter).map(this.mapper);
19222
19295
  }
19223
- logger$10.error("Failed to fetch entity");
19296
+ logger$11.error("Failed to fetch entity");
19224
19297
  return [];
19225
19298
  }
19226
19299
  async id(v) {
@@ -19296,7 +19369,7 @@ var EntityCollection = class extends Destroyable {
19296
19369
  this._hasMore$.next(this.fetchController.hasMore ?? false);
19297
19370
  this._loading$.next(false);
19298
19371
  } catch (error) {
19299
- logger$10.error(`Failed to fetch initial collection data`, error);
19372
+ logger$11.error(`Failed to fetch initial collection data`, error);
19300
19373
  this._hasMore$.next(this.fetchController.hasMore ?? false);
19301
19374
  this._loading$.next(false);
19302
19375
  this.onError?.(new CollectionFetchError("fetchMore", error));
@@ -19310,7 +19383,7 @@ var EntityCollection = class extends Destroyable {
19310
19383
  if (data) this.upsertData(data);
19311
19384
  return data;
19312
19385
  } catch (error) {
19313
- logger$10.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
19386
+ logger$11.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
19314
19387
  this._loading$.next(false);
19315
19388
  this.onError?.(new CollectionFetchError(`tryFetch(${String(key)})`, error));
19316
19389
  }
@@ -19559,13 +19632,13 @@ var Address = class extends Destroyable {
19559
19632
  //#endregion
19560
19633
  //#region src/core/utils.ts
19561
19634
  var import_cjs$8 = require_cjs();
19562
- const logger$9 = getLogger();
19635
+ const logger$10 = getLogger();
19563
19636
  const isRPCConnectResult = (e) => {
19564
- logger$9.debug("isRPCConnectResult check:", e);
19637
+ logger$10.debug("isRPCConnectResult check:", e);
19565
19638
  if (!e || typeof e !== "object") return false;
19566
19639
  const result = e;
19567
19640
  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";
19568
- logger$9.debug("isRPCConnectResult check result:", is);
19641
+ logger$10.debug("isRPCConnectResult check result:", is);
19569
19642
  return is;
19570
19643
  };
19571
19644
  var PendingRPC = class PendingRPC {
@@ -19574,7 +19647,7 @@ var PendingRPC = class PendingRPC {
19574
19647
  }
19575
19648
  constructor(request, responses$, options) {
19576
19649
  this.id = v4_default();
19577
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`);
19650
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`);
19578
19651
  this.request = request;
19579
19652
  const timeoutMs = options?.timeoutMs ?? PendingRPC.defaultTimeoutMs;
19580
19653
  const signal = options?.signal;
@@ -19600,22 +19673,22 @@ var PendingRPC = class PendingRPC {
19600
19673
  isSettled = true;
19601
19674
  if (response.error) {
19602
19675
  const rpcError = new JSONRPCError(response.error.code, response.error.message, response.error.data, void 0, request.id);
19603
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
19676
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
19604
19677
  reject(rpcError);
19605
19678
  } else {
19606
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
19679
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
19607
19680
  resolve(response);
19608
19681
  }
19609
19682
  subscription.unsubscribe();
19610
19683
  },
19611
19684
  error: (error) => {
19612
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`, error);
19685
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`, error);
19613
19686
  isSettled = true;
19614
19687
  reject(error);
19615
19688
  subscription.unsubscribe();
19616
19689
  },
19617
19690
  complete: () => {
19618
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
19691
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
19619
19692
  if (!isSettled) reject(new RPCTimeoutError(request.id, timeoutMs));
19620
19693
  subscription.unsubscribe();
19621
19694
  }
@@ -19636,7 +19709,7 @@ var PendingRPC = class PendingRPC {
19636
19709
  //#endregion
19637
19710
  //#region src/managers/ClientSessionManager.ts
19638
19711
  var import_cjs$7 = require_cjs();
19639
- const logger$8 = getLogger();
19712
+ const logger$9 = getLogger();
19640
19713
  const getAddressSearchURI = (options) => {
19641
19714
  const to = options.to?.split("?")[0];
19642
19715
  const from$9 = options.from?.startsWith("subscriber://") ? options.from.replace("subscriber://", "") : options.from;
@@ -19734,7 +19807,7 @@ var ClientSessionManager = class extends Destroyable {
19734
19807
  try {
19735
19808
  return await this.transport.execute(request, options);
19736
19809
  } catch (error) {
19737
- logger$8.debug("[Session] Execute Error", error);
19810
+ logger$9.debug("[Session] Execute Error", error);
19738
19811
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
19739
19812
  throw error;
19740
19813
  }
@@ -19748,13 +19821,13 @@ var ClientSessionManager = class extends Destroyable {
19748
19821
  return true;
19749
19822
  }
19750
19823
  setupMessageHandlers() {
19751
- logger$8.debug("[Session] Setting up message handlers");
19824
+ logger$9.debug("[Session] Setting up message handlers");
19752
19825
  this.subscribeTo(this.authStateEvent$, async (authStateEvent) => {
19753
- logger$8.debug("[Session] Authorization state event received:", authStateEvent);
19826
+ logger$9.debug("[Session] Authorization state event received:", authStateEvent);
19754
19827
  try {
19755
19828
  await this.updateAuthorizationStateInStorage(authStateEvent.authorization_state);
19756
19829
  } catch (error) {
19757
- logger$8.error("[Session] Failed to handle authorization state update:", error);
19830
+ logger$9.error("[Session] Failed to handle authorization state update:", error);
19758
19831
  this._errors$.next(new AuthStateHandlerError(error));
19759
19832
  }
19760
19833
  });
@@ -19762,29 +19835,29 @@ var ClientSessionManager = class extends Destroyable {
19762
19835
  if (this._authState$.value.kind === "authenticated") this._authState$.next({ kind: "unauthenticated" });
19763
19836
  });
19764
19837
  this.subscribeTo(this.transport.connectionStatus$.pipe((0, import_cjs$7.filter)((status) => status === "connected"), (0, import_cjs$7.exhaustMap)(() => {
19765
- logger$8.debug("[Session] Connection established, initiating authentication");
19838
+ logger$9.debug("[Session] Connection established, initiating authentication");
19766
19839
  return (0, import_cjs$7.from)(this.authenticate()).pipe((0, import_cjs$7.catchError)((error) => {
19767
19840
  this.handleAuthenticationError(error).catch((err) => {
19768
- logger$8.error("[Session] Error handling authentication failure:", err);
19841
+ logger$9.error("[Session] Error handling authentication failure:", err);
19769
19842
  });
19770
19843
  return import_cjs$7.EMPTY;
19771
19844
  }));
19772
19845
  })), void 0);
19773
19846
  this.subscribeTo(this.vertoInvite$, async (invite) => {
19774
- logger$8.debug("[Session] Verto invite received:", invite);
19847
+ logger$9.debug("[Session] Verto invite received:", invite);
19775
19848
  try {
19776
19849
  await this.createInboundCall(invite);
19777
19850
  } catch (error) {
19778
- logger$8.error("[Session] Error handling Verto invite:", error);
19851
+ logger$9.error("[Session] Error handling Verto invite:", error);
19779
19852
  this._errors$.next(new VertoInviteHandlerError(error));
19780
19853
  }
19781
19854
  });
19782
19855
  this.subscribeTo(this.vertoAttach$, async (attach) => {
19783
- logger$8.debug("[Session] Verto attach received:", attach);
19856
+ logger$9.debug("[Session] Verto attach received:", attach);
19784
19857
  try {
19785
19858
  await this.handleVertoAttach(attach);
19786
19859
  } catch (error) {
19787
- logger$8.error("[Session] Error handling Verto attach:", error);
19860
+ logger$9.error("[Session] Error handling Verto attach:", error);
19788
19861
  this._errors$.next(new VertoAttachHandlerError(error));
19789
19862
  }
19790
19863
  });
@@ -19794,36 +19867,36 @@ var ClientSessionManager = class extends Destroyable {
19794
19867
  const storedState = await this.storage.getItem(this.authorizationStateKey);
19795
19868
  this.authorizationState$.next(storedState ?? void 0);
19796
19869
  } catch (error) {
19797
- logger$8.error("Failed to retrieve authorization state from storage:", error);
19870
+ logger$9.error("Failed to retrieve authorization state from storage:", error);
19798
19871
  this.authorizationState$.next(void 0);
19799
19872
  }
19800
19873
  }
19801
19874
  async updateAuthorizationStateInStorage(authorizationState) {
19802
19875
  if (!authorizationState) {
19803
- logger$8.debug("[Session] Removing authorization state from storage");
19876
+ logger$9.debug("[Session] Removing authorization state from storage");
19804
19877
  try {
19805
19878
  await this.storage.removeItem(this.authorizationStateKey);
19806
19879
  this.authorizationState$.next(void 0);
19807
19880
  } catch (error) {
19808
- logger$8.error("Failed to remove authorization state from storage:", error);
19881
+ logger$9.error("Failed to remove authorization state from storage:", error);
19809
19882
  throw error;
19810
19883
  }
19811
19884
  return;
19812
19885
  }
19813
19886
  try {
19814
- logger$8.debug("[Session] Updating authorization state in storage");
19887
+ logger$9.debug("[Session] Updating authorization state in storage");
19815
19888
  await this.storage.setItem(this.authorizationStateKey, authorizationState);
19816
19889
  this.authorizationState$.next(authorizationState);
19817
19890
  } catch (error) {
19818
- logger$8.error("Failed to retrieve authorization state from storage:", error);
19891
+ logger$9.error("Failed to retrieve authorization state from storage:", error);
19819
19892
  throw error;
19820
19893
  }
19821
19894
  }
19822
19895
  get authStateEvent$() {
19823
19896
  return this.cachedObservable("authStateEvent$", () => this.signalingEvent$.pipe((0, import_cjs$7.tap)((msg) => {
19824
- logger$8.debug("[Session] Received incoming message:", msg);
19897
+ logger$9.debug("[Session] Received incoming message:", msg);
19825
19898
  }), filterAs(isSignalwireAuthorizationStateMetadata, "params"), (0, import_cjs$7.tap)((event) => {
19826
- logger$8.debug("[Session] Authorization state event received:", event.authorization_state);
19899
+ logger$9.debug("[Session] Authorization state event received:", event.authorization_state);
19827
19900
  })));
19828
19901
  }
19829
19902
  get signalingEvent$() {
@@ -19861,42 +19934,72 @@ var ClientSessionManager = class extends Destroyable {
19861
19934
  await (0, import_cjs$7.firstValueFrom)(this.authenticated$.pipe((0, import_cjs$7.takeUntil)(this.destroyed$), (0, import_cjs$7.filter)(Boolean), (0, import_cjs$7.take)(1), (0, import_cjs$7.timeout)({ first: 15e3 })));
19862
19935
  }
19863
19936
  async handleAuthenticationError(error) {
19864
- logger$8.error("Authentication error:", error);
19937
+ logger$9.error("Authentication error:", error);
19865
19938
  const isRecoverableAuthError = error instanceof JSONRPCError && (error.code === RPC_ERROR_REQUESTER_VALIDATION_FAILED || error.code === RPC_ERROR_INVALID_PARAMS || error.code === RPC_ERROR_AUTHENTICATION_FAILED);
19866
19939
  const hasStoredState = await (0, import_cjs$7.firstValueFrom)(this.authorizationState$.pipe((0, import_cjs$7.take)(1))) !== void 0;
19867
19940
  if (isRecoverableAuthError && hasStoredState) {
19868
- logger$8.debug("[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh");
19941
+ logger$9.debug("[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh");
19869
19942
  try {
19870
19943
  await this.cleanupStoredConnectionParams();
19871
19944
  } catch (cleanupError) {
19872
- logger$8.error("Failed to cleanup stored connection params:", cleanupError);
19945
+ logger$9.error("Failed to cleanup stored connection params:", cleanupError);
19873
19946
  } finally {
19874
19947
  this.transport.reconnect();
19875
19948
  }
19876
19949
  } else this._errors$.next(error);
19877
19950
  }
19951
+ /**
19952
+ * Clear the resume state (authorization_state + protocol) only.
19953
+ *
19954
+ * This is the stale-auth-state recovery helper used by handleAuthError:
19955
+ * the server rejected a reconnect, so the resume state is discarded and a
19956
+ * fresh connect follows. Attach records are deliberately preserved — the
19957
+ * session lives on through the reconnect and reattachCalls() needs the
19958
+ * stored call references afterwards. Do NOT add detachAll() here.
19959
+ *
19960
+ * For public teardown (disconnect/destroy), use {@link teardownSessionState}
19961
+ * instead, which clears the attach records as well.
19962
+ */
19878
19963
  async cleanupStoredConnectionParams() {
19879
19964
  await this.transport.setProtocol(void 0);
19880
19965
  await this.updateAuthorizationStateInStorage(void 0);
19881
19966
  this._authorization$.next(void 0);
19882
19967
  }
19968
+ /**
19969
+ * Public-teardown helper for disconnect()/destroy(). Clears the resume
19970
+ * state (authorization_state + protocol) AND the attach records as one
19971
+ * atomic unit.
19972
+ *
19973
+ * The two stores are coupled: the backend only honors attach records
19974
+ * within the session identified by the resume state, so ending the
19975
+ * session must clear both. Clearing one without the other strands records
19976
+ * no future session can honor (disconnect) or revives a session the
19977
+ * developer explicitly ended (destroy).
19978
+ *
19979
+ * Distinct from {@link cleanupStoredConnectionParams}, which keeps the
19980
+ * attach records for the stale-auth-state recovery path.
19981
+ */
19982
+ async teardownSessionState() {
19983
+ await this.cleanupStoredConnectionParams();
19984
+ await this.attachManager.detachAll();
19985
+ }
19883
19986
  async updateAuthState(authorization_state) {
19884
19987
  try {
19885
19988
  await this.storage.setItem(this.authorizationStateKey, authorization_state);
19886
19989
  } catch (error) {
19887
- logger$8.error("Failed to update authorization state in storage:", error);
19990
+ logger$9.error("Failed to update authorization state in storage:", error);
19888
19991
  this._errors$.next(new AuthStateHandlerError(error));
19889
19992
  }
19890
19993
  }
19891
19994
  async reauthenticate(token, dpopToken, options) {
19892
- logger$8.debug("[Session] Re-authenticating session");
19995
+ logger$9.debug("[Session] Re-authenticating session");
19893
19996
  try {
19894
19997
  let resolvedDpopToken = dpopToken;
19895
19998
  if (!resolvedDpopToken && this.dpopManager?.initialized) try {
19896
19999
  resolvedDpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
19897
20000
  } catch (error) {
19898
20001
  if (this.clientBound) throw error;
19899
- logger$8.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
20002
+ logger$9.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
19900
20003
  }
19901
20004
  const request = RPCReauthenticate({
19902
20005
  project: this._authorization$.value?.project_id ?? "",
@@ -19904,24 +20007,24 @@ var ClientSessionManager = class extends Destroyable {
19904
20007
  ...resolvedDpopToken ? { dpop_token: resolvedDpopToken } : {}
19905
20008
  });
19906
20009
  await (0, import_cjs$7.lastValueFrom)((0, import_cjs$7.from)(this.transport.execute(request)).pipe(throwOnRPCError(), (0, import_cjs$7.take)(1), (0, import_cjs$7.catchError)((err) => {
19907
- logger$8.error("[Session] Re-authentication RPC failed:", err);
20010
+ logger$9.error("[Session] Re-authentication RPC failed:", err);
19908
20011
  throw err;
19909
20012
  })));
19910
20013
  if (options?.clientBound) this._wasClientBound = true;
19911
- logger$8.debug("[Session] Re-authentication successful, updating stored auth state");
20014
+ logger$9.debug("[Session] Re-authentication successful, updating stored auth state");
19912
20015
  } catch (error) {
19913
- logger$8.error("[Session] Re-authentication failed:", error);
20016
+ logger$9.error("[Session] Re-authentication failed:", error);
19914
20017
  this._errors$.next(new AuthStateHandlerError(error));
19915
20018
  throw error;
19916
20019
  }
19917
20020
  }
19918
20021
  async authenticate() {
19919
- logger$8.debug("[Session] Starting authentication process");
20022
+ logger$9.debug("[Session] Starting authentication process");
19920
20023
  const persistedParams = await (0, import_cjs$7.firstValueFrom)((0, import_cjs$7.combineLatest)({
19921
20024
  protocol: this.transport.protocol$,
19922
20025
  authorization_state: this.authorizationState$
19923
20026
  }).pipe((0, import_cjs$7.take)(1)));
19924
- logger$8.debug("[Session] Persisted params:\n", {
20027
+ logger$9.debug("[Session] Persisted params:\n", {
19925
20028
  protocol: persistedParams.protocol,
19926
20029
  authStateLength: persistedParams.authorization_state?.length
19927
20030
  });
@@ -19929,16 +20032,16 @@ var ClientSessionManager = class extends Destroyable {
19929
20032
  const storedToken = this.getCredential().token;
19930
20033
  const isReconnect = hasReconnectState && storedToken;
19931
20034
  let dpopToken;
19932
- if (isReconnect) logger$8.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
20035
+ if (isReconnect) logger$9.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
19933
20036
  else if (this.onBeforeReconnect && this.clientBound) {
19934
- logger$8.debug("[Session] Refreshing credentials before fresh connect");
20037
+ logger$9.debug("[Session] Refreshing credentials before fresh connect");
19935
20038
  await this.onBeforeReconnect();
19936
20039
  }
19937
20040
  if ((!isReconnect || this.clientBound) && this.dpopManager?.initialized) try {
19938
20041
  dpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.connect" });
19939
20042
  } catch (error) {
19940
20043
  if (this.clientBound) throw error;
19941
- logger$8.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
20044
+ logger$9.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
19942
20045
  }
19943
20046
  const rpcConnectRequest = RPCConnect({
19944
20047
  authentication: isReconnect ? { jwt_token: storedToken } : this.authentication,
@@ -19955,12 +20058,12 @@ var ClientSessionManager = class extends Destroyable {
19955
20058
  } : {}
19956
20059
  });
19957
20060
  const response = await (0, import_cjs$7.lastValueFrom)((0, import_cjs$7.from)(this.transport.execute(rpcConnectRequest)).pipe(throwOnRPCError(), (0, import_cjs$7.map)((res) => res.result), (0, import_cjs$7.filter)(isRPCConnectResult), (0, import_cjs$7.tap)(() => {
19958
- logger$8.debug("[Session] Response passed filter, processing authentication result");
20061
+ logger$9.debug("[Session] Response passed filter, processing authentication result");
19959
20062
  }), (0, import_cjs$7.take)(1), (0, import_cjs$7.catchError)((err) => {
19960
- logger$8.error("[Session] Authentication RPC failed:", err);
20063
+ logger$9.error("[Session] Authentication RPC failed:", err);
19961
20064
  throw err;
19962
20065
  })));
19963
- logger$8.debug("[Session] Processing authentication result:", {
20066
+ logger$9.debug("[Session] Processing authentication result:", {
19964
20067
  hasProtocol: !!response.protocol,
19965
20068
  hasAuthorization: !!response.authorization,
19966
20069
  hasIceServers: !!response.ice_servers
@@ -19969,12 +20072,12 @@ var ClientSessionManager = class extends Destroyable {
19969
20072
  this._authorization$.next(response.authorization);
19970
20073
  this._iceServers$.next(response.ice_servers ?? []);
19971
20074
  this._authState$.next({ kind: "authenticated" });
19972
- logger$8.debug("[Session] Authentication completed successfully");
20075
+ logger$9.debug("[Session] Authentication completed successfully");
19973
20076
  }
19974
20077
  async disconnect() {
19975
20078
  this.transport.disconnect();
19976
20079
  this._authState$.next({ kind: "unauthenticated" });
19977
- await this.cleanupStoredConnectionParams();
20080
+ await this.teardownSessionState();
19978
20081
  }
19979
20082
  async createInboundCall(invite) {
19980
20083
  const callSession = await this.createCall({
@@ -20005,11 +20108,11 @@ var ClientSessionManager = class extends Destroyable {
20005
20108
  async handleVertoAttach(attach) {
20006
20109
  const { callID } = attach;
20007
20110
  if (callID in this._calls$.value) {
20008
- logger$8.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
20111
+ logger$9.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
20009
20112
  return;
20010
20113
  }
20011
20114
  const storedOptions = await this.attachManager.consumePendingAttachment(callID);
20012
- logger$8.debug(`[Session] Creating reattached call for callID: ${callID}`);
20115
+ logger$9.debug(`[Session] Creating reattached call for callID: ${callID}`);
20013
20116
  const callSession = await this.createCall({
20014
20117
  nodeId: attach.node_id,
20015
20118
  callId: callID,
@@ -20040,7 +20143,7 @@ var ClientSessionManager = class extends Destroyable {
20040
20143
  });
20041
20144
  return callSession;
20042
20145
  } catch (error) {
20043
- logger$8.error("[Session] Error creating outbound call:", error);
20146
+ logger$9.error("[Session] Error creating outbound call:", error);
20044
20147
  callSession?.destroy();
20045
20148
  const callError = new CallCreateError(error instanceof import_cjs$7.TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
20046
20149
  this._errors$.next(callError);
@@ -20058,7 +20161,7 @@ var ClientSessionManager = class extends Destroyable {
20058
20161
  address = this._directory.get(addressId);
20059
20162
  if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
20060
20163
  } catch {
20061
- logger$8.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
20164
+ logger$9.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
20062
20165
  }
20063
20166
  const callSession = this.callFactory.createCall(address, { ...options });
20064
20167
  this.subscribeTo(callSession.status$.pipe((0, import_cjs$7.filter)((status) => status === "destroyed"), (0, import_cjs$7.take)(1)), () => {
@@ -20067,7 +20170,7 @@ var ClientSessionManager = class extends Destroyable {
20067
20170
  });
20068
20171
  return callSession;
20069
20172
  } catch (error) {
20070
- logger$8.error("[Session] Error creating call session:", error);
20173
+ logger$9.error("[Session] Error creating call session:", error);
20071
20174
  throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
20072
20175
  }
20073
20176
  }
@@ -20116,7 +20219,7 @@ const isString = (obj) => typeof obj === "string";
20116
20219
  //#endregion
20117
20220
  //#region src/managers/ConversationsManager.ts
20118
20221
  var import_cjs$6 = require_cjs();
20119
- const logger$7 = getLogger();
20222
+ const logger$8 = getLogger();
20120
20223
  var ConversationMessagesFetcher = class extends Fetcher {
20121
20224
  constructor(groupId, http) {
20122
20225
  super(`/api/fabric/conversations/${groupId}/messages`, "page_size=100", http);
@@ -20156,13 +20259,13 @@ var ConversationsManager = class {
20156
20259
  }
20157
20260
  throw new ConversationError("Join Failed - Unexpected response");
20158
20261
  } catch (error) {
20159
- logger$7.error("[ConversationsManager] Failed to join conversation:", error);
20262
+ logger$8.error("[ConversationsManager] Failed to join conversation:", error);
20160
20263
  throw error;
20161
20264
  }
20162
20265
  }
20163
20266
  async getConversationMessageCollection(addressId) {
20164
20267
  const groupId = this.groupIds.get(addressId) ?? await this.join(addressId);
20165
- return Promise.resolve(new ConversationMessageCollection(groupId, this.clientSession.signalingEvent$.pipe(filterAs(isConversationMessageMetadata, "params"), (0, import_cjs$6.tap)((event) => logger$7.debug("[ConversationsManager ] Conversation Event:", event)), (0, import_cjs$6.map)((params) => ({ ...params }))), this.http, this.onError));
20268
+ return Promise.resolve(new ConversationMessageCollection(groupId, this.clientSession.signalingEvent$.pipe(filterAs(isConversationMessageMetadata, "params"), (0, import_cjs$6.tap)((event) => logger$8.debug("[ConversationsManager ] Conversation Event:", event)), (0, import_cjs$6.map)((params) => ({ ...params }))), this.http, this.onError));
20166
20269
  }
20167
20270
  async sendText(text, destinationAddressId) {
20168
20271
  const groupId = this.groupIds.get(destinationAddressId) ?? await this.join(destinationAddressId);
@@ -20179,7 +20282,7 @@ var ConversationsManager = class {
20179
20282
  })).ok) return;
20180
20283
  throw new ConversationError("Send Text Failed - Unexpected response");
20181
20284
  } catch (error) {
20182
- logger$7.error("[ConversationsManager] Failed to send text message:", error);
20285
+ logger$8.error("[ConversationsManager] Failed to send text message:", error);
20183
20286
  throw error;
20184
20287
  }
20185
20288
  }
@@ -20188,7 +20291,7 @@ var ConversationsManager = class {
20188
20291
  //#endregion
20189
20292
  //#region src/managers/DeviceTokenManager.ts
20190
20293
  var import_cjs$5 = require_cjs();
20191
- const logger$6 = getLogger();
20294
+ const logger$7 = getLogger();
20192
20295
  /**
20193
20296
  * Resolves the token expiry timestamp (epoch seconds) using a 3-tier priority chain:
20194
20297
  * 1. `data.expires_at` — server-provided absolute timestamp
@@ -20198,7 +20301,7 @@ const logger$6 = getLogger();
20198
20301
  function resolveExpiresAt(data) {
20199
20302
  if (data.expires_at) return data.expires_at;
20200
20303
  if (data.expires_in) return Math.floor(Date.now() / 1e3) + data.expires_in;
20201
- logger$6.warn("[DeviceToken] Could not determine token expiry, using default");
20304
+ logger$7.warn("[DeviceToken] Could not determine token expiry, using default");
20202
20305
  return Math.floor(Date.now() / 1e3) + DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
20203
20306
  }
20204
20307
  /**
@@ -20231,11 +20334,12 @@ var DeviceTokenManager = class extends Destroyable {
20231
20334
  this.getCredential = getCredential;
20232
20335
  this._currentToken$ = this.createBehaviorSubject(null);
20233
20336
  this._refreshInProgress = false;
20337
+ this._paused = false;
20234
20338
  this._effectiveExpireIn = DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
20235
20339
  this.subscribeTo(this._currentToken$.pipe((0, import_cjs$5.filter)(Boolean), (0, import_cjs$5.switchMap)((tokenData) => {
20236
20340
  const expiresAt = resolveExpiresAt(tokenData);
20237
20341
  const refreshIn = Math.max(expiresAt * 1e3 - Date.now() - DEVICE_TOKEN_REFRESH_BUFFER_MS, 1e3);
20238
- logger$6.debug(`[DeviceToken] Scheduling Client Bound SAT refresh in ${refreshIn}ms`);
20342
+ logger$7.debug(`[DeviceToken] Scheduling Client Bound SAT refresh in ${refreshIn}ms`);
20239
20343
  return (0, import_cjs$5.timer)(refreshIn);
20240
20344
  })), () => {
20241
20345
  this.executeRefresh();
@@ -20249,7 +20353,12 @@ var DeviceTokenManager = class extends Destroyable {
20249
20353
  * Activates the Client Bound SAT flow when the user's token has
20250
20354
  * `sat:refresh` scope.
20251
20355
  *
20252
- * Steps:
20356
+ * Returns an {@link ActivationResult} indicating whether the manager
20357
+ * took ownership of refresh duties. The caller must use the `activated`
20358
+ * boolean to decide whether to keep its own refresh path armed — when
20359
+ * `activated` is `false`, the caller is responsible for refresh.
20360
+ *
20361
+ * Steps on success:
20253
20362
  * 1. Check user's `sat_claims` for `sat:refresh` scope
20254
20363
  * 2. Call `/api/fabric/subscriber/devices/token` with a DPoP proof
20255
20364
  * 3. Reauthenticate the session with the Client Bound SAT + DPoP proof
@@ -20258,32 +20367,51 @@ var DeviceTokenManager = class extends Destroyable {
20258
20367
  async activate(user, session, updateCredential) {
20259
20368
  const { satClaims } = user;
20260
20369
  if (!satClaims?.scope?.includes(SAT_REFRESH_SCOPE)) {
20261
- logger$6.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
20262
- return;
20370
+ logger$7.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
20371
+ return {
20372
+ activated: false,
20373
+ reason: "no-scope"
20374
+ };
20263
20375
  }
20264
20376
  this._session = session;
20265
20377
  this._updateCredential = updateCredential;
20266
20378
  try {
20379
+ const cached = this._currentToken$.value;
20380
+ if (cached && this.isTokenFresh(cached)) {
20381
+ logger$7.debug("[DeviceToken] Reusing cached Client Bound SAT — skipping /devices/token");
20382
+ const rpcProof$1 = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
20383
+ await session.reauthenticate(cached.token, rpcProof$1, { clientBound: true });
20384
+ updateCredential({ token: cached.token });
20385
+ return { activated: true };
20386
+ }
20267
20387
  const tokenData = await this.obtainToken();
20268
20388
  if (!tokenData.expires_at && !tokenData.expires_in && satClaims.expires_at) tokenData.expires_at = satClaims.expires_at;
20269
20389
  this._effectiveExpireIn = resolveExpireIn(tokenData);
20270
20390
  const rpcProof = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
20271
20391
  await session.reauthenticate(tokenData.token, rpcProof, { clientBound: true });
20272
20392
  updateCredential({ token: tokenData.token });
20273
- logger$6.info("[DeviceToken] Client Bound SAT activated successfully");
20393
+ logger$7.info("[DeviceToken] Client Bound SAT activated successfully");
20274
20394
  this._currentToken$.next(tokenData);
20395
+ return { activated: true };
20275
20396
  } catch (error) {
20276
- logger$6.error("[DeviceToken] Failed to activate Client Bound SAT:", error);
20397
+ logger$7.error("[DeviceToken] Failed to activate Client Bound SAT:", error);
20277
20398
  this.errorHandler(new DPoPInitError(error, "Failed to activate Client Bound SAT"));
20278
- const credential = this.getCredential();
20279
- const expiresAt = satClaims.expires_at ?? (credential.expiry_at ? credential.expiry_at / 1e3 : Date.now() / 1e3 + DEVICE_TOKEN_DEFAULT_EXPIRE_IN / 1e3);
20280
- this._currentToken$.next({
20281
- token: credential.token ?? "",
20282
- expires_at: expiresAt
20283
- });
20399
+ return {
20400
+ activated: false,
20401
+ reason: "endpoint-failed"
20402
+ };
20284
20403
  }
20285
20404
  }
20286
20405
  /**
20406
+ * Returns true when the cached token has enough headroom before expiry to
20407
+ * be safely reused on reactivation. The headroom matches the refresh
20408
+ * buffer, so a token within the refresh window is treated as stale (the
20409
+ * reactive pipeline is about to refresh it anyway).
20410
+ */
20411
+ isTokenFresh(token) {
20412
+ return resolveExpiresAt(token) * 1e3 - Date.now() > DEVICE_TOKEN_REFRESH_BUFFER_MS;
20413
+ }
20414
+ /**
20287
20415
  * Obtains a Client Bound SAT from `/api/fabric/subscriber/devices/token`.
20288
20416
  * Returns the full {@link DeviceTokenResponse} including expiry metadata.
20289
20417
  */
@@ -20313,7 +20441,7 @@ var DeviceTokenManager = class extends Destroyable {
20313
20441
  * handled by the reactive pipeline).
20314
20442
  */
20315
20443
  async refreshToken(session, currentToken, updateCredential) {
20316
- logger$6.debug("[DeviceToken] Refreshing Client Bound SAT");
20444
+ logger$7.debug("[DeviceToken] Refreshing Client Bound SAT");
20317
20445
  const dpopProof = await this.dpopManager.createHttpProof({
20318
20446
  method: "POST",
20319
20447
  uri: DEVICE_REFRESH_ENDPOINT,
@@ -20335,7 +20463,7 @@ var DeviceTokenManager = class extends Destroyable {
20335
20463
  const rpcProof = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
20336
20464
  await session.reauthenticate(data.token, rpcProof);
20337
20465
  updateCredential({ token: data.token });
20338
- logger$6.info("[DeviceToken] Client Bound SAT refreshed successfully");
20466
+ logger$7.info("[DeviceToken] Client Bound SAT refreshed successfully");
20339
20467
  return data;
20340
20468
  }
20341
20469
  /**
@@ -20344,18 +20472,22 @@ var DeviceTokenManager = class extends Destroyable {
20344
20472
  * On all retries exhausted, emits to `errorHandler`.
20345
20473
  */
20346
20474
  async executeRefresh() {
20475
+ if (this._paused) {
20476
+ logger$7.debug("[DeviceToken] Manager paused, skipping refresh");
20477
+ return;
20478
+ }
20347
20479
  if (this._refreshInProgress) {
20348
- logger$6.debug("[DeviceToken] Refresh already in progress, skipping");
20480
+ logger$7.debug("[DeviceToken] Refresh already in progress, skipping");
20349
20481
  return;
20350
20482
  }
20351
20483
  const session = this._session;
20352
20484
  const updateCredential = this._updateCredential;
20353
20485
  if (!session || !updateCredential) {
20354
- logger$6.warn("[DeviceToken] Cannot refresh: session or updateCredential not set");
20486
+ logger$7.warn("[DeviceToken] Cannot refresh: session or updateCredential not set");
20355
20487
  return;
20356
20488
  }
20357
20489
  if (!session.authenticated) {
20358
- logger$6.debug("[DeviceToken] Session not authenticated, deferring refresh");
20490
+ logger$7.debug("[DeviceToken] Session not authenticated, deferring refresh");
20359
20491
  return;
20360
20492
  }
20361
20493
  this._refreshInProgress = true;
@@ -20365,7 +20497,7 @@ var DeviceTokenManager = class extends Destroyable {
20365
20497
  const newTokenData = await this.retryRefresh(session, currentToken, updateCredential);
20366
20498
  this._currentToken$.next(newTokenData);
20367
20499
  } catch (error) {
20368
- logger$6.error("[DeviceToken] Automatic Client Bound SAT refresh failed:", error);
20500
+ logger$7.error("[DeviceToken] Automatic Client Bound SAT refresh failed:", error);
20369
20501
  this.errorHandler(error instanceof TokenRefreshError ? error : new TokenRefreshError("Automatic token refresh failed", error));
20370
20502
  } finally {
20371
20503
  this._refreshInProgress = false;
@@ -20383,18 +20515,215 @@ var DeviceTokenManager = class extends Destroyable {
20383
20515
  lastError = error;
20384
20516
  if (attempt < DEVICE_TOKEN_REFRESH_MAX_RETRIES - 1) {
20385
20517
  const delay$1 = DEVICE_TOKEN_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt);
20386
- logger$6.warn(`[DeviceToken] Refresh attempt ${attempt + 1} failed, retrying in ${delay$1}ms`);
20518
+ logger$7.warn(`[DeviceToken] Refresh attempt ${attempt + 1} failed, retrying in ${delay$1}ms`);
20387
20519
  await new Promise((resolve) => setTimeout(resolve, delay$1));
20388
20520
  }
20389
20521
  }
20390
20522
  throw lastError instanceof Error ? lastError : new TokenRefreshError("All refresh retries exhausted", lastError);
20391
20523
  }
20524
+ /**
20525
+ * Stops the reactive refresh pipeline from firing. Use when the underlying
20526
+ * session is being torn down (e.g., during {@link SignalWire.disconnect})
20527
+ * so a scheduled refresh cannot fire against a destroyed session.
20528
+ *
20529
+ * The manager's state (cached token, effective TTL, subscriptions) is
20530
+ * preserved — call {@link resume} to re-enable firing after reconnect.
20531
+ */
20532
+ pause() {
20533
+ this._paused = true;
20534
+ }
20535
+ /** Re-enables the reactive refresh pipeline after a previous {@link pause}. */
20536
+ resume() {
20537
+ this._paused = false;
20538
+ }
20392
20539
  /** Cleans up the manager, cancelling the reactive pipeline and all subscriptions. */
20393
20540
  destroy() {
20394
20541
  super.destroy();
20395
20542
  }
20396
20543
  };
20397
20544
 
20545
+ //#endregion
20546
+ //#region src/managers/CredentialRefreshCoordinator.ts
20547
+ const logger$6 = getLogger();
20548
+ const defaultDeviceTokenManagerFactory = (dpopManager, http, errorHandler, getCredential) => new DeviceTokenManager(dpopManager, http, errorHandler, getCredential);
20549
+ /**
20550
+ * Centralizes credential-refresh ownership across the two competing
20551
+ * mechanisms — developer-provided `CredentialProvider.refresh()` and the
20552
+ * Client Bound SAT path via {@link DeviceTokenManager}.
20553
+ *
20554
+ * Maintains the invariant: **at most one refresh mechanism is armed at a
20555
+ * time, and at least one is armed whenever the current credential has an
20556
+ * `expiry_at`**.
20557
+ *
20558
+ * Replaces the previous design where refresh state was distributed across
20559
+ * `SignalWire` (timer field, scheduler method, activation helper) and
20560
+ * `DeviceTokenManager` (reactive pipeline). Centralizing the invariant in
20561
+ * one component eliminates the bug class that produced issue #19074.
20562
+ *
20563
+ * Race-safety:
20564
+ * - `_activating` flag prevents overlapping `activate()` calls from racing.
20565
+ * - `_activationGeneration` lets late resolutions detect they've been
20566
+ * preempted by a newer activation (e.g., reconnect during in-flight
20567
+ * `obtainToken`).
20568
+ */
20569
+ var CredentialRefreshCoordinator = class extends Destroyable {
20570
+ constructor(dpopManager, deps) {
20571
+ super();
20572
+ this.deps = deps;
20573
+ this._activating = false;
20574
+ this._activationGeneration = 0;
20575
+ if (dpopManager?.initialized) this._deviceTokenManager = (deps.deviceTokenManagerFactory ?? defaultDeviceTokenManagerFactory)(dpopManager, deps.http, (error) => deps.notifier.onError(error), () => deps.store.read());
20576
+ }
20577
+ /** True when the Client Bound SAT path is available (DPoP initialized). */
20578
+ get clientBoundSATAvailable() {
20579
+ return this._deviceTokenManager !== void 0;
20580
+ }
20581
+ /** True when the developer-provided refresh timer is currently armed. */
20582
+ get developerRefreshArmed() {
20583
+ return this._developerTimerId !== void 0;
20584
+ }
20585
+ /**
20586
+ * Arms the developer-provided refresh timer to fire shortly before
20587
+ * `expiresAt`. Replaces any previously scheduled developer refresh.
20588
+ *
20589
+ * Idempotent — multiple calls just reschedule. On retry exhaustion,
20590
+ * invokes `deps.onRefreshExhausted` so the orchestrator can disconnect.
20591
+ */
20592
+ scheduleDeveloperRefresh(provider, expiresAt, attempt = 0) {
20593
+ if (this._developerTimerId !== void 0) clearTimeout(this._developerTimerId);
20594
+ const refreshInterval = attempt === 0 ? Math.max(expiresAt - Date.now() - CREDENTIAL_REFRESH_BUFFER_MS, 1e3) : Math.min(CREDENTIAL_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt) * (.5 + Math.random() * .5), CREDENTIAL_REFRESH_MAX_DELAY_MS);
20595
+ this._developerTimerId = setTimeout(async () => {
20596
+ try {
20597
+ if (!provider.refresh) throw new InvalidCredentialsError("Credential provider does not support refresh");
20598
+ const newCredentials = await provider.refresh();
20599
+ this.deps.store.write(newCredentials);
20600
+ this.deps.store.persist(newCredentials);
20601
+ logger$6.info("[Coordinator] Credentials refreshed successfully.");
20602
+ if (newCredentials.expiry_at) this.scheduleDeveloperRefresh(provider, newCredentials.expiry_at, 0);
20603
+ } catch (error) {
20604
+ const nextAttempt = attempt + 1;
20605
+ logger$6.error(`[Coordinator] Credential refresh failed (attempt ${nextAttempt}/${CREDENTIAL_REFRESH_MAX_RETRIES}):`, error);
20606
+ this.deps.notifier.onError(error instanceof Error ? error : new Error(String(error), { cause: error }));
20607
+ if (nextAttempt < CREDENTIAL_REFRESH_MAX_RETRIES) this.scheduleDeveloperRefresh(provider, expiresAt, nextAttempt);
20608
+ else {
20609
+ logger$6.error("[Coordinator] Credential refresh exhausted all retries. Disconnecting.");
20610
+ this.deps.notifier.onError(new TokenRefreshError("Credential refresh failed after max retries"));
20611
+ this.deps.notifier.onRefreshExhausted();
20612
+ }
20613
+ }
20614
+ }, refreshInterval);
20615
+ }
20616
+ /**
20617
+ * Cancels any scheduled developer-provided refresh. Idempotent.
20618
+ *
20619
+ * @internal Used by the coordinator's own activation flow. External
20620
+ * callers should use {@link suspend} for disconnect-time quiescence —
20621
+ * `suspend()` also pauses the internal Client Bound SAT pipeline.
20622
+ */
20623
+ cancelDeveloperRefresh() {
20624
+ if (this._developerTimerId !== void 0) {
20625
+ clearTimeout(this._developerTimerId);
20626
+ this._developerTimerId = void 0;
20627
+ }
20628
+ }
20629
+ /**
20630
+ * Suspends both refresh paths — cancels the developer timer and pauses
20631
+ * the internal reactive pipeline. Use when the underlying session is
20632
+ * being torn down (e.g., {@link SignalWire.disconnect}). The next
20633
+ * {@link activate} call re-enables the internal pipeline.
20634
+ *
20635
+ * The internal manager's cached token survives — see
20636
+ * {@link DeviceTokenManager.pause} — so a subsequent reconnect can
20637
+ * skip the `/devices/token` exchange entirely.
20638
+ */
20639
+ suspend() {
20640
+ this.cancelDeveloperRefresh();
20641
+ this._deviceTokenManager?.pause();
20642
+ }
20643
+ /**
20644
+ * Asks the Client Bound SAT path to take over refresh. If it accepts,
20645
+ * the developer-provided timer (if any) is cancelled. If it declines, the
20646
+ * developer timer remains armed and a `credential_refresh_fallback`
20647
+ * warning is emitted.
20648
+ *
20649
+ * **Idempotent** — re-entrant calls during an in-flight activation are
20650
+ * dropped. Use `_activationGeneration` to detect stale resolutions
20651
+ * (e.g., a reconnect-triggered activate() that races with an earlier one).
20652
+ */
20653
+ async activate(user, session) {
20654
+ if (this._activating) {
20655
+ logger$6.debug("[Coordinator] activate() in flight; ignoring re-entrant call");
20656
+ return;
20657
+ }
20658
+ if (!this._deviceTokenManager) return;
20659
+ this._deviceTokenManager.resume();
20660
+ const generation = ++this._activationGeneration;
20661
+ this._activating = true;
20662
+ try {
20663
+ const result = await this.withActivationTimeout(this._deviceTokenManager.activate(user, session, (cred) => this.deps.store.merge(cred)));
20664
+ if (generation !== this._activationGeneration) {
20665
+ logger$6.debug("[Coordinator] activate() result discarded (preempted by newer activation)");
20666
+ return;
20667
+ }
20668
+ if (result.activated) {
20669
+ this.cancelDeveloperRefresh();
20670
+ logger$6.debug("[Coordinator] Developer refresh disabled — Client Bound SAT owns refresh");
20671
+ return;
20672
+ }
20673
+ logger$6.warn(`[SignalWire] [SW-REFRESH-FALLBACK] Client Bound SAT declined (reason=${result.reason}); using developer-provided refresh handler.`);
20674
+ this.deps.notifier.onWarning({
20675
+ code: "credential_refresh_fallback",
20676
+ source: "CredentialProvider",
20677
+ reason: result.reason,
20678
+ message: `Client Bound SAT activation declined (${result.reason}); using developer-provided refresh.`
20679
+ });
20680
+ } finally {
20681
+ this._activating = false;
20682
+ }
20683
+ }
20684
+ destroy() {
20685
+ this.cancelDeveloperRefresh();
20686
+ this._deviceTokenManager?.destroy();
20687
+ this._deviceTokenManager = void 0;
20688
+ super.destroy();
20689
+ }
20690
+ /**
20691
+ * Races the manager's `activate()` against a hard timeout. A wedged HTTP
20692
+ * layer (e.g., proxy issues) could otherwise hang the activation
20693
+ * indefinitely, leaving the session with no refresh mechanism while the
20694
+ * `_activating` guard blocks subsequent retries.
20695
+ *
20696
+ * On timeout, the manager is paused immediately. The inner `activate()`
20697
+ * may still complete in the background and emit to `_currentToken$`,
20698
+ * which would normally arm the reactive refresh pipeline; pausing
20699
+ * prevents that pipeline from firing while the developer refresh path
20700
+ * is the active mechanism — preserving the "at most one mechanism
20701
+ * armed" invariant. The next `activate()` call resumes the manager.
20702
+ */
20703
+ async withActivationTimeout(inner) {
20704
+ return new Promise((resolve) => {
20705
+ const timer$4 = setTimeout(() => {
20706
+ this._deviceTokenManager?.pause();
20707
+ resolve({
20708
+ activated: false,
20709
+ reason: "activation-timeout"
20710
+ });
20711
+ }, CREDENTIAL_ACTIVATE_TIMEOUT_MS);
20712
+ inner.then((result) => {
20713
+ clearTimeout(timer$4);
20714
+ resolve(result);
20715
+ }, (error) => {
20716
+ clearTimeout(timer$4);
20717
+ this.deps.notifier.onError(error instanceof Error ? error : new Error(String(error)));
20718
+ resolve({
20719
+ activated: false,
20720
+ reason: "endpoint-failed"
20721
+ });
20722
+ });
20723
+ });
20724
+ }
20725
+ };
20726
+
20398
20727
  //#endregion
20399
20728
  //#region src/managers/DiagnosticsCollector.ts
20400
20729
  const logger$5 = getLogger();
@@ -20915,6 +21244,10 @@ var TransportManager = class extends Destroyable {
20915
21244
  if (!isSignalwireRequest(message)) return true;
20916
21245
  const eventChannel = message.params.event_channel;
20917
21246
  if (!eventChannel) return true;
21247
+ if (message.params.event_type.startsWith("conversation.")) {
21248
+ logger$2.debug(`[Transport] Received conversation event: ${message.params.event_type} (event_channel: ${eventChannel})`);
21249
+ return true;
21250
+ }
20918
21251
  const currentProtocol = this._currentProtocol;
20919
21252
  if (!currentProtocol) return true;
20920
21253
  if (!eventChannel.includes(currentProtocol)) {
@@ -21108,6 +21441,7 @@ var SignalWire = class extends Destroyable {
21108
21441
  this._isConnected$ = this.createBehaviorSubject(false);
21109
21442
  this._isRegistered$ = this.createBehaviorSubject(false);
21110
21443
  this._errors$ = this.createReplaySubject(1);
21444
+ this._warnings$ = this.createReplaySubject(10);
21111
21445
  this._options = {};
21112
21446
  this._deps = new DependencyContainer();
21113
21447
  this._credentialProvider = credentialProvider;
@@ -21167,6 +21501,27 @@ var SignalWire = class extends Destroyable {
21167
21501
  */
21168
21502
  async resolveCredentials() {
21169
21503
  const fingerprint = await this.initDPoP();
21504
+ this._refreshCoordinator = new CredentialRefreshCoordinator(this._dpopManager, {
21505
+ http: this._deps.http,
21506
+ notifier: {
21507
+ onError: (error) => this._errors$.next(error),
21508
+ onWarning: (warning) => this._warnings$.next(warning),
21509
+ onRefreshExhausted: () => void this.disconnect()
21510
+ },
21511
+ store: {
21512
+ read: () => this._deps.credential,
21513
+ write: (credential) => {
21514
+ this._deps.credential = credential;
21515
+ },
21516
+ merge: (partial) => {
21517
+ this._deps.credential = {
21518
+ ...this._deps.credential,
21519
+ ...partial
21520
+ };
21521
+ },
21522
+ persist: (credential) => this.persistCredential(credential)
21523
+ }
21524
+ });
21170
21525
  if (this._credentialProvider) return this.validateCredentials(this._credentialProvider, void 0, fingerprint);
21171
21526
  for (const scope of this._deps.persistSession ? ["local", "session"] : ["session"]) try {
21172
21527
  const cached = await this._deps.storage.getItem("sw:cached_credential", scope);
@@ -21196,7 +21551,16 @@ var SignalWire = class extends Destroyable {
21196
21551
  logger$1.error("[SignalWire] Provided credentials have expired.");
21197
21552
  throw new InvalidCredentialsError("Provided credentials have expired.");
21198
21553
  }
21199
- if (_credentials.expiry_at && credentialProvider?.refresh) this.scheduleCredentialRefresh(credentialProvider, _credentials.expiry_at);
21554
+ if (_credentials.expiry_at && credentialProvider?.refresh) this._refreshCoordinator?.scheduleDeveloperRefresh(credentialProvider, _credentials.expiry_at);
21555
+ else if (_credentials.expiry_at && !credentialProvider?.refresh) {
21556
+ logger$1.warn(`[SignalWire] [SW-NO-REFRESH-HANDLER] Credential has expiry_at=${_credentials.expiry_at} but no refresh handler. Session will terminate at expiry unless the SAT has 'sat:refresh' scope.`);
21557
+ this._warnings$.next({
21558
+ code: "credential_no_refresh_handler",
21559
+ source: "CredentialProvider",
21560
+ message: "Credential has expiry_at but no refresh handler. Session will terminate at expiry unless the SAT carries 'sat:refresh' scope.",
21561
+ expiresAt: _credentials.expiry_at
21562
+ });
21563
+ }
21200
21564
  this._deps.credential = _credentials;
21201
21565
  this.persistCredential(_credentials);
21202
21566
  if (this.isConnected && this._clientSession.authenticated && _credentials.token) try {
@@ -21207,35 +21571,6 @@ var SignalWire = class extends Destroyable {
21207
21571
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21208
21572
  }
21209
21573
  }
21210
- /**
21211
- * Schedules credential refresh with exponential backoff retry on failure.
21212
- * On success, resets attempt counter and schedules the next refresh.
21213
- * After exhausting retries, emits TokenRefreshError and disconnects.
21214
- */
21215
- scheduleCredentialRefresh(credentialProvider, expiresAt, attempt = 0) {
21216
- if (this._refreshTimerId !== void 0) clearTimeout(this._refreshTimerId);
21217
- const refreshInterval = attempt === 0 ? Math.max(expiresAt - Date.now() - CREDENTIAL_REFRESH_BUFFER_MS, 1e3) : Math.min(CREDENTIAL_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt) * (.5 + Math.random() * .5), CREDENTIAL_REFRESH_MAX_DELAY_MS);
21218
- this._refreshTimerId = setTimeout(async () => {
21219
- try {
21220
- if (!credentialProvider.refresh) throw new InvalidCredentialsError("Credential provider does not support refresh");
21221
- const newCredentials = await credentialProvider.refresh();
21222
- this._deps.credential = newCredentials;
21223
- this.persistCredential(newCredentials);
21224
- logger$1.info("[SignalWire] Credentials refreshed successfully.");
21225
- if (newCredentials.expiry_at) this.scheduleCredentialRefresh(credentialProvider, newCredentials.expiry_at, 0);
21226
- } catch (error) {
21227
- const nextAttempt = attempt + 1;
21228
- logger$1.error(`[SignalWire] Credential refresh failed (attempt ${nextAttempt}/${CREDENTIAL_REFRESH_MAX_RETRIES}):`, error);
21229
- this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21230
- if (nextAttempt < CREDENTIAL_REFRESH_MAX_RETRIES) this.scheduleCredentialRefresh(credentialProvider, expiresAt, nextAttempt);
21231
- else {
21232
- logger$1.error("[SignalWire] Credential refresh exhausted all retries. Disconnecting.");
21233
- this._errors$.next(new TokenRefreshError("Credential refresh failed after max retries"));
21234
- this.disconnect();
21235
- }
21236
- }
21237
- }, refreshInterval);
21238
- }
21239
21574
  /** Persist credential to localStorage when persistSession is enabled. */
21240
21575
  persistCredential(credential) {
21241
21576
  if (!credential.token) return;
@@ -21329,6 +21664,7 @@ var SignalWire = class extends Destroyable {
21329
21664
  logger$1.debug("[SignalWire] Credential expired, refreshing before reconnect");
21330
21665
  const newCredentials = await this._credentialProvider.authenticate(fingerprint ? { fingerprint } : void 0);
21331
21666
  this._deps.credential = newCredentials;
21667
+ if (newCredentials.expiry_at && this._credentialProvider.refresh) this._refreshCoordinator?.scheduleDeveloperRefresh(this._credentialProvider, newCredentials.expiry_at);
21332
21668
  logger$1.debug("[SignalWire] Credential refreshed successfully for reconnect");
21333
21669
  } catch (error) {
21334
21670
  logger$1.error("[SignalWire] Failed to refresh credentials for reconnect:", error);
@@ -21340,33 +21676,12 @@ var SignalWire = class extends Destroyable {
21340
21676
  this._errors$.next(error);
21341
21677
  });
21342
21678
  await this._clientSession.connect();
21343
- if (this._dpopManager?.initialized) {
21344
- if (this._refreshTimerId) {
21345
- clearTimeout(this._refreshTimerId);
21346
- this._refreshTimerId = void 0;
21347
- logger$1.debug("[SignalWire] Developer refresh disabled — Client Bound SAT activation starting");
21348
- }
21349
- this._deviceTokenManager = new DeviceTokenManager(this._dpopManager, this._deps.http, (error) => this._errors$.next(error), () => this._deps.credential);
21350
- await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
21351
- this._deps.credential = {
21352
- ...this._deps.credential,
21353
- ...cred
21354
- };
21355
- });
21356
- }
21679
+ await this._refreshCoordinator?.activate(this._deps.user, this._clientSession);
21357
21680
  this.subscribeTo(this._clientSession.authenticated$.pipe((0, import_cjs$1.skip)(1), (0, import_cjs$1.filter)(Boolean)), async () => {
21358
21681
  try {
21359
- if (this._deviceTokenManager) {
21360
- await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
21361
- this._deps.credential = {
21362
- ...this._deps.credential,
21363
- ...cred
21364
- };
21365
- });
21366
- logger$1.debug("[SignalWire] Client Bound SAT re-activated after reconnect");
21367
- }
21682
+ await this._refreshCoordinator?.activate(this._deps.user, this._clientSession);
21368
21683
  } catch (error) {
21369
- logger$1.error("[SignalWire] Client Bound SAT re-activation failed (non-fatal):", error);
21684
+ logger$1.error("[SignalWire] Refresh re-arm after reconnect failed (non-fatal):", error);
21370
21685
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21371
21686
  }
21372
21687
  try {
@@ -21452,6 +21767,19 @@ var SignalWire = class extends Destroyable {
21452
21767
  get errors$() {
21453
21768
  return this.deferEmission(this._errors$.asObservable());
21454
21769
  }
21770
+ /**
21771
+ * Observable stream of non-fatal SDK warnings.
21772
+ *
21773
+ * Subscribe to detect SDK behaviors that affect session liveness or developer-facing
21774
+ * contracts but do not warrant disconnection — e.g., a fallback from Client Bound SAT
21775
+ * refresh to the developer-provided `refresh()` because the SAT lacks `sat:refresh`
21776
+ * scope. Discriminated by `code`.
21777
+ *
21778
+ * Independent from {@link errors$}: existing error consumers are not notified.
21779
+ */
21780
+ get warnings$() {
21781
+ return this.deferEmission(this._warnings$.asObservable());
21782
+ }
21455
21783
  /** Platform WebRTC capabilities detected at construction time. */
21456
21784
  get platformCapabilities() {
21457
21785
  this._platformCapabilities ??= detectPlatformCapabilities(this._options.webRTCApiProvider);
@@ -21520,7 +21848,7 @@ var SignalWire = class extends Destroyable {
21520
21848
  logger$1.warn("[SignalWire] Failed to initialize VisibilityController:", error);
21521
21849
  }
21522
21850
  try {
21523
- this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "3.30.0" });
21851
+ this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.1" });
21524
21852
  } catch (error) {
21525
21853
  logger$1.warn("[SignalWire] Failed to initialize DiagnosticsCollector:", error);
21526
21854
  }
@@ -21528,14 +21856,19 @@ var SignalWire = class extends Destroyable {
21528
21856
  /**
21529
21857
  * Disconnects the WebSocket and tears down the current session.
21530
21858
  *
21859
+ * Ends the session identified by the protocol and clears its persisted
21860
+ * resume state (`authorization_state` + protocol) and attach records
21861
+ * together — a later {@link connect} with the same credentials starts a
21862
+ * fresh session and cannot reattach to the ended session's calls.
21863
+ * Credentials and device preferences are preserved. To temporarily stop
21864
+ * receiving inbound calls while keeping the session alive, use
21865
+ * `unregister()` instead.
21866
+ *
21531
21867
  * The client can be reconnected by calling {@link connect} again,
21532
21868
  * which creates a fresh transport and session.
21533
21869
  */
21534
21870
  async disconnect() {
21535
- if (this._refreshTimerId) {
21536
- clearTimeout(this._refreshTimerId);
21537
- this._refreshTimerId = void 0;
21538
- }
21871
+ this._refreshCoordinator?.suspend();
21539
21872
  this._diagnosticsCollector?.record("connection", "disconnected");
21540
21873
  await this.teardownTransportAndSession();
21541
21874
  this._isConnected$.next(false);
@@ -21922,15 +22255,20 @@ var SignalWire = class extends Destroyable {
21922
22255
  prefs.preferredVideoInput = null;
21923
22256
  await this._deviceController.clearDeviceState();
21924
22257
  }
21925
- /** Destroys the client, clearing timers and releasing all resources. */
22258
+ /**
22259
+ * Destroys the client, clearing timers and releasing all resources.
22260
+ *
22261
+ * Intentionally destroying the client ends its session: the resume state
22262
+ * (`authorization_state` + protocol) and the attach records are both
22263
+ * cleared. Credentials and device preferences are preserved — use
22264
+ * {@link resetToDefaults} for a full wipe. To temporarily stop receiving
22265
+ * inbound calls while keeping the session alive, use `unregister()`.
22266
+ */
21926
22267
  destroy() {
21927
- if (this._refreshTimerId) {
21928
- clearTimeout(this._refreshTimerId);
21929
- this._refreshTimerId = void 0;
21930
- }
21931
- this._deviceTokenManager?.destroy();
22268
+ this._refreshCoordinator?.destroy();
22269
+ this._refreshCoordinator = void 0;
21932
22270
  this._dpopManager?.destroy();
21933
- if (this._attachManager) this._attachManager.detachAll();
22271
+ this._clientSession.teardownSessionState();
21934
22272
  this._transport.destroy();
21935
22273
  this._clientSession.destroy();
21936
22274
  try {
@@ -22045,7 +22383,7 @@ var StaticCredentialProvider = class {
22045
22383
  /**
22046
22384
  * Library version from package.json, injected at build time.
22047
22385
  */
22048
- const version = "3.30.0";
22386
+ const version = "4.0.0-rc.1";
22049
22387
  /**
22050
22388
  * Flag indicating the library has been loaded and is ready to use.
22051
22389
  * For UMD builds: `window.SignalWire.ready`
@@ -22067,7 +22405,7 @@ const ready = true;
22067
22405
  */
22068
22406
  const emitReadyEvent = () => {
22069
22407
  if (typeof window !== "undefined") {
22070
- const event = new CustomEvent("signalwire:js:ready", { detail: { version: "3.30.0" } });
22408
+ const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.1" } });
22071
22409
  window.dispatchEvent(event);
22072
22410
  }
22073
22411
  };