@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.
package/dist/browser.mjs CHANGED
@@ -9464,9 +9464,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9464
9464
  defaultLogger$1 = new Logger();
9465
9465
  defaultLogger$1.getLogger = function getLogger$1(name) {
9466
9466
  if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
9467
- var logger$32 = _loggersByName[name];
9468
- if (!logger$32) logger$32 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9469
- return logger$32;
9467
+ var logger$33 = _loggersByName[name];
9468
+ if (!logger$33) logger$33 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9469
+ return logger$33;
9470
9470
  };
9471
9471
  var _log = typeof window !== undefinedType ? window.log : void 0;
9472
9472
  defaultLogger$1.noConflict = function() {
@@ -9502,8 +9502,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
9502
9502
  defaultLogger.setLevel(defaultLoggerLevel);
9503
9503
  let userLogger = null;
9504
9504
  /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
9505
- const setLogger = (logger$32) => {
9506
- userLogger = logger$32;
9505
+ const setLogger = (logger$33) => {
9506
+ userLogger = logger$33;
9507
9507
  };
9508
9508
  let debugOptions = {};
9509
9509
  /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
@@ -9547,8 +9547,8 @@ const wsTraffic = (options) => {
9547
9547
  loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
9548
9548
  };
9549
9549
  const getLogger = () => {
9550
- const logger$32 = getLoggerInstance();
9551
- return new Proxy(logger$32, { get(_target, prop, _receiver) {
9550
+ const logger$33 = getLoggerInstance();
9551
+ return new Proxy(logger$33, { get(_target, prop, _receiver) {
9552
9552
  if (prop === "wsTraffic") return wsTraffic;
9553
9553
  const instance = getLoggerInstance();
9554
9554
  const value = Reflect.get(instance, prop);
@@ -9600,7 +9600,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
9600
9600
 
9601
9601
  //#endregion
9602
9602
  //#region src/controllers/HTTPRequestController.ts
9603
- const logger$31 = getLogger();
9603
+ const logger$32 = getLogger();
9604
9604
  const GET_PARAMS = {
9605
9605
  method: "GET",
9606
9606
  headers: { Accept: "application/json" }
@@ -9664,7 +9664,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9664
9664
  this._responses$.next(response);
9665
9665
  return response;
9666
9666
  } catch (error) {
9667
- logger$31.error("[HTTPRequestController] Request error:", error);
9667
+ logger$32.error("[HTTPRequestController] Request error:", error);
9668
9668
  this._status$.next("error");
9669
9669
  const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
9670
9670
  this._errors$.next(err);
@@ -9691,7 +9691,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9691
9691
  const url = this.buildURL(request.url);
9692
9692
  const headers = this.buildHeaders(request.headers);
9693
9693
  const timeout$5 = request.timeout ?? this.requestTimeout;
9694
- logger$31.debug("[HTTPRequestController] Executing request:", {
9694
+ logger$32.debug("[HTTPRequestController] Executing request:", {
9695
9695
  method: request.method,
9696
9696
  url,
9697
9697
  headers: Object.keys(headers).reduce((acc, key) => {
@@ -9711,7 +9711,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9711
9711
  });
9712
9712
  clearTimeout(timeoutId);
9713
9713
  const httpResponse = await this.convertResponse(response);
9714
- logger$31.debug("[HTTPRequestController] Response received:", {
9714
+ logger$32.debug("[HTTPRequestController] Response received:", {
9715
9715
  status: response.status,
9716
9716
  statusText: response.statusText,
9717
9717
  headers: [...response.headers.entries()],
@@ -9721,7 +9721,7 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9721
9721
  } catch (error) {
9722
9722
  clearTimeout(timeoutId);
9723
9723
  if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
9724
- logger$31.error("[HTTPRequestController] Request failed:", error);
9724
+ logger$32.error("[HTTPRequestController] Request failed:", error);
9725
9725
  throw error;
9726
9726
  }
9727
9727
  }
@@ -9735,8 +9735,8 @@ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9735
9735
  const credential = this.getCredential();
9736
9736
  if (credential.token) {
9737
9737
  headers.Authorization = `Bearer ${credential.token}`;
9738
- logger$31.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9739
- } else logger$31.warn("[HTTPRequestController] No credentials available for authentication");
9738
+ logger$32.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9739
+ } else logger$32.warn("[HTTPRequestController] No credentials available for authentication");
9740
9740
  return headers;
9741
9741
  }
9742
9742
  /**
@@ -9906,6 +9906,13 @@ const CREDENTIAL_REFRESH_RETRY_BASE_MS = 1e3;
9906
9906
  const CREDENTIAL_REFRESH_MAX_DELAY_MS = 3e4;
9907
9907
  /** Buffer in milliseconds before token expiry to trigger refresh. */
9908
9908
  const CREDENTIAL_REFRESH_BUFFER_MS = 5e3;
9909
+ /**
9910
+ * Maximum time the coordinator will wait for `DeviceTokenManager.activate()`
9911
+ * to resolve before treating the activation as failed and falling back to
9912
+ * the developer-provided refresh path. Prevents a wedged HTTP layer from
9913
+ * leaving the session with no active refresh mechanism.
9914
+ */
9915
+ const CREDENTIAL_ACTIVATE_TIMEOUT_MS = 3e4;
9909
9916
  /** JSON-RPC error code for requester validation failure (corrupted auth state). */
9910
9917
  const RPC_ERROR_REQUESTER_VALIDATION_FAILED = -32003;
9911
9918
  /** JSON-RPC error code for invalid params (e.g., missing authentication block). */
@@ -10008,7 +10015,7 @@ function fromMsToSec(milliseconds) {
10008
10015
 
10009
10016
  //#endregion
10010
10017
  //#region src/containers/PreferencesContainer.ts
10011
- const logger$30 = getLogger();
10018
+ const logger$31 = getLogger();
10012
10019
  var PreferencesContainer = class PreferencesContainer {
10013
10020
  static get instance() {
10014
10021
  this._instance ??= new PreferencesContainer();
@@ -10670,7 +10677,7 @@ var ClientPreferences = class {
10670
10677
  if (!this._storage) return;
10671
10678
  const data = collectStoredPreferences();
10672
10679
  this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
10673
- logger$30.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10680
+ logger$31.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10674
10681
  });
10675
10682
  }
10676
10683
  /** Loads preferences from storage and applies them to the container. */
@@ -10679,7 +10686,7 @@ var ClientPreferences = class {
10679
10686
  this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
10680
10687
  if (stored) applyStoredPreferences(stored);
10681
10688
  }).catch((error) => {
10682
- logger$30.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10689
+ logger$31.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10683
10690
  });
10684
10691
  }
10685
10692
  };
@@ -10701,7 +10708,7 @@ function toError(value) {
10701
10708
  //#endregion
10702
10709
  //#region src/controllers/NavigatorDeviceController.ts
10703
10710
  var import_cjs$29 = require_cjs();
10704
- const logger$29 = getLogger();
10711
+ const logger$30 = getLogger();
10705
10712
  /** Maps a device kind to its storage key. */
10706
10713
  const DEVICE_STORAGE_KEYS = {
10707
10714
  audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
@@ -10723,7 +10730,7 @@ var NavigatorDeviceController = class extends Destroyable {
10723
10730
  super();
10724
10731
  this.webRTCApiProvider = webRTCApiProvider;
10725
10732
  this.deviceChangeHandler = () => {
10726
- logger$29.debug("[DeviceController] Device change detected");
10733
+ logger$30.debug("[DeviceController] Device change detected");
10727
10734
  this.enumerateDevices();
10728
10735
  };
10729
10736
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
@@ -10788,13 +10795,13 @@ var NavigatorDeviceController = class extends Destroyable {
10788
10795
  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$)));
10789
10796
  }
10790
10797
  get selectedAudioInputDevice$() {
10791
- 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))));
10798
+ 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))));
10792
10799
  }
10793
10800
  get selectedAudioOutputDevice$() {
10794
- 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))));
10801
+ 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))));
10795
10802
  }
10796
10803
  get selectedVideoInputDevice$() {
10797
- 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))));
10804
+ 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))));
10798
10805
  }
10799
10806
  get selectedAudioInputDevice() {
10800
10807
  if (this._audioInputDisabled$.value) return null;
@@ -10869,7 +10876,7 @@ var NavigatorDeviceController = class extends Destroyable {
10869
10876
  if (device) this.persistDeviceSelection("audioinput", device);
10870
10877
  }
10871
10878
  selectVideoInputDevice(device) {
10872
- logger$29.debug("[DeviceController] Setting selected video input device:", device);
10879
+ logger$30.debug("[DeviceController] Setting selected video input device:", device);
10873
10880
  if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
10874
10881
  const previous = this._selectedDevicesState$.value.videoinput;
10875
10882
  if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
@@ -10926,7 +10933,7 @@ var NavigatorDeviceController = class extends Destroyable {
10926
10933
  }
10927
10934
  const fromHistory = this._deviceHistory.findInHistory(kind, devices);
10928
10935
  if (fromHistory) {
10929
- logger$29.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10936
+ logger$30.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10930
10937
  this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
10931
10938
  return fromHistory;
10932
10939
  }
@@ -10979,7 +10986,7 @@ var NavigatorDeviceController = class extends Destroyable {
10979
10986
  try {
10980
10987
  await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
10981
10988
  } catch (error) {
10982
- logger$29.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10989
+ logger$30.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10983
10990
  }
10984
10991
  }
10985
10992
  async loadPersistedDevices() {
@@ -10995,7 +11002,7 @@ var NavigatorDeviceController = class extends Destroyable {
10995
11002
  [kind]: stored
10996
11003
  };
10997
11004
  } catch (error) {
10998
- logger$29.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11005
+ logger$30.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
10999
11006
  }
11000
11007
  }
11001
11008
  /** Clears device history, persisted selections, and re-enumerates devices. */
@@ -11013,7 +11020,7 @@ var NavigatorDeviceController = class extends Destroyable {
11013
11020
  this.disableDeviceMonitoring();
11014
11021
  this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
11015
11022
  if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11016
- logger$29.debug("[DeviceController] Polling devices due to interval");
11023
+ logger$30.debug("[DeviceController] Polling devices due to interval");
11017
11024
  this.enumerateDevices();
11018
11025
  });
11019
11026
  this.enumerateDevices();
@@ -11039,13 +11046,13 @@ var NavigatorDeviceController = class extends Destroyable {
11039
11046
  videoinput: []
11040
11047
  });
11041
11048
  this._devicesState$.next(devicesByKind);
11042
- logger$29.debug("[DeviceController] Devices enumerated:", {
11049
+ logger$30.debug("[DeviceController] Devices enumerated:", {
11043
11050
  audioInputs: devicesByKind.audioinput.length,
11044
11051
  audioOutputs: devicesByKind.audiooutput.length,
11045
11052
  videoInputs: devicesByKind.videoinput.length
11046
11053
  });
11047
11054
  } catch (error) {
11048
- logger$29.error("[DeviceController] Failed to enumerate devices:", error);
11055
+ logger$30.error("[DeviceController] Failed to enumerate devices:", error);
11049
11056
  this._errors$.next(toError(error));
11050
11057
  }
11051
11058
  }
@@ -11061,7 +11068,7 @@ var NavigatorDeviceController = class extends Destroyable {
11061
11068
  stream.getTracks().forEach((t) => t.stop());
11062
11069
  return capabilities;
11063
11070
  } catch (error) {
11064
- logger$29.error("[DeviceController] Failed to get device capabilities:", error);
11071
+ logger$30.error("[DeviceController] Failed to get device capabilities:", error);
11065
11072
  this._errors$.next(toError(error));
11066
11073
  throw error;
11067
11074
  }
@@ -11312,7 +11319,7 @@ var DependencyContainer = class {
11312
11319
 
11313
11320
  //#endregion
11314
11321
  //#region src/controllers/CryptoController.ts
11315
- const logger$28 = getLogger();
11322
+ const logger$29 = getLogger();
11316
11323
  const DPOP_DB_NAME = "sw-dpop";
11317
11324
  const DPOP_DB_VERSION = 1;
11318
11325
  const DPOP_STORE_NAME = "keys";
@@ -11371,7 +11378,7 @@ async function loadKeyPairFromDB() {
11371
11378
  tx.oncomplete = () => db.close();
11372
11379
  });
11373
11380
  } catch (error) {
11374
- logger$28.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11381
+ logger$29.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11375
11382
  return null;
11376
11383
  }
11377
11384
  }
@@ -11391,7 +11398,7 @@ async function saveKeyPairToDB(keyPair) {
11391
11398
  };
11392
11399
  });
11393
11400
  } catch (error) {
11394
- logger$28.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11401
+ logger$29.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11395
11402
  }
11396
11403
  }
11397
11404
  async function deleteKeyPairFromDB() {
@@ -11410,7 +11417,7 @@ async function deleteKeyPairFromDB() {
11410
11417
  };
11411
11418
  });
11412
11419
  } catch (error) {
11413
- logger$28.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11420
+ logger$29.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11414
11421
  }
11415
11422
  }
11416
11423
  /**
@@ -11470,13 +11477,13 @@ var CryptoController = class {
11470
11477
  this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
11471
11478
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11472
11479
  this._initialized = true;
11473
- logger$28.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11480
+ logger$29.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11474
11481
  return this._fingerprint;
11475
11482
  } catch (error) {
11476
- logger$28.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11483
+ logger$29.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11477
11484
  await deleteKeyPairFromDB();
11478
11485
  }
11479
- logger$28.debug("[DPoP] Generating RSA key pair");
11486
+ logger$29.debug("[DPoP] Generating RSA key pair");
11480
11487
  this._keyPair = await crypto.subtle.generateKey({
11481
11488
  name: "RSASSA-PKCS1-v1_5",
11482
11489
  modulusLength: 2048,
@@ -11491,7 +11498,7 @@ var CryptoController = class {
11491
11498
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11492
11499
  this._initialized = true;
11493
11500
  await saveKeyPairToDB(this._keyPair);
11494
- logger$28.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11501
+ logger$29.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11495
11502
  return this._fingerprint;
11496
11503
  }
11497
11504
  /**
@@ -11557,7 +11564,7 @@ var CryptoController = class {
11557
11564
  this._fingerprint = null;
11558
11565
  this._initialized = false;
11559
11566
  deleteKeyPairFromDB();
11560
- logger$28.debug("[DPoP] Controller destroyed");
11567
+ logger$29.debug("[DPoP] Controller destroyed");
11561
11568
  }
11562
11569
  get publicJwk() {
11563
11570
  if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
@@ -11581,7 +11588,7 @@ var CryptoController = class {
11581
11588
  //#endregion
11582
11589
  //#region src/controllers/NetworkMonitor.ts
11583
11590
  var import_cjs$28 = require_cjs();
11584
- const logger$27 = getLogger();
11591
+ const logger$28 = getLogger();
11585
11592
  /**
11586
11593
  * Safely check whether we are running in a browser environment
11587
11594
  * with `window` and the relevant event targets.
@@ -11638,7 +11645,7 @@ var NetworkMonitor = class extends Destroyable {
11638
11645
  }
11639
11646
  attachListeners() {
11640
11647
  if (!hasBrowserNetworkEvents()) {
11641
- logger$27.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11648
+ logger$28.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11642
11649
  return;
11643
11650
  }
11644
11651
  window.addEventListener("online", this._onOnline);
@@ -11646,7 +11653,7 @@ var NetworkMonitor = class extends Destroyable {
11646
11653
  const connection = getNetworkConnection();
11647
11654
  if (connection) connection.addEventListener("change", this._onConnectionChange);
11648
11655
  this._listenersAttached = true;
11649
- logger$27.debug("NetworkMonitor: event listeners attached");
11656
+ logger$28.debug("NetworkMonitor: event listeners attached");
11650
11657
  }
11651
11658
  removeListeners() {
11652
11659
  if (!this._listenersAttached) return;
@@ -11657,10 +11664,10 @@ var NetworkMonitor = class extends Destroyable {
11657
11664
  if (connection) connection.removeEventListener("change", this._onConnectionChange);
11658
11665
  }
11659
11666
  this._listenersAttached = false;
11660
- logger$27.debug("NetworkMonitor: event listeners removed");
11667
+ logger$28.debug("NetworkMonitor: event listeners removed");
11661
11668
  }
11662
11669
  handleOnline() {
11663
- logger$27.info("NetworkMonitor: browser went online");
11670
+ logger$28.info("NetworkMonitor: browser went online");
11664
11671
  this._isOnline$.next(true);
11665
11672
  this._networkChange$.next({
11666
11673
  type: "online",
@@ -11669,7 +11676,7 @@ var NetworkMonitor = class extends Destroyable {
11669
11676
  });
11670
11677
  }
11671
11678
  handleOffline() {
11672
- logger$27.info("NetworkMonitor: browser went offline");
11679
+ logger$28.info("NetworkMonitor: browser went offline");
11673
11680
  this._isOnline$.next(false);
11674
11681
  this._networkChange$.next({
11675
11682
  type: "offline",
@@ -11678,7 +11685,7 @@ var NetworkMonitor = class extends Destroyable {
11678
11685
  }
11679
11686
  handleConnectionChange() {
11680
11687
  const networkType = getNetworkType();
11681
- logger$27.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11688
+ logger$28.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11682
11689
  this._networkChange$.next({
11683
11690
  type: "connection_change",
11684
11691
  timestamp: Date.now(),
@@ -11794,7 +11801,7 @@ function getNavigatorMediaDevices() {
11794
11801
  //#endregion
11795
11802
  //#region src/controllers/PreflightRunner.ts
11796
11803
  var import_cjs$27 = require_cjs();
11797
- const logger$26 = getLogger();
11804
+ const logger$27 = getLogger();
11798
11805
  const DEFAULT_MEDIA_TEST_DURATION_S = 10;
11799
11806
  const ICE_GATHERING_TIMEOUT_MS = 1e4;
11800
11807
  const SIGNALING_RTT_TIMEOUT_MS = 5e3;
@@ -11843,7 +11850,7 @@ var PreflightRunner = class extends Destroyable {
11843
11850
  if (!this._options.skipMediaTest) try {
11844
11851
  bandwidth = await this.testMediaBandwidth(destination);
11845
11852
  } catch (error) {
11846
- logger$26.warn("[PreflightRunner] Media bandwidth test failed:", error);
11853
+ logger$27.warn("[PreflightRunner] Media bandwidth test failed:", error);
11847
11854
  warnings.push("Media bandwidth test failed");
11848
11855
  }
11849
11856
  return {
@@ -11855,7 +11862,7 @@ var PreflightRunner = class extends Destroyable {
11855
11862
  warnings
11856
11863
  };
11857
11864
  } catch (error) {
11858
- logger$26.error("[PreflightRunner] Preflight test failed:", error);
11865
+ logger$27.error("[PreflightRunner] Preflight test failed:", error);
11859
11866
  throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
11860
11867
  } finally {
11861
11868
  this.destroy();
@@ -11886,7 +11893,7 @@ var PreflightRunner = class extends Destroyable {
11886
11893
  if (track.kind === "video" && track.readyState === "live") videoWorking = true;
11887
11894
  }
11888
11895
  } catch (error) {
11889
- logger$26.warn("[PreflightRunner] Device test failed:", error);
11896
+ logger$27.warn("[PreflightRunner] Device test failed:", error);
11890
11897
  } finally {
11891
11898
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
11892
11899
  }
@@ -11944,7 +11951,7 @@ var PreflightRunner = class extends Destroyable {
11944
11951
  rttMs
11945
11952
  };
11946
11953
  } catch (error) {
11947
- logger$26.warn("[PreflightRunner] ICE connectivity test failed:", error);
11954
+ logger$27.warn("[PreflightRunner] ICE connectivity test failed:", error);
11948
11955
  return {
11949
11956
  type: "failed",
11950
11957
  turnReachable: false,
@@ -11992,7 +11999,7 @@ var PreflightRunner = class extends Destroyable {
11992
11999
  //#endregion
11993
12000
  //#region src/controllers/VisibilityController.ts
11994
12001
  var import_cjs$26 = require_cjs();
11995
- const logger$25 = getLogger();
12002
+ const logger$26 = getLogger();
11996
12003
  /**
11997
12004
  * Checks whether the document visibility API is available.
11998
12005
  */
@@ -12029,8 +12036,8 @@ var VisibilityController = class extends Destroyable {
12029
12036
  this._boundHandler = this._handleVisibilityChange.bind(this);
12030
12037
  if (this._hasVisibilityApi) {
12031
12038
  document.addEventListener("visibilitychange", this._boundHandler);
12032
- logger$25.debug("VisibilityController: listening for visibilitychange events");
12033
- } else logger$25.debug("VisibilityController: document visibility API not available, defaulting to visible");
12039
+ logger$26.debug("VisibilityController: listening for visibilitychange events");
12040
+ } else logger$26.debug("VisibilityController: document visibility API not available, defaulting to visible");
12034
12041
  }
12035
12042
  /**
12036
12043
  * Observable of the current visibility state.
@@ -12055,7 +12062,7 @@ var VisibilityController = class extends Destroyable {
12055
12062
  destroy() {
12056
12063
  if (this._hasVisibilityApi) {
12057
12064
  document.removeEventListener("visibilitychange", this._boundHandler);
12058
- logger$25.debug("VisibilityController: removed visibilitychange listener");
12065
+ logger$26.debug("VisibilityController: removed visibilitychange listener");
12059
12066
  }
12060
12067
  super.destroy();
12061
12068
  }
@@ -12073,7 +12080,7 @@ var VisibilityController = class extends Destroyable {
12073
12080
  timestamp: Date.now()
12074
12081
  };
12075
12082
  this._visibilityChange$.next(changeEvent);
12076
- logger$25.debug("VisibilityController: visibility changed", {
12083
+ logger$26.debug("VisibilityController: visibility changed", {
12077
12084
  from: previousState,
12078
12085
  to: newState
12079
12086
  });
@@ -12314,7 +12321,7 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
12314
12321
 
12315
12322
  //#endregion
12316
12323
  //#region src/managers/AttachManager.ts
12317
- const logger$24 = getLogger();
12324
+ const logger$25 = getLogger();
12318
12325
  var AttachManager = class {
12319
12326
  constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
12320
12327
  this.storage = storage;
@@ -12335,7 +12342,7 @@ var AttachManager = class {
12335
12342
  try {
12336
12343
  return await this.storage.getItem(this.attachKey) ?? {};
12337
12344
  } catch (error) {
12338
- logger$24.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12345
+ logger$25.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12339
12346
  return {};
12340
12347
  }
12341
12348
  }
@@ -12343,7 +12350,7 @@ var AttachManager = class {
12343
12350
  try {
12344
12351
  await this.storage.setItem(this.attachKey, attached);
12345
12352
  } catch (error) {
12346
- logger$24.warn("[AttachManager] Failed to write attached calls to storage", error);
12353
+ logger$25.warn("[AttachManager] Failed to write attached calls to storage", error);
12347
12354
  }
12348
12355
  }
12349
12356
  /**
@@ -12362,7 +12369,7 @@ var AttachManager = class {
12362
12369
  }
12363
12370
  async attach(call) {
12364
12371
  if (!call.to) {
12365
- logger$24.warn("[AttachManager] Skip attach for calls with no destination");
12372
+ logger$25.warn("[AttachManager] Skip attach for calls with no destination");
12366
12373
  return;
12367
12374
  }
12368
12375
  const destination = call.to;
@@ -12415,15 +12422,15 @@ var AttachManager = class {
12415
12422
  callId,
12416
12423
  ...options
12417
12424
  });
12418
- logger$24.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12425
+ logger$25.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12419
12426
  succeeded = true;
12420
12427
  break;
12421
12428
  } catch (error) {
12422
- logger$24.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12429
+ logger$25.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12423
12430
  if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
12424
12431
  }
12425
12432
  if (!succeeded) {
12426
- logger$24.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12433
+ logger$25.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12427
12434
  await this.detach({
12428
12435
  id: callId,
12429
12436
  mediaDirections: attachment.mediaDirections
@@ -13360,7 +13367,9 @@ const DEFAULT_MEMBER_CAPABILITIES = {
13360
13367
  position: false,
13361
13368
  meta: false,
13362
13369
  remove: false,
13363
- audioFlags: false
13370
+ audioFlags: false,
13371
+ denoise: false,
13372
+ lowbitrate: false
13364
13373
  };
13365
13374
  /**
13366
13375
  * Default call capabilities with no permissions
@@ -13433,7 +13442,9 @@ function computeMemberCapabilities(flags, memberType) {
13433
13442
  position: hasBooleanCapability(flags, memberType, null, "position"),
13434
13443
  meta: hasBooleanCapability(flags, memberType, null, "meta"),
13435
13444
  remove: hasBooleanCapability(flags, memberType, null, "remove"),
13436
- audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags")
13445
+ audioFlags: hasBooleanCapability(flags, memberType, null, "audioflags"),
13446
+ denoise: hasBooleanCapability(flags, memberType, null, "denoise"),
13447
+ lowbitrate: hasBooleanCapability(flags, memberType, null, "lowbitrate")
13437
13448
  };
13438
13449
  }
13439
13450
  /**
@@ -13590,7 +13601,7 @@ function toggleHandraiseMethod(is) {
13590
13601
 
13591
13602
  //#endregion
13592
13603
  //#region src/core/entities/Participant.ts
13593
- const logger$23 = getLogger();
13604
+ const logger$24 = getLogger();
13594
13605
  const initialState = {};
13595
13606
  /**
13596
13607
  * Represents a participant in a call.
@@ -13816,6 +13827,10 @@ var Participant = class extends Destroyable {
13816
13827
  get nodeId() {
13817
13828
  return this._state$.value.node_id;
13818
13829
  }
13830
+ /** Call ID for this participant's leg, or `undefined` if not available. */
13831
+ get callId() {
13832
+ return this._state$.value.call_id;
13833
+ }
13819
13834
  /** @internal */
13820
13835
  get value() {
13821
13836
  return this._state$.value;
@@ -13877,8 +13892,9 @@ var Participant = class extends Destroyable {
13877
13892
  noise_suppression: !this.noiseSuppression
13878
13893
  });
13879
13894
  }
13895
+ /** Toggles low-bitrate mode for this participant's media. */
13880
13896
  async toggleLowbitrate() {
13881
- throw new UnimplementedError();
13897
+ await this.executeMethod(this.id, "call.lowbitrate.set", { lowbitrate: !this.lowbitrate });
13882
13898
  }
13883
13899
  /**
13884
13900
  * Adjusts the **conference-only** microphone energy gate / sensitivity level
@@ -13925,10 +13941,27 @@ var Participant = class extends Destroyable {
13925
13941
  }
13926
13942
  /**
13927
13943
  * Sets the participant's position in the video layout.
13944
+ *
13945
+ * Requires the `member.position` capability. The gateway keys positions by the
13946
+ * **target member's own** `call_id`/`node_id` (see issue #19400 and the legacy
13947
+ * `setPositions` implementation), so this sends the participant's own call
13948
+ * context — matching {@link Participant.remove}. A resolved promise does not
13949
+ * guarantee a visible change: the backend silently returns `200` (no-op) for
13950
+ * non-conference targets.
13951
+ *
13928
13952
  * @param value - The {@link VideoPosition} to assign (e.g. `'auto'`, `'reserved-0'`).
13929
13953
  */
13930
13954
  async setPosition(value) {
13931
- await this.executeMethod(this.id, "call.member.position.set", { position: value });
13955
+ const state = this._state$.value;
13956
+ const target = {
13957
+ member_id: this.id,
13958
+ call_id: state.call_id ?? "",
13959
+ node_id: state.node_id ?? ""
13960
+ };
13961
+ await this.executeMethod(target, "call.member.position.set", { targets: [{
13962
+ target,
13963
+ position: value
13964
+ }] });
13932
13965
  }
13933
13966
  /** Removes this participant from the call. */
13934
13967
  async remove() {
@@ -14023,7 +14056,7 @@ var SelfParticipant = class extends Participant {
14023
14056
  try {
14024
14057
  await this.vertoManager.addScreenMedia();
14025
14058
  } catch (error) {
14026
- logger$23.error("[Participant.startScreenShare] Screen share error:", error);
14059
+ logger$24.error("[Participant.startScreenShare] Screen share error:", error);
14027
14060
  }
14028
14061
  }
14029
14062
  /** Observable of the current screen share status. */
@@ -14043,7 +14076,7 @@ var SelfParticipant = class extends Participant {
14043
14076
  try {
14044
14077
  await this.vertoManager.addInputDevice(options);
14045
14078
  } catch (error) {
14046
- logger$23.error("[Participant.startScreenShare] Screen share error:", error);
14079
+ logger$24.error("[Participant.startScreenShare] Screen share error:", error);
14047
14080
  }
14048
14081
  }
14049
14082
  /** Removes an additional media input device by ID. */
@@ -14105,7 +14138,7 @@ var SelfParticipant = class extends Participant {
14105
14138
  */
14106
14139
  exitStudioModeIfActive() {
14107
14140
  if (this._studioAudio$.value) {
14108
- logger$23.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14141
+ logger$24.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14109
14142
  this._studioAudio$.next(false);
14110
14143
  }
14111
14144
  }
@@ -14129,7 +14162,7 @@ var SelfParticipant = class extends Participant {
14129
14162
  try {
14130
14163
  await super.mute();
14131
14164
  } catch (error) {
14132
- logger$23.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14165
+ logger$24.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14133
14166
  } finally {
14134
14167
  this.vertoManager.muteMainAudioInputDevice();
14135
14168
  }
@@ -14139,7 +14172,7 @@ var SelfParticipant = class extends Participant {
14139
14172
  try {
14140
14173
  await super.unmute();
14141
14174
  } catch (error) {
14142
- logger$23.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14175
+ logger$24.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14143
14176
  } finally {
14144
14177
  await this.vertoManager.unmuteMainAudioInputDevice();
14145
14178
  }
@@ -14149,7 +14182,7 @@ var SelfParticipant = class extends Participant {
14149
14182
  try {
14150
14183
  await super.muteVideo();
14151
14184
  } catch (error) {
14152
- logger$23.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14185
+ logger$24.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14153
14186
  } finally {
14154
14187
  this.vertoManager.muteMainVideoInputDevice();
14155
14188
  }
@@ -14159,7 +14192,7 @@ var SelfParticipant = class extends Participant {
14159
14192
  try {
14160
14193
  await super.unmuteVideo();
14161
14194
  } catch (error) {
14162
- logger$23.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14195
+ logger$24.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14163
14196
  } finally {
14164
14197
  await this.vertoManager.unmuteMainVideoInputDevice();
14165
14198
  }
@@ -14364,7 +14397,7 @@ function filterAs(predicate, resultPath) {
14364
14397
  //#endregion
14365
14398
  //#region src/operators/throwOnRPCError.ts
14366
14399
  var import_cjs$21 = require_cjs();
14367
- const logger$22 = getLogger();
14400
+ const logger$23 = getLogger();
14368
14401
  /**
14369
14402
  * RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
14370
14403
  * Passes successful responses through unchanged.
@@ -14372,14 +14405,14 @@ const logger$22 = getLogger();
14372
14405
  function throwOnRPCError() {
14373
14406
  return (0, import_cjs$21.map)((response) => {
14374
14407
  if (response.error) {
14375
- logger$22.error("[throwOnRPCError] RPC error response:", {
14408
+ logger$23.error("[throwOnRPCError] RPC error response:", {
14376
14409
  code: response.error.code,
14377
14410
  message: response.error.message,
14378
14411
  data: response.error.data
14379
14412
  });
14380
14413
  throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
14381
14414
  }
14382
- logger$22.debug("[throwOnRPCError] RPC successful response:", response);
14415
+ logger$23.debug("[throwOnRPCError] RPC successful response:", response);
14383
14416
  return response;
14384
14417
  });
14385
14418
  }
@@ -14387,7 +14420,7 @@ function throwOnRPCError() {
14387
14420
  //#endregion
14388
14421
  //#region src/managers/CallEventsManager.ts
14389
14422
  var import_cjs$20 = require_cjs();
14390
- const logger$21 = getLogger();
14423
+ const logger$22 = getLogger();
14391
14424
  const initialSessionState = {};
14392
14425
  /** @internal */
14393
14426
  var CallEventsManager = class extends Destroyable {
@@ -14491,7 +14524,7 @@ var CallEventsManager = class extends Destroyable {
14491
14524
  }
14492
14525
  initSubscriptions() {
14493
14526
  this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
14494
- logger$21.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14527
+ logger$22.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14495
14528
  callId: callJoinedEvent.call_id,
14496
14529
  roomSessionId: callJoinedEvent.room_session_id
14497
14530
  });
@@ -14518,19 +14551,19 @@ var CallEventsManager = class extends Destroyable {
14518
14551
  if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
14519
14552
  });
14520
14553
  this.subscribeTo(this.memberUpdates$, (member) => {
14521
- logger$21.debug("[CallEventsManager] Handling member update event for member ID:", member);
14554
+ logger$22.debug("[CallEventsManager] Handling member update event for member ID:", member);
14522
14555
  this.upsertParticipant(member);
14523
14556
  });
14524
14557
  this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
14525
- logger$21.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14558
+ logger$22.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14526
14559
  const participants = { ...this._participants$.value };
14527
14560
  if (memberLeftEvent.member.member_id in participants) {
14528
14561
  delete participants[memberLeftEvent.member.member_id];
14529
14562
  this._participants$.next(participants);
14530
- } else logger$21.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14563
+ } else logger$22.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14531
14564
  });
14532
14565
  this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
14533
- logger$21.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14566
+ logger$22.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14534
14567
  const roomSession = callUpdatedEvent.room_session;
14535
14568
  this._sessionState$.next({
14536
14569
  ...this._sessionState$.value,
@@ -14545,7 +14578,7 @@ var CallEventsManager = class extends Destroyable {
14545
14578
  });
14546
14579
  });
14547
14580
  this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
14548
- logger$21.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14581
+ logger$22.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14549
14582
  this._sessionState$.next({
14550
14583
  ...this._sessionState$.value,
14551
14584
  layout_name: layoutChangedEvent.id,
@@ -14555,10 +14588,10 @@ var CallEventsManager = class extends Destroyable {
14555
14588
  });
14556
14589
  }
14557
14590
  updateParticipantPositions(layoutChangedEvent) {
14558
- 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.");
14591
+ 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.");
14559
14592
  layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
14560
14593
  if (!(layer.member_id in this._participants$.value)) {
14561
- logger$21.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14594
+ logger$22.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14562
14595
  return false;
14563
14596
  }
14564
14597
  return true;
@@ -14581,7 +14614,7 @@ var CallEventsManager = class extends Destroyable {
14581
14614
  layouts: response.result.layouts
14582
14615
  });
14583
14616
  }).catch((error) => {
14584
- logger$21.error("[CallEventsManager] Error fetching layouts:", error);
14617
+ logger$22.error("[CallEventsManager] Error fetching layouts:", error);
14585
14618
  });
14586
14619
  }
14587
14620
  updateParticipants(members) {
@@ -14597,7 +14630,7 @@ var CallEventsManager = class extends Destroyable {
14597
14630
  }
14598
14631
  const participant = this._participants$.value[member.member_id];
14599
14632
  const oldValue = participant.value;
14600
- logger$21.debug("[CallEventsManager] Updating participant:", member.member_id, {
14633
+ logger$22.debug("[CallEventsManager] Updating participant:", member.member_id, {
14601
14634
  oldValue,
14602
14635
  newValue: member
14603
14636
  });
@@ -14610,17 +14643,17 @@ var CallEventsManager = class extends Destroyable {
14610
14643
  }
14611
14644
  get callJoinedEvent$() {
14612
14645
  return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
14613
- logger$21.debug("[CallEventsManager] Call joined event:", event);
14646
+ logger$22.debug("[CallEventsManager] Call joined event:", event);
14614
14647
  })));
14615
14648
  }
14616
14649
  get layoutChangedEvent$() {
14617
14650
  return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
14618
- logger$21.debug("[CallEventsManager] Layout changed event:", event);
14651
+ logger$22.debug("[CallEventsManager] Layout changed event:", event);
14619
14652
  })));
14620
14653
  }
14621
14654
  get memberUpdates$() {
14622
14655
  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) => {
14623
- logger$21.debug("[CallEventsManager] Member update event:", event);
14656
+ logger$22.debug("[CallEventsManager] Member update event:", event);
14624
14657
  })));
14625
14658
  }
14626
14659
  destroy() {
@@ -14877,7 +14910,7 @@ function appendStereoParams(fmtpLine, maxBitrate) {
14877
14910
  //#endregion
14878
14911
  //#region src/controllers/ICEGatheringController.ts
14879
14912
  var import_cjs$19 = require_cjs();
14880
- const logger$20 = getLogger();
14913
+ const logger$21 = getLogger();
14881
14914
  var ICEGatheringController = class extends Destroyable {
14882
14915
  constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
14883
14916
  super();
@@ -14885,23 +14918,23 @@ var ICEGatheringController = class extends Destroyable {
14885
14918
  this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
14886
14919
  this.onicegatheringstatechangeHandler = () => {
14887
14920
  const { iceGatheringState } = this.peerConnection;
14888
- logger$20.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14921
+ logger$21.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14889
14922
  if (iceGatheringState === "gathering") this._iceCandidatesState.next({
14890
14923
  state: "gathering",
14891
14924
  validSDP: false
14892
14925
  });
14893
14926
  };
14894
14927
  this.onicecandidateHandler = (event) => {
14895
- logger$20.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14928
+ logger$21.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14896
14929
  this.removeTimer("iceCandidateTimer");
14897
14930
  if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
14898
14931
  if (this.peerConnection.iceGatheringState !== "complete") {
14899
- logger$20.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14932
+ logger$21.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14900
14933
  this.handleICECandidateTimeout();
14901
14934
  }
14902
14935
  }, this.iceCandidateTimeout);
14903
14936
  else {
14904
- logger$20.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14937
+ logger$21.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14905
14938
  this.removeTimer("iceGatheringTimer");
14906
14939
  this.handleICEGatheringComplete();
14907
14940
  }
@@ -14919,7 +14952,7 @@ var ICEGatheringController = class extends Destroyable {
14919
14952
  this.setupEventListeners();
14920
14953
  this.iceGatheringTimer = setTimeout(() => {
14921
14954
  if (this.peerConnection.iceGatheringState !== "complete") {
14922
- logger$20.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14955
+ logger$21.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14923
14956
  this.handleICEGatheringTimeout();
14924
14957
  }
14925
14958
  }, this.iceGatheringTimeout);
@@ -14946,9 +14979,9 @@ var ICEGatheringController = class extends Destroyable {
14946
14979
  this.relayOnly = value;
14947
14980
  }
14948
14981
  handleICEGatheringComplete() {
14949
- logger$20.debug("[ICEGatheringController] Handling ICE gathering complete");
14950
- logger$20.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14951
- logger$20.debug("[ICEGatheringController] ICE gathering complete");
14982
+ logger$21.debug("[ICEGatheringController] Handling ICE gathering complete");
14983
+ logger$21.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14984
+ logger$21.debug("[ICEGatheringController] ICE gathering complete");
14952
14985
  this._iceCandidatesState.next({
14953
14986
  state: "complete",
14954
14987
  validSDP: this.hasValidLocalDescriptionSDP
@@ -14964,21 +14997,21 @@ var ICEGatheringController = class extends Destroyable {
14964
14997
  this.removeTimer("iceGatheringTimer");
14965
14998
  const validSDP = this.hasValidLocalDescriptionSDP;
14966
14999
  if (validSDP) {
14967
- logger$20.debug("[ICEGatheringController] Local SDP is valid");
15000
+ logger$21.debug("[ICEGatheringController] Local SDP is valid");
14968
15001
  this._iceCandidatesState.next({
14969
15002
  state: "timeout",
14970
15003
  validSDP
14971
15004
  });
14972
15005
  this.stopGathering();
14973
- } else logger$20.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
15006
+ } else logger$21.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14974
15007
  }
14975
15008
  handleICECandidateTimeout() {
14976
15009
  if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
14977
- logger$20.warn("[ICEGatheringController] ICE candidate timeout");
15010
+ logger$21.warn("[ICEGatheringController] ICE candidate timeout");
14978
15011
  const validSDP = this.hasValidLocalDescriptionSDP;
14979
15012
  if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
14980
15013
  else {
14981
- logger$20.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
15014
+ logger$21.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14982
15015
  this._iceCandidatesState.next({
14983
15016
  state: "timeout",
14984
15017
  validSDP
@@ -14987,7 +15020,7 @@ var ICEGatheringController = class extends Destroyable {
14987
15020
  }
14988
15021
  }
14989
15022
  restartICEGatheringWithRelayOnly() {
14990
- logger$20.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
15023
+ logger$21.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
14991
15024
  this.relayOnly = true;
14992
15025
  this.peerConnection.setConfiguration({
14993
15026
  ...this.peerConnection.getConfiguration(),
@@ -15002,7 +15035,7 @@ var ICEGatheringController = class extends Destroyable {
15002
15035
  }
15003
15036
  }
15004
15037
  clearAllTimers() {
15005
- logger$20.debug("[ICEGatheringController] Clearing all timers");
15038
+ logger$21.debug("[ICEGatheringController] Clearing all timers");
15006
15039
  this.removeTimer("iceGatheringTimer");
15007
15040
  this.removeTimer("iceCandidateTimer");
15008
15041
  }
@@ -15011,7 +15044,7 @@ var ICEGatheringController = class extends Destroyable {
15011
15044
  this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
15012
15045
  }
15013
15046
  destroy() {
15014
- logger$20.debug("[ICEGatheringController] Destroying ICEGatheringController");
15047
+ logger$21.debug("[ICEGatheringController] Destroying ICEGatheringController");
15015
15048
  this.clearAllTimers();
15016
15049
  this.removeEventListeners();
15017
15050
  super.destroy();
@@ -15021,7 +15054,7 @@ var ICEGatheringController = class extends Destroyable {
15021
15054
  //#endregion
15022
15055
  //#region src/controllers/LocalAudioPipeline.ts
15023
15056
  var import_cjs$18 = require_cjs();
15024
- const logger$19 = getLogger();
15057
+ const logger$20 = getLogger();
15025
15058
  /**
15026
15059
  * Web Audio pipeline for the local microphone stream.
15027
15060
  *
@@ -15133,7 +15166,7 @@ var LocalAudioPipeline = class extends Destroyable {
15133
15166
  try {
15134
15167
  this._inputSource.disconnect();
15135
15168
  } catch (error) {
15136
- logger$19.debug("[LocalAudioPipeline] input disconnect warning:", error);
15169
+ logger$20.debug("[LocalAudioPipeline] input disconnect warning:", error);
15137
15170
  }
15138
15171
  this._inputSource = null;
15139
15172
  }
@@ -15143,7 +15176,7 @@ var LocalAudioPipeline = class extends Destroyable {
15143
15176
  this._inputSource = this._audioContext.createMediaStreamSource(this._inputStream);
15144
15177
  this._inputSource.connect(this._gainNode);
15145
15178
  if (this._audioContext.state === "suspended") this._audioContext.resume().catch((error) => {
15146
- logger$19.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15179
+ logger$20.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15147
15180
  });
15148
15181
  }
15149
15182
  destroy() {
@@ -15158,7 +15191,7 @@ var LocalAudioPipeline = class extends Destroyable {
15158
15191
  this._analyser.disconnect();
15159
15192
  } catch {}
15160
15193
  this._audioContext.close().catch((error) => {
15161
- logger$19.debug("[LocalAudioPipeline] audio context close warning:", error);
15194
+ logger$20.debug("[LocalAudioPipeline] audio context close warning:", error);
15162
15195
  });
15163
15196
  super.destroy();
15164
15197
  }
@@ -15185,7 +15218,7 @@ var LocalAudioPipeline = class extends Destroyable {
15185
15218
  //#endregion
15186
15219
  //#region src/controllers/LocalStreamController.ts
15187
15220
  var import_cjs$17 = require_cjs();
15188
- const logger$18 = getLogger();
15221
+ const logger$19 = getLogger();
15189
15222
  var LocalStreamController = class extends Destroyable {
15190
15223
  constructor(options) {
15191
15224
  super();
@@ -15223,26 +15256,26 @@ var LocalStreamController = class extends Destroyable {
15223
15256
  * Build the local media stream based on the provided options.
15224
15257
  */
15225
15258
  async buildLocalStream() {
15226
- logger$18.debug("[LocalStreamController] Building local media stream.");
15259
+ logger$19.debug("[LocalStreamController] Building local media stream.");
15227
15260
  let stream;
15228
15261
  if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
15229
15262
  const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
15230
15263
  stream = new MediaStream(tracks);
15231
15264
  } else if (this.options.propose === "screenshare") {
15232
- logger$18.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15265
+ logger$19.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15233
15266
  stream = await this.options.getDisplayMedia({
15234
15267
  video: true,
15235
15268
  audio: Boolean(this.options.inputAudioDeviceConstraints)
15236
15269
  });
15237
- logger$18.debug("[LocalStreamController] Screen share media obtained:", stream);
15270
+ logger$19.debug("[LocalStreamController] Screen share media obtained:", stream);
15238
15271
  } else {
15239
15272
  const constraints = {
15240
15273
  audio: this.options.inputAudioDeviceConstraints,
15241
15274
  video: this.options.inputVideoDeviceConstraints
15242
15275
  };
15243
- logger$18.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15276
+ logger$19.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15244
15277
  stream = await this.options.getUserMedia(constraints);
15245
- logger$18.debug("[LocalStreamController] User media obtained:", stream);
15278
+ logger$19.debug("[LocalStreamController] User media obtained:", stream);
15246
15279
  }
15247
15280
  this._localStream$.next(stream);
15248
15281
  this._localAudioTracks$.next(stream.getAudioTracks());
@@ -15261,7 +15294,7 @@ var LocalStreamController = class extends Destroyable {
15261
15294
  this._localStream$.next(localStream);
15262
15295
  if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
15263
15296
  else this._localAudioTracks$.next(localStream.getAudioTracks());
15264
- logger$18.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15297
+ logger$19.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15265
15298
  return localStream;
15266
15299
  }
15267
15300
  /**
@@ -15273,7 +15306,7 @@ var LocalStreamController = class extends Destroyable {
15273
15306
  const stream = this._localStream$.value;
15274
15307
  const track = stream?.getTracks().find((t) => t.id === trackId);
15275
15308
  if (!track) {
15276
- logger$18.debug(`[LocalStreamController] track not found: ${trackId}`);
15309
+ logger$19.debug(`[LocalStreamController] track not found: ${trackId}`);
15277
15310
  return;
15278
15311
  }
15279
15312
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
@@ -15282,7 +15315,7 @@ var LocalStreamController = class extends Destroyable {
15282
15315
  this._localStream$.next(stream);
15283
15316
  if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
15284
15317
  else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
15285
- logger$18.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15318
+ logger$19.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15286
15319
  return track;
15287
15320
  }
15288
15321
  /**
@@ -15317,7 +15350,7 @@ var LocalStreamController = class extends Destroyable {
15317
15350
  */
15318
15351
  stopAllTracks() {
15319
15352
  this._localStream$.value?.getTracks().forEach((track) => {
15320
- logger$18.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15353
+ logger$19.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15321
15354
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
15322
15355
  track.stop();
15323
15356
  });
@@ -15333,7 +15366,7 @@ var LocalStreamController = class extends Destroyable {
15333
15366
 
15334
15367
  //#endregion
15335
15368
  //#region src/controllers/TransceiverController.ts
15336
- const logger$17 = getLogger();
15369
+ const logger$18 = getLogger();
15337
15370
  const getDirection = (send, recv) => {
15338
15371
  if (send && recv) return "sendrecv";
15339
15372
  else if (send && !recv) return "sendonly";
@@ -15441,7 +15474,7 @@ var TransceiverController = class extends Destroyable {
15441
15474
  sendEncodings: isAudio ? void 0 : this.sendEncodings,
15442
15475
  streams: direction === "recvonly" ? void 0 : [localStream]
15443
15476
  };
15444
- logger$17.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15477
+ logger$18.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15445
15478
  transceiver,
15446
15479
  transceiverParams
15447
15480
  });
@@ -15449,11 +15482,11 @@ var TransceiverController = class extends Destroyable {
15449
15482
  await transceiver.sender.replaceTrack(track);
15450
15483
  transceiver.direction = transceiverParams.direction;
15451
15484
  if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
15452
- logger$17.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15485
+ logger$18.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15453
15486
  transceiver.sender.setStreams(...transceiverParams.streams);
15454
15487
  }
15455
15488
  } else {
15456
- logger$17.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15489
+ logger$18.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15457
15490
  this.peerConnection.addTransceiver(track, transceiverParams);
15458
15491
  }
15459
15492
  }
@@ -15467,13 +15500,13 @@ var TransceiverController = class extends Destroyable {
15467
15500
  if (options.updateTransceiverDirection) transceiver.direction = "inactive";
15468
15501
  }
15469
15502
  } catch (error) {
15470
- logger$17.error("[TransceiverController] stopTrackSender error", kind, error);
15503
+ logger$18.error("[TransceiverController] stopTrackSender error", kind, error);
15471
15504
  this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
15472
15505
  }
15473
15506
  }
15474
15507
  async restoreTrackSender(kind) {
15475
15508
  try {
15476
- logger$17.debug("[TransceiverController] restoreTrackSender called", kind);
15509
+ logger$18.debug("[TransceiverController] restoreTrackSender called", kind);
15477
15510
  const constraints = {};
15478
15511
  const transceivers = this.transceiverByKind(kind);
15479
15512
  for (const transceiver of transceivers) {
@@ -15483,23 +15516,23 @@ var TransceiverController = class extends Destroyable {
15483
15516
  if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
15484
15517
  }
15485
15518
  }
15486
- logger$17.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15519
+ logger$18.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15487
15520
  if (Object.keys(constraints).length === 0) {
15488
- logger$17.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15521
+ logger$18.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15489
15522
  return;
15490
15523
  }
15491
15524
  const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
15492
- logger$17.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15525
+ logger$18.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15493
15526
  for (const newTrack of newTracks) {
15494
15527
  this.options.localStreamController.addTrack(newTrack);
15495
15528
  const trackKind = newTrack.kind;
15496
15529
  const transceiverOfKind = this.transceiverByKind(trackKind)[0];
15497
15530
  transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
15498
- logger$17.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15531
+ logger$18.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15499
15532
  await transceiverOfKind.sender.replaceTrack(newTrack);
15500
15533
  }
15501
15534
  } catch (error) {
15502
- logger$17.error("[TransceiverController] restoreTrackSender error", kind, error);
15535
+ logger$18.error("[TransceiverController] restoreTrackSender error", kind, error);
15503
15536
  this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
15504
15537
  }
15505
15538
  }
@@ -15540,14 +15573,14 @@ var TransceiverController = class extends Destroyable {
15540
15573
  };
15541
15574
  try {
15542
15575
  await track.applyConstraints(constraintsToApply);
15543
- logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15544
- logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15576
+ logger$18.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15577
+ logger$18.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15545
15578
  } catch (error) {
15546
- logger$17.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15579
+ logger$18.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15547
15580
  try {
15548
15581
  await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
15549
15582
  } catch (fallbackError) {
15550
- logger$17.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15583
+ logger$18.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15551
15584
  this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
15552
15585
  }
15553
15586
  }
@@ -15575,7 +15608,7 @@ var TransceiverController = class extends Destroyable {
15575
15608
  if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
15576
15609
  await sender.replaceTrack(newTrack);
15577
15610
  this.options.localStreamController.addTrack(newTrack);
15578
- logger$17.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15611
+ logger$18.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15579
15612
  }
15580
15613
  getMediaDirections() {
15581
15614
  if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
@@ -15606,7 +15639,7 @@ var TransceiverController = class extends Destroyable {
15606
15639
  //#endregion
15607
15640
  //#region src/controllers/RTCPeerConnectionController.ts
15608
15641
  var import_cjs$16 = require_cjs();
15609
- const logger$16 = getLogger();
15642
+ const logger$17 = getLogger();
15610
15643
  var RTCPeerConnectionController = class extends Destroyable {
15611
15644
  constructor(options = {}, remoteSessionDescription, deviceController) {
15612
15645
  super();
@@ -15622,43 +15655,43 @@ var RTCPeerConnectionController = class extends Destroyable {
15622
15655
  this.oniceconnectionstatechangeHandler = () => {
15623
15656
  if (this.peerConnection) {
15624
15657
  const { iceConnectionState } = this.peerConnection;
15625
- logger$16.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15658
+ logger$17.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15626
15659
  this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
15627
15660
  }
15628
15661
  };
15629
15662
  this.onconnectionstatechangeHandler = () => {
15630
15663
  if (this.peerConnection) {
15631
15664
  const { connectionState } = this.peerConnection;
15632
- logger$16.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15665
+ logger$17.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15633
15666
  if (connectionState === "connected") this.removeConnectionTimer();
15634
15667
  this._connectionState$.next(this.peerConnection.connectionState);
15635
15668
  }
15636
15669
  };
15637
15670
  this.onsignalingstatechangeHandler = () => {
15638
- logger$16.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15671
+ logger$17.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15639
15672
  };
15640
15673
  this.onicegatheringstatechangeHandler = () => {
15641
15674
  if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
15642
15675
  };
15643
15676
  this.onnegotiationneededHandler = (event) => {
15644
- logger$16.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15677
+ logger$17.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15645
15678
  this.negotiationNeeded$.next();
15646
15679
  };
15647
15680
  this.updateSelectedInputDevice = async (kind, deviceInfo) => {
15648
15681
  try {
15649
15682
  const { localStream } = this;
15650
15683
  if (!localStream) {
15651
- logger$16.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15684
+ logger$17.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15652
15685
  return;
15653
15686
  }
15654
- logger$16.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15687
+ logger$17.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15655
15688
  const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
15656
15689
  if (track) {
15657
15690
  this.transceiverController?.stopTrackSender(kind);
15658
15691
  this.localStreamController.removeTrack(track.id);
15659
- logger$16.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15692
+ logger$17.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15660
15693
  if (!deviceInfo) {
15661
- logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15694
+ logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15662
15695
  return;
15663
15696
  }
15664
15697
  const streamTrack = (await this.getUserMedia({ [kind]: {
@@ -15666,15 +15699,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15666
15699
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
15667
15700
  } })).getTracks().find((t) => t.kind === kind);
15668
15701
  if (streamTrack) {
15669
- logger$16.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15702
+ logger$17.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15670
15703
  this.localStreamController.addTrack(streamTrack);
15671
15704
  await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
15672
- logger$16.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15705
+ logger$17.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15673
15706
  }
15674
15707
  }
15675
- logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15708
+ logger$17.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15676
15709
  } catch (error) {
15677
- logger$16.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15710
+ logger$17.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15678
15711
  this._errors$.next(toError(error));
15679
15712
  throw error;
15680
15713
  }
@@ -15924,15 +15957,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15924
15957
  this.setupPeerConnection();
15925
15958
  this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$16.auditTime)(0), (0, import_cjs$16.exhaustMap)(async () => this.startNegotiation())), {
15926
15959
  next: () => {
15927
- logger$16.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15960
+ logger$17.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15928
15961
  },
15929
15962
  error: (error) => {
15930
- logger$16.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15963
+ logger$17.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15931
15964
  this._errors$.next(toError(error));
15932
15965
  }
15933
15966
  });
15934
15967
  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]) => {
15935
- logger$16.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15968
+ logger$17.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15936
15969
  kind,
15937
15970
  deviceInfo
15938
15971
  });
@@ -15949,7 +15982,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15949
15982
  this._initialized$.next(true);
15950
15983
  }
15951
15984
  } catch (error) {
15952
- logger$16.error("[RTCPeerConnectionController] Initialization error:", error);
15985
+ logger$17.error("[RTCPeerConnectionController] Initialization error:", error);
15953
15986
  this._errors$.next(toError(error));
15954
15987
  this.destroy();
15955
15988
  }
@@ -15981,22 +16014,22 @@ var RTCPeerConnectionController = class extends Destroyable {
15981
16014
  }
15982
16015
  async startNegotiation() {
15983
16016
  if (this.isNegotiating) {
15984
- logger$16.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
16017
+ logger$17.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15985
16018
  return;
15986
16019
  }
15987
16020
  this.setupEventListeners();
15988
16021
  if (this.type === "answer") {
15989
- logger$16.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
16022
+ logger$17.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15990
16023
  return;
15991
16024
  }
15992
16025
  this._isNegotiating$.next(true);
15993
- logger$16.debug("[RTCPeerConnectionController] Starting negotiation.");
16026
+ logger$17.debug("[RTCPeerConnectionController] Starting negotiation.");
15994
16027
  try {
15995
16028
  const { offerOptions } = this;
15996
- logger$16.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
16029
+ logger$17.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15997
16030
  await this.createOffer(offerOptions);
15998
16031
  } catch (error) {
15999
- logger$16.error("[RTCPeerConnectionController] Error during negotiation:", error);
16032
+ logger$17.error("[RTCPeerConnectionController] Error during negotiation:", error);
16000
16033
  this._errors$.next(toError(error));
16001
16034
  }
16002
16035
  }
@@ -16012,14 +16045,14 @@ var RTCPeerConnectionController = class extends Destroyable {
16012
16045
  let readyToConnect = status !== "failed";
16013
16046
  try {
16014
16047
  if (status === "received" && sdp) {
16015
- logger$16.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16048
+ logger$17.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16016
16049
  await this._setRemoteDescription({
16017
16050
  type: "answer",
16018
16051
  sdp
16019
16052
  });
16020
16053
  }
16021
16054
  } catch (error) {
16022
- logger$16.error("[RTCPeerConnectionController] Error updating answer status:", error);
16055
+ logger$17.error("[RTCPeerConnectionController] Error updating answer status:", error);
16023
16056
  this._errors$.next(toError(error));
16024
16057
  readyToConnect = false;
16025
16058
  } finally {
@@ -16038,7 +16071,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16038
16071
  await this.handleOfferReceived();
16039
16072
  break;
16040
16073
  case "failed":
16041
- logger$16.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16074
+ logger$17.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16042
16075
  break;
16043
16076
  case "sent":
16044
16077
  default:
@@ -16070,7 +16103,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16070
16103
  }
16071
16104
  await this.setupLocalTracks();
16072
16105
  const { answerOptions } = this;
16073
- logger$16.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16106
+ logger$17.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16074
16107
  await this.createAnswer(answerOptions);
16075
16108
  }
16076
16109
  async handleOfferReceived() {
@@ -16078,7 +16111,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16078
16111
  this._isNegotiating$.next(true);
16079
16112
  await this._setRemoteDescription(this.sdpInit);
16080
16113
  const { answerOptions } = this;
16081
- logger$16.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16114
+ logger$17.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16082
16115
  await this.createAnswer(answerOptions);
16083
16116
  }
16084
16117
  readyToConnect() {
@@ -16086,7 +16119,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16086
16119
  this.connectionTimer = setTimeout(() => {
16087
16120
  this.removeConnectionTimer();
16088
16121
  if (this.peerConnection?.connectionState !== "connected") {
16089
- logger$16.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16122
+ logger$17.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16090
16123
  this.iceGatheringController.restartICEGatheringWithRelayOnly();
16091
16124
  }
16092
16125
  }, this.connectionTimeout);
@@ -16108,14 +16141,14 @@ var RTCPeerConnectionController = class extends Destroyable {
16108
16141
  const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
16109
16142
  if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
16110
16143
  result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
16111
- logger$16.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16144
+ logger$17.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16112
16145
  preferredAudioCodecs,
16113
16146
  preferredVideoCodecs
16114
16147
  });
16115
16148
  }
16116
16149
  if (stereo) {
16117
16150
  result = enableStereoOpus(result);
16118
- logger$16.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16151
+ logger$17.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16119
16152
  }
16120
16153
  return Promise.resolve(result);
16121
16154
  }
@@ -16171,25 +16204,25 @@ var RTCPeerConnectionController = class extends Destroyable {
16171
16204
  ...this.peerConnection.getConfiguration(),
16172
16205
  iceTransportPolicy: "relay"
16173
16206
  });
16174
- logger$16.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16207
+ logger$17.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16175
16208
  } catch (error) {
16176
- logger$16.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16209
+ logger$17.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16177
16210
  }
16178
16211
  this.setupEventListeners();
16179
16212
  this._isNegotiating$.next(true);
16180
- logger$16.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16213
+ logger$17.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16181
16214
  try {
16182
16215
  const offer = await this.peerConnection.createOffer({ iceRestart: true });
16183
16216
  await this.setLocalDescription(offer);
16184
16217
  } catch (error) {
16185
- logger$16.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16218
+ logger$17.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16186
16219
  this._errors$.next(toError(error));
16187
16220
  this.negotiationEnded();
16188
16221
  if (policyChanged) this.restoreIceTransportPolicy();
16189
16222
  throw error;
16190
16223
  }
16191
16224
  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) => {
16192
- logger$16.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16225
+ logger$17.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16193
16226
  this.restoreIceTransportPolicy();
16194
16227
  });
16195
16228
  }
@@ -16199,9 +16232,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16199
16232
  ...this.peerConnection.getConfiguration(),
16200
16233
  iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
16201
16234
  });
16202
- logger$16.debug("[RTCPeerConnectionController] ICE transport policy restored");
16235
+ logger$17.debug("[RTCPeerConnectionController] ICE transport policy restored");
16203
16236
  } catch (error) {
16204
- logger$16.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16237
+ logger$17.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16205
16238
  }
16206
16239
  }
16207
16240
  /**
@@ -16213,13 +16246,13 @@ var RTCPeerConnectionController = class extends Destroyable {
16213
16246
  await this.setupRemoteTracks();
16214
16247
  }
16215
16248
  async setupLocalTracks() {
16216
- logger$16.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16249
+ logger$17.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16217
16250
  const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
16218
16251
  if (this.transceiverController?.useAddStream ?? false) {
16219
- logger$16.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16252
+ logger$17.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16220
16253
  this.peerConnection?.addStream(localStream);
16221
16254
  if (!this.isNegotiating) {
16222
- logger$16.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16255
+ logger$17.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16223
16256
  this.negotiationNeeded$.next();
16224
16257
  }
16225
16258
  return;
@@ -16235,7 +16268,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16235
16268
  const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
16236
16269
  await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
16237
16270
  } else {
16238
- logger$16.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16271
+ logger$17.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16239
16272
  this.peerConnection?.addTrack(track, localStream);
16240
16273
  }
16241
16274
  }
@@ -16252,7 +16285,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16252
16285
  async setupRemoteTracks() {
16253
16286
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
16254
16287
  this.peerConnection.ontrack = (event) => {
16255
- logger$16.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16288
+ logger$17.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16256
16289
  if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
16257
16290
  else {
16258
16291
  const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
@@ -16276,7 +16309,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16276
16309
  try {
16277
16310
  stream = await this.getUserMedia({ audio: constraints });
16278
16311
  } catch (error) {
16279
- logger$16.error("[RTCPeerConnectionController] Failed to re-acquire mic for pipeline restore:", error);
16312
+ logger$17.error("[RTCPeerConnectionController] Failed to re-acquire mic for pipeline restore:", error);
16280
16313
  this._errors$.next(toError(error));
16281
16314
  return;
16282
16315
  }
@@ -16298,7 +16331,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16298
16331
  try {
16299
16332
  this._localAudioPipeline = new LocalAudioPipeline();
16300
16333
  } catch (error) {
16301
- logger$16.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16334
+ logger$17.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16302
16335
  return null;
16303
16336
  }
16304
16337
  this.subscribeTo(this.localStreamController.localAudioTracks$, () => {
@@ -16320,7 +16353,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16320
16353
  try {
16321
16354
  await sender.replaceTrack(this._localAudioPipeline.outputTrack);
16322
16355
  } catch (error) {
16323
- logger$16.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16356
+ logger$17.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16324
16357
  }
16325
16358
  }
16326
16359
  /**
@@ -16336,9 +16369,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16336
16369
  try {
16337
16370
  const localStream = this.localStreamController.addTrack(track);
16338
16371
  this.peerConnection.addTrack(track, localStream);
16339
- logger$16.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16372
+ logger$17.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16340
16373
  } catch (error) {
16341
- logger$16.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16374
+ logger$17.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16342
16375
  this._errors$.next(toError(error));
16343
16376
  throw error;
16344
16377
  }
@@ -16355,15 +16388,15 @@ var RTCPeerConnectionController = class extends Destroyable {
16355
16388
  }
16356
16389
  const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
16357
16390
  if (!sender) {
16358
- logger$16.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16391
+ logger$17.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16359
16392
  return;
16360
16393
  }
16361
16394
  try {
16362
16395
  this.peerConnection.removeTrack(sender);
16363
16396
  this.localStreamController.removeTrack(trackId);
16364
- logger$16.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16397
+ logger$17.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16365
16398
  } catch (error) {
16366
- logger$16.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16399
+ logger$17.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16367
16400
  this._errors$.next(toError(error));
16368
16401
  throw error;
16369
16402
  }
@@ -16390,7 +16423,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16390
16423
  async replaceAudioTrackWithConstraints(constraints) {
16391
16424
  const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
16392
16425
  if (!senders || senders.length === 0) {
16393
- logger$16.warn("[RTCPeerConnectionController] No live audio sender to replace");
16426
+ logger$17.warn("[RTCPeerConnectionController] No live audio sender to replace");
16394
16427
  return;
16395
16428
  }
16396
16429
  for (const sender of senders) {
@@ -16408,7 +16441,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16408
16441
  const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
16409
16442
  await sender.replaceTrack(newTrack);
16410
16443
  this.localStreamController.addTrack(newTrack);
16411
- logger$16.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16444
+ logger$17.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16412
16445
  }
16413
16446
  }
16414
16447
  /**
@@ -16416,7 +16449,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16416
16449
  * Completes all observables to prevent memory leaks.
16417
16450
  */
16418
16451
  destroy() {
16419
- logger$16.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16452
+ logger$17.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16420
16453
  this.removeConnectionTimer();
16421
16454
  this._iceGatheringController?.destroy();
16422
16455
  this._localAudioPipeline?.destroy();
@@ -16442,7 +16475,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16442
16475
  }
16443
16476
  stopRemoteTracks() {
16444
16477
  this._remoteStream$.value?.getTracks().forEach((track) => {
16445
- logger$16.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16478
+ logger$17.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16446
16479
  track.stop();
16447
16480
  });
16448
16481
  }
@@ -16459,7 +16492,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16459
16492
  ...params,
16460
16493
  sdp: finalRemote
16461
16494
  };
16462
- logger$16.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16495
+ logger$17.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16463
16496
  return this.peerConnection.setRemoteDescription(answer);
16464
16497
  }
16465
16498
  };
@@ -16474,7 +16507,11 @@ function isVertoInviteMessage(value) {
16474
16507
  const msg = value;
16475
16508
  return msg.method === "verto.invite" && isObject(msg.params) && hasProperty(msg.params, "sdp") && hasProperty(msg.params, "callID");
16476
16509
  }
16477
- function isVertoByeMessage(value) {
16510
+ /**
16511
+ * Guards inbound `verto.bye` frames (server → client). Outbound bye messages are
16512
+ * built locally and never type-guarded, so only the inbound shape is asserted.
16513
+ */
16514
+ function isVertoByeInboundMessage(value) {
16478
16515
  if (!isVertoMethodMessage(value)) return false;
16479
16516
  return value.method === "verto.bye";
16480
16517
  }
@@ -16494,11 +16531,19 @@ function isVertoMediaParamsInnerParams(value) {
16494
16531
  function isVertoPingInnerParams(value) {
16495
16532
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.ping";
16496
16533
  }
16534
+ /**
16535
+ * Guards the params of an inbound `verto.bye` frame (top-level `callID`). Use
16536
+ * this to recognise the bye payload extracted by `vertoBye$`, e.g. when racing
16537
+ * it against a boolean answer/reject signal.
16538
+ */
16539
+ function isVertoByeInboundParamsGuard(value) {
16540
+ return isObject(value) && hasProperty(value, "callID") && typeof value.callID === "string";
16541
+ }
16497
16542
 
16498
16543
  //#endregion
16499
16544
  //#region src/managers/VertoManager.ts
16500
16545
  var import_cjs$15 = require_cjs();
16501
- const logger$15 = getLogger();
16546
+ const logger$16 = getLogger();
16502
16547
  /**
16503
16548
  * Decide what value goes on the `node_id` field of a `webrtc.verto` envelope.
16504
16549
  *
@@ -16554,7 +16599,7 @@ var WebRTCVertoManager = class extends VertoManager {
16554
16599
  try {
16555
16600
  await this.executeVerto(vertoModifyMessage);
16556
16601
  } catch (error) {
16557
- logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16602
+ logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16558
16603
  throw error;
16559
16604
  }
16560
16605
  }
@@ -16567,7 +16612,7 @@ var WebRTCVertoManager = class extends VertoManager {
16567
16612
  try {
16568
16613
  await this.executeVerto(vertoModifyMessage);
16569
16614
  } catch (error) {
16570
- logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16615
+ logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16571
16616
  throw error;
16572
16617
  }
16573
16618
  }
@@ -16620,7 +16665,7 @@ var WebRTCVertoManager = class extends VertoManager {
16620
16665
  if (event.member_id) this.setSelfIdIfNull(event.member_id);
16621
16666
  });
16622
16667
  this.subscribeTo(this.vertoMedia$, (event) => {
16623
- logger$15.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16668
+ logger$16.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16624
16669
  const { sdp, callID } = event;
16625
16670
  this.emitMainSignalingStatus(callID, "ringing");
16626
16671
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16629,7 +16674,7 @@ var WebRTCVertoManager = class extends VertoManager {
16629
16674
  });
16630
16675
  });
16631
16676
  this.subscribeTo(this.vertoAnswer$, (event) => {
16632
- logger$15.debug("[WebRTCManager] Received Verto answer event:", event);
16677
+ logger$16.debug("[WebRTCManager] Received Verto answer event:", event);
16633
16678
  const { sdp, callID } = event;
16634
16679
  this.emitMainSignalingStatus(callID, "connecting");
16635
16680
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16638,7 +16683,7 @@ var WebRTCVertoManager = class extends VertoManager {
16638
16683
  });
16639
16684
  });
16640
16685
  this.subscribeTo(this.vertoMediaParams$, (event) => {
16641
- logger$15.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16686
+ logger$16.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16642
16687
  const { mediaParams, callID } = event;
16643
16688
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
16644
16689
  const { audio, video } = mediaParams;
@@ -16652,7 +16697,7 @@ var WebRTCVertoManager = class extends VertoManager {
16652
16697
  timestamp: Date.now()
16653
16698
  });
16654
16699
  } catch (error) {
16655
- logger$15.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16700
+ logger$16.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16656
16701
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16657
16702
  }
16658
16703
  })();
@@ -16674,13 +16719,13 @@ var WebRTCVertoManager = class extends VertoManager {
16674
16719
  */
16675
16720
  setNodeIdIfNull(nodeId) {
16676
16721
  if (!this._nodeId$.value && nodeId) {
16677
- logger$15.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16722
+ logger$16.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16678
16723
  this._nodeId$.next(nodeId);
16679
16724
  }
16680
16725
  }
16681
16726
  setSelfIdIfNull(selfId) {
16682
16727
  if (!this._selfId$.value && selfId) {
16683
- logger$15.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16728
+ logger$16.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16684
16729
  this._selfId$.next(selfId);
16685
16730
  }
16686
16731
  }
@@ -16689,7 +16734,7 @@ var WebRTCVertoManager = class extends VertoManager {
16689
16734
  const vertoPongMessage = VertoPong({ ...vertoPing });
16690
16735
  await this.executeVerto(vertoPongMessage);
16691
16736
  } catch (error) {
16692
- logger$15.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16737
+ logger$16.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16693
16738
  this.onError?.(new VertoPongError(error));
16694
16739
  }
16695
16740
  }
@@ -16699,7 +16744,7 @@ var WebRTCVertoManager = class extends VertoManager {
16699
16744
  if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
16700
16745
  if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
16701
16746
  } catch (error) {
16702
- logger$15.warn("[WebRTCManager] Error updating media constraints:", error);
16747
+ logger$16.warn("[WebRTCManager] Error updating media constraints:", error);
16703
16748
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16704
16749
  throw error;
16705
16750
  }
@@ -16729,20 +16774,20 @@ var WebRTCVertoManager = class extends VertoManager {
16729
16774
  try {
16730
16775
  const pc = this.mainPeerConnection.peerConnection;
16731
16776
  if (!pc) {
16732
- logger$15.warn("[WebRTCManager] No peer connection for keyframe request");
16777
+ logger$16.warn("[WebRTCManager] No peer connection for keyframe request");
16733
16778
  return;
16734
16779
  }
16735
16780
  const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
16736
16781
  if (!videoReceiver) {
16737
- logger$15.warn("[WebRTCManager] No video receiver for keyframe request");
16782
+ logger$16.warn("[WebRTCManager] No video receiver for keyframe request");
16738
16783
  return;
16739
16784
  }
16740
16785
  if (typeof videoReceiver.requestKeyFrame === "function") {
16741
16786
  videoReceiver.requestKeyFrame();
16742
- logger$15.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16743
- } else logger$15.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16787
+ logger$16.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16788
+ } else logger$16.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16744
16789
  } catch (error) {
16745
- logger$15.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16790
+ logger$16.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16746
16791
  }
16747
16792
  }
16748
16793
  /**
@@ -16760,13 +16805,13 @@ var WebRTCVertoManager = class extends VertoManager {
16760
16805
  try {
16761
16806
  const controller = this.mainPeerConnection;
16762
16807
  if (!controller.peerConnection) {
16763
- logger$15.warn("[WebRTCManager] No peer connection for ICE restart");
16808
+ logger$16.warn("[WebRTCManager] No peer connection for ICE restart");
16764
16809
  return;
16765
16810
  }
16766
16811
  await controller.triggerIceRestart(relayOnly);
16767
- logger$15.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16812
+ logger$16.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16768
16813
  } catch (error) {
16769
- logger$15.error("[WebRTCManager] ICE restart failed:", error);
16814
+ logger$16.error("[WebRTCManager] ICE restart failed:", error);
16770
16815
  throw error;
16771
16816
  }
16772
16817
  }
@@ -16784,13 +16829,13 @@ var WebRTCVertoManager = class extends VertoManager {
16784
16829
  const entries = Array.from(this._rtcPeerConnectionsMap.entries());
16785
16830
  for (const [id, controller] of entries) try {
16786
16831
  if (!controller.peerConnection) {
16787
- logger$15.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16832
+ logger$16.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16788
16833
  continue;
16789
16834
  }
16790
16835
  await controller.triggerIceRestart(relayOnly);
16791
- logger$15.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16836
+ logger$16.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16792
16837
  } catch (error) {
16793
- logger$15.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16838
+ logger$16.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16794
16839
  }
16795
16840
  }
16796
16841
  /**
@@ -16802,7 +16847,7 @@ var WebRTCVertoManager = class extends VertoManager {
16802
16847
  requestKeyframeAll() {
16803
16848
  for (const [id, controller] of this._rtcPeerConnectionsMap) {
16804
16849
  if (controller.isScreenShare) {
16805
- logger$15.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16850
+ logger$16.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16806
16851
  continue;
16807
16852
  }
16808
16853
  try {
@@ -16812,10 +16857,10 @@ var WebRTCVertoManager = class extends VertoManager {
16812
16857
  if (!videoReceiver) continue;
16813
16858
  if (typeof videoReceiver.requestKeyFrame === "function") {
16814
16859
  videoReceiver.requestKeyFrame();
16815
- logger$15.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16860
+ logger$16.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16816
16861
  }
16817
16862
  } catch (error) {
16818
- logger$15.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16863
+ logger$16.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16819
16864
  }
16820
16865
  }
16821
16866
  }
@@ -16832,7 +16877,7 @@ var WebRTCVertoManager = class extends VertoManager {
16832
16877
  return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16833
16878
  }
16834
16879
  get vertoBye$() {
16835
- return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16880
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeInboundMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16836
16881
  }
16837
16882
  get vertoAttach$() {
16838
16883
  return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
@@ -16876,7 +16921,7 @@ var WebRTCVertoManager = class extends VertoManager {
16876
16921
  default:
16877
16922
  }
16878
16923
  } catch (error) {
16879
- logger$15.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16924
+ logger$16.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16880
16925
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16881
16926
  if (vertoMethod === "verto.modify") this.onModifyFailed?.();
16882
16927
  }
@@ -16891,7 +16936,7 @@ var WebRTCVertoManager = class extends VertoManager {
16891
16936
  sdp
16892
16937
  });
16893
16938
  } catch (error) {
16894
- logger$15.warn("[WebRTCManager] Error processing modify response:", error);
16939
+ logger$16.warn("[WebRTCManager] Error processing modify response:", error);
16895
16940
  const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
16896
16941
  this.onError?.(modifyError);
16897
16942
  }
@@ -16901,7 +16946,7 @@ var WebRTCVertoManager = class extends VertoManager {
16901
16946
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callId);
16902
16947
  if (!rtcPeerConnController) {
16903
16948
  const signalingError = new DependencyError(`Cannot emit signaling status, RTCPeerConnectionController not found for callID: ${callId}`);
16904
- logger$15.error("[WebRTCManager] Failed to emit signaling status:", {
16949
+ logger$16.error("[WebRTCManager] Failed to emit signaling status:", {
16905
16950
  callId,
16906
16951
  status,
16907
16952
  signalingError
@@ -16917,7 +16962,7 @@ var WebRTCVertoManager = class extends VertoManager {
16917
16962
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
16918
16963
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
16919
16964
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
16920
- logger$15.debug("[WebRTCManager] Verto invite response:", {
16965
+ logger$16.debug("[WebRTCManager] Verto invite response:", {
16921
16966
  callId,
16922
16967
  memberId,
16923
16968
  response
@@ -16927,14 +16972,14 @@ var WebRTCVertoManager = class extends VertoManager {
16927
16972
  if (callId) {
16928
16973
  this.webRtcCallSession.addCallId(callId);
16929
16974
  this.attachManager.attach(this.buildAttachableCall(callId));
16930
- } else logger$15.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16975
+ } else logger$16.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16931
16976
  nodeId: this.nodeId,
16932
16977
  callId
16933
16978
  });
16934
- logger$15.info("[WebRTCManager] Verto invite successful");
16935
- logger$15.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16979
+ logger$16.info("[WebRTCManager] Verto invite successful");
16980
+ logger$16.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16936
16981
  } else {
16937
- logger$15.error("[WebRTCManager] Verto invite failed:", response);
16982
+ logger$16.error("[WebRTCManager] Verto invite failed:", response);
16938
16983
  const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
16939
16984
  this.onError?.(inviteError);
16940
16985
  }
@@ -16979,17 +17024,17 @@ var WebRTCVertoManager = class extends VertoManager {
16979
17024
  if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
16980
17025
  }
16981
17026
  async handleInboundAnswer(rtcPeerConnController) {
16982
- logger$15.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
17027
+ logger$16.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16983
17028
  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);
16984
17029
  if (vertoByeOrAccepted === null) {
16985
- logger$15.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
17030
+ logger$16.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16986
17031
  return;
16987
17032
  }
16988
- if (isVertoByeMessage(vertoByeOrAccepted)) {
16989
- logger$15.info("[WebRTCManager] Inbound call ended by remote before answer.");
17033
+ if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
17034
+ logger$16.info("[WebRTCManager] Inbound call ended by remote before answer.");
16990
17035
  this.callSession?.destroy();
16991
17036
  } else if (!vertoByeOrAccepted) {
16992
- logger$15.info("[WebRTCManager] Inbound call rejected by user.");
17037
+ logger$16.info("[WebRTCManager] Inbound call rejected by user.");
16993
17038
  try {
16994
17039
  await this.bye("USER_BUSY");
16995
17040
  } finally {
@@ -16997,19 +17042,19 @@ var WebRTCVertoManager = class extends VertoManager {
16997
17042
  this.callSession?.destroy();
16998
17043
  }
16999
17044
  } else {
17000
- logger$15.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
17045
+ logger$16.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
17001
17046
  const answerOptions = this.webRtcCallSession.answerMediaOptions;
17002
17047
  try {
17003
17048
  await rtcPeerConnController.acceptInbound(answerOptions);
17004
17049
  } catch (error) {
17005
- logger$15.error("[WebRTCManager] Error creating inbound answer:", error);
17050
+ logger$16.error("[WebRTCManager] Error creating inbound answer:", error);
17006
17051
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
17007
17052
  }
17008
17053
  }
17009
17054
  }
17010
17055
  setupVertoAttachHandler() {
17011
17056
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
17012
- logger$15.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
17057
+ logger$16.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
17013
17058
  const { callID } = vertoAttach;
17014
17059
  await this.attachManager.attach({
17015
17060
  nodeId: this.nodeId ?? void 0,
@@ -17081,17 +17126,17 @@ var WebRTCVertoManager = class extends VertoManager {
17081
17126
  };
17082
17127
  }
17083
17128
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
17084
- logger$15.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17129
+ logger$16.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17085
17130
  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);
17086
17131
  if (vertoByeOrAccepted === null) {
17087
- logger$15.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17132
+ logger$16.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17088
17133
  return;
17089
17134
  }
17090
- if (isVertoByeMessage(vertoByeOrAccepted)) {
17091
- logger$15.info("[WebRTCManager] Call ended before answer was sent.");
17135
+ if (isVertoByeInboundParamsGuard(vertoByeOrAccepted)) {
17136
+ logger$16.info("[WebRTCManager] Call ended before answer was sent.");
17092
17137
  this.callSession?.destroy();
17093
17138
  } else if (!vertoByeOrAccepted) {
17094
- logger$15.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17139
+ logger$16.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17095
17140
  try {
17096
17141
  await this.bye("USER_BUSY");
17097
17142
  } finally {
@@ -17099,14 +17144,14 @@ var WebRTCVertoManager = class extends VertoManager {
17099
17144
  this.callSession?.destroy();
17100
17145
  }
17101
17146
  } else {
17102
- logger$15.debug("[WebRTCManager] Call accepted, sending answer");
17147
+ logger$16.debug("[WebRTCManager] Call accepted, sending answer");
17103
17148
  try {
17104
17149
  this.emitMainSignalingStatus(rtcPeerConnectionController.id, "connecting");
17105
17150
  await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
17106
17151
  await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
17107
17152
  await this.attachManager.attach(this.buildAttachableCall());
17108
17153
  } catch (error) {
17109
- logger$15.error("[WebRTCManager] Error sending Verto answer:", error);
17154
+ logger$16.error("[WebRTCManager] Error sending Verto answer:", error);
17110
17155
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
17111
17156
  await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
17112
17157
  }
@@ -17205,10 +17250,10 @@ var WebRTCVertoManager = class extends VertoManager {
17205
17250
  });
17206
17251
  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$)));
17207
17252
  this._screenShareStatus$.next("started");
17208
- logger$15.info("[WebRTCManager] Screen share started successfully.");
17253
+ logger$16.info("[WebRTCManager] Screen share started successfully.");
17209
17254
  return rtcPeerConnController.id;
17210
17255
  } catch (error) {
17211
- logger$15.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17256
+ logger$16.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17212
17257
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
17213
17258
  if (rtcPeerConnController) rtcPeerConnController.destroy();
17214
17259
  this._screenShareStatus$.next("none");
@@ -17227,9 +17272,9 @@ var WebRTCVertoManager = class extends VertoManager {
17227
17272
  if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
17228
17273
  }
17229
17274
  async removeScreenMedia() {
17230
- if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$15.warn("[WebRTCManager] No active screen share to stop.");
17275
+ if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$16.warn("[WebRTCManager] No active screen share to stop.");
17231
17276
  if (!this._screenShareId) {
17232
- logger$15.debug("[WebRTCManager] No screen share peer connection found.");
17277
+ logger$16.debug("[WebRTCManager] No screen share peer connection found.");
17233
17278
  return;
17234
17279
  }
17235
17280
  this._screenShareStatus$.next("stopping");
@@ -17258,7 +17303,7 @@ var WebRTCVertoManager = class extends VertoManager {
17258
17303
  dialogParams: this.dialogParams(rtcPeerConnController)
17259
17304
  }));
17260
17305
  } catch (error) {
17261
- logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17306
+ logger$16.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17262
17307
  throw error;
17263
17308
  }
17264
17309
  }
@@ -17276,7 +17321,7 @@ var WebRTCVertoManager = class extends VertoManager {
17276
17321
  try {
17277
17322
  await this.executeVerto(vertoInfoMessage);
17278
17323
  } catch (error) {
17279
- logger$15.warn("[WebRTCManager] Error sending DTMF digits:", error);
17324
+ logger$16.warn("[WebRTCManager] Error sending DTMF digits:", error);
17280
17325
  throw error;
17281
17326
  }
17282
17327
  }
@@ -17287,10 +17332,10 @@ var WebRTCVertoManager = class extends VertoManager {
17287
17332
  action: "transfer"
17288
17333
  });
17289
17334
  try {
17290
- logger$15.debug("[WebRTCManager] Transferring call with options:", options);
17335
+ logger$16.debug("[WebRTCManager] Transferring call with options:", options);
17291
17336
  await this.executeVerto(message);
17292
17337
  } catch (error) {
17293
- logger$15.error("[WebRTCManager] Error transferring call:", error);
17338
+ logger$16.error("[WebRTCManager] Error transferring call:", error);
17294
17339
  throw error;
17295
17340
  }
17296
17341
  }
@@ -17307,7 +17352,7 @@ var WebRTCVertoManager = class extends VertoManager {
17307
17352
  //#endregion
17308
17353
  //#region src/controllers/RemoteAudioMeter.ts
17309
17354
  var import_cjs$14 = require_cjs();
17310
- const logger$14 = getLogger();
17355
+ const logger$15 = getLogger();
17311
17356
  /**
17312
17357
  * Read-only audio level meter for a remote MediaStream. Attaches an
17313
17358
  * AnalyserNode to a MediaStreamAudioSourceNode so it observes the stream
@@ -17342,7 +17387,7 @@ var RemoteAudioMeter = class extends Destroyable {
17342
17387
  try {
17343
17388
  this._source.disconnect();
17344
17389
  } catch (error) {
17345
- logger$14.debug("[RemoteAudioMeter] source disconnect warning:", error);
17390
+ logger$15.debug("[RemoteAudioMeter] source disconnect warning:", error);
17346
17391
  }
17347
17392
  this._source = null;
17348
17393
  this._stream = null;
@@ -17359,7 +17404,7 @@ var RemoteAudioMeter = class extends Destroyable {
17359
17404
  this._source = null;
17360
17405
  }
17361
17406
  this._audioContext.close().catch((error) => {
17362
- logger$14.debug("[RemoteAudioMeter] audio context close warning:", error);
17407
+ logger$15.debug("[RemoteAudioMeter] audio context close warning:", error);
17363
17408
  });
17364
17409
  super.destroy();
17365
17410
  }
@@ -17378,7 +17423,7 @@ var RemoteAudioMeter = class extends Destroyable {
17378
17423
  //#endregion
17379
17424
  //#region src/controllers/RTCStatsMonitor.ts
17380
17425
  var import_cjs$13 = require_cjs();
17381
- const logger$13 = getLogger();
17426
+ const logger$14 = getLogger();
17382
17427
  const DEFAULT_POLLING_INTERVAL_MS = 1e3;
17383
17428
  const DEFAULT_BASELINE_SAMPLES = 10;
17384
17429
  const DEFAULT_NO_AUDIO_PACKET_THRESHOLD_MS = 2e3;
@@ -17468,9 +17513,9 @@ var RTCStatsMonitor = class extends Destroyable {
17468
17513
  const now = Date.now();
17469
17514
  this.lastAudioPacketChangeTime = now;
17470
17515
  this.lastVideoPacketChangeTime = now;
17471
- logger$13.debug("[RTCStatsMonitor] Starting stats monitoring");
17516
+ logger$14.debug("[RTCStatsMonitor] Starting stats monitoring");
17472
17517
  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) => {
17473
- logger$13.warn("[RTCStatsMonitor] Failed to get stats:", err);
17518
+ logger$14.warn("[RTCStatsMonitor] Failed to get stats:", err);
17474
17519
  return import_cjs$13.EMPTY;
17475
17520
  }))), (0, import_cjs$13.filter)(() => this.running), (0, import_cjs$13.map)((report) => this.extractSample(report))), (sample$1) => this._sample$.next(sample$1));
17476
17521
  this.subscribeTo(this._sample$.pipe((0, import_cjs$13.take)(this.baselineSampleCount), (0, import_cjs$13.toArray)(), (0, import_cjs$13.map)((samples) => ({
@@ -17478,7 +17523,7 @@ var RTCStatsMonitor = class extends Destroyable {
17478
17523
  jitter: samples.reduce((s, b) => s + b.audioJitter, 0) / samples.length,
17479
17524
  ready: true
17480
17525
  }))), (baseline) => {
17481
- logger$13.debug(`[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`);
17526
+ logger$14.debug(`[RTCStatsMonitor] Baseline established: rtt=${baseline.rtt.toFixed(1)}ms, jitter=${baseline.jitter.toFixed(1)}ms`);
17482
17527
  this._baseline$.next(baseline);
17483
17528
  });
17484
17529
  this.subscribeTo(this._sample$.pipe((0, import_cjs$13.scan)((acc, sample$1) => ({
@@ -17515,10 +17560,10 @@ var RTCStatsMonitor = class extends Destroyable {
17515
17560
  stop() {
17516
17561
  if (!this.running) return;
17517
17562
  this.running = false;
17518
- logger$13.debug("[RTCStatsMonitor] Stopping stats monitoring");
17563
+ logger$14.debug("[RTCStatsMonitor] Stopping stats monitoring");
17519
17564
  }
17520
17565
  destroy() {
17521
- logger$13.debug("[RTCStatsMonitor] Destroying RTCStatsMonitor");
17566
+ logger$14.debug("[RTCStatsMonitor] Destroying RTCStatsMonitor");
17522
17567
  this.stop();
17523
17568
  super.destroy();
17524
17569
  }
@@ -17647,7 +17692,7 @@ var RTCStatsMonitor = class extends Destroyable {
17647
17692
  //#endregion
17648
17693
  //#region src/managers/CallRecoveryManager.ts
17649
17694
  var import_cjs$12 = require_cjs();
17650
- const logger$12 = getLogger();
17695
+ const logger$13 = getLogger();
17651
17696
  const DEFAULT_DEBOUNCE_TIME_MS = 2e3;
17652
17697
  const DEFAULT_COOLDOWN_MS = 1e4;
17653
17698
  const DEFAULT_ICE_GRACE_PERIOD_MS = 3e3;
@@ -17744,10 +17789,10 @@ var CallRecoveryManager = class extends Destroyable {
17744
17789
  */
17745
17790
  async requestIceRestart() {
17746
17791
  if (this._recoveryState$.value === "recovering") {
17747
- logger$12.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
17792
+ logger$13.info("CallRecoveryManager: manual ICE restart skipped — recovery already in progress");
17748
17793
  return;
17749
17794
  }
17750
- logger$12.info("CallRecoveryManager: manual ICE restart requested");
17795
+ logger$13.info("CallRecoveryManager: manual ICE restart requested");
17751
17796
  this.transitionTo("recovering");
17752
17797
  await this.executeIceRestart(false);
17753
17798
  this.startCooldown();
@@ -17763,7 +17808,7 @@ var CallRecoveryManager = class extends Destroyable {
17763
17808
  * WebSocket reconnect or call state recovers to 'connected'.
17764
17809
  */
17765
17810
  reset() {
17766
- logger$12.info("CallRecoveryManager: resetting counters");
17811
+ logger$13.info("CallRecoveryManager: resetting counters");
17767
17812
  this._attemptCount = 0;
17768
17813
  this._keyframeBurstCount = 0;
17769
17814
  this._keyframeBurstStart = 0;
@@ -17778,7 +17823,7 @@ var CallRecoveryManager = class extends Destroyable {
17778
17823
  */
17779
17824
  notifyModifyFailed() {
17780
17825
  if (this._recoveryState$.value === "cooldown" || this._recoveryState$.value === "idle") {
17781
- logger$12.info("CallRecoveryManager: verto.modify failed — re-entering recovery");
17826
+ logger$13.info("CallRecoveryManager: verto.modify failed — re-entering recovery");
17782
17827
  this._cooldownUntil = 0;
17783
17828
  this.transitionTo("idle");
17784
17829
  this.pushTrigger({
@@ -17802,7 +17847,7 @@ var CallRecoveryManager = class extends Destroyable {
17802
17847
  reason: `bandwidth ${bitrateKbps}kbps below threshold ${this._config.degradationBitrateThreshold}kbps`,
17803
17848
  timestamp: Date.now()
17804
17849
  });
17805
- logger$12.warn(`CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`);
17850
+ logger$13.warn(`CallRecoveryManager: disabling video — bandwidth ${bitrateKbps}kbps < ${this._config.degradationBitrateThreshold}kbps`);
17806
17851
  } else if (wasConstrained && bitrateKbps >= this._config.degradationRecoveryThreshold) {
17807
17852
  this._bandwidthConstrained$.next(false);
17808
17853
  this._callbacks.enableVideo();
@@ -17811,7 +17856,7 @@ var CallRecoveryManager = class extends Destroyable {
17811
17856
  reason: `bandwidth ${bitrateKbps}kbps recovered above ${this._config.degradationRecoveryThreshold}kbps`,
17812
17857
  timestamp: Date.now()
17813
17858
  });
17814
- logger$12.info(`CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`);
17859
+ logger$13.info(`CallRecoveryManager: restoring video — bandwidth ${bitrateKbps}kbps >= ${this._config.degradationRecoveryThreshold}kbps`);
17815
17860
  }
17816
17861
  }
17817
17862
  /**
@@ -17830,14 +17875,14 @@ var CallRecoveryManager = class extends Destroyable {
17830
17875
  handleWebSocketReconnect() {
17831
17876
  const pcState = this._callbacks.getPeerConnectionState();
17832
17877
  if (pcState === "connected" || pcState === "completed") {
17833
- logger$12.info("CallRecoveryManager: signal-only reconnect — peer connection still alive");
17878
+ logger$13.info("CallRecoveryManager: signal-only reconnect — peer connection still alive");
17834
17879
  this.emitEvent({
17835
17880
  action: "signal_reconnect",
17836
17881
  reason: "WebSocket reconnected, peer connection still connected",
17837
17882
  timestamp: Date.now()
17838
17883
  });
17839
17884
  } else {
17840
- logger$12.info("CallRecoveryManager: full reconnect — peer connection also down");
17885
+ logger$13.info("CallRecoveryManager: full reconnect — peer connection also down");
17841
17886
  this.emitEvent({
17842
17887
  action: "full_reconnect",
17843
17888
  reason: "WebSocket reconnected, peer connection not connected — ICE restart needed",
@@ -17861,7 +17906,7 @@ var CallRecoveryManager = class extends Destroyable {
17861
17906
  }), (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$))), {
17862
17907
  next: () => {},
17863
17908
  error: (err) => {
17864
- logger$12.error("CallRecoveryManager: pipeline error", err);
17909
+ logger$13.error("CallRecoveryManager: pipeline error", err);
17865
17910
  this.transitionTo("idle");
17866
17911
  }
17867
17912
  });
@@ -17888,27 +17933,27 @@ var CallRecoveryManager = class extends Destroyable {
17888
17933
  reason: `no packet loss for ${this._config.packetLossRecoveryDelaySec}s — restoring video`,
17889
17934
  timestamp: Date.now()
17890
17935
  });
17891
- logger$12.info(`CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`);
17936
+ logger$13.info(`CallRecoveryManager: restoring video — no packet loss for ${this._config.packetLossRecoveryDelaySec}s`);
17892
17937
  });
17893
17938
  }
17894
17939
  passGateChecks(signalingReady) {
17895
17940
  if (this._callbacks.isNegotiating()) {
17896
- logger$12.debug("CallRecoveryManager: gate blocked — negotiation in progress");
17941
+ logger$13.debug("CallRecoveryManager: gate blocked — negotiation in progress");
17897
17942
  this.transitionTo("idle");
17898
17943
  return false;
17899
17944
  }
17900
17945
  if (!signalingReady) {
17901
- logger$12.debug("CallRecoveryManager: gate blocked — signaling not ready");
17946
+ logger$13.debug("CallRecoveryManager: gate blocked — signaling not ready");
17902
17947
  this.transitionTo("idle");
17903
17948
  return false;
17904
17949
  }
17905
17950
  if (!this._callbacks.isCallConnected()) {
17906
- logger$12.debug("CallRecoveryManager: gate blocked — call not connected");
17951
+ logger$13.debug("CallRecoveryManager: gate blocked — call not connected");
17907
17952
  this.transitionTo("idle");
17908
17953
  return false;
17909
17954
  }
17910
17955
  if (this.isCooldownActive()) {
17911
- logger$12.debug("CallRecoveryManager: gate blocked — cooldown active");
17956
+ logger$13.debug("CallRecoveryManager: gate blocked — cooldown active");
17912
17957
  this.transitionTo("cooldown");
17913
17958
  return false;
17914
17959
  }
@@ -17919,9 +17964,9 @@ var CallRecoveryManager = class extends Destroyable {
17919
17964
  }
17920
17965
  executeTieredRecovery(trigger) {
17921
17966
  this.transitionTo("recovering");
17922
- logger$12.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
17967
+ logger$13.info(`CallRecoveryManager: starting tiered recovery — source=${trigger.source} detail=${trigger.detail}`);
17923
17968
  return (0, import_cjs$12.from)(this.runTiers(trigger)).pipe((0, import_cjs$12.tap)(() => this.startCooldown()), (0, import_cjs$12.catchError)((err) => {
17924
- logger$12.error("CallRecoveryManager: tiered recovery failed", err);
17969
+ logger$13.error("CallRecoveryManager: tiered recovery failed", err);
17925
17970
  this.startCooldown();
17926
17971
  return import_cjs$12.EMPTY;
17927
17972
  }));
@@ -17929,7 +17974,7 @@ var CallRecoveryManager = class extends Destroyable {
17929
17974
  async runTiers(trigger) {
17930
17975
  this.executeKeyframe(trigger.detail);
17931
17976
  if (trigger.issueType && DEGRADATION_ONLY_ISSUES.has(trigger.issueType)) {
17932
- logger$12.debug(`CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`);
17977
+ logger$13.debug(`CallRecoveryManager: degradation-only issue (${trigger.issueType}) — Tier 1 only, skipping ICE restart`);
17933
17978
  return;
17934
17979
  }
17935
17980
  if (this._attemptCount < this._config.maxAttempts) {
@@ -17946,13 +17991,13 @@ var CallRecoveryManager = class extends Destroyable {
17946
17991
  maxAttempts: this._config.maxAttempts,
17947
17992
  timestamp: Date.now()
17948
17993
  });
17949
- logger$12.warn("CallRecoveryManager: max recovery attempts reached");
17994
+ logger$13.warn("CallRecoveryManager: max recovery attempts reached");
17950
17995
  }
17951
17996
  }
17952
17997
  executeKeyframe(reason) {
17953
17998
  const now = Date.now();
17954
17999
  if (now < this._keyframeCooldownUntil) {
17955
- logger$12.debug("CallRecoveryManager: keyframe request skipped — cooldown active");
18000
+ logger$13.debug("CallRecoveryManager: keyframe request skipped — cooldown active");
17956
18001
  return;
17957
18002
  }
17958
18003
  if (now - this._keyframeBurstStart > this._config.keyframeBurstWindowMs) {
@@ -17961,7 +18006,7 @@ var CallRecoveryManager = class extends Destroyable {
17961
18006
  }
17962
18007
  if (this._keyframeBurstCount >= this._config.keyframeMaxBurst) {
17963
18008
  this._keyframeCooldownUntil = now + this._config.keyframeCooldownMs;
17964
- logger$12.debug(`CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`);
18009
+ logger$13.debug(`CallRecoveryManager: keyframe burst limit reached (${this._config.keyframeMaxBurst}), cooldown until ${this._keyframeCooldownUntil}`);
17965
18010
  return;
17966
18011
  }
17967
18012
  this._keyframeBurstCount += 1;
@@ -17971,12 +18016,12 @@ var CallRecoveryManager = class extends Destroyable {
17971
18016
  reason,
17972
18017
  timestamp: now
17973
18018
  });
17974
- logger$12.debug(`CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`);
18019
+ logger$13.debug(`CallRecoveryManager: keyframe requested (burst ${this._keyframeBurstCount}/${this._config.keyframeMaxBurst})`);
17975
18020
  }
17976
18021
  async executeIceRestart(relayOnly) {
17977
18022
  this._attemptCount += 1;
17978
18023
  const tier = relayOnly ? "Tier 3 (relay-only)" : "Tier 2 (standard)";
17979
- logger$12.info(`CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`);
18024
+ logger$13.info(`CallRecoveryManager: ${tier} ICE restart — attempt ${this._attemptCount}/${this._config.maxAttempts}`);
17980
18025
  this.emitEvent({
17981
18026
  action: "reinvite_started",
17982
18027
  reason: `${tier} ICE restart`,
@@ -17993,7 +18038,7 @@ var CallRecoveryManager = class extends Destroyable {
17993
18038
  maxAttempts: this._config.maxAttempts,
17994
18039
  timestamp: Date.now()
17995
18040
  });
17996
- logger$12.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);
18041
+ logger$13.info(`CallRecoveryManager: ${tier} ICE restart succeeded`);
17997
18042
  this._attemptCount = 0;
17998
18043
  return true;
17999
18044
  }
@@ -18004,7 +18049,7 @@ var CallRecoveryManager = class extends Destroyable {
18004
18049
  maxAttempts: this._config.maxAttempts,
18005
18050
  timestamp: Date.now()
18006
18051
  });
18007
- logger$12.warn(`CallRecoveryManager: ${tier} ICE restart failed`);
18052
+ logger$13.warn(`CallRecoveryManager: ${tier} ICE restart failed`);
18008
18053
  return false;
18009
18054
  } catch {
18010
18055
  this.emitEvent({
@@ -18014,7 +18059,7 @@ var CallRecoveryManager = class extends Destroyable {
18014
18059
  maxAttempts: this._config.maxAttempts,
18015
18060
  timestamp: Date.now()
18016
18061
  });
18017
- logger$12.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);
18062
+ logger$13.warn(`CallRecoveryManager: ${tier} ICE restart timed out`);
18018
18063
  return false;
18019
18064
  }
18020
18065
  }
@@ -18035,7 +18080,7 @@ var CallRecoveryManager = class extends Destroyable {
18035
18080
  transitionTo(state) {
18036
18081
  const prev = this._recoveryState$.value;
18037
18082
  if (prev !== state) {
18038
- logger$12.debug(`CallRecoveryManager: state ${prev} -> ${state}`);
18083
+ logger$13.debug(`CallRecoveryManager: state ${prev} -> ${state}`);
18039
18084
  this._recoveryState$.next(state);
18040
18085
  }
18041
18086
  }
@@ -18138,7 +18183,13 @@ function mosToQualityLevel(mos) {
18138
18183
  //#endregion
18139
18184
  //#region src/core/entities/Call.ts
18140
18185
  var import_cjs$11 = require_cjs();
18141
- const logger$11 = getLogger();
18186
+ const logger$12 = getLogger();
18187
+ /**
18188
+ * Verto method for setting member layout positions. Its gateway DTO requires a
18189
+ * `targets` array whose entries are `{ target, position }` (NOT bare targets),
18190
+ * so {@link WebRTCCall.buildMethodParams} special-cases it. See issue #19400.
18191
+ */
18192
+ const POSITION_SET_METHOD = "call.member.position.set";
18142
18193
  /**
18143
18194
  * Ratio between the critical and warning RTT spike multipliers.
18144
18195
  * Warning threshold = baseline * warningMultiplier (default 3x)
@@ -18156,7 +18207,7 @@ const fromDestinationParams = (destination) => {
18156
18207
  });
18157
18208
  return params;
18158
18209
  } catch (error) {
18159
- logger$11.warn(`Failed to parse destination URI: ${destination}`, error);
18210
+ logger$12.warn(`Failed to parse destination URI: ${destination}`, error);
18160
18211
  return {};
18161
18212
  }
18162
18213
  };
@@ -18346,7 +18397,7 @@ var WebRTCCall = class extends Destroyable {
18346
18397
  * @throws {JSONRPCError} If the RPC call returns an error.
18347
18398
  */
18348
18399
  async executeMethod(target, method, args) {
18349
- const params = this.buildMethodParams(target, args);
18400
+ const params = this.buildMethodParams(target, args, method);
18350
18401
  const request = buildRPCRequest({
18351
18402
  method,
18352
18403
  params
@@ -18356,16 +18407,20 @@ var WebRTCCall = class extends Destroyable {
18356
18407
  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);
18357
18408
  return response;
18358
18409
  } catch (error) {
18359
- logger$11.error(`[Call] Error executing method ${method} with params`, params, error);
18410
+ logger$12.error(`[Call] Error executing method ${method} with params`, params, error);
18360
18411
  throw error;
18361
18412
  }
18362
18413
  }
18363
- buildMethodParams(target, args) {
18414
+ buildMethodParams(target, args, method) {
18364
18415
  const self = {
18365
18416
  node_id: this.nodeId ?? "",
18366
18417
  call_id: this.id,
18367
18418
  member_id: this.vertoManager.selfId ?? ""
18368
18419
  };
18420
+ if (method === POSITION_SET_METHOD) return {
18421
+ ...args,
18422
+ self
18423
+ };
18369
18424
  if (typeof target === "object") return {
18370
18425
  ...args,
18371
18426
  self,
@@ -18559,9 +18614,9 @@ var WebRTCCall = class extends Destroyable {
18559
18614
  */
18560
18615
  initResilienceSubsystems() {
18561
18616
  const pc = this.rtcPeerConnection;
18562
- logger$11.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
18617
+ logger$12.debug(`[Call] initResilienceSubsystems: pc=${pc ? "exists" : "undefined"}, connectionState=${pc?.connectionState}`);
18563
18618
  if (!pc) {
18564
- logger$11.warn("[Call] No peer connection available, skipping resilience init");
18619
+ logger$12.warn("[Call] No peer connection available, skipping resilience init");
18565
18620
  return;
18566
18621
  }
18567
18622
  try {
@@ -18596,14 +18651,14 @@ var WebRTCCall = class extends Destroyable {
18596
18651
  disableVideo: () => {
18597
18652
  try {
18598
18653
  this.vertoManager.muteMainVideoInputDevice();
18599
- logger$11.debug("[Call] Recovery manager disabled video");
18654
+ logger$12.debug("[Call] Recovery manager disabled video");
18600
18655
  } catch {
18601
- logger$11.debug("[Call] Recovery manager failed to disable video");
18656
+ logger$12.debug("[Call] Recovery manager failed to disable video");
18602
18657
  }
18603
18658
  },
18604
18659
  enableVideo: () => {
18605
18660
  this.vertoManager.unmuteMainVideoInputDevice().catch(() => {
18606
- logger$11.debug("[Call] Recovery manager failed to enable video");
18661
+ logger$12.debug("[Call] Recovery manager failed to enable video");
18607
18662
  });
18608
18663
  },
18609
18664
  isNegotiating: () => this.vertoManager.mainPeerConnection.isNegotiating,
@@ -18653,7 +18708,7 @@ var WebRTCCall = class extends Destroyable {
18653
18708
  this.subscribeTo(this._recoveryManager.recoveryEvent$, (event) => {
18654
18709
  this._recoveryEvent$.next(event);
18655
18710
  if (event.action === "max_attempts_reached") {
18656
- logger$11.warn("[Call] All recovery attempts exhausted, terminating call");
18711
+ logger$12.warn("[Call] All recovery attempts exhausted, terminating call");
18657
18712
  this.emitError({
18658
18713
  kind: "network",
18659
18714
  fatal: true,
@@ -18673,13 +18728,13 @@ var WebRTCCall = class extends Destroyable {
18673
18728
  else if (event.type === "online") this._recoveryManager?.handleWebSocketReconnect();
18674
18729
  });
18675
18730
  this.subscribeTo(this.clientSession.authenticated$.pipe((0, import_cjs$11.skip)(1), (0, import_cjs$11.filter)(Boolean)), () => {
18676
- logger$11.debug("[Call] WebSocket reconnected — notifying recovery manager");
18731
+ logger$12.debug("[Call] WebSocket reconnected — notifying recovery manager");
18677
18732
  this._recoveryManager?.handleWebSocketReconnect();
18678
18733
  });
18679
18734
  this._statsMonitor.start();
18680
- logger$11.debug("[Call] Resilience subsystems initialized for call", this.id);
18735
+ logger$12.debug("[Call] Resilience subsystems initialized for call", this.id);
18681
18736
  } catch (error) {
18682
- logger$11.warn("[Call] Failed to initialize resilience subsystems:", error);
18737
+ logger$12.warn("[Call] Failed to initialize resilience subsystems:", error);
18683
18738
  }
18684
18739
  }
18685
18740
  /**
@@ -18762,19 +18817,19 @@ var WebRTCCall = class extends Destroyable {
18762
18817
  }
18763
18818
  isCallSessionEvent(event) {
18764
18819
  try {
18765
- logger$11.debug("[Call] Checking if event is for this call session:", event);
18820
+ logger$12.debug("[Call] Checking if event is for this call session:", event);
18766
18821
  const callId = getValueFrom(event, "params.params.callID") ?? getValueFrom(event, "params.call_id");
18767
18822
  const roomSessionId = getValueFrom(event, "params.room_session_id");
18768
- logger$11.debug(`[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`);
18823
+ logger$12.debug(`[Call] Extracted session identifiers callID: ${callId} and roomSessionID: ${roomSessionId} from event:`);
18769
18824
  return callId === this.id || !!callId && this.callEventsManager.isCallIdValid(callId) || !!roomSessionId && this.callEventsManager.isRoomSessionIdValid(roomSessionId);
18770
18825
  } catch (error) {
18771
- logger$11.error("[Call] Error checking if event is for this call session:", error);
18826
+ logger$12.error("[Call] Error checking if event is for this call session:", error);
18772
18827
  return false;
18773
18828
  }
18774
18829
  }
18775
18830
  get callSessionEvents$() {
18776
18831
  return this.cachedObservable("callSessionEvents$", () => this.clientSession.signalingEvent$.pipe((0, import_cjs$11.filter)((event) => this.isCallSessionEvent(event)), (0, import_cjs$11.tap)((event) => {
18777
- logger$11.debug("[Call] Received call session event:", event);
18832
+ logger$12.debug("[Call] Received call session event:", event);
18778
18833
  }), (0, import_cjs$11.takeUntil)(this.destroyed$), (0, import_cjs$11.share)()));
18779
18834
  }
18780
18835
  /** Observable of call-updated events. */
@@ -18844,16 +18899,16 @@ var WebRTCCall = class extends Destroyable {
18844
18899
  this._customSubscriptions.set(eventType, filtered$);
18845
18900
  }, (error) => {
18846
18901
  this._customSubscriptions.delete(eventType);
18847
- logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed, not caching:`, error);
18902
+ logger$12.warn(`[Call] verto.subscribe for '${eventType}' failed, not caching:`, error);
18848
18903
  });
18849
18904
  this._customSubscriptions.set(eventType, filtered$);
18850
18905
  return filtered$;
18851
18906
  }
18852
18907
  get webrtcMessages$() {
18853
- 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)()));
18908
+ 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)()));
18854
18909
  }
18855
18910
  get callEvent$() {
18856
- 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)()));
18911
+ 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)()));
18857
18912
  }
18858
18913
  get layoutEvent$() {
18859
18914
  return this.cachedObservable("layoutEvent$", () => this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params")));
@@ -18931,10 +18986,21 @@ var WebRTCCall = class extends Destroyable {
18931
18986
  return this.deferEmission(this._answered$.asObservable());
18932
18987
  }
18933
18988
  /**
18934
- * Sets the call layout and participant positions.
18989
+ * Sets the call layout and, optionally, individual participant positions.
18990
+ *
18991
+ * The gateway `call.layout.set` DTO has **no** `positions` member, so when
18992
+ * `positions` is provided this method issues a `call.member.position.set`
18993
+ * request per member (via {@link Participant.setPosition}, which keys each
18994
+ * position by that member's own call context) alongside `call.layout.set`
18995
+ * (issue #19400, Flag #6).
18996
+ *
18997
+ * **These operations are NOT atomic.** The layout is applied first, then each
18998
+ * member position sequentially, so members may briefly flash into their
18999
+ * default slots before being moved to the requested positions.
18935
19000
  *
18936
19001
  * @param layout - Layout name (must be one of {@link layouts}).
18937
- * @param positions - Map of member IDs to {@link VideoPosition} values.
19002
+ * @param positions - Optional map of member IDs to {@link VideoPosition} values.
19003
+ * When omitted or empty, only the layout is changed.
18938
19004
  * @throws {InvalidParams} If the layout is not in the available {@link layouts}.
18939
19005
  *
18940
19006
  * @example
@@ -18947,10 +19013,17 @@ var WebRTCCall = class extends Destroyable {
18947
19013
  async setLayout(layout, positions) {
18948
19014
  if (!this.layouts.includes(layout)) throw new InvalidParams(`Layout ${layout} is not available in the current call layouts: ${this.layouts.join(", ")}`);
18949
19015
  const selfId = await (0, import_cjs$11.firstValueFrom)(this.selfId$.pipe((0, import_cjs$11.filter)((id) => id !== null)));
18950
- await this.executeMethod(selfId, "call.layout.set", {
18951
- layout,
18952
- positions
18953
- });
19016
+ await this.executeMethod(selfId, "call.layout.set", { layout });
19017
+ const positionEntries = Object.entries(positions ?? {});
19018
+ if (positionEntries.length === 0) return;
19019
+ for (const [memberId, position] of positionEntries) {
19020
+ const participant = this.participants.find((p) => p.id === memberId);
19021
+ if (!participant) {
19022
+ logger$12.warn(`[Call] setLayout: member ${memberId} not found in participants; skipping position ${position}`);
19023
+ continue;
19024
+ }
19025
+ await participant.setPosition(position);
19026
+ }
18954
19027
  }
18955
19028
  /**
18956
19029
  * Transfers the call to another destination.
@@ -18982,7 +19055,7 @@ var WebRTCCall = class extends Destroyable {
18982
19055
  setLocalMicrophoneGain(value) {
18983
19056
  const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18984
19057
  if (!pipeline) {
18985
- logger$11.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
19058
+ logger$12.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
18986
19059
  return;
18987
19060
  }
18988
19061
  const percent = Math.max(0, Math.min(200, value));
@@ -19027,7 +19100,7 @@ var WebRTCCall = class extends Destroyable {
19027
19100
  enablePushToTalk() {
19028
19101
  const pipeline = this.vertoManager.ensureLocalAudioPipeline();
19029
19102
  if (!pipeline) {
19030
- logger$11.warn("[Call] enablePushToTalk: audio pipeline unavailable");
19103
+ logger$12.warn("[Call] enablePushToTalk: audio pipeline unavailable");
19031
19104
  return;
19032
19105
  }
19033
19106
  pipeline.setPTTActive(false);
@@ -19190,7 +19263,7 @@ var CallFactory = class {
19190
19263
  //#endregion
19191
19264
  //#region src/behaviors/Collection.ts
19192
19265
  var import_cjs$10 = require_cjs();
19193
- const logger$10 = getLogger();
19266
+ const logger$11 = getLogger();
19194
19267
  var Fetcher = class {
19195
19268
  constructor(endpoint, params, http) {
19196
19269
  this.endpoint = endpoint;
@@ -19214,7 +19287,7 @@ var Fetcher = class {
19214
19287
  this.hasMore = !!this.nextUrl;
19215
19288
  return result.data.filter(this.filter).map(this.mapper);
19216
19289
  }
19217
- logger$10.error("Failed to fetch entity");
19290
+ logger$11.error("Failed to fetch entity");
19218
19291
  return [];
19219
19292
  }
19220
19293
  async id(v) {
@@ -19290,7 +19363,7 @@ var EntityCollection = class extends Destroyable {
19290
19363
  this._hasMore$.next(this.fetchController.hasMore ?? false);
19291
19364
  this._loading$.next(false);
19292
19365
  } catch (error) {
19293
- logger$10.error(`Failed to fetch initial collection data`, error);
19366
+ logger$11.error(`Failed to fetch initial collection data`, error);
19294
19367
  this._hasMore$.next(this.fetchController.hasMore ?? false);
19295
19368
  this._loading$.next(false);
19296
19369
  this.onError?.(new CollectionFetchError("fetchMore", error));
@@ -19304,7 +19377,7 @@ var EntityCollection = class extends Destroyable {
19304
19377
  if (data) this.upsertData(data);
19305
19378
  return data;
19306
19379
  } catch (error) {
19307
- logger$10.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
19380
+ logger$11.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
19308
19381
  this._loading$.next(false);
19309
19382
  this.onError?.(new CollectionFetchError(`tryFetch(${String(key)})`, error));
19310
19383
  }
@@ -19553,13 +19626,13 @@ var Address = class extends Destroyable {
19553
19626
  //#endregion
19554
19627
  //#region src/core/utils.ts
19555
19628
  var import_cjs$8 = require_cjs();
19556
- const logger$9 = getLogger();
19629
+ const logger$10 = getLogger();
19557
19630
  const isRPCConnectResult = (e) => {
19558
- logger$9.debug("isRPCConnectResult check:", e);
19631
+ logger$10.debug("isRPCConnectResult check:", e);
19559
19632
  if (!e || typeof e !== "object") return false;
19560
19633
  const result = e;
19561
19634
  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";
19562
- logger$9.debug("isRPCConnectResult check result:", is);
19635
+ logger$10.debug("isRPCConnectResult check result:", is);
19563
19636
  return is;
19564
19637
  };
19565
19638
  var PendingRPC = class PendingRPC {
@@ -19568,7 +19641,7 @@ var PendingRPC = class PendingRPC {
19568
19641
  }
19569
19642
  constructor(request, responses$, options) {
19570
19643
  this.id = v4_default();
19571
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`);
19644
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}: method:${request.method}] Creating PendingRPC`);
19572
19645
  this.request = request;
19573
19646
  const timeoutMs = options?.timeoutMs ?? PendingRPC.defaultTimeoutMs;
19574
19647
  const signal = options?.signal;
@@ -19594,22 +19667,22 @@ var PendingRPC = class PendingRPC {
19594
19667
  isSettled = true;
19595
19668
  if (response.error) {
19596
19669
  const rpcError = new JSONRPCError(response.error.code, response.error.message, response.error.data, void 0, request.id);
19597
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
19670
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
19598
19671
  reject(rpcError);
19599
19672
  } else {
19600
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
19673
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
19601
19674
  resolve(response);
19602
19675
  }
19603
19676
  subscription.unsubscribe();
19604
19677
  },
19605
19678
  error: (error) => {
19606
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`, error);
19679
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with error:`, error);
19607
19680
  isSettled = true;
19608
19681
  reject(error);
19609
19682
  subscription.unsubscribe();
19610
19683
  },
19611
19684
  complete: () => {
19612
- logger$9.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
19685
+ logger$10.debug(`[PendingRPC(${this.id}) request:${request.id}] Observable completed`);
19613
19686
  if (!isSettled) reject(new RPCTimeoutError(request.id, timeoutMs));
19614
19687
  subscription.unsubscribe();
19615
19688
  }
@@ -19630,7 +19703,7 @@ var PendingRPC = class PendingRPC {
19630
19703
  //#endregion
19631
19704
  //#region src/managers/ClientSessionManager.ts
19632
19705
  var import_cjs$7 = require_cjs();
19633
- const logger$8 = getLogger();
19706
+ const logger$9 = getLogger();
19634
19707
  const getAddressSearchURI = (options) => {
19635
19708
  const to = options.to?.split("?")[0];
19636
19709
  const from$9 = options.from?.startsWith("subscriber://") ? options.from.replace("subscriber://", "") : options.from;
@@ -19728,7 +19801,7 @@ var ClientSessionManager = class extends Destroyable {
19728
19801
  try {
19729
19802
  return await this.transport.execute(request, options);
19730
19803
  } catch (error) {
19731
- logger$8.debug("[Session] Execute Error", error);
19804
+ logger$9.debug("[Session] Execute Error", error);
19732
19805
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
19733
19806
  throw error;
19734
19807
  }
@@ -19742,13 +19815,13 @@ var ClientSessionManager = class extends Destroyable {
19742
19815
  return true;
19743
19816
  }
19744
19817
  setupMessageHandlers() {
19745
- logger$8.debug("[Session] Setting up message handlers");
19818
+ logger$9.debug("[Session] Setting up message handlers");
19746
19819
  this.subscribeTo(this.authStateEvent$, async (authStateEvent) => {
19747
- logger$8.debug("[Session] Authorization state event received:", authStateEvent);
19820
+ logger$9.debug("[Session] Authorization state event received:", authStateEvent);
19748
19821
  try {
19749
19822
  await this.updateAuthorizationStateInStorage(authStateEvent.authorization_state);
19750
19823
  } catch (error) {
19751
- logger$8.error("[Session] Failed to handle authorization state update:", error);
19824
+ logger$9.error("[Session] Failed to handle authorization state update:", error);
19752
19825
  this._errors$.next(new AuthStateHandlerError(error));
19753
19826
  }
19754
19827
  });
@@ -19756,29 +19829,29 @@ var ClientSessionManager = class extends Destroyable {
19756
19829
  if (this._authState$.value.kind === "authenticated") this._authState$.next({ kind: "unauthenticated" });
19757
19830
  });
19758
19831
  this.subscribeTo(this.transport.connectionStatus$.pipe((0, import_cjs$7.filter)((status) => status === "connected"), (0, import_cjs$7.exhaustMap)(() => {
19759
- logger$8.debug("[Session] Connection established, initiating authentication");
19832
+ logger$9.debug("[Session] Connection established, initiating authentication");
19760
19833
  return (0, import_cjs$7.from)(this.authenticate()).pipe((0, import_cjs$7.catchError)((error) => {
19761
19834
  this.handleAuthenticationError(error).catch((err) => {
19762
- logger$8.error("[Session] Error handling authentication failure:", err);
19835
+ logger$9.error("[Session] Error handling authentication failure:", err);
19763
19836
  });
19764
19837
  return import_cjs$7.EMPTY;
19765
19838
  }));
19766
19839
  })), void 0);
19767
19840
  this.subscribeTo(this.vertoInvite$, async (invite) => {
19768
- logger$8.debug("[Session] Verto invite received:", invite);
19841
+ logger$9.debug("[Session] Verto invite received:", invite);
19769
19842
  try {
19770
19843
  await this.createInboundCall(invite);
19771
19844
  } catch (error) {
19772
- logger$8.error("[Session] Error handling Verto invite:", error);
19845
+ logger$9.error("[Session] Error handling Verto invite:", error);
19773
19846
  this._errors$.next(new VertoInviteHandlerError(error));
19774
19847
  }
19775
19848
  });
19776
19849
  this.subscribeTo(this.vertoAttach$, async (attach) => {
19777
- logger$8.debug("[Session] Verto attach received:", attach);
19850
+ logger$9.debug("[Session] Verto attach received:", attach);
19778
19851
  try {
19779
19852
  await this.handleVertoAttach(attach);
19780
19853
  } catch (error) {
19781
- logger$8.error("[Session] Error handling Verto attach:", error);
19854
+ logger$9.error("[Session] Error handling Verto attach:", error);
19782
19855
  this._errors$.next(new VertoAttachHandlerError(error));
19783
19856
  }
19784
19857
  });
@@ -19788,36 +19861,36 @@ var ClientSessionManager = class extends Destroyable {
19788
19861
  const storedState = await this.storage.getItem(this.authorizationStateKey);
19789
19862
  this.authorizationState$.next(storedState ?? void 0);
19790
19863
  } catch (error) {
19791
- logger$8.error("Failed to retrieve authorization state from storage:", error);
19864
+ logger$9.error("Failed to retrieve authorization state from storage:", error);
19792
19865
  this.authorizationState$.next(void 0);
19793
19866
  }
19794
19867
  }
19795
19868
  async updateAuthorizationStateInStorage(authorizationState) {
19796
19869
  if (!authorizationState) {
19797
- logger$8.debug("[Session] Removing authorization state from storage");
19870
+ logger$9.debug("[Session] Removing authorization state from storage");
19798
19871
  try {
19799
19872
  await this.storage.removeItem(this.authorizationStateKey);
19800
19873
  this.authorizationState$.next(void 0);
19801
19874
  } catch (error) {
19802
- logger$8.error("Failed to remove authorization state from storage:", error);
19875
+ logger$9.error("Failed to remove authorization state from storage:", error);
19803
19876
  throw error;
19804
19877
  }
19805
19878
  return;
19806
19879
  }
19807
19880
  try {
19808
- logger$8.debug("[Session] Updating authorization state in storage");
19881
+ logger$9.debug("[Session] Updating authorization state in storage");
19809
19882
  await this.storage.setItem(this.authorizationStateKey, authorizationState);
19810
19883
  this.authorizationState$.next(authorizationState);
19811
19884
  } catch (error) {
19812
- logger$8.error("Failed to retrieve authorization state from storage:", error);
19885
+ logger$9.error("Failed to retrieve authorization state from storage:", error);
19813
19886
  throw error;
19814
19887
  }
19815
19888
  }
19816
19889
  get authStateEvent$() {
19817
19890
  return this.cachedObservable("authStateEvent$", () => this.signalingEvent$.pipe((0, import_cjs$7.tap)((msg) => {
19818
- logger$8.debug("[Session] Received incoming message:", msg);
19891
+ logger$9.debug("[Session] Received incoming message:", msg);
19819
19892
  }), filterAs(isSignalwireAuthorizationStateMetadata, "params"), (0, import_cjs$7.tap)((event) => {
19820
- logger$8.debug("[Session] Authorization state event received:", event.authorization_state);
19893
+ logger$9.debug("[Session] Authorization state event received:", event.authorization_state);
19821
19894
  })));
19822
19895
  }
19823
19896
  get signalingEvent$() {
@@ -19855,42 +19928,72 @@ var ClientSessionManager = class extends Destroyable {
19855
19928
  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 })));
19856
19929
  }
19857
19930
  async handleAuthenticationError(error) {
19858
- logger$8.error("Authentication error:", error);
19931
+ logger$9.error("Authentication error:", error);
19859
19932
  const isRecoverableAuthError = error instanceof JSONRPCError && (error.code === RPC_ERROR_REQUESTER_VALIDATION_FAILED || error.code === RPC_ERROR_INVALID_PARAMS || error.code === RPC_ERROR_AUTHENTICATION_FAILED);
19860
19933
  const hasStoredState = await (0, import_cjs$7.firstValueFrom)(this.authorizationState$.pipe((0, import_cjs$7.take)(1))) !== void 0;
19861
19934
  if (isRecoverableAuthError && hasStoredState) {
19862
- logger$8.debug("[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh");
19935
+ logger$9.debug("[Session] Recoverable auth error — cleaning up stored state and reconnecting fresh");
19863
19936
  try {
19864
19937
  await this.cleanupStoredConnectionParams();
19865
19938
  } catch (cleanupError) {
19866
- logger$8.error("Failed to cleanup stored connection params:", cleanupError);
19939
+ logger$9.error("Failed to cleanup stored connection params:", cleanupError);
19867
19940
  } finally {
19868
19941
  this.transport.reconnect();
19869
19942
  }
19870
19943
  } else this._errors$.next(error);
19871
19944
  }
19945
+ /**
19946
+ * Clear the resume state (authorization_state + protocol) only.
19947
+ *
19948
+ * This is the stale-auth-state recovery helper used by handleAuthError:
19949
+ * the server rejected a reconnect, so the resume state is discarded and a
19950
+ * fresh connect follows. Attach records are deliberately preserved — the
19951
+ * session lives on through the reconnect and reattachCalls() needs the
19952
+ * stored call references afterwards. Do NOT add detachAll() here.
19953
+ *
19954
+ * For public teardown (disconnect/destroy), use {@link teardownSessionState}
19955
+ * instead, which clears the attach records as well.
19956
+ */
19872
19957
  async cleanupStoredConnectionParams() {
19873
19958
  await this.transport.setProtocol(void 0);
19874
19959
  await this.updateAuthorizationStateInStorage(void 0);
19875
19960
  this._authorization$.next(void 0);
19876
19961
  }
19962
+ /**
19963
+ * Public-teardown helper for disconnect()/destroy(). Clears the resume
19964
+ * state (authorization_state + protocol) AND the attach records as one
19965
+ * atomic unit.
19966
+ *
19967
+ * The two stores are coupled: the backend only honors attach records
19968
+ * within the session identified by the resume state, so ending the
19969
+ * session must clear both. Clearing one without the other strands records
19970
+ * no future session can honor (disconnect) or revives a session the
19971
+ * developer explicitly ended (destroy).
19972
+ *
19973
+ * Distinct from {@link cleanupStoredConnectionParams}, which keeps the
19974
+ * attach records for the stale-auth-state recovery path.
19975
+ */
19976
+ async teardownSessionState() {
19977
+ await this.cleanupStoredConnectionParams();
19978
+ await this.attachManager.detachAll();
19979
+ }
19877
19980
  async updateAuthState(authorization_state) {
19878
19981
  try {
19879
19982
  await this.storage.setItem(this.authorizationStateKey, authorization_state);
19880
19983
  } catch (error) {
19881
- logger$8.error("Failed to update authorization state in storage:", error);
19984
+ logger$9.error("Failed to update authorization state in storage:", error);
19882
19985
  this._errors$.next(new AuthStateHandlerError(error));
19883
19986
  }
19884
19987
  }
19885
19988
  async reauthenticate(token, dpopToken, options) {
19886
- logger$8.debug("[Session] Re-authenticating session");
19989
+ logger$9.debug("[Session] Re-authenticating session");
19887
19990
  try {
19888
19991
  let resolvedDpopToken = dpopToken;
19889
19992
  if (!resolvedDpopToken && this.dpopManager?.initialized) try {
19890
19993
  resolvedDpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
19891
19994
  } catch (error) {
19892
19995
  if (this.clientBound) throw error;
19893
- logger$8.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
19996
+ logger$9.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
19894
19997
  }
19895
19998
  const request = RPCReauthenticate({
19896
19999
  project: this._authorization$.value?.project_id ?? "",
@@ -19898,24 +20001,24 @@ var ClientSessionManager = class extends Destroyable {
19898
20001
  ...resolvedDpopToken ? { dpop_token: resolvedDpopToken } : {}
19899
20002
  });
19900
20003
  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) => {
19901
- logger$8.error("[Session] Re-authentication RPC failed:", err);
20004
+ logger$9.error("[Session] Re-authentication RPC failed:", err);
19902
20005
  throw err;
19903
20006
  })));
19904
20007
  if (options?.clientBound) this._wasClientBound = true;
19905
- logger$8.debug("[Session] Re-authentication successful, updating stored auth state");
20008
+ logger$9.debug("[Session] Re-authentication successful, updating stored auth state");
19906
20009
  } catch (error) {
19907
- logger$8.error("[Session] Re-authentication failed:", error);
20010
+ logger$9.error("[Session] Re-authentication failed:", error);
19908
20011
  this._errors$.next(new AuthStateHandlerError(error));
19909
20012
  throw error;
19910
20013
  }
19911
20014
  }
19912
20015
  async authenticate() {
19913
- logger$8.debug("[Session] Starting authentication process");
20016
+ logger$9.debug("[Session] Starting authentication process");
19914
20017
  const persistedParams = await (0, import_cjs$7.firstValueFrom)((0, import_cjs$7.combineLatest)({
19915
20018
  protocol: this.transport.protocol$,
19916
20019
  authorization_state: this.authorizationState$
19917
20020
  }).pipe((0, import_cjs$7.take)(1)));
19918
- logger$8.debug("[Session] Persisted params:\n", {
20021
+ logger$9.debug("[Session] Persisted params:\n", {
19919
20022
  protocol: persistedParams.protocol,
19920
20023
  authStateLength: persistedParams.authorization_state?.length
19921
20024
  });
@@ -19923,16 +20026,16 @@ var ClientSessionManager = class extends Destroyable {
19923
20026
  const storedToken = this.getCredential().token;
19924
20027
  const isReconnect = hasReconnectState && storedToken;
19925
20028
  let dpopToken;
19926
- if (isReconnect) logger$8.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
20029
+ if (isReconnect) logger$9.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
19927
20030
  else if (this.onBeforeReconnect && this.clientBound) {
19928
- logger$8.debug("[Session] Refreshing credentials before fresh connect");
20031
+ logger$9.debug("[Session] Refreshing credentials before fresh connect");
19929
20032
  await this.onBeforeReconnect();
19930
20033
  }
19931
20034
  if ((!isReconnect || this.clientBound) && this.dpopManager?.initialized) try {
19932
20035
  dpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.connect" });
19933
20036
  } catch (error) {
19934
20037
  if (this.clientBound) throw error;
19935
- logger$8.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
20038
+ logger$9.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
19936
20039
  }
19937
20040
  const rpcConnectRequest = RPCConnect({
19938
20041
  authentication: isReconnect ? { jwt_token: storedToken } : this.authentication,
@@ -19949,12 +20052,12 @@ var ClientSessionManager = class extends Destroyable {
19949
20052
  } : {}
19950
20053
  });
19951
20054
  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)(() => {
19952
- logger$8.debug("[Session] Response passed filter, processing authentication result");
20055
+ logger$9.debug("[Session] Response passed filter, processing authentication result");
19953
20056
  }), (0, import_cjs$7.take)(1), (0, import_cjs$7.catchError)((err) => {
19954
- logger$8.error("[Session] Authentication RPC failed:", err);
20057
+ logger$9.error("[Session] Authentication RPC failed:", err);
19955
20058
  throw err;
19956
20059
  })));
19957
- logger$8.debug("[Session] Processing authentication result:", {
20060
+ logger$9.debug("[Session] Processing authentication result:", {
19958
20061
  hasProtocol: !!response.protocol,
19959
20062
  hasAuthorization: !!response.authorization,
19960
20063
  hasIceServers: !!response.ice_servers
@@ -19963,12 +20066,12 @@ var ClientSessionManager = class extends Destroyable {
19963
20066
  this._authorization$.next(response.authorization);
19964
20067
  this._iceServers$.next(response.ice_servers ?? []);
19965
20068
  this._authState$.next({ kind: "authenticated" });
19966
- logger$8.debug("[Session] Authentication completed successfully");
20069
+ logger$9.debug("[Session] Authentication completed successfully");
19967
20070
  }
19968
20071
  async disconnect() {
19969
20072
  this.transport.disconnect();
19970
20073
  this._authState$.next({ kind: "unauthenticated" });
19971
- await this.cleanupStoredConnectionParams();
20074
+ await this.teardownSessionState();
19972
20075
  }
19973
20076
  async createInboundCall(invite) {
19974
20077
  const callSession = await this.createCall({
@@ -19999,11 +20102,11 @@ var ClientSessionManager = class extends Destroyable {
19999
20102
  async handleVertoAttach(attach) {
20000
20103
  const { callID } = attach;
20001
20104
  if (callID in this._calls$.value) {
20002
- logger$8.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
20105
+ logger$9.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
20003
20106
  return;
20004
20107
  }
20005
20108
  const storedOptions = await this.attachManager.consumePendingAttachment(callID);
20006
- logger$8.debug(`[Session] Creating reattached call for callID: ${callID}`);
20109
+ logger$9.debug(`[Session] Creating reattached call for callID: ${callID}`);
20007
20110
  const callSession = await this.createCall({
20008
20111
  nodeId: attach.node_id,
20009
20112
  callId: callID,
@@ -20034,7 +20137,7 @@ var ClientSessionManager = class extends Destroyable {
20034
20137
  });
20035
20138
  return callSession;
20036
20139
  } catch (error) {
20037
- logger$8.error("[Session] Error creating outbound call:", error);
20140
+ logger$9.error("[Session] Error creating outbound call:", error);
20038
20141
  callSession?.destroy();
20039
20142
  const callError = new CallCreateError(error instanceof import_cjs$7.TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
20040
20143
  this._errors$.next(callError);
@@ -20052,7 +20155,7 @@ var ClientSessionManager = class extends Destroyable {
20052
20155
  address = this._directory.get(addressId);
20053
20156
  if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
20054
20157
  } catch {
20055
- logger$8.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
20158
+ logger$9.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
20056
20159
  }
20057
20160
  const callSession = this.callFactory.createCall(address, { ...options });
20058
20161
  this.subscribeTo(callSession.status$.pipe((0, import_cjs$7.filter)((status) => status === "destroyed"), (0, import_cjs$7.take)(1)), () => {
@@ -20061,7 +20164,7 @@ var ClientSessionManager = class extends Destroyable {
20061
20164
  });
20062
20165
  return callSession;
20063
20166
  } catch (error) {
20064
- logger$8.error("[Session] Error creating call session:", error);
20167
+ logger$9.error("[Session] Error creating call session:", error);
20065
20168
  throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
20066
20169
  }
20067
20170
  }
@@ -20110,7 +20213,7 @@ const isString = (obj) => typeof obj === "string";
20110
20213
  //#endregion
20111
20214
  //#region src/managers/ConversationsManager.ts
20112
20215
  var import_cjs$6 = require_cjs();
20113
- const logger$7 = getLogger();
20216
+ const logger$8 = getLogger();
20114
20217
  var ConversationMessagesFetcher = class extends Fetcher {
20115
20218
  constructor(groupId, http) {
20116
20219
  super(`/api/fabric/conversations/${groupId}/messages`, "page_size=100", http);
@@ -20150,13 +20253,13 @@ var ConversationsManager = class {
20150
20253
  }
20151
20254
  throw new ConversationError("Join Failed - Unexpected response");
20152
20255
  } catch (error) {
20153
- logger$7.error("[ConversationsManager] Failed to join conversation:", error);
20256
+ logger$8.error("[ConversationsManager] Failed to join conversation:", error);
20154
20257
  throw error;
20155
20258
  }
20156
20259
  }
20157
20260
  async getConversationMessageCollection(addressId) {
20158
20261
  const groupId = this.groupIds.get(addressId) ?? await this.join(addressId);
20159
- 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));
20262
+ 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));
20160
20263
  }
20161
20264
  async sendText(text, destinationAddressId) {
20162
20265
  const groupId = this.groupIds.get(destinationAddressId) ?? await this.join(destinationAddressId);
@@ -20173,7 +20276,7 @@ var ConversationsManager = class {
20173
20276
  })).ok) return;
20174
20277
  throw new ConversationError("Send Text Failed - Unexpected response");
20175
20278
  } catch (error) {
20176
- logger$7.error("[ConversationsManager] Failed to send text message:", error);
20279
+ logger$8.error("[ConversationsManager] Failed to send text message:", error);
20177
20280
  throw error;
20178
20281
  }
20179
20282
  }
@@ -20182,7 +20285,7 @@ var ConversationsManager = class {
20182
20285
  //#endregion
20183
20286
  //#region src/managers/DeviceTokenManager.ts
20184
20287
  var import_cjs$5 = require_cjs();
20185
- const logger$6 = getLogger();
20288
+ const logger$7 = getLogger();
20186
20289
  /**
20187
20290
  * Resolves the token expiry timestamp (epoch seconds) using a 3-tier priority chain:
20188
20291
  * 1. `data.expires_at` — server-provided absolute timestamp
@@ -20192,7 +20295,7 @@ const logger$6 = getLogger();
20192
20295
  function resolveExpiresAt(data) {
20193
20296
  if (data.expires_at) return data.expires_at;
20194
20297
  if (data.expires_in) return Math.floor(Date.now() / 1e3) + data.expires_in;
20195
- logger$6.warn("[DeviceToken] Could not determine token expiry, using default");
20298
+ logger$7.warn("[DeviceToken] Could not determine token expiry, using default");
20196
20299
  return Math.floor(Date.now() / 1e3) + DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
20197
20300
  }
20198
20301
  /**
@@ -20225,11 +20328,12 @@ var DeviceTokenManager = class extends Destroyable {
20225
20328
  this.getCredential = getCredential;
20226
20329
  this._currentToken$ = this.createBehaviorSubject(null);
20227
20330
  this._refreshInProgress = false;
20331
+ this._paused = false;
20228
20332
  this._effectiveExpireIn = DEVICE_TOKEN_DEFAULT_EXPIRE_IN;
20229
20333
  this.subscribeTo(this._currentToken$.pipe((0, import_cjs$5.filter)(Boolean), (0, import_cjs$5.switchMap)((tokenData) => {
20230
20334
  const expiresAt = resolveExpiresAt(tokenData);
20231
20335
  const refreshIn = Math.max(expiresAt * 1e3 - Date.now() - DEVICE_TOKEN_REFRESH_BUFFER_MS, 1e3);
20232
- logger$6.debug(`[DeviceToken] Scheduling Client Bound SAT refresh in ${refreshIn}ms`);
20336
+ logger$7.debug(`[DeviceToken] Scheduling Client Bound SAT refresh in ${refreshIn}ms`);
20233
20337
  return (0, import_cjs$5.timer)(refreshIn);
20234
20338
  })), () => {
20235
20339
  this.executeRefresh();
@@ -20243,7 +20347,12 @@ var DeviceTokenManager = class extends Destroyable {
20243
20347
  * Activates the Client Bound SAT flow when the user's token has
20244
20348
  * `sat:refresh` scope.
20245
20349
  *
20246
- * Steps:
20350
+ * Returns an {@link ActivationResult} indicating whether the manager
20351
+ * took ownership of refresh duties. The caller must use the `activated`
20352
+ * boolean to decide whether to keep its own refresh path armed — when
20353
+ * `activated` is `false`, the caller is responsible for refresh.
20354
+ *
20355
+ * Steps on success:
20247
20356
  * 1. Check user's `sat_claims` for `sat:refresh` scope
20248
20357
  * 2. Call `/api/fabric/subscriber/devices/token` with a DPoP proof
20249
20358
  * 3. Reauthenticate the session with the Client Bound SAT + DPoP proof
@@ -20252,32 +20361,51 @@ var DeviceTokenManager = class extends Destroyable {
20252
20361
  async activate(user, session, updateCredential) {
20253
20362
  const { satClaims } = user;
20254
20363
  if (!satClaims?.scope?.includes(SAT_REFRESH_SCOPE)) {
20255
- logger$6.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
20256
- return;
20364
+ logger$7.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
20365
+ return {
20366
+ activated: false,
20367
+ reason: "no-scope"
20368
+ };
20257
20369
  }
20258
20370
  this._session = session;
20259
20371
  this._updateCredential = updateCredential;
20260
20372
  try {
20373
+ const cached = this._currentToken$.value;
20374
+ if (cached && this.isTokenFresh(cached)) {
20375
+ logger$7.debug("[DeviceToken] Reusing cached Client Bound SAT — skipping /devices/token");
20376
+ const rpcProof$1 = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
20377
+ await session.reauthenticate(cached.token, rpcProof$1, { clientBound: true });
20378
+ updateCredential({ token: cached.token });
20379
+ return { activated: true };
20380
+ }
20261
20381
  const tokenData = await this.obtainToken();
20262
20382
  if (!tokenData.expires_at && !tokenData.expires_in && satClaims.expires_at) tokenData.expires_at = satClaims.expires_at;
20263
20383
  this._effectiveExpireIn = resolveExpireIn(tokenData);
20264
20384
  const rpcProof = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
20265
20385
  await session.reauthenticate(tokenData.token, rpcProof, { clientBound: true });
20266
20386
  updateCredential({ token: tokenData.token });
20267
- logger$6.info("[DeviceToken] Client Bound SAT activated successfully");
20387
+ logger$7.info("[DeviceToken] Client Bound SAT activated successfully");
20268
20388
  this._currentToken$.next(tokenData);
20389
+ return { activated: true };
20269
20390
  } catch (error) {
20270
- logger$6.error("[DeviceToken] Failed to activate Client Bound SAT:", error);
20391
+ logger$7.error("[DeviceToken] Failed to activate Client Bound SAT:", error);
20271
20392
  this.errorHandler(new DPoPInitError(error, "Failed to activate Client Bound SAT"));
20272
- const credential = this.getCredential();
20273
- const expiresAt = satClaims.expires_at ?? (credential.expiry_at ? credential.expiry_at / 1e3 : Date.now() / 1e3 + DEVICE_TOKEN_DEFAULT_EXPIRE_IN / 1e3);
20274
- this._currentToken$.next({
20275
- token: credential.token ?? "",
20276
- expires_at: expiresAt
20277
- });
20393
+ return {
20394
+ activated: false,
20395
+ reason: "endpoint-failed"
20396
+ };
20278
20397
  }
20279
20398
  }
20280
20399
  /**
20400
+ * Returns true when the cached token has enough headroom before expiry to
20401
+ * be safely reused on reactivation. The headroom matches the refresh
20402
+ * buffer, so a token within the refresh window is treated as stale (the
20403
+ * reactive pipeline is about to refresh it anyway).
20404
+ */
20405
+ isTokenFresh(token) {
20406
+ return resolveExpiresAt(token) * 1e3 - Date.now() > DEVICE_TOKEN_REFRESH_BUFFER_MS;
20407
+ }
20408
+ /**
20281
20409
  * Obtains a Client Bound SAT from `/api/fabric/subscriber/devices/token`.
20282
20410
  * Returns the full {@link DeviceTokenResponse} including expiry metadata.
20283
20411
  */
@@ -20307,7 +20435,7 @@ var DeviceTokenManager = class extends Destroyable {
20307
20435
  * handled by the reactive pipeline).
20308
20436
  */
20309
20437
  async refreshToken(session, currentToken, updateCredential) {
20310
- logger$6.debug("[DeviceToken] Refreshing Client Bound SAT");
20438
+ logger$7.debug("[DeviceToken] Refreshing Client Bound SAT");
20311
20439
  const dpopProof = await this.dpopManager.createHttpProof({
20312
20440
  method: "POST",
20313
20441
  uri: DEVICE_REFRESH_ENDPOINT,
@@ -20329,7 +20457,7 @@ var DeviceTokenManager = class extends Destroyable {
20329
20457
  const rpcProof = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
20330
20458
  await session.reauthenticate(data.token, rpcProof);
20331
20459
  updateCredential({ token: data.token });
20332
- logger$6.info("[DeviceToken] Client Bound SAT refreshed successfully");
20460
+ logger$7.info("[DeviceToken] Client Bound SAT refreshed successfully");
20333
20461
  return data;
20334
20462
  }
20335
20463
  /**
@@ -20338,18 +20466,22 @@ var DeviceTokenManager = class extends Destroyable {
20338
20466
  * On all retries exhausted, emits to `errorHandler`.
20339
20467
  */
20340
20468
  async executeRefresh() {
20469
+ if (this._paused) {
20470
+ logger$7.debug("[DeviceToken] Manager paused, skipping refresh");
20471
+ return;
20472
+ }
20341
20473
  if (this._refreshInProgress) {
20342
- logger$6.debug("[DeviceToken] Refresh already in progress, skipping");
20474
+ logger$7.debug("[DeviceToken] Refresh already in progress, skipping");
20343
20475
  return;
20344
20476
  }
20345
20477
  const session = this._session;
20346
20478
  const updateCredential = this._updateCredential;
20347
20479
  if (!session || !updateCredential) {
20348
- logger$6.warn("[DeviceToken] Cannot refresh: session or updateCredential not set");
20480
+ logger$7.warn("[DeviceToken] Cannot refresh: session or updateCredential not set");
20349
20481
  return;
20350
20482
  }
20351
20483
  if (!session.authenticated) {
20352
- logger$6.debug("[DeviceToken] Session not authenticated, deferring refresh");
20484
+ logger$7.debug("[DeviceToken] Session not authenticated, deferring refresh");
20353
20485
  return;
20354
20486
  }
20355
20487
  this._refreshInProgress = true;
@@ -20359,7 +20491,7 @@ var DeviceTokenManager = class extends Destroyable {
20359
20491
  const newTokenData = await this.retryRefresh(session, currentToken, updateCredential);
20360
20492
  this._currentToken$.next(newTokenData);
20361
20493
  } catch (error) {
20362
- logger$6.error("[DeviceToken] Automatic Client Bound SAT refresh failed:", error);
20494
+ logger$7.error("[DeviceToken] Automatic Client Bound SAT refresh failed:", error);
20363
20495
  this.errorHandler(error instanceof TokenRefreshError ? error : new TokenRefreshError("Automatic token refresh failed", error));
20364
20496
  } finally {
20365
20497
  this._refreshInProgress = false;
@@ -20377,18 +20509,215 @@ var DeviceTokenManager = class extends Destroyable {
20377
20509
  lastError = error;
20378
20510
  if (attempt < DEVICE_TOKEN_REFRESH_MAX_RETRIES - 1) {
20379
20511
  const delay$1 = DEVICE_TOKEN_REFRESH_RETRY_BASE_MS * Math.pow(2, attempt);
20380
- logger$6.warn(`[DeviceToken] Refresh attempt ${attempt + 1} failed, retrying in ${delay$1}ms`);
20512
+ logger$7.warn(`[DeviceToken] Refresh attempt ${attempt + 1} failed, retrying in ${delay$1}ms`);
20381
20513
  await new Promise((resolve) => setTimeout(resolve, delay$1));
20382
20514
  }
20383
20515
  }
20384
20516
  throw lastError instanceof Error ? lastError : new TokenRefreshError("All refresh retries exhausted", lastError);
20385
20517
  }
20518
+ /**
20519
+ * Stops the reactive refresh pipeline from firing. Use when the underlying
20520
+ * session is being torn down (e.g., during {@link SignalWire.disconnect})
20521
+ * so a scheduled refresh cannot fire against a destroyed session.
20522
+ *
20523
+ * The manager's state (cached token, effective TTL, subscriptions) is
20524
+ * preserved — call {@link resume} to re-enable firing after reconnect.
20525
+ */
20526
+ pause() {
20527
+ this._paused = true;
20528
+ }
20529
+ /** Re-enables the reactive refresh pipeline after a previous {@link pause}. */
20530
+ resume() {
20531
+ this._paused = false;
20532
+ }
20386
20533
  /** Cleans up the manager, cancelling the reactive pipeline and all subscriptions. */
20387
20534
  destroy() {
20388
20535
  super.destroy();
20389
20536
  }
20390
20537
  };
20391
20538
 
20539
+ //#endregion
20540
+ //#region src/managers/CredentialRefreshCoordinator.ts
20541
+ const logger$6 = getLogger();
20542
+ const defaultDeviceTokenManagerFactory = (dpopManager, http, errorHandler, getCredential) => new DeviceTokenManager(dpopManager, http, errorHandler, getCredential);
20543
+ /**
20544
+ * Centralizes credential-refresh ownership across the two competing
20545
+ * mechanisms — developer-provided `CredentialProvider.refresh()` and the
20546
+ * Client Bound SAT path via {@link DeviceTokenManager}.
20547
+ *
20548
+ * Maintains the invariant: **at most one refresh mechanism is armed at a
20549
+ * time, and at least one is armed whenever the current credential has an
20550
+ * `expiry_at`**.
20551
+ *
20552
+ * Replaces the previous design where refresh state was distributed across
20553
+ * `SignalWire` (timer field, scheduler method, activation helper) and
20554
+ * `DeviceTokenManager` (reactive pipeline). Centralizing the invariant in
20555
+ * one component eliminates the bug class that produced issue #19074.
20556
+ *
20557
+ * Race-safety:
20558
+ * - `_activating` flag prevents overlapping `activate()` calls from racing.
20559
+ * - `_activationGeneration` lets late resolutions detect they've been
20560
+ * preempted by a newer activation (e.g., reconnect during in-flight
20561
+ * `obtainToken`).
20562
+ */
20563
+ var CredentialRefreshCoordinator = class extends Destroyable {
20564
+ constructor(dpopManager, deps) {
20565
+ super();
20566
+ this.deps = deps;
20567
+ this._activating = false;
20568
+ this._activationGeneration = 0;
20569
+ if (dpopManager?.initialized) this._deviceTokenManager = (deps.deviceTokenManagerFactory ?? defaultDeviceTokenManagerFactory)(dpopManager, deps.http, (error) => deps.notifier.onError(error), () => deps.store.read());
20570
+ }
20571
+ /** True when the Client Bound SAT path is available (DPoP initialized). */
20572
+ get clientBoundSATAvailable() {
20573
+ return this._deviceTokenManager !== void 0;
20574
+ }
20575
+ /** True when the developer-provided refresh timer is currently armed. */
20576
+ get developerRefreshArmed() {
20577
+ return this._developerTimerId !== void 0;
20578
+ }
20579
+ /**
20580
+ * Arms the developer-provided refresh timer to fire shortly before
20581
+ * `expiresAt`. Replaces any previously scheduled developer refresh.
20582
+ *
20583
+ * Idempotent — multiple calls just reschedule. On retry exhaustion,
20584
+ * invokes `deps.onRefreshExhausted` so the orchestrator can disconnect.
20585
+ */
20586
+ scheduleDeveloperRefresh(provider, expiresAt, attempt = 0) {
20587
+ if (this._developerTimerId !== void 0) clearTimeout(this._developerTimerId);
20588
+ 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);
20589
+ this._developerTimerId = setTimeout(async () => {
20590
+ try {
20591
+ if (!provider.refresh) throw new InvalidCredentialsError("Credential provider does not support refresh");
20592
+ const newCredentials = await provider.refresh();
20593
+ this.deps.store.write(newCredentials);
20594
+ this.deps.store.persist(newCredentials);
20595
+ logger$6.info("[Coordinator] Credentials refreshed successfully.");
20596
+ if (newCredentials.expiry_at) this.scheduleDeveloperRefresh(provider, newCredentials.expiry_at, 0);
20597
+ } catch (error) {
20598
+ const nextAttempt = attempt + 1;
20599
+ logger$6.error(`[Coordinator] Credential refresh failed (attempt ${nextAttempt}/${CREDENTIAL_REFRESH_MAX_RETRIES}):`, error);
20600
+ this.deps.notifier.onError(error instanceof Error ? error : new Error(String(error), { cause: error }));
20601
+ if (nextAttempt < CREDENTIAL_REFRESH_MAX_RETRIES) this.scheduleDeveloperRefresh(provider, expiresAt, nextAttempt);
20602
+ else {
20603
+ logger$6.error("[Coordinator] Credential refresh exhausted all retries. Disconnecting.");
20604
+ this.deps.notifier.onError(new TokenRefreshError("Credential refresh failed after max retries"));
20605
+ this.deps.notifier.onRefreshExhausted();
20606
+ }
20607
+ }
20608
+ }, refreshInterval);
20609
+ }
20610
+ /**
20611
+ * Cancels any scheduled developer-provided refresh. Idempotent.
20612
+ *
20613
+ * @internal Used by the coordinator's own activation flow. External
20614
+ * callers should use {@link suspend} for disconnect-time quiescence —
20615
+ * `suspend()` also pauses the internal Client Bound SAT pipeline.
20616
+ */
20617
+ cancelDeveloperRefresh() {
20618
+ if (this._developerTimerId !== void 0) {
20619
+ clearTimeout(this._developerTimerId);
20620
+ this._developerTimerId = void 0;
20621
+ }
20622
+ }
20623
+ /**
20624
+ * Suspends both refresh paths — cancels the developer timer and pauses
20625
+ * the internal reactive pipeline. Use when the underlying session is
20626
+ * being torn down (e.g., {@link SignalWire.disconnect}). The next
20627
+ * {@link activate} call re-enables the internal pipeline.
20628
+ *
20629
+ * The internal manager's cached token survives — see
20630
+ * {@link DeviceTokenManager.pause} — so a subsequent reconnect can
20631
+ * skip the `/devices/token` exchange entirely.
20632
+ */
20633
+ suspend() {
20634
+ this.cancelDeveloperRefresh();
20635
+ this._deviceTokenManager?.pause();
20636
+ }
20637
+ /**
20638
+ * Asks the Client Bound SAT path to take over refresh. If it accepts,
20639
+ * the developer-provided timer (if any) is cancelled. If it declines, the
20640
+ * developer timer remains armed and a `credential_refresh_fallback`
20641
+ * warning is emitted.
20642
+ *
20643
+ * **Idempotent** — re-entrant calls during an in-flight activation are
20644
+ * dropped. Use `_activationGeneration` to detect stale resolutions
20645
+ * (e.g., a reconnect-triggered activate() that races with an earlier one).
20646
+ */
20647
+ async activate(user, session) {
20648
+ if (this._activating) {
20649
+ logger$6.debug("[Coordinator] activate() in flight; ignoring re-entrant call");
20650
+ return;
20651
+ }
20652
+ if (!this._deviceTokenManager) return;
20653
+ this._deviceTokenManager.resume();
20654
+ const generation = ++this._activationGeneration;
20655
+ this._activating = true;
20656
+ try {
20657
+ const result = await this.withActivationTimeout(this._deviceTokenManager.activate(user, session, (cred) => this.deps.store.merge(cred)));
20658
+ if (generation !== this._activationGeneration) {
20659
+ logger$6.debug("[Coordinator] activate() result discarded (preempted by newer activation)");
20660
+ return;
20661
+ }
20662
+ if (result.activated) {
20663
+ this.cancelDeveloperRefresh();
20664
+ logger$6.debug("[Coordinator] Developer refresh disabled — Client Bound SAT owns refresh");
20665
+ return;
20666
+ }
20667
+ logger$6.warn(`[SignalWire] [SW-REFRESH-FALLBACK] Client Bound SAT declined (reason=${result.reason}); using developer-provided refresh handler.`);
20668
+ this.deps.notifier.onWarning({
20669
+ code: "credential_refresh_fallback",
20670
+ source: "CredentialProvider",
20671
+ reason: result.reason,
20672
+ message: `Client Bound SAT activation declined (${result.reason}); using developer-provided refresh.`
20673
+ });
20674
+ } finally {
20675
+ this._activating = false;
20676
+ }
20677
+ }
20678
+ destroy() {
20679
+ this.cancelDeveloperRefresh();
20680
+ this._deviceTokenManager?.destroy();
20681
+ this._deviceTokenManager = void 0;
20682
+ super.destroy();
20683
+ }
20684
+ /**
20685
+ * Races the manager's `activate()` against a hard timeout. A wedged HTTP
20686
+ * layer (e.g., proxy issues) could otherwise hang the activation
20687
+ * indefinitely, leaving the session with no refresh mechanism while the
20688
+ * `_activating` guard blocks subsequent retries.
20689
+ *
20690
+ * On timeout, the manager is paused immediately. The inner `activate()`
20691
+ * may still complete in the background and emit to `_currentToken$`,
20692
+ * which would normally arm the reactive refresh pipeline; pausing
20693
+ * prevents that pipeline from firing while the developer refresh path
20694
+ * is the active mechanism — preserving the "at most one mechanism
20695
+ * armed" invariant. The next `activate()` call resumes the manager.
20696
+ */
20697
+ async withActivationTimeout(inner) {
20698
+ return new Promise((resolve) => {
20699
+ const timer$4 = setTimeout(() => {
20700
+ this._deviceTokenManager?.pause();
20701
+ resolve({
20702
+ activated: false,
20703
+ reason: "activation-timeout"
20704
+ });
20705
+ }, CREDENTIAL_ACTIVATE_TIMEOUT_MS);
20706
+ inner.then((result) => {
20707
+ clearTimeout(timer$4);
20708
+ resolve(result);
20709
+ }, (error) => {
20710
+ clearTimeout(timer$4);
20711
+ this.deps.notifier.onError(error instanceof Error ? error : new Error(String(error)));
20712
+ resolve({
20713
+ activated: false,
20714
+ reason: "endpoint-failed"
20715
+ });
20716
+ });
20717
+ });
20718
+ }
20719
+ };
20720
+
20392
20721
  //#endregion
20393
20722
  //#region src/managers/DiagnosticsCollector.ts
20394
20723
  const logger$5 = getLogger();
@@ -20909,6 +21238,10 @@ var TransportManager = class extends Destroyable {
20909
21238
  if (!isSignalwireRequest(message)) return true;
20910
21239
  const eventChannel = message.params.event_channel;
20911
21240
  if (!eventChannel) return true;
21241
+ if (message.params.event_type.startsWith("conversation.")) {
21242
+ logger$2.debug(`[Transport] Received conversation event: ${message.params.event_type} (event_channel: ${eventChannel})`);
21243
+ return true;
21244
+ }
20912
21245
  const currentProtocol = this._currentProtocol;
20913
21246
  if (!currentProtocol) return true;
20914
21247
  if (!eventChannel.includes(currentProtocol)) {
@@ -21102,6 +21435,7 @@ var SignalWire = class extends Destroyable {
21102
21435
  this._isConnected$ = this.createBehaviorSubject(false);
21103
21436
  this._isRegistered$ = this.createBehaviorSubject(false);
21104
21437
  this._errors$ = this.createReplaySubject(1);
21438
+ this._warnings$ = this.createReplaySubject(10);
21105
21439
  this._options = {};
21106
21440
  this._deps = new DependencyContainer();
21107
21441
  this._credentialProvider = credentialProvider;
@@ -21161,6 +21495,27 @@ var SignalWire = class extends Destroyable {
21161
21495
  */
21162
21496
  async resolveCredentials() {
21163
21497
  const fingerprint = await this.initDPoP();
21498
+ this._refreshCoordinator = new CredentialRefreshCoordinator(this._dpopManager, {
21499
+ http: this._deps.http,
21500
+ notifier: {
21501
+ onError: (error) => this._errors$.next(error),
21502
+ onWarning: (warning) => this._warnings$.next(warning),
21503
+ onRefreshExhausted: () => void this.disconnect()
21504
+ },
21505
+ store: {
21506
+ read: () => this._deps.credential,
21507
+ write: (credential) => {
21508
+ this._deps.credential = credential;
21509
+ },
21510
+ merge: (partial) => {
21511
+ this._deps.credential = {
21512
+ ...this._deps.credential,
21513
+ ...partial
21514
+ };
21515
+ },
21516
+ persist: (credential) => this.persistCredential(credential)
21517
+ }
21518
+ });
21164
21519
  if (this._credentialProvider) return this.validateCredentials(this._credentialProvider, void 0, fingerprint);
21165
21520
  for (const scope of this._deps.persistSession ? ["local", "session"] : ["session"]) try {
21166
21521
  const cached = await this._deps.storage.getItem("sw:cached_credential", scope);
@@ -21190,7 +21545,16 @@ var SignalWire = class extends Destroyable {
21190
21545
  logger$1.error("[SignalWire] Provided credentials have expired.");
21191
21546
  throw new InvalidCredentialsError("Provided credentials have expired.");
21192
21547
  }
21193
- if (_credentials.expiry_at && credentialProvider?.refresh) this.scheduleCredentialRefresh(credentialProvider, _credentials.expiry_at);
21548
+ if (_credentials.expiry_at && credentialProvider?.refresh) this._refreshCoordinator?.scheduleDeveloperRefresh(credentialProvider, _credentials.expiry_at);
21549
+ else if (_credentials.expiry_at && !credentialProvider?.refresh) {
21550
+ 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.`);
21551
+ this._warnings$.next({
21552
+ code: "credential_no_refresh_handler",
21553
+ source: "CredentialProvider",
21554
+ message: "Credential has expiry_at but no refresh handler. Session will terminate at expiry unless the SAT carries 'sat:refresh' scope.",
21555
+ expiresAt: _credentials.expiry_at
21556
+ });
21557
+ }
21194
21558
  this._deps.credential = _credentials;
21195
21559
  this.persistCredential(_credentials);
21196
21560
  if (this.isConnected && this._clientSession.authenticated && _credentials.token) try {
@@ -21201,35 +21565,6 @@ var SignalWire = class extends Destroyable {
21201
21565
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21202
21566
  }
21203
21567
  }
21204
- /**
21205
- * Schedules credential refresh with exponential backoff retry on failure.
21206
- * On success, resets attempt counter and schedules the next refresh.
21207
- * After exhausting retries, emits TokenRefreshError and disconnects.
21208
- */
21209
- scheduleCredentialRefresh(credentialProvider, expiresAt, attempt = 0) {
21210
- if (this._refreshTimerId !== void 0) clearTimeout(this._refreshTimerId);
21211
- 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);
21212
- this._refreshTimerId = setTimeout(async () => {
21213
- try {
21214
- if (!credentialProvider.refresh) throw new InvalidCredentialsError("Credential provider does not support refresh");
21215
- const newCredentials = await credentialProvider.refresh();
21216
- this._deps.credential = newCredentials;
21217
- this.persistCredential(newCredentials);
21218
- logger$1.info("[SignalWire] Credentials refreshed successfully.");
21219
- if (newCredentials.expiry_at) this.scheduleCredentialRefresh(credentialProvider, newCredentials.expiry_at, 0);
21220
- } catch (error) {
21221
- const nextAttempt = attempt + 1;
21222
- logger$1.error(`[SignalWire] Credential refresh failed (attempt ${nextAttempt}/${CREDENTIAL_REFRESH_MAX_RETRIES}):`, error);
21223
- this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21224
- if (nextAttempt < CREDENTIAL_REFRESH_MAX_RETRIES) this.scheduleCredentialRefresh(credentialProvider, expiresAt, nextAttempt);
21225
- else {
21226
- logger$1.error("[SignalWire] Credential refresh exhausted all retries. Disconnecting.");
21227
- this._errors$.next(new TokenRefreshError("Credential refresh failed after max retries"));
21228
- this.disconnect();
21229
- }
21230
- }
21231
- }, refreshInterval);
21232
- }
21233
21568
  /** Persist credential to localStorage when persistSession is enabled. */
21234
21569
  persistCredential(credential) {
21235
21570
  if (!credential.token) return;
@@ -21323,6 +21658,7 @@ var SignalWire = class extends Destroyable {
21323
21658
  logger$1.debug("[SignalWire] Credential expired, refreshing before reconnect");
21324
21659
  const newCredentials = await this._credentialProvider.authenticate(fingerprint ? { fingerprint } : void 0);
21325
21660
  this._deps.credential = newCredentials;
21661
+ if (newCredentials.expiry_at && this._credentialProvider.refresh) this._refreshCoordinator?.scheduleDeveloperRefresh(this._credentialProvider, newCredentials.expiry_at);
21326
21662
  logger$1.debug("[SignalWire] Credential refreshed successfully for reconnect");
21327
21663
  } catch (error) {
21328
21664
  logger$1.error("[SignalWire] Failed to refresh credentials for reconnect:", error);
@@ -21334,33 +21670,12 @@ var SignalWire = class extends Destroyable {
21334
21670
  this._errors$.next(error);
21335
21671
  });
21336
21672
  await this._clientSession.connect();
21337
- if (this._dpopManager?.initialized) {
21338
- if (this._refreshTimerId) {
21339
- clearTimeout(this._refreshTimerId);
21340
- this._refreshTimerId = void 0;
21341
- logger$1.debug("[SignalWire] Developer refresh disabled — Client Bound SAT activation starting");
21342
- }
21343
- this._deviceTokenManager = new DeviceTokenManager(this._dpopManager, this._deps.http, (error) => this._errors$.next(error), () => this._deps.credential);
21344
- await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
21345
- this._deps.credential = {
21346
- ...this._deps.credential,
21347
- ...cred
21348
- };
21349
- });
21350
- }
21673
+ await this._refreshCoordinator?.activate(this._deps.user, this._clientSession);
21351
21674
  this.subscribeTo(this._clientSession.authenticated$.pipe((0, import_cjs$1.skip)(1), (0, import_cjs$1.filter)(Boolean)), async () => {
21352
21675
  try {
21353
- if (this._deviceTokenManager) {
21354
- await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
21355
- this._deps.credential = {
21356
- ...this._deps.credential,
21357
- ...cred
21358
- };
21359
- });
21360
- logger$1.debug("[SignalWire] Client Bound SAT re-activated after reconnect");
21361
- }
21676
+ await this._refreshCoordinator?.activate(this._deps.user, this._clientSession);
21362
21677
  } catch (error) {
21363
- logger$1.error("[SignalWire] Client Bound SAT re-activation failed (non-fatal):", error);
21678
+ logger$1.error("[SignalWire] Refresh re-arm after reconnect failed (non-fatal):", error);
21364
21679
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21365
21680
  }
21366
21681
  try {
@@ -21446,6 +21761,19 @@ var SignalWire = class extends Destroyable {
21446
21761
  get errors$() {
21447
21762
  return this.deferEmission(this._errors$.asObservable());
21448
21763
  }
21764
+ /**
21765
+ * Observable stream of non-fatal SDK warnings.
21766
+ *
21767
+ * Subscribe to detect SDK behaviors that affect session liveness or developer-facing
21768
+ * contracts but do not warrant disconnection — e.g., a fallback from Client Bound SAT
21769
+ * refresh to the developer-provided `refresh()` because the SAT lacks `sat:refresh`
21770
+ * scope. Discriminated by `code`.
21771
+ *
21772
+ * Independent from {@link errors$}: existing error consumers are not notified.
21773
+ */
21774
+ get warnings$() {
21775
+ return this.deferEmission(this._warnings$.asObservable());
21776
+ }
21449
21777
  /** Platform WebRTC capabilities detected at construction time. */
21450
21778
  get platformCapabilities() {
21451
21779
  this._platformCapabilities ??= detectPlatformCapabilities(this._options.webRTCApiProvider);
@@ -21514,7 +21842,7 @@ var SignalWire = class extends Destroyable {
21514
21842
  logger$1.warn("[SignalWire] Failed to initialize VisibilityController:", error);
21515
21843
  }
21516
21844
  try {
21517
- this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "3.30.0" });
21845
+ this._diagnosticsCollector = new DiagnosticsCollector({ sdkVersion: "4.0.0-rc.1" });
21518
21846
  } catch (error) {
21519
21847
  logger$1.warn("[SignalWire] Failed to initialize DiagnosticsCollector:", error);
21520
21848
  }
@@ -21522,14 +21850,19 @@ var SignalWire = class extends Destroyable {
21522
21850
  /**
21523
21851
  * Disconnects the WebSocket and tears down the current session.
21524
21852
  *
21853
+ * Ends the session identified by the protocol and clears its persisted
21854
+ * resume state (`authorization_state` + protocol) and attach records
21855
+ * together — a later {@link connect} with the same credentials starts a
21856
+ * fresh session and cannot reattach to the ended session's calls.
21857
+ * Credentials and device preferences are preserved. To temporarily stop
21858
+ * receiving inbound calls while keeping the session alive, use
21859
+ * `unregister()` instead.
21860
+ *
21525
21861
  * The client can be reconnected by calling {@link connect} again,
21526
21862
  * which creates a fresh transport and session.
21527
21863
  */
21528
21864
  async disconnect() {
21529
- if (this._refreshTimerId) {
21530
- clearTimeout(this._refreshTimerId);
21531
- this._refreshTimerId = void 0;
21532
- }
21865
+ this._refreshCoordinator?.suspend();
21533
21866
  this._diagnosticsCollector?.record("connection", "disconnected");
21534
21867
  await this.teardownTransportAndSession();
21535
21868
  this._isConnected$.next(false);
@@ -21916,15 +22249,20 @@ var SignalWire = class extends Destroyable {
21916
22249
  prefs.preferredVideoInput = null;
21917
22250
  await this._deviceController.clearDeviceState();
21918
22251
  }
21919
- /** Destroys the client, clearing timers and releasing all resources. */
22252
+ /**
22253
+ * Destroys the client, clearing timers and releasing all resources.
22254
+ *
22255
+ * Intentionally destroying the client ends its session: the resume state
22256
+ * (`authorization_state` + protocol) and the attach records are both
22257
+ * cleared. Credentials and device preferences are preserved — use
22258
+ * {@link resetToDefaults} for a full wipe. To temporarily stop receiving
22259
+ * inbound calls while keeping the session alive, use `unregister()`.
22260
+ */
21920
22261
  destroy() {
21921
- if (this._refreshTimerId) {
21922
- clearTimeout(this._refreshTimerId);
21923
- this._refreshTimerId = void 0;
21924
- }
21925
- this._deviceTokenManager?.destroy();
22262
+ this._refreshCoordinator?.destroy();
22263
+ this._refreshCoordinator = void 0;
21926
22264
  this._dpopManager?.destroy();
21927
- if (this._attachManager) this._attachManager.detachAll();
22265
+ this._clientSession.teardownSessionState();
21928
22266
  this._transport.destroy();
21929
22267
  this._clientSession.destroy();
21930
22268
  try {
@@ -22039,7 +22377,7 @@ var StaticCredentialProvider = class {
22039
22377
  /**
22040
22378
  * Library version from package.json, injected at build time.
22041
22379
  */
22042
- const version = "3.30.0";
22380
+ const version = "4.0.0-rc.1";
22043
22381
  /**
22044
22382
  * Flag indicating the library has been loaded and is ready to use.
22045
22383
  * For UMD builds: `window.SignalWire.ready`
@@ -22061,7 +22399,7 @@ const ready = true;
22061
22399
  */
22062
22400
  const emitReadyEvent = () => {
22063
22401
  if (typeof window !== "undefined") {
22064
- const event = new CustomEvent("signalwire:js:ready", { detail: { version: "3.30.0" } });
22402
+ const event = new CustomEvent("signalwire:js:ready", { detail: { version: "4.0.0-rc.1" } });
22065
22403
  window.dispatchEvent(event);
22066
22404
  }
22067
22405
  };