@signalwire/js 4.0.0-beta.11 → 4.0.0-beta.12

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
@@ -457,9 +457,9 @@ var require_Subscriber = /* @__PURE__ */ __commonJSMin(((exports) => {
457
457
  var NotificationFactories_1 = require_NotificationFactories();
458
458
  var timeoutProvider_1 = require_timeoutProvider();
459
459
  var errorContext_1$2 = require_errorContext();
460
- var Subscriber$1 = function(_super) {
461
- __extends$16(Subscriber$2, _super);
462
- function Subscriber$2(destination) {
460
+ var Subscriber = function(_super) {
461
+ __extends$16(Subscriber$1, _super);
462
+ function Subscriber$1(destination) {
463
463
  var _this = _super.call(this) || this;
464
464
  _this.isStopped = false;
465
465
  if (destination) {
@@ -468,54 +468,54 @@ var require_Subscriber = /* @__PURE__ */ __commonJSMin(((exports) => {
468
468
  } else _this.destination = exports.EMPTY_OBSERVER;
469
469
  return _this;
470
470
  }
471
- Subscriber$2.create = function(next, error, complete) {
471
+ Subscriber$1.create = function(next, error, complete) {
472
472
  return new SafeSubscriber(next, error, complete);
473
473
  };
474
- Subscriber$2.prototype.next = function(value) {
474
+ Subscriber$1.prototype.next = function(value) {
475
475
  if (this.isStopped) handleStoppedNotification(NotificationFactories_1.nextNotification(value), this);
476
476
  else this._next(value);
477
477
  };
478
- Subscriber$2.prototype.error = function(err) {
478
+ Subscriber$1.prototype.error = function(err) {
479
479
  if (this.isStopped) handleStoppedNotification(NotificationFactories_1.errorNotification(err), this);
480
480
  else {
481
481
  this.isStopped = true;
482
482
  this._error(err);
483
483
  }
484
484
  };
485
- Subscriber$2.prototype.complete = function() {
485
+ Subscriber$1.prototype.complete = function() {
486
486
  if (this.isStopped) handleStoppedNotification(NotificationFactories_1.COMPLETE_NOTIFICATION, this);
487
487
  else {
488
488
  this.isStopped = true;
489
489
  this._complete();
490
490
  }
491
491
  };
492
- Subscriber$2.prototype.unsubscribe = function() {
492
+ Subscriber$1.prototype.unsubscribe = function() {
493
493
  if (!this.closed) {
494
494
  this.isStopped = true;
495
495
  _super.prototype.unsubscribe.call(this);
496
496
  this.destination = null;
497
497
  }
498
498
  };
499
- Subscriber$2.prototype._next = function(value) {
499
+ Subscriber$1.prototype._next = function(value) {
500
500
  this.destination.next(value);
501
501
  };
502
- Subscriber$2.prototype._error = function(err) {
502
+ Subscriber$1.prototype._error = function(err) {
503
503
  try {
504
504
  this.destination.error(err);
505
505
  } finally {
506
506
  this.unsubscribe();
507
507
  }
508
508
  };
509
- Subscriber$2.prototype._complete = function() {
509
+ Subscriber$1.prototype._complete = function() {
510
510
  try {
511
511
  this.destination.complete();
512
512
  } finally {
513
513
  this.unsubscribe();
514
514
  }
515
515
  };
516
- return Subscriber$2;
516
+ return Subscriber$1;
517
517
  }(Subscription_1$10.Subscription);
518
- exports.Subscriber = Subscriber$1;
518
+ exports.Subscriber = Subscriber;
519
519
  var _bind = Function.prototype.bind;
520
520
  function bind(fn, thisArg) {
521
521
  return _bind.call(fn, thisArg);
@@ -579,7 +579,7 @@ var require_Subscriber = /* @__PURE__ */ __commonJSMin(((exports) => {
579
579
  return _this;
580
580
  }
581
581
  return SafeSubscriber$1;
582
- }(Subscriber$1);
582
+ }(Subscriber);
583
583
  exports.SafeSubscriber = SafeSubscriber;
584
584
  function handleUnhandledError(error) {
585
585
  if (config_1$2.config.useDeprecatedSynchronousErrorHandling) errorContext_1$2.captureError(error);
@@ -1125,9 +1125,9 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1125
1125
  var ObjectUnsubscribedError_1$1 = require_ObjectUnsubscribedError();
1126
1126
  var arrRemove_1$6 = require_arrRemove();
1127
1127
  var errorContext_1 = require_errorContext();
1128
- var Subject$2 = function(_super) {
1129
- __extends$13(Subject$3, _super);
1130
- function Subject$3() {
1128
+ var Subject$1 = function(_super) {
1129
+ __extends$13(Subject$2, _super);
1130
+ function Subject$2() {
1131
1131
  var _this = _super.call(this) || this;
1132
1132
  _this.closed = false;
1133
1133
  _this.currentObservers = null;
@@ -1137,15 +1137,15 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1137
1137
  _this.thrownError = null;
1138
1138
  return _this;
1139
1139
  }
1140
- Subject$3.prototype.lift = function(operator) {
1140
+ Subject$2.prototype.lift = function(operator) {
1141
1141
  var subject = new AnonymousSubject(this, this);
1142
1142
  subject.operator = operator;
1143
1143
  return subject;
1144
1144
  };
1145
- Subject$3.prototype._throwIfClosed = function() {
1145
+ Subject$2.prototype._throwIfClosed = function() {
1146
1146
  if (this.closed) throw new ObjectUnsubscribedError_1$1.ObjectUnsubscribedError();
1147
1147
  };
1148
- Subject$3.prototype.next = function(value) {
1148
+ Subject$2.prototype.next = function(value) {
1149
1149
  var _this = this;
1150
1150
  errorContext_1.errorContext(function() {
1151
1151
  var e_1, _a;
@@ -1166,7 +1166,7 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1166
1166
  }
1167
1167
  });
1168
1168
  };
1169
- Subject$3.prototype.error = function(err) {
1169
+ Subject$2.prototype.error = function(err) {
1170
1170
  var _this = this;
1171
1171
  errorContext_1.errorContext(function() {
1172
1172
  _this._throwIfClosed();
@@ -1178,7 +1178,7 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1178
1178
  }
1179
1179
  });
1180
1180
  };
1181
- Subject$3.prototype.complete = function() {
1181
+ Subject$2.prototype.complete = function() {
1182
1182
  var _this = this;
1183
1183
  errorContext_1.errorContext(function() {
1184
1184
  _this._throwIfClosed();
@@ -1189,11 +1189,11 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1189
1189
  }
1190
1190
  });
1191
1191
  };
1192
- Subject$3.prototype.unsubscribe = function() {
1192
+ Subject$2.prototype.unsubscribe = function() {
1193
1193
  this.isStopped = this.closed = true;
1194
1194
  this.observers = this.currentObservers = null;
1195
1195
  };
1196
- Object.defineProperty(Subject$3.prototype, "observed", {
1196
+ Object.defineProperty(Subject$2.prototype, "observed", {
1197
1197
  get: function() {
1198
1198
  var _a;
1199
1199
  return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0;
@@ -1201,16 +1201,16 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1201
1201
  enumerable: false,
1202
1202
  configurable: true
1203
1203
  });
1204
- Subject$3.prototype._trySubscribe = function(subscriber) {
1204
+ Subject$2.prototype._trySubscribe = function(subscriber) {
1205
1205
  this._throwIfClosed();
1206
1206
  return _super.prototype._trySubscribe.call(this, subscriber);
1207
1207
  };
1208
- Subject$3.prototype._subscribe = function(subscriber) {
1208
+ Subject$2.prototype._subscribe = function(subscriber) {
1209
1209
  this._throwIfClosed();
1210
1210
  this._checkFinalizedStatuses(subscriber);
1211
1211
  return this._innerSubscribe(subscriber);
1212
1212
  };
1213
- Subject$3.prototype._innerSubscribe = function(subscriber) {
1213
+ Subject$2.prototype._innerSubscribe = function(subscriber) {
1214
1214
  var _this = this;
1215
1215
  var _a = this, hasError = _a.hasError, isStopped = _a.isStopped, observers = _a.observers;
1216
1216
  if (hasError || isStopped) return Subscription_1$6.EMPTY_SUBSCRIPTION;
@@ -1221,22 +1221,22 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1221
1221
  arrRemove_1$6.arrRemove(observers, subscriber);
1222
1222
  });
1223
1223
  };
1224
- Subject$3.prototype._checkFinalizedStatuses = function(subscriber) {
1224
+ Subject$2.prototype._checkFinalizedStatuses = function(subscriber) {
1225
1225
  var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, isStopped = _a.isStopped;
1226
1226
  if (hasError) subscriber.error(thrownError);
1227
1227
  else if (isStopped) subscriber.complete();
1228
1228
  };
1229
- Subject$3.prototype.asObservable = function() {
1229
+ Subject$2.prototype.asObservable = function() {
1230
1230
  var observable = new Observable_1$24.Observable();
1231
1231
  observable.source = this;
1232
1232
  return observable;
1233
1233
  };
1234
- Subject$3.create = function(destination, source) {
1234
+ Subject$2.create = function(destination, source) {
1235
1235
  return new AnonymousSubject(destination, source);
1236
1236
  };
1237
- return Subject$3;
1237
+ return Subject$2;
1238
1238
  }(Observable_1$24.Observable);
1239
- exports.Subject = Subject$2;
1239
+ exports.Subject = Subject$1;
1240
1240
  var AnonymousSubject = function(_super) {
1241
1241
  __extends$13(AnonymousSubject$1, _super);
1242
1242
  function AnonymousSubject$1(destination, source) {
@@ -1262,7 +1262,7 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1262
1262
  return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : Subscription_1$6.EMPTY_SUBSCRIPTION;
1263
1263
  };
1264
1264
  return AnonymousSubject$1;
1265
- }(Subject$2);
1265
+ }(Subject$1);
1266
1266
  exports.AnonymousSubject = AnonymousSubject;
1267
1267
  }));
1268
1268
 
@@ -1289,37 +1289,37 @@ var require_BehaviorSubject = /* @__PURE__ */ __commonJSMin(((exports) => {
1289
1289
  })();
1290
1290
  Object.defineProperty(exports, "__esModule", { value: true });
1291
1291
  exports.BehaviorSubject = void 0;
1292
- var BehaviorSubject$2 = function(_super) {
1293
- __extends$12(BehaviorSubject$3, _super);
1294
- function BehaviorSubject$3(_value) {
1292
+ var BehaviorSubject$1 = function(_super) {
1293
+ __extends$12(BehaviorSubject$2, _super);
1294
+ function BehaviorSubject$2(_value) {
1295
1295
  var _this = _super.call(this) || this;
1296
1296
  _this._value = _value;
1297
1297
  return _this;
1298
1298
  }
1299
- Object.defineProperty(BehaviorSubject$3.prototype, "value", {
1299
+ Object.defineProperty(BehaviorSubject$2.prototype, "value", {
1300
1300
  get: function() {
1301
1301
  return this.getValue();
1302
1302
  },
1303
1303
  enumerable: false,
1304
1304
  configurable: true
1305
1305
  });
1306
- BehaviorSubject$3.prototype._subscribe = function(subscriber) {
1306
+ BehaviorSubject$2.prototype._subscribe = function(subscriber) {
1307
1307
  var subscription = _super.prototype._subscribe.call(this, subscriber);
1308
1308
  !subscription.closed && subscriber.next(this._value);
1309
1309
  return subscription;
1310
1310
  };
1311
- BehaviorSubject$3.prototype.getValue = function() {
1311
+ BehaviorSubject$2.prototype.getValue = function() {
1312
1312
  var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, _value = _a._value;
1313
1313
  if (hasError) throw thrownError;
1314
1314
  this._throwIfClosed();
1315
1315
  return _value;
1316
1316
  };
1317
- BehaviorSubject$3.prototype.next = function(value) {
1317
+ BehaviorSubject$2.prototype.next = function(value) {
1318
1318
  _super.prototype.next.call(this, this._value = value);
1319
1319
  };
1320
- return BehaviorSubject$3;
1320
+ return BehaviorSubject$2;
1321
1321
  }(require_Subject().Subject);
1322
- exports.BehaviorSubject = BehaviorSubject$2;
1322
+ exports.BehaviorSubject = BehaviorSubject$1;
1323
1323
  }));
1324
1324
 
1325
1325
  //#endregion
@@ -3091,13 +3091,13 @@ var require_of = /* @__PURE__ */ __commonJSMin(((exports) => {
3091
3091
  exports.of = void 0;
3092
3092
  var args_1$12 = require_args();
3093
3093
  var from_1$7 = require_from();
3094
- function of$1() {
3094
+ function of$2() {
3095
3095
  var args = [];
3096
3096
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
3097
3097
  var scheduler = args_1$12.popScheduler(args);
3098
3098
  return from_1$7.from(args, scheduler);
3099
3099
  }
3100
- exports.of = of$1;
3100
+ exports.of = of$2;
3101
3101
  }));
3102
3102
 
3103
3103
  //#endregion
@@ -3246,7 +3246,7 @@ var require_firstValueFrom = /* @__PURE__ */ __commonJSMin(((exports) => {
3246
3246
  exports.firstValueFrom = void 0;
3247
3247
  var EmptyError_1$5 = require_EmptyError();
3248
3248
  var Subscriber_1$2 = require_Subscriber();
3249
- function firstValueFrom$6(source, config) {
3249
+ function firstValueFrom$7(source, config) {
3250
3250
  var hasConfig = typeof config === "object";
3251
3251
  return new Promise(function(resolve, reject) {
3252
3252
  var subscriber = new Subscriber_1$2.SafeSubscriber({
@@ -3263,7 +3263,7 @@ var require_firstValueFrom = /* @__PURE__ */ __commonJSMin(((exports) => {
3263
3263
  source.subscribe(subscriber);
3264
3264
  });
3265
3265
  }
3266
- exports.firstValueFrom = firstValueFrom$6;
3266
+ exports.firstValueFrom = firstValueFrom$7;
3267
3267
  }));
3268
3268
 
3269
3269
  //#endregion
@@ -3390,7 +3390,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3390
3390
  exports.map = void 0;
3391
3391
  var lift_1$64 = require_lift();
3392
3392
  var OperatorSubscriber_1$54 = require_OperatorSubscriber();
3393
- function map$18(project, thisArg) {
3393
+ function map$20(project, thisArg) {
3394
3394
  return lift_1$64.operate(function(source, subscriber) {
3395
3395
  var index = 0;
3396
3396
  source.subscribe(OperatorSubscriber_1$54.createOperatorSubscriber(subscriber, function(value) {
@@ -3398,7 +3398,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3398
3398
  }));
3399
3399
  });
3400
3400
  }
3401
- exports.map = map$18;
3401
+ exports.map = map$20;
3402
3402
  }));
3403
3403
 
3404
3404
  //#endregion
@@ -4140,7 +4140,7 @@ var require_timer = /* @__PURE__ */ __commonJSMin(((exports) => {
4140
4140
  var async_1$11 = require_async();
4141
4141
  var isScheduler_1 = require_isScheduler();
4142
4142
  var isDate_1$1 = require_isDate();
4143
- function timer$2(dueTime, intervalOrScheduler, scheduler) {
4143
+ function timer$3(dueTime, intervalOrScheduler, scheduler) {
4144
4144
  if (dueTime === void 0) dueTime = 0;
4145
4145
  if (scheduler === void 0) scheduler = async_1$11.async;
4146
4146
  var intervalDuration = -1;
@@ -4159,7 +4159,7 @@ var require_timer = /* @__PURE__ */ __commonJSMin(((exports) => {
4159
4159
  }, due);
4160
4160
  });
4161
4161
  }
4162
- exports.timer = timer$2;
4162
+ exports.timer = timer$3;
4163
4163
  }));
4164
4164
 
4165
4165
  //#endregion
@@ -4169,13 +4169,13 @@ var require_interval = /* @__PURE__ */ __commonJSMin(((exports) => {
4169
4169
  exports.interval = void 0;
4170
4170
  var async_1$10 = require_async();
4171
4171
  var timer_1$6 = require_timer();
4172
- function interval$2(period, scheduler) {
4172
+ function interval$4(period, scheduler) {
4173
4173
  if (period === void 0) period = 0;
4174
4174
  if (scheduler === void 0) scheduler = async_1$10.asyncScheduler;
4175
4175
  if (period < 0) period = 0;
4176
4176
  return timer_1$6.timer(period, period, scheduler);
4177
4177
  }
4178
- exports.interval = interval$2;
4178
+ exports.interval = interval$4;
4179
4179
  }));
4180
4180
 
4181
4181
  //#endregion
@@ -4327,13 +4327,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
4327
4327
  var innerFrom_1$28 = require_innerFrom();
4328
4328
  var argsOrArgArray_1$4 = require_argsOrArgArray();
4329
4329
  var OperatorSubscriber_1$48 = require_OperatorSubscriber();
4330
- function race$4() {
4330
+ function race$5() {
4331
4331
  var sources = [];
4332
4332
  for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
4333
4333
  sources = argsOrArgArray_1$4.argsOrArgArray(sources);
4334
4334
  return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
4335
4335
  }
4336
- exports.race = race$4;
4336
+ exports.race = race$5;
4337
4337
  function raceInit(sources) {
4338
4338
  return function(subscriber) {
4339
4339
  var subscriptions = [];
@@ -5331,7 +5331,7 @@ var require_take = /* @__PURE__ */ __commonJSMin(((exports) => {
5331
5331
  var empty_1$3 = require_empty();
5332
5332
  var lift_1$46 = require_lift();
5333
5333
  var OperatorSubscriber_1$35 = require_OperatorSubscriber();
5334
- function take$8(count$1) {
5334
+ function take$9(count$1) {
5335
5335
  return count$1 <= 0 ? function() {
5336
5336
  return empty_1$3.EMPTY;
5337
5337
  } : lift_1$46.operate(function(source, subscriber) {
@@ -5344,7 +5344,7 @@ var require_take = /* @__PURE__ */ __commonJSMin(((exports) => {
5344
5344
  }));
5345
5345
  });
5346
5346
  }
5347
- exports.take = take$8;
5347
+ exports.take = take$9;
5348
5348
  }));
5349
5349
 
5350
5350
  //#endregion
@@ -5470,7 +5470,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5470
5470
  var identity_1$10 = require_identity();
5471
5471
  var lift_1$42 = require_lift();
5472
5472
  var OperatorSubscriber_1$31 = require_OperatorSubscriber();
5473
- function distinctUntilChanged$8(comparator, keySelector) {
5473
+ function distinctUntilChanged$9(comparator, keySelector) {
5474
5474
  if (keySelector === void 0) keySelector = identity_1$10.identity;
5475
5475
  comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
5476
5476
  return lift_1$42.operate(function(source, subscriber) {
@@ -5486,7 +5486,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5486
5486
  }));
5487
5487
  });
5488
5488
  }
5489
- exports.distinctUntilChanged = distinctUntilChanged$8;
5489
+ exports.distinctUntilChanged = distinctUntilChanged$9;
5490
5490
  function defaultCompare(a, b) {
5491
5491
  return a === b;
5492
5492
  }
@@ -7182,17 +7182,17 @@ var require_timeInterval = /* @__PURE__ */ __commonJSMin(((exports) => {
7182
7182
  var last$2 = scheduler.now();
7183
7183
  source.subscribe(OperatorSubscriber_1$6.createOperatorSubscriber(subscriber, function(value) {
7184
7184
  var now = scheduler.now();
7185
- var interval$3 = now - last$2;
7185
+ var interval$5 = now - last$2;
7186
7186
  last$2 = now;
7187
- subscriber.next(new TimeInterval(value, interval$3));
7187
+ subscriber.next(new TimeInterval(value, interval$5));
7188
7188
  }));
7189
7189
  });
7190
7190
  }
7191
7191
  exports.timeInterval = timeInterval;
7192
7192
  var TimeInterval = function() {
7193
- function TimeInterval$1(value, interval$3) {
7193
+ function TimeInterval$1(value, interval$5) {
7194
7194
  this.value = value;
7195
- this.interval = interval$3;
7195
+ this.interval = interval$5;
7196
7196
  }
7197
7197
  return TimeInterval$1;
7198
7198
  }();
@@ -8933,12 +8933,12 @@ var require_cjs = /* @__PURE__ */ __commonJSMin(((exports) => {
8933
8933
 
8934
8934
  //#endregion
8935
8935
  //#region src/behaviors/Destroyable.ts
8936
- var import_cjs$29 = require_cjs();
8936
+ var import_cjs$30 = require_cjs();
8937
8937
  var Destroyable = class {
8938
8938
  constructor() {
8939
8939
  this.subscriptions = [];
8940
8940
  this.subjects = [];
8941
- this._destroyed$ = new import_cjs$29.Subject();
8941
+ this._destroyed$ = new import_cjs$30.Subject();
8942
8942
  }
8943
8943
  destroy() {
8944
8944
  this._observableCache?.clear();
@@ -8974,7 +8974,7 @@ var Destroyable = class {
8974
8974
  this._observableCache ??= /* @__PURE__ */ new Map();
8975
8975
  let cached = this._observableCache.get(publicKey);
8976
8976
  if (!cached) {
8977
- cached = factory().pipe((0, import_cjs$29.observeOn)(import_cjs$29.asapScheduler));
8977
+ cached = factory().pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8978
8978
  this._observableCache.set(publicKey, cached);
8979
8979
  }
8980
8980
  return cached;
@@ -8988,24 +8988,24 @@ var Destroyable = class {
8988
8988
  * Do NOT use for observables consumed internally by the SDK.
8989
8989
  */
8990
8990
  deferEmission(observable) {
8991
- return observable.pipe((0, import_cjs$29.observeOn)(import_cjs$29.asapScheduler));
8991
+ return observable.pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8992
8992
  }
8993
8993
  subscribeTo(observable, observerOrNext) {
8994
8994
  const subscription = observable.subscribe(observerOrNext);
8995
8995
  this.subscriptions.push(subscription);
8996
8996
  }
8997
8997
  createSubject() {
8998
- const subject = new import_cjs$29.Subject();
8998
+ const subject = new import_cjs$30.Subject();
8999
8999
  this.subjects.push(subject);
9000
9000
  return subject;
9001
9001
  }
9002
9002
  createReplaySubject(bufferSize, windowTime$1) {
9003
- const subject = new import_cjs$29.ReplaySubject(bufferSize, windowTime$1);
9003
+ const subject = new import_cjs$30.ReplaySubject(bufferSize, windowTime$1);
9004
9004
  this.subjects.push(subject);
9005
9005
  return subject;
9006
9006
  }
9007
9007
  createBehaviorSubject(initialValue) {
9008
- const subject = new import_cjs$29.BehaviorSubject(initialValue);
9008
+ const subject = new import_cjs$30.BehaviorSubject(initialValue);
9009
9009
  this.subjects.push(subject);
9010
9010
  return subject;
9011
9011
  }
@@ -9466,9 +9466,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9466
9466
  defaultLogger$1 = new Logger();
9467
9467
  defaultLogger$1.getLogger = function getLogger$1(name) {
9468
9468
  if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
9469
- var logger$30 = _loggersByName[name];
9470
- if (!logger$30) logger$30 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9471
- return logger$30;
9469
+ var logger$32 = _loggersByName[name];
9470
+ if (!logger$32) logger$32 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9471
+ return logger$32;
9472
9472
  };
9473
9473
  var _log = typeof window !== undefinedType ? window.log : void 0;
9474
9474
  defaultLogger$1.noConflict = function() {
@@ -9504,8 +9504,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
9504
9504
  defaultLogger.setLevel(defaultLoggerLevel);
9505
9505
  let userLogger = null;
9506
9506
  /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
9507
- const setLogger = (logger$30) => {
9508
- userLogger = logger$30;
9507
+ const setLogger = (logger$32) => {
9508
+ userLogger = logger$32;
9509
9509
  };
9510
9510
  let debugOptions = {};
9511
9511
  /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
@@ -9544,13 +9544,13 @@ const wsTraffic = (options) => {
9544
9544
  loggerInstance.debug(`[WebSocket] ${options.type.toUpperCase()}: non-JSON message`);
9545
9545
  return;
9546
9546
  }
9547
- else payload = options.payload;
9547
+ else ({payload} = options);
9548
9548
  const msg = shouldStringify(payload) ? JSON.stringify(payload, null, 2) : payload;
9549
9549
  loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
9550
9550
  };
9551
9551
  const getLogger = () => {
9552
- const logger$30 = getLoggerInstance();
9553
- return new Proxy(logger$30, { get(_target, prop, _receiver) {
9552
+ const logger$32 = getLoggerInstance();
9553
+ return new Proxy(logger$32, { get(_target, prop, _receiver) {
9554
9554
  if (prop === "wsTraffic") return wsTraffic;
9555
9555
  const instance = getLoggerInstance();
9556
9556
  const value = Reflect.get(instance, prop);
@@ -9602,8 +9602,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
9602
9602
 
9603
9603
  //#endregion
9604
9604
  //#region src/controllers/HTTPRequestController.ts
9605
- var import_cjs$28 = require_cjs();
9606
- const logger$29 = getLogger();
9605
+ const logger$31 = getLogger();
9607
9606
  const GET_PARAMS = {
9608
9607
  method: "GET",
9609
9608
  headers: { Accept: "application/json" }
@@ -9615,7 +9614,7 @@ const POST_PARAMS = {
9615
9614
  "Content-Type": "application/json"
9616
9615
  }
9617
9616
  };
9618
- var HTTPRequestController = class HTTPRequestController {
9617
+ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9619
9618
  static {
9620
9619
  this.defaultMaxRetries = 3;
9621
9620
  }
@@ -9636,11 +9635,12 @@ var HTTPRequestController = class HTTPRequestController {
9636
9635
  ]);
9637
9636
  }
9638
9637
  constructor(baseURL, getCredential, options = {}) {
9638
+ super();
9639
9639
  this.baseURL = baseURL;
9640
9640
  this.getCredential = getCredential;
9641
- this._responses$ = new import_cjs$28.Subject();
9642
- this._errors$ = new import_cjs$28.Subject();
9643
- this._status$ = new import_cjs$28.BehaviorSubject("idle");
9641
+ this._responses$ = this.createSubject();
9642
+ this._errors$ = this.createSubject();
9643
+ this._status$ = this.createBehaviorSubject("idle");
9644
9644
  this.maxRetries = options.maxRetries ?? HTTPRequestController.defaultMaxRetries;
9645
9645
  this.retryDelayMin = options.retryDelayMin ?? HTTPRequestController.defaultRetryDelayMinMs;
9646
9646
  this.retryDelayMax = options.retryDelayMax ?? HTTPRequestController.defaultRetryDelayMaxMs;
@@ -9666,7 +9666,7 @@ var HTTPRequestController = class HTTPRequestController {
9666
9666
  this._responses$.next(response);
9667
9667
  return response;
9668
9668
  } catch (error) {
9669
- logger$29.error("[HTTPRequestController] Request error:", error);
9669
+ logger$31.error("[HTTPRequestController] Request error:", error);
9670
9670
  this._status$.next("error");
9671
9671
  const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
9672
9672
  this._errors$.next(err);
@@ -9693,7 +9693,7 @@ var HTTPRequestController = class HTTPRequestController {
9693
9693
  const url = this.buildURL(request.url);
9694
9694
  const headers = this.buildHeaders(request.headers);
9695
9695
  const timeout$5 = request.timeout ?? this.requestTimeout;
9696
- logger$29.debug("[HTTPRequestController] Executing request:", {
9696
+ logger$31.debug("[HTTPRequestController] Executing request:", {
9697
9697
  method: request.method,
9698
9698
  url,
9699
9699
  headers: Object.keys(headers).reduce((acc, key) => {
@@ -9713,7 +9713,7 @@ var HTTPRequestController = class HTTPRequestController {
9713
9713
  });
9714
9714
  clearTimeout(timeoutId);
9715
9715
  const httpResponse = await this.convertResponse(response);
9716
- logger$29.debug("[HTTPRequestController] Response received:", {
9716
+ logger$31.debug("[HTTPRequestController] Response received:", {
9717
9717
  status: response.status,
9718
9718
  statusText: response.statusText,
9719
9719
  headers: [...response.headers.entries()],
@@ -9723,7 +9723,7 @@ var HTTPRequestController = class HTTPRequestController {
9723
9723
  } catch (error) {
9724
9724
  clearTimeout(timeoutId);
9725
9725
  if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
9726
- logger$29.error("[HTTPRequestController] Request failed:", error);
9726
+ logger$31.error("[HTTPRequestController] Request failed:", error);
9727
9727
  throw error;
9728
9728
  }
9729
9729
  }
@@ -9737,8 +9737,8 @@ var HTTPRequestController = class HTTPRequestController {
9737
9737
  const credential = this.getCredential();
9738
9738
  if (credential.token) {
9739
9739
  headers.Authorization = `Bearer ${credential.token}`;
9740
- logger$29.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9741
- } else logger$29.warn("[HTTPRequestController] No credentials available for authentication");
9740
+ logger$31.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9741
+ } else logger$31.warn("[HTTPRequestController] No credentials available for authentication");
9742
9742
  return headers;
9743
9743
  }
9744
9744
  /**
@@ -9950,6 +9950,18 @@ const DEFAULT_ICE_DISCONNECTED_GRACE_PERIOD_MS = 3e3;
9950
9950
  const DEFAULT_ICE_RESTART_TIMEOUT_MS$1 = 5e3;
9951
9951
  /** Maximum recovery attempts before emitting 'max_attempts_reached'. */
9952
9952
  const DEFAULT_MAX_RECOVERY_ATTEMPTS = 3;
9953
+ /** Upper bound in ms for waiting on iceGatheringState === 'complete' after an ICE restart. */
9954
+ const ICE_GATHERING_COMPLETE_TIMEOUT_MS = 1e4;
9955
+ /** Upper bound in ms for waiting on RTCPeerConnection.connectionState === 'connected' after a recovery ICE restart. */
9956
+ const PEER_CONNECTION_RECOVERY_WAIT_MS = 5e3;
9957
+ /** Polling interval in ms while waiting for RTCPeerConnection.connectionState to transition. */
9958
+ const PEER_CONNECTION_RECOVERY_POLL_MS = 100;
9959
+ /** Polling interval for LocalAudioPipeline.level$ (ms). ~30fps is smooth for meters. */
9960
+ const AUDIO_LEVEL_POLL_INTERVAL_MS = 33;
9961
+ /** RMS level threshold (0..1) above which the local participant is considered speaking. */
9962
+ const VAD_THRESHOLD = .03;
9963
+ /** Hold window in ms below the threshold before speaking$ flips back to false. */
9964
+ const VAD_HOLD_MS = 250;
9953
9965
  /** Whether to persist device selections to storage by default. */
9954
9966
  const DEFAULT_PERSIST_DEVICE_SELECTION = true;
9955
9967
  /** Whether to auto-apply device changes to active calls by default. */
@@ -9998,7 +10010,7 @@ function fromMsToSec(milliseconds) {
9998
10010
 
9999
10011
  //#endregion
10000
10012
  //#region src/containers/PreferencesContainer.ts
10001
- const logger$28 = getLogger();
10013
+ const logger$30 = getLogger();
10002
10014
  var PreferencesContainer = class PreferencesContainer {
10003
10015
  static get instance() {
10004
10016
  this._instance ??= new PreferencesContainer();
@@ -10660,7 +10672,7 @@ var ClientPreferences = class {
10660
10672
  if (!this._storage) return;
10661
10673
  const data = collectStoredPreferences();
10662
10674
  this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
10663
- logger$28.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10675
+ logger$30.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10664
10676
  });
10665
10677
  }
10666
10678
  /** Loads preferences from storage and applies them to the container. */
@@ -10669,7 +10681,7 @@ var ClientPreferences = class {
10669
10681
  this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
10670
10682
  if (stored) applyStoredPreferences(stored);
10671
10683
  }).catch((error) => {
10672
- logger$28.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10684
+ logger$30.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10673
10685
  });
10674
10686
  }
10675
10687
  };
@@ -10690,8 +10702,8 @@ function toError(value) {
10690
10702
 
10691
10703
  //#endregion
10692
10704
  //#region src/controllers/NavigatorDeviceController.ts
10693
- var import_cjs$27 = require_cjs();
10694
- const logger$27 = getLogger();
10705
+ var import_cjs$29 = require_cjs();
10706
+ const logger$29 = getLogger();
10695
10707
  /** Maps a device kind to its storage key. */
10696
10708
  const DEVICE_STORAGE_KEYS = {
10697
10709
  audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
@@ -10713,7 +10725,7 @@ var NavigatorDeviceController = class extends Destroyable {
10713
10725
  super();
10714
10726
  this.webRTCApiProvider = webRTCApiProvider;
10715
10727
  this.deviceChangeHandler = () => {
10716
- logger$27.debug("[DeviceController] Device change detected");
10728
+ logger$29.debug("[DeviceController] Device change detected");
10717
10729
  this.enumerateDevices();
10718
10730
  };
10719
10731
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
@@ -10750,17 +10762,17 @@ var NavigatorDeviceController = class extends Destroyable {
10750
10762
  return {};
10751
10763
  }
10752
10764
  get errors$() {
10753
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$)));
10765
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$)));
10754
10766
  }
10755
10767
  /** Observable that emits when the SDK auto-switches a device. */
10756
10768
  get deviceRecovered$() {
10757
- return this._deviceRecovered$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$));
10769
+ return this._deviceRecovered$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$));
10758
10770
  }
10759
10771
  get videoInputDisabled$() {
10760
- return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10772
+ return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10761
10773
  }
10762
10774
  get audioInputDisabled$() {
10763
- return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10775
+ return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10764
10776
  }
10765
10777
  get videoInputDisabled() {
10766
10778
  return this._videoInputDisabled$.value;
@@ -10769,22 +10781,22 @@ var NavigatorDeviceController = class extends Destroyable {
10769
10781
  return this._audioInputDisabled$.value;
10770
10782
  }
10771
10783
  get audioInputDevices$() {
10772
- return this.cachedObservable("audioInputDevices$", () => this._devicesState$.pipe((0, import_cjs$27.map)((state) => state.audioinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10784
+ return this.cachedObservable("audioInputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10773
10785
  }
10774
10786
  get audioOutputDevices$() {
10775
- return this.cachedObservable("audioOutputDevices$", () => this._devicesState$.pipe((0, import_cjs$27.map)((state) => state.audiooutput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10787
+ return this.cachedObservable("audioOutputDevices$", () => this._devicesState$.pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10776
10788
  }
10777
10789
  get videoInputDevices$() {
10778
- return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe((0, import_cjs$27.map)((state) => state.videoinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10790
+ 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$)));
10779
10791
  }
10780
10792
  get selectedAudioInputDevice$() {
10781
- return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$27.map)((state) => state.audioinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$), (0, import_cjs$27.tap)((info) => logger$27.debug("[DeviceController] Selected audio input device changed:", info))));
10793
+ 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))));
10782
10794
  }
10783
10795
  get selectedAudioOutputDevice$() {
10784
- return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$27.map)((state) => state.audiooutput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$), (0, import_cjs$27.tap)((info) => logger$27.debug("[DeviceController] Selected audio output device changed:", info))));
10796
+ 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))));
10785
10797
  }
10786
10798
  get selectedVideoInputDevice$() {
10787
- return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$27.map)((state) => state.videoinput), (0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$), (0, import_cjs$27.tap)((info) => logger$27.debug("[DeviceController] Selected video input device changed:", info))));
10799
+ 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))));
10788
10800
  }
10789
10801
  get selectedAudioInputDevice() {
10790
10802
  if (this._audioInputDisabled$.value) return null;
@@ -10859,7 +10871,7 @@ var NavigatorDeviceController = class extends Destroyable {
10859
10871
  if (device) this.persistDeviceSelection("audioinput", device);
10860
10872
  }
10861
10873
  selectVideoInputDevice(device) {
10862
- logger$27.debug("[DeviceController] Setting selected video input device:", device);
10874
+ logger$29.debug("[DeviceController] Setting selected video input device:", device);
10863
10875
  if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
10864
10876
  const previous = this._selectedDevicesState$.value.videoinput;
10865
10877
  if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
@@ -10880,7 +10892,7 @@ var NavigatorDeviceController = class extends Destroyable {
10880
10892
  }
10881
10893
  init() {
10882
10894
  this.loadPersistedDevices();
10883
- this.subscribeTo(this._devicesState$.pipe((0, import_cjs$27.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10895
+ this.subscribeTo(this._devicesState$.pipe((0, import_cjs$29.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10884
10896
  const currentSelected = this._selectedDevicesState$.value;
10885
10897
  const newAudioInput = this._audioInputDisabled$.value ? null : this.resolveDevice("audioinput", devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10886
10898
  const newAudioOutput = this.resolveDevice("audiooutput", devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
@@ -10916,7 +10928,7 @@ var NavigatorDeviceController = class extends Destroyable {
10916
10928
  }
10917
10929
  const fromHistory = this._deviceHistory.findInHistory(kind, devices);
10918
10930
  if (fromHistory) {
10919
- logger$27.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10931
+ logger$29.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10920
10932
  this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
10921
10933
  return fromHistory;
10922
10934
  }
@@ -10969,7 +10981,7 @@ var NavigatorDeviceController = class extends Destroyable {
10969
10981
  try {
10970
10982
  await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
10971
10983
  } catch (error) {
10972
- logger$27.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10984
+ logger$29.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10973
10985
  }
10974
10986
  }
10975
10987
  async loadPersistedDevices() {
@@ -10985,7 +10997,7 @@ var NavigatorDeviceController = class extends Destroyable {
10985
10997
  [kind]: stored
10986
10998
  };
10987
10999
  } catch (error) {
10988
- logger$27.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11000
+ logger$29.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
10989
11001
  }
10990
11002
  }
10991
11003
  /** Clears device history, persisted selections, and re-enumerates devices. */
@@ -11002,8 +11014,8 @@ var NavigatorDeviceController = class extends Destroyable {
11002
11014
  enableDeviceMonitoring() {
11003
11015
  this.disableDeviceMonitoring();
11004
11016
  this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
11005
- if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$27.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11006
- logger$27.debug("[DeviceController] Polling devices due to interval");
11017
+ if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11018
+ logger$29.debug("[DeviceController] Polling devices due to interval");
11007
11019
  this.enumerateDevices();
11008
11020
  });
11009
11021
  this.enumerateDevices();
@@ -11029,13 +11041,13 @@ var NavigatorDeviceController = class extends Destroyable {
11029
11041
  videoinput: []
11030
11042
  });
11031
11043
  this._devicesState$.next(devicesByKind);
11032
- logger$27.debug("[DeviceController] Devices enumerated:", {
11044
+ logger$29.debug("[DeviceController] Devices enumerated:", {
11033
11045
  audioInputs: devicesByKind.audioinput.length,
11034
11046
  audioOutputs: devicesByKind.audiooutput.length,
11035
11047
  videoInputs: devicesByKind.videoinput.length
11036
11048
  });
11037
11049
  } catch (error) {
11038
- logger$27.error("[DeviceController] Failed to enumerate devices:", error);
11050
+ logger$29.error("[DeviceController] Failed to enumerate devices:", error);
11039
11051
  this._errors$.next(toError(error));
11040
11052
  }
11041
11053
  }
@@ -11051,7 +11063,7 @@ var NavigatorDeviceController = class extends Destroyable {
11051
11063
  stream.getTracks().forEach((t) => t.stop());
11052
11064
  return capabilities;
11053
11065
  } catch (error) {
11054
- logger$27.error("[DeviceController] Failed to get device capabilities:", error);
11066
+ logger$29.error("[DeviceController] Failed to get device capabilities:", error);
11055
11067
  this._errors$.next(toError(error));
11056
11068
  throw error;
11057
11069
  }
@@ -11203,15 +11215,15 @@ var DependencyContainer = class {
11203
11215
  this._baseURL = this.apiHost;
11204
11216
  this._credential = {};
11205
11217
  }
11206
- get subscriberId() {
11207
- return this.subscriber.id;
11218
+ get userId() {
11219
+ return this.user.id;
11208
11220
  }
11209
- get subscriber() {
11210
- if (!this._subscriber) throw new DependencyError("Subscriber");
11211
- return this._subscriber;
11221
+ get user() {
11222
+ if (!this._user) throw new DependencyError("User");
11223
+ return this._user;
11212
11224
  }
11213
- set subscriber(subscriber) {
11214
- this._subscriber = subscriber;
11225
+ set user(user) {
11226
+ this._user = user;
11215
11227
  }
11216
11228
  get storage() {
11217
11229
  if (!this._storageManager) {
@@ -11257,16 +11269,16 @@ var DependencyContainer = class {
11257
11269
  this._deviceController = void 0;
11258
11270
  }
11259
11271
  get authorizationStateKey() {
11260
- return `sw:${this.subscriberId}:as`;
11272
+ return `sw:${this.userId}:as`;
11261
11273
  }
11262
11274
  get protocolKey() {
11263
- return `sw:${this.subscriberId}:pt`;
11275
+ return `sw:${this.userId}:pt`;
11264
11276
  }
11265
11277
  get attachedCallsKey() {
11266
- return `sw:${this.subscriberId}:att`;
11278
+ return `sw:${this.userId}:att`;
11267
11279
  }
11268
- getSubscriberFromAddressId() {
11269
- return this.subscriber.addresses[0]?.id ?? "";
11280
+ getUserFromAddressId() {
11281
+ return this.user.addresses[0]?.id ?? "";
11270
11282
  }
11271
11283
  set baseURL(baseURL) {
11272
11284
  this._baseURL = baseURL;
@@ -11302,7 +11314,7 @@ var DependencyContainer = class {
11302
11314
 
11303
11315
  //#endregion
11304
11316
  //#region src/controllers/CryptoController.ts
11305
- const logger$26 = getLogger();
11317
+ const logger$28 = getLogger();
11306
11318
  const DPOP_DB_NAME = "sw-dpop";
11307
11319
  const DPOP_DB_VERSION = 1;
11308
11320
  const DPOP_STORE_NAME = "keys";
@@ -11361,7 +11373,7 @@ async function loadKeyPairFromDB() {
11361
11373
  tx.oncomplete = () => db.close();
11362
11374
  });
11363
11375
  } catch (error) {
11364
- logger$26.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11376
+ logger$28.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11365
11377
  return null;
11366
11378
  }
11367
11379
  }
@@ -11381,7 +11393,7 @@ async function saveKeyPairToDB(keyPair) {
11381
11393
  };
11382
11394
  });
11383
11395
  } catch (error) {
11384
- logger$26.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11396
+ logger$28.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11385
11397
  }
11386
11398
  }
11387
11399
  async function deleteKeyPairFromDB() {
@@ -11400,7 +11412,7 @@ async function deleteKeyPairFromDB() {
11400
11412
  };
11401
11413
  });
11402
11414
  } catch (error) {
11403
- logger$26.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11415
+ logger$28.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11404
11416
  }
11405
11417
  }
11406
11418
  /**
@@ -11460,13 +11472,13 @@ var CryptoController = class {
11460
11472
  this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
11461
11473
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11462
11474
  this._initialized = true;
11463
- logger$26.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11475
+ logger$28.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11464
11476
  return this._fingerprint;
11465
11477
  } catch (error) {
11466
- logger$26.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11478
+ logger$28.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11467
11479
  await deleteKeyPairFromDB();
11468
11480
  }
11469
- logger$26.debug("[DPoP] Generating RSA key pair");
11481
+ logger$28.debug("[DPoP] Generating RSA key pair");
11470
11482
  this._keyPair = await crypto.subtle.generateKey({
11471
11483
  name: "RSASSA-PKCS1-v1_5",
11472
11484
  modulusLength: 2048,
@@ -11481,7 +11493,7 @@ var CryptoController = class {
11481
11493
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11482
11494
  this._initialized = true;
11483
11495
  await saveKeyPairToDB(this._keyPair);
11484
- logger$26.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11496
+ logger$28.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11485
11497
  return this._fingerprint;
11486
11498
  }
11487
11499
  /**
@@ -11547,7 +11559,7 @@ var CryptoController = class {
11547
11559
  this._fingerprint = null;
11548
11560
  this._initialized = false;
11549
11561
  deleteKeyPairFromDB();
11550
- logger$26.debug("[DPoP] Controller destroyed");
11562
+ logger$28.debug("[DPoP] Controller destroyed");
11551
11563
  }
11552
11564
  get publicJwk() {
11553
11565
  if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
@@ -11570,8 +11582,8 @@ var CryptoController = class {
11570
11582
 
11571
11583
  //#endregion
11572
11584
  //#region src/controllers/NetworkMonitor.ts
11573
- var import_cjs$26 = require_cjs();
11574
- const logger$25 = getLogger();
11585
+ var import_cjs$28 = require_cjs();
11586
+ const logger$27 = getLogger();
11575
11587
  /**
11576
11588
  * Safely check whether we are running in a browser environment
11577
11589
  * with `window` and the relevant event targets.
@@ -11614,13 +11626,13 @@ var NetworkMonitor = class extends Destroyable {
11614
11626
  this.attachListeners();
11615
11627
  }
11616
11628
  get isOnline$() {
11617
- return this._isOnline$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11629
+ return this._isOnline$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11618
11630
  }
11619
11631
  get isOnline() {
11620
11632
  return this._isOnline$.value;
11621
11633
  }
11622
11634
  get networkChange$() {
11623
- return this._networkChange$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11635
+ return this._networkChange$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11624
11636
  }
11625
11637
  destroy() {
11626
11638
  this.removeListeners();
@@ -11628,7 +11640,7 @@ var NetworkMonitor = class extends Destroyable {
11628
11640
  }
11629
11641
  attachListeners() {
11630
11642
  if (!hasBrowserNetworkEvents()) {
11631
- logger$25.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11643
+ logger$27.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11632
11644
  return;
11633
11645
  }
11634
11646
  window.addEventListener("online", this._onOnline);
@@ -11636,7 +11648,7 @@ var NetworkMonitor = class extends Destroyable {
11636
11648
  const connection = getNetworkConnection();
11637
11649
  if (connection) connection.addEventListener("change", this._onConnectionChange);
11638
11650
  this._listenersAttached = true;
11639
- logger$25.debug("NetworkMonitor: event listeners attached");
11651
+ logger$27.debug("NetworkMonitor: event listeners attached");
11640
11652
  }
11641
11653
  removeListeners() {
11642
11654
  if (!this._listenersAttached) return;
@@ -11647,10 +11659,10 @@ var NetworkMonitor = class extends Destroyable {
11647
11659
  if (connection) connection.removeEventListener("change", this._onConnectionChange);
11648
11660
  }
11649
11661
  this._listenersAttached = false;
11650
- logger$25.debug("NetworkMonitor: event listeners removed");
11662
+ logger$27.debug("NetworkMonitor: event listeners removed");
11651
11663
  }
11652
11664
  handleOnline() {
11653
- logger$25.info("NetworkMonitor: browser went online");
11665
+ logger$27.info("NetworkMonitor: browser went online");
11654
11666
  this._isOnline$.next(true);
11655
11667
  this._networkChange$.next({
11656
11668
  type: "online",
@@ -11659,7 +11671,7 @@ var NetworkMonitor = class extends Destroyable {
11659
11671
  });
11660
11672
  }
11661
11673
  handleOffline() {
11662
- logger$25.info("NetworkMonitor: browser went offline");
11674
+ logger$27.info("NetworkMonitor: browser went offline");
11663
11675
  this._isOnline$.next(false);
11664
11676
  this._networkChange$.next({
11665
11677
  type: "offline",
@@ -11668,7 +11680,7 @@ var NetworkMonitor = class extends Destroyable {
11668
11680
  }
11669
11681
  handleConnectionChange() {
11670
11682
  const networkType = getNetworkType();
11671
- logger$25.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11683
+ logger$27.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11672
11684
  this._networkChange$.next({
11673
11685
  type: "connection_change",
11674
11686
  timestamp: Date.now(),
@@ -11783,8 +11795,8 @@ function getNavigatorMediaDevices() {
11783
11795
 
11784
11796
  //#endregion
11785
11797
  //#region src/controllers/PreflightRunner.ts
11786
- var import_cjs$25 = require_cjs();
11787
- const logger$24 = getLogger();
11798
+ var import_cjs$27 = require_cjs();
11799
+ const logger$26 = getLogger();
11788
11800
  const DEFAULT_MEDIA_TEST_DURATION_S = 10;
11789
11801
  const ICE_GATHERING_TIMEOUT_MS = 1e4;
11790
11802
  const SIGNALING_RTT_TIMEOUT_MS = 5e3;
@@ -11833,7 +11845,7 @@ var PreflightRunner = class extends Destroyable {
11833
11845
  if (!this._options.skipMediaTest) try {
11834
11846
  bandwidth = await this.testMediaBandwidth(destination);
11835
11847
  } catch (error) {
11836
- logger$24.warn("[PreflightRunner] Media bandwidth test failed:", error);
11848
+ logger$26.warn("[PreflightRunner] Media bandwidth test failed:", error);
11837
11849
  warnings.push("Media bandwidth test failed");
11838
11850
  }
11839
11851
  return {
@@ -11845,7 +11857,7 @@ var PreflightRunner = class extends Destroyable {
11845
11857
  warnings
11846
11858
  };
11847
11859
  } catch (error) {
11848
- logger$24.error("[PreflightRunner] Preflight test failed:", error);
11860
+ logger$26.error("[PreflightRunner] Preflight test failed:", error);
11849
11861
  throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
11850
11862
  } finally {
11851
11863
  this.destroy();
@@ -11876,7 +11888,7 @@ var PreflightRunner = class extends Destroyable {
11876
11888
  if (track.kind === "video" && track.readyState === "live") videoWorking = true;
11877
11889
  }
11878
11890
  } catch (error) {
11879
- logger$24.warn("[PreflightRunner] Device test failed:", error);
11891
+ logger$26.warn("[PreflightRunner] Device test failed:", error);
11880
11892
  } finally {
11881
11893
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
11882
11894
  }
@@ -11903,7 +11915,7 @@ var PreflightRunner = class extends Destroyable {
11903
11915
  const candidateTypes = /* @__PURE__ */ new Set();
11904
11916
  const startTime = Date.now();
11905
11917
  const gatheringComplete = new Promise((resolve) => {
11906
- const timer$3 = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);
11918
+ const timer$4 = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);
11907
11919
  peerConnection.onicecandidate = (event) => {
11908
11920
  if (event.candidate) {
11909
11921
  const candidateStr = event.candidate.candidate;
@@ -11911,7 +11923,7 @@ var PreflightRunner = class extends Destroyable {
11911
11923
  if (candidateStr.includes("typ srflx")) candidateTypes.add("srflx");
11912
11924
  if (candidateStr.includes("typ relay")) candidateTypes.add("relay");
11913
11925
  } else {
11914
- clearTimeout(timer$3);
11926
+ clearTimeout(timer$4);
11915
11927
  resolve();
11916
11928
  }
11917
11929
  };
@@ -11934,7 +11946,7 @@ var PreflightRunner = class extends Destroyable {
11934
11946
  rttMs
11935
11947
  };
11936
11948
  } catch (error) {
11937
- logger$24.warn("[PreflightRunner] ICE connectivity test failed:", error);
11949
+ logger$26.warn("[PreflightRunner] ICE connectivity test failed:", error);
11938
11950
  return {
11939
11951
  type: "failed",
11940
11952
  turnReachable: false,
@@ -11952,7 +11964,7 @@ var PreflightRunner = class extends Destroyable {
11952
11964
  audio: true,
11953
11965
  video: false
11954
11966
  });
11955
- await (0, import_cjs$25.firstValueFrom)(call.status$.pipe((0, import_cjs$25.filter)((s) => s === "connected"), (0, import_cjs$25.take)(1), (0, import_cjs$25.timeout)(SIGNALING_RTT_TIMEOUT_MS)));
11967
+ await (0, import_cjs$27.firstValueFrom)(call.status$.pipe((0, import_cjs$27.filter)((s) => s === "connected"), (0, import_cjs$27.take)(1), (0, import_cjs$27.timeout)(SIGNALING_RTT_TIMEOUT_MS)));
11956
11968
  const durationMs = this._options.duration * 1e3;
11957
11969
  await new Promise((resolve) => setTimeout(resolve, durationMs));
11958
11970
  const metrics = call.networkMetrics;
@@ -11981,8 +11993,8 @@ var PreflightRunner = class extends Destroyable {
11981
11993
 
11982
11994
  //#endregion
11983
11995
  //#region src/controllers/VisibilityController.ts
11984
- var import_cjs$24 = require_cjs();
11985
- const logger$23 = getLogger();
11996
+ var import_cjs$26 = require_cjs();
11997
+ const logger$25 = getLogger();
11986
11998
  /**
11987
11999
  * Checks whether the document visibility API is available.
11988
12000
  */
@@ -12019,15 +12031,15 @@ var VisibilityController = class extends Destroyable {
12019
12031
  this._boundHandler = this._handleVisibilityChange.bind(this);
12020
12032
  if (this._hasVisibilityApi) {
12021
12033
  document.addEventListener("visibilitychange", this._boundHandler);
12022
- logger$23.debug("VisibilityController: listening for visibilitychange events");
12023
- } else logger$23.debug("VisibilityController: document visibility API not available, defaulting to visible");
12034
+ logger$25.debug("VisibilityController: listening for visibilitychange events");
12035
+ } else logger$25.debug("VisibilityController: document visibility API not available, defaulting to visible");
12024
12036
  }
12025
12037
  /**
12026
12038
  * Observable of the current visibility state.
12027
12039
  * Emits 'visible' or 'hidden'. Always starts with the current state.
12028
12040
  */
12029
12041
  get visibility$() {
12030
- return this._visibility$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12042
+ return this._visibility$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12031
12043
  }
12032
12044
  /**
12033
12045
  * The current visibility state value.
@@ -12040,12 +12052,12 @@ var VisibilityController = class extends Destroyable {
12040
12052
  * Each event includes the previous state, new state, and timestamp.
12041
12053
  */
12042
12054
  get visibilityChange$() {
12043
- return this._visibilityChange$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12055
+ return this._visibilityChange$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12044
12056
  }
12045
12057
  destroy() {
12046
12058
  if (this._hasVisibilityApi) {
12047
12059
  document.removeEventListener("visibilitychange", this._boundHandler);
12048
- logger$23.debug("VisibilityController: removed visibilitychange listener");
12060
+ logger$25.debug("VisibilityController: removed visibilitychange listener");
12049
12061
  }
12050
12062
  super.destroy();
12051
12063
  }
@@ -12063,7 +12075,7 @@ var VisibilityController = class extends Destroyable {
12063
12075
  timestamp: Date.now()
12064
12076
  };
12065
12077
  this._visibilityChange$.next(changeEvent);
12066
- logger$23.debug("VisibilityController: visibility changed", {
12078
+ logger$25.debug("VisibilityController: visibility changed", {
12067
12079
  from: previousState,
12068
12080
  to: newState
12069
12081
  });
@@ -12072,13 +12084,13 @@ var VisibilityController = class extends Destroyable {
12072
12084
 
12073
12085
  //#endregion
12074
12086
  //#region src/behaviors/Fetchable.ts
12075
- var import_cjs$23 = require_cjs();
12087
+ var import_cjs$25 = require_cjs();
12076
12088
  var Fetchable = class extends Destroyable {
12077
12089
  constructor(fromPath, http) {
12078
12090
  super();
12079
12091
  this.fromPath = fromPath;
12080
12092
  this.http = http;
12081
- this.fetched$ = (0, import_cjs$23.defer)(() => (0, import_cjs$23.from)(this.fetch())).pipe((0, import_cjs$23.shareReplay)(1), (0, import_cjs$23.takeUntil)(this.destroyed$));
12093
+ this.fetched$ = (0, import_cjs$25.defer)(() => (0, import_cjs$25.from)(this.fetch())).pipe((0, import_cjs$25.shareReplay)(1), (0, import_cjs$25.takeUntil)(this.destroyed$));
12082
12094
  }
12083
12095
  async fetch() {
12084
12096
  const response = await this.http.request({
@@ -12096,14 +12108,14 @@ var Fetchable = class extends Destroyable {
12096
12108
  };
12097
12109
 
12098
12110
  //#endregion
12099
- //#region src/core/entities/Subscriber.ts
12111
+ //#region src/core/entities/User.ts
12100
12112
  /**
12101
- * Authenticated subscriber profile.
12113
+ * Authenticated user profile.
12102
12114
  *
12103
12115
  * Fetched automatically when a {@link SignalWire} connects.
12104
12116
  * Contains identity, contact, and organization details.
12105
12117
  */
12106
- var Subscriber = class extends Fetchable {
12118
+ var User = class extends Fetchable {
12107
12119
  constructor(http) {
12108
12120
  super("/api/fabric/subscriber/info", http);
12109
12121
  }
@@ -12313,20 +12325,18 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
12313
12325
 
12314
12326
  //#endregion
12315
12327
  //#region src/managers/AttachManager.ts
12316
- const logger$22 = getLogger();
12328
+ const logger$24 = getLogger();
12317
12329
  var AttachManager = class {
12318
12330
  constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
12319
12331
  this.storage = storage;
12320
12332
  this.deviceController = deviceController;
12321
12333
  this.reconnectCallsTimeout = reconnectCallsTimeout;
12322
12334
  this.attachKey = attachKey;
12335
+ this.writeQueue = Promise.resolve();
12323
12336
  }
12324
12337
  async detachAll() {
12325
- const attached = await this.readAttached();
12326
- for (const callId of Object.keys(attached)) await this.detach({
12327
- id: callId,
12328
- nodeId: attached[callId].nodeId,
12329
- mediaDirections: attached[callId].mediaDirections
12338
+ await this.mutate((attached) => {
12339
+ return {};
12330
12340
  });
12331
12341
  }
12332
12342
  setSession(session) {
@@ -12336,7 +12346,7 @@ var AttachManager = class {
12336
12346
  try {
12337
12347
  return await this.storage.getItem(this.attachKey) ?? {};
12338
12348
  } catch (error) {
12339
- logger$22.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12349
+ logger$24.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12340
12350
  return {};
12341
12351
  }
12342
12352
  }
@@ -12344,34 +12354,50 @@ var AttachManager = class {
12344
12354
  try {
12345
12355
  await this.storage.setItem(this.attachKey, attached);
12346
12356
  } catch (error) {
12347
- logger$22.warn("[AttachManager] Failed to write attached calls to storage", error);
12357
+ logger$24.warn("[AttachManager] Failed to write attached calls to storage", error);
12348
12358
  }
12349
12359
  }
12360
+ /**
12361
+ * Serialize a read-modify-write operation against the attached-calls
12362
+ * storage. The mutator receives the current state and returns the new
12363
+ * state. Concurrent calls queue behind the in-flight one so writes never
12364
+ * interleave.
12365
+ */
12366
+ async mutate(mutator) {
12367
+ const next = this.writeQueue.then(async () => {
12368
+ const updated = await mutator(await this.readAttached());
12369
+ await this.writeAttached(updated);
12370
+ });
12371
+ this.writeQueue = next.catch(() => void 0);
12372
+ return next;
12373
+ }
12350
12374
  async attach(call) {
12351
12375
  if (!call.to) {
12352
- logger$22.warn("[AttachManager] Skip attach for calls with no destination");
12376
+ logger$24.warn("[AttachManager] Skip attach for calls with no destination");
12353
12377
  return;
12354
12378
  }
12379
+ const destination = call.to;
12355
12380
  const attachment = {
12356
12381
  nodeId: call.nodeId,
12357
- destination: call.to,
12382
+ destination,
12358
12383
  mediaDirections: call.mediaDirections,
12359
12384
  audioInputDevice: call.mediaDirections.audio !== "inactive" ? this.deviceController.selectedAudioInputDevice : null,
12360
12385
  videoInputDevice: call.mediaDirections.video !== "inactive" ? this.deviceController.selectedVideoInputDevice : null,
12361
12386
  attachedAt: Date.now()
12362
12387
  };
12363
- const updated = {
12364
- ...await this.readAttached(),
12388
+ await this.mutate((attached) => ({
12389
+ ...attached,
12365
12390
  [call.id]: attachment
12366
- };
12367
- await this.writeAttached(updated);
12391
+ }));
12368
12392
  }
12369
12393
  async detach(call) {
12370
- const { [call.id]: _, ...remaining } = await this.readAttached();
12371
- await this.writeAttached(remaining);
12394
+ await this.mutate((attached) => {
12395
+ const { [call.id]: _, ...remaining } = attached;
12396
+ return remaining;
12397
+ });
12372
12398
  }
12373
12399
  async flush() {
12374
- await this.writeAttached({});
12400
+ await this.mutate(() => ({}));
12375
12401
  }
12376
12402
  /**
12377
12403
  * Reattach to previously active calls by sending verto.invite with
@@ -12400,15 +12426,15 @@ var AttachManager = class {
12400
12426
  callId,
12401
12427
  ...options
12402
12428
  });
12403
- logger$22.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12429
+ logger$24.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12404
12430
  succeeded = true;
12405
12431
  break;
12406
12432
  } catch (error) {
12407
- logger$22.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12433
+ logger$24.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12408
12434
  if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
12409
12435
  }
12410
12436
  if (!succeeded) {
12411
- logger$22.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12437
+ logger$24.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12412
12438
  await this.detach({
12413
12439
  id: callId,
12414
12440
  mediaDirections: attachment.mediaDirections
@@ -12443,20 +12469,31 @@ var AttachManager = class {
12443
12469
  };
12444
12470
  }
12445
12471
  /**
12446
- * Consume stored attachment data for a pending call (used by session-level
12447
- * verto.attach handler as a future path when server supports it).
12472
+ * Look up stored attachment data for a call id and return CallOptions
12473
+ * suitable for rehydrating a reattached call. Returns undefined when no
12474
+ * matching entry exists in storage.
12475
+ *
12476
+ * Used by the session-level verto.attach handler when the server pushes
12477
+ * an attach event for a call the client doesn't have an object for yet
12478
+ * (e.g. after a reload).
12448
12479
  */
12449
- consumePendingAttachment(_callId) {}
12450
- async detachExpired() {
12480
+ async consumePendingAttachment(callId) {
12451
12481
  const attached = await this.readAttached();
12482
+ if (!Object.hasOwn(attached, callId)) return;
12483
+ return this.buildCallOptions(attached[callId]);
12484
+ }
12485
+ async detachExpired() {
12452
12486
  const now = Date.now();
12453
12487
  const timeout$5 = this.reconnectCallsTimeout;
12454
- const expired = Object.entries(attached).filter(([, attachment]) => now - attachment.attachedAt > timeout$5);
12455
- if (expired.length > 0) {
12488
+ await this.mutate((attached) => {
12456
12489
  const remaining = { ...attached };
12457
- for (const [callId] of expired) delete remaining[callId];
12458
- await this.writeAttached(remaining);
12459
- }
12490
+ let changed = false;
12491
+ for (const [callId, attachment] of Object.entries(attached)) if (now - attachment.attachedAt > timeout$5) {
12492
+ delete remaining[callId];
12493
+ changed = true;
12494
+ }
12495
+ return changed ? remaining : attached;
12496
+ });
12460
12497
  }
12461
12498
  };
12462
12499
 
@@ -12503,12 +12540,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
12503
12540
  exports.race = void 0;
12504
12541
  var argsOrArgArray_1 = require_argsOrArgArray();
12505
12542
  var raceWith_1$1 = require_raceWith();
12506
- function race$3() {
12543
+ function race$4() {
12507
12544
  var args = [];
12508
12545
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
12509
12546
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
12510
12547
  }
12511
- exports.race = race$3;
12548
+ exports.race = race$4;
12512
12549
  }));
12513
12550
 
12514
12551
  //#endregion
@@ -13436,7 +13473,7 @@ function computeCapabilities(capabilities) {
13436
13473
 
13437
13474
  //#endregion
13438
13475
  //#region src/core/capabilities/SelfCapabilities.ts
13439
- var import_cjs$22 = require_cjs();
13476
+ var import_cjs$24 = require_cjs();
13440
13477
  /**
13441
13478
  * SelfCapabilities manages the capability state for the self participant.
13442
13479
  *
@@ -13472,7 +13509,7 @@ var SelfCapabilities = class extends Destroyable {
13472
13509
  }
13473
13510
  /** Observable for self member capabilities */
13474
13511
  get self$() {
13475
- return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.self), (0, import_cjs$22.distinctUntilChanged)()));
13512
+ return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.self), (0, import_cjs$24.distinctUntilChanged)()));
13476
13513
  }
13477
13514
  /** Current self member capabilities */
13478
13515
  get self() {
@@ -13480,7 +13517,7 @@ var SelfCapabilities = class extends Destroyable {
13480
13517
  }
13481
13518
  /** Observable for other member capabilities */
13482
13519
  get member$() {
13483
- return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.member), (0, import_cjs$22.distinctUntilChanged)()));
13520
+ return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.member), (0, import_cjs$24.distinctUntilChanged)()));
13484
13521
  }
13485
13522
  /** Current other member capabilities */
13486
13523
  get member() {
@@ -13488,7 +13525,7 @@ var SelfCapabilities = class extends Destroyable {
13488
13525
  }
13489
13526
  /** Observable for end call capability */
13490
13527
  get end$() {
13491
- return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.end), (0, import_cjs$22.distinctUntilChanged)()));
13528
+ return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.end), (0, import_cjs$24.distinctUntilChanged)()));
13492
13529
  }
13493
13530
  /** Current end call capability */
13494
13531
  get end() {
@@ -13496,7 +13533,7 @@ var SelfCapabilities = class extends Destroyable {
13496
13533
  }
13497
13534
  /** Observable for set layout capability */
13498
13535
  get setLayout$() {
13499
- return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.setLayout), (0, import_cjs$22.distinctUntilChanged)()));
13536
+ return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.setLayout), (0, import_cjs$24.distinctUntilChanged)()));
13500
13537
  }
13501
13538
  /** Current set layout capability */
13502
13539
  get setLayout() {
@@ -13504,7 +13541,7 @@ var SelfCapabilities = class extends Destroyable {
13504
13541
  }
13505
13542
  /** Observable for send digit capability */
13506
13543
  get sendDigit$() {
13507
- return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.sendDigit), (0, import_cjs$22.distinctUntilChanged)()));
13544
+ return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.sendDigit), (0, import_cjs$24.distinctUntilChanged)()));
13508
13545
  }
13509
13546
  /** Current send digit capability */
13510
13547
  get sendDigit() {
@@ -13512,7 +13549,7 @@ var SelfCapabilities = class extends Destroyable {
13512
13549
  }
13513
13550
  /** Observable for vmuted hide capability */
13514
13551
  get vmutedHide$() {
13515
- return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.vmutedHide), (0, import_cjs$22.distinctUntilChanged)()));
13552
+ return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.vmutedHide), (0, import_cjs$24.distinctUntilChanged)()));
13516
13553
  }
13517
13554
  /** Current vmuted hide capability */
13518
13555
  get vmutedHide() {
@@ -13520,7 +13557,7 @@ var SelfCapabilities = class extends Destroyable {
13520
13557
  }
13521
13558
  /** Observable for lock capability */
13522
13559
  get lock$() {
13523
- return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.lock), (0, import_cjs$22.distinctUntilChanged)()));
13560
+ return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.lock), (0, import_cjs$24.distinctUntilChanged)()));
13524
13561
  }
13525
13562
  /** Current lock capability */
13526
13563
  get lock() {
@@ -13528,7 +13565,7 @@ var SelfCapabilities = class extends Destroyable {
13528
13565
  }
13529
13566
  /** Observable for device capability */
13530
13567
  get device$() {
13531
- return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.device), (0, import_cjs$22.distinctUntilChanged)()));
13568
+ return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.device), (0, import_cjs$24.distinctUntilChanged)()));
13532
13569
  }
13533
13570
  /** Current device capability */
13534
13571
  get device() {
@@ -13536,7 +13573,7 @@ var SelfCapabilities = class extends Destroyable {
13536
13573
  }
13537
13574
  /** Observable for screenshare capability */
13538
13575
  get screenshare$() {
13539
- return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.screenshare), (0, import_cjs$22.distinctUntilChanged)()));
13576
+ return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.screenshare), (0, import_cjs$24.distinctUntilChanged)()));
13540
13577
  }
13541
13578
  /** Current screenshare capability */
13542
13579
  get screenshare() {
@@ -13564,7 +13601,7 @@ function toggleHandraiseMethod(is) {
13564
13601
 
13565
13602
  //#endregion
13566
13603
  //#region src/core/entities/Participant.ts
13567
- const logger$21 = getLogger();
13604
+ const logger$23 = getLogger();
13568
13605
  const initialState = {};
13569
13606
  /**
13570
13607
  * Represents a participant in a call.
@@ -13616,15 +13653,35 @@ var Participant = class extends Destroyable {
13616
13653
  get deaf$() {
13617
13654
  return this.cachedObservable("deaf$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)()));
13618
13655
  }
13619
- /** Observable of the participant's microphone input volume. */
13656
+ /**
13657
+ * Observable of the participant's **server-side** microphone input volume
13658
+ * as reported by the mix engine. This is gain applied on the bridged audio
13659
+ * leg (FreeSWITCH channel read volume), NOT the local browser mic. For a
13660
+ * local PC mic control, see {@link Call.setLocalMicrophoneGain}.
13661
+ *
13662
+ * @see {@link setAudioInputVolume}
13663
+ */
13620
13664
  get inputVolume$() {
13621
13665
  return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)()));
13622
13666
  }
13623
- /** Observable of the participant's speaker output volume. */
13667
+ /**
13668
+ * Observable of the participant's **server-side** speaker output volume as
13669
+ * reported by the mix engine (FreeSWITCH channel write volume). NOT the
13670
+ * local HTML `<audio>` element volume — set that on your own element.
13671
+ *
13672
+ * @see {@link setAudioOutputVolume}
13673
+ */
13624
13674
  get outputVolume$() {
13625
13675
  return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)()));
13626
13676
  }
13627
- /** Observable of the microphone input sensitivity level. */
13677
+ /**
13678
+ * Observable of the **conference-only** microphone energy/gate sensitivity
13679
+ * level for this member. Routes through the conferencing mix engine and has
13680
+ * no effect on 1:1 WebRTC calls. Populated from `member.updated` events for
13681
+ * conference members.
13682
+ *
13683
+ * @see {@link setAudioInputSensitivity}
13684
+ */
13628
13685
  get inputSensitivity$() {
13629
13686
  return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)()));
13630
13687
  }
@@ -13652,9 +13709,9 @@ var Participant = class extends Destroyable {
13652
13709
  get meta$() {
13653
13710
  return this.cachedObservable("meta$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.meta), (0, import_operators$1.distinctUntilChanged)()));
13654
13711
  }
13655
- /** Observable of the participant's subscriber ID. */
13656
- get subscriberId$() {
13657
- return this.cachedObservable("subscriberId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)()));
13712
+ /** Observable of the participant's user ID. */
13713
+ get userId$() {
13714
+ return this.cachedObservable("userId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)()));
13658
13715
  }
13659
13716
  /** Observable of the participant's address ID. */
13660
13717
  get addressId$() {
@@ -13712,15 +13769,25 @@ var Participant = class extends Destroyable {
13712
13769
  get deaf() {
13713
13770
  return this._state$.value.deaf ?? false;
13714
13771
  }
13715
- /** Current microphone input volume level, or `undefined` if not set. */
13772
+ /**
13773
+ * Current **server-side** microphone input volume as reported by the mix
13774
+ * engine, or `undefined` if not set. Not the local PC mic — see
13775
+ * {@link Call.setLocalMicrophoneGain} for browser-side control.
13776
+ */
13716
13777
  get inputVolume() {
13717
13778
  return this._state$.value.input_volume;
13718
13779
  }
13719
- /** Current speaker output volume level, or `undefined` if not set. */
13780
+ /**
13781
+ * Current **server-side** speaker output volume from the mix engine, or
13782
+ * `undefined` if not set. Not the local `<audio>` element volume.
13783
+ */
13720
13784
  get outputVolume() {
13721
13785
  return this._state$.value.output_volume;
13722
13786
  }
13723
- /** Current microphone input sensitivity level, or `undefined` if not set. */
13787
+ /**
13788
+ * Current **conference-only** microphone sensitivity/gate level, or
13789
+ * `undefined` if not set. Applies only to conference members.
13790
+ */
13724
13791
  get inputSensitivity() {
13725
13792
  return this._state$.value.input_sensitivity;
13726
13793
  }
@@ -13748,8 +13815,8 @@ var Participant = class extends Destroyable {
13748
13815
  get meta() {
13749
13816
  return this._state$.value.meta;
13750
13817
  }
13751
- /** Subscriber ID of this participant, or `undefined` if not available. */
13752
- get subscriberId() {
13818
+ /** User ID of this participant, or `undefined` if not available. */
13819
+ get userId() {
13753
13820
  return this._state$.value.subscriber_id;
13754
13821
  }
13755
13822
  /** Address ID of this participant, or `undefined` if not available. */
@@ -13824,19 +13891,44 @@ var Participant = class extends Destroyable {
13824
13891
  async toggleLowbitrate() {
13825
13892
  throw new UnimplementedError();
13826
13893
  }
13827
- /** Sets the microphone input sensitivity level. */
13894
+ /**
13895
+ * Adjusts the **conference-only** microphone energy gate / sensitivity level
13896
+ * for this member. Routes through the conferencing mix engine
13897
+ * (`signalwire.conferencing member.set_input_sensitivity`) and has no effect
13898
+ * on 1:1 WebRTC calls — for those, use browser audio constraints via
13899
+ * {@link Call.setNoiseSuppression} / {@link Call.setAutoGainControl}.
13900
+ *
13901
+ * This is **not** a local PC mic gain control; it only changes how the
13902
+ * server-side mixer decides to open the mic gate on this participant.
13903
+ *
13904
+ * @param value - Sensitivity level as understood by the conference engine
13905
+ * (integer, larger values are more sensitive).
13906
+ */
13828
13907
  async setAudioInputSensitivity(value) {
13829
13908
  await this.executeMethod(this.id, "call.microphone.sensitivity.set", { sensitivity: value });
13830
13909
  }
13831
13910
  /**
13832
- * Sets the microphone input volume level.
13911
+ * Sets the **server-side** microphone volume on this participant's bridged
13912
+ * call leg. Applies a multiplier to the audio flowing through the mix
13913
+ * engine (FreeSWITCH channel read volume) — changes what other participants
13914
+ * hear, not what the local browser captures.
13915
+ *
13916
+ * For local PC mic gain, use {@link Call.setLocalMicrophoneGain} instead.
13917
+ *
13833
13918
  * @param value - Volume level (0-100).
13834
13919
  */
13835
13920
  async setAudioInputVolume(value) {
13836
13921
  await this.executeMethod(this.id, "call.microphone.volume.set", { volume: value });
13837
13922
  }
13838
13923
  /**
13839
- * Sets the speaker output volume level.
13924
+ * Sets the **server-side** speaker volume on this participant's bridged call
13925
+ * leg (FreeSWITCH channel write volume) — what this participant hears from
13926
+ * the mix before it reaches their client.
13927
+ *
13928
+ * For local playback volume (the `<audio>` element the consumer attaches
13929
+ * `remoteStream` to), set `audioElement.volume` directly in the consumer's
13930
+ * code.
13931
+ *
13840
13932
  * @param value - Volume level (0-100).
13841
13933
  */
13842
13934
  async setAudioOutputVolume(value) {
@@ -13942,7 +14034,7 @@ var SelfParticipant = class extends Participant {
13942
14034
  try {
13943
14035
  await this.vertoManager.addScreenMedia();
13944
14036
  } catch (error) {
13945
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14037
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13946
14038
  }
13947
14039
  }
13948
14040
  /** Observable of the current screen share status. */
@@ -13962,7 +14054,7 @@ var SelfParticipant = class extends Participant {
13962
14054
  try {
13963
14055
  await this.vertoManager.addInputDevice(options);
13964
14056
  } catch (error) {
13965
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14057
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13966
14058
  }
13967
14059
  }
13968
14060
  /** Removes an additional media input device by ID. */
@@ -14024,7 +14116,7 @@ var SelfParticipant = class extends Participant {
14024
14116
  */
14025
14117
  exitStudioModeIfActive() {
14026
14118
  if (this._studioAudio$.value) {
14027
- logger$21.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14119
+ logger$23.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14028
14120
  this._studioAudio$.next(false);
14029
14121
  }
14030
14122
  }
@@ -14048,7 +14140,7 @@ var SelfParticipant = class extends Participant {
14048
14140
  try {
14049
14141
  await super.mute();
14050
14142
  } catch (error) {
14051
- logger$21.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14143
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14052
14144
  } finally {
14053
14145
  this.vertoManager.muteMainAudioInputDevice();
14054
14146
  }
@@ -14058,7 +14150,7 @@ var SelfParticipant = class extends Participant {
14058
14150
  try {
14059
14151
  await super.unmute();
14060
14152
  } catch (error) {
14061
- logger$21.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14153
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14062
14154
  } finally {
14063
14155
  await this.vertoManager.unmuteMainAudioInputDevice();
14064
14156
  }
@@ -14068,7 +14160,7 @@ var SelfParticipant = class extends Participant {
14068
14160
  try {
14069
14161
  await super.muteVideo();
14070
14162
  } catch (error) {
14071
- logger$21.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14163
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14072
14164
  } finally {
14073
14165
  this.vertoManager.muteMainVideoInputDevice();
14074
14166
  }
@@ -14078,7 +14170,7 @@ var SelfParticipant = class extends Participant {
14078
14170
  try {
14079
14171
  await super.unmuteVideo();
14080
14172
  } catch (error) {
14081
- logger$21.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14173
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14082
14174
  } finally {
14083
14175
  await this.vertoManager.unmuteMainVideoInputDevice();
14084
14176
  }
@@ -14172,7 +14264,7 @@ function isLayoutChangedPayload(value) {
14172
14264
 
14173
14265
  //#endregion
14174
14266
  //#region src/operators/filterNull.ts
14175
- var import_cjs$21 = require_cjs();
14267
+ var import_cjs$23 = require_cjs();
14176
14268
  /**
14177
14269
  * RxJS operator that filters out `null` and `undefined` values with type narrowing.
14178
14270
  *
@@ -14184,7 +14276,7 @@ var import_cjs$21 = require_cjs();
14184
14276
  * ```
14185
14277
  */
14186
14278
  function filterNull() {
14187
- return (0, import_cjs$21.filter)((value) => value != null);
14279
+ return (0, import_cjs$23.filter)((value) => value != null);
14188
14280
  }
14189
14281
 
14190
14282
  //#endregion
@@ -14199,7 +14291,7 @@ const getValueFrom = (obj, path, defaultValue) => {
14199
14291
 
14200
14292
  //#endregion
14201
14293
  //#region src/operators/filterEventAs.ts
14202
- var import_cjs$20 = require_cjs();
14294
+ var import_cjs$22 = require_cjs();
14203
14295
  var import_operators = require_operators();
14204
14296
  /**
14205
14297
  * RxJS operator that filters events based on a predicate and maps matching events.
@@ -14233,7 +14325,7 @@ var import_operators = require_operators();
14233
14325
  * ```
14234
14326
  */
14235
14327
  function ifIsMap(predicate, mapFn) {
14236
- return (0, import_cjs$20.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14328
+ return (0, import_cjs$22.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14237
14329
  }
14238
14330
  /**
14239
14331
  * Generic RxJS operator that filters events using a type guard and extracts a property.
@@ -14275,38 +14367,38 @@ function ifIsMap(predicate, mapFn) {
14275
14367
  * ```
14276
14368
  */
14277
14369
  function filterAs(predicate, resultPath) {
14278
- return (0, import_cjs$20.pipe)(ifIsMap(predicate, (event) => {
14370
+ return (0, import_cjs$22.pipe)(ifIsMap(predicate, (event) => {
14279
14371
  return getValueFrom(event, resultPath);
14280
14372
  }), (0, import_operators.filter)((value) => value !== void 0));
14281
14373
  }
14282
14374
 
14283
14375
  //#endregion
14284
14376
  //#region src/operators/throwOnRPCError.ts
14285
- var import_cjs$19 = require_cjs();
14286
- const logger$20 = getLogger();
14377
+ var import_cjs$21 = require_cjs();
14378
+ const logger$22 = getLogger();
14287
14379
  /**
14288
14380
  * RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
14289
14381
  * Passes successful responses through unchanged.
14290
14382
  */
14291
14383
  function throwOnRPCError() {
14292
- return (0, import_cjs$19.map)((response) => {
14384
+ return (0, import_cjs$21.map)((response) => {
14293
14385
  if (response.error) {
14294
- logger$20.error("[throwOnRPCError] RPC error response:", {
14386
+ logger$22.error("[throwOnRPCError] RPC error response:", {
14295
14387
  code: response.error.code,
14296
14388
  message: response.error.message,
14297
14389
  data: response.error.data
14298
14390
  });
14299
14391
  throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
14300
14392
  }
14301
- logger$20.debug("[throwOnRPCError] RPC successful response:", response);
14393
+ logger$22.debug("[throwOnRPCError] RPC successful response:", response);
14302
14394
  return response;
14303
14395
  });
14304
14396
  }
14305
14397
 
14306
14398
  //#endregion
14307
14399
  //#region src/managers/CallEventsManager.ts
14308
- var import_cjs$18 = require_cjs();
14309
- const logger$19 = getLogger();
14400
+ var import_cjs$20 = require_cjs();
14401
+ const logger$21 = getLogger();
14310
14402
  const initialSessionState = {};
14311
14403
  /** @internal */
14312
14404
  var CallEventsManager = class extends Destroyable {
@@ -14322,7 +14414,7 @@ var CallEventsManager = class extends Destroyable {
14322
14414
  this.initSubscriptions();
14323
14415
  }
14324
14416
  get participants$() {
14325
- return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$18.map)((participantsRecord) => Object.values(participantsRecord))));
14417
+ return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$20.map)((participantsRecord) => Object.values(participantsRecord))));
14326
14418
  }
14327
14419
  get participants() {
14328
14420
  return Object.values(this._participants$.value);
@@ -14340,40 +14432,40 @@ var CallEventsManager = class extends Destroyable {
14340
14432
  return this.callIds.has(callId);
14341
14433
  }
14342
14434
  get recording$() {
14343
- return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recording), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14435
+ return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recording), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14344
14436
  }
14345
14437
  get recordings$() {
14346
- return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recordings), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14438
+ return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recordings), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14347
14439
  }
14348
14440
  get streaming$() {
14349
- return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streaming), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14441
+ return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streaming), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14350
14442
  }
14351
14443
  get streams$() {
14352
- return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streams), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14444
+ return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streams), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14353
14445
  }
14354
14446
  get playbacks$() {
14355
- return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.playbacks), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14447
+ return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.playbacks), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14356
14448
  }
14357
14449
  get raiseHandPriority$() {
14358
- return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.prioritize_handraise), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14450
+ return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.prioritize_handraise), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14359
14451
  }
14360
14452
  get locked$() {
14361
- return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.locked), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14453
+ return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.locked), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14362
14454
  }
14363
14455
  get meta$() {
14364
- return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.meta), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14456
+ return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.meta), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14365
14457
  }
14366
14458
  get capabilities$() {
14367
- return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.capabilities), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14459
+ return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.capabilities), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14368
14460
  }
14369
14461
  get layout$() {
14370
- return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_name), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14462
+ return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_name), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14371
14463
  }
14372
14464
  get layouts$() {
14373
- return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layouts), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14465
+ return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layouts), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14374
14466
  }
14375
14467
  get layoutLayers$() {
14376
- return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_layers), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14468
+ return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_layers), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14377
14469
  }
14378
14470
  get self() {
14379
14471
  return this._self$.value;
@@ -14410,7 +14502,7 @@ var CallEventsManager = class extends Destroyable {
14410
14502
  }
14411
14503
  initSubscriptions() {
14412
14504
  this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
14413
- logger$19.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14505
+ logger$21.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14414
14506
  callId: callJoinedEvent.call_id,
14415
14507
  roomSessionId: callJoinedEvent.room_session_id
14416
14508
  });
@@ -14437,19 +14529,19 @@ var CallEventsManager = class extends Destroyable {
14437
14529
  if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
14438
14530
  });
14439
14531
  this.subscribeTo(this.memberUpdates$, (member) => {
14440
- logger$19.debug("[CallEventsManager] Handling member update event for member ID:", member);
14532
+ logger$21.debug("[CallEventsManager] Handling member update event for member ID:", member);
14441
14533
  this.upsertParticipant(member);
14442
14534
  });
14443
14535
  this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
14444
- logger$19.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14536
+ logger$21.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14445
14537
  const participants = { ...this._participants$.value };
14446
14538
  if (memberLeftEvent.member.member_id in participants) {
14447
14539
  delete participants[memberLeftEvent.member.member_id];
14448
14540
  this._participants$.next(participants);
14449
- } else logger$19.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14541
+ } else logger$21.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14450
14542
  });
14451
14543
  this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
14452
- logger$19.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14544
+ logger$21.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14453
14545
  const roomSession = callUpdatedEvent.room_session;
14454
14546
  this._sessionState$.next({
14455
14547
  ...this._sessionState$.value,
@@ -14464,7 +14556,7 @@ var CallEventsManager = class extends Destroyable {
14464
14556
  });
14465
14557
  });
14466
14558
  this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
14467
- logger$19.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14559
+ logger$21.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14468
14560
  this._sessionState$.next({
14469
14561
  ...this._sessionState$.value,
14470
14562
  layout_name: layoutChangedEvent.id,
@@ -14474,10 +14566,10 @@ var CallEventsManager = class extends Destroyable {
14474
14566
  });
14475
14567
  }
14476
14568
  updateParticipantPositions(layoutChangedEvent) {
14477
- if (Object.keys(this._participants$.value).length > 0 && !layoutChangedEvent.layers.some((layer) => !!layer.member_id)) logger$19.warn("[CallEventsManager] No layers with member_id found in layout.changed event. Nothing to update.");
14569
+ 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.");
14478
14570
  layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
14479
14571
  if (!(layer.member_id in this._participants$.value)) {
14480
- logger$19.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14572
+ logger$21.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14481
14573
  return false;
14482
14574
  }
14483
14575
  return true;
@@ -14500,7 +14592,7 @@ var CallEventsManager = class extends Destroyable {
14500
14592
  layouts: response.result.layouts
14501
14593
  });
14502
14594
  }).catch((error) => {
14503
- logger$19.error("[CallEventsManager] Error fetching layouts:", error);
14595
+ logger$21.error("[CallEventsManager] Error fetching layouts:", error);
14504
14596
  });
14505
14597
  }
14506
14598
  updateParticipants(members) {
@@ -14516,7 +14608,7 @@ var CallEventsManager = class extends Destroyable {
14516
14608
  }
14517
14609
  const participant = this._participants$.value[member.member_id];
14518
14610
  const oldValue = participant.value;
14519
- logger$19.debug("[CallEventsManager] Updating participant:", member.member_id, {
14611
+ logger$21.debug("[CallEventsManager] Updating participant:", member.member_id, {
14520
14612
  oldValue,
14521
14613
  newValue: member
14522
14614
  });
@@ -14528,18 +14620,18 @@ var CallEventsManager = class extends Destroyable {
14528
14620
  this._participants$.next(this._participants$.value);
14529
14621
  }
14530
14622
  get callJoinedEvent$() {
14531
- return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$18.filter)(isCallJoinedPayload), (0, import_cjs$18.tap)((event) => {
14532
- logger$19.debug("[CallEventsManager] Call joined event:", event);
14623
+ return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
14624
+ logger$21.debug("[CallEventsManager] Call joined event:", event);
14533
14625
  })));
14534
14626
  }
14535
14627
  get layoutChangedEvent$() {
14536
- return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$18.tap)((event) => {
14537
- logger$19.debug("[CallEventsManager] Layout changed event:", event);
14628
+ return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
14629
+ logger$21.debug("[CallEventsManager] Layout changed event:", event);
14538
14630
  })));
14539
14631
  }
14540
14632
  get memberUpdates$() {
14541
- return this.cachedObservable("memberUpdates$", () => (0, import_cjs$18.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$18.map)((event) => event.member), (0, import_cjs$18.tap)((event) => {
14542
- logger$19.debug("[CallEventsManager] Member update event:", event);
14633
+ 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) => {
14634
+ logger$21.debug("[CallEventsManager] Member update event:", event);
14543
14635
  })));
14544
14636
  }
14545
14637
  destroy() {
@@ -14795,8 +14887,8 @@ function appendStereoParams(fmtpLine, maxBitrate) {
14795
14887
 
14796
14888
  //#endregion
14797
14889
  //#region src/controllers/ICEGatheringController.ts
14798
- var import_cjs$17 = require_cjs();
14799
- const logger$18 = getLogger();
14890
+ var import_cjs$19 = require_cjs();
14891
+ const logger$20 = getLogger();
14800
14892
  var ICEGatheringController = class extends Destroyable {
14801
14893
  constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
14802
14894
  super();
@@ -14804,23 +14896,23 @@ var ICEGatheringController = class extends Destroyable {
14804
14896
  this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
14805
14897
  this.onicegatheringstatechangeHandler = () => {
14806
14898
  const { iceGatheringState } = this.peerConnection;
14807
- logger$18.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14899
+ logger$20.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14808
14900
  if (iceGatheringState === "gathering") this._iceCandidatesState.next({
14809
14901
  state: "gathering",
14810
14902
  validSDP: false
14811
14903
  });
14812
14904
  };
14813
14905
  this.onicecandidateHandler = (event) => {
14814
- logger$18.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14906
+ logger$20.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14815
14907
  this.removeTimer("iceCandidateTimer");
14816
14908
  if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
14817
14909
  if (this.peerConnection.iceGatheringState !== "complete") {
14818
- logger$18.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14910
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14819
14911
  this.handleICECandidateTimeout();
14820
14912
  }
14821
14913
  }, this.iceCandidateTimeout);
14822
14914
  else {
14823
- logger$18.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14915
+ logger$20.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14824
14916
  this.removeTimer("iceGatheringTimer");
14825
14917
  this.handleICEGatheringComplete();
14826
14918
  }
@@ -14833,12 +14925,12 @@ var ICEGatheringController = class extends Destroyable {
14833
14925
  this.iceGatheringTimeout = options.iceGatheringTimeout ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;
14834
14926
  this.relayOnly = options.relayOnly ?? false;
14835
14927
  this.setupEventListeners();
14836
- this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$17.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14928
+ this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$19.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14837
14929
  if (isNegotiating) {
14838
14930
  this.setupEventListeners();
14839
14931
  this.iceGatheringTimer = setTimeout(() => {
14840
14932
  if (this.peerConnection.iceGatheringState !== "complete") {
14841
- logger$18.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14933
+ logger$20.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14842
14934
  this.handleICEGatheringTimeout();
14843
14935
  }
14844
14936
  }, this.iceGatheringTimeout);
@@ -14852,7 +14944,7 @@ var ICEGatheringController = class extends Destroyable {
14852
14944
  this.peerConnection.addEventListener("icegatheringstatechange", this.onicegatheringstatechangeHandler);
14853
14945
  }
14854
14946
  get iceCandidatesState$() {
14855
- return this._iceCandidatesState.pipe((0, import_cjs$17.withLatestFrom)(this.peerConnectionControllerNegotiating$), (0, import_cjs$17.filter)(([_, isNegotiating]) => isNegotiating), (0, import_cjs$17.map)(([state, _]) => state.state));
14947
+ return this._iceCandidatesState.pipe((0, import_cjs$19.withLatestFrom)(this.peerConnectionControllerNegotiating$), (0, import_cjs$19.filter)(([_, isNegotiating]) => isNegotiating), (0, import_cjs$19.map)(([state, _]) => state.state));
14856
14948
  }
14857
14949
  get hasValidLocalDescriptionSDP() {
14858
14950
  const sdp = this.peerConnection.localDescription?.sdp;
@@ -14865,9 +14957,9 @@ var ICEGatheringController = class extends Destroyable {
14865
14957
  this.relayOnly = value;
14866
14958
  }
14867
14959
  handleICEGatheringComplete() {
14868
- logger$18.debug("[ICEGatheringController] Handling ICE gathering complete");
14869
- logger$18.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14870
- logger$18.debug("[ICEGatheringController] ICE gathering complete");
14960
+ logger$20.debug("[ICEGatheringController] Handling ICE gathering complete");
14961
+ logger$20.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14962
+ logger$20.debug("[ICEGatheringController] ICE gathering complete");
14871
14963
  this._iceCandidatesState.next({
14872
14964
  state: "complete",
14873
14965
  validSDP: this.hasValidLocalDescriptionSDP
@@ -14883,21 +14975,21 @@ var ICEGatheringController = class extends Destroyable {
14883
14975
  this.removeTimer("iceGatheringTimer");
14884
14976
  const validSDP = this.hasValidLocalDescriptionSDP;
14885
14977
  if (validSDP) {
14886
- logger$18.debug("[ICEGatheringController] Local SDP is valid");
14978
+ logger$20.debug("[ICEGatheringController] Local SDP is valid");
14887
14979
  this._iceCandidatesState.next({
14888
14980
  state: "timeout",
14889
14981
  validSDP
14890
14982
  });
14891
14983
  this.stopGathering();
14892
- } else logger$18.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14984
+ } else logger$20.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14893
14985
  }
14894
14986
  handleICECandidateTimeout() {
14895
14987
  if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
14896
- logger$18.warn("[ICEGatheringController] ICE candidate timeout");
14988
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout");
14897
14989
  const validSDP = this.hasValidLocalDescriptionSDP;
14898
14990
  if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
14899
14991
  else {
14900
- logger$18.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14992
+ logger$20.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14901
14993
  this._iceCandidatesState.next({
14902
14994
  state: "timeout",
14903
14995
  validSDP
@@ -14906,22 +14998,22 @@ var ICEGatheringController = class extends Destroyable {
14906
14998
  }
14907
14999
  }
14908
15000
  restartICEGatheringWithRelayOnly() {
14909
- logger$18.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
15001
+ logger$20.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
14910
15002
  this.relayOnly = true;
14911
15003
  this.peerConnection.setConfiguration({
14912
15004
  ...this.peerConnection.getConfiguration(),
14913
15005
  iceTransportPolicy: "relay"
14914
15006
  });
14915
- if (!(this.peerConnection.connectionState === "connected")) this.peerConnection.restartIce();
15007
+ this.peerConnection.restartIce();
14916
15008
  }
14917
- removeTimer(timer$3) {
14918
- if (this[timer$3]) {
14919
- clearTimeout(this[timer$3]);
14920
- this[timer$3] = void 0;
15009
+ removeTimer(timer$4) {
15010
+ if (this[timer$4]) {
15011
+ clearTimeout(this[timer$4]);
15012
+ this[timer$4] = void 0;
14921
15013
  }
14922
15014
  }
14923
15015
  clearAllTimers() {
14924
- logger$18.debug("[ICEGatheringController] Clearing all timers");
15016
+ logger$20.debug("[ICEGatheringController] Clearing all timers");
14925
15017
  this.removeTimer("iceGatheringTimer");
14926
15018
  this.removeTimer("iceCandidateTimer");
14927
15019
  }
@@ -14930,17 +15022,181 @@ var ICEGatheringController = class extends Destroyable {
14930
15022
  this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
14931
15023
  }
14932
15024
  destroy() {
14933
- logger$18.debug("[ICEGatheringController] Destroying ICEGatheringController");
15025
+ logger$20.debug("[ICEGatheringController] Destroying ICEGatheringController");
14934
15026
  this.clearAllTimers();
14935
15027
  this.removeEventListeners();
14936
15028
  super.destroy();
14937
15029
  }
14938
15030
  };
14939
15031
 
15032
+ //#endregion
15033
+ //#region src/controllers/LocalAudioPipeline.ts
15034
+ var import_cjs$18 = require_cjs();
15035
+ const logger$19 = getLogger();
15036
+ /**
15037
+ * Web Audio pipeline for the local microphone stream.
15038
+ *
15039
+ * Wraps the raw mic `MediaStreamTrack` in a graph of:
15040
+ *
15041
+ * ```
15042
+ * MediaStreamAudioSourceNode → GainNode → AnalyserNode → MediaStreamAudioDestinationNode
15043
+ * ```
15044
+ *
15045
+ * The {@link outputTrack} from the destination node is what callers should
15046
+ * attach to the `RTCRtpSender` in place of the raw mic track. The same
15047
+ * destination track is reused across input changes (device switch, mute /
15048
+ * unmute track replacement) so the sender reference stays stable — only the
15049
+ * source end of the graph is rebuilt.
15050
+ *
15051
+ * The pipeline owns a single {@link AudioContext}. Callers must invoke
15052
+ * {@link destroy} to release it when the call ends.
15053
+ */
15054
+ var LocalAudioPipeline = class extends Destroyable {
15055
+ constructor(options = {}) {
15056
+ super();
15057
+ this._inputSource = null;
15058
+ this._inputStream = null;
15059
+ this._lastSpokeAt = 0;
15060
+ this._gain$ = this.createBehaviorSubject(1);
15061
+ this._pttMultiplier = 1;
15062
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
15063
+ this._gainNode = this._audioContext.createGain();
15064
+ this._analyser = this._audioContext.createAnalyser();
15065
+ this._analyser.fftSize = 2048;
15066
+ this._analyser.smoothingTimeConstant = .3;
15067
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
15068
+ this._destination = this._audioContext.createMediaStreamDestination();
15069
+ this._gainNode.connect(this._analyser);
15070
+ this._analyser.connect(this._destination);
15071
+ this._speakingThreshold = options.speakingThreshold ?? VAD_THRESHOLD;
15072
+ this._speakingHoldMs = options.speakingHoldMs ?? VAD_HOLD_MS;
15073
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
15074
+ const initial = options.initialGain ?? 1;
15075
+ this._gain$.next(initial);
15076
+ this.applyEffectiveGain();
15077
+ }
15078
+ /** Observable of the current gain value (0..2). */
15079
+ get gain$() {
15080
+ return this._gain$.asObservable();
15081
+ }
15082
+ /** Current gain value (0..2). */
15083
+ get gain() {
15084
+ return this._gain$.value;
15085
+ }
15086
+ /**
15087
+ * Processed output track to attach to the RTCRtpSender. Stable reference
15088
+ * across input changes, so `sender.replaceTrack(pipeline.outputTrack)` only
15089
+ * needs to be called once.
15090
+ */
15091
+ get outputTrack() {
15092
+ const [track] = this._destination.stream.getAudioTracks();
15093
+ return track;
15094
+ }
15095
+ /**
15096
+ * Root-mean-square audio level of the input signal, 0..1. Emits on a fixed
15097
+ * interval (~30fps by default).
15098
+ */
15099
+ get level$() {
15100
+ return this.deferEmission((0, import_cjs$18.interval)(this._pollIntervalMs, import_cjs$18.animationFrameScheduler).pipe((0, import_cjs$18.map)(() => this.computeLevel())));
15101
+ }
15102
+ /**
15103
+ * Boolean VAD derived from {@link level$}. True while level ≥ threshold or
15104
+ * during the hold window after the last frame that crossed the threshold.
15105
+ */
15106
+ get speaking$() {
15107
+ return this.deferEmission(this.level$.pipe((0, import_cjs$18.map)((level) => this.evaluateSpeaking(level)), (0, import_cjs$18.distinctUntilChanged)()));
15108
+ }
15109
+ /**
15110
+ * Set gain multiplier applied to the input signal. 0 = silence,
15111
+ * 1 = unity, 2 = 2x. Values are clamped to [0, 2]. The effective gain on
15112
+ * the graph also respects the current PTT state.
15113
+ */
15114
+ setGain(value) {
15115
+ const clamped = Math.max(0, Math.min(2, value));
15116
+ this._gain$.next(clamped);
15117
+ this.applyEffectiveGain();
15118
+ }
15119
+ /**
15120
+ * Silence the graph when `active = false`, otherwise restore the configured
15121
+ * gain. Use this from a PTT handler: released → `false`, held → `true`.
15122
+ * Orthogonal to {@link setGain} — once PTT returns to active, the last
15123
+ * configured gain reappears.
15124
+ */
15125
+ setPTTActive(active) {
15126
+ this._pttMultiplier = active ? 1 : 0;
15127
+ this.applyEffectiveGain();
15128
+ }
15129
+ applyEffectiveGain() {
15130
+ this._gainNode.gain.value = this._gain$.value * this._pttMultiplier;
15131
+ }
15132
+ /**
15133
+ * Wire a new raw mic track as the pipeline's input. Replaces any previous
15134
+ * input source and reconnects the graph so {@link outputTrack} continues
15135
+ * to emit the processed audio. Pass `null` to disconnect the input (the
15136
+ * output track stays alive but emits silence).
15137
+ *
15138
+ * Also resumes the underlying AudioContext on attach — Chrome creates it
15139
+ * in a suspended state and the graph won't process (the destination
15140
+ * track emits silence) until resume() succeeds.
15141
+ */
15142
+ setInputTrack(track) {
15143
+ if (this._inputSource) {
15144
+ try {
15145
+ this._inputSource.disconnect();
15146
+ } catch (error) {
15147
+ logger$19.debug("[LocalAudioPipeline] input disconnect warning:", error);
15148
+ }
15149
+ this._inputSource = null;
15150
+ }
15151
+ if (this._inputStream) this._inputStream = null;
15152
+ if (!track) return;
15153
+ this._inputStream = new MediaStream([track]);
15154
+ this._inputSource = this._audioContext.createMediaStreamSource(this._inputStream);
15155
+ this._inputSource.connect(this._gainNode);
15156
+ if (this._audioContext.state === "suspended") this._audioContext.resume().catch((error) => {
15157
+ logger$19.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15158
+ });
15159
+ }
15160
+ destroy() {
15161
+ if (this._inputSource) {
15162
+ try {
15163
+ this._inputSource.disconnect();
15164
+ } catch {}
15165
+ this._inputSource = null;
15166
+ }
15167
+ try {
15168
+ this._gainNode.disconnect();
15169
+ this._analyser.disconnect();
15170
+ } catch {}
15171
+ this._audioContext.close().catch((error) => {
15172
+ logger$19.debug("[LocalAudioPipeline] audio context close warning:", error);
15173
+ });
15174
+ super.destroy();
15175
+ }
15176
+ computeLevel() {
15177
+ if (!this._inputSource) return 0;
15178
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
15179
+ let sum = 0;
15180
+ for (const sample$1 of this._analyserBuffer) {
15181
+ const normalized = (sample$1 - 128) / 128;
15182
+ sum += normalized * normalized;
15183
+ }
15184
+ return Math.sqrt(sum / this._analyserBuffer.length);
15185
+ }
15186
+ evaluateSpeaking(level) {
15187
+ const now = Date.now();
15188
+ if (level >= this._speakingThreshold) {
15189
+ this._lastSpokeAt = now;
15190
+ return true;
15191
+ }
15192
+ return now - this._lastSpokeAt < this._speakingHoldMs;
15193
+ }
15194
+ };
15195
+
14940
15196
  //#endregion
14941
15197
  //#region src/controllers/LocalStreamController.ts
14942
- var import_cjs$16 = require_cjs();
14943
- const logger$17 = getLogger();
15198
+ var import_cjs$17 = require_cjs();
15199
+ const logger$18 = getLogger();
14944
15200
  var LocalStreamController = class extends Destroyable {
14945
15201
  constructor(options) {
14946
15202
  super();
@@ -14954,16 +15210,16 @@ var LocalStreamController = class extends Destroyable {
14954
15210
  this._mediaTrackEnded$ = this.createSubject();
14955
15211
  }
14956
15212
  get localStream$() {
14957
- return this._localStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15213
+ return this._localStream$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14958
15214
  }
14959
15215
  get localAudioTracks$() {
14960
- return this._localAudioTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15216
+ return this._localAudioTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14961
15217
  }
14962
15218
  get localVideoTracks$() {
14963
- return this._localVideoTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15219
+ return this._localVideoTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14964
15220
  }
14965
15221
  get mediaTrackEnded$() {
14966
- return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15222
+ return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14967
15223
  }
14968
15224
  get localStream() {
14969
15225
  return this._localStream$.value;
@@ -14978,26 +15234,26 @@ var LocalStreamController = class extends Destroyable {
14978
15234
  * Build the local media stream based on the provided options.
14979
15235
  */
14980
15236
  async buildLocalStream() {
14981
- logger$17.debug("[LocalStreamController] Building local media stream.");
15237
+ logger$18.debug("[LocalStreamController] Building local media stream.");
14982
15238
  let stream;
14983
15239
  if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
14984
15240
  const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
14985
15241
  stream = new MediaStream(tracks);
14986
15242
  } else if (this.options.propose === "screenshare") {
14987
- logger$17.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15243
+ logger$18.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
14988
15244
  stream = await this.options.getDisplayMedia({
14989
15245
  video: true,
14990
15246
  audio: Boolean(this.options.inputAudioDeviceConstraints)
14991
15247
  });
14992
- logger$17.debug("[LocalStreamController] Screen share media obtained:", stream);
15248
+ logger$18.debug("[LocalStreamController] Screen share media obtained:", stream);
14993
15249
  } else {
14994
15250
  const constraints = {
14995
15251
  audio: this.options.inputAudioDeviceConstraints,
14996
15252
  video: this.options.inputVideoDeviceConstraints
14997
15253
  };
14998
- logger$17.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15254
+ logger$18.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
14999
15255
  stream = await this.options.getUserMedia(constraints);
15000
- logger$17.debug("[LocalStreamController] User media obtained:", stream);
15256
+ logger$18.debug("[LocalStreamController] User media obtained:", stream);
15001
15257
  }
15002
15258
  this._localStream$.next(stream);
15003
15259
  return stream;
@@ -15014,7 +15270,7 @@ var LocalStreamController = class extends Destroyable {
15014
15270
  this._localStream$.next(localStream);
15015
15271
  if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
15016
15272
  else this._localAudioTracks$.next(localStream.getAudioTracks());
15017
- logger$17.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15273
+ logger$18.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15018
15274
  return localStream;
15019
15275
  }
15020
15276
  /**
@@ -15026,7 +15282,7 @@ var LocalStreamController = class extends Destroyable {
15026
15282
  const stream = this._localStream$.value;
15027
15283
  const track = stream?.getTracks().find((t) => t.id === trackId);
15028
15284
  if (!track) {
15029
- logger$17.debug(`[LocalStreamController] track not found: ${trackId}`);
15285
+ logger$18.debug(`[LocalStreamController] track not found: ${trackId}`);
15030
15286
  return;
15031
15287
  }
15032
15288
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
@@ -15035,7 +15291,7 @@ var LocalStreamController = class extends Destroyable {
15035
15291
  this._localStream$.next(stream);
15036
15292
  if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
15037
15293
  else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
15038
- logger$17.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15294
+ logger$18.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15039
15295
  return track;
15040
15296
  }
15041
15297
  /**
@@ -15070,7 +15326,7 @@ var LocalStreamController = class extends Destroyable {
15070
15326
  */
15071
15327
  stopAllTracks() {
15072
15328
  this._localStream$.value?.getTracks().forEach((track) => {
15073
- logger$17.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15329
+ logger$18.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15074
15330
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
15075
15331
  track.stop();
15076
15332
  });
@@ -15086,7 +15342,7 @@ var LocalStreamController = class extends Destroyable {
15086
15342
 
15087
15343
  //#endregion
15088
15344
  //#region src/controllers/TransceiverController.ts
15089
- const logger$16 = getLogger();
15345
+ const logger$17 = getLogger();
15090
15346
  const getDirection = (send, recv) => {
15091
15347
  if (send && recv) return "sendrecv";
15092
15348
  else if (send && !recv) return "sendonly";
@@ -15188,7 +15444,7 @@ var TransceiverController = class extends Destroyable {
15188
15444
  sendEncodings: isAudio ? void 0 : this.sendEncodings,
15189
15445
  streams: direction === "recvonly" ? void 0 : [localStream]
15190
15446
  };
15191
- logger$16.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15447
+ logger$17.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15192
15448
  transceiver,
15193
15449
  transceiverParams
15194
15450
  });
@@ -15196,11 +15452,11 @@ var TransceiverController = class extends Destroyable {
15196
15452
  await transceiver.sender.replaceTrack(track);
15197
15453
  transceiver.direction = transceiverParams.direction;
15198
15454
  if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
15199
- logger$16.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15455
+ logger$17.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15200
15456
  transceiver.sender.setStreams(...transceiverParams.streams);
15201
15457
  }
15202
15458
  } else {
15203
- logger$16.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15459
+ logger$17.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15204
15460
  this.peerConnection.addTransceiver(track, transceiverParams);
15205
15461
  }
15206
15462
  }
@@ -15214,13 +15470,13 @@ var TransceiverController = class extends Destroyable {
15214
15470
  if (options.updateTransceiverDirection) transceiver.direction = "inactive";
15215
15471
  }
15216
15472
  } catch (error) {
15217
- logger$16.error("[TransceiverController] stopTrackSender error", kind, error);
15473
+ logger$17.error("[TransceiverController] stopTrackSender error", kind, error);
15218
15474
  this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
15219
15475
  }
15220
15476
  }
15221
15477
  async restoreTrackSender(kind) {
15222
15478
  try {
15223
- logger$16.debug("[TransceiverController] restoreTrackSender called", kind);
15479
+ logger$17.debug("[TransceiverController] restoreTrackSender called", kind);
15224
15480
  const constraints = {};
15225
15481
  const transceivers = this.transceiverByKind(kind);
15226
15482
  for (const transceiver of transceivers) {
@@ -15230,23 +15486,23 @@ var TransceiverController = class extends Destroyable {
15230
15486
  if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
15231
15487
  }
15232
15488
  }
15233
- logger$16.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15489
+ logger$17.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15234
15490
  if (Object.keys(constraints).length === 0) {
15235
- logger$16.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15491
+ logger$17.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15236
15492
  return;
15237
15493
  }
15238
15494
  const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
15239
- logger$16.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15495
+ logger$17.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15240
15496
  for (const newTrack of newTracks) {
15241
15497
  this.options.localStreamController.addTrack(newTrack);
15242
15498
  const trackKind = newTrack.kind;
15243
15499
  const transceiverOfKind = this.transceiverByKind(trackKind)[0];
15244
15500
  transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
15245
- logger$16.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15501
+ logger$17.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15246
15502
  await transceiverOfKind.sender.replaceTrack(newTrack);
15247
15503
  }
15248
15504
  } catch (error) {
15249
- logger$16.error("[TransceiverController] restoreTrackSender error", kind, error);
15505
+ logger$17.error("[TransceiverController] restoreTrackSender error", kind, error);
15250
15506
  this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
15251
15507
  }
15252
15508
  }
@@ -15287,14 +15543,14 @@ var TransceiverController = class extends Destroyable {
15287
15543
  };
15288
15544
  try {
15289
15545
  await track.applyConstraints(constraintsToApply);
15290
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15291
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15546
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15547
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15292
15548
  } catch (error) {
15293
- logger$16.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15549
+ logger$17.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15294
15550
  try {
15295
15551
  await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
15296
15552
  } catch (fallbackError) {
15297
- logger$16.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15553
+ logger$17.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15298
15554
  this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
15299
15555
  }
15300
15556
  }
@@ -15322,7 +15578,7 @@ var TransceiverController = class extends Destroyable {
15322
15578
  if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
15323
15579
  await sender.replaceTrack(newTrack);
15324
15580
  this.options.localStreamController.addTrack(newTrack);
15325
- logger$16.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15581
+ logger$17.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15326
15582
  }
15327
15583
  getMediaDirections() {
15328
15584
  if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
@@ -15352,60 +15608,60 @@ var TransceiverController = class extends Destroyable {
15352
15608
 
15353
15609
  //#endregion
15354
15610
  //#region src/controllers/RTCPeerConnectionController.ts
15355
- var import_cjs$15 = require_cjs();
15356
- const logger$15 = getLogger();
15611
+ var import_cjs$16 = require_cjs();
15612
+ const logger$16 = getLogger();
15357
15613
  var RTCPeerConnectionController = class extends Destroyable {
15358
15614
  constructor(options = {}, remoteSessionDescription, deviceController) {
15359
15615
  super();
15360
15616
  this.options = options;
15361
15617
  this.firstSDPExchangeCompleted = false;
15362
15618
  this.negotiationNeeded$ = this.createSubject();
15363
- this.localDescription$ = (0, import_cjs$15.defer)(() => (0, import_cjs$15.from)(this.init())).pipe((0, import_cjs$15.switchMap)(() => this.iceGatheringController.iceCandidatesState$.pipe((0, import_cjs$15.filter)((iceCandidateState) => !["new", "gathering"].includes(iceCandidateState)), (0, import_cjs$15.tap)(() => {
15619
+ this.localDescription$ = (0, import_cjs$16.defer)(() => (0, import_cjs$16.from)(this.init())).pipe((0, import_cjs$16.switchMap)(() => this.iceGatheringController.iceCandidatesState$.pipe((0, import_cjs$16.filter)((iceCandidateState) => !["new", "gathering"].includes(iceCandidateState)), (0, import_cjs$16.tap)(() => {
15364
15620
  this.negotiationEnded();
15365
- }), (0, import_cjs$15.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$15.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$15.tap)((desc) => {
15621
+ }), (0, import_cjs$16.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$16.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$16.tap)((desc) => {
15366
15622
  if (desc.type === "answer") this._type = "offer";
15367
- }))), (0, import_cjs$15.shareReplay)(1), (0, import_cjs$15.takeUntil)(this.destroyed$));
15623
+ }))), (0, import_cjs$16.shareReplay)(1), (0, import_cjs$16.takeUntil)(this.destroyed$));
15368
15624
  this.connectionTimeout = 3e3;
15369
15625
  this.oniceconnectionstatechangeHandler = () => {
15370
15626
  if (this.peerConnection) {
15371
15627
  const { iceConnectionState } = this.peerConnection;
15372
- logger$15.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15628
+ logger$16.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15373
15629
  this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
15374
15630
  }
15375
15631
  };
15376
15632
  this.onconnectionstatechangeHandler = () => {
15377
15633
  if (this.peerConnection) {
15378
15634
  const { connectionState } = this.peerConnection;
15379
- logger$15.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15635
+ logger$16.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15380
15636
  if (connectionState === "connected") this.removeConnectionTimer();
15381
15637
  this._connectionState$.next(this.peerConnection.connectionState);
15382
15638
  }
15383
15639
  };
15384
15640
  this.onsignalingstatechangeHandler = () => {
15385
- logger$15.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15641
+ logger$16.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15386
15642
  };
15387
15643
  this.onicegatheringstatechangeHandler = () => {
15388
15644
  if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
15389
15645
  };
15390
15646
  this.onnegotiationneededHandler = (event) => {
15391
- logger$15.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15647
+ logger$16.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15392
15648
  this.negotiationNeeded$.next();
15393
15649
  };
15394
15650
  this.updateSelectedInputDevice = async (kind, deviceInfo) => {
15395
15651
  try {
15396
15652
  const { localStream } = this;
15397
15653
  if (!localStream) {
15398
- logger$15.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15654
+ logger$16.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15399
15655
  return;
15400
15656
  }
15401
- logger$15.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15657
+ logger$16.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15402
15658
  const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
15403
15659
  if (track) {
15404
15660
  this.transceiverController?.stopTrackSender(kind);
15405
- this.localStream?.removeTrack(track);
15406
- logger$15.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15661
+ this.localStreamController.removeTrack(track.id);
15662
+ logger$16.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15407
15663
  if (!deviceInfo) {
15408
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15664
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15409
15665
  return;
15410
15666
  }
15411
15667
  const streamTrack = (await this.getUserMedia({ [kind]: {
@@ -15413,15 +15669,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15413
15669
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
15414
15670
  } })).getTracks().find((t) => t.kind === kind);
15415
15671
  if (streamTrack) {
15416
- logger$15.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15417
- this.localStream?.addTrack(streamTrack);
15672
+ logger$16.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15673
+ this.localStreamController.addTrack(streamTrack);
15418
15674
  await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
15419
- logger$15.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15675
+ logger$16.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15420
15676
  }
15421
15677
  }
15422
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15678
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15423
15679
  } catch (error) {
15424
- logger$15.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15680
+ logger$16.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15425
15681
  this._errors$.next(toError(error));
15426
15682
  throw error;
15427
15683
  }
@@ -15438,6 +15694,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15438
15694
  this._remoteDescription$ = this.createReplaySubject(1);
15439
15695
  this._remoteStream$ = this.createBehaviorSubject(null);
15440
15696
  this._remoteOfferMediaDirections = null;
15697
+ this._localAudioPipeline = null;
15441
15698
  this.deviceController = deviceController ?? {};
15442
15699
  this.id = options.callId ?? v4_default();
15443
15700
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -15507,43 +15764,43 @@ var RTCPeerConnectionController = class extends Destroyable {
15507
15764
  };
15508
15765
  }
15509
15766
  get iceGatheringState$() {
15510
- return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15767
+ return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15511
15768
  }
15512
15769
  get mediaTrackEnded$() {
15513
- return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15770
+ return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15514
15771
  }
15515
15772
  get errors$() {
15516
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15773
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15517
15774
  }
15518
15775
  get iceCandidates$() {
15519
- return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15776
+ return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15520
15777
  }
15521
15778
  get initialized$() {
15522
- return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$15.filter)((initialized) => initialized), (0, import_cjs$15.takeUntil)(this.destroyed$)));
15779
+ return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$16.filter)((initialized) => initialized), (0, import_cjs$16.takeUntil)(this.destroyed$)));
15523
15780
  }
15524
15781
  get remoteDescription$() {
15525
- return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15782
+ return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15526
15783
  }
15527
15784
  get localStream$() {
15528
- return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15785
+ return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15529
15786
  }
15530
15787
  get remoteStream$() {
15531
- return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15788
+ return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15532
15789
  }
15533
15790
  get localAudioTracks$() {
15534
- return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15791
+ return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15535
15792
  }
15536
15793
  get localVideoTracks$() {
15537
- return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15794
+ return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15538
15795
  }
15539
15796
  get iceConnectionState$() {
15540
- return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15797
+ return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15541
15798
  }
15542
15799
  get connectionState$() {
15543
- return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15800
+ return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15544
15801
  }
15545
15802
  get signalingState$() {
15546
- return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15803
+ return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15547
15804
  }
15548
15805
  get type() {
15549
15806
  return this._type;
@@ -15656,17 +15913,17 @@ var RTCPeerConnectionController = class extends Destroyable {
15656
15913
  async doInit() {
15657
15914
  try {
15658
15915
  this.setupPeerConnection();
15659
- this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$15.auditTime)(0), (0, import_cjs$15.exhaustMap)(async () => this.startNegotiation())), {
15916
+ this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$16.auditTime)(0), (0, import_cjs$16.exhaustMap)(async () => this.startNegotiation())), {
15660
15917
  next: () => {
15661
- logger$15.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15918
+ logger$16.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15662
15919
  },
15663
15920
  error: (error) => {
15664
- logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15921
+ logger$16.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15665
15922
  this._errors$.next(toError(error));
15666
15923
  }
15667
15924
  });
15668
- this.subscribeTo((0, import_cjs$15.merge)(this.deviceController.selectedAudioInputDevice$.pipe((0, import_cjs$15.map)((deviceInfo) => ["audio", deviceInfo])), this.deviceController.selectedVideoInputDevice$.pipe((0, import_cjs$15.map)((deviceInfo) => ["video", deviceInfo]))).pipe((0, import_cjs$15.skipWhile)(() => !this.localStreamController.localStream)), async ([kind, deviceInfo]) => {
15669
- logger$15.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15925
+ 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]) => {
15926
+ logger$16.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15670
15927
  kind,
15671
15928
  deviceInfo
15672
15929
  });
@@ -15683,7 +15940,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15683
15940
  this._initialized$.next(true);
15684
15941
  }
15685
15942
  } catch (error) {
15686
- logger$15.error("[RTCPeerConnectionController] Initialization error:", error);
15943
+ logger$16.error("[RTCPeerConnectionController] Initialization error:", error);
15687
15944
  this._errors$.next(toError(error));
15688
15945
  this.destroy();
15689
15946
  }
@@ -15715,22 +15972,22 @@ var RTCPeerConnectionController = class extends Destroyable {
15715
15972
  }
15716
15973
  async startNegotiation() {
15717
15974
  if (this.isNegotiating) {
15718
- logger$15.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15975
+ logger$16.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15719
15976
  return;
15720
15977
  }
15721
15978
  this.setupEventListeners();
15722
15979
  if (this.type === "answer") {
15723
- logger$15.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15980
+ logger$16.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15724
15981
  return;
15725
15982
  }
15726
15983
  this._isNegotiating$.next(true);
15727
- logger$15.debug("[RTCPeerConnectionController] Starting negotiation.");
15984
+ logger$16.debug("[RTCPeerConnectionController] Starting negotiation.");
15728
15985
  try {
15729
15986
  const { offerOptions } = this;
15730
- logger$15.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15987
+ logger$16.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15731
15988
  await this.createOffer(offerOptions);
15732
15989
  } catch (error) {
15733
- logger$15.error("[RTCPeerConnectionController] Error during negotiation:", error);
15990
+ logger$16.error("[RTCPeerConnectionController] Error during negotiation:", error);
15734
15991
  this._errors$.next(toError(error));
15735
15992
  }
15736
15993
  }
@@ -15746,14 +16003,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15746
16003
  let readyToConnect = status !== "failed";
15747
16004
  try {
15748
16005
  if (status === "received" && sdp) {
15749
- logger$15.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16006
+ logger$16.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
15750
16007
  await this._setRemoteDescription({
15751
16008
  type: "answer",
15752
16009
  sdp
15753
16010
  });
15754
16011
  }
15755
16012
  } catch (error) {
15756
- logger$15.error("[RTCPeerConnectionController] Error updating answer status:", error);
16013
+ logger$16.error("[RTCPeerConnectionController] Error updating answer status:", error);
15757
16014
  this._errors$.next(toError(error));
15758
16015
  readyToConnect = false;
15759
16016
  } finally {
@@ -15772,7 +16029,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15772
16029
  await this.handleOfferReceived();
15773
16030
  break;
15774
16031
  case "failed":
15775
- logger$15.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16032
+ logger$16.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
15776
16033
  break;
15777
16034
  case "sent":
15778
16035
  default:
@@ -15804,7 +16061,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15804
16061
  }
15805
16062
  await this.setupLocalTracks();
15806
16063
  const { answerOptions } = this;
15807
- logger$15.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16064
+ logger$16.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
15808
16065
  await this.createAnswer(answerOptions);
15809
16066
  }
15810
16067
  async handleOfferReceived() {
@@ -15812,7 +16069,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15812
16069
  this._isNegotiating$.next(true);
15813
16070
  await this._setRemoteDescription(this.sdpInit);
15814
16071
  const { answerOptions } = this;
15815
- logger$15.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16072
+ logger$16.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
15816
16073
  await this.createAnswer(answerOptions);
15817
16074
  }
15818
16075
  readyToConnect() {
@@ -15820,7 +16077,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15820
16077
  this.connectionTimer = setTimeout(() => {
15821
16078
  this.removeConnectionTimer();
15822
16079
  if (this.peerConnection?.connectionState !== "connected") {
15823
- logger$15.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16080
+ logger$16.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
15824
16081
  this.iceGatheringController.restartICEGatheringWithRelayOnly();
15825
16082
  }
15826
16083
  }, this.connectionTimeout);
@@ -15842,14 +16099,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15842
16099
  const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
15843
16100
  if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
15844
16101
  result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
15845
- logger$15.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16102
+ logger$16.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
15846
16103
  preferredAudioCodecs,
15847
16104
  preferredVideoCodecs
15848
16105
  });
15849
16106
  }
15850
16107
  if (stereo) {
15851
16108
  result = enableStereoOpus(result);
15852
- logger$15.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16109
+ logger$16.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
15853
16110
  }
15854
16111
  return Promise.resolve(result);
15855
16112
  }
@@ -15883,9 +16140,6 @@ var RTCPeerConnectionController = class extends Destroyable {
15883
16140
  negotiationEnded() {
15884
16141
  this._isNegotiating$.next(false);
15885
16142
  }
15886
- restarIce() {
15887
- this.peerConnection?.restartIce();
15888
- }
15889
16143
  /**
15890
16144
  * Trigger an ICE restart through the existing negotiation pipeline.
15891
16145
  *
@@ -15908,24 +16162,27 @@ var RTCPeerConnectionController = class extends Destroyable {
15908
16162
  ...this.peerConnection.getConfiguration(),
15909
16163
  iceTransportPolicy: "relay"
15910
16164
  });
15911
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16165
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
15912
16166
  } catch (error) {
15913
- logger$15.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16167
+ logger$16.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
15914
16168
  }
15915
16169
  this.setupEventListeners();
15916
16170
  this._isNegotiating$.next(true);
15917
- logger$15.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16171
+ logger$16.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
15918
16172
  try {
15919
16173
  const offer = await this.peerConnection.createOffer({ iceRestart: true });
15920
16174
  await this.setLocalDescription(offer);
15921
16175
  } catch (error) {
15922
- logger$15.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16176
+ logger$16.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
15923
16177
  this._errors$.next(toError(error));
15924
16178
  this.negotiationEnded();
15925
16179
  if (policyChanged) this.restoreIceTransportPolicy();
15926
16180
  throw error;
15927
16181
  }
15928
- if (policyChanged) this.restoreIceTransportPolicy();
16182
+ 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) => {
16183
+ logger$16.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16184
+ this.restoreIceTransportPolicy();
16185
+ });
15929
16186
  }
15930
16187
  restoreIceTransportPolicy() {
15931
16188
  try {
@@ -15933,9 +16190,9 @@ var RTCPeerConnectionController = class extends Destroyable {
15933
16190
  ...this.peerConnection.getConfiguration(),
15934
16191
  iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
15935
16192
  });
15936
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy restored");
16193
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy restored");
15937
16194
  } catch (error) {
15938
- logger$15.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16195
+ logger$16.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
15939
16196
  }
15940
16197
  }
15941
16198
  /**
@@ -15947,13 +16204,13 @@ var RTCPeerConnectionController = class extends Destroyable {
15947
16204
  await this.setupRemoteTracks();
15948
16205
  }
15949
16206
  async setupLocalTracks() {
15950
- logger$15.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16207
+ logger$16.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
15951
16208
  const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
15952
16209
  if (this.transceiverController?.useAddStream ?? false) {
15953
- logger$15.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16210
+ logger$16.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
15954
16211
  this.peerConnection?.addStream(localStream);
15955
16212
  if (!this.isNegotiating) {
15956
- logger$15.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16213
+ logger$16.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
15957
16214
  this.negotiationNeeded$.next();
15958
16215
  }
15959
16216
  return;
@@ -15969,7 +16226,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15969
16226
  const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
15970
16227
  await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
15971
16228
  } else {
15972
- logger$15.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16229
+ logger$16.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
15973
16230
  this.peerConnection?.addTrack(track, localStream);
15974
16231
  }
15975
16232
  }
@@ -15986,7 +16243,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15986
16243
  async setupRemoteTracks() {
15987
16244
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
15988
16245
  this.peerConnection.ontrack = (event) => {
15989
- logger$15.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16246
+ logger$16.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
15990
16247
  if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
15991
16248
  else {
15992
16249
  const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
@@ -15998,6 +16255,45 @@ var RTCPeerConnectionController = class extends Destroyable {
15998
16255
  }
15999
16256
  async restoreTrackSender(kind) {
16000
16257
  await this.transceiverController?.restoreTrackSender(kind);
16258
+ if (kind !== "video" && this._localAudioPipeline) await this.applyLocalAudioPipelineToSender();
16259
+ }
16260
+ /**
16261
+ * Return the lazily-created {@link LocalAudioPipeline}, constructing it on
16262
+ * first access. On creation the current audio sender's track is routed
16263
+ * through the pipeline (input → gain → analyser → destination) and the
16264
+ * sender is switched to emit the processed track. Returns `null` when no
16265
+ * audio sender exists yet (pre-negotiation).
16266
+ */
16267
+ ensureLocalAudioPipeline() {
16268
+ if (this._localAudioPipeline) return this._localAudioPipeline;
16269
+ if (!this.peerConnection) return null;
16270
+ try {
16271
+ this._localAudioPipeline = new LocalAudioPipeline();
16272
+ } catch (error) {
16273
+ logger$16.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16274
+ return null;
16275
+ }
16276
+ this.subscribeTo(this.localStreamController.localAudioTracks$, () => {
16277
+ this.applyLocalAudioPipelineToSender();
16278
+ });
16279
+ this.applyLocalAudioPipelineToSender();
16280
+ return this._localAudioPipeline;
16281
+ }
16282
+ /** The active LocalAudioPipeline, or null if it hasn't been created yet. */
16283
+ get localAudioPipeline() {
16284
+ return this._localAudioPipeline;
16285
+ }
16286
+ async applyLocalAudioPipelineToSender() {
16287
+ if (!this._localAudioPipeline || !this.peerConnection) return;
16288
+ const raw = this.localStreamController.localAudioTracks.at(0);
16289
+ this._localAudioPipeline.setInputTrack(raw ?? null);
16290
+ const sender = (this.transceiverController?.audioTransceivers.at(0))?.sender ?? this.peerConnection.getSenders().find((s) => s.track?.kind === "audio");
16291
+ if (!sender || !raw) return;
16292
+ try {
16293
+ await sender.replaceTrack(this._localAudioPipeline.outputTrack);
16294
+ } catch (error) {
16295
+ logger$16.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16296
+ }
16001
16297
  }
16002
16298
  /**
16003
16299
  * Add a local media track to the peer connection.
@@ -16012,9 +16308,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16012
16308
  try {
16013
16309
  const localStream = this.localStreamController.addTrack(track);
16014
16310
  this.peerConnection.addTrack(track, localStream);
16015
- logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16311
+ logger$16.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16016
16312
  } catch (error) {
16017
- logger$15.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16313
+ logger$16.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16018
16314
  this._errors$.next(toError(error));
16019
16315
  throw error;
16020
16316
  }
@@ -16031,15 +16327,15 @@ var RTCPeerConnectionController = class extends Destroyable {
16031
16327
  }
16032
16328
  const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
16033
16329
  if (!sender) {
16034
- logger$15.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16330
+ logger$16.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16035
16331
  return;
16036
16332
  }
16037
16333
  try {
16038
16334
  this.peerConnection.removeTrack(sender);
16039
16335
  this.localStreamController.removeTrack(trackId);
16040
- logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16336
+ logger$16.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16041
16337
  } catch (error) {
16042
- logger$15.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16338
+ logger$16.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16043
16339
  this._errors$.next(toError(error));
16044
16340
  throw error;
16045
16341
  }
@@ -16066,7 +16362,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16066
16362
  async replaceAudioTrackWithConstraints(constraints) {
16067
16363
  const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
16068
16364
  if (!senders || senders.length === 0) {
16069
- logger$15.warn("[RTCPeerConnectionController] No live audio sender to replace");
16365
+ logger$16.warn("[RTCPeerConnectionController] No live audio sender to replace");
16070
16366
  return;
16071
16367
  }
16072
16368
  for (const sender of senders) {
@@ -16084,7 +16380,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16084
16380
  const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
16085
16381
  await sender.replaceTrack(newTrack);
16086
16382
  this.localStreamController.addTrack(newTrack);
16087
- logger$15.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16383
+ logger$16.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16088
16384
  }
16089
16385
  }
16090
16386
  /**
@@ -16092,9 +16388,11 @@ var RTCPeerConnectionController = class extends Destroyable {
16092
16388
  * Completes all observables to prevent memory leaks.
16093
16389
  */
16094
16390
  destroy() {
16095
- logger$15.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16391
+ logger$16.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16096
16392
  this.removeConnectionTimer();
16097
16393
  this._iceGatheringController?.destroy();
16394
+ this._localAudioPipeline?.destroy();
16395
+ this._localAudioPipeline = null;
16098
16396
  this.localStreamController.destroy();
16099
16397
  this.transceiverController?.destroy();
16100
16398
  if (this.peerConnection) {
@@ -16116,7 +16414,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16116
16414
  }
16117
16415
  stopRemoteTracks() {
16118
16416
  this._remoteStream$.value?.getTracks().forEach((track) => {
16119
- logger$15.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16417
+ logger$16.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16120
16418
  track.stop();
16121
16419
  });
16122
16420
  }
@@ -16133,7 +16431,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16133
16431
  ...params,
16134
16432
  sdp: finalRemote
16135
16433
  };
16136
- logger$15.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16434
+ logger$16.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16137
16435
  return this.peerConnection.setRemoteDescription(answer);
16138
16436
  }
16139
16437
  };
@@ -16171,8 +16469,25 @@ function isVertoPingInnerParams(value) {
16171
16469
 
16172
16470
  //#endregion
16173
16471
  //#region src/managers/VertoManager.ts
16174
- var import_cjs$14 = require_cjs();
16175
- const logger$14 = getLogger();
16472
+ var import_cjs$15 = require_cjs();
16473
+ const logger$15 = getLogger();
16474
+ /**
16475
+ * Decide what value goes on the `node_id` field of a `webrtc.verto` envelope.
16476
+ *
16477
+ * - **Reattach invite:** must carry the persisted nodeId so the server routes
16478
+ * the new connection to the FreeSWITCH instance that holds the existing call.
16479
+ * - **Fresh invite, caller-supplied `CallOptions.nodeId`:** carry the explicit
16480
+ * value as a steering hint (dev/staging traffic pinning). Server may honour
16481
+ * or ignore for placement reasons.
16482
+ * - **Fresh invite, no caller nodeId:** strip to `''` = "server picks".
16483
+ * - **Non-invite frames** (verto.modify, verto.bye, etc.): always carry the
16484
+ * current `_nodeId$.value` so the frame targets the node hosting the call.
16485
+ *
16486
+ * Pure function — exported for unit testing.
16487
+ */
16488
+ function resolveInviteNodeId(args) {
16489
+ return args.isInvite && !args.reattach && !args.explicitNodeId ? "" : args.currentNodeId ?? "";
16490
+ }
16176
16491
  var VertoManager = class extends Destroyable {
16177
16492
  constructor(callSession) {
16178
16493
  super();
@@ -16211,7 +16526,7 @@ var WebRTCVertoManager = class extends VertoManager {
16211
16526
  try {
16212
16527
  await this.executeVerto(vertoModifyMessage);
16213
16528
  } catch (error) {
16214
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16529
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16215
16530
  throw error;
16216
16531
  }
16217
16532
  }
@@ -16224,7 +16539,7 @@ var WebRTCVertoManager = class extends VertoManager {
16224
16539
  try {
16225
16540
  await this.executeVerto(vertoModifyMessage);
16226
16541
  } catch (error) {
16227
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16542
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16228
16543
  throw error;
16229
16544
  }
16230
16545
  }
@@ -16264,7 +16579,7 @@ var WebRTCVertoManager = class extends VertoManager {
16264
16579
  return rtcPeerConnection;
16265
16580
  }
16266
16581
  get signalingStatus$() {
16267
- return this.cachedObservable("signalingStatus$", () => (0, import_cjs$14.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$14.filter)((connectionState) => [
16582
+ return this.cachedObservable("signalingStatus$", () => (0, import_cjs$15.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$15.filter)((connectionState) => [
16268
16583
  "connected",
16269
16584
  "disconnected",
16270
16585
  "failed"
@@ -16277,7 +16592,7 @@ var WebRTCVertoManager = class extends VertoManager {
16277
16592
  if (event.member_id) this.setSelfIdIfNull(event.member_id);
16278
16593
  });
16279
16594
  this.subscribeTo(this.vertoMedia$, (event) => {
16280
- logger$14.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16595
+ logger$15.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16281
16596
  this._signalingStatus$.next("ringing");
16282
16597
  const { sdp, callID } = event;
16283
16598
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16286,7 +16601,7 @@ var WebRTCVertoManager = class extends VertoManager {
16286
16601
  });
16287
16602
  });
16288
16603
  this.subscribeTo(this.vertoAnswer$, (event) => {
16289
- logger$14.debug("[WebRTCManager] Received Verto answer event:", event);
16604
+ logger$15.debug("[WebRTCManager] Received Verto answer event:", event);
16290
16605
  this._signalingStatus$.next("connecting");
16291
16606
  const { sdp, callID } = event;
16292
16607
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16295,7 +16610,7 @@ var WebRTCVertoManager = class extends VertoManager {
16295
16610
  });
16296
16611
  });
16297
16612
  this.subscribeTo(this.vertoMediaParams$, (event) => {
16298
- logger$14.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16613
+ logger$15.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16299
16614
  const { mediaParams, callID } = event;
16300
16615
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
16301
16616
  const { audio, video } = mediaParams;
@@ -16309,7 +16624,7 @@ var WebRTCVertoManager = class extends VertoManager {
16309
16624
  timestamp: Date.now()
16310
16625
  });
16311
16626
  } catch (error) {
16312
- logger$14.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16627
+ logger$15.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16313
16628
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16314
16629
  }
16315
16630
  })();
@@ -16331,13 +16646,13 @@ var WebRTCVertoManager = class extends VertoManager {
16331
16646
  */
16332
16647
  setNodeIdIfNull(nodeId) {
16333
16648
  if (!this._nodeId$.value && nodeId) {
16334
- logger$14.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16649
+ logger$15.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16335
16650
  this._nodeId$.next(nodeId);
16336
16651
  }
16337
16652
  }
16338
16653
  setSelfIdIfNull(selfId) {
16339
16654
  if (!this._selfId$.value && selfId) {
16340
- logger$14.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16655
+ logger$15.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16341
16656
  this._selfId$.next(selfId);
16342
16657
  }
16343
16658
  }
@@ -16346,7 +16661,7 @@ var WebRTCVertoManager = class extends VertoManager {
16346
16661
  const vertoPongMessage = VertoPong({ ...vertoPing });
16347
16662
  await this.executeVerto(vertoPongMessage);
16348
16663
  } catch (error) {
16349
- logger$14.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16664
+ logger$15.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16350
16665
  this.onError?.(new VertoPongError(error));
16351
16666
  }
16352
16667
  }
@@ -16356,7 +16671,7 @@ var WebRTCVertoManager = class extends VertoManager {
16356
16671
  if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
16357
16672
  if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
16358
16673
  } catch (error) {
16359
- logger$14.warn("[WebRTCManager] Error updating media constraints:", error);
16674
+ logger$15.warn("[WebRTCManager] Error updating media constraints:", error);
16360
16675
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16361
16676
  throw error;
16362
16677
  }
@@ -16386,20 +16701,20 @@ var WebRTCVertoManager = class extends VertoManager {
16386
16701
  try {
16387
16702
  const pc = this.mainPeerConnection.peerConnection;
16388
16703
  if (!pc) {
16389
- logger$14.warn("[WebRTCManager] No peer connection for keyframe request");
16704
+ logger$15.warn("[WebRTCManager] No peer connection for keyframe request");
16390
16705
  return;
16391
16706
  }
16392
16707
  const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
16393
16708
  if (!videoReceiver) {
16394
- logger$14.warn("[WebRTCManager] No video receiver for keyframe request");
16709
+ logger$15.warn("[WebRTCManager] No video receiver for keyframe request");
16395
16710
  return;
16396
16711
  }
16397
16712
  if (typeof videoReceiver.requestKeyFrame === "function") {
16398
16713
  videoReceiver.requestKeyFrame();
16399
- logger$14.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16400
- } else logger$14.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16714
+ logger$15.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16715
+ } else logger$15.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16401
16716
  } catch (error) {
16402
- logger$14.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16717
+ logger$15.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16403
16718
  }
16404
16719
  }
16405
16720
  /**
@@ -16417,13 +16732,13 @@ var WebRTCVertoManager = class extends VertoManager {
16417
16732
  try {
16418
16733
  const controller = this.mainPeerConnection;
16419
16734
  if (!controller.peerConnection) {
16420
- logger$14.warn("[WebRTCManager] No peer connection for ICE restart");
16735
+ logger$15.warn("[WebRTCManager] No peer connection for ICE restart");
16421
16736
  return;
16422
16737
  }
16423
16738
  await controller.triggerIceRestart(relayOnly);
16424
- logger$14.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16739
+ logger$15.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16425
16740
  } catch (error) {
16426
- logger$14.error("[WebRTCManager] ICE restart failed:", error);
16741
+ logger$15.error("[WebRTCManager] ICE restart failed:", error);
16427
16742
  throw error;
16428
16743
  }
16429
16744
  }
@@ -16441,13 +16756,13 @@ var WebRTCVertoManager = class extends VertoManager {
16441
16756
  const entries = Array.from(this._rtcPeerConnectionsMap.entries());
16442
16757
  for (const [id, controller] of entries) try {
16443
16758
  if (!controller.peerConnection) {
16444
- logger$14.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16759
+ logger$15.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16445
16760
  continue;
16446
16761
  }
16447
16762
  await controller.triggerIceRestart(relayOnly);
16448
- logger$14.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16763
+ logger$15.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16449
16764
  } catch (error) {
16450
- logger$14.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16765
+ logger$15.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16451
16766
  }
16452
16767
  }
16453
16768
  /**
@@ -16459,7 +16774,7 @@ var WebRTCVertoManager = class extends VertoManager {
16459
16774
  requestKeyframeAll() {
16460
16775
  for (const [id, controller] of this._rtcPeerConnectionsMap) {
16461
16776
  if (controller.isScreenShare) {
16462
- logger$14.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16777
+ logger$15.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16463
16778
  continue;
16464
16779
  }
16465
16780
  try {
@@ -16469,33 +16784,33 @@ var WebRTCVertoManager = class extends VertoManager {
16469
16784
  if (!videoReceiver) continue;
16470
16785
  if (typeof videoReceiver.requestKeyFrame === "function") {
16471
16786
  videoReceiver.requestKeyFrame();
16472
- logger$14.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16787
+ logger$15.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16473
16788
  }
16474
16789
  } catch (error) {
16475
- logger$14.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16790
+ logger$15.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16476
16791
  }
16477
16792
  }
16478
16793
  }
16479
16794
  get callJoinedEvent$() {
16480
- return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.takeUntil)(this.destroyed$));
16795
+ return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$15.filter)(isCallJoinedPayload), (0, import_cjs$15.takeUntil)(this.destroyed$));
16481
16796
  }
16482
16797
  get vertoMedia$() {
16483
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$));
16798
+ return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$));
16484
16799
  }
16485
16800
  get vertoAnswer$() {
16486
- return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16801
+ return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16487
16802
  }
16488
16803
  get vertoMediaParams$() {
16489
- return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16804
+ return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16490
16805
  }
16491
16806
  get vertoBye$() {
16492
- return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16807
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16493
16808
  }
16494
16809
  get vertoAttach$() {
16495
- return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16810
+ return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16496
16811
  }
16497
16812
  get vertoPing$() {
16498
- return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16813
+ return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16499
16814
  }
16500
16815
  async executeVerto(message, optionals = {}) {
16501
16816
  const webrtcVertoMessage = WebrtcVerto({
@@ -16533,7 +16848,7 @@ var WebRTCVertoManager = class extends VertoManager {
16533
16848
  default:
16534
16849
  }
16535
16850
  } catch (error) {
16536
- logger$14.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16851
+ logger$15.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16537
16852
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16538
16853
  if (vertoMethod === "verto.modify") this.onModifyFailed?.();
16539
16854
  }
@@ -16548,7 +16863,7 @@ var WebRTCVertoManager = class extends VertoManager {
16548
16863
  sdp
16549
16864
  });
16550
16865
  } catch (error) {
16551
- logger$14.warn("[WebRTCManager] Error processing modify response:", error);
16866
+ logger$15.warn("[WebRTCManager] Error processing modify response:", error);
16552
16867
  const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
16553
16868
  this.onError?.(modifyError);
16554
16869
  }
@@ -16560,7 +16875,7 @@ var WebRTCVertoManager = class extends VertoManager {
16560
16875
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
16561
16876
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
16562
16877
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
16563
- logger$14.debug("[WebRTCManager] Verto invite response:", {
16878
+ logger$15.debug("[WebRTCManager] Verto invite response:", {
16564
16879
  callId,
16565
16880
  memberId,
16566
16881
  response
@@ -16570,14 +16885,14 @@ var WebRTCVertoManager = class extends VertoManager {
16570
16885
  if (callId) {
16571
16886
  this.webRtcCallSession.addCallId(callId);
16572
16887
  this.attachManager.attach(this.buildAttachableCall(callId));
16573
- } else logger$14.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16888
+ } else logger$15.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16574
16889
  nodeId: this.nodeId,
16575
16890
  callId
16576
16891
  });
16577
- logger$14.info("[WebRTCManager] Verto invite successful");
16578
- logger$14.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16892
+ logger$15.info("[WebRTCManager] Verto invite successful");
16893
+ logger$15.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16579
16894
  } else {
16580
- logger$14.error("[WebRTCManager] Verto invite failed:", response);
16895
+ logger$15.error("[WebRTCManager] Verto invite failed:", response);
16581
16896
  const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
16582
16897
  this.onError?.(inviteError);
16583
16898
  }
@@ -16622,17 +16937,17 @@ var WebRTCVertoManager = class extends VertoManager {
16622
16937
  if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
16623
16938
  }
16624
16939
  async handleInboundAnswer(rtcPeerConnController) {
16625
- logger$14.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16626
- const vertoByeOrAccepted = await (0, import_cjs$14.firstValueFrom)((0, import_cjs$14.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$14.takeUntil)(this.destroyed$))).catch(() => null);
16940
+ logger$15.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16941
+ 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);
16627
16942
  if (vertoByeOrAccepted === null) {
16628
- logger$14.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16943
+ logger$15.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16629
16944
  return;
16630
16945
  }
16631
16946
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16632
- logger$14.info("[WebRTCManager] Inbound call ended by remote before answer.");
16947
+ logger$15.info("[WebRTCManager] Inbound call ended by remote before answer.");
16633
16948
  this.callSession?.destroy();
16634
16949
  } else if (!vertoByeOrAccepted) {
16635
- logger$14.info("[WebRTCManager] Inbound call rejected by user.");
16950
+ logger$15.info("[WebRTCManager] Inbound call rejected by user.");
16636
16951
  try {
16637
16952
  await this.bye("USER_BUSY");
16638
16953
  } finally {
@@ -16640,19 +16955,19 @@ var WebRTCVertoManager = class extends VertoManager {
16640
16955
  this.callSession?.destroy();
16641
16956
  }
16642
16957
  } else {
16643
- logger$14.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16958
+ logger$15.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16644
16959
  const answerOptions = this.webRtcCallSession.answerMediaOptions;
16645
16960
  try {
16646
16961
  await rtcPeerConnController.acceptInbound(answerOptions);
16647
16962
  } catch (error) {
16648
- logger$14.error("[WebRTCManager] Error creating inbound answer:", error);
16963
+ logger$15.error("[WebRTCManager] Error creating inbound answer:", error);
16649
16964
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16650
16965
  }
16651
16966
  }
16652
16967
  }
16653
16968
  setupVertoAttachHandler() {
16654
16969
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
16655
- logger$14.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16970
+ logger$15.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16656
16971
  const { callID } = vertoAttach;
16657
16972
  await this.attachManager.attach({
16658
16973
  nodeId: this.nodeId ?? void 0,
@@ -16666,12 +16981,12 @@ var WebRTCVertoManager = class extends VertoManager {
16666
16981
  });
16667
16982
  }
16668
16983
  initObservables(rtcPeerConnController) {
16669
- this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe((0, import_cjs$14.filter)((state) => state === "connected"), (0, import_cjs$14.map)(() => rtcPeerConnController.mediaDirections), (0, import_cjs$14.startWith)(rtcPeerConnController.mediaDirections), (0, import_cjs$14.takeUntil)(this.destroyed$));
16670
- this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16671
- this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16984
+ this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe((0, import_cjs$15.filter)((state) => state === "connected"), (0, import_cjs$15.map)(() => rtcPeerConnController.mediaDirections), (0, import_cjs$15.startWith)(rtcPeerConnController.mediaDirections), (0, import_cjs$15.takeUntil)(this.destroyed$));
16985
+ this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16986
+ this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16672
16987
  }
16673
16988
  setupLocalDescriptionHandler(rtcPeerConnController) {
16674
- this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$14.filter)((description) => description !== null), (0, import_cjs$14.takeUntil)(this.destroyed$)), (description) => {
16989
+ this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$15.filter)((description) => description !== null), (0, import_cjs$15.takeUntil)(this.destroyed$)), (description) => {
16675
16990
  const { type, sdp } = description;
16676
16991
  const dialogParams = this.dialogParams(rtcPeerConnController);
16677
16992
  const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
@@ -16712,26 +17027,29 @@ var WebRTCVertoManager = class extends VertoManager {
16712
17027
  else if (rtcPeerConnController.isAdditionalDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);
16713
17028
  else if (rtcPeerConnController.isScreenShare) subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);
16714
17029
  }
16715
- const isInvite = isVertoInviteMessage(vertoMessage);
16716
- const isReattach = isInvite && this.webRtcCallSession.options.reattach;
16717
17030
  return {
16718
17031
  callID: rtcPeerConnController.id,
16719
- node_id: isInvite && !isReattach ? "" : this._nodeId$.value ?? "",
17032
+ node_id: resolveInviteNodeId({
17033
+ isInvite: isVertoInviteMessage(vertoMessage),
17034
+ reattach: this.webRtcCallSession.options.reattach === true,
17035
+ explicitNodeId: this.webRtcCallSession.options.nodeId,
17036
+ currentNodeId: this._nodeId$.value
17037
+ }),
16720
17038
  subscribe
16721
17039
  };
16722
17040
  }
16723
17041
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
16724
- logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
16725
- const vertoByeOrAccepted = await (0, import_cjs$14.firstValueFrom)((0, import_cjs$14.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$14.takeUntil)(this.destroyed$))).catch(() => null);
17042
+ logger$15.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17043
+ 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);
16726
17044
  if (vertoByeOrAccepted === null) {
16727
- logger$14.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17045
+ logger$15.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
16728
17046
  return;
16729
17047
  }
16730
17048
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16731
- logger$14.info("[WebRTCManager] Call ended before answer was sent.");
17049
+ logger$15.info("[WebRTCManager] Call ended before answer was sent.");
16732
17050
  this.callSession?.destroy();
16733
17051
  } else if (!vertoByeOrAccepted) {
16734
- logger$14.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17052
+ logger$15.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
16735
17053
  try {
16736
17054
  await this.bye("USER_BUSY");
16737
17055
  } finally {
@@ -16739,14 +17057,14 @@ var WebRTCVertoManager = class extends VertoManager {
16739
17057
  this.callSession?.destroy();
16740
17058
  }
16741
17059
  } else {
16742
- logger$14.debug("[WebRTCManager] Call accepted, sending answer");
17060
+ logger$15.debug("[WebRTCManager] Call accepted, sending answer");
16743
17061
  try {
16744
17062
  this._signalingStatus$.next("connecting");
16745
17063
  await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
16746
17064
  await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
16747
17065
  await this.attachManager.attach(this.buildAttachableCall());
16748
17066
  } catch (error) {
16749
- logger$14.error("[WebRTCManager] Error sending Verto answer:", error);
17067
+ logger$15.error("[WebRTCManager] Error sending Verto answer:", error);
16750
17068
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16751
17069
  await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
16752
17070
  }
@@ -16787,6 +17105,14 @@ var WebRTCVertoManager = class extends VertoManager {
16787
17105
  async unmuteMainVideoInputDevice() {
16788
17106
  return this.mainPeerConnection.restoreTrackSender("video");
16789
17107
  }
17108
+ /** Get or lazily create the local audio pipeline for the main peer connection. */
17109
+ ensureLocalAudioPipeline() {
17110
+ return this.mainPeerConnection.ensureLocalAudioPipeline();
17111
+ }
17112
+ /** The currently-active local audio pipeline, or null if it hasn't been created. */
17113
+ get localAudioPipeline() {
17114
+ return this.mainPeerConnection.localAudioPipeline;
17115
+ }
16790
17116
  async addInputDevice(options = {
16791
17117
  audio: false,
16792
17118
  video: true
@@ -16835,12 +17161,12 @@ var WebRTCVertoManager = class extends VertoManager {
16835
17161
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
16836
17162
  this.onError?.(error);
16837
17163
  });
16838
- await (0, import_cjs$14.firstValueFrom)(rtcPeerConnController.connectionState$.pipe((0, import_cjs$14.filter)((state) => state === "connected"), (0, import_cjs$14.take)(1), (0, import_cjs$14.timeout)(this._screenShareTimeoutMs), (0, import_cjs$14.takeUntil)(this.destroyed$)));
17164
+ 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$)));
16839
17165
  this._screenShareStatus$.next("started");
16840
- logger$14.info("[WebRTCManager] Screen share started successfully.");
17166
+ logger$15.info("[WebRTCManager] Screen share started successfully.");
16841
17167
  return rtcPeerConnController.id;
16842
17168
  } catch (error) {
16843
- logger$14.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17169
+ logger$15.warn("[WebRTCManager] Error initializing additional peer connection:", error);
16844
17170
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16845
17171
  if (rtcPeerConnController) rtcPeerConnController.destroy();
16846
17172
  this._screenShareStatus$.next("none");
@@ -16859,9 +17185,9 @@ var WebRTCVertoManager = class extends VertoManager {
16859
17185
  if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
16860
17186
  }
16861
17187
  async removeScreenMedia() {
16862
- if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$14.warn("[WebRTCManager] No active screen share to stop.");
17188
+ if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$15.warn("[WebRTCManager] No active screen share to stop.");
16863
17189
  if (!this._screenShareId) {
16864
- logger$14.debug("[WebRTCManager] No screen share peer connection found.");
17190
+ logger$15.debug("[WebRTCManager] No screen share peer connection found.");
16865
17191
  return;
16866
17192
  }
16867
17193
  this._screenShareStatus$.next("stopping");
@@ -16890,7 +17216,7 @@ var WebRTCVertoManager = class extends VertoManager {
16890
17216
  dialogParams: this.dialogParams(rtcPeerConnController)
16891
17217
  }));
16892
17218
  } catch (error) {
16893
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17219
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
16894
17220
  throw error;
16895
17221
  }
16896
17222
  }
@@ -16908,7 +17234,7 @@ var WebRTCVertoManager = class extends VertoManager {
16908
17234
  try {
16909
17235
  await this.executeVerto(vertoInfoMessage);
16910
17236
  } catch (error) {
16911
- logger$14.warn("[WebRTCManager] Error sending DTMF digits:", error);
17237
+ logger$15.warn("[WebRTCManager] Error sending DTMF digits:", error);
16912
17238
  throw error;
16913
17239
  }
16914
17240
  }
@@ -16919,10 +17245,10 @@ var WebRTCVertoManager = class extends VertoManager {
16919
17245
  action: "transfer"
16920
17246
  });
16921
17247
  try {
16922
- logger$14.debug("[WebRTCManager] Transferring call with options:", options);
17248
+ logger$15.debug("[WebRTCManager] Transferring call with options:", options);
16923
17249
  await this.executeVerto(message);
16924
17250
  } catch (error) {
16925
- logger$14.error("[WebRTCManager] Error transferring call:", error);
17251
+ logger$15.error("[WebRTCManager] Error transferring call:", error);
16926
17252
  throw error;
16927
17253
  }
16928
17254
  }
@@ -16936,6 +17262,77 @@ var WebRTCVertoManager = class extends VertoManager {
16936
17262
  }
16937
17263
  };
16938
17264
 
17265
+ //#endregion
17266
+ //#region src/controllers/RemoteAudioMeter.ts
17267
+ var import_cjs$14 = require_cjs();
17268
+ const logger$14 = getLogger();
17269
+ /**
17270
+ * Read-only audio level meter for a remote MediaStream. Attaches an
17271
+ * AnalyserNode to a MediaStreamAudioSourceNode so it observes the stream
17272
+ * without affecting the caller's playback path (no GainNode, no destination).
17273
+ *
17274
+ * The server delivers all remote audio as a single mixed stream — there is
17275
+ * no per-participant demux — so this meter reports the aggregate remote
17276
+ * level, not per-member.
17277
+ */
17278
+ var RemoteAudioMeter = class extends Destroyable {
17279
+ constructor(options = {}) {
17280
+ super();
17281
+ this._source = null;
17282
+ this._stream = null;
17283
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
17284
+ this._analyser = this._audioContext.createAnalyser();
17285
+ this._analyser.fftSize = 2048;
17286
+ this._analyser.smoothingTimeConstant = .3;
17287
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
17288
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
17289
+ }
17290
+ /** RMS level of the remote audio, 0..1. 0 when no stream is attached. */
17291
+ get level$() {
17292
+ return this.deferEmission((0, import_cjs$14.interval)(this._pollIntervalMs, import_cjs$14.animationFrameScheduler).pipe((0, import_cjs$14.map)(() => this.computeLevel())));
17293
+ }
17294
+ /**
17295
+ * Attach (or replace) the MediaStream whose audio track is being metered.
17296
+ * Pass null to detach without destroying the meter.
17297
+ */
17298
+ setStream(stream) {
17299
+ if (this._source) {
17300
+ try {
17301
+ this._source.disconnect();
17302
+ } catch (error) {
17303
+ logger$14.debug("[RemoteAudioMeter] source disconnect warning:", error);
17304
+ }
17305
+ this._source = null;
17306
+ this._stream = null;
17307
+ }
17308
+ if (!stream || stream.getAudioTracks().length === 0) return;
17309
+ this._stream = new MediaStream(stream.getAudioTracks());
17310
+ this._source = this._audioContext.createMediaStreamSource(this._stream);
17311
+ }
17312
+ destroy() {
17313
+ if (this._source) {
17314
+ try {
17315
+ this._source.disconnect();
17316
+ } catch {}
17317
+ this._source = null;
17318
+ }
17319
+ this._audioContext.close().catch((error) => {
17320
+ logger$14.debug("[RemoteAudioMeter] audio context close warning:", error);
17321
+ });
17322
+ super.destroy();
17323
+ }
17324
+ computeLevel() {
17325
+ if (!this._source) return 0;
17326
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
17327
+ let sum = 0;
17328
+ for (const sample$1 of this._analyserBuffer) {
17329
+ const normalized = (sample$1 - 128) / 128;
17330
+ sum += normalized * normalized;
17331
+ }
17332
+ return Math.sqrt(sum / this._analyserBuffer.length);
17333
+ }
17334
+ };
17335
+
16939
17336
  //#endregion
16940
17337
  //#region src/controllers/RTCStatsMonitor.ts
16941
17338
  var import_cjs$13 = require_cjs();
@@ -17093,11 +17490,11 @@ var RTCStatsMonitor = class extends Destroyable {
17093
17490
  let availableOutgoingBitrate;
17094
17491
  report.forEach((stat) => {
17095
17492
  if (isInboundRtpStat(stat)) if (stat.kind === "audio") {
17096
- audioPacketsReceived += stat.packetsReceived ?? this.lastAudioPacketsReceived;
17493
+ audioPacketsReceived += stat.packetsReceived ?? 0;
17097
17494
  audioPacketsLost += stat.packetsLost ?? 0;
17098
17495
  audioJitter = Math.max(audioJitter, (stat.jitter ?? 0) * 1e3);
17099
17496
  } else {
17100
- videoPacketsReceived += stat.packetsReceived ?? this.lastVideoPacketsReceived;
17497
+ videoPacketsReceived += stat.packetsReceived ?? 0;
17101
17498
  videoPacketsLost += stat.packetsLost ?? 0;
17102
17499
  }
17103
17500
  if (isCandidatePairStat(stat) && stat.state === "succeeded" && stat.nominated) {
@@ -17749,6 +18146,8 @@ var WebRTCCall = class extends Destroyable {
17749
18146
  this._bandwidthConstrained$ = this.createBehaviorSubject(false);
17750
18147
  this._mediaParamsUpdated$ = this.createSubject();
17751
18148
  this._customSubscriptions = /* @__PURE__ */ new Map();
18149
+ this._pushToTalkEnabled = false;
18150
+ this._remoteAudioMeter = null;
17752
18151
  this.id = options.callId ?? v4_default();
17753
18152
  this.to = options.to;
17754
18153
  this._userVariables$.next({
@@ -18147,10 +18546,10 @@ var WebRTCCall = class extends Destroyable {
18147
18546
  try {
18148
18547
  if (this.vertoManager.requestIceRestartAll) await this.vertoManager.requestIceRestartAll(relayOnly);
18149
18548
  else await this.vertoManager.requestIceRestart?.(relayOnly);
18150
- return true;
18151
18549
  } catch {
18152
18550
  return false;
18153
18551
  }
18552
+ return this.waitForPeerConnectionConnected();
18154
18553
  },
18155
18554
  disableVideo: () => {
18156
18555
  try {
@@ -18242,6 +18641,27 @@ var WebRTCCall = class extends Destroyable {
18242
18641
  }
18243
18642
  }
18244
18643
  /**
18644
+ * Wait for the underlying RTCPeerConnection to reach 'connected' after
18645
+ * triggering an ICE restart. Resolves true on success, false on failure
18646
+ * or if the state doesn't transition within the configured timeout.
18647
+ *
18648
+ * Polls connectionState directly because the recovery manager already
18649
+ * wraps this call in its own withTimeout(); a separate listener-based
18650
+ * implementation would race the outer timeout in subtle ways.
18651
+ */
18652
+ async waitForPeerConnectionConnected() {
18653
+ const pc = this.rtcPeerConnection;
18654
+ if (!pc) return false;
18655
+ const deadline = Date.now() + PEER_CONNECTION_RECOVERY_WAIT_MS;
18656
+ for (;;) {
18657
+ const state = pc.connectionState;
18658
+ if (state === "connected") return true;
18659
+ if (state === "failed" || state === "closed") return false;
18660
+ if (Date.now() >= deadline) return false;
18661
+ await new Promise((resolve) => setTimeout(resolve, PEER_CONNECTION_RECOVERY_POLL_MS));
18662
+ }
18663
+ }
18664
+ /**
18245
18665
  * @internal Stop and destroy resilience subsystems (on disconnect/destroy).
18246
18666
  * Clears references so they can be re-created on reconnect.
18247
18667
  */
@@ -18378,8 +18798,13 @@ var WebRTCCall = class extends Destroyable {
18378
18798
  const cached = this._customSubscriptions.get(eventType);
18379
18799
  if (cached) return cached;
18380
18800
  const filtered$ = this.callSessionEvents$.pipe((0, import_cjs$11.filter)((event) => event.event_type === eventType), (0, import_cjs$11.map)((event) => JSON.parse(JSON.stringify(event))), (0, import_cjs$11.takeUntil)(this._destroyed$));
18801
+ this._sendVertoSubscribe(eventType).then(() => {
18802
+ this._customSubscriptions.set(eventType, filtered$);
18803
+ }, (error) => {
18804
+ this._customSubscriptions.delete(eventType);
18805
+ logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed, not caching:`, error);
18806
+ });
18381
18807
  this._customSubscriptions.set(eventType, filtered$);
18382
- this._sendVertoSubscribe(eventType);
18383
18808
  return filtered$;
18384
18809
  }
18385
18810
  get webrtcMessages$() {
@@ -18494,37 +18919,156 @@ var WebRTCCall = class extends Destroyable {
18494
18919
  async transfer(options) {
18495
18920
  return this.vertoManager.transfer(options);
18496
18921
  }
18922
+ /**
18923
+ * Set the local microphone gain as a percentage applied before transmission.
18924
+ *
18925
+ * - `0` = silent
18926
+ * - `100` = unity (no change, default)
18927
+ * - `200` = 2× digital boost (max; expect clipping / noise amplification)
18928
+ *
18929
+ * Values are clamped to [0, 200]. Engages the local audio pipeline on
18930
+ * first use (one-time cost).
18931
+ *
18932
+ * Note: this is a **digital** multiplier applied in a Web Audio GainNode
18933
+ * between your mic track and the RTCRtpSender — it does not change the
18934
+ * physical mic's hardware sensitivity. Browsers' autoGainControl can
18935
+ * fight the setting; call {@link setAutoGainControl}(false) for
18936
+ * predictable behaviour.
18937
+ *
18938
+ * @param value - Gain percentage (0..200; 100 = unity).
18939
+ */
18940
+ setLocalMicrophoneGain(value) {
18941
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18942
+ if (!pipeline) {
18943
+ logger$11.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
18944
+ return;
18945
+ }
18946
+ const percent = Math.max(0, Math.min(200, value));
18947
+ pipeline.setGain(percent / 100);
18948
+ }
18949
+ /** Observable of the current local microphone gain (0..200, where 100 = unity). */
18950
+ get localMicrophoneGain$() {
18951
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18952
+ if (!pipeline) return (0, import_cjs$11.of)(100).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18953
+ return this.publicCachedObservable("localMicrophoneGain$", () => pipeline.gain$.pipe((0, import_cjs$11.map)((multiplier) => multiplier * 100), (0, import_cjs$11.takeUntil)(this._destroyed$)));
18954
+ }
18955
+ /**
18956
+ * Observable of the RMS audio level of the local microphone, 0..1.
18957
+ * Emits at ~30fps while a mic track is active. Engages the local audio
18958
+ * pipeline on first subscription.
18959
+ */
18960
+ get localAudioLevel$() {
18961
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18962
+ if (!pipeline) return (0, import_cjs$11.of)(0).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18963
+ return this.publicCachedObservable("localAudioLevel$", () => pipeline.level$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$), (0, import_cjs$11.share)()));
18964
+ }
18965
+ /**
18966
+ * Observable that is `true` while the local participant is speaking
18967
+ * (RMS level above the VAD threshold, with hold time to avoid flicker).
18968
+ */
18969
+ get localSpeaking$() {
18970
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18971
+ if (!pipeline) return (0, import_cjs$11.of)(false).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18972
+ return this.publicCachedObservable("localSpeaking$", () => pipeline.speaking$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$), (0, import_cjs$11.share)()));
18973
+ }
18974
+ /**
18975
+ * Enable push-to-talk: while {@link setPushToTalkActive} has been called
18976
+ * with `false`, the microphone gain is forced to 0; calling
18977
+ * {@link setPushToTalkActive} with `true` restores the configured gain.
18978
+ * Use this instead of mute/unmute for instant talk/silence transitions
18979
+ * because it doesn't rebuild the track.
18980
+ *
18981
+ * This method installs the pipeline but does not attach any keyboard
18982
+ * listener — consumers bind the key themselves and call
18983
+ * {@link setPushToTalkActive} on keydown/keyup.
18984
+ */
18985
+ enablePushToTalk() {
18986
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18987
+ if (!pipeline) {
18988
+ logger$11.warn("[Call] enablePushToTalk: audio pipeline unavailable");
18989
+ return;
18990
+ }
18991
+ pipeline.setPTTActive(false);
18992
+ this._pushToTalkEnabled = true;
18993
+ }
18994
+ /** Disable push-to-talk; mic gain returns to the configured value. */
18995
+ disablePushToTalk() {
18996
+ this.vertoManager.localAudioPipeline?.setPTTActive(true);
18997
+ this._pushToTalkEnabled = false;
18998
+ }
18999
+ /**
19000
+ * While push-to-talk is enabled, sets the talk state. `true` = transmitting,
19001
+ * `false` = silent. No-op if push-to-talk has not been enabled.
19002
+ */
19003
+ setPushToTalkActive(active) {
19004
+ if (!this._pushToTalkEnabled) return;
19005
+ this.vertoManager.localAudioPipeline?.setPTTActive(active);
19006
+ }
19007
+ /**
19008
+ * Toggle echo cancellation on the local mic at runtime. Applied via
19009
+ * `track.applyConstraints`; browsers that don't honour runtime constraints
19010
+ * (notably iOS Safari) fall back to re-acquiring the track with the new
19011
+ * constraint set and plumbing the replacement through the local audio
19012
+ * pipeline if one is active.
19013
+ */
19014
+ async setEchoCancellation(enabled) {
19015
+ await this.vertoManager.updateMediaConstraints({ audio: { echoCancellation: enabled } });
19016
+ }
19017
+ /** Toggle browser noise suppression on the local mic at runtime. */
19018
+ async setNoiseSuppression(enabled) {
19019
+ await this.vertoManager.updateMediaConstraints({ audio: { noiseSuppression: enabled } });
19020
+ }
19021
+ /** Toggle browser automatic gain control on the local mic at runtime. */
19022
+ async setAutoGainControl(enabled) {
19023
+ await this.vertoManager.updateMediaConstraints({ audio: { autoGainControl: enabled } });
19024
+ }
19025
+ /**
19026
+ * Observable of the aggregate remote audio level, 0..1 RMS. The server
19027
+ * delivers a single mixed audio stream for all remote participants — this
19028
+ * meter reports that mix. Per-participant audio is not available client-side.
19029
+ *
19030
+ * Engages a shared AudioContext on first subscription (cheap — one
19031
+ * AnalyserNode, no GainNode, no destination) so it does not affect the
19032
+ * caller's audio element playback.
19033
+ */
19034
+ get remoteAudioLevel$() {
19035
+ return this.publicCachedObservable("remoteAudioLevel$", () => {
19036
+ this._remoteAudioMeter ??= new RemoteAudioMeter();
19037
+ const meter = this._remoteAudioMeter;
19038
+ this.subscribeTo(this.vertoManager.remoteStream$, (stream) => {
19039
+ meter.setStream(stream);
19040
+ });
19041
+ return meter.level$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$), (0, import_cjs$11.share)());
19042
+ });
19043
+ }
18497
19044
  /** Destroys the call, releasing all resources and subscriptions. */
18498
19045
  destroy() {
18499
19046
  if (this._status$.value === "destroyed") return;
18500
19047
  this._status$.next("destroyed");
18501
19048
  this.stopResilienceSubsystems();
19049
+ this._remoteAudioMeter?.destroy();
19050
+ this._remoteAudioMeter = null;
18502
19051
  this.vertoManager.destroy();
18503
19052
  this.callEventsManager.destroy();
18504
19053
  super.destroy();
18505
19054
  }
18506
19055
  /**
18507
19056
  * @internal Send a verto.subscribe message to add an event type to the
18508
- * server's subscription list for this call. Best-effort failures are
18509
- * logged but don't prevent the filtered observable from being returned.
19057
+ * server's subscription list for this call. Returns the underlying RPC
19058
+ * promise so callers can decide whether to cache the observable on success
19059
+ * or retry on failure.
18510
19060
  */
18511
- _sendVertoSubscribe(eventType) {
18512
- try {
18513
- const message = VertoSubscribe({
18514
- sessid: this.id,
18515
- eventChannel: [eventType]
18516
- });
18517
- const params = {
18518
- callID: this.id,
18519
- node_id: this.vertoManager.nodeId ?? "",
18520
- message
18521
- };
18522
- this.clientSession.execute(WebrtcVerto(params)).catch((error) => {
18523
- logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed (non-fatal):`, error);
18524
- });
18525
- } catch (error) {
18526
- logger$11.warn(`[Call] Failed to send verto.subscribe for '${eventType}':`, error);
18527
- }
19061
+ async _sendVertoSubscribe(eventType) {
19062
+ const message = VertoSubscribe({
19063
+ sessid: this.id,
19064
+ eventChannel: [eventType]
19065
+ });
19066
+ const params = {
19067
+ callID: this.id,
19068
+ node_id: this.vertoManager.nodeId ?? "",
19069
+ message
19070
+ };
19071
+ await this.clientSession.execute(WebrtcVerto(params));
18528
19072
  }
18529
19073
  };
18530
19074
 
@@ -18541,11 +19085,21 @@ function inferCallErrorKind(error) {
18541
19085
  if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError) return "network";
18542
19086
  return "internal";
18543
19087
  }
19088
+ /** JSON-RPC error codes that ClientSessionManager treats as recoverable at the
19089
+ * session level. Surfacing one of these against an in-flight call should not
19090
+ * destroy the call, because the session will reauthenticate and any pending
19091
+ * RPC can then be retried. */
19092
+ const RECOVERABLE_RPC_CODES = new Set([
19093
+ RPC_ERROR_REQUESTER_VALIDATION_FAILED,
19094
+ RPC_ERROR_AUTHENTICATION_FAILED,
19095
+ RPC_ERROR_INVALID_PARAMS
19096
+ ]);
18544
19097
  /** Determines whether an error should be fatal (destroy the call). */
18545
19098
  function isFatalError(error) {
18546
19099
  if (error instanceof VertoPongError) return false;
18547
19100
  if (error instanceof MediaTrackError) return false;
18548
19101
  if (error instanceof RPCTimeoutError) return false;
19102
+ if (error instanceof JSONRPCError && RECOVERABLE_RPC_CODES.has(error.code)) return false;
18549
19103
  return true;
18550
19104
  }
18551
19105
  /**
@@ -18983,10 +19537,10 @@ var PendingRPC = class PendingRPC {
18983
19537
  }
18984
19538
  let isSettled = false;
18985
19539
  const subscription = (0, import_cjs$8.race)(responses$.pipe((0, import_cjs$8.filter)((result) => result.id === request.id), (0, import_cjs$8.take)(1)), new import_cjs$8.Observable((subscriber) => {
18986
- const timer$3 = setTimeout(() => {
19540
+ const timer$4 = setTimeout(() => {
18987
19541
  subscriber.error(new RPCTimeoutError(request.id, timeoutMs));
18988
19542
  }, timeoutMs);
18989
- return () => clearTimeout(timer$3);
19543
+ return () => clearTimeout(timer$4);
18990
19544
  }), signal ? new import_cjs$8.Observable((subscriber) => {
18991
19545
  const abortHandler = () => {
18992
19546
  subscriber.error(new DOMException("The operation was aborted", "AbortError"));
@@ -19052,7 +19606,7 @@ var ClientSessionManager = class extends Destroyable {
19052
19606
  this.attachManager = attachManager;
19053
19607
  this.dpopManager = dpopManager;
19054
19608
  this.callCreateTimeout = 6e3;
19055
- this.agent = `signalwire-typescript-sdk/1.0.0`;
19609
+ this.agent = `signalwire-js/4.0.0`;
19056
19610
  this.eventAcks = true;
19057
19611
  this.authorizationState$ = this.createReplaySubject(1);
19058
19612
  this.connectVersion = {
@@ -19064,7 +19618,7 @@ var ClientSessionManager = class extends Destroyable {
19064
19618
  this._errors$ = this.createReplaySubject(1);
19065
19619
  this._authState$ = this.createBehaviorSubject({ kind: "unauthenticated" });
19066
19620
  this._wasClientBound = false;
19067
- this._subscriberInfo$ = this.createBehaviorSubject(null);
19621
+ this._userInfo$ = this.createBehaviorSubject(null);
19068
19622
  this._calls$ = this.createBehaviorSubject({});
19069
19623
  this._iceServers$ = this.createBehaviorSubject([]);
19070
19624
  attachManager.setSession(this);
@@ -19077,11 +19631,11 @@ var ClientSessionManager = class extends Destroyable {
19077
19631
  get incomingCalls() {
19078
19632
  return Object.values(this._calls$.value).filter((call) => call.direction === "inbound");
19079
19633
  }
19080
- get subscriberInfo$() {
19081
- return this._subscriberInfo$.asObservable();
19634
+ get userInfo$() {
19635
+ return this._userInfo$.asObservable();
19082
19636
  }
19083
- get subscriberInfo() {
19084
- return this._subscriberInfo$.value;
19637
+ get userInfo() {
19638
+ return this._userInfo$.value;
19085
19639
  }
19086
19640
  get calls$() {
19087
19641
  return this.cachedObservable("calls$", () => this._calls$.pipe((0, import_cjs$7.map)((calls) => Object.values(calls))));
@@ -19386,7 +19940,6 @@ var ClientSessionManager = class extends Destroyable {
19386
19940
  displayDirection: invite.display_direction,
19387
19941
  userVariables: invite.userVariables
19388
19942
  });
19389
- await (0, import_cjs$7.firstValueFrom)(callSession.status$);
19390
19943
  this._calls$.next({
19391
19944
  [`${callSession.id}`]: callSession,
19392
19945
  ...this._calls$.value
@@ -19407,7 +19960,7 @@ var ClientSessionManager = class extends Destroyable {
19407
19960
  logger$8.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
19408
19961
  return;
19409
19962
  }
19410
- const storedOptions = this.attachManager.consumePendingAttachment(callID);
19963
+ const storedOptions = await this.attachManager.consumePendingAttachment(callID);
19411
19964
  logger$8.debug(`[Session] Creating reattached call for callID: ${callID}`);
19412
19965
  const callSession = await this.createCall({
19413
19966
  nodeId: attach.node_id,
@@ -19419,7 +19972,6 @@ var ClientSessionManager = class extends Destroyable {
19419
19972
  reattach: true,
19420
19973
  ...storedOptions
19421
19974
  });
19422
- await (0, import_cjs$7.firstValueFrom)(callSession.status$);
19423
19975
  this._calls$.next({
19424
19976
  [`${callSession.id}`]: callSession,
19425
19977
  ...this._calls$.value
@@ -19529,22 +20081,22 @@ var ConversationMessageCollection = class extends EntityCollection {
19529
20081
  }
19530
20082
  };
19531
20083
  var ConversationsManager = class {
19532
- constructor(clientSession, http, getSubscriberAddressId, onError) {
20084
+ constructor(clientSession, http, getUserAddressId, onError) {
19533
20085
  this.clientSession = clientSession;
19534
20086
  this.http = http;
19535
- this.getSubscriberAddressId = getSubscriberAddressId;
20087
+ this.getUserAddressId = getUserAddressId;
19536
20088
  this.onError = onError;
19537
20089
  this.groupIds = /* @__PURE__ */ new Map();
19538
20090
  }
19539
20091
  async join(addressId) {
19540
- const subscriberFromAddressId = this.getSubscriberAddressId();
20092
+ const userFromAddressId = this.getUserAddressId();
19541
20093
  try {
19542
20094
  const response = await this.http.request({
19543
20095
  ...POST_PARAMS,
19544
20096
  url: `/api/fabric/conversations/join`,
19545
20097
  body: JSON.stringify({
19546
- from_fabric_address_id: subscriberFromAddressId,
19547
- fabric_address_ids: [addressId, subscriberFromAddressId]
20098
+ from_fabric_address_id: userFromAddressId,
20099
+ fabric_address_ids: [addressId, userFromAddressId]
19548
20100
  })
19549
20101
  });
19550
20102
  if (response.ok && !!response.body) {
@@ -19566,14 +20118,14 @@ var ConversationsManager = class {
19566
20118
  }
19567
20119
  async sendText(text, destinationAddressId) {
19568
20120
  const groupId = this.groupIds.get(destinationAddressId) ?? await this.join(destinationAddressId);
19569
- const subscriberFromAddressId = this.getSubscriberAddressId();
20121
+ const userFromAddressId = this.getUserAddressId();
19570
20122
  try {
19571
20123
  if ((await this.http.request({
19572
20124
  ...POST_PARAMS,
19573
20125
  url: "/api/fabric/messages",
19574
20126
  body: JSON.stringify({
19575
20127
  group_id: groupId,
19576
- from_fabric_address_id: subscriberFromAddressId,
20128
+ from_fabric_address_id: userFromAddressId,
19577
20129
  text
19578
20130
  })
19579
20131
  })).ok) return;
@@ -19646,17 +20198,17 @@ var DeviceTokenManager = class extends Destroyable {
19646
20198
  return this._effectiveExpireIn;
19647
20199
  }
19648
20200
  /**
19649
- * Activates the Client Bound SAT flow when the subscriber's token has
20201
+ * Activates the Client Bound SAT flow when the user's token has
19650
20202
  * `sat:refresh` scope.
19651
20203
  *
19652
20204
  * Steps:
19653
- * 1. Check subscriber's `sat_claims` for `sat:refresh` scope
20205
+ * 1. Check user's `sat_claims` for `sat:refresh` scope
19654
20206
  * 2. Call `/api/fabric/subscriber/devices/token` with a DPoP proof
19655
20207
  * 3. Reauthenticate the session with the Client Bound SAT + DPoP proof
19656
20208
  * 4. Emit token to trigger the reactive refresh pipeline
19657
20209
  */
19658
- async activate(subscriber, session, updateCredential) {
19659
- const { satClaims } = subscriber;
20210
+ async activate(user, session, updateCredential) {
20211
+ const { satClaims } = user;
19660
20212
  if (!satClaims?.scope?.includes(SAT_REFRESH_SCOPE)) {
19661
20213
  logger$6.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
19662
20214
  return;
@@ -19939,9 +20491,9 @@ const isEmptyArray = (a) => {
19939
20491
  };
19940
20492
 
19941
20493
  //#endregion
19942
- //#region src/utils/warnup.ts
20494
+ //#region src/utils/warmup.ts
19943
20495
  var import_cjs$4 = require_cjs();
19944
- const warnup = (observable) => {
20496
+ const warmup = (observable) => {
19945
20497
  observable.pipe((0, import_cjs$4.take)(1)).subscribe();
19946
20498
  };
19947
20499
 
@@ -19990,7 +20542,7 @@ var DirectoryManager = class extends Destroyable {
19990
20542
  return address;
19991
20543
  }));
19992
20544
  if (observable) {
19993
- warnup(observable);
20545
+ warmup(observable);
19994
20546
  this._observableRegistry.set(id, observable);
19995
20547
  }
19996
20548
  this._addressesInstances.set(id, address);
@@ -20142,10 +20694,9 @@ var WebSocketController = class WebSocketController extends Destroyable {
20142
20694
  else this._status$.next("disconnected");
20143
20695
  }
20144
20696
  reconnect() {
20145
- if (this.shouldReconnect) {
20146
- this._status$.next("reconnecting");
20147
- this.scheduleReconnection();
20148
- } else this._status$.next("disconnected");
20697
+ this.shouldReconnect = true;
20698
+ this._status$.next("reconnecting");
20699
+ this.scheduleReconnection();
20149
20700
  }
20150
20701
  send(data) {
20151
20702
  if (this._status$.value === "connected" && this.socket?.readyState === 1) {
@@ -20504,7 +21055,7 @@ var SignalWire = class extends Destroyable {
20504
21055
  constructor(credentialProvider, options = {}) {
20505
21056
  super();
20506
21057
  this.preferences = new ClientPreferences();
20507
- this._subscriber$ = this.createBehaviorSubject(void 0);
21058
+ this._user$ = this.createBehaviorSubject(void 0);
20508
21059
  this._directory$ = this.createBehaviorSubject(void 0);
20509
21060
  this._isConnected$ = this.createBehaviorSubject(false);
20510
21061
  this._isRegistered$ = this.createBehaviorSubject(false);
@@ -20644,7 +21195,7 @@ var SignalWire = class extends Destroyable {
20644
21195
  if (this._deps.persistSession) this._deps.storage.setItem("sw:cached_credential", credential, "local");
20645
21196
  }
20646
21197
  async init() {
20647
- this._subscriber$.next(new Subscriber(this._deps.http));
21198
+ this._user$.next(new User(this._deps.http));
20648
21199
  if (!this._options.skipConnection) await this.connect();
20649
21200
  if (!this._options.reconnectAttachedCalls && this._attachManager) await this._attachManager.flush();
20650
21201
  if (!this._options.skipRegister) try {
@@ -20706,14 +21257,15 @@ var SignalWire = class extends Destroyable {
20706
21257
  * `'reconnecting'`, `'disconnecting'`, or `'disconnected'`.
20707
21258
  */
20708
21259
  async connect() {
21260
+ await this.teardownTransportAndSession();
20709
21261
  try {
20710
- const subscriber = this._subscriber$.value;
20711
- if (!subscriber) throw new UnexpectedError("Subscriber not initialized before connect");
20712
- if (!await (0, import_cjs$1.firstValueFrom)(subscriber.fetched$)) throw new UnexpectedError("Failed to fetch subscriber information - fetched$ emitted false");
20713
- this._deps.subscriber = subscriber;
21262
+ const user = this._user$.value;
21263
+ if (!user) throw new UnexpectedError("User not initialized before connect");
21264
+ if (!await (0, import_cjs$1.firstValueFrom)(user.fetched$)) throw new UnexpectedError("Failed to fetch user information - fetched$ emitted false");
21265
+ this._deps.user = user;
20714
21266
  } catch (error) {
20715
- logger$1.error(`[SignalWire] Failed to fetch subscriber information: ${error instanceof Error ? error.message : "Unknown error"}. This usually means the subscriber token is invalid or expired.`);
20716
- throw new UnexpectedError("Error fetching subscriber information", { cause: error });
21267
+ logger$1.error(`[SignalWire] Failed to fetch user information: ${error instanceof Error ? error.message : "Unknown error"}. This usually means the user token is invalid or expired.`);
21268
+ throw new UnexpectedError("Error fetching user information", { cause: error });
20717
21269
  }
20718
21270
  const errorHandler = (error) => {
20719
21271
  this._errors$.next(error);
@@ -20747,7 +21299,7 @@ var SignalWire = class extends Destroyable {
20747
21299
  logger$1.debug("[SignalWire] Developer refresh disabled — Client Bound SAT activation starting");
20748
21300
  }
20749
21301
  this._deviceTokenManager = new DeviceTokenManager(this._dpopManager, this._deps.http, (error) => this._errors$.next(error), () => this._deps.credential);
20750
- await this._deviceTokenManager.activate(this._deps.subscriber, this._clientSession, (cred) => {
21302
+ await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
20751
21303
  this._deps.credential = {
20752
21304
  ...this._deps.credential,
20753
21305
  ...cred
@@ -20757,7 +21309,7 @@ var SignalWire = class extends Destroyable {
20757
21309
  this.subscribeTo(this._clientSession.authenticated$.pipe((0, import_cjs$1.skip)(1), (0, import_cjs$1.filter)(Boolean)), async () => {
20758
21310
  try {
20759
21311
  if (this._deviceTokenManager) {
20760
- await this._deviceTokenManager.activate(this._deps.subscriber, this._clientSession, (cred) => {
21312
+ await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
20761
21313
  this._deps.credential = {
20762
21314
  ...this._deps.credential,
20763
21315
  ...cred
@@ -20770,15 +21322,15 @@ var SignalWire = class extends Destroyable {
20770
21322
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20771
21323
  }
20772
21324
  try {
20773
- logger$1.debug("[SignalWire] Re-registering subscriber after reconnect");
21325
+ logger$1.debug("[SignalWire] Re-registering user after reconnect");
20774
21326
  await this.register();
20775
- logger$1.debug("[SignalWire] Subscriber re-registered successfully after reconnect");
21327
+ logger$1.debug("[SignalWire] User re-registered successfully after reconnect");
20776
21328
  } catch (error) {
20777
21329
  logger$1.error("[SignalWire] Re-registration failed after reconnect:", error);
20778
21330
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20779
21331
  }
20780
21332
  });
20781
- const conversationManager = new ConversationsManager(this._clientSession, this._deps.http, () => this._deps.getSubscriberFromAddressId(), errorHandler);
21333
+ const conversationManager = new ConversationsManager(this._clientSession, this._deps.http, () => this._deps.getUserFromAddressId(), errorHandler);
20782
21334
  const directory = new DirectoryManager(this._deps.http, this._clientSession, conversationManager, errorHandler);
20783
21335
  this._directory$.next(directory);
20784
21336
  this._clientSession.setDirectory(directory);
@@ -20789,22 +21341,22 @@ var SignalWire = class extends Destroyable {
20789
21341
  });
20790
21342
  }
20791
21343
  /**
20792
- * Observable that emits the {@link Subscriber} profile once fetched,
21344
+ * Observable that emits the {@link User} profile once fetched,
20793
21345
  * or `undefined` before authentication completes.
20794
21346
  *
20795
21347
  * @example
20796
21348
  * ```ts
20797
- * client.subscriber$.subscribe(sub => {
20798
- * if (sub) console.log('Logged in as', sub.email);
21349
+ * client.user$.subscribe(u => {
21350
+ * if (u) console.log('Logged in as', u.email);
20799
21351
  * });
20800
21352
  * ```
20801
21353
  */
20802
- get subscriber$() {
20803
- return this.deferEmission(this._subscriber$.asObservable());
21354
+ get user$() {
21355
+ return this.deferEmission(this._user$.asObservable());
20804
21356
  }
20805
- /** Current subscriber snapshot, or `undefined` if not yet authenticated. */
20806
- get subscriber() {
20807
- return this._subscriber$.value;
21357
+ /** Current user snapshot, or `undefined` if not yet authenticated. */
21358
+ get user() {
21359
+ return this._user$.value;
20808
21360
  }
20809
21361
  /**
20810
21362
  * Observable that emits the {@link Directory} instance once the client is connected,
@@ -20828,11 +21380,11 @@ var SignalWire = class extends Destroyable {
20828
21380
  get directory() {
20829
21381
  return this._directory$.value;
20830
21382
  }
20831
- /** Observable that emits when the subscriber registration state changes. */
21383
+ /** Observable that emits when the user registration state changes. */
20832
21384
  get isRegistered$() {
20833
21385
  return this.deferEmission(this._isRegistered$.asObservable());
20834
21386
  }
20835
- /** Whether the subscriber is currently registered. */
21387
+ /** Whether the user is currently registered. */
20836
21388
  get isRegistered() {
20837
21389
  return this._isRegistered$.value;
20838
21390
  }
@@ -20937,15 +21489,35 @@ var SignalWire = class extends Destroyable {
20937
21489
  this._refreshTimerId = void 0;
20938
21490
  }
20939
21491
  this._diagnosticsCollector?.record("connection", "disconnected");
20940
- await this._clientSession.disconnect();
20941
- this._clientSession.destroy();
21492
+ await this.teardownTransportAndSession();
20942
21493
  this._isConnected$.next(false);
20943
21494
  }
21495
+ /**
21496
+ * Tear down the current transport / session / attach manager. Safe to call
21497
+ * when nothing has been initialized yet (e.g. first connect()).
21498
+ */
21499
+ async teardownTransportAndSession() {
21500
+ const session = this._clientSession;
21501
+ const transport = this._transport;
21502
+ if (session) {
21503
+ try {
21504
+ await session.disconnect();
21505
+ } catch (error) {
21506
+ logger$1.warn("[SignalWire] Error disconnecting previous session:", error);
21507
+ }
21508
+ session.destroy();
21509
+ }
21510
+ if (transport) transport.destroy();
21511
+ this._clientSession = void 0;
21512
+ this._publicSession = void 0;
21513
+ this._transport = void 0;
21514
+ this._attachManager = void 0;
21515
+ }
20944
21516
  async waitAuthentication() {
20945
21517
  await (0, import_cjs$1.firstValueFrom)(this.ready$.pipe((0, import_cjs$1.filter)((ready$1) => ready$1 === true)));
20946
21518
  }
20947
21519
  /**
20948
- * Registers the subscriber as online to receive inbound calls and events.
21520
+ * Registers the user as online to receive inbound calls and events.
20949
21521
  *
20950
21522
  * Waits for authentication to complete before sending the registration.
20951
21523
  * If the initial attempt fails, reauthentication is attempted automatically.
@@ -20960,26 +21532,31 @@ var SignalWire = class extends Destroyable {
20960
21532
  params: {}
20961
21533
  }));
20962
21534
  this._isRegistered$.next(true);
21535
+ return;
20963
21536
  } catch (error) {
20964
- logger$1.debug("[SignalWire] Failed to register subscriber, trying reauthentication...");
20965
- if (this._deps.credential.token) this._clientSession.reauthenticate(this._deps.credential.token).then(async () => {
21537
+ if (!this._deps.credential.token) {
21538
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21539
+ throw error;
21540
+ }
21541
+ logger$1.debug("[SignalWire] Failed to register user, trying reauthentication...");
21542
+ try {
21543
+ await this._clientSession.reauthenticate(this._deps.credential.token);
20966
21544
  logger$1.debug("[SignalWire] Reauthentication successful, retrying register()");
20967
21545
  await this._transport.execute(RPCExecute({
20968
21546
  method: "subscriber.online",
20969
21547
  params: {}
20970
21548
  }));
20971
21549
  this._isRegistered$.next(true);
20972
- }).catch((reauthError) => {
21550
+ } catch (reauthError) {
20973
21551
  logger$1.error("[SignalWire] Reauthentication failed during register():", reauthError);
20974
- const registerError = new InvalidCredentialsError("Failed to register subscriber, and reauthentication attempt also failed. Please check your credentials.", { cause: reauthError instanceof Error ? reauthError : new Error(String(reauthError), { cause: reauthError }) });
21552
+ const registerError = new InvalidCredentialsError("Failed to register user, and reauthentication attempt also failed. Please check your credentials.", { cause: reauthError instanceof Error ? reauthError : new Error(String(reauthError), { cause: reauthError }) });
20975
21553
  this._errors$.next(registerError);
20976
- });
20977
- this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20978
- throw error;
21554
+ throw registerError;
21555
+ }
20979
21556
  }
20980
21557
  }
20981
21558
  /**
20982
- * Unregisters the subscriber, going offline for inbound calls.
21559
+ * Unregisters the user, going offline for inbound calls.
20983
21560
  *
20984
21561
  * The WebSocket connection remains open; use {@link disconnect} to fully close it.
20985
21562
  */
@@ -20991,7 +21568,7 @@ var SignalWire = class extends Destroyable {
20991
21568
  }));
20992
21569
  this._isRegistered$.next(false);
20993
21570
  } catch (error) {
20994
- logger$1.error("[SignalWire] Failed to unregister subscriber:", error);
21571
+ logger$1.error("[SignalWire] Failed to unregister user:", error);
20995
21572
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20996
21573
  throw error;
20997
21574
  }
@@ -21131,6 +21708,36 @@ var SignalWire = class extends Destroyable {
21131
21708
  selectAudioOutputDevice(device) {
21132
21709
  this._deviceController.selectAudioOutputDevice(device);
21133
21710
  }
21711
+ /**
21712
+ * Apply the currently selected audio output device to an HTMLMediaElement
21713
+ * (e.g. the `<audio>` or `<video>` element the consumer attached the
21714
+ * remote stream to). Uses `HTMLMediaElement.setSinkId` under the hood.
21715
+ * Returns a `Promise<boolean>`: `true` if the sink was applied,
21716
+ * `false` if the browser doesn't support `setSinkId` or no device is
21717
+ * selected.
21718
+ *
21719
+ * @example
21720
+ * ```ts
21721
+ * audioEl.srcObject = call.remoteStream;
21722
+ * await client.applySelectedAudioOutputDevice(audioEl);
21723
+ * ```
21724
+ */
21725
+ async applySelectedAudioOutputDevice(element) {
21726
+ const device = this._deviceController.selectedAudioOutputDevice;
21727
+ if (!device?.deviceId) return false;
21728
+ const withSink = element;
21729
+ if (typeof withSink.setSinkId !== "function") {
21730
+ logger$1.warn("[SignalWire] setSinkId not supported on this element / browser");
21731
+ return false;
21732
+ }
21733
+ try {
21734
+ await withSink.setSinkId(device.deviceId);
21735
+ return true;
21736
+ } catch (error) {
21737
+ logger$1.warn("[SignalWire] Failed to apply audio output device:", error);
21738
+ return false;
21739
+ }
21740
+ }
21134
21741
  /** Starts monitoring for media device changes (connect/disconnect). */
21135
21742
  enableDeviceMonitoring() {
21136
21743
  this._deviceController.enableDeviceMonitoring();
@@ -21311,6 +21918,7 @@ var EmbedTokenCredentialProvider = class {
21311
21918
  try {
21312
21919
  const response = await fetch(url, {
21313
21920
  method: "POST",
21921
+ headers: { "Content-Type": "application/json" },
21314
21922
  body: JSON.stringify({ token: this.embedToken }),
21315
21923
  signal: controller.signal
21316
21924
  });
@@ -21435,5 +22043,5 @@ emitReadyEvent();
21435
22043
  if (typeof process === "undefined") globalThis.process = { env: { NODE_ENV: "production" } };
21436
22044
 
21437
22045
  //#endregion
21438
- export { Address, CallCreateError, ClientPreferences, CollectionFetchError, DPoPInitError, DeviceTokenError, InvalidCredentialsError, MediaTrackError, MessageParseError, OverconstrainedFallbackError, Participant, PreflightError, RecoveryError, SelfCapabilities, SelfParticipant, SignalWire, StaticCredentialProvider, Subscriber, TokenRefreshError, UnexpectedError, VertoPongError, WebRTCCall, embeddableCall, isSelfParticipant, ready, setDebugOptions, setLogLevel, setLogger, version };
22046
+ export { Address, CallCreateError, ClientPreferences, CollectionFetchError, DPoPInitError, DeviceTokenError, EmbedTokenCredentialProvider, InvalidCredentialsError, MediaTrackError, MessageParseError, OverconstrainedFallbackError, Participant, PreflightError, RecoveryError, SelfCapabilities, SelfParticipant, SignalWire, StaticCredentialProvider, TokenRefreshError, UnexpectedError, User, VertoPongError, WebRTCCall, embeddableCall, getLogger, isSelfParticipant, ready, setDebugOptions, setLogLevel, setLogger, version };
21439
22047
  //# sourceMappingURL=browser.mjs.map