@signalwire/js 4.0.0-dev-20260413184938 → 4.0.0-dev-20260416174405

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_operators = require('./operators-C3TtzZHH.cjs');
1
+ const require_operators = require('./operators-BT3jl--r.cjs');
2
2
  let jwt_decode = require("jwt-decode");
3
3
  let rxjs = require("rxjs");
4
4
  let uuid = require("uuid");
@@ -10,13 +10,11 @@ var Destroyable = class {
10
10
  this.subscriptions = [];
11
11
  this.subjects = [];
12
12
  this._destroyed$ = new rxjs.Subject();
13
- this._change$ = new rxjs.Subject();
14
13
  }
15
14
  destroy() {
16
15
  this._observableCache?.clear();
17
16
  this.subscriptions.forEach((sub) => sub.unsubscribe());
18
17
  this.subjects.forEach((subject) => subject.complete());
19
- this._change$.complete();
20
18
  this._destroyed$.next();
21
19
  this._destroyed$.complete();
22
20
  }
@@ -70,24 +68,18 @@ var Destroyable = class {
70
68
  createSubject() {
71
69
  const subject = new rxjs.Subject();
72
70
  this.subjects.push(subject);
73
- this.subscriptions.push(subject.pipe((0, rxjs.map)(() => void 0)).subscribe(this._change$));
74
71
  return subject;
75
72
  }
76
73
  createReplaySubject(bufferSize, windowTime) {
77
74
  const subject = new rxjs.ReplaySubject(bufferSize, windowTime);
78
75
  this.subjects.push(subject);
79
- this.subscriptions.push(subject.pipe((0, rxjs.map)(() => void 0)).subscribe(this._change$));
80
76
  return subject;
81
77
  }
82
78
  createBehaviorSubject(initialValue) {
83
79
  const subject = new rxjs.BehaviorSubject(initialValue);
84
80
  this.subjects.push(subject);
85
- this.subscriptions.push(subject.pipe((0, rxjs.skip)(1), (0, rxjs.map)(() => void 0)).subscribe(this._change$));
86
81
  return subject;
87
82
  }
88
- get $() {
89
- return this._change$.pipe((0, rxjs.map)(() => this));
90
- }
91
83
  /**
92
84
  * Observable that emits when the instance is destroyed
93
85
  */
@@ -436,6 +428,14 @@ const DEVICE_TOKEN_REFRESH_BUFFER_MS = 3e4;
436
428
  const DEVICE_TOKEN_REFRESH_MAX_RETRIES = 3;
437
429
  /** Base delay in milliseconds for exponential backoff on refresh retry. */
438
430
  const DEVICE_TOKEN_REFRESH_RETRY_BASE_MS = 1e3;
431
+ /** Maximum retry attempts for developer credential refresh on transient failure. */
432
+ const CREDENTIAL_REFRESH_MAX_RETRIES = 5;
433
+ /** Base delay in milliseconds for exponential backoff on credential refresh retry. */
434
+ const CREDENTIAL_REFRESH_RETRY_BASE_MS = 1e3;
435
+ /** Maximum delay in milliseconds for credential refresh backoff. */
436
+ const CREDENTIAL_REFRESH_MAX_DELAY_MS = 3e4;
437
+ /** Buffer in milliseconds before token expiry to trigger refresh. */
438
+ const CREDENTIAL_REFRESH_BUFFER_MS = 5e3;
439
439
  /** JSON-RPC error code for requester validation failure (corrupted auth state). */
440
440
  const RPC_ERROR_REQUESTER_VALIDATION_FAILED = -32003;
441
441
  /** JSON-RPC error code for invalid params (e.g., missing authentication block). */
@@ -1202,6 +1202,20 @@ var ClientPreferences = class {
1202
1202
  }
1203
1203
  };
1204
1204
 
1205
+ //#endregion
1206
+ //#region src/utils/toError.ts
1207
+ /**
1208
+ * Normalizes an unknown caught value into a proper Error instance.
1209
+ *
1210
+ * In catch blocks, the caught value is `unknown` — it could be an Error,
1211
+ * string, number, or any other value. This utility ensures a consistent
1212
+ * Error object is produced.
1213
+ */
1214
+ function toError(value) {
1215
+ if (value instanceof Error) return value;
1216
+ return new Error(String(value));
1217
+ }
1218
+
1205
1219
  //#endregion
1206
1220
  //#region src/controllers/NavigatorDeviceController.ts
1207
1221
  const logger$26 = require_operators.getLogger();
@@ -1549,7 +1563,7 @@ var NavigatorDeviceController = class extends Destroyable {
1549
1563
  });
1550
1564
  } catch (error) {
1551
1565
  logger$26.error("[DeviceController] Failed to enumerate devices:", error);
1552
- this._errors$.next(error);
1566
+ this._errors$.next(toError(error));
1553
1567
  }
1554
1568
  }
1555
1569
  async getDeviceCapabilities(deviceInfo) {
@@ -1565,7 +1579,7 @@ var NavigatorDeviceController = class extends Destroyable {
1565
1579
  return capabilities;
1566
1580
  } catch (error) {
1567
1581
  logger$26.error("[DeviceController] Failed to get device capabilities:", error);
1568
- this._errors$.next(error);
1582
+ this._errors$.next(toError(error));
1569
1583
  throw error;
1570
1584
  }
1571
1585
  }
@@ -3200,83 +3214,83 @@ var Participant = class extends Destroyable {
3200
3214
  }
3201
3215
  /** Observable of the participant's display name. */
3202
3216
  get name$() {
3203
- return this.cachedObservable("name$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.name), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3217
+ return this.cachedObservable("name$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.name), (0, rxjs_operators.distinctUntilChanged)()));
3204
3218
  }
3205
3219
  /** Observable of the participant type (e.g. `'member'`, `'screen'`). */
3206
3220
  get type$() {
3207
- return this.cachedObservable("type$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.type), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3221
+ return this.cachedObservable("type$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.type), (0, rxjs_operators.distinctUntilChanged)()));
3208
3222
  }
3209
3223
  /** Observable indicating whether the participant has raised their hand. */
3210
3224
  get handraised$() {
3211
- return this.cachedObservable("handraised$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.handraised), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3225
+ return this.cachedObservable("handraised$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.handraised), (0, rxjs_operators.distinctUntilChanged)()));
3212
3226
  }
3213
3227
  /** Observable indicating whether the participant is visible in the layout. */
3214
3228
  get visible$() {
3215
- return this.cachedObservable("visible$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.visible), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3229
+ return this.cachedObservable("visible$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.visible), (0, rxjs_operators.distinctUntilChanged)()));
3216
3230
  }
3217
3231
  /** Observable indicating whether the participant's audio is muted. */
3218
3232
  get audioMuted$() {
3219
- return this.cachedObservable("audioMuted$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.audio_muted), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3233
+ return this.cachedObservable("audioMuted$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.audio_muted), (0, rxjs_operators.distinctUntilChanged)()));
3220
3234
  }
3221
3235
  /** Observable indicating whether the participant's video is muted. */
3222
3236
  get videoMuted$() {
3223
- return this.cachedObservable("videoMuted$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.video_muted), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3237
+ return this.cachedObservable("videoMuted$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.video_muted), (0, rxjs_operators.distinctUntilChanged)()));
3224
3238
  }
3225
3239
  /** Observable indicating whether the participant is deafened. */
3226
3240
  get deaf$() {
3227
- return this.cachedObservable("deaf$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.deaf), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3241
+ return this.cachedObservable("deaf$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.deaf), (0, rxjs_operators.distinctUntilChanged)()));
3228
3242
  }
3229
3243
  /** Observable of the participant's microphone input volume. */
3230
3244
  get inputVolume$() {
3231
- return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.input_volume), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3245
+ return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.input_volume), (0, rxjs_operators.distinctUntilChanged)()));
3232
3246
  }
3233
3247
  /** Observable of the participant's speaker output volume. */
3234
3248
  get outputVolume$() {
3235
- return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.output_volume), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3249
+ return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.output_volume), (0, rxjs_operators.distinctUntilChanged)()));
3236
3250
  }
3237
3251
  /** Observable of the microphone input sensitivity level. */
3238
3252
  get inputSensitivity$() {
3239
- return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.input_sensitivity), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3253
+ return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.input_sensitivity), (0, rxjs_operators.distinctUntilChanged)()));
3240
3254
  }
3241
3255
  /** Observable indicating whether echo cancellation is enabled. */
3242
3256
  get echoCancellation$() {
3243
- return this.cachedObservable("echoCancellation$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.echo_cancellation), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3257
+ return this.cachedObservable("echoCancellation$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.echo_cancellation), (0, rxjs_operators.distinctUntilChanged)()));
3244
3258
  }
3245
3259
  /** Observable indicating whether auto-gain control is enabled. */
3246
3260
  get autoGain$() {
3247
- return this.cachedObservable("autoGain$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.auto_gain), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3261
+ return this.cachedObservable("autoGain$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.auto_gain), (0, rxjs_operators.distinctUntilChanged)()));
3248
3262
  }
3249
3263
  /** Observable indicating whether noise suppression is enabled. */
3250
3264
  get noiseSuppression$() {
3251
- return this.cachedObservable("noiseSuppression$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.noise_suppression), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3265
+ return this.cachedObservable("noiseSuppression$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.noise_suppression), (0, rxjs_operators.distinctUntilChanged)()));
3252
3266
  }
3253
3267
  /** Observable indicating whether low-bitrate mode is active. */
3254
3268
  get lowbitrate$() {
3255
- return this.cachedObservable("lowbitrate$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.lowbitrate), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3269
+ return this.cachedObservable("lowbitrate$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.lowbitrate), (0, rxjs_operators.distinctUntilChanged)()));
3256
3270
  }
3257
3271
  /** Observable indicating whether noise reduction is active. */
3258
3272
  get denoise$() {
3259
- return this.cachedObservable("denoise$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.denoise), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3273
+ return this.cachedObservable("denoise$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.denoise), (0, rxjs_operators.distinctUntilChanged)()));
3260
3274
  }
3261
3275
  /** Observable of custom metadata for this participant. */
3262
3276
  get meta$() {
3263
- return this.cachedObservable("meta$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.meta), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3277
+ return this.cachedObservable("meta$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.meta), (0, rxjs_operators.distinctUntilChanged)()));
3264
3278
  }
3265
3279
  /** Observable of the participant's subscriber ID. */
3266
3280
  get subscriberId$() {
3267
- return this.cachedObservable("subscriberId$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.subscriber_id), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3281
+ return this.cachedObservable("subscriberId$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.subscriber_id), (0, rxjs_operators.distinctUntilChanged)()));
3268
3282
  }
3269
3283
  /** Observable of the participant's address ID. */
3270
3284
  get addressId$() {
3271
- return this.cachedObservable("addressId$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.address_id), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3285
+ return this.cachedObservable("addressId$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.address_id), (0, rxjs_operators.distinctUntilChanged)()));
3272
3286
  }
3273
3287
  /** Observable of the server node ID for this participant. */
3274
3288
  get nodeId$() {
3275
- return this.cachedObservable("nodeId$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.node_id), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3289
+ return this.cachedObservable("nodeId$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.node_id), (0, rxjs_operators.distinctUntilChanged)()));
3276
3290
  }
3277
3291
  /** Observable indicating whether the participant is currently speaking. */
3278
3292
  get isTalking$() {
3279
- return this.cachedObservable("isTalking$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.talking), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3293
+ return this.cachedObservable("isTalking$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.talking), (0, rxjs_operators.distinctUntilChanged)()));
3280
3294
  }
3281
3295
  /** Whether the participant is currently speaking. */
3282
3296
  get isTalking() {
@@ -3284,7 +3298,7 @@ var Participant = class extends Destroyable {
3284
3298
  }
3285
3299
  /** Observable of the participant's layout position. */
3286
3300
  get position$() {
3287
- return this.cachedObservable("position$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.position), (0, rxjs_operators.distinctUntilChanged)(), require_operators.filterNull()));
3301
+ return this.cachedObservable("position$", () => this._state$.pipe((0, rxjs_operators.map)((state) => state.position), (0, rxjs_operators.distinctUntilChanged)()));
3288
3302
  }
3289
3303
  /** Current layout position. */
3290
3304
  get position() {
@@ -3891,7 +3905,7 @@ var CallEventsManager = class extends Destroyable {
3891
3905
  roomSessionId: callJoinedEvent.room_session_id
3892
3906
  });
3893
3907
  const sessionState = callJoinedEvent.room_session;
3894
- const { capabilities } = callJoinedEvent;
3908
+ const capabilities = callJoinedEvent.capabilities;
3895
3909
  this.selfId = this.selfId ?? callJoinedEvent.member_id;
3896
3910
  this.originCallId = this.originCallId ?? callJoinedEvent.origin_call_id;
3897
3911
  this.callIds.add(callJoinedEvent.call_id);
@@ -4895,7 +4909,7 @@ var RTCPeerConnectionController = class extends Destroyable {
4895
4909
  logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
4896
4910
  } catch (error) {
4897
4911
  logger$15.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
4898
- this._errors$.next(error);
4912
+ this._errors$.next(toError(error));
4899
4913
  throw error;
4900
4914
  }
4901
4915
  };
@@ -5135,7 +5149,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5135
5149
  },
5136
5150
  error: (error) => {
5137
5151
  logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
5138
- this._errors$.next(error);
5152
+ this._errors$.next(toError(error));
5139
5153
  }
5140
5154
  });
5141
5155
  this.subscribeTo((0, rxjs.merge)(this.deviceController.selectedAudioInputDevice$.pipe((0, rxjs.map)((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe((0, rxjs.map)((deviceInfo) => ["video", deviceInfo]))).pipe((0, rxjs.skipWhile)(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
@@ -5157,7 +5171,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5157
5171
  }
5158
5172
  } catch (error) {
5159
5173
  logger$15.error("[RTCPeerConnectionController] Initialization error:", error);
5160
- this._errors$.next(error);
5174
+ this._errors$.next(toError(error));
5161
5175
  this.destroy();
5162
5176
  }
5163
5177
  }
@@ -5204,7 +5218,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5204
5218
  await this.createOffer(offerOptions);
5205
5219
  } catch (error) {
5206
5220
  logger$15.error("[RTCPeerConnectionController] Error during negotiation:", error);
5207
- this._errors$.next(error);
5221
+ this._errors$.next(toError(error));
5208
5222
  }
5209
5223
  }
5210
5224
  /**
@@ -5227,7 +5241,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5227
5241
  }
5228
5242
  } catch (error) {
5229
5243
  logger$15.error("[RTCPeerConnectionController] Error updating answer status:", error);
5230
- this._errors$.next(error);
5244
+ this._errors$.next(toError(error));
5231
5245
  readyToConnect = false;
5232
5246
  } finally {
5233
5247
  if (readyToConnect) this.readyToConnect();
@@ -5393,7 +5407,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5393
5407
  await this.setLocalDescription(offer);
5394
5408
  } catch (error) {
5395
5409
  logger$15.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
5396
- this._errors$.next(error);
5410
+ this._errors$.next(toError(error));
5397
5411
  this.negotiationEnded();
5398
5412
  if (policyChanged) this.restoreIceTransportPolicy();
5399
5413
  throw error;
@@ -5488,7 +5502,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5488
5502
  logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
5489
5503
  } catch (error) {
5490
5504
  logger$15.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
5491
- this._errors$.next(error);
5505
+ this._errors$.next(toError(error));
5492
5506
  throw error;
5493
5507
  }
5494
5508
  }
@@ -5513,7 +5527,7 @@ var RTCPeerConnectionController = class extends Destroyable {
5513
5527
  logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
5514
5528
  } catch (error) {
5515
5529
  logger$15.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
5516
- this._errors$.next(error);
5530
+ this._errors$.next(toError(error));
5517
5531
  throw error;
5518
5532
  }
5519
5533
  }
@@ -5664,7 +5678,7 @@ var WebRTCVertoManager = class extends VertoManager {
5664
5678
  this.webRTCApiProvider = webRTCApiProvider;
5665
5679
  this._rtcPeerConnections$ = this.createBehaviorSubject([]);
5666
5680
  this._selfId$ = this.createBehaviorSubject(null);
5667
- this._signalingStatus$ = this.createBehaviorSubject(null);
5681
+ this._signalingStatus$ = this.createReplaySubject(1);
5668
5682
  this._screenShareStatus$ = this.createBehaviorSubject("none");
5669
5683
  this._rtcPeerConnectionsMap = /* @__PURE__ */ new Map();
5670
5684
  this._screenShareTimeoutMs = 5e4;
@@ -5736,7 +5750,7 @@ var WebRTCVertoManager = class extends VertoManager {
5736
5750
  return rtcPeerConnection;
5737
5751
  }
5738
5752
  get signalingStatus$() {
5739
- return this.cachedObservable("signalingStatus$", () => (0, rxjs.merge)(this._signalingStatus$.pipe(require_operators.filterNull()), this.mainPeerConnection.connectionState$.pipe((0, rxjs.filter)((connectionState) => [
5753
+ return this.cachedObservable("signalingStatus$", () => (0, rxjs.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, rxjs.filter)((connectionState) => [
5740
5754
  "connected",
5741
5755
  "disconnected",
5742
5756
  "failed"
@@ -6194,7 +6208,11 @@ var WebRTCVertoManager = class extends VertoManager {
6194
6208
  }
6195
6209
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
6196
6210
  logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
6197
- const vertoByeOrAccepted = await (0, rxjs.firstValueFrom)((0, rxjs.race)(this.vertoBye$, this.webRtcCallSession.answered$));
6211
+ const vertoByeOrAccepted = await (0, rxjs.firstValueFrom)((0, rxjs.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, rxjs.takeUntil)(this.destroyed$))).catch(() => null);
6212
+ if (vertoByeOrAccepted === null) {
6213
+ logger$14.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
6214
+ return;
6215
+ }
6198
6216
  if (isVertoByeMessage(vertoByeOrAccepted)) {
6199
6217
  logger$14.info("[WebRTCManager] Call ended before answer was sent.");
6200
6218
  this.callSession?.destroy();
@@ -6303,7 +6321,7 @@ var WebRTCVertoManager = class extends VertoManager {
6303
6321
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
6304
6322
  this.onError?.(error);
6305
6323
  });
6306
- await (0, rxjs.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, rxjs.filter)((state) => state === "connected"), (0, rxjs.take)(1), (0, rxjs.timeout)(this._screenShareTimeoutMs)));
6324
+ await (0, rxjs.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, rxjs.filter)((state) => state === "connected"), (0, rxjs.take)(1), (0, rxjs.timeout)(this._screenShareTimeoutMs), (0, rxjs.takeUntil)(this.destroyed$)));
6307
6325
  this._screenShareStatus$.next("started");
6308
6326
  logger$14.info("[WebRTCManager] Screen share started successfully.");
6309
6327
  return rtcPeerConnController.id;
@@ -8105,8 +8123,6 @@ var EntityCollection = class extends Destroyable {
8105
8123
  this.fetchController = fetchController;
8106
8124
  this.update$ = update$;
8107
8125
  this.onError = onError;
8108
- this.loading$ = this.createBehaviorSubject(false);
8109
- this.values$ = this.createReplaySubject(1);
8110
8126
  this.collectionData = /* @__PURE__ */ new Map();
8111
8127
  this.observablesRegistry = /* @__PURE__ */ new Map();
8112
8128
  this.upsertData = (data) => {
@@ -8126,22 +8142,28 @@ var EntityCollection = class extends Destroyable {
8126
8142
  }
8127
8143
  this.collectionData.set(data.id, updated);
8128
8144
  this.observablesRegistry.get(data.id)?.next(updated);
8129
- this.values$.next(Array.from(this.collectionData.values()));
8145
+ this._values$.next(Array.from(this.collectionData.values()));
8130
8146
  };
8147
+ this._loading$ = this.createBehaviorSubject(false);
8148
+ this._values$ = this.createReplaySubject(1);
8131
8149
  this._hasMore$ = this.createBehaviorSubject(true);
8132
- this._destroy$ = new rxjs.Subject();
8133
- this.updateSubscription = this.update$.subscribe(this.upsertData);
8134
- this.loading$.next(false);
8135
- this.hasMore$ = (0, rxjs.defer)(() => (0, rxjs.from)(this.init())).pipe((0, rxjs.switchMap)(() => this._hasMore$), (0, rxjs.distinctUntilChanged)(), (0, rxjs.shareReplay)(1), (0, rxjs.takeUntil)(this._destroy$));
8150
+ this.subscribeTo(this.update$, this.upsertData);
8151
+ this.hasMore$ = (0, rxjs.defer)(() => (0, rxjs.from)(this.init())).pipe((0, rxjs.switchMap)(() => this._hasMore$), (0, rxjs.distinctUntilChanged)(), (0, rxjs.shareReplay)(1), (0, rxjs.takeUntil)(this.destroyed$));
8152
+ }
8153
+ get loading$() {
8154
+ return this._loading$.asObservable();
8136
8155
  }
8137
8156
  get loading() {
8138
- return this.loading$.value;
8157
+ return this._loading$.value;
8158
+ }
8159
+ get values$() {
8160
+ return this._values$.asObservable();
8139
8161
  }
8140
8162
  get hasMore() {
8141
8163
  return this.fetchController.hasMore ?? true;
8142
8164
  }
8143
8165
  get updated$() {
8144
- return this.cachedObservable("updated$", () => this.loading$.pipe((0, rxjs.distinctUntilChanged)(), (0, rxjs.skip)(1), (0, rxjs.filter)((loading) => !loading), (0, rxjs.map)(() => void 0), (0, rxjs.takeUntil)(this._destroy$)));
8166
+ return this.cachedObservable("updated$", () => this._loading$.pipe((0, rxjs.distinctUntilChanged)(), (0, rxjs.skip)(1), (0, rxjs.filter)((loading) => !loading), (0, rxjs.map)(() => void 0), (0, rxjs.takeUntil)(this.destroyed$)));
8145
8167
  }
8146
8168
  get values() {
8147
8169
  return Array.from(this.collectionData.values());
@@ -8155,27 +8177,27 @@ var EntityCollection = class extends Destroyable {
8155
8177
  }
8156
8178
  async fetchMore() {
8157
8179
  try {
8158
- this.loading$.next(true);
8180
+ this._loading$.next(true);
8159
8181
  (await this.fetchController.next()).forEach(this.upsertData);
8160
8182
  this._hasMore$.next(this.fetchController.hasMore ?? false);
8161
- this.loading$.next(false);
8183
+ this._loading$.next(false);
8162
8184
  } catch (error) {
8163
8185
  logger$10.error(`Failed to fetch initial collection data`, error);
8164
8186
  this._hasMore$.next(this.fetchController.hasMore ?? false);
8165
- this.loading$.next(false);
8187
+ this._loading$.next(false);
8166
8188
  this.onError?.(new require_operators.CollectionFetchError("fetchMore", error));
8167
8189
  }
8168
8190
  }
8169
8191
  async tryFetch(key, value) {
8170
8192
  try {
8171
- this.loading$.next(true);
8193
+ this._loading$.next(true);
8172
8194
  const data = await this.fetchController[key]?.(value);
8173
- this.loading$.next(false);
8195
+ this._loading$.next(false);
8174
8196
  if (data) this.upsertData(data);
8175
8197
  return data;
8176
8198
  } catch (error) {
8177
8199
  logger$10.error(`Failed to fetch data for (${String(key)}:${String(value)}) :`, error);
8178
- this.loading$.next(false);
8200
+ this._loading$.next(false);
8179
8201
  this.onError?.(new require_operators.CollectionFetchError(`tryFetch(${String(key)})`, error));
8180
8202
  }
8181
8203
  }
@@ -8196,11 +8218,8 @@ var EntityCollection = class extends Destroyable {
8196
8218
  if (this.fetchController.hasMore !== false) this.fetchMore();
8197
8219
  }
8198
8220
  destroy() {
8199
- this._destroy$.next();
8200
- this._destroy$.complete();
8201
- this.updateSubscription.unsubscribe();
8202
- this.loading$.complete();
8203
8221
  this.observablesRegistry.forEach((subject) => subject.complete());
8222
+ this.observablesRegistry.clear();
8204
8223
  super.destroy();
8205
8224
  }
8206
8225
  };
@@ -8528,8 +8547,8 @@ var ClientSessionManager = class extends Destroyable {
8528
8547
  };
8529
8548
  this._authorization$ = this.createBehaviorSubject(void 0);
8530
8549
  this._errors$ = this.createReplaySubject(1);
8531
- this._authenticated$ = this.createBehaviorSubject(false);
8532
- this._clientBound = false;
8550
+ this._authState$ = this.createBehaviorSubject({ kind: "unauthenticated" });
8551
+ this._wasClientBound = false;
8533
8552
  this._subscriberInfo$ = this.createBehaviorSubject(null);
8534
8553
  this._calls$ = this.createBehaviorSubject({});
8535
8554
  this._iceServers$ = this.createBehaviorSubject([]);
@@ -8568,10 +8587,10 @@ var ClientSessionManager = class extends Destroyable {
8568
8587
  return this._errors$.asObservable();
8569
8588
  }
8570
8589
  get authenticated$() {
8571
- return this._authenticated$.asObservable();
8590
+ return this._authState$.pipe((0, rxjs.map)((state) => state.kind === "authenticated"), (0, rxjs.distinctUntilChanged)());
8572
8591
  }
8573
8592
  get authenticated() {
8574
- return this._authenticated$.value;
8593
+ return this._authState$.value.kind === "authenticated";
8575
8594
  }
8576
8595
  /**
8577
8596
  * Whether this session is client-bound (using a Client Bound SAT).
@@ -8580,7 +8599,11 @@ var ClientSessionManager = class extends Destroyable {
8580
8599
  * @internal
8581
8600
  */
8582
8601
  get clientBound() {
8583
- return this._clientBound;
8602
+ return this._wasClientBound;
8603
+ }
8604
+ /** @internal Current auth state for debugging/testing. */
8605
+ get authState() {
8606
+ return this._authState$.value;
8584
8607
  }
8585
8608
  /**
8586
8609
  * Set the directory instance
@@ -8618,6 +8641,9 @@ var ClientSessionManager = class extends Destroyable {
8618
8641
  this._errors$.next(new require_operators.AuthStateHandlerError(error));
8619
8642
  }
8620
8643
  });
8644
+ this.subscribeTo(this.transport.connectionStatus$.pipe((0, rxjs.filter)((status) => status === "disconnected" || status === "reconnecting")), () => {
8645
+ if (this._authState$.value.kind === "authenticated") this._authState$.next({ kind: "unauthenticated" });
8646
+ });
8621
8647
  this.subscribeTo(this.transport.connectionStatus$.pipe((0, rxjs.filter)((status) => status === "connected"), (0, rxjs.exhaustMap)(() => {
8622
8648
  logger$8.debug("[Session] Connection established, initiating authentication");
8623
8649
  return (0, rxjs.from)(this.authenticate()).pipe((0, rxjs.catchError)((error) => {
@@ -8752,7 +8778,7 @@ var ClientSessionManager = class extends Destroyable {
8752
8778
  if (!resolvedDpopToken && this.dpopManager?.initialized) try {
8753
8779
  resolvedDpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.reauthenticate" });
8754
8780
  } catch (error) {
8755
- if (this._clientBound) throw error;
8781
+ if (this.clientBound) throw error;
8756
8782
  logger$8.warn("[Session] Failed to create DPoP proof for reauthenticate:", error);
8757
8783
  }
8758
8784
  const request = RPCReauthenticate({
@@ -8764,7 +8790,7 @@ var ClientSessionManager = class extends Destroyable {
8764
8790
  logger$8.error("[Session] Re-authentication RPC failed:", err);
8765
8791
  throw err;
8766
8792
  })));
8767
- if (options?.clientBound) this._clientBound = true;
8793
+ if (options?.clientBound) this._wasClientBound = true;
8768
8794
  logger$8.debug("[Session] Re-authentication successful, updating stored auth state");
8769
8795
  } catch (error) {
8770
8796
  logger$8.error("[Session] Re-authentication failed:", error);
@@ -8787,14 +8813,14 @@ var ClientSessionManager = class extends Destroyable {
8787
8813
  const isReconnect = hasReconnectState && storedToken;
8788
8814
  let dpopToken;
8789
8815
  if (isReconnect) logger$8.debug("[Session] Reconnecting with stored jwt_token + authorization_state");
8790
- else if (this.onBeforeReconnect && this._clientBound) {
8816
+ else if (this.onBeforeReconnect && this.clientBound) {
8791
8817
  logger$8.debug("[Session] Refreshing credentials before fresh connect");
8792
8818
  await this.onBeforeReconnect();
8793
8819
  }
8794
- if ((!isReconnect || this._clientBound) && this.dpopManager?.initialized) try {
8820
+ if ((!isReconnect || this.clientBound) && this.dpopManager?.initialized) try {
8795
8821
  dpopToken = await this.dpopManager.createRpcProof({ method: "signalwire.connect" });
8796
8822
  } catch (error) {
8797
- if (this._clientBound) throw error;
8823
+ if (this.clientBound) throw error;
8798
8824
  logger$8.warn("[Session] Failed to create DPoP proof for connect, proceeding without:", error);
8799
8825
  }
8800
8826
  const rpcConnectRequest = RPCConnect({
@@ -8825,12 +8851,12 @@ var ClientSessionManager = class extends Destroyable {
8825
8851
  if (response.protocol) await this.transport.setProtocol(response.protocol);
8826
8852
  this._authorization$.next(response.authorization);
8827
8853
  this._iceServers$.next(response.ice_servers ?? []);
8828
- this._authenticated$.next(true);
8854
+ this._authState$.next({ kind: "authenticated" });
8829
8855
  logger$8.debug("[Session] Authentication completed successfully");
8830
8856
  }
8831
8857
  async disconnect() {
8832
8858
  this.transport.disconnect();
8833
- this._authenticated$.next(false);
8859
+ this._authState$.next({ kind: "unauthenticated" });
8834
8860
  await this.cleanupStoredConnectionParams();
8835
8861
  }
8836
8862
  async createInboundCall(invite) {
@@ -9604,11 +9630,10 @@ var WebSocketController = class WebSocketController extends Destroyable {
9604
9630
  }
9605
9631
  send(data) {
9606
9632
  if (this._status$.value === "connected" && this.socket?.readyState === 1) {
9607
- try {
9608
- logger$3.debug(`[WebSocketConnectionManager] Sending message:\n${JSON.stringify(JSON.parse(data), null, 2)}`);
9609
- } catch {
9610
- logger$3.warn(`[WebSocketConnectionManager] Sending non-JSON message:\n${data}`);
9611
- }
9633
+ logger$3.wsTraffic({
9634
+ type: "send",
9635
+ raw: data
9636
+ });
9612
9637
  this.socket.send(data);
9613
9638
  } else this.messageQueue.push(data);
9614
9639
  }
@@ -9671,11 +9696,10 @@ var WebSocketController = class WebSocketController extends Destroyable {
9671
9696
  this.handleConnectionError();
9672
9697
  }
9673
9698
  handleMessage(event) {
9674
- try {
9675
- logger$3.debug(`[WebSocketConnectionManager] Received message:\n${JSON.stringify(JSON.parse(event.data), null, 2)}`);
9676
- } catch {
9677
- logger$3.warn(`[WebSocketConnectionManager] Received non-JSON message:\n${event.data}`);
9678
- }
9699
+ logger$3.wsTraffic({
9700
+ type: "recv",
9701
+ raw: event.data
9702
+ });
9679
9703
  this._incomingMessages$.next(event);
9680
9704
  }
9681
9705
  handleConnectionError() {
@@ -9976,6 +10000,9 @@ var SignalWire = class extends Destroyable {
9976
10000
  if (this._options.webSocketConstructor) this._deps.WebSocket = this._options.webSocketConstructor;
9977
10001
  if (this._options.savePreferences) this.preferences.enableSavePreferences(this._deps.storage);
9978
10002
  if (this._options.webRTCApiProvider) this._deps.webRTCApiProvider = this._options.webRTCApiProvider;
10003
+ if (this._options.logger !== void 0) require_operators.setLogger(this._options.logger);
10004
+ if (this._options.logLevel) require_operators.setLogLevel(this._options.logLevel);
10005
+ if (this._options.debug) require_operators.setDebugOptions(this._options.debug);
9979
10006
  this._deviceController = this._deps.deviceController;
9980
10007
  if (!this._options.skipDeviceMonitoring) this._deviceController.enableDeviceMonitoring();
9981
10008
  this.subscribeTo(this._deviceController.errors$, (error) => {
@@ -10049,28 +10076,7 @@ var SignalWire = class extends Destroyable {
10049
10076
  logger$1.error("[SignalWire] Provided credentials have expired.");
10050
10077
  throw new require_operators.InvalidCredentialsError("Provided credentials have expired.");
10051
10078
  }
10052
- if (_credentials.expiry_at && credentialProvider?.refresh) {
10053
- const refreshFn = async () => {
10054
- if (!credentialProvider.refresh) throw new require_operators.InvalidCredentialsError("Credential provider does not support refresh");
10055
- return credentialProvider.refresh();
10056
- };
10057
- const refreshInterval = Math.max(_credentials.expiry_at - Date.now() - 5e3, 1e3);
10058
- this._refreshTimerId = setTimeout(async () => {
10059
- try {
10060
- const newCredentials = await refreshFn();
10061
- this._deps.credential = newCredentials;
10062
- this.persistCredential(newCredentials);
10063
- logger$1.info("[SignalWire] Credentials refreshed successfully.");
10064
- this.validateCredentials(credentialProvider, newCredentials).catch((error) => {
10065
- logger$1.error("[SignalWire] Credential refresh error:", error);
10066
- this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
10067
- });
10068
- } catch (error) {
10069
- logger$1.error("[SignalWire] Credential refresh failed:", error);
10070
- this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
10071
- }
10072
- }, refreshInterval);
10073
- }
10079
+ if (_credentials.expiry_at && credentialProvider?.refresh) this.scheduleCredentialRefresh(credentialProvider, _credentials.expiry_at);
10074
10080
  this._deps.credential = _credentials;
10075
10081
  this.persistCredential(_credentials);
10076
10082
  if (this.isConnected && this._clientSession.authenticated && _credentials.token) try {
@@ -10081,6 +10087,35 @@ var SignalWire = class extends Destroyable {
10081
10087
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
10082
10088
  }
10083
10089
  }
10090
+ /**
10091
+ * Schedules credential refresh with exponential backoff retry on failure.
10092
+ * On success, resets attempt counter and schedules the next refresh.
10093
+ * After exhausting retries, emits TokenRefreshError and disconnects.
10094
+ */
10095
+ scheduleCredentialRefresh(credentialProvider, expiresAt, attempt = 0) {
10096
+ if (this._refreshTimerId !== void 0) clearTimeout(this._refreshTimerId);
10097
+ 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);
10098
+ this._refreshTimerId = setTimeout(async () => {
10099
+ try {
10100
+ if (!credentialProvider.refresh) throw new require_operators.InvalidCredentialsError("Credential provider does not support refresh");
10101
+ const newCredentials = await credentialProvider.refresh();
10102
+ this._deps.credential = newCredentials;
10103
+ this.persistCredential(newCredentials);
10104
+ logger$1.info("[SignalWire] Credentials refreshed successfully.");
10105
+ if (newCredentials.expiry_at) this.scheduleCredentialRefresh(credentialProvider, newCredentials.expiry_at, 0);
10106
+ } catch (error) {
10107
+ const nextAttempt = attempt + 1;
10108
+ logger$1.error(`[SignalWire] Credential refresh failed (attempt ${nextAttempt}/${CREDENTIAL_REFRESH_MAX_RETRIES}):`, error);
10109
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
10110
+ if (nextAttempt < CREDENTIAL_REFRESH_MAX_RETRIES) this.scheduleCredentialRefresh(credentialProvider, expiresAt, nextAttempt);
10111
+ else {
10112
+ logger$1.error("[SignalWire] Credential refresh exhausted all retries. Disconnecting.");
10113
+ this._errors$.next(new require_operators.TokenRefreshError("Credential refresh failed after max retries"));
10114
+ this.disconnect();
10115
+ }
10116
+ }
10117
+ }, refreshInterval);
10118
+ }
10084
10119
  /** Persist credential to localStorage when persistSession is enabled. */
10085
10120
  persistCredential(credential) {
10086
10121
  if (!credential.token) return;
@@ -10122,9 +10157,9 @@ var SignalWire = class extends Destroyable {
10122
10157
  * unexpectedly (e.g. network change, server restart). Reconnection uses an
10123
10158
  * **exponential back-off** strategy:
10124
10159
  *
10125
- * - First retry after `reconnectDelayMin` (default **1 s**).
10160
+ * - First retry after `reconnectDelayMin` (default **0.1 s**).
10126
10161
  * - Each subsequent retry doubles the delay up to `reconnectDelayMax`
10127
- * (default **30 s**).
10162
+ * (default **3 s**).
10128
10163
  * - The delay resets to `reconnectDelayMin` once a connection succeeds.
10129
10164
  * - A per-attempt `connectionTimeout` (default **10 s**) aborts the
10130
10165
  * attempt and schedules the next retry if the server does not respond.
@@ -10176,6 +10211,8 @@ var SignalWire = class extends Destroyable {
10176
10211
  logger$1.debug("[SignalWire] Credential refreshed successfully for reconnect");
10177
10212
  } catch (error) {
10178
10213
  logger$1.error("[SignalWire] Failed to refresh credentials for reconnect:", error);
10214
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
10215
+ throw error;
10179
10216
  }
10180
10217
  };
10181
10218
  this.subscribeTo(this._clientSession.errors$, (error) => {
@@ -10374,6 +10411,10 @@ var SignalWire = class extends Destroyable {
10374
10411
  * which creates a fresh transport and session.
10375
10412
  */
10376
10413
  async disconnect() {
10414
+ if (this._refreshTimerId) {
10415
+ clearTimeout(this._refreshTimerId);
10416
+ this._refreshTimerId = void 0;
10417
+ }
10377
10418
  this._diagnosticsCollector?.record("connection", "disconnected");
10378
10419
  await this._clientSession.disconnect();
10379
10420
  this._clientSession.destroy();
@@ -10790,7 +10831,12 @@ async function embeddableCall(options) {
10790
10831
  if (!embedToken) requiredFailed.push("embedToken");
10791
10832
  if (!host) requiredFailed.push("host");
10792
10833
  if (requiredFailed.length > 0) return Promise.reject(new require_operators.DependencyError(`Missing required options: ${requiredFailed.join(", ")}`));
10793
- return await new SignalWire(new EmbedTokenCredentialProvider(host, embedToken)).dial(to);
10834
+ const client = new SignalWire(new EmbedTokenCredentialProvider(host, embedToken));
10835
+ const call = await client.dial(to);
10836
+ call.status$.pipe((0, rxjs.first)((status) => status === "destroyed")).subscribe(() => {
10837
+ client.disconnect();
10838
+ });
10839
+ return call;
10794
10840
  }
10795
10841
 
10796
10842
  //#endregion
@@ -10875,5 +10921,8 @@ exports.WebRTCCall = WebRTCCall;
10875
10921
  exports.embeddableCall = embeddableCall;
10876
10922
  exports.isSelfParticipant = isSelfParticipant;
10877
10923
  exports.ready = ready;
10924
+ exports.setDebugOptions = require_operators.setDebugOptions;
10925
+ exports.setLogLevel = require_operators.setLogLevel;
10926
+ exports.setLogger = require_operators.setLogger;
10878
10927
  exports.version = version;
10879
10928
  //# sourceMappingURL=index.cjs.map