@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.
@@ -461,9 +461,9 @@ var require_Subscriber = /* @__PURE__ */ __commonJSMin(((exports) => {
461
461
  var NotificationFactories_1 = require_NotificationFactories();
462
462
  var timeoutProvider_1 = require_timeoutProvider();
463
463
  var errorContext_1$2 = require_errorContext();
464
- var Subscriber$1 = function(_super) {
465
- __extends$16(Subscriber$2, _super);
466
- function Subscriber$2(destination) {
464
+ var Subscriber = function(_super) {
465
+ __extends$16(Subscriber$1, _super);
466
+ function Subscriber$1(destination) {
467
467
  var _this = _super.call(this) || this;
468
468
  _this.isStopped = false;
469
469
  if (destination) {
@@ -472,54 +472,54 @@ var require_Subscriber = /* @__PURE__ */ __commonJSMin(((exports) => {
472
472
  } else _this.destination = exports.EMPTY_OBSERVER;
473
473
  return _this;
474
474
  }
475
- Subscriber$2.create = function(next, error, complete) {
475
+ Subscriber$1.create = function(next, error, complete) {
476
476
  return new SafeSubscriber(next, error, complete);
477
477
  };
478
- Subscriber$2.prototype.next = function(value) {
478
+ Subscriber$1.prototype.next = function(value) {
479
479
  if (this.isStopped) handleStoppedNotification(NotificationFactories_1.nextNotification(value), this);
480
480
  else this._next(value);
481
481
  };
482
- Subscriber$2.prototype.error = function(err) {
482
+ Subscriber$1.prototype.error = function(err) {
483
483
  if (this.isStopped) handleStoppedNotification(NotificationFactories_1.errorNotification(err), this);
484
484
  else {
485
485
  this.isStopped = true;
486
486
  this._error(err);
487
487
  }
488
488
  };
489
- Subscriber$2.prototype.complete = function() {
489
+ Subscriber$1.prototype.complete = function() {
490
490
  if (this.isStopped) handleStoppedNotification(NotificationFactories_1.COMPLETE_NOTIFICATION, this);
491
491
  else {
492
492
  this.isStopped = true;
493
493
  this._complete();
494
494
  }
495
495
  };
496
- Subscriber$2.prototype.unsubscribe = function() {
496
+ Subscriber$1.prototype.unsubscribe = function() {
497
497
  if (!this.closed) {
498
498
  this.isStopped = true;
499
499
  _super.prototype.unsubscribe.call(this);
500
500
  this.destination = null;
501
501
  }
502
502
  };
503
- Subscriber$2.prototype._next = function(value) {
503
+ Subscriber$1.prototype._next = function(value) {
504
504
  this.destination.next(value);
505
505
  };
506
- Subscriber$2.prototype._error = function(err) {
506
+ Subscriber$1.prototype._error = function(err) {
507
507
  try {
508
508
  this.destination.error(err);
509
509
  } finally {
510
510
  this.unsubscribe();
511
511
  }
512
512
  };
513
- Subscriber$2.prototype._complete = function() {
513
+ Subscriber$1.prototype._complete = function() {
514
514
  try {
515
515
  this.destination.complete();
516
516
  } finally {
517
517
  this.unsubscribe();
518
518
  }
519
519
  };
520
- return Subscriber$2;
520
+ return Subscriber$1;
521
521
  }(Subscription_1$10.Subscription);
522
- exports.Subscriber = Subscriber$1;
522
+ exports.Subscriber = Subscriber;
523
523
  var _bind = Function.prototype.bind;
524
524
  function bind(fn, thisArg) {
525
525
  return _bind.call(fn, thisArg);
@@ -583,7 +583,7 @@ var require_Subscriber = /* @__PURE__ */ __commonJSMin(((exports) => {
583
583
  return _this;
584
584
  }
585
585
  return SafeSubscriber$1;
586
- }(Subscriber$1);
586
+ }(Subscriber);
587
587
  exports.SafeSubscriber = SafeSubscriber;
588
588
  function handleUnhandledError(error) {
589
589
  if (config_1$2.config.useDeprecatedSynchronousErrorHandling) errorContext_1$2.captureError(error);
@@ -1129,9 +1129,9 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1129
1129
  var ObjectUnsubscribedError_1$1 = require_ObjectUnsubscribedError();
1130
1130
  var arrRemove_1$6 = require_arrRemove();
1131
1131
  var errorContext_1 = require_errorContext();
1132
- var Subject$2 = function(_super) {
1133
- __extends$13(Subject$3, _super);
1134
- function Subject$3() {
1132
+ var Subject$1 = function(_super) {
1133
+ __extends$13(Subject$2, _super);
1134
+ function Subject$2() {
1135
1135
  var _this = _super.call(this) || this;
1136
1136
  _this.closed = false;
1137
1137
  _this.currentObservers = null;
@@ -1141,15 +1141,15 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1141
1141
  _this.thrownError = null;
1142
1142
  return _this;
1143
1143
  }
1144
- Subject$3.prototype.lift = function(operator) {
1144
+ Subject$2.prototype.lift = function(operator) {
1145
1145
  var subject = new AnonymousSubject(this, this);
1146
1146
  subject.operator = operator;
1147
1147
  return subject;
1148
1148
  };
1149
- Subject$3.prototype._throwIfClosed = function() {
1149
+ Subject$2.prototype._throwIfClosed = function() {
1150
1150
  if (this.closed) throw new ObjectUnsubscribedError_1$1.ObjectUnsubscribedError();
1151
1151
  };
1152
- Subject$3.prototype.next = function(value) {
1152
+ Subject$2.prototype.next = function(value) {
1153
1153
  var _this = this;
1154
1154
  errorContext_1.errorContext(function() {
1155
1155
  var e_1, _a;
@@ -1170,7 +1170,7 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1170
1170
  }
1171
1171
  });
1172
1172
  };
1173
- Subject$3.prototype.error = function(err) {
1173
+ Subject$2.prototype.error = function(err) {
1174
1174
  var _this = this;
1175
1175
  errorContext_1.errorContext(function() {
1176
1176
  _this._throwIfClosed();
@@ -1182,7 +1182,7 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1182
1182
  }
1183
1183
  });
1184
1184
  };
1185
- Subject$3.prototype.complete = function() {
1185
+ Subject$2.prototype.complete = function() {
1186
1186
  var _this = this;
1187
1187
  errorContext_1.errorContext(function() {
1188
1188
  _this._throwIfClosed();
@@ -1193,11 +1193,11 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1193
1193
  }
1194
1194
  });
1195
1195
  };
1196
- Subject$3.prototype.unsubscribe = function() {
1196
+ Subject$2.prototype.unsubscribe = function() {
1197
1197
  this.isStopped = this.closed = true;
1198
1198
  this.observers = this.currentObservers = null;
1199
1199
  };
1200
- Object.defineProperty(Subject$3.prototype, "observed", {
1200
+ Object.defineProperty(Subject$2.prototype, "observed", {
1201
1201
  get: function() {
1202
1202
  var _a;
1203
1203
  return ((_a = this.observers) === null || _a === void 0 ? void 0 : _a.length) > 0;
@@ -1205,16 +1205,16 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1205
1205
  enumerable: false,
1206
1206
  configurable: true
1207
1207
  });
1208
- Subject$3.prototype._trySubscribe = function(subscriber) {
1208
+ Subject$2.prototype._trySubscribe = function(subscriber) {
1209
1209
  this._throwIfClosed();
1210
1210
  return _super.prototype._trySubscribe.call(this, subscriber);
1211
1211
  };
1212
- Subject$3.prototype._subscribe = function(subscriber) {
1212
+ Subject$2.prototype._subscribe = function(subscriber) {
1213
1213
  this._throwIfClosed();
1214
1214
  this._checkFinalizedStatuses(subscriber);
1215
1215
  return this._innerSubscribe(subscriber);
1216
1216
  };
1217
- Subject$3.prototype._innerSubscribe = function(subscriber) {
1217
+ Subject$2.prototype._innerSubscribe = function(subscriber) {
1218
1218
  var _this = this;
1219
1219
  var _a = this, hasError = _a.hasError, isStopped = _a.isStopped, observers = _a.observers;
1220
1220
  if (hasError || isStopped) return Subscription_1$6.EMPTY_SUBSCRIPTION;
@@ -1225,22 +1225,22 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1225
1225
  arrRemove_1$6.arrRemove(observers, subscriber);
1226
1226
  });
1227
1227
  };
1228
- Subject$3.prototype._checkFinalizedStatuses = function(subscriber) {
1228
+ Subject$2.prototype._checkFinalizedStatuses = function(subscriber) {
1229
1229
  var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, isStopped = _a.isStopped;
1230
1230
  if (hasError) subscriber.error(thrownError);
1231
1231
  else if (isStopped) subscriber.complete();
1232
1232
  };
1233
- Subject$3.prototype.asObservable = function() {
1233
+ Subject$2.prototype.asObservable = function() {
1234
1234
  var observable = new Observable_1$24.Observable();
1235
1235
  observable.source = this;
1236
1236
  return observable;
1237
1237
  };
1238
- Subject$3.create = function(destination, source) {
1238
+ Subject$2.create = function(destination, source) {
1239
1239
  return new AnonymousSubject(destination, source);
1240
1240
  };
1241
- return Subject$3;
1241
+ return Subject$2;
1242
1242
  }(Observable_1$24.Observable);
1243
- exports.Subject = Subject$2;
1243
+ exports.Subject = Subject$1;
1244
1244
  var AnonymousSubject = function(_super) {
1245
1245
  __extends$13(AnonymousSubject$1, _super);
1246
1246
  function AnonymousSubject$1(destination, source) {
@@ -1266,7 +1266,7 @@ var require_Subject = /* @__PURE__ */ __commonJSMin(((exports) => {
1266
1266
  return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : Subscription_1$6.EMPTY_SUBSCRIPTION;
1267
1267
  };
1268
1268
  return AnonymousSubject$1;
1269
- }(Subject$2);
1269
+ }(Subject$1);
1270
1270
  exports.AnonymousSubject = AnonymousSubject;
1271
1271
  }));
1272
1272
 
@@ -1293,37 +1293,37 @@ var require_BehaviorSubject = /* @__PURE__ */ __commonJSMin(((exports) => {
1293
1293
  })();
1294
1294
  Object.defineProperty(exports, "__esModule", { value: true });
1295
1295
  exports.BehaviorSubject = void 0;
1296
- var BehaviorSubject$2 = function(_super) {
1297
- __extends$12(BehaviorSubject$3, _super);
1298
- function BehaviorSubject$3(_value) {
1296
+ var BehaviorSubject$1 = function(_super) {
1297
+ __extends$12(BehaviorSubject$2, _super);
1298
+ function BehaviorSubject$2(_value) {
1299
1299
  var _this = _super.call(this) || this;
1300
1300
  _this._value = _value;
1301
1301
  return _this;
1302
1302
  }
1303
- Object.defineProperty(BehaviorSubject$3.prototype, "value", {
1303
+ Object.defineProperty(BehaviorSubject$2.prototype, "value", {
1304
1304
  get: function() {
1305
1305
  return this.getValue();
1306
1306
  },
1307
1307
  enumerable: false,
1308
1308
  configurable: true
1309
1309
  });
1310
- BehaviorSubject$3.prototype._subscribe = function(subscriber) {
1310
+ BehaviorSubject$2.prototype._subscribe = function(subscriber) {
1311
1311
  var subscription = _super.prototype._subscribe.call(this, subscriber);
1312
1312
  !subscription.closed && subscriber.next(this._value);
1313
1313
  return subscription;
1314
1314
  };
1315
- BehaviorSubject$3.prototype.getValue = function() {
1315
+ BehaviorSubject$2.prototype.getValue = function() {
1316
1316
  var _a = this, hasError = _a.hasError, thrownError = _a.thrownError, _value = _a._value;
1317
1317
  if (hasError) throw thrownError;
1318
1318
  this._throwIfClosed();
1319
1319
  return _value;
1320
1320
  };
1321
- BehaviorSubject$3.prototype.next = function(value) {
1321
+ BehaviorSubject$2.prototype.next = function(value) {
1322
1322
  _super.prototype.next.call(this, this._value = value);
1323
1323
  };
1324
- return BehaviorSubject$3;
1324
+ return BehaviorSubject$2;
1325
1325
  }(require_Subject().Subject);
1326
- exports.BehaviorSubject = BehaviorSubject$2;
1326
+ exports.BehaviorSubject = BehaviorSubject$1;
1327
1327
  }));
1328
1328
 
1329
1329
  //#endregion
@@ -3095,13 +3095,13 @@ var require_of = /* @__PURE__ */ __commonJSMin(((exports) => {
3095
3095
  exports.of = void 0;
3096
3096
  var args_1$12 = require_args();
3097
3097
  var from_1$7 = require_from();
3098
- function of$1() {
3098
+ function of$2() {
3099
3099
  var args = [];
3100
3100
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
3101
3101
  var scheduler = args_1$12.popScheduler(args);
3102
3102
  return from_1$7.from(args, scheduler);
3103
3103
  }
3104
- exports.of = of$1;
3104
+ exports.of = of$2;
3105
3105
  }));
3106
3106
 
3107
3107
  //#endregion
@@ -3250,7 +3250,7 @@ var require_firstValueFrom = /* @__PURE__ */ __commonJSMin(((exports) => {
3250
3250
  exports.firstValueFrom = void 0;
3251
3251
  var EmptyError_1$5 = require_EmptyError();
3252
3252
  var Subscriber_1$2 = require_Subscriber();
3253
- function firstValueFrom$6(source, config) {
3253
+ function firstValueFrom$7(source, config) {
3254
3254
  var hasConfig = typeof config === "object";
3255
3255
  return new Promise(function(resolve, reject) {
3256
3256
  var subscriber = new Subscriber_1$2.SafeSubscriber({
@@ -3267,7 +3267,7 @@ var require_firstValueFrom = /* @__PURE__ */ __commonJSMin(((exports) => {
3267
3267
  source.subscribe(subscriber);
3268
3268
  });
3269
3269
  }
3270
- exports.firstValueFrom = firstValueFrom$6;
3270
+ exports.firstValueFrom = firstValueFrom$7;
3271
3271
  }));
3272
3272
 
3273
3273
  //#endregion
@@ -3394,7 +3394,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3394
3394
  exports.map = void 0;
3395
3395
  var lift_1$64 = require_lift();
3396
3396
  var OperatorSubscriber_1$54 = require_OperatorSubscriber();
3397
- function map$18(project, thisArg) {
3397
+ function map$20(project, thisArg) {
3398
3398
  return lift_1$64.operate(function(source, subscriber) {
3399
3399
  var index = 0;
3400
3400
  source.subscribe(OperatorSubscriber_1$54.createOperatorSubscriber(subscriber, function(value) {
@@ -3402,7 +3402,7 @@ var require_map = /* @__PURE__ */ __commonJSMin(((exports) => {
3402
3402
  }));
3403
3403
  });
3404
3404
  }
3405
- exports.map = map$18;
3405
+ exports.map = map$20;
3406
3406
  }));
3407
3407
 
3408
3408
  //#endregion
@@ -4144,7 +4144,7 @@ var require_timer = /* @__PURE__ */ __commonJSMin(((exports) => {
4144
4144
  var async_1$11 = require_async();
4145
4145
  var isScheduler_1 = require_isScheduler();
4146
4146
  var isDate_1$1 = require_isDate();
4147
- function timer$2(dueTime, intervalOrScheduler, scheduler) {
4147
+ function timer$3(dueTime, intervalOrScheduler, scheduler) {
4148
4148
  if (dueTime === void 0) dueTime = 0;
4149
4149
  if (scheduler === void 0) scheduler = async_1$11.async;
4150
4150
  var intervalDuration = -1;
@@ -4163,7 +4163,7 @@ var require_timer = /* @__PURE__ */ __commonJSMin(((exports) => {
4163
4163
  }, due);
4164
4164
  });
4165
4165
  }
4166
- exports.timer = timer$2;
4166
+ exports.timer = timer$3;
4167
4167
  }));
4168
4168
 
4169
4169
  //#endregion
@@ -4173,13 +4173,13 @@ var require_interval = /* @__PURE__ */ __commonJSMin(((exports) => {
4173
4173
  exports.interval = void 0;
4174
4174
  var async_1$10 = require_async();
4175
4175
  var timer_1$6 = require_timer();
4176
- function interval$2(period, scheduler) {
4176
+ function interval$4(period, scheduler) {
4177
4177
  if (period === void 0) period = 0;
4178
4178
  if (scheduler === void 0) scheduler = async_1$10.asyncScheduler;
4179
4179
  if (period < 0) period = 0;
4180
4180
  return timer_1$6.timer(period, period, scheduler);
4181
4181
  }
4182
- exports.interval = interval$2;
4182
+ exports.interval = interval$4;
4183
4183
  }));
4184
4184
 
4185
4185
  //#endregion
@@ -4331,13 +4331,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
4331
4331
  var innerFrom_1$28 = require_innerFrom();
4332
4332
  var argsOrArgArray_1$4 = require_argsOrArgArray();
4333
4333
  var OperatorSubscriber_1$48 = require_OperatorSubscriber();
4334
- function race$4() {
4334
+ function race$5() {
4335
4335
  var sources = [];
4336
4336
  for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
4337
4337
  sources = argsOrArgArray_1$4.argsOrArgArray(sources);
4338
4338
  return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
4339
4339
  }
4340
- exports.race = race$4;
4340
+ exports.race = race$5;
4341
4341
  function raceInit(sources) {
4342
4342
  return function(subscriber) {
4343
4343
  var subscriptions = [];
@@ -5335,7 +5335,7 @@ var require_take = /* @__PURE__ */ __commonJSMin(((exports) => {
5335
5335
  var empty_1$3 = require_empty();
5336
5336
  var lift_1$46 = require_lift();
5337
5337
  var OperatorSubscriber_1$35 = require_OperatorSubscriber();
5338
- function take$8(count$1) {
5338
+ function take$9(count$1) {
5339
5339
  return count$1 <= 0 ? function() {
5340
5340
  return empty_1$3.EMPTY;
5341
5341
  } : lift_1$46.operate(function(source, subscriber) {
@@ -5348,7 +5348,7 @@ var require_take = /* @__PURE__ */ __commonJSMin(((exports) => {
5348
5348
  }));
5349
5349
  });
5350
5350
  }
5351
- exports.take = take$8;
5351
+ exports.take = take$9;
5352
5352
  }));
5353
5353
 
5354
5354
  //#endregion
@@ -5474,7 +5474,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5474
5474
  var identity_1$10 = require_identity();
5475
5475
  var lift_1$42 = require_lift();
5476
5476
  var OperatorSubscriber_1$31 = require_OperatorSubscriber();
5477
- function distinctUntilChanged$8(comparator, keySelector) {
5477
+ function distinctUntilChanged$9(comparator, keySelector) {
5478
5478
  if (keySelector === void 0) keySelector = identity_1$10.identity;
5479
5479
  comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
5480
5480
  return lift_1$42.operate(function(source, subscriber) {
@@ -5490,7 +5490,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5490
5490
  }));
5491
5491
  });
5492
5492
  }
5493
- exports.distinctUntilChanged = distinctUntilChanged$8;
5493
+ exports.distinctUntilChanged = distinctUntilChanged$9;
5494
5494
  function defaultCompare(a, b) {
5495
5495
  return a === b;
5496
5496
  }
@@ -7186,17 +7186,17 @@ var require_timeInterval = /* @__PURE__ */ __commonJSMin(((exports) => {
7186
7186
  var last$2 = scheduler.now();
7187
7187
  source.subscribe(OperatorSubscriber_1$6.createOperatorSubscriber(subscriber, function(value) {
7188
7188
  var now = scheduler.now();
7189
- var interval$3 = now - last$2;
7189
+ var interval$5 = now - last$2;
7190
7190
  last$2 = now;
7191
- subscriber.next(new TimeInterval(value, interval$3));
7191
+ subscriber.next(new TimeInterval(value, interval$5));
7192
7192
  }));
7193
7193
  });
7194
7194
  }
7195
7195
  exports.timeInterval = timeInterval;
7196
7196
  var TimeInterval = function() {
7197
- function TimeInterval$1(value, interval$3) {
7197
+ function TimeInterval$1(value, interval$5) {
7198
7198
  this.value = value;
7199
- this.interval = interval$3;
7199
+ this.interval = interval$5;
7200
7200
  }
7201
7201
  return TimeInterval$1;
7202
7202
  }();
@@ -8937,12 +8937,12 @@ var require_cjs = /* @__PURE__ */ __commonJSMin(((exports) => {
8937
8937
 
8938
8938
  //#endregion
8939
8939
  //#region src/behaviors/Destroyable.ts
8940
- var import_cjs$29 = require_cjs();
8940
+ var import_cjs$30 = require_cjs();
8941
8941
  var Destroyable = class {
8942
8942
  constructor() {
8943
8943
  this.subscriptions = [];
8944
8944
  this.subjects = [];
8945
- this._destroyed$ = new import_cjs$29.Subject();
8945
+ this._destroyed$ = new import_cjs$30.Subject();
8946
8946
  }
8947
8947
  destroy() {
8948
8948
  this._observableCache?.clear();
@@ -8978,7 +8978,7 @@ var Destroyable = class {
8978
8978
  this._observableCache ??= /* @__PURE__ */ new Map();
8979
8979
  let cached = this._observableCache.get(publicKey);
8980
8980
  if (!cached) {
8981
- cached = factory().pipe((0, import_cjs$29.observeOn)(import_cjs$29.asapScheduler));
8981
+ cached = factory().pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8982
8982
  this._observableCache.set(publicKey, cached);
8983
8983
  }
8984
8984
  return cached;
@@ -8992,24 +8992,24 @@ var Destroyable = class {
8992
8992
  * Do NOT use for observables consumed internally by the SDK.
8993
8993
  */
8994
8994
  deferEmission(observable) {
8995
- return observable.pipe((0, import_cjs$29.observeOn)(import_cjs$29.asapScheduler));
8995
+ return observable.pipe((0, import_cjs$30.observeOn)(import_cjs$30.asapScheduler));
8996
8996
  }
8997
8997
  subscribeTo(observable, observerOrNext) {
8998
8998
  const subscription = observable.subscribe(observerOrNext);
8999
8999
  this.subscriptions.push(subscription);
9000
9000
  }
9001
9001
  createSubject() {
9002
- const subject = new import_cjs$29.Subject();
9002
+ const subject = new import_cjs$30.Subject();
9003
9003
  this.subjects.push(subject);
9004
9004
  return subject;
9005
9005
  }
9006
9006
  createReplaySubject(bufferSize, windowTime$1) {
9007
- const subject = new import_cjs$29.ReplaySubject(bufferSize, windowTime$1);
9007
+ const subject = new import_cjs$30.ReplaySubject(bufferSize, windowTime$1);
9008
9008
  this.subjects.push(subject);
9009
9009
  return subject;
9010
9010
  }
9011
9011
  createBehaviorSubject(initialValue) {
9012
- const subject = new import_cjs$29.BehaviorSubject(initialValue);
9012
+ const subject = new import_cjs$30.BehaviorSubject(initialValue);
9013
9013
  this.subjects.push(subject);
9014
9014
  return subject;
9015
9015
  }
@@ -9470,9 +9470,9 @@ var require_loglevel = /* @__PURE__ */ __commonJSMin(((exports, module) => {
9470
9470
  defaultLogger$1 = new Logger();
9471
9471
  defaultLogger$1.getLogger = function getLogger$1(name) {
9472
9472
  if (typeof name !== "symbol" && typeof name !== "string" || name === "") throw new TypeError("You must supply a name when creating a logger.");
9473
- var logger$30 = _loggersByName[name];
9474
- if (!logger$30) logger$30 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9475
- return logger$30;
9473
+ var logger$32 = _loggersByName[name];
9474
+ if (!logger$32) logger$32 = _loggersByName[name] = new Logger(name, defaultLogger$1.methodFactory);
9475
+ return logger$32;
9476
9476
  };
9477
9477
  var _log = typeof window !== undefinedType ? window.log : void 0;
9478
9478
  defaultLogger$1.noConflict = function() {
@@ -9508,8 +9508,8 @@ const defaultLoggerLevel = defaultLogger.levels.WARN;
9508
9508
  defaultLogger.setLevel(defaultLoggerLevel);
9509
9509
  let userLogger = null;
9510
9510
  /** Replace the built-in logger with a custom implementation. Pass `null` to restore defaults. */
9511
- const setLogger = (logger$30) => {
9512
- userLogger = logger$30;
9511
+ const setLogger = (logger$32) => {
9512
+ userLogger = logger$32;
9513
9513
  };
9514
9514
  let debugOptions = {};
9515
9515
  /** Configure debug options (e.g., `{ logWsTraffic: true }`). */
@@ -9548,13 +9548,13 @@ const wsTraffic = (options) => {
9548
9548
  loggerInstance.debug(`[WebSocket] ${options.type.toUpperCase()}: non-JSON message`);
9549
9549
  return;
9550
9550
  }
9551
- else payload = options.payload;
9551
+ else ({payload} = options);
9552
9552
  const msg = shouldStringify(payload) ? JSON.stringify(payload, null, 2) : payload;
9553
9553
  loggerInstance.debug(`${options.type.toUpperCase()}: \n`, msg, "\n");
9554
9554
  };
9555
9555
  const getLogger = () => {
9556
- const logger$30 = getLoggerInstance();
9557
- return new Proxy(logger$30, { get(_target, prop, _receiver) {
9556
+ const logger$32 = getLoggerInstance();
9557
+ return new Proxy(logger$32, { get(_target, prop, _receiver) {
9558
9558
  if (prop === "wsTraffic") return wsTraffic;
9559
9559
  const instance = getLoggerInstance();
9560
9560
  const value = Reflect.get(instance, prop);
@@ -9606,8 +9606,7 @@ const asyncRetry = async ({ asyncCallable, maxRetries: retries = DEFAULT_MAX_RET
9606
9606
 
9607
9607
  //#endregion
9608
9608
  //#region src/controllers/HTTPRequestController.ts
9609
- var import_cjs$28 = require_cjs();
9610
- const logger$29 = getLogger();
9609
+ const logger$31 = getLogger();
9611
9610
  const GET_PARAMS = {
9612
9611
  method: "GET",
9613
9612
  headers: { Accept: "application/json" }
@@ -9619,7 +9618,7 @@ const POST_PARAMS = {
9619
9618
  "Content-Type": "application/json"
9620
9619
  }
9621
9620
  };
9622
- var HTTPRequestController = class HTTPRequestController {
9621
+ var HTTPRequestController = class HTTPRequestController extends Destroyable {
9623
9622
  static {
9624
9623
  this.defaultMaxRetries = 3;
9625
9624
  }
@@ -9640,11 +9639,12 @@ var HTTPRequestController = class HTTPRequestController {
9640
9639
  ]);
9641
9640
  }
9642
9641
  constructor(baseURL, getCredential, options = {}) {
9642
+ super();
9643
9643
  this.baseURL = baseURL;
9644
9644
  this.getCredential = getCredential;
9645
- this._responses$ = new import_cjs$28.Subject();
9646
- this._errors$ = new import_cjs$28.Subject();
9647
- this._status$ = new import_cjs$28.BehaviorSubject("idle");
9645
+ this._responses$ = this.createSubject();
9646
+ this._errors$ = this.createSubject();
9647
+ this._status$ = this.createBehaviorSubject("idle");
9648
9648
  this.maxRetries = options.maxRetries ?? HTTPRequestController.defaultMaxRetries;
9649
9649
  this.retryDelayMin = options.retryDelayMin ?? HTTPRequestController.defaultRetryDelayMinMs;
9650
9650
  this.retryDelayMax = options.retryDelayMax ?? HTTPRequestController.defaultRetryDelayMaxMs;
@@ -9670,7 +9670,7 @@ var HTTPRequestController = class HTTPRequestController {
9670
9670
  this._responses$.next(response);
9671
9671
  return response;
9672
9672
  } catch (error) {
9673
- logger$29.error("[HTTPRequestController] Request error:", error);
9673
+ logger$31.error("[HTTPRequestController] Request error:", error);
9674
9674
  this._status$.next("error");
9675
9675
  const err = error instanceof Error ? error : new Error("HTTP request failed", { cause: error });
9676
9676
  this._errors$.next(err);
@@ -9697,7 +9697,7 @@ var HTTPRequestController = class HTTPRequestController {
9697
9697
  const url = this.buildURL(request.url);
9698
9698
  const headers = this.buildHeaders(request.headers);
9699
9699
  const timeout$5 = request.timeout ?? this.requestTimeout;
9700
- logger$29.debug("[HTTPRequestController] Executing request:", {
9700
+ logger$31.debug("[HTTPRequestController] Executing request:", {
9701
9701
  method: request.method,
9702
9702
  url,
9703
9703
  headers: Object.keys(headers).reduce((acc, key) => {
@@ -9717,7 +9717,7 @@ var HTTPRequestController = class HTTPRequestController {
9717
9717
  });
9718
9718
  clearTimeout(timeoutId);
9719
9719
  const httpResponse = await this.convertResponse(response);
9720
- logger$29.debug("[HTTPRequestController] Response received:", {
9720
+ logger$31.debug("[HTTPRequestController] Response received:", {
9721
9721
  status: response.status,
9722
9722
  statusText: response.statusText,
9723
9723
  headers: [...response.headers.entries()],
@@ -9727,7 +9727,7 @@ var HTTPRequestController = class HTTPRequestController {
9727
9727
  } catch (error) {
9728
9728
  clearTimeout(timeoutId);
9729
9729
  if (error instanceof Error && error.name === "AbortError") throw new RequestTimeoutError(`Request timeout after ${timeout$5}ms`, { cause: error });
9730
- logger$29.error("[HTTPRequestController] Request failed:", error);
9730
+ logger$31.error("[HTTPRequestController] Request failed:", error);
9731
9731
  throw error;
9732
9732
  }
9733
9733
  }
@@ -9741,8 +9741,8 @@ var HTTPRequestController = class HTTPRequestController {
9741
9741
  const credential = this.getCredential();
9742
9742
  if (credential.token) {
9743
9743
  headers.Authorization = `Bearer ${credential.token}`;
9744
- logger$29.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9745
- } else logger$29.warn("[HTTPRequestController] No credentials available for authentication");
9744
+ logger$31.debug("[HTTPRequestController] Using Bearer token auth, token length:", credential.token.length);
9745
+ } else logger$31.warn("[HTTPRequestController] No credentials available for authentication");
9746
9746
  return headers;
9747
9747
  }
9748
9748
  /**
@@ -9954,6 +9954,18 @@ const DEFAULT_ICE_DISCONNECTED_GRACE_PERIOD_MS = 3e3;
9954
9954
  const DEFAULT_ICE_RESTART_TIMEOUT_MS$1 = 5e3;
9955
9955
  /** Maximum recovery attempts before emitting 'max_attempts_reached'. */
9956
9956
  const DEFAULT_MAX_RECOVERY_ATTEMPTS = 3;
9957
+ /** Upper bound in ms for waiting on iceGatheringState === 'complete' after an ICE restart. */
9958
+ const ICE_GATHERING_COMPLETE_TIMEOUT_MS = 1e4;
9959
+ /** Upper bound in ms for waiting on RTCPeerConnection.connectionState === 'connected' after a recovery ICE restart. */
9960
+ const PEER_CONNECTION_RECOVERY_WAIT_MS = 5e3;
9961
+ /** Polling interval in ms while waiting for RTCPeerConnection.connectionState to transition. */
9962
+ const PEER_CONNECTION_RECOVERY_POLL_MS = 100;
9963
+ /** Polling interval for LocalAudioPipeline.level$ (ms). ~30fps is smooth for meters. */
9964
+ const AUDIO_LEVEL_POLL_INTERVAL_MS = 33;
9965
+ /** RMS level threshold (0..1) above which the local participant is considered speaking. */
9966
+ const VAD_THRESHOLD = .03;
9967
+ /** Hold window in ms below the threshold before speaking$ flips back to false. */
9968
+ const VAD_HOLD_MS = 250;
9957
9969
  /** Whether to persist device selections to storage by default. */
9958
9970
  const DEFAULT_PERSIST_DEVICE_SELECTION = true;
9959
9971
  /** Whether to auto-apply device changes to active calls by default. */
@@ -10002,7 +10014,7 @@ function fromMsToSec(milliseconds) {
10002
10014
 
10003
10015
  //#endregion
10004
10016
  //#region src/containers/PreferencesContainer.ts
10005
- const logger$28 = getLogger();
10017
+ const logger$30 = getLogger();
10006
10018
  var PreferencesContainer = class PreferencesContainer {
10007
10019
  static get instance() {
10008
10020
  this._instance ??= new PreferencesContainer();
@@ -10664,7 +10676,7 @@ var ClientPreferences = class {
10664
10676
  if (!this._storage) return;
10665
10677
  const data = collectStoredPreferences();
10666
10678
  this._storage.setItem(PREFERENCES_STORAGE_KEY, data, "local").catch((error) => {
10667
- logger$28.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10679
+ logger$30.error(`[ClientPreferences] Failed to save preferences: ${String(error)}`);
10668
10680
  });
10669
10681
  }
10670
10682
  /** Loads preferences from storage and applies them to the container. */
@@ -10673,7 +10685,7 @@ var ClientPreferences = class {
10673
10685
  this._storage.getItem(PREFERENCES_STORAGE_KEY, "local").then((stored) => {
10674
10686
  if (stored) applyStoredPreferences(stored);
10675
10687
  }).catch((error) => {
10676
- logger$28.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10688
+ logger$30.error(`[ClientPreferences] Failed to load preferences: ${String(error)}`);
10677
10689
  });
10678
10690
  }
10679
10691
  };
@@ -10694,8 +10706,8 @@ function toError(value) {
10694
10706
 
10695
10707
  //#endregion
10696
10708
  //#region src/controllers/NavigatorDeviceController.ts
10697
- var import_cjs$27 = require_cjs();
10698
- const logger$27 = getLogger();
10709
+ var import_cjs$29 = require_cjs();
10710
+ const logger$29 = getLogger();
10699
10711
  /** Maps a device kind to its storage key. */
10700
10712
  const DEVICE_STORAGE_KEYS = {
10701
10713
  audioinput: DEVICE_STORAGE_KEY_AUDIO_INPUT,
@@ -10717,7 +10729,7 @@ var NavigatorDeviceController = class extends Destroyable {
10717
10729
  super();
10718
10730
  this.webRTCApiProvider = webRTCApiProvider;
10719
10731
  this.deviceChangeHandler = () => {
10720
- logger$27.debug("[DeviceController] Device change detected");
10732
+ logger$29.debug("[DeviceController] Device change detected");
10721
10733
  this.enumerateDevices();
10722
10734
  };
10723
10735
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
@@ -10754,17 +10766,17 @@ var NavigatorDeviceController = class extends Destroyable {
10754
10766
  return {};
10755
10767
  }
10756
10768
  get errors$() {
10757
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$)));
10769
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$)));
10758
10770
  }
10759
10771
  /** Observable that emits when the SDK auto-switches a device. */
10760
10772
  get deviceRecovered$() {
10761
- return this._deviceRecovered$.asObservable().pipe((0, import_cjs$27.takeUntil)(this.destroyed$));
10773
+ return this._deviceRecovered$.asObservable().pipe((0, import_cjs$29.takeUntil)(this.destroyed$));
10762
10774
  }
10763
10775
  get videoInputDisabled$() {
10764
- return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10776
+ return this.cachedObservable("videoInputDisabled$", () => this._videoInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10765
10777
  }
10766
10778
  get audioInputDisabled$() {
10767
- return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$27.distinctUntilChanged)(), (0, import_cjs$27.takeUntil)(this.destroyed$)));
10779
+ return this.cachedObservable("audioInputDisabled$", () => this._audioInputDisabled$.asObservable().pipe((0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$)));
10768
10780
  }
10769
10781
  get videoInputDisabled() {
10770
10782
  return this._videoInputDisabled$.value;
@@ -10773,22 +10785,22 @@ var NavigatorDeviceController = class extends Destroyable {
10773
10785
  return this._audioInputDisabled$.value;
10774
10786
  }
10775
10787
  get audioInputDevices$() {
10776
- 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$)));
10788
+ 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$)));
10777
10789
  }
10778
10790
  get audioOutputDevices$() {
10779
- 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$)));
10791
+ 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$)));
10780
10792
  }
10781
10793
  get videoInputDevices$() {
10782
- 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$)));
10794
+ 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$)));
10783
10795
  }
10784
10796
  get selectedAudioInputDevice$() {
10785
- 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))));
10797
+ return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audioinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected audio input device changed:", info))));
10786
10798
  }
10787
10799
  get selectedAudioOutputDevice$() {
10788
- 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))));
10800
+ return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.audiooutput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected audio output device changed:", info))));
10789
10801
  }
10790
10802
  get selectedVideoInputDevice$() {
10791
- 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))));
10803
+ return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$29.map)((state) => state.videoinput), (0, import_cjs$29.distinctUntilChanged)(), (0, import_cjs$29.takeUntil)(this.destroyed$), (0, import_cjs$29.tap)((info) => logger$29.debug("[DeviceController] Selected video input device changed:", info))));
10792
10804
  }
10793
10805
  get selectedAudioInputDevice() {
10794
10806
  if (this._audioInputDisabled$.value) return null;
@@ -10863,7 +10875,7 @@ var NavigatorDeviceController = class extends Destroyable {
10863
10875
  if (device) this.persistDeviceSelection("audioinput", device);
10864
10876
  }
10865
10877
  selectVideoInputDevice(device) {
10866
- logger$27.debug("[DeviceController] Setting selected video input device:", device);
10878
+ logger$29.debug("[DeviceController] Setting selected video input device:", device);
10867
10879
  if (this._videoInputDisabled$.value && device) this._videoInputDisabled$.next(false);
10868
10880
  const previous = this._selectedDevicesState$.value.videoinput;
10869
10881
  if (previous && previous.deviceId !== device?.deviceId) this._deviceHistory.push("videoinput", previous);
@@ -10884,7 +10896,7 @@ var NavigatorDeviceController = class extends Destroyable {
10884
10896
  }
10885
10897
  init() {
10886
10898
  this.loadPersistedDevices();
10887
- this.subscribeTo(this._devicesState$.pipe((0, import_cjs$27.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10899
+ this.subscribeTo(this._devicesState$.pipe((0, import_cjs$29.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10888
10900
  const currentSelected = this._selectedDevicesState$.value;
10889
10901
  const newAudioInput = this._audioInputDisabled$.value ? null : this.resolveDevice("audioinput", devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10890
10902
  const newAudioOutput = this.resolveDevice("audiooutput", devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
@@ -10920,7 +10932,7 @@ var NavigatorDeviceController = class extends Destroyable {
10920
10932
  }
10921
10933
  const fromHistory = this._deviceHistory.findInHistory(kind, devices);
10922
10934
  if (fromHistory) {
10923
- logger$27.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10935
+ logger$29.debug(`[DeviceController] Device disappeared, falling back to history: ${fromHistory.label}`);
10924
10936
  this.emitDeviceRecovered(kind, selected, fromHistory, "device_disconnected");
10925
10937
  return fromHistory;
10926
10938
  }
@@ -10973,7 +10985,7 @@ var NavigatorDeviceController = class extends Destroyable {
10973
10985
  try {
10974
10986
  await this._storageManager.setItem(DEVICE_STORAGE_KEYS[kind], stored, "local");
10975
10987
  } catch (error) {
10976
- logger$27.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10988
+ logger$29.error(`[DeviceController] Failed to persist device selection for ${kind}:`, error);
10977
10989
  }
10978
10990
  }
10979
10991
  async loadPersistedDevices() {
@@ -10989,7 +11001,7 @@ var NavigatorDeviceController = class extends Destroyable {
10989
11001
  [kind]: stored
10990
11002
  };
10991
11003
  } catch (error) {
10992
- logger$27.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
11004
+ logger$29.error(`[DeviceController] Failed to load persisted device for ${kind}:`, error);
10993
11005
  }
10994
11006
  }
10995
11007
  /** Clears device history, persisted selections, and re-enumerates devices. */
@@ -11006,8 +11018,8 @@ var NavigatorDeviceController = class extends Destroyable {
11006
11018
  enableDeviceMonitoring() {
11007
11019
  this.disableDeviceMonitoring();
11008
11020
  this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
11009
- if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$27.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11010
- logger$27.debug("[DeviceController] Polling devices due to interval");
11021
+ if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$29.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
11022
+ logger$29.debug("[DeviceController] Polling devices due to interval");
11011
11023
  this.enumerateDevices();
11012
11024
  });
11013
11025
  this.enumerateDevices();
@@ -11033,13 +11045,13 @@ var NavigatorDeviceController = class extends Destroyable {
11033
11045
  videoinput: []
11034
11046
  });
11035
11047
  this._devicesState$.next(devicesByKind);
11036
- logger$27.debug("[DeviceController] Devices enumerated:", {
11048
+ logger$29.debug("[DeviceController] Devices enumerated:", {
11037
11049
  audioInputs: devicesByKind.audioinput.length,
11038
11050
  audioOutputs: devicesByKind.audiooutput.length,
11039
11051
  videoInputs: devicesByKind.videoinput.length
11040
11052
  });
11041
11053
  } catch (error) {
11042
- logger$27.error("[DeviceController] Failed to enumerate devices:", error);
11054
+ logger$29.error("[DeviceController] Failed to enumerate devices:", error);
11043
11055
  this._errors$.next(toError(error));
11044
11056
  }
11045
11057
  }
@@ -11055,7 +11067,7 @@ var NavigatorDeviceController = class extends Destroyable {
11055
11067
  stream.getTracks().forEach((t) => t.stop());
11056
11068
  return capabilities;
11057
11069
  } catch (error) {
11058
- logger$27.error("[DeviceController] Failed to get device capabilities:", error);
11070
+ logger$29.error("[DeviceController] Failed to get device capabilities:", error);
11059
11071
  this._errors$.next(toError(error));
11060
11072
  throw error;
11061
11073
  }
@@ -11207,15 +11219,15 @@ var DependencyContainer = class {
11207
11219
  this._baseURL = this.apiHost;
11208
11220
  this._credential = {};
11209
11221
  }
11210
- get subscriberId() {
11211
- return this.subscriber.id;
11222
+ get userId() {
11223
+ return this.user.id;
11212
11224
  }
11213
- get subscriber() {
11214
- if (!this._subscriber) throw new DependencyError("Subscriber");
11215
- return this._subscriber;
11225
+ get user() {
11226
+ if (!this._user) throw new DependencyError("User");
11227
+ return this._user;
11216
11228
  }
11217
- set subscriber(subscriber) {
11218
- this._subscriber = subscriber;
11229
+ set user(user) {
11230
+ this._user = user;
11219
11231
  }
11220
11232
  get storage() {
11221
11233
  if (!this._storageManager) {
@@ -11261,16 +11273,16 @@ var DependencyContainer = class {
11261
11273
  this._deviceController = void 0;
11262
11274
  }
11263
11275
  get authorizationStateKey() {
11264
- return `sw:${this.subscriberId}:as`;
11276
+ return `sw:${this.userId}:as`;
11265
11277
  }
11266
11278
  get protocolKey() {
11267
- return `sw:${this.subscriberId}:pt`;
11279
+ return `sw:${this.userId}:pt`;
11268
11280
  }
11269
11281
  get attachedCallsKey() {
11270
- return `sw:${this.subscriberId}:att`;
11282
+ return `sw:${this.userId}:att`;
11271
11283
  }
11272
- getSubscriberFromAddressId() {
11273
- return this.subscriber.addresses[0]?.id ?? "";
11284
+ getUserFromAddressId() {
11285
+ return this.user.addresses[0]?.id ?? "";
11274
11286
  }
11275
11287
  set baseURL(baseURL) {
11276
11288
  this._baseURL = baseURL;
@@ -11306,7 +11318,7 @@ var DependencyContainer = class {
11306
11318
 
11307
11319
  //#endregion
11308
11320
  //#region src/controllers/CryptoController.ts
11309
- const logger$26 = getLogger();
11321
+ const logger$28 = getLogger();
11310
11322
  const DPOP_DB_NAME = "sw-dpop";
11311
11323
  const DPOP_DB_VERSION = 1;
11312
11324
  const DPOP_STORE_NAME = "keys";
@@ -11365,7 +11377,7 @@ async function loadKeyPairFromDB() {
11365
11377
  tx.oncomplete = () => db.close();
11366
11378
  });
11367
11379
  } catch (error) {
11368
- logger$26.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11380
+ logger$28.warn("[DPoP] Failed to load key pair from IndexedDB:", error);
11369
11381
  return null;
11370
11382
  }
11371
11383
  }
@@ -11385,7 +11397,7 @@ async function saveKeyPairToDB(keyPair) {
11385
11397
  };
11386
11398
  });
11387
11399
  } catch (error) {
11388
- logger$26.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11400
+ logger$28.warn("[DPoP] Failed to save key pair to IndexedDB:", error);
11389
11401
  }
11390
11402
  }
11391
11403
  async function deleteKeyPairFromDB() {
@@ -11404,7 +11416,7 @@ async function deleteKeyPairFromDB() {
11404
11416
  };
11405
11417
  });
11406
11418
  } catch (error) {
11407
- logger$26.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11419
+ logger$28.warn("[DPoP] Failed to delete key pair from IndexedDB:", error);
11408
11420
  }
11409
11421
  }
11410
11422
  /**
@@ -11464,13 +11476,13 @@ var CryptoController = class {
11464
11476
  this._publicJwk = await crypto.subtle.exportKey("jwk", stored.publicKey);
11465
11477
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11466
11478
  this._initialized = true;
11467
- logger$26.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11479
+ logger$28.debug("[DPoP] Key pair restored from IndexedDB, fingerprint:", this._fingerprint);
11468
11480
  return this._fingerprint;
11469
11481
  } catch (error) {
11470
- logger$26.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11482
+ logger$28.warn("[DPoP] Stored key pair unusable, generating new one:", error);
11471
11483
  await deleteKeyPairFromDB();
11472
11484
  }
11473
- logger$26.debug("[DPoP] Generating RSA key pair");
11485
+ logger$28.debug("[DPoP] Generating RSA key pair");
11474
11486
  this._keyPair = await crypto.subtle.generateKey({
11475
11487
  name: "RSASSA-PKCS1-v1_5",
11476
11488
  modulusLength: 2048,
@@ -11485,7 +11497,7 @@ var CryptoController = class {
11485
11497
  this._fingerprint = await computeJwkThumbprint(this._publicJwk);
11486
11498
  this._initialized = true;
11487
11499
  await saveKeyPairToDB(this._keyPair);
11488
- logger$26.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11500
+ logger$28.debug("[DPoP] Key pair generated and persisted, fingerprint:", this._fingerprint);
11489
11501
  return this._fingerprint;
11490
11502
  }
11491
11503
  /**
@@ -11551,7 +11563,7 @@ var CryptoController = class {
11551
11563
  this._fingerprint = null;
11552
11564
  this._initialized = false;
11553
11565
  deleteKeyPairFromDB();
11554
- logger$26.debug("[DPoP] Controller destroyed");
11566
+ logger$28.debug("[DPoP] Controller destroyed");
11555
11567
  }
11556
11568
  get publicJwk() {
11557
11569
  if (!this._publicJwk) throw new DPoPInitError("CryptoController not initialized. Call init() first.");
@@ -11574,8 +11586,8 @@ var CryptoController = class {
11574
11586
 
11575
11587
  //#endregion
11576
11588
  //#region src/controllers/NetworkMonitor.ts
11577
- var import_cjs$26 = require_cjs();
11578
- const logger$25 = getLogger();
11589
+ var import_cjs$28 = require_cjs();
11590
+ const logger$27 = getLogger();
11579
11591
  /**
11580
11592
  * Safely check whether we are running in a browser environment
11581
11593
  * with `window` and the relevant event targets.
@@ -11618,13 +11630,13 @@ var NetworkMonitor = class extends Destroyable {
11618
11630
  this.attachListeners();
11619
11631
  }
11620
11632
  get isOnline$() {
11621
- return this._isOnline$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11633
+ return this._isOnline$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11622
11634
  }
11623
11635
  get isOnline() {
11624
11636
  return this._isOnline$.value;
11625
11637
  }
11626
11638
  get networkChange$() {
11627
- return this._networkChange$.asObservable().pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
11639
+ return this._networkChange$.asObservable().pipe((0, import_cjs$28.takeUntil)(this._destroyed$));
11628
11640
  }
11629
11641
  destroy() {
11630
11642
  this.removeListeners();
@@ -11632,7 +11644,7 @@ var NetworkMonitor = class extends Destroyable {
11632
11644
  }
11633
11645
  attachListeners() {
11634
11646
  if (!hasBrowserNetworkEvents()) {
11635
- logger$25.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11647
+ logger$27.debug("NetworkMonitor: no browser environment detected, skipping event listeners");
11636
11648
  return;
11637
11649
  }
11638
11650
  window.addEventListener("online", this._onOnline);
@@ -11640,7 +11652,7 @@ var NetworkMonitor = class extends Destroyable {
11640
11652
  const connection = getNetworkConnection();
11641
11653
  if (connection) connection.addEventListener("change", this._onConnectionChange);
11642
11654
  this._listenersAttached = true;
11643
- logger$25.debug("NetworkMonitor: event listeners attached");
11655
+ logger$27.debug("NetworkMonitor: event listeners attached");
11644
11656
  }
11645
11657
  removeListeners() {
11646
11658
  if (!this._listenersAttached) return;
@@ -11651,10 +11663,10 @@ var NetworkMonitor = class extends Destroyable {
11651
11663
  if (connection) connection.removeEventListener("change", this._onConnectionChange);
11652
11664
  }
11653
11665
  this._listenersAttached = false;
11654
- logger$25.debug("NetworkMonitor: event listeners removed");
11666
+ logger$27.debug("NetworkMonitor: event listeners removed");
11655
11667
  }
11656
11668
  handleOnline() {
11657
- logger$25.info("NetworkMonitor: browser went online");
11669
+ logger$27.info("NetworkMonitor: browser went online");
11658
11670
  this._isOnline$.next(true);
11659
11671
  this._networkChange$.next({
11660
11672
  type: "online",
@@ -11663,7 +11675,7 @@ var NetworkMonitor = class extends Destroyable {
11663
11675
  });
11664
11676
  }
11665
11677
  handleOffline() {
11666
- logger$25.info("NetworkMonitor: browser went offline");
11678
+ logger$27.info("NetworkMonitor: browser went offline");
11667
11679
  this._isOnline$.next(false);
11668
11680
  this._networkChange$.next({
11669
11681
  type: "offline",
@@ -11672,7 +11684,7 @@ var NetworkMonitor = class extends Destroyable {
11672
11684
  }
11673
11685
  handleConnectionChange() {
11674
11686
  const networkType = getNetworkType();
11675
- logger$25.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11687
+ logger$27.info(`NetworkMonitor: connection changed — effectiveType=${networkType ?? "unknown"}`);
11676
11688
  this._networkChange$.next({
11677
11689
  type: "connection_change",
11678
11690
  timestamp: Date.now(),
@@ -11787,8 +11799,8 @@ function getNavigatorMediaDevices() {
11787
11799
 
11788
11800
  //#endregion
11789
11801
  //#region src/controllers/PreflightRunner.ts
11790
- var import_cjs$25 = require_cjs();
11791
- const logger$24 = getLogger();
11802
+ var import_cjs$27 = require_cjs();
11803
+ const logger$26 = getLogger();
11792
11804
  const DEFAULT_MEDIA_TEST_DURATION_S = 10;
11793
11805
  const ICE_GATHERING_TIMEOUT_MS = 1e4;
11794
11806
  const SIGNALING_RTT_TIMEOUT_MS = 5e3;
@@ -11837,7 +11849,7 @@ var PreflightRunner = class extends Destroyable {
11837
11849
  if (!this._options.skipMediaTest) try {
11838
11850
  bandwidth = await this.testMediaBandwidth(destination);
11839
11851
  } catch (error) {
11840
- logger$24.warn("[PreflightRunner] Media bandwidth test failed:", error);
11852
+ logger$26.warn("[PreflightRunner] Media bandwidth test failed:", error);
11841
11853
  warnings.push("Media bandwidth test failed");
11842
11854
  }
11843
11855
  return {
@@ -11849,7 +11861,7 @@ var PreflightRunner = class extends Destroyable {
11849
11861
  warnings
11850
11862
  };
11851
11863
  } catch (error) {
11852
- logger$24.error("[PreflightRunner] Preflight test failed:", error);
11864
+ logger$26.error("[PreflightRunner] Preflight test failed:", error);
11853
11865
  throw new PreflightError("preflight", error instanceof Error ? error : new Error(String(error)));
11854
11866
  } finally {
11855
11867
  this.destroy();
@@ -11880,7 +11892,7 @@ var PreflightRunner = class extends Destroyable {
11880
11892
  if (track.kind === "video" && track.readyState === "live") videoWorking = true;
11881
11893
  }
11882
11894
  } catch (error) {
11883
- logger$24.warn("[PreflightRunner] Device test failed:", error);
11895
+ logger$26.warn("[PreflightRunner] Device test failed:", error);
11884
11896
  } finally {
11885
11897
  if (audioStream) audioStream.getTracks().forEach((t) => t.stop());
11886
11898
  }
@@ -11907,7 +11919,7 @@ var PreflightRunner = class extends Destroyable {
11907
11919
  const candidateTypes = /* @__PURE__ */ new Set();
11908
11920
  const startTime = Date.now();
11909
11921
  const gatheringComplete = new Promise((resolve) => {
11910
- const timer$3 = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);
11922
+ const timer$4 = setTimeout(resolve, ICE_GATHERING_TIMEOUT_MS);
11911
11923
  peerConnection.onicecandidate = (event) => {
11912
11924
  if (event.candidate) {
11913
11925
  const candidateStr = event.candidate.candidate;
@@ -11915,7 +11927,7 @@ var PreflightRunner = class extends Destroyable {
11915
11927
  if (candidateStr.includes("typ srflx")) candidateTypes.add("srflx");
11916
11928
  if (candidateStr.includes("typ relay")) candidateTypes.add("relay");
11917
11929
  } else {
11918
- clearTimeout(timer$3);
11930
+ clearTimeout(timer$4);
11919
11931
  resolve();
11920
11932
  }
11921
11933
  };
@@ -11938,7 +11950,7 @@ var PreflightRunner = class extends Destroyable {
11938
11950
  rttMs
11939
11951
  };
11940
11952
  } catch (error) {
11941
- logger$24.warn("[PreflightRunner] ICE connectivity test failed:", error);
11953
+ logger$26.warn("[PreflightRunner] ICE connectivity test failed:", error);
11942
11954
  return {
11943
11955
  type: "failed",
11944
11956
  turnReachable: false,
@@ -11956,7 +11968,7 @@ var PreflightRunner = class extends Destroyable {
11956
11968
  audio: true,
11957
11969
  video: false
11958
11970
  });
11959
- 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)));
11971
+ 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)));
11960
11972
  const durationMs = this._options.duration * 1e3;
11961
11973
  await new Promise((resolve) => setTimeout(resolve, durationMs));
11962
11974
  const metrics = call.networkMetrics;
@@ -11985,8 +11997,8 @@ var PreflightRunner = class extends Destroyable {
11985
11997
 
11986
11998
  //#endregion
11987
11999
  //#region src/controllers/VisibilityController.ts
11988
- var import_cjs$24 = require_cjs();
11989
- const logger$23 = getLogger();
12000
+ var import_cjs$26 = require_cjs();
12001
+ const logger$25 = getLogger();
11990
12002
  /**
11991
12003
  * Checks whether the document visibility API is available.
11992
12004
  */
@@ -12023,15 +12035,15 @@ var VisibilityController = class extends Destroyable {
12023
12035
  this._boundHandler = this._handleVisibilityChange.bind(this);
12024
12036
  if (this._hasVisibilityApi) {
12025
12037
  document.addEventListener("visibilitychange", this._boundHandler);
12026
- logger$23.debug("VisibilityController: listening for visibilitychange events");
12027
- } else logger$23.debug("VisibilityController: document visibility API not available, defaulting to visible");
12038
+ logger$25.debug("VisibilityController: listening for visibilitychange events");
12039
+ } else logger$25.debug("VisibilityController: document visibility API not available, defaulting to visible");
12028
12040
  }
12029
12041
  /**
12030
12042
  * Observable of the current visibility state.
12031
12043
  * Emits 'visible' or 'hidden'. Always starts with the current state.
12032
12044
  */
12033
12045
  get visibility$() {
12034
- return this._visibility$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12046
+ return this._visibility$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12035
12047
  }
12036
12048
  /**
12037
12049
  * The current visibility state value.
@@ -12044,12 +12056,12 @@ var VisibilityController = class extends Destroyable {
12044
12056
  * Each event includes the previous state, new state, and timestamp.
12045
12057
  */
12046
12058
  get visibilityChange$() {
12047
- return this._visibilityChange$.pipe((0, import_cjs$24.takeUntil)(this._destroyed$));
12059
+ return this._visibilityChange$.pipe((0, import_cjs$26.takeUntil)(this._destroyed$));
12048
12060
  }
12049
12061
  destroy() {
12050
12062
  if (this._hasVisibilityApi) {
12051
12063
  document.removeEventListener("visibilitychange", this._boundHandler);
12052
- logger$23.debug("VisibilityController: removed visibilitychange listener");
12064
+ logger$25.debug("VisibilityController: removed visibilitychange listener");
12053
12065
  }
12054
12066
  super.destroy();
12055
12067
  }
@@ -12067,7 +12079,7 @@ var VisibilityController = class extends Destroyable {
12067
12079
  timestamp: Date.now()
12068
12080
  };
12069
12081
  this._visibilityChange$.next(changeEvent);
12070
- logger$23.debug("VisibilityController: visibility changed", {
12082
+ logger$25.debug("VisibilityController: visibility changed", {
12071
12083
  from: previousState,
12072
12084
  to: newState
12073
12085
  });
@@ -12076,13 +12088,13 @@ var VisibilityController = class extends Destroyable {
12076
12088
 
12077
12089
  //#endregion
12078
12090
  //#region src/behaviors/Fetchable.ts
12079
- var import_cjs$23 = require_cjs();
12091
+ var import_cjs$25 = require_cjs();
12080
12092
  var Fetchable = class extends Destroyable {
12081
12093
  constructor(fromPath, http) {
12082
12094
  super();
12083
12095
  this.fromPath = fromPath;
12084
12096
  this.http = http;
12085
- 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$));
12097
+ 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$));
12086
12098
  }
12087
12099
  async fetch() {
12088
12100
  const response = await this.http.request({
@@ -12100,14 +12112,14 @@ var Fetchable = class extends Destroyable {
12100
12112
  };
12101
12113
 
12102
12114
  //#endregion
12103
- //#region src/core/entities/Subscriber.ts
12115
+ //#region src/core/entities/User.ts
12104
12116
  /**
12105
- * Authenticated subscriber profile.
12117
+ * Authenticated user profile.
12106
12118
  *
12107
12119
  * Fetched automatically when a {@link SignalWire} connects.
12108
12120
  * Contains identity, contact, and organization details.
12109
12121
  */
12110
- var Subscriber = class extends Fetchable {
12122
+ var User = class extends Fetchable {
12111
12123
  constructor(http) {
12112
12124
  super("/api/fabric/subscriber/info", http);
12113
12125
  }
@@ -12317,20 +12329,18 @@ const RPCEventAckResponse = (id) => makeRPCResponse({
12317
12329
 
12318
12330
  //#endregion
12319
12331
  //#region src/managers/AttachManager.ts
12320
- const logger$22 = getLogger();
12332
+ const logger$24 = getLogger();
12321
12333
  var AttachManager = class {
12322
12334
  constructor(storage, deviceController, reconnectCallsTimeout, attachKey) {
12323
12335
  this.storage = storage;
12324
12336
  this.deviceController = deviceController;
12325
12337
  this.reconnectCallsTimeout = reconnectCallsTimeout;
12326
12338
  this.attachKey = attachKey;
12339
+ this.writeQueue = Promise.resolve();
12327
12340
  }
12328
12341
  async detachAll() {
12329
- const attached = await this.readAttached();
12330
- for (const callId of Object.keys(attached)) await this.detach({
12331
- id: callId,
12332
- nodeId: attached[callId].nodeId,
12333
- mediaDirections: attached[callId].mediaDirections
12342
+ await this.mutate((attached) => {
12343
+ return {};
12334
12344
  });
12335
12345
  }
12336
12346
  setSession(session) {
@@ -12340,7 +12350,7 @@ var AttachManager = class {
12340
12350
  try {
12341
12351
  return await this.storage.getItem(this.attachKey) ?? {};
12342
12352
  } catch (error) {
12343
- logger$22.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12353
+ logger$24.warn("[AttachManager] Failed to retrieve attached calls from storage", error);
12344
12354
  return {};
12345
12355
  }
12346
12356
  }
@@ -12348,34 +12358,50 @@ var AttachManager = class {
12348
12358
  try {
12349
12359
  await this.storage.setItem(this.attachKey, attached);
12350
12360
  } catch (error) {
12351
- logger$22.warn("[AttachManager] Failed to write attached calls to storage", error);
12361
+ logger$24.warn("[AttachManager] Failed to write attached calls to storage", error);
12352
12362
  }
12353
12363
  }
12364
+ /**
12365
+ * Serialize a read-modify-write operation against the attached-calls
12366
+ * storage. The mutator receives the current state and returns the new
12367
+ * state. Concurrent calls queue behind the in-flight one so writes never
12368
+ * interleave.
12369
+ */
12370
+ async mutate(mutator) {
12371
+ const next = this.writeQueue.then(async () => {
12372
+ const updated = await mutator(await this.readAttached());
12373
+ await this.writeAttached(updated);
12374
+ });
12375
+ this.writeQueue = next.catch(() => void 0);
12376
+ return next;
12377
+ }
12354
12378
  async attach(call) {
12355
12379
  if (!call.to) {
12356
- logger$22.warn("[AttachManager] Skip attach for calls with no destination");
12380
+ logger$24.warn("[AttachManager] Skip attach for calls with no destination");
12357
12381
  return;
12358
12382
  }
12383
+ const destination = call.to;
12359
12384
  const attachment = {
12360
12385
  nodeId: call.nodeId,
12361
- destination: call.to,
12386
+ destination,
12362
12387
  mediaDirections: call.mediaDirections,
12363
12388
  audioInputDevice: call.mediaDirections.audio !== "inactive" ? this.deviceController.selectedAudioInputDevice : null,
12364
12389
  videoInputDevice: call.mediaDirections.video !== "inactive" ? this.deviceController.selectedVideoInputDevice : null,
12365
12390
  attachedAt: Date.now()
12366
12391
  };
12367
- const updated = {
12368
- ...await this.readAttached(),
12392
+ await this.mutate((attached) => ({
12393
+ ...attached,
12369
12394
  [call.id]: attachment
12370
- };
12371
- await this.writeAttached(updated);
12395
+ }));
12372
12396
  }
12373
12397
  async detach(call) {
12374
- const { [call.id]: _, ...remaining } = await this.readAttached();
12375
- await this.writeAttached(remaining);
12398
+ await this.mutate((attached) => {
12399
+ const { [call.id]: _, ...remaining } = attached;
12400
+ return remaining;
12401
+ });
12376
12402
  }
12377
12403
  async flush() {
12378
- await this.writeAttached({});
12404
+ await this.mutate(() => ({}));
12379
12405
  }
12380
12406
  /**
12381
12407
  * Reattach to previously active calls by sending verto.invite with
@@ -12404,15 +12430,15 @@ var AttachManager = class {
12404
12430
  callId,
12405
12431
  ...options
12406
12432
  });
12407
- logger$22.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12433
+ logger$24.info(`[AttachManager] Reattached call ${callId} (attempt ${attempt})`);
12408
12434
  succeeded = true;
12409
12435
  break;
12410
12436
  } catch (error) {
12411
- logger$22.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12437
+ logger$24.warn(`[AttachManager] Reattach attempt ${attempt}/3 failed for call ${callId}:`, error);
12412
12438
  if (attempt < 3) await new Promise((r) => setTimeout(r, (attempt + 1) * 1e3));
12413
12439
  }
12414
12440
  if (!succeeded) {
12415
- logger$22.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12441
+ logger$24.warn(`[AttachManager] Reattach failed after 3 attempts for call ${callId}, removing reference`);
12416
12442
  await this.detach({
12417
12443
  id: callId,
12418
12444
  mediaDirections: attachment.mediaDirections
@@ -12447,20 +12473,31 @@ var AttachManager = class {
12447
12473
  };
12448
12474
  }
12449
12475
  /**
12450
- * Consume stored attachment data for a pending call (used by session-level
12451
- * verto.attach handler as a future path when server supports it).
12476
+ * Look up stored attachment data for a call id and return CallOptions
12477
+ * suitable for rehydrating a reattached call. Returns undefined when no
12478
+ * matching entry exists in storage.
12479
+ *
12480
+ * Used by the session-level verto.attach handler when the server pushes
12481
+ * an attach event for a call the client doesn't have an object for yet
12482
+ * (e.g. after a reload).
12452
12483
  */
12453
- consumePendingAttachment(_callId) {}
12454
- async detachExpired() {
12484
+ async consumePendingAttachment(callId) {
12455
12485
  const attached = await this.readAttached();
12486
+ if (!Object.hasOwn(attached, callId)) return;
12487
+ return this.buildCallOptions(attached[callId]);
12488
+ }
12489
+ async detachExpired() {
12456
12490
  const now = Date.now();
12457
12491
  const timeout$5 = this.reconnectCallsTimeout;
12458
- const expired = Object.entries(attached).filter(([, attachment]) => now - attachment.attachedAt > timeout$5);
12459
- if (expired.length > 0) {
12492
+ await this.mutate((attached) => {
12460
12493
  const remaining = { ...attached };
12461
- for (const [callId] of expired) delete remaining[callId];
12462
- await this.writeAttached(remaining);
12463
- }
12494
+ let changed = false;
12495
+ for (const [callId, attachment] of Object.entries(attached)) if (now - attachment.attachedAt > timeout$5) {
12496
+ delete remaining[callId];
12497
+ changed = true;
12498
+ }
12499
+ return changed ? remaining : attached;
12500
+ });
12464
12501
  }
12465
12502
  };
12466
12503
 
@@ -12507,12 +12544,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
12507
12544
  exports.race = void 0;
12508
12545
  var argsOrArgArray_1 = require_argsOrArgArray();
12509
12546
  var raceWith_1$1 = require_raceWith();
12510
- function race$3() {
12547
+ function race$4() {
12511
12548
  var args = [];
12512
12549
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
12513
12550
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
12514
12551
  }
12515
- exports.race = race$3;
12552
+ exports.race = race$4;
12516
12553
  }));
12517
12554
 
12518
12555
  //#endregion
@@ -13440,7 +13477,7 @@ function computeCapabilities(capabilities) {
13440
13477
 
13441
13478
  //#endregion
13442
13479
  //#region src/core/capabilities/SelfCapabilities.ts
13443
- var import_cjs$22 = require_cjs();
13480
+ var import_cjs$24 = require_cjs();
13444
13481
  /**
13445
13482
  * SelfCapabilities manages the capability state for the self participant.
13446
13483
  *
@@ -13476,7 +13513,7 @@ var SelfCapabilities = class extends Destroyable {
13476
13513
  }
13477
13514
  /** Observable for self member capabilities */
13478
13515
  get self$() {
13479
- return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.self), (0, import_cjs$22.distinctUntilChanged)()));
13516
+ return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.self), (0, import_cjs$24.distinctUntilChanged)()));
13480
13517
  }
13481
13518
  /** Current self member capabilities */
13482
13519
  get self() {
@@ -13484,7 +13521,7 @@ var SelfCapabilities = class extends Destroyable {
13484
13521
  }
13485
13522
  /** Observable for other member capabilities */
13486
13523
  get member$() {
13487
- return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.member), (0, import_cjs$22.distinctUntilChanged)()));
13524
+ return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.member), (0, import_cjs$24.distinctUntilChanged)()));
13488
13525
  }
13489
13526
  /** Current other member capabilities */
13490
13527
  get member() {
@@ -13492,7 +13529,7 @@ var SelfCapabilities = class extends Destroyable {
13492
13529
  }
13493
13530
  /** Observable for end call capability */
13494
13531
  get end$() {
13495
- return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.end), (0, import_cjs$22.distinctUntilChanged)()));
13532
+ return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.end), (0, import_cjs$24.distinctUntilChanged)()));
13496
13533
  }
13497
13534
  /** Current end call capability */
13498
13535
  get end() {
@@ -13500,7 +13537,7 @@ var SelfCapabilities = class extends Destroyable {
13500
13537
  }
13501
13538
  /** Observable for set layout capability */
13502
13539
  get setLayout$() {
13503
- return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.setLayout), (0, import_cjs$22.distinctUntilChanged)()));
13540
+ return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.setLayout), (0, import_cjs$24.distinctUntilChanged)()));
13504
13541
  }
13505
13542
  /** Current set layout capability */
13506
13543
  get setLayout() {
@@ -13508,7 +13545,7 @@ var SelfCapabilities = class extends Destroyable {
13508
13545
  }
13509
13546
  /** Observable for send digit capability */
13510
13547
  get sendDigit$() {
13511
- return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.sendDigit), (0, import_cjs$22.distinctUntilChanged)()));
13548
+ return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.sendDigit), (0, import_cjs$24.distinctUntilChanged)()));
13512
13549
  }
13513
13550
  /** Current send digit capability */
13514
13551
  get sendDigit() {
@@ -13516,7 +13553,7 @@ var SelfCapabilities = class extends Destroyable {
13516
13553
  }
13517
13554
  /** Observable for vmuted hide capability */
13518
13555
  get vmutedHide$() {
13519
- return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.vmutedHide), (0, import_cjs$22.distinctUntilChanged)()));
13556
+ return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.vmutedHide), (0, import_cjs$24.distinctUntilChanged)()));
13520
13557
  }
13521
13558
  /** Current vmuted hide capability */
13522
13559
  get vmutedHide() {
@@ -13524,7 +13561,7 @@ var SelfCapabilities = class extends Destroyable {
13524
13561
  }
13525
13562
  /** Observable for lock capability */
13526
13563
  get lock$() {
13527
- return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.lock), (0, import_cjs$22.distinctUntilChanged)()));
13564
+ return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.lock), (0, import_cjs$24.distinctUntilChanged)()));
13528
13565
  }
13529
13566
  /** Current lock capability */
13530
13567
  get lock() {
@@ -13532,7 +13569,7 @@ var SelfCapabilities = class extends Destroyable {
13532
13569
  }
13533
13570
  /** Observable for device capability */
13534
13571
  get device$() {
13535
- return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.device), (0, import_cjs$22.distinctUntilChanged)()));
13572
+ return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.device), (0, import_cjs$24.distinctUntilChanged)()));
13536
13573
  }
13537
13574
  /** Current device capability */
13538
13575
  get device() {
@@ -13540,7 +13577,7 @@ var SelfCapabilities = class extends Destroyable {
13540
13577
  }
13541
13578
  /** Observable for screenshare capability */
13542
13579
  get screenshare$() {
13543
- return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$22.map)((state) => state.screenshare), (0, import_cjs$22.distinctUntilChanged)()));
13580
+ return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$24.map)((state) => state.screenshare), (0, import_cjs$24.distinctUntilChanged)()));
13544
13581
  }
13545
13582
  /** Current screenshare capability */
13546
13583
  get screenshare() {
@@ -13568,7 +13605,7 @@ function toggleHandraiseMethod(is) {
13568
13605
 
13569
13606
  //#endregion
13570
13607
  //#region src/core/entities/Participant.ts
13571
- const logger$21 = getLogger();
13608
+ const logger$23 = getLogger();
13572
13609
  const initialState = {};
13573
13610
  /**
13574
13611
  * Represents a participant in a call.
@@ -13620,15 +13657,35 @@ var Participant = class extends Destroyable {
13620
13657
  get deaf$() {
13621
13658
  return this.cachedObservable("deaf$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)()));
13622
13659
  }
13623
- /** Observable of the participant's microphone input volume. */
13660
+ /**
13661
+ * Observable of the participant's **server-side** microphone input volume
13662
+ * as reported by the mix engine. This is gain applied on the bridged audio
13663
+ * leg (FreeSWITCH channel read volume), NOT the local browser mic. For a
13664
+ * local PC mic control, see {@link Call.setLocalMicrophoneGain}.
13665
+ *
13666
+ * @see {@link setAudioInputVolume}
13667
+ */
13624
13668
  get inputVolume$() {
13625
13669
  return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)()));
13626
13670
  }
13627
- /** Observable of the participant's speaker output volume. */
13671
+ /**
13672
+ * Observable of the participant's **server-side** speaker output volume as
13673
+ * reported by the mix engine (FreeSWITCH channel write volume). NOT the
13674
+ * local HTML `<audio>` element volume — set that on your own element.
13675
+ *
13676
+ * @see {@link setAudioOutputVolume}
13677
+ */
13628
13678
  get outputVolume$() {
13629
13679
  return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)()));
13630
13680
  }
13631
- /** Observable of the microphone input sensitivity level. */
13681
+ /**
13682
+ * Observable of the **conference-only** microphone energy/gate sensitivity
13683
+ * level for this member. Routes through the conferencing mix engine and has
13684
+ * no effect on 1:1 WebRTC calls. Populated from `member.updated` events for
13685
+ * conference members.
13686
+ *
13687
+ * @see {@link setAudioInputSensitivity}
13688
+ */
13632
13689
  get inputSensitivity$() {
13633
13690
  return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)()));
13634
13691
  }
@@ -13656,9 +13713,9 @@ var Participant = class extends Destroyable {
13656
13713
  get meta$() {
13657
13714
  return this.cachedObservable("meta$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.meta), (0, import_operators$1.distinctUntilChanged)()));
13658
13715
  }
13659
- /** Observable of the participant's subscriber ID. */
13660
- get subscriberId$() {
13661
- return this.cachedObservable("subscriberId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)()));
13716
+ /** Observable of the participant's user ID. */
13717
+ get userId$() {
13718
+ return this.cachedObservable("userId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)()));
13662
13719
  }
13663
13720
  /** Observable of the participant's address ID. */
13664
13721
  get addressId$() {
@@ -13716,15 +13773,25 @@ var Participant = class extends Destroyable {
13716
13773
  get deaf() {
13717
13774
  return this._state$.value.deaf ?? false;
13718
13775
  }
13719
- /** Current microphone input volume level, or `undefined` if not set. */
13776
+ /**
13777
+ * Current **server-side** microphone input volume as reported by the mix
13778
+ * engine, or `undefined` if not set. Not the local PC mic — see
13779
+ * {@link Call.setLocalMicrophoneGain} for browser-side control.
13780
+ */
13720
13781
  get inputVolume() {
13721
13782
  return this._state$.value.input_volume;
13722
13783
  }
13723
- /** Current speaker output volume level, or `undefined` if not set. */
13784
+ /**
13785
+ * Current **server-side** speaker output volume from the mix engine, or
13786
+ * `undefined` if not set. Not the local `<audio>` element volume.
13787
+ */
13724
13788
  get outputVolume() {
13725
13789
  return this._state$.value.output_volume;
13726
13790
  }
13727
- /** Current microphone input sensitivity level, or `undefined` if not set. */
13791
+ /**
13792
+ * Current **conference-only** microphone sensitivity/gate level, or
13793
+ * `undefined` if not set. Applies only to conference members.
13794
+ */
13728
13795
  get inputSensitivity() {
13729
13796
  return this._state$.value.input_sensitivity;
13730
13797
  }
@@ -13752,8 +13819,8 @@ var Participant = class extends Destroyable {
13752
13819
  get meta() {
13753
13820
  return this._state$.value.meta;
13754
13821
  }
13755
- /** Subscriber ID of this participant, or `undefined` if not available. */
13756
- get subscriberId() {
13822
+ /** User ID of this participant, or `undefined` if not available. */
13823
+ get userId() {
13757
13824
  return this._state$.value.subscriber_id;
13758
13825
  }
13759
13826
  /** Address ID of this participant, or `undefined` if not available. */
@@ -13828,19 +13895,44 @@ var Participant = class extends Destroyable {
13828
13895
  async toggleLowbitrate() {
13829
13896
  throw new UnimplementedError();
13830
13897
  }
13831
- /** Sets the microphone input sensitivity level. */
13898
+ /**
13899
+ * Adjusts the **conference-only** microphone energy gate / sensitivity level
13900
+ * for this member. Routes through the conferencing mix engine
13901
+ * (`signalwire.conferencing member.set_input_sensitivity`) and has no effect
13902
+ * on 1:1 WebRTC calls — for those, use browser audio constraints via
13903
+ * {@link Call.setNoiseSuppression} / {@link Call.setAutoGainControl}.
13904
+ *
13905
+ * This is **not** a local PC mic gain control; it only changes how the
13906
+ * server-side mixer decides to open the mic gate on this participant.
13907
+ *
13908
+ * @param value - Sensitivity level as understood by the conference engine
13909
+ * (integer, larger values are more sensitive).
13910
+ */
13832
13911
  async setAudioInputSensitivity(value) {
13833
13912
  await this.executeMethod(this.id, "call.microphone.sensitivity.set", { sensitivity: value });
13834
13913
  }
13835
13914
  /**
13836
- * Sets the microphone input volume level.
13915
+ * Sets the **server-side** microphone volume on this participant's bridged
13916
+ * call leg. Applies a multiplier to the audio flowing through the mix
13917
+ * engine (FreeSWITCH channel read volume) — changes what other participants
13918
+ * hear, not what the local browser captures.
13919
+ *
13920
+ * For local PC mic gain, use {@link Call.setLocalMicrophoneGain} instead.
13921
+ *
13837
13922
  * @param value - Volume level (0-100).
13838
13923
  */
13839
13924
  async setAudioInputVolume(value) {
13840
13925
  await this.executeMethod(this.id, "call.microphone.volume.set", { volume: value });
13841
13926
  }
13842
13927
  /**
13843
- * Sets the speaker output volume level.
13928
+ * Sets the **server-side** speaker volume on this participant's bridged call
13929
+ * leg (FreeSWITCH channel write volume) — what this participant hears from
13930
+ * the mix before it reaches their client.
13931
+ *
13932
+ * For local playback volume (the `<audio>` element the consumer attaches
13933
+ * `remoteStream` to), set `audioElement.volume` directly in the consumer's
13934
+ * code.
13935
+ *
13844
13936
  * @param value - Volume level (0-100).
13845
13937
  */
13846
13938
  async setAudioOutputVolume(value) {
@@ -13946,7 +14038,7 @@ var SelfParticipant = class extends Participant {
13946
14038
  try {
13947
14039
  await this.vertoManager.addScreenMedia();
13948
14040
  } catch (error) {
13949
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14041
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13950
14042
  }
13951
14043
  }
13952
14044
  /** Observable of the current screen share status. */
@@ -13966,7 +14058,7 @@ var SelfParticipant = class extends Participant {
13966
14058
  try {
13967
14059
  await this.vertoManager.addInputDevice(options);
13968
14060
  } catch (error) {
13969
- logger$21.error("[Participant.startScreenShare] Screen share error:", error);
14061
+ logger$23.error("[Participant.startScreenShare] Screen share error:", error);
13970
14062
  }
13971
14063
  }
13972
14064
  /** Removes an additional media input device by ID. */
@@ -14028,7 +14120,7 @@ var SelfParticipant = class extends Participant {
14028
14120
  */
14029
14121
  exitStudioModeIfActive() {
14030
14122
  if (this._studioAudio$.value) {
14031
- logger$21.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14123
+ logger$23.debug("[SelfParticipant] Exiting studio audio mode due to individual flag toggle");
14032
14124
  this._studioAudio$.next(false);
14033
14125
  }
14034
14126
  }
@@ -14052,7 +14144,7 @@ var SelfParticipant = class extends Participant {
14052
14144
  try {
14053
14145
  await super.mute();
14054
14146
  } catch (error) {
14055
- logger$21.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14147
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while muting audio input, proceeding with local toggle anyway", error);
14056
14148
  } finally {
14057
14149
  this.vertoManager.muteMainAudioInputDevice();
14058
14150
  }
@@ -14062,7 +14154,7 @@ var SelfParticipant = class extends Participant {
14062
14154
  try {
14063
14155
  await super.unmute();
14064
14156
  } catch (error) {
14065
- logger$21.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14157
+ logger$23.warn("[Participant.toggleAudioInput] Server Error while unmuting audio input, proceeding with local toggle anyway", error);
14066
14158
  } finally {
14067
14159
  await this.vertoManager.unmuteMainAudioInputDevice();
14068
14160
  }
@@ -14072,7 +14164,7 @@ var SelfParticipant = class extends Participant {
14072
14164
  try {
14073
14165
  await super.muteVideo();
14074
14166
  } catch (error) {
14075
- logger$21.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14167
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while muting video input, proceeding with local toggle anyway", error);
14076
14168
  } finally {
14077
14169
  this.vertoManager.muteMainVideoInputDevice();
14078
14170
  }
@@ -14082,7 +14174,7 @@ var SelfParticipant = class extends Participant {
14082
14174
  try {
14083
14175
  await super.unmuteVideo();
14084
14176
  } catch (error) {
14085
- logger$21.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14177
+ logger$23.warn("[Participant.toggleVideoInput] Server Error while unmuting video input, proceeding with local toggle anyway", error);
14086
14178
  } finally {
14087
14179
  await this.vertoManager.unmuteMainVideoInputDevice();
14088
14180
  }
@@ -14176,7 +14268,7 @@ function isLayoutChangedPayload(value) {
14176
14268
 
14177
14269
  //#endregion
14178
14270
  //#region src/operators/filterNull.ts
14179
- var import_cjs$21 = require_cjs();
14271
+ var import_cjs$23 = require_cjs();
14180
14272
  /**
14181
14273
  * RxJS operator that filters out `null` and `undefined` values with type narrowing.
14182
14274
  *
@@ -14188,7 +14280,7 @@ var import_cjs$21 = require_cjs();
14188
14280
  * ```
14189
14281
  */
14190
14282
  function filterNull() {
14191
- return (0, import_cjs$21.filter)((value) => value != null);
14283
+ return (0, import_cjs$23.filter)((value) => value != null);
14192
14284
  }
14193
14285
 
14194
14286
  //#endregion
@@ -14203,7 +14295,7 @@ const getValueFrom = (obj, path, defaultValue) => {
14203
14295
 
14204
14296
  //#endregion
14205
14297
  //#region src/operators/filterEventAs.ts
14206
- var import_cjs$20 = require_cjs();
14298
+ var import_cjs$22 = require_cjs();
14207
14299
  var import_operators = require_operators();
14208
14300
  /**
14209
14301
  * RxJS operator that filters events based on a predicate and maps matching events.
@@ -14237,7 +14329,7 @@ var import_operators = require_operators();
14237
14329
  * ```
14238
14330
  */
14239
14331
  function ifIsMap(predicate, mapFn) {
14240
- return (0, import_cjs$20.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14332
+ return (0, import_cjs$22.pipe)((0, import_operators.filter)(predicate), (0, import_operators.map)(mapFn));
14241
14333
  }
14242
14334
  /**
14243
14335
  * Generic RxJS operator that filters events using a type guard and extracts a property.
@@ -14279,38 +14371,38 @@ function ifIsMap(predicate, mapFn) {
14279
14371
  * ```
14280
14372
  */
14281
14373
  function filterAs(predicate, resultPath) {
14282
- return (0, import_cjs$20.pipe)(ifIsMap(predicate, (event) => {
14374
+ return (0, import_cjs$22.pipe)(ifIsMap(predicate, (event) => {
14283
14375
  return getValueFrom(event, resultPath);
14284
14376
  }), (0, import_operators.filter)((value) => value !== void 0));
14285
14377
  }
14286
14378
 
14287
14379
  //#endregion
14288
14380
  //#region src/operators/throwOnRPCError.ts
14289
- var import_cjs$19 = require_cjs();
14290
- const logger$20 = getLogger();
14381
+ var import_cjs$21 = require_cjs();
14382
+ const logger$22 = getLogger();
14291
14383
  /**
14292
14384
  * RxJS operator that throws a {@link JSONRPCError} when the RPC response contains an error.
14293
14385
  * Passes successful responses through unchanged.
14294
14386
  */
14295
14387
  function throwOnRPCError() {
14296
- return (0, import_cjs$19.map)((response) => {
14388
+ return (0, import_cjs$21.map)((response) => {
14297
14389
  if (response.error) {
14298
- logger$20.error("[throwOnRPCError] RPC error response:", {
14390
+ logger$22.error("[throwOnRPCError] RPC error response:", {
14299
14391
  code: response.error.code,
14300
14392
  message: response.error.message,
14301
14393
  data: response.error.data
14302
14394
  });
14303
14395
  throw new JSONRPCError(response.error.code, response.error.message, response.error.data);
14304
14396
  }
14305
- logger$20.debug("[throwOnRPCError] RPC successful response:", response);
14397
+ logger$22.debug("[throwOnRPCError] RPC successful response:", response);
14306
14398
  return response;
14307
14399
  });
14308
14400
  }
14309
14401
 
14310
14402
  //#endregion
14311
14403
  //#region src/managers/CallEventsManager.ts
14312
- var import_cjs$18 = require_cjs();
14313
- const logger$19 = getLogger();
14404
+ var import_cjs$20 = require_cjs();
14405
+ const logger$21 = getLogger();
14314
14406
  const initialSessionState = {};
14315
14407
  /** @internal */
14316
14408
  var CallEventsManager = class extends Destroyable {
@@ -14326,7 +14418,7 @@ var CallEventsManager = class extends Destroyable {
14326
14418
  this.initSubscriptions();
14327
14419
  }
14328
14420
  get participants$() {
14329
- return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$18.map)((participantsRecord) => Object.values(participantsRecord))));
14421
+ return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$20.map)((participantsRecord) => Object.values(participantsRecord))));
14330
14422
  }
14331
14423
  get participants() {
14332
14424
  return Object.values(this._participants$.value);
@@ -14344,40 +14436,40 @@ var CallEventsManager = class extends Destroyable {
14344
14436
  return this.callIds.has(callId);
14345
14437
  }
14346
14438
  get recording$() {
14347
- return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recording), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14439
+ return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recording), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14348
14440
  }
14349
14441
  get recordings$() {
14350
- return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.recordings), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14442
+ return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.recordings), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14351
14443
  }
14352
14444
  get streaming$() {
14353
- return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streaming), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14445
+ return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streaming), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14354
14446
  }
14355
14447
  get streams$() {
14356
- return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.streams), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14448
+ return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.streams), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14357
14449
  }
14358
14450
  get playbacks$() {
14359
- return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.playbacks), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14451
+ return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.playbacks), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14360
14452
  }
14361
14453
  get raiseHandPriority$() {
14362
- return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.prioritize_handraise), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14454
+ return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.prioritize_handraise), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14363
14455
  }
14364
14456
  get locked$() {
14365
- return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.locked), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14457
+ return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.locked), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14366
14458
  }
14367
14459
  get meta$() {
14368
- return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.meta), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14460
+ return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.meta), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14369
14461
  }
14370
14462
  get capabilities$() {
14371
- return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.capabilities), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14463
+ return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.capabilities), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14372
14464
  }
14373
14465
  get layout$() {
14374
- return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_name), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14466
+ return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_name), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14375
14467
  }
14376
14468
  get layouts$() {
14377
- return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layouts), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14469
+ return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layouts), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14378
14470
  }
14379
14471
  get layoutLayers$() {
14380
- return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$18.map)((state) => state.layout_layers), (0, import_cjs$18.distinctUntilChanged)(), filterNull()));
14472
+ return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$20.map)((state) => state.layout_layers), (0, import_cjs$20.distinctUntilChanged)(), filterNull()));
14381
14473
  }
14382
14474
  get self() {
14383
14475
  return this._self$.value;
@@ -14414,7 +14506,7 @@ var CallEventsManager = class extends Destroyable {
14414
14506
  }
14415
14507
  initSubscriptions() {
14416
14508
  this.subscribeTo(this.callJoinedEvent$, (callJoinedEvent) => {
14417
- logger$19.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14509
+ logger$21.debug("[CallEventsManager] Handling call.joined event for call/session IDs:", {
14418
14510
  callId: callJoinedEvent.call_id,
14419
14511
  roomSessionId: callJoinedEvent.room_session_id
14420
14512
  });
@@ -14441,19 +14533,19 @@ var CallEventsManager = class extends Destroyable {
14441
14533
  if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
14442
14534
  });
14443
14535
  this.subscribeTo(this.memberUpdates$, (member) => {
14444
- logger$19.debug("[CallEventsManager] Handling member update event for member ID:", member);
14536
+ logger$21.debug("[CallEventsManager] Handling member update event for member ID:", member);
14445
14537
  this.upsertParticipant(member);
14446
14538
  });
14447
14539
  this.subscribeTo(this.webRtcCallSession.memberLeft$, (memberLeftEvent) => {
14448
- logger$19.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14540
+ logger$21.debug("[CallEventsManager] Handling member.left event for member ID:", memberLeftEvent.member.member_id);
14449
14541
  const participants = { ...this._participants$.value };
14450
14542
  if (memberLeftEvent.member.member_id in participants) {
14451
14543
  delete participants[memberLeftEvent.member.member_id];
14452
14544
  this._participants$.next(participants);
14453
- } else logger$19.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14545
+ } else logger$21.warn(`[CallEventsManager] Received member.left event for unknown member ID: ${memberLeftEvent.member.member_id}`);
14454
14546
  });
14455
14547
  this.subscribeTo(this.webRtcCallSession.callUpdated$, (callUpdatedEvent) => {
14456
- logger$19.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14548
+ logger$21.debug("[CallEventsManager] Handling call.updated event:", callUpdatedEvent);
14457
14549
  const roomSession = callUpdatedEvent.room_session;
14458
14550
  this._sessionState$.next({
14459
14551
  ...this._sessionState$.value,
@@ -14468,7 +14560,7 @@ var CallEventsManager = class extends Destroyable {
14468
14560
  });
14469
14561
  });
14470
14562
  this.subscribeTo(this.layoutChangedEvent$, (layoutChangedEvent) => {
14471
- logger$19.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14563
+ logger$21.debug("[CallEventsManager] Handling layout.changed event:", layoutChangedEvent);
14472
14564
  this._sessionState$.next({
14473
14565
  ...this._sessionState$.value,
14474
14566
  layout_name: layoutChangedEvent.id,
@@ -14478,10 +14570,10 @@ var CallEventsManager = class extends Destroyable {
14478
14570
  });
14479
14571
  }
14480
14572
  updateParticipantPositions(layoutChangedEvent) {
14481
- 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.");
14573
+ 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.");
14482
14574
  layoutChangedEvent.layers.filter((layer) => !!layer.member_id).filter((layer) => {
14483
14575
  if (!(layer.member_id in this._participants$.value)) {
14484
- logger$19.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14576
+ logger$21.warn(`[CallEventsManager] Skipping layout layer for unknown member_id: ${layer.member_id}`);
14485
14577
  return false;
14486
14578
  }
14487
14579
  return true;
@@ -14504,7 +14596,7 @@ var CallEventsManager = class extends Destroyable {
14504
14596
  layouts: response.result.layouts
14505
14597
  });
14506
14598
  }).catch((error) => {
14507
- logger$19.error("[CallEventsManager] Error fetching layouts:", error);
14599
+ logger$21.error("[CallEventsManager] Error fetching layouts:", error);
14508
14600
  });
14509
14601
  }
14510
14602
  updateParticipants(members) {
@@ -14520,7 +14612,7 @@ var CallEventsManager = class extends Destroyable {
14520
14612
  }
14521
14613
  const participant = this._participants$.value[member.member_id];
14522
14614
  const oldValue = participant.value;
14523
- logger$19.debug("[CallEventsManager] Updating participant:", member.member_id, {
14615
+ logger$21.debug("[CallEventsManager] Updating participant:", member.member_id, {
14524
14616
  oldValue,
14525
14617
  newValue: member
14526
14618
  });
@@ -14532,18 +14624,18 @@ var CallEventsManager = class extends Destroyable {
14532
14624
  this._participants$.next(this._participants$.value);
14533
14625
  }
14534
14626
  get callJoinedEvent$() {
14535
- return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$18.filter)(isCallJoinedPayload), (0, import_cjs$18.tap)((event) => {
14536
- logger$19.debug("[CallEventsManager] Call joined event:", event);
14627
+ return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$20.filter)(isCallJoinedPayload), (0, import_cjs$20.tap)((event) => {
14628
+ logger$21.debug("[CallEventsManager] Call joined event:", event);
14537
14629
  })));
14538
14630
  }
14539
14631
  get layoutChangedEvent$() {
14540
- return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$18.tap)((event) => {
14541
- logger$19.debug("[CallEventsManager] Layout changed event:", event);
14632
+ return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$20.tap)((event) => {
14633
+ logger$21.debug("[CallEventsManager] Layout changed event:", event);
14542
14634
  })));
14543
14635
  }
14544
14636
  get memberUpdates$() {
14545
- 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) => {
14546
- logger$19.debug("[CallEventsManager] Member update event:", event);
14637
+ 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) => {
14638
+ logger$21.debug("[CallEventsManager] Member update event:", event);
14547
14639
  })));
14548
14640
  }
14549
14641
  destroy() {
@@ -14799,8 +14891,8 @@ function appendStereoParams(fmtpLine, maxBitrate) {
14799
14891
 
14800
14892
  //#endregion
14801
14893
  //#region src/controllers/ICEGatheringController.ts
14802
- var import_cjs$17 = require_cjs();
14803
- const logger$18 = getLogger();
14894
+ var import_cjs$19 = require_cjs();
14895
+ const logger$20 = getLogger();
14804
14896
  var ICEGatheringController = class extends Destroyable {
14805
14897
  constructor(peerConnection, peerConnectionControllerNegotiating$, options = {}) {
14806
14898
  super();
@@ -14808,23 +14900,23 @@ var ICEGatheringController = class extends Destroyable {
14808
14900
  this.peerConnectionControllerNegotiating$ = peerConnectionControllerNegotiating$;
14809
14901
  this.onicegatheringstatechangeHandler = () => {
14810
14902
  const { iceGatheringState } = this.peerConnection;
14811
- logger$18.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14903
+ logger$20.debug(`[ICEGatheringController] ICE gathering state changed to: ${iceGatheringState}`);
14812
14904
  if (iceGatheringState === "gathering") this._iceCandidatesState.next({
14813
14905
  state: "gathering",
14814
14906
  validSDP: false
14815
14907
  });
14816
14908
  };
14817
14909
  this.onicecandidateHandler = (event) => {
14818
- logger$18.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14910
+ logger$20.debug("[ICEGatheringController] ICE candidate event received:", event.candidate);
14819
14911
  this.removeTimer("iceCandidateTimer");
14820
14912
  if (event.candidate) this.iceCandidateTimer = setTimeout(() => {
14821
14913
  if (this.peerConnection.iceGatheringState !== "complete") {
14822
- logger$18.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14914
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout, using current SDP");
14823
14915
  this.handleICECandidateTimeout();
14824
14916
  }
14825
14917
  }, this.iceCandidateTimeout);
14826
14918
  else {
14827
- logger$18.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14919
+ logger$20.debug("[ICEGatheringController] ICE gathering completed: null candidate received");
14828
14920
  this.removeTimer("iceGatheringTimer");
14829
14921
  this.handleICEGatheringComplete();
14830
14922
  }
@@ -14837,12 +14929,12 @@ var ICEGatheringController = class extends Destroyable {
14837
14929
  this.iceGatheringTimeout = options.iceGatheringTimeout ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;
14838
14930
  this.relayOnly = options.relayOnly ?? false;
14839
14931
  this.setupEventListeners();
14840
- this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$17.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14932
+ this.subscribeTo(this.peerConnectionControllerNegotiating$.pipe((0, import_cjs$19.filter)((isNegotiating) => isNegotiating)), (isNegotiating) => {
14841
14933
  if (isNegotiating) {
14842
14934
  this.setupEventListeners();
14843
14935
  this.iceGatheringTimer = setTimeout(() => {
14844
14936
  if (this.peerConnection.iceGatheringState !== "complete") {
14845
- logger$18.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14937
+ logger$20.warn("[ICEGatheringController] ICE gathering timeout, using current SDP");
14846
14938
  this.handleICEGatheringTimeout();
14847
14939
  }
14848
14940
  }, this.iceGatheringTimeout);
@@ -14856,7 +14948,7 @@ var ICEGatheringController = class extends Destroyable {
14856
14948
  this.peerConnection.addEventListener("icegatheringstatechange", this.onicegatheringstatechangeHandler);
14857
14949
  }
14858
14950
  get iceCandidatesState$() {
14859
- 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));
14951
+ 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));
14860
14952
  }
14861
14953
  get hasValidLocalDescriptionSDP() {
14862
14954
  const sdp = this.peerConnection.localDescription?.sdp;
@@ -14869,9 +14961,9 @@ var ICEGatheringController = class extends Destroyable {
14869
14961
  this.relayOnly = value;
14870
14962
  }
14871
14963
  handleICEGatheringComplete() {
14872
- logger$18.debug("[ICEGatheringController] Handling ICE gathering complete");
14873
- logger$18.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14874
- logger$18.debug("[ICEGatheringController] ICE gathering complete");
14964
+ logger$20.debug("[ICEGatheringController] Handling ICE gathering complete");
14965
+ logger$20.debug(`[ICEGatheringController] Checking ICE gathering state: ${this.peerConnection.iceGatheringState}`);
14966
+ logger$20.debug("[ICEGatheringController] ICE gathering complete");
14875
14967
  this._iceCandidatesState.next({
14876
14968
  state: "complete",
14877
14969
  validSDP: this.hasValidLocalDescriptionSDP
@@ -14887,21 +14979,21 @@ var ICEGatheringController = class extends Destroyable {
14887
14979
  this.removeTimer("iceGatheringTimer");
14888
14980
  const validSDP = this.hasValidLocalDescriptionSDP;
14889
14981
  if (validSDP) {
14890
- logger$18.debug("[ICEGatheringController] Local SDP is valid");
14982
+ logger$20.debug("[ICEGatheringController] Local SDP is valid");
14891
14983
  this._iceCandidatesState.next({
14892
14984
  state: "timeout",
14893
14985
  validSDP
14894
14986
  });
14895
14987
  this.stopGathering();
14896
- } else logger$18.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14988
+ } else logger$20.debug("### ICE gathering timeout\n", this.peerConnection.localDescription?.sdp);
14897
14989
  }
14898
14990
  handleICECandidateTimeout() {
14899
14991
  if (this.iceCandidateTimer) this.removeTimer("iceCandidateTimer");
14900
- logger$18.warn("[ICEGatheringController] ICE candidate timeout");
14992
+ logger$20.warn("[ICEGatheringController] ICE candidate timeout");
14901
14993
  const validSDP = this.hasValidLocalDescriptionSDP;
14902
14994
  if (!validSDP && !this.relayOnly) this.restartICEGatheringWithRelayOnly();
14903
14995
  else {
14904
- logger$18.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14996
+ logger$20.debug("[ICEGatheringController] Using current SDP due to ICE candidate timeout");
14905
14997
  this._iceCandidatesState.next({
14906
14998
  state: "timeout",
14907
14999
  validSDP
@@ -14910,22 +15002,22 @@ var ICEGatheringController = class extends Destroyable {
14910
15002
  }
14911
15003
  }
14912
15004
  restartICEGatheringWithRelayOnly() {
14913
- logger$18.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
15005
+ logger$20.debug("[ICEGatheringController] Restarting ICE gathering with relay-only candidates");
14914
15006
  this.relayOnly = true;
14915
15007
  this.peerConnection.setConfiguration({
14916
15008
  ...this.peerConnection.getConfiguration(),
14917
15009
  iceTransportPolicy: "relay"
14918
15010
  });
14919
- if (!(this.peerConnection.connectionState === "connected")) this.peerConnection.restartIce();
15011
+ this.peerConnection.restartIce();
14920
15012
  }
14921
- removeTimer(timer$3) {
14922
- if (this[timer$3]) {
14923
- clearTimeout(this[timer$3]);
14924
- this[timer$3] = void 0;
15013
+ removeTimer(timer$4) {
15014
+ if (this[timer$4]) {
15015
+ clearTimeout(this[timer$4]);
15016
+ this[timer$4] = void 0;
14925
15017
  }
14926
15018
  }
14927
15019
  clearAllTimers() {
14928
- logger$18.debug("[ICEGatheringController] Clearing all timers");
15020
+ logger$20.debug("[ICEGatheringController] Clearing all timers");
14929
15021
  this.removeTimer("iceGatheringTimer");
14930
15022
  this.removeTimer("iceCandidateTimer");
14931
15023
  }
@@ -14934,17 +15026,181 @@ var ICEGatheringController = class extends Destroyable {
14934
15026
  this.peerConnection.removeEventListener("icecandidate", this.onicecandidateHandler);
14935
15027
  }
14936
15028
  destroy() {
14937
- logger$18.debug("[ICEGatheringController] Destroying ICEGatheringController");
15029
+ logger$20.debug("[ICEGatheringController] Destroying ICEGatheringController");
14938
15030
  this.clearAllTimers();
14939
15031
  this.removeEventListeners();
14940
15032
  super.destroy();
14941
15033
  }
14942
15034
  };
14943
15035
 
15036
+ //#endregion
15037
+ //#region src/controllers/LocalAudioPipeline.ts
15038
+ var import_cjs$18 = require_cjs();
15039
+ const logger$19 = getLogger();
15040
+ /**
15041
+ * Web Audio pipeline for the local microphone stream.
15042
+ *
15043
+ * Wraps the raw mic `MediaStreamTrack` in a graph of:
15044
+ *
15045
+ * ```
15046
+ * MediaStreamAudioSourceNode → GainNode → AnalyserNode → MediaStreamAudioDestinationNode
15047
+ * ```
15048
+ *
15049
+ * The {@link outputTrack} from the destination node is what callers should
15050
+ * attach to the `RTCRtpSender` in place of the raw mic track. The same
15051
+ * destination track is reused across input changes (device switch, mute /
15052
+ * unmute track replacement) so the sender reference stays stable — only the
15053
+ * source end of the graph is rebuilt.
15054
+ *
15055
+ * The pipeline owns a single {@link AudioContext}. Callers must invoke
15056
+ * {@link destroy} to release it when the call ends.
15057
+ */
15058
+ var LocalAudioPipeline = class extends Destroyable {
15059
+ constructor(options = {}) {
15060
+ super();
15061
+ this._inputSource = null;
15062
+ this._inputStream = null;
15063
+ this._lastSpokeAt = 0;
15064
+ this._gain$ = this.createBehaviorSubject(1);
15065
+ this._pttMultiplier = 1;
15066
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
15067
+ this._gainNode = this._audioContext.createGain();
15068
+ this._analyser = this._audioContext.createAnalyser();
15069
+ this._analyser.fftSize = 2048;
15070
+ this._analyser.smoothingTimeConstant = .3;
15071
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
15072
+ this._destination = this._audioContext.createMediaStreamDestination();
15073
+ this._gainNode.connect(this._analyser);
15074
+ this._analyser.connect(this._destination);
15075
+ this._speakingThreshold = options.speakingThreshold ?? VAD_THRESHOLD;
15076
+ this._speakingHoldMs = options.speakingHoldMs ?? VAD_HOLD_MS;
15077
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
15078
+ const initial = options.initialGain ?? 1;
15079
+ this._gain$.next(initial);
15080
+ this.applyEffectiveGain();
15081
+ }
15082
+ /** Observable of the current gain value (0..2). */
15083
+ get gain$() {
15084
+ return this._gain$.asObservable();
15085
+ }
15086
+ /** Current gain value (0..2). */
15087
+ get gain() {
15088
+ return this._gain$.value;
15089
+ }
15090
+ /**
15091
+ * Processed output track to attach to the RTCRtpSender. Stable reference
15092
+ * across input changes, so `sender.replaceTrack(pipeline.outputTrack)` only
15093
+ * needs to be called once.
15094
+ */
15095
+ get outputTrack() {
15096
+ const [track] = this._destination.stream.getAudioTracks();
15097
+ return track;
15098
+ }
15099
+ /**
15100
+ * Root-mean-square audio level of the input signal, 0..1. Emits on a fixed
15101
+ * interval (~30fps by default).
15102
+ */
15103
+ get level$() {
15104
+ return this.deferEmission((0, import_cjs$18.interval)(this._pollIntervalMs, import_cjs$18.animationFrameScheduler).pipe((0, import_cjs$18.map)(() => this.computeLevel())));
15105
+ }
15106
+ /**
15107
+ * Boolean VAD derived from {@link level$}. True while level ≥ threshold or
15108
+ * during the hold window after the last frame that crossed the threshold.
15109
+ */
15110
+ get speaking$() {
15111
+ return this.deferEmission(this.level$.pipe((0, import_cjs$18.map)((level) => this.evaluateSpeaking(level)), (0, import_cjs$18.distinctUntilChanged)()));
15112
+ }
15113
+ /**
15114
+ * Set gain multiplier applied to the input signal. 0 = silence,
15115
+ * 1 = unity, 2 = 2x. Values are clamped to [0, 2]. The effective gain on
15116
+ * the graph also respects the current PTT state.
15117
+ */
15118
+ setGain(value) {
15119
+ const clamped = Math.max(0, Math.min(2, value));
15120
+ this._gain$.next(clamped);
15121
+ this.applyEffectiveGain();
15122
+ }
15123
+ /**
15124
+ * Silence the graph when `active = false`, otherwise restore the configured
15125
+ * gain. Use this from a PTT handler: released → `false`, held → `true`.
15126
+ * Orthogonal to {@link setGain} — once PTT returns to active, the last
15127
+ * configured gain reappears.
15128
+ */
15129
+ setPTTActive(active) {
15130
+ this._pttMultiplier = active ? 1 : 0;
15131
+ this.applyEffectiveGain();
15132
+ }
15133
+ applyEffectiveGain() {
15134
+ this._gainNode.gain.value = this._gain$.value * this._pttMultiplier;
15135
+ }
15136
+ /**
15137
+ * Wire a new raw mic track as the pipeline's input. Replaces any previous
15138
+ * input source and reconnects the graph so {@link outputTrack} continues
15139
+ * to emit the processed audio. Pass `null` to disconnect the input (the
15140
+ * output track stays alive but emits silence).
15141
+ *
15142
+ * Also resumes the underlying AudioContext on attach — Chrome creates it
15143
+ * in a suspended state and the graph won't process (the destination
15144
+ * track emits silence) until resume() succeeds.
15145
+ */
15146
+ setInputTrack(track) {
15147
+ if (this._inputSource) {
15148
+ try {
15149
+ this._inputSource.disconnect();
15150
+ } catch (error) {
15151
+ logger$19.debug("[LocalAudioPipeline] input disconnect warning:", error);
15152
+ }
15153
+ this._inputSource = null;
15154
+ }
15155
+ if (this._inputStream) this._inputStream = null;
15156
+ if (!track) return;
15157
+ this._inputStream = new MediaStream([track]);
15158
+ this._inputSource = this._audioContext.createMediaStreamSource(this._inputStream);
15159
+ this._inputSource.connect(this._gainNode);
15160
+ if (this._audioContext.state === "suspended") this._audioContext.resume().catch((error) => {
15161
+ logger$19.warn("[LocalAudioPipeline] AudioContext resume failed:", error);
15162
+ });
15163
+ }
15164
+ destroy() {
15165
+ if (this._inputSource) {
15166
+ try {
15167
+ this._inputSource.disconnect();
15168
+ } catch {}
15169
+ this._inputSource = null;
15170
+ }
15171
+ try {
15172
+ this._gainNode.disconnect();
15173
+ this._analyser.disconnect();
15174
+ } catch {}
15175
+ this._audioContext.close().catch((error) => {
15176
+ logger$19.debug("[LocalAudioPipeline] audio context close warning:", error);
15177
+ });
15178
+ super.destroy();
15179
+ }
15180
+ computeLevel() {
15181
+ if (!this._inputSource) return 0;
15182
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
15183
+ let sum = 0;
15184
+ for (const sample$1 of this._analyserBuffer) {
15185
+ const normalized = (sample$1 - 128) / 128;
15186
+ sum += normalized * normalized;
15187
+ }
15188
+ return Math.sqrt(sum / this._analyserBuffer.length);
15189
+ }
15190
+ evaluateSpeaking(level) {
15191
+ const now = Date.now();
15192
+ if (level >= this._speakingThreshold) {
15193
+ this._lastSpokeAt = now;
15194
+ return true;
15195
+ }
15196
+ return now - this._lastSpokeAt < this._speakingHoldMs;
15197
+ }
15198
+ };
15199
+
14944
15200
  //#endregion
14945
15201
  //#region src/controllers/LocalStreamController.ts
14946
- var import_cjs$16 = require_cjs();
14947
- const logger$17 = getLogger();
15202
+ var import_cjs$17 = require_cjs();
15203
+ const logger$18 = getLogger();
14948
15204
  var LocalStreamController = class extends Destroyable {
14949
15205
  constructor(options) {
14950
15206
  super();
@@ -14958,16 +15214,16 @@ var LocalStreamController = class extends Destroyable {
14958
15214
  this._mediaTrackEnded$ = this.createSubject();
14959
15215
  }
14960
15216
  get localStream$() {
14961
- return this._localStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15217
+ return this._localStream$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14962
15218
  }
14963
15219
  get localAudioTracks$() {
14964
- return this._localAudioTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15220
+ return this._localAudioTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14965
15221
  }
14966
15222
  get localVideoTracks$() {
14967
- return this._localVideoTracks$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15223
+ return this._localVideoTracks$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14968
15224
  }
14969
15225
  get mediaTrackEnded$() {
14970
- return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$));
15226
+ return this._mediaTrackEnded$.asObservable().pipe((0, import_cjs$17.takeUntil)(this.destroyed$));
14971
15227
  }
14972
15228
  get localStream() {
14973
15229
  return this._localStream$.value;
@@ -14982,26 +15238,26 @@ var LocalStreamController = class extends Destroyable {
14982
15238
  * Build the local media stream based on the provided options.
14983
15239
  */
14984
15240
  async buildLocalStream() {
14985
- logger$17.debug("[LocalStreamController] Building local media stream.");
15241
+ logger$18.debug("[LocalStreamController] Building local media stream.");
14986
15242
  let stream;
14987
15243
  if (this.options.inputAudioStream ?? this.options.inputVideoStream) {
14988
15244
  const tracks = [...this.options.inputAudioStream?.getTracks() ?? [], ...this.options.inputVideoStream?.getTracks() ?? []];
14989
15245
  stream = new MediaStream(tracks);
14990
15246
  } else if (this.options.propose === "screenshare") {
14991
- logger$17.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
15247
+ logger$18.debug("[LocalStreamController] Requesting display media for screen sharing with audio:", Boolean(this.options.inputAudioDeviceConstraints));
14992
15248
  stream = await this.options.getDisplayMedia({
14993
15249
  video: true,
14994
15250
  audio: Boolean(this.options.inputAudioDeviceConstraints)
14995
15251
  });
14996
- logger$17.debug("[LocalStreamController] Screen share media obtained:", stream);
15252
+ logger$18.debug("[LocalStreamController] Screen share media obtained:", stream);
14997
15253
  } else {
14998
15254
  const constraints = {
14999
15255
  audio: this.options.inputAudioDeviceConstraints,
15000
15256
  video: this.options.inputVideoDeviceConstraints
15001
15257
  };
15002
- logger$17.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15258
+ logger$18.debug("[LocalStreamController] Requesting user media with constraints:", constraints);
15003
15259
  stream = await this.options.getUserMedia(constraints);
15004
- logger$17.debug("[LocalStreamController] User media obtained:", stream);
15260
+ logger$18.debug("[LocalStreamController] User media obtained:", stream);
15005
15261
  }
15006
15262
  this._localStream$.next(stream);
15007
15263
  return stream;
@@ -15018,7 +15274,7 @@ var LocalStreamController = class extends Destroyable {
15018
15274
  this._localStream$.next(localStream);
15019
15275
  if (track.kind === "video") this._localVideoTracks$.next(localStream.getVideoTracks());
15020
15276
  else this._localAudioTracks$.next(localStream.getAudioTracks());
15021
- logger$17.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15277
+ logger$18.debug(`[LocalStreamController] ${track.kind} track added:`, track.id);
15022
15278
  return localStream;
15023
15279
  }
15024
15280
  /**
@@ -15030,7 +15286,7 @@ var LocalStreamController = class extends Destroyable {
15030
15286
  const stream = this._localStream$.value;
15031
15287
  const track = stream?.getTracks().find((t) => t.id === trackId);
15032
15288
  if (!track) {
15033
- logger$17.debug(`[LocalStreamController] track not found: ${trackId}`);
15289
+ logger$18.debug(`[LocalStreamController] track not found: ${trackId}`);
15034
15290
  return;
15035
15291
  }
15036
15292
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
@@ -15039,7 +15295,7 @@ var LocalStreamController = class extends Destroyable {
15039
15295
  this._localStream$.next(stream);
15040
15296
  if (track.kind === "video") this._localVideoTracks$.next(stream?.getVideoTracks() ?? []);
15041
15297
  else this._localAudioTracks$.next(stream?.getAudioTracks() ?? []);
15042
- logger$17.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15298
+ logger$18.debug(`[LocalStreamController] ${track.kind} track removed:`, trackId);
15043
15299
  return track;
15044
15300
  }
15045
15301
  /**
@@ -15074,7 +15330,7 @@ var LocalStreamController = class extends Destroyable {
15074
15330
  */
15075
15331
  stopAllTracks() {
15076
15332
  this._localStream$.value?.getTracks().forEach((track) => {
15077
- logger$17.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15333
+ logger$18.debug(`[LocalStreamController] Stopping local track: ${track.kind}`);
15078
15334
  track.removeEventListener("ended", this.mediaTrackEndedHandler);
15079
15335
  track.stop();
15080
15336
  });
@@ -15090,7 +15346,7 @@ var LocalStreamController = class extends Destroyable {
15090
15346
 
15091
15347
  //#endregion
15092
15348
  //#region src/controllers/TransceiverController.ts
15093
- const logger$16 = getLogger();
15349
+ const logger$17 = getLogger();
15094
15350
  const getDirection = (send, recv) => {
15095
15351
  if (send && recv) return "sendrecv";
15096
15352
  else if (send && !recv) return "sendonly";
@@ -15192,7 +15448,7 @@ var TransceiverController = class extends Destroyable {
15192
15448
  sendEncodings: isAudio ? void 0 : this.sendEncodings,
15193
15449
  streams: direction === "recvonly" ? void 0 : [localStream]
15194
15450
  };
15195
- logger$16.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15451
+ logger$17.debug(`[TransceiverController] Setting up transceiver sender for local ${track.kind} track:`, {
15196
15452
  transceiver,
15197
15453
  transceiverParams
15198
15454
  });
@@ -15200,11 +15456,11 @@ var TransceiverController = class extends Destroyable {
15200
15456
  await transceiver.sender.replaceTrack(track);
15201
15457
  transceiver.direction = transceiverParams.direction;
15202
15458
  if (transceiverParams.streams?.some((stream) => Boolean(stream))) {
15203
- logger$16.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15459
+ logger$17.debug(`[TransceiverController] Setting streams for transceiver sender for local ${track.kind} track:`, transceiverParams.streams);
15204
15460
  transceiver.sender.setStreams(...transceiverParams.streams);
15205
15461
  }
15206
15462
  } else {
15207
- logger$16.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15463
+ logger$17.debug(`[TransceiverController] Adding new transceiver for local ${track.kind} track:`, track.id);
15208
15464
  this.peerConnection.addTransceiver(track, transceiverParams);
15209
15465
  }
15210
15466
  }
@@ -15218,13 +15474,13 @@ var TransceiverController = class extends Destroyable {
15218
15474
  if (options.updateTransceiverDirection) transceiver.direction = "inactive";
15219
15475
  }
15220
15476
  } catch (error) {
15221
- logger$16.error("[TransceiverController] stopTrackSender error", kind, error);
15477
+ logger$17.error("[TransceiverController] stopTrackSender error", kind, error);
15222
15478
  this.options.onError?.(new MediaTrackError("stopTrackSender", kind, error));
15223
15479
  }
15224
15480
  }
15225
15481
  async restoreTrackSender(kind) {
15226
15482
  try {
15227
- logger$16.debug("[TransceiverController] restoreTrackSender called", kind);
15483
+ logger$17.debug("[TransceiverController] restoreTrackSender called", kind);
15228
15484
  const constraints = {};
15229
15485
  const transceivers = this.transceiverByKind(kind);
15230
15486
  for (const transceiver of transceivers) {
@@ -15234,23 +15490,23 @@ var TransceiverController = class extends Destroyable {
15234
15490
  if (trackKind === "audio" || trackKind === "video") constraints[trackKind] = this.getConstraintsFor(trackKind);
15235
15491
  }
15236
15492
  }
15237
- logger$16.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15493
+ logger$17.debug("[TransceiverController] restoreTrackSender constraints:", constraints);
15238
15494
  if (Object.keys(constraints).length === 0) {
15239
- logger$16.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15495
+ logger$17.warn("[TransceiverController] restoreTrackSender: no tracks need restoration", kind);
15240
15496
  return;
15241
15497
  }
15242
15498
  const newTracks = (await this.options.getUserMedia(constraints)).getTracks();
15243
- logger$16.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15499
+ logger$17.debug("[TransceiverController] restoreTrackSender new tracks:", newTracks);
15244
15500
  for (const newTrack of newTracks) {
15245
15501
  this.options.localStreamController.addTrack(newTrack);
15246
15502
  const trackKind = newTrack.kind;
15247
15503
  const transceiverOfKind = this.transceiverByKind(trackKind)[0];
15248
15504
  transceiverOfKind.direction = trackKind === "audio" ? this.audioDirection : this.videoDirection;
15249
- logger$16.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15505
+ logger$17.debug("[TransceiverController] restoreTrackSender setting direction for", trackKind, transceiverOfKind.direction);
15250
15506
  await transceiverOfKind.sender.replaceTrack(newTrack);
15251
15507
  }
15252
15508
  } catch (error) {
15253
- logger$16.error("[TransceiverController] restoreTrackSender error", kind, error);
15509
+ logger$17.error("[TransceiverController] restoreTrackSender error", kind, error);
15254
15510
  this.options.onError?.(new MediaTrackError("restoreTrackSender", kind, error));
15255
15511
  }
15256
15512
  }
@@ -15291,14 +15547,14 @@ var TransceiverController = class extends Destroyable {
15291
15547
  };
15292
15548
  try {
15293
15549
  await track.applyConstraints(constraintsToApply);
15294
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15295
- logger$16.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15550
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, constraintsToApply);
15551
+ logger$17.debug(`[TransceiverController] Updated ${kind} sender constraints:`, track.getConstraints());
15296
15552
  } catch (error) {
15297
- logger$16.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15553
+ logger$17.warn(`[TransceiverController] applyConstraints failed for ${kind} track ${track.id}, attempting track replacement fallback:`, error);
15298
15554
  try {
15299
15555
  await this.replaceTrackFallback(sender, track, kind, constraintsToApply);
15300
15556
  } catch (fallbackError) {
15301
- logger$16.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15557
+ logger$17.warn(`[TransceiverController] Track replacement fallback also failed for ${kind} track:`, fallbackError);
15302
15558
  this.options.onError?.(new MediaTrackError("updateSendersConstraints", kind, fallbackError));
15303
15559
  }
15304
15560
  }
@@ -15326,7 +15582,7 @@ var TransceiverController = class extends Destroyable {
15326
15582
  if (!newTrack) throw new MediaTrackError("replaceTrackFallback", kind, /* @__PURE__ */ new Error("getUserMedia returned no track of the requested kind"));
15327
15583
  await sender.replaceTrack(newTrack);
15328
15584
  this.options.localStreamController.addTrack(newTrack);
15329
- logger$16.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15585
+ logger$17.debug(`[TransceiverController] Track replacement fallback succeeded for ${kind}. New track: ${newTrack.id}`);
15330
15586
  }
15331
15587
  getMediaDirections() {
15332
15588
  if (this.peerConnection.connectionState === "connected") return this.peerConnection.getTransceivers().reduce((acc, transceiver) => {
@@ -15356,60 +15612,60 @@ var TransceiverController = class extends Destroyable {
15356
15612
 
15357
15613
  //#endregion
15358
15614
  //#region src/controllers/RTCPeerConnectionController.ts
15359
- var import_cjs$15 = require_cjs();
15360
- const logger$15 = getLogger();
15615
+ var import_cjs$16 = require_cjs();
15616
+ const logger$16 = getLogger();
15361
15617
  var RTCPeerConnectionController = class extends Destroyable {
15362
15618
  constructor(options = {}, remoteSessionDescription, deviceController) {
15363
15619
  super();
15364
15620
  this.options = options;
15365
15621
  this.firstSDPExchangeCompleted = false;
15366
15622
  this.negotiationNeeded$ = this.createSubject();
15367
- 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)(() => {
15623
+ 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)(() => {
15368
15624
  this.negotiationEnded();
15369
- }), (0, import_cjs$15.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$15.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$15.tap)((desc) => {
15625
+ }), (0, import_cjs$16.filter)(() => this.shouldEmitLocalDescription), (0, import_cjs$16.map)(() => this.peerConnection?.localDescription), filterNull(), (0, import_cjs$16.tap)((desc) => {
15370
15626
  if (desc.type === "answer") this._type = "offer";
15371
- }))), (0, import_cjs$15.shareReplay)(1), (0, import_cjs$15.takeUntil)(this.destroyed$));
15627
+ }))), (0, import_cjs$16.shareReplay)(1), (0, import_cjs$16.takeUntil)(this.destroyed$));
15372
15628
  this.connectionTimeout = 3e3;
15373
15629
  this.oniceconnectionstatechangeHandler = () => {
15374
15630
  if (this.peerConnection) {
15375
15631
  const { iceConnectionState } = this.peerConnection;
15376
- logger$15.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15632
+ logger$16.debug(`[RTCPeerConnectionController] ICE connection state changed to: ${iceConnectionState}`);
15377
15633
  this._iceConnectionState$.next(this.peerConnection.iceConnectionState);
15378
15634
  }
15379
15635
  };
15380
15636
  this.onconnectionstatechangeHandler = () => {
15381
15637
  if (this.peerConnection) {
15382
15638
  const { connectionState } = this.peerConnection;
15383
- logger$15.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15639
+ logger$16.debug(`[RTCPeerConnectionController] Connection state changed to: ${connectionState}`);
15384
15640
  if (connectionState === "connected") this.removeConnectionTimer();
15385
15641
  this._connectionState$.next(this.peerConnection.connectionState);
15386
15642
  }
15387
15643
  };
15388
15644
  this.onsignalingstatechangeHandler = () => {
15389
- logger$15.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15645
+ logger$16.debug(`[RTCPeerConnectionController] Signaling state changed to: ${this.peerConnection?.signalingState}`);
15390
15646
  };
15391
15647
  this.onicegatheringstatechangeHandler = () => {
15392
15648
  if (this.peerConnection) this._iceGatheringState$.next(this.peerConnection.iceGatheringState);
15393
15649
  };
15394
15650
  this.onnegotiationneededHandler = (event) => {
15395
- logger$15.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15651
+ logger$16.debug("[RTCPeerConnectionController] Negotiation needed event received.", event);
15396
15652
  this.negotiationNeeded$.next();
15397
15653
  };
15398
15654
  this.updateSelectedInputDevice = async (kind, deviceInfo) => {
15399
15655
  try {
15400
15656
  const { localStream } = this;
15401
15657
  if (!localStream) {
15402
- logger$15.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15658
+ logger$16.warn("[RTCPeerConnectionController] No local stream available to update input device.");
15403
15659
  return;
15404
15660
  }
15405
- logger$15.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15661
+ logger$16.debug(`[RTCPeerConnectionController] Updating selected ${kind} input device:`, localStream.getTracks());
15406
15662
  const track = localStream.getTracks().find((track$1) => track$1.kind === kind);
15407
15663
  if (track) {
15408
15664
  this.transceiverController?.stopTrackSender(kind);
15409
- this.localStream?.removeTrack(track);
15410
- logger$15.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15665
+ this.localStreamController.removeTrack(track.id);
15666
+ logger$16.debug(`[RTCPeerConnectionController] Stopped existing ${kind} track: ${track.id}`, localStream.getTracks());
15411
15667
  if (!deviceInfo) {
15412
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15668
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
15413
15669
  return;
15414
15670
  }
15415
15671
  const streamTrack = (await this.getUserMedia({ [kind]: {
@@ -15417,15 +15673,15 @@ var RTCPeerConnectionController = class extends Destroyable {
15417
15673
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
15418
15674
  } })).getTracks().find((t) => t.kind === kind);
15419
15675
  if (streamTrack) {
15420
- logger$15.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15421
- this.localStream?.addTrack(streamTrack);
15676
+ logger$16.debug(`[RTCPeerConnectionController] Adding new ${kind} track: ${streamTrack.id}`);
15677
+ this.localStreamController.addTrack(streamTrack);
15422
15678
  await this.transceiverController?.replaceSenderTrack(kind, streamTrack);
15423
- logger$15.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15679
+ logger$16.debug(`[RTCPeerConnectionController] Added new ${kind} track: ${streamTrack.id}`, this.localStream?.getTracks());
15424
15680
  }
15425
15681
  }
15426
- logger$15.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15682
+ logger$16.debug(`[RTCPeerConnectionController] ${kind} input device selected:`, deviceInfo?.label);
15427
15683
  } catch (error) {
15428
- logger$15.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15684
+ logger$16.error(`[RTCPeerConnectionController] Failed to select ${kind} input device:`, error);
15429
15685
  this._errors$.next(toError(error));
15430
15686
  throw error;
15431
15687
  }
@@ -15442,6 +15698,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15442
15698
  this._remoteDescription$ = this.createReplaySubject(1);
15443
15699
  this._remoteStream$ = this.createBehaviorSubject(null);
15444
15700
  this._remoteOfferMediaDirections = null;
15701
+ this._localAudioPipeline = null;
15445
15702
  this.deviceController = deviceController ?? {};
15446
15703
  this.id = options.callId ?? v4_default();
15447
15704
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -15511,43 +15768,43 @@ var RTCPeerConnectionController = class extends Destroyable {
15511
15768
  };
15512
15769
  }
15513
15770
  get iceGatheringState$() {
15514
- return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15771
+ return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15515
15772
  }
15516
15773
  get mediaTrackEnded$() {
15517
- return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15774
+ return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15518
15775
  }
15519
15776
  get errors$() {
15520
- return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15777
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15521
15778
  }
15522
15779
  get iceCandidates$() {
15523
- return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15780
+ return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15524
15781
  }
15525
15782
  get initialized$() {
15526
- return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$15.filter)((initialized) => initialized), (0, import_cjs$15.takeUntil)(this.destroyed$)));
15783
+ return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$16.filter)((initialized) => initialized), (0, import_cjs$16.takeUntil)(this.destroyed$)));
15527
15784
  }
15528
15785
  get remoteDescription$() {
15529
- return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15786
+ return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15530
15787
  }
15531
15788
  get localStream$() {
15532
- return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15789
+ return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15533
15790
  }
15534
15791
  get remoteStream$() {
15535
- return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15792
+ return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15536
15793
  }
15537
15794
  get localAudioTracks$() {
15538
- return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15795
+ return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15539
15796
  }
15540
15797
  get localVideoTracks$() {
15541
- return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15798
+ return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15542
15799
  }
15543
15800
  get iceConnectionState$() {
15544
- return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15801
+ return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15545
15802
  }
15546
15803
  get connectionState$() {
15547
- return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15804
+ return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15548
15805
  }
15549
15806
  get signalingState$() {
15550
- return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$15.takeUntil)(this.destroyed$)));
15807
+ return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$16.takeUntil)(this.destroyed$)));
15551
15808
  }
15552
15809
  get type() {
15553
15810
  return this._type;
@@ -15660,17 +15917,17 @@ var RTCPeerConnectionController = class extends Destroyable {
15660
15917
  async doInit() {
15661
15918
  try {
15662
15919
  this.setupPeerConnection();
15663
- this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$15.auditTime)(0), (0, import_cjs$15.exhaustMap)(async () => this.startNegotiation())), {
15920
+ this.subscribeTo(this.negotiationNeeded$.pipe((0, import_cjs$16.auditTime)(0), (0, import_cjs$16.exhaustMap)(async () => this.startNegotiation())), {
15664
15921
  next: () => {
15665
- logger$15.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15922
+ logger$16.debug("[RTCPeerConnectionController] Start Negotiation completed successfully");
15666
15923
  },
15667
15924
  error: (error) => {
15668
- logger$15.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15925
+ logger$16.error("[RTCPeerConnectionController] Start Negotiation error:", error);
15669
15926
  this._errors$.next(toError(error));
15670
15927
  }
15671
15928
  });
15672
- 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]) => {
15673
- logger$15.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15929
+ 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]) => {
15930
+ logger$16.debug(`[RTCPeerConnectionController] Selected input device changed for:`, {
15674
15931
  kind,
15675
15932
  deviceInfo
15676
15933
  });
@@ -15687,7 +15944,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15687
15944
  this._initialized$.next(true);
15688
15945
  }
15689
15946
  } catch (error) {
15690
- logger$15.error("[RTCPeerConnectionController] Initialization error:", error);
15947
+ logger$16.error("[RTCPeerConnectionController] Initialization error:", error);
15691
15948
  this._errors$.next(toError(error));
15692
15949
  this.destroy();
15693
15950
  }
@@ -15719,22 +15976,22 @@ var RTCPeerConnectionController = class extends Destroyable {
15719
15976
  }
15720
15977
  async startNegotiation() {
15721
15978
  if (this.isNegotiating) {
15722
- logger$15.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15979
+ logger$16.debug("[RTCPeerConnectionController] Negotiation already in progress, skipping.");
15723
15980
  return;
15724
15981
  }
15725
15982
  this.setupEventListeners();
15726
15983
  if (this.type === "answer") {
15727
- logger$15.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15984
+ logger$16.debug("[RTCPeerConnectionController] This is an answer type still, skipping offer creation.");
15728
15985
  return;
15729
15986
  }
15730
15987
  this._isNegotiating$.next(true);
15731
- logger$15.debug("[RTCPeerConnectionController] Starting negotiation.");
15988
+ logger$16.debug("[RTCPeerConnectionController] Starting negotiation.");
15732
15989
  try {
15733
15990
  const { offerOptions } = this;
15734
- logger$15.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15991
+ logger$16.debug("[RTCPeerConnectionController] Creating offer with options:", offerOptions);
15735
15992
  await this.createOffer(offerOptions);
15736
15993
  } catch (error) {
15737
- logger$15.error("[RTCPeerConnectionController] Error during negotiation:", error);
15994
+ logger$16.error("[RTCPeerConnectionController] Error during negotiation:", error);
15738
15995
  this._errors$.next(toError(error));
15739
15996
  }
15740
15997
  }
@@ -15750,14 +16007,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15750
16007
  let readyToConnect = status !== "failed";
15751
16008
  try {
15752
16009
  if (status === "received" && sdp) {
15753
- logger$15.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
16010
+ logger$16.debug("[RTCPeerConnectionController] Received answer SDP:", sdp);
15754
16011
  await this._setRemoteDescription({
15755
16012
  type: "answer",
15756
16013
  sdp
15757
16014
  });
15758
16015
  }
15759
16016
  } catch (error) {
15760
- logger$15.error("[RTCPeerConnectionController] Error updating answer status:", error);
16017
+ logger$16.error("[RTCPeerConnectionController] Error updating answer status:", error);
15761
16018
  this._errors$.next(toError(error));
15762
16019
  readyToConnect = false;
15763
16020
  } finally {
@@ -15776,7 +16033,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15776
16033
  await this.handleOfferReceived();
15777
16034
  break;
15778
16035
  case "failed":
15779
- logger$15.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
16036
+ logger$16.error("[RTCPeerConnectionController] Offer failed to be processed by remote.");
15780
16037
  break;
15781
16038
  case "sent":
15782
16039
  default:
@@ -15808,7 +16065,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15808
16065
  }
15809
16066
  await this.setupLocalTracks();
15810
16067
  const { answerOptions } = this;
15811
- logger$15.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
16068
+ logger$16.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
15812
16069
  await this.createAnswer(answerOptions);
15813
16070
  }
15814
16071
  async handleOfferReceived() {
@@ -15816,7 +16073,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15816
16073
  this._isNegotiating$.next(true);
15817
16074
  await this._setRemoteDescription(this.sdpInit);
15818
16075
  const { answerOptions } = this;
15819
- logger$15.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
16076
+ logger$16.debug("[RTCPeerConnectionController] Creating answer with options:", answerOptions);
15820
16077
  await this.createAnswer(answerOptions);
15821
16078
  }
15822
16079
  readyToConnect() {
@@ -15824,7 +16081,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15824
16081
  this.connectionTimer = setTimeout(() => {
15825
16082
  this.removeConnectionTimer();
15826
16083
  if (this.peerConnection?.connectionState !== "connected") {
15827
- logger$15.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
16084
+ logger$16.debug("[RTCPeerConnectionController] Connection timeout, restarting ICE gathering with relay only.");
15828
16085
  this.iceGatheringController.restartICEGatheringWithRelayOnly();
15829
16086
  }
15830
16087
  }, this.connectionTimeout);
@@ -15846,14 +16103,14 @@ var RTCPeerConnectionController = class extends Destroyable {
15846
16103
  const stereo = this.options.stereo ?? PreferencesContainer.instance.stereoAudio;
15847
16104
  if (preferredAudioCodecs.length > 0 || preferredVideoCodecs.length > 0) {
15848
16105
  result = setCodecPreferences(result, preferredAudioCodecs, preferredVideoCodecs);
15849
- logger$15.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
16106
+ logger$16.debug("[RTCPeerConnectionController] Applied codec preferences to SDP", {
15850
16107
  preferredAudioCodecs,
15851
16108
  preferredVideoCodecs
15852
16109
  });
15853
16110
  }
15854
16111
  if (stereo) {
15855
16112
  result = enableStereoOpus(result);
15856
- logger$15.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
16113
+ logger$16.debug("[RTCPeerConnectionController] Applied stereo Opus to SDP");
15857
16114
  }
15858
16115
  return Promise.resolve(result);
15859
16116
  }
@@ -15887,9 +16144,6 @@ var RTCPeerConnectionController = class extends Destroyable {
15887
16144
  negotiationEnded() {
15888
16145
  this._isNegotiating$.next(false);
15889
16146
  }
15890
- restarIce() {
15891
- this.peerConnection?.restartIce();
15892
- }
15893
16147
  /**
15894
16148
  * Trigger an ICE restart through the existing negotiation pipeline.
15895
16149
  *
@@ -15912,24 +16166,27 @@ var RTCPeerConnectionController = class extends Destroyable {
15912
16166
  ...this.peerConnection.getConfiguration(),
15913
16167
  iceTransportPolicy: "relay"
15914
16168
  });
15915
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
16169
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy set to relay-only");
15916
16170
  } catch (error) {
15917
- logger$15.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
16171
+ logger$16.warn("[RTCPeerConnectionController] Failed to set relay-only policy:", error);
15918
16172
  }
15919
16173
  this.setupEventListeners();
15920
16174
  this._isNegotiating$.next(true);
15921
- logger$15.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
16175
+ logger$16.debug(`[RTCPeerConnectionController] Triggering ICE restart${relayOnly ? " (relay-only)" : ""}.`);
15922
16176
  try {
15923
16177
  const offer = await this.peerConnection.createOffer({ iceRestart: true });
15924
16178
  await this.setLocalDescription(offer);
15925
16179
  } catch (error) {
15926
- logger$15.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
16180
+ logger$16.error("[RTCPeerConnectionController] ICE restart offer failed:", error);
15927
16181
  this._errors$.next(toError(error));
15928
16182
  this.negotiationEnded();
15929
16183
  if (policyChanged) this.restoreIceTransportPolicy();
15930
16184
  throw error;
15931
16185
  }
15932
- if (policyChanged) this.restoreIceTransportPolicy();
16186
+ 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) => {
16187
+ logger$16.warn("[RTCPeerConnectionController] Error waiting for ICE gathering to complete:", error);
16188
+ this.restoreIceTransportPolicy();
16189
+ });
15933
16190
  }
15934
16191
  restoreIceTransportPolicy() {
15935
16192
  try {
@@ -15937,9 +16194,9 @@ var RTCPeerConnectionController = class extends Destroyable {
15937
16194
  ...this.peerConnection.getConfiguration(),
15938
16195
  iceTransportPolicy: this.options.relayOnly ? "relay" : "all"
15939
16196
  });
15940
- logger$15.debug("[RTCPeerConnectionController] ICE transport policy restored");
16197
+ logger$16.debug("[RTCPeerConnectionController] ICE transport policy restored");
15941
16198
  } catch (error) {
15942
- logger$15.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
16199
+ logger$16.warn("[RTCPeerConnectionController] Failed to restore ICE transport policy:", error);
15943
16200
  }
15944
16201
  }
15945
16202
  /**
@@ -15951,13 +16208,13 @@ var RTCPeerConnectionController = class extends Destroyable {
15951
16208
  await this.setupRemoteTracks();
15952
16209
  }
15953
16210
  async setupLocalTracks() {
15954
- logger$15.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
16211
+ logger$16.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
15955
16212
  const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
15956
16213
  if (this.transceiverController?.useAddStream ?? false) {
15957
- logger$15.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
16214
+ logger$16.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
15958
16215
  this.peerConnection?.addStream(localStream);
15959
16216
  if (!this.isNegotiating) {
15960
- logger$15.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
16217
+ logger$16.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
15961
16218
  this.negotiationNeeded$.next();
15962
16219
  }
15963
16220
  return;
@@ -15973,7 +16230,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15973
16230
  const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
15974
16231
  await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
15975
16232
  } else {
15976
- logger$15.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
16233
+ logger$16.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
15977
16234
  this.peerConnection?.addTrack(track, localStream);
15978
16235
  }
15979
16236
  }
@@ -15990,7 +16247,7 @@ var RTCPeerConnectionController = class extends Destroyable {
15990
16247
  async setupRemoteTracks() {
15991
16248
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
15992
16249
  this.peerConnection.ontrack = (event) => {
15993
- logger$15.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
16250
+ logger$16.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
15994
16251
  if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
15995
16252
  else {
15996
16253
  const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
@@ -16002,6 +16259,45 @@ var RTCPeerConnectionController = class extends Destroyable {
16002
16259
  }
16003
16260
  async restoreTrackSender(kind) {
16004
16261
  await this.transceiverController?.restoreTrackSender(kind);
16262
+ if (kind !== "video" && this._localAudioPipeline) await this.applyLocalAudioPipelineToSender();
16263
+ }
16264
+ /**
16265
+ * Return the lazily-created {@link LocalAudioPipeline}, constructing it on
16266
+ * first access. On creation the current audio sender's track is routed
16267
+ * through the pipeline (input → gain → analyser → destination) and the
16268
+ * sender is switched to emit the processed track. Returns `null` when no
16269
+ * audio sender exists yet (pre-negotiation).
16270
+ */
16271
+ ensureLocalAudioPipeline() {
16272
+ if (this._localAudioPipeline) return this._localAudioPipeline;
16273
+ if (!this.peerConnection) return null;
16274
+ try {
16275
+ this._localAudioPipeline = new LocalAudioPipeline();
16276
+ } catch (error) {
16277
+ logger$16.warn("[RTCPeerConnectionController] Failed to create LocalAudioPipeline:", error);
16278
+ return null;
16279
+ }
16280
+ this.subscribeTo(this.localStreamController.localAudioTracks$, () => {
16281
+ this.applyLocalAudioPipelineToSender();
16282
+ });
16283
+ this.applyLocalAudioPipelineToSender();
16284
+ return this._localAudioPipeline;
16285
+ }
16286
+ /** The active LocalAudioPipeline, or null if it hasn't been created yet. */
16287
+ get localAudioPipeline() {
16288
+ return this._localAudioPipeline;
16289
+ }
16290
+ async applyLocalAudioPipelineToSender() {
16291
+ if (!this._localAudioPipeline || !this.peerConnection) return;
16292
+ const raw = this.localStreamController.localAudioTracks.at(0);
16293
+ this._localAudioPipeline.setInputTrack(raw ?? null);
16294
+ const sender = (this.transceiverController?.audioTransceivers.at(0))?.sender ?? this.peerConnection.getSenders().find((s) => s.track?.kind === "audio");
16295
+ if (!sender || !raw) return;
16296
+ try {
16297
+ await sender.replaceTrack(this._localAudioPipeline.outputTrack);
16298
+ } catch (error) {
16299
+ logger$16.warn("[RTCPeerConnectionController] Failed to route audio sender through pipeline:", error);
16300
+ }
16005
16301
  }
16006
16302
  /**
16007
16303
  * Add a local media track to the peer connection.
@@ -16016,9 +16312,9 @@ var RTCPeerConnectionController = class extends Destroyable {
16016
16312
  try {
16017
16313
  const localStream = this.localStreamController.addTrack(track);
16018
16314
  this.peerConnection.addTrack(track, localStream);
16019
- logger$15.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16315
+ logger$16.debug(`[RTCPeerConnectionController] ${track.kind} track added:`, track.id);
16020
16316
  } catch (error) {
16021
- logger$15.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16317
+ logger$16.error(`[RTCPeerConnectionController] Failed to add ${track.kind} track:`, error);
16022
16318
  this._errors$.next(toError(error));
16023
16319
  throw error;
16024
16320
  }
@@ -16035,15 +16331,15 @@ var RTCPeerConnectionController = class extends Destroyable {
16035
16331
  }
16036
16332
  const sender = this.peerConnection.getSenders().find((sender$1) => sender$1.track?.id === trackId);
16037
16333
  if (!sender) {
16038
- logger$15.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16334
+ logger$16.debug(`[RTCPeerConnectionController] track not found: ${trackId}`);
16039
16335
  return;
16040
16336
  }
16041
16337
  try {
16042
16338
  this.peerConnection.removeTrack(sender);
16043
16339
  this.localStreamController.removeTrack(trackId);
16044
- logger$15.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16340
+ logger$16.debug(`[RTCPeerConnectionController] ${sender.track?.kind} track removed:`, trackId);
16045
16341
  } catch (error) {
16046
- logger$15.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16342
+ logger$16.error(`[RTCPeerConnectionController] Failed to remove ${sender.track?.kind} track:`, error);
16047
16343
  this._errors$.next(toError(error));
16048
16344
  throw error;
16049
16345
  }
@@ -16070,7 +16366,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16070
16366
  async replaceAudioTrackWithConstraints(constraints) {
16071
16367
  const senders = this.peerConnection?.getSenders().filter((s) => s.track?.kind === "audio" && s.track.readyState === "live");
16072
16368
  if (!senders || senders.length === 0) {
16073
- logger$15.warn("[RTCPeerConnectionController] No live audio sender to replace");
16369
+ logger$16.warn("[RTCPeerConnectionController] No live audio sender to replace");
16074
16370
  return;
16075
16371
  }
16076
16372
  for (const sender of senders) {
@@ -16088,7 +16384,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16088
16384
  const newTrack = (await this.getUserMedia({ audio: mergedConstraints })).getAudioTracks()[0];
16089
16385
  await sender.replaceTrack(newTrack);
16090
16386
  this.localStreamController.addTrack(newTrack);
16091
- logger$15.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16387
+ logger$16.debug(`[RTCPeerConnectionController] Audio track replaced for server-pushed params. New track: ${newTrack.id}`);
16092
16388
  }
16093
16389
  }
16094
16390
  /**
@@ -16096,9 +16392,11 @@ var RTCPeerConnectionController = class extends Destroyable {
16096
16392
  * Completes all observables to prevent memory leaks.
16097
16393
  */
16098
16394
  destroy() {
16099
- logger$15.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16395
+ logger$16.debug(`[RTCPeerConnectionController] Destroying RTCPeerConnectionController. ${this.propose}`);
16100
16396
  this.removeConnectionTimer();
16101
16397
  this._iceGatheringController?.destroy();
16398
+ this._localAudioPipeline?.destroy();
16399
+ this._localAudioPipeline = null;
16102
16400
  this.localStreamController.destroy();
16103
16401
  this.transceiverController?.destroy();
16104
16402
  if (this.peerConnection) {
@@ -16120,7 +16418,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16120
16418
  }
16121
16419
  stopRemoteTracks() {
16122
16420
  this._remoteStream$.value?.getTracks().forEach((track) => {
16123
- logger$15.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16421
+ logger$16.debug(`[RTCPeerConnectionController] Stopping remote track: ${track.kind}`);
16124
16422
  track.stop();
16125
16423
  });
16126
16424
  }
@@ -16137,7 +16435,7 @@ var RTCPeerConnectionController = class extends Destroyable {
16137
16435
  ...params,
16138
16436
  sdp: finalRemote
16139
16437
  };
16140
- logger$15.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16438
+ logger$16.debug("[RTCPeerConnectionController] Setting remote description:", answer);
16141
16439
  return this.peerConnection.setRemoteDescription(answer);
16142
16440
  }
16143
16441
  };
@@ -16175,8 +16473,25 @@ function isVertoPingInnerParams(value) {
16175
16473
 
16176
16474
  //#endregion
16177
16475
  //#region src/managers/VertoManager.ts
16178
- var import_cjs$14 = require_cjs();
16179
- const logger$14 = getLogger();
16476
+ var import_cjs$15 = require_cjs();
16477
+ const logger$15 = getLogger();
16478
+ /**
16479
+ * Decide what value goes on the `node_id` field of a `webrtc.verto` envelope.
16480
+ *
16481
+ * - **Reattach invite:** must carry the persisted nodeId so the server routes
16482
+ * the new connection to the FreeSWITCH instance that holds the existing call.
16483
+ * - **Fresh invite, caller-supplied `CallOptions.nodeId`:** carry the explicit
16484
+ * value as a steering hint (dev/staging traffic pinning). Server may honour
16485
+ * or ignore for placement reasons.
16486
+ * - **Fresh invite, no caller nodeId:** strip to `''` = "server picks".
16487
+ * - **Non-invite frames** (verto.modify, verto.bye, etc.): always carry the
16488
+ * current `_nodeId$.value` so the frame targets the node hosting the call.
16489
+ *
16490
+ * Pure function — exported for unit testing.
16491
+ */
16492
+ function resolveInviteNodeId(args) {
16493
+ return args.isInvite && !args.reattach && !args.explicitNodeId ? "" : args.currentNodeId ?? "";
16494
+ }
16180
16495
  var VertoManager = class extends Destroyable {
16181
16496
  constructor(callSession) {
16182
16497
  super();
@@ -16215,7 +16530,7 @@ var WebRTCVertoManager = class extends VertoManager {
16215
16530
  try {
16216
16531
  await this.executeVerto(vertoModifyMessage);
16217
16532
  } catch (error) {
16218
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16533
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto hold:", error);
16219
16534
  throw error;
16220
16535
  }
16221
16536
  }
@@ -16228,7 +16543,7 @@ var WebRTCVertoManager = class extends VertoManager {
16228
16543
  try {
16229
16544
  await this.executeVerto(vertoModifyMessage);
16230
16545
  } catch (error) {
16231
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16546
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto unhold:", error);
16232
16547
  throw error;
16233
16548
  }
16234
16549
  }
@@ -16268,7 +16583,7 @@ var WebRTCVertoManager = class extends VertoManager {
16268
16583
  return rtcPeerConnection;
16269
16584
  }
16270
16585
  get signalingStatus$() {
16271
- return this.cachedObservable("signalingStatus$", () => (0, import_cjs$14.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$14.filter)((connectionState) => [
16586
+ return this.cachedObservable("signalingStatus$", () => (0, import_cjs$15.merge)(this._signalingStatus$.asObservable(), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$15.filter)((connectionState) => [
16272
16587
  "connected",
16273
16588
  "disconnected",
16274
16589
  "failed"
@@ -16281,7 +16596,7 @@ var WebRTCVertoManager = class extends VertoManager {
16281
16596
  if (event.member_id) this.setSelfIdIfNull(event.member_id);
16282
16597
  });
16283
16598
  this.subscribeTo(this.vertoMedia$, (event) => {
16284
- logger$14.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16599
+ logger$15.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
16285
16600
  this._signalingStatus$.next("ringing");
16286
16601
  const { sdp, callID } = event;
16287
16602
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16290,7 +16605,7 @@ var WebRTCVertoManager = class extends VertoManager {
16290
16605
  });
16291
16606
  });
16292
16607
  this.subscribeTo(this.vertoAnswer$, (event) => {
16293
- logger$14.debug("[WebRTCManager] Received Verto answer event:", event);
16608
+ logger$15.debug("[WebRTCManager] Received Verto answer event:", event);
16294
16609
  this._signalingStatus$.next("connecting");
16295
16610
  const { sdp, callID } = event;
16296
16611
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
@@ -16299,7 +16614,7 @@ var WebRTCVertoManager = class extends VertoManager {
16299
16614
  });
16300
16615
  });
16301
16616
  this.subscribeTo(this.vertoMediaParams$, (event) => {
16302
- logger$14.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16617
+ logger$15.debug("[WebRTCManager] Received Verto mediaParams event:", event);
16303
16618
  const { mediaParams, callID } = event;
16304
16619
  const rtcPeerConnController = this._rtcPeerConnectionsMap.get(callID);
16305
16620
  const { audio, video } = mediaParams;
@@ -16313,7 +16628,7 @@ var WebRTCVertoManager = class extends VertoManager {
16313
16628
  timestamp: Date.now()
16314
16629
  });
16315
16630
  } catch (error) {
16316
- logger$14.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16631
+ logger$15.warn("[WebRTCManager] Error applying server-pushed media params:", error);
16317
16632
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16318
16633
  }
16319
16634
  })();
@@ -16335,13 +16650,13 @@ var WebRTCVertoManager = class extends VertoManager {
16335
16650
  */
16336
16651
  setNodeIdIfNull(nodeId) {
16337
16652
  if (!this._nodeId$.value && nodeId) {
16338
- logger$14.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16653
+ logger$15.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
16339
16654
  this._nodeId$.next(nodeId);
16340
16655
  }
16341
16656
  }
16342
16657
  setSelfIdIfNull(selfId) {
16343
16658
  if (!this._selfId$.value && selfId) {
16344
- logger$14.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16659
+ logger$15.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
16345
16660
  this._selfId$.next(selfId);
16346
16661
  }
16347
16662
  }
@@ -16350,7 +16665,7 @@ var WebRTCVertoManager = class extends VertoManager {
16350
16665
  const vertoPongMessage = VertoPong({ ...vertoPing });
16351
16666
  await this.executeVerto(vertoPongMessage);
16352
16667
  } catch (error) {
16353
- logger$14.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16668
+ logger$15.warn("[WebRTCManager] Call might disconnect, error sending Verto pong:", error);
16354
16669
  this.onError?.(new VertoPongError(error));
16355
16670
  }
16356
16671
  }
@@ -16360,7 +16675,7 @@ var WebRTCVertoManager = class extends VertoManager {
16360
16675
  if (audio) await this.mainPeerConnection.updateSendersConstraints("audio", audio);
16361
16676
  if (video) await this.mainPeerConnection.updateSendersConstraints("video", video);
16362
16677
  } catch (error) {
16363
- logger$14.warn("[WebRTCManager] Error updating media constraints:", error);
16678
+ logger$15.warn("[WebRTCManager] Error updating media constraints:", error);
16364
16679
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16365
16680
  throw error;
16366
16681
  }
@@ -16390,20 +16705,20 @@ var WebRTCVertoManager = class extends VertoManager {
16390
16705
  try {
16391
16706
  const pc = this.mainPeerConnection.peerConnection;
16392
16707
  if (!pc) {
16393
- logger$14.warn("[WebRTCManager] No peer connection for keyframe request");
16708
+ logger$15.warn("[WebRTCManager] No peer connection for keyframe request");
16394
16709
  return;
16395
16710
  }
16396
16711
  const videoReceiver = pc.getReceivers().find((r) => r.track.kind === "video");
16397
16712
  if (!videoReceiver) {
16398
- logger$14.warn("[WebRTCManager] No video receiver for keyframe request");
16713
+ logger$15.warn("[WebRTCManager] No video receiver for keyframe request");
16399
16714
  return;
16400
16715
  }
16401
16716
  if (typeof videoReceiver.requestKeyFrame === "function") {
16402
16717
  videoReceiver.requestKeyFrame();
16403
- logger$14.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16404
- } else logger$14.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16718
+ logger$15.debug("[WebRTCManager] Keyframe requested via RTCRtpReceiver.requestKeyFrame()");
16719
+ } else logger$15.debug("[WebRTCManager] requestKeyFrame() not supported, skipping");
16405
16720
  } catch (error) {
16406
- logger$14.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16721
+ logger$15.warn("[WebRTCManager] Keyframe request failed (non-fatal):", error);
16407
16722
  }
16408
16723
  }
16409
16724
  /**
@@ -16421,13 +16736,13 @@ var WebRTCVertoManager = class extends VertoManager {
16421
16736
  try {
16422
16737
  const controller = this.mainPeerConnection;
16423
16738
  if (!controller.peerConnection) {
16424
- logger$14.warn("[WebRTCManager] No peer connection for ICE restart");
16739
+ logger$15.warn("[WebRTCManager] No peer connection for ICE restart");
16425
16740
  return;
16426
16741
  }
16427
16742
  await controller.triggerIceRestart(relayOnly);
16428
- logger$14.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16743
+ logger$15.info(`[WebRTCManager] ICE restart initiated${relayOnly ? " (relay-only)" : ""}`);
16429
16744
  } catch (error) {
16430
- logger$14.error("[WebRTCManager] ICE restart failed:", error);
16745
+ logger$15.error("[WebRTCManager] ICE restart failed:", error);
16431
16746
  throw error;
16432
16747
  }
16433
16748
  }
@@ -16445,13 +16760,13 @@ var WebRTCVertoManager = class extends VertoManager {
16445
16760
  const entries = Array.from(this._rtcPeerConnectionsMap.entries());
16446
16761
  for (const [id, controller] of entries) try {
16447
16762
  if (!controller.peerConnection) {
16448
- logger$14.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16763
+ logger$15.debug(`[WebRTCManager] No peer connection for leg ${id}, skipping ICE restart`);
16449
16764
  continue;
16450
16765
  }
16451
16766
  await controller.triggerIceRestart(relayOnly);
16452
- logger$14.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16767
+ logger$15.info(`[WebRTCManager] ICE restart initiated for leg ${id}${relayOnly ? " (relay-only)" : ""}`);
16453
16768
  } catch (error) {
16454
- logger$14.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16769
+ logger$15.warn(`[WebRTCManager] ICE restart failed for leg ${id}:`, error);
16455
16770
  }
16456
16771
  }
16457
16772
  /**
@@ -16463,7 +16778,7 @@ var WebRTCVertoManager = class extends VertoManager {
16463
16778
  requestKeyframeAll() {
16464
16779
  for (const [id, controller] of this._rtcPeerConnectionsMap) {
16465
16780
  if (controller.isScreenShare) {
16466
- logger$14.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16781
+ logger$15.debug(`[WebRTCManager] Skipping keyframe for send-only screen share leg ${id}`);
16467
16782
  continue;
16468
16783
  }
16469
16784
  try {
@@ -16473,33 +16788,33 @@ var WebRTCVertoManager = class extends VertoManager {
16473
16788
  if (!videoReceiver) continue;
16474
16789
  if (typeof videoReceiver.requestKeyFrame === "function") {
16475
16790
  videoReceiver.requestKeyFrame();
16476
- logger$14.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16791
+ logger$15.debug(`[WebRTCManager] Keyframe requested for leg ${id}`);
16477
16792
  }
16478
16793
  } catch (error) {
16479
- logger$14.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16794
+ logger$15.warn(`[WebRTCManager] Keyframe request failed for leg ${id} (non-fatal):`, error);
16480
16795
  }
16481
16796
  }
16482
16797
  }
16483
16798
  get callJoinedEvent$() {
16484
- return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.takeUntil)(this.destroyed$));
16799
+ return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$15.filter)(isCallJoinedPayload), (0, import_cjs$15.takeUntil)(this.destroyed$));
16485
16800
  }
16486
16801
  get vertoMedia$() {
16487
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$));
16802
+ return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$));
16488
16803
  }
16489
16804
  get vertoAnswer$() {
16490
- return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16805
+ return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16491
16806
  }
16492
16807
  get vertoMediaParams$() {
16493
- return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16808
+ return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16494
16809
  }
16495
16810
  get vertoBye$() {
16496
- return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16811
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16497
16812
  }
16498
16813
  get vertoAttach$() {
16499
- return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16814
+ return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16500
16815
  }
16501
16816
  get vertoPing$() {
16502
- return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$14.takeUntil)(this.destroyed$)));
16817
+ return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$15.takeUntil)(this.destroyed$)));
16503
16818
  }
16504
16819
  async executeVerto(message, optionals = {}) {
16505
16820
  const webrtcVertoMessage = WebrtcVerto({
@@ -16537,7 +16852,7 @@ var WebRTCVertoManager = class extends VertoManager {
16537
16852
  default:
16538
16853
  }
16539
16854
  } catch (error) {
16540
- logger$14.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16855
+ logger$15.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
16541
16856
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16542
16857
  if (vertoMethod === "verto.modify") this.onModifyFailed?.();
16543
16858
  }
@@ -16552,7 +16867,7 @@ var WebRTCVertoManager = class extends VertoManager {
16552
16867
  sdp
16553
16868
  });
16554
16869
  } catch (error) {
16555
- logger$14.warn("[WebRTCManager] Error processing modify response:", error);
16870
+ logger$15.warn("[WebRTCManager] Error processing modify response:", error);
16556
16871
  const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
16557
16872
  this.onError?.(modifyError);
16558
16873
  }
@@ -16564,7 +16879,7 @@ var WebRTCVertoManager = class extends VertoManager {
16564
16879
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
16565
16880
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
16566
16881
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
16567
- logger$14.debug("[WebRTCManager] Verto invite response:", {
16882
+ logger$15.debug("[WebRTCManager] Verto invite response:", {
16568
16883
  callId,
16569
16884
  memberId,
16570
16885
  response
@@ -16574,14 +16889,14 @@ var WebRTCVertoManager = class extends VertoManager {
16574
16889
  if (callId) {
16575
16890
  this.webRtcCallSession.addCallId(callId);
16576
16891
  this.attachManager.attach(this.buildAttachableCall(callId));
16577
- } else logger$14.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16892
+ } else logger$15.warn("[WebRTCManager] Cannot attach call, missing callId:", {
16578
16893
  nodeId: this.nodeId,
16579
16894
  callId
16580
16895
  });
16581
- logger$14.info("[WebRTCManager] Verto invite successful");
16582
- logger$14.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16896
+ logger$15.info("[WebRTCManager] Verto invite successful");
16897
+ logger$15.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
16583
16898
  } else {
16584
- logger$14.error("[WebRTCManager] Verto invite failed:", response);
16899
+ logger$15.error("[WebRTCManager] Verto invite failed:", response);
16585
16900
  const inviteError = response.error ? new JSONRPCError(response.error.code, response.error.message, response.error.data) : /* @__PURE__ */ new Error("Verto invite failed: unexpected response");
16586
16901
  this.onError?.(inviteError);
16587
16902
  }
@@ -16626,17 +16941,17 @@ var WebRTCVertoManager = class extends VertoManager {
16626
16941
  if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
16627
16942
  }
16628
16943
  async handleInboundAnswer(rtcPeerConnController) {
16629
- logger$14.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16630
- 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);
16944
+ logger$15.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
16945
+ 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);
16631
16946
  if (vertoByeOrAccepted === null) {
16632
- logger$14.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16947
+ logger$15.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
16633
16948
  return;
16634
16949
  }
16635
16950
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16636
- logger$14.info("[WebRTCManager] Inbound call ended by remote before answer.");
16951
+ logger$15.info("[WebRTCManager] Inbound call ended by remote before answer.");
16637
16952
  this.callSession?.destroy();
16638
16953
  } else if (!vertoByeOrAccepted) {
16639
- logger$14.info("[WebRTCManager] Inbound call rejected by user.");
16954
+ logger$15.info("[WebRTCManager] Inbound call rejected by user.");
16640
16955
  try {
16641
16956
  await this.bye("USER_BUSY");
16642
16957
  } finally {
@@ -16644,19 +16959,19 @@ var WebRTCVertoManager = class extends VertoManager {
16644
16959
  this.callSession?.destroy();
16645
16960
  }
16646
16961
  } else {
16647
- logger$14.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16962
+ logger$15.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
16648
16963
  const answerOptions = this.webRtcCallSession.answerMediaOptions;
16649
16964
  try {
16650
16965
  await rtcPeerConnController.acceptInbound(answerOptions);
16651
16966
  } catch (error) {
16652
- logger$14.error("[WebRTCManager] Error creating inbound answer:", error);
16967
+ logger$15.error("[WebRTCManager] Error creating inbound answer:", error);
16653
16968
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16654
16969
  }
16655
16970
  }
16656
16971
  }
16657
16972
  setupVertoAttachHandler() {
16658
16973
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
16659
- logger$14.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16974
+ logger$15.debug("[WebRTCManager] Received Verto attach event for existing call:", vertoAttach);
16660
16975
  const { callID } = vertoAttach;
16661
16976
  await this.attachManager.attach({
16662
16977
  nodeId: this.nodeId ?? void 0,
@@ -16670,12 +16985,12 @@ var WebRTCVertoManager = class extends VertoManager {
16670
16985
  });
16671
16986
  }
16672
16987
  initObservables(rtcPeerConnController) {
16673
- 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$));
16674
- this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16675
- this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$14.takeUntil)(this.destroyed$));
16988
+ 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$));
16989
+ this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16990
+ this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$15.takeUntil)(this.destroyed$));
16676
16991
  }
16677
16992
  setupLocalDescriptionHandler(rtcPeerConnController) {
16678
- this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$14.filter)((description) => description !== null), (0, import_cjs$14.takeUntil)(this.destroyed$)), (description) => {
16993
+ this.subscribeTo(rtcPeerConnController.localDescription$.pipe((0, import_cjs$15.filter)((description) => description !== null), (0, import_cjs$15.takeUntil)(this.destroyed$)), (description) => {
16679
16994
  const { type, sdp } = description;
16680
16995
  const dialogParams = this.dialogParams(rtcPeerConnController);
16681
16996
  const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
@@ -16716,26 +17031,29 @@ var WebRTCVertoManager = class extends VertoManager {
16716
17031
  else if (rtcPeerConnController.isAdditionalDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);
16717
17032
  else if (rtcPeerConnController.isScreenShare) subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);
16718
17033
  }
16719
- const isInvite = isVertoInviteMessage(vertoMessage);
16720
- const isReattach = isInvite && this.webRtcCallSession.options.reattach;
16721
17034
  return {
16722
17035
  callID: rtcPeerConnController.id,
16723
- node_id: isInvite && !isReattach ? "" : this._nodeId$.value ?? "",
17036
+ node_id: resolveInviteNodeId({
17037
+ isInvite: isVertoInviteMessage(vertoMessage),
17038
+ reattach: this.webRtcCallSession.options.reattach === true,
17039
+ explicitNodeId: this.webRtcCallSession.options.nodeId,
17040
+ currentNodeId: this._nodeId$.value
17041
+ }),
16724
17042
  subscribe
16725
17043
  };
16726
17044
  }
16727
17045
  async sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnectionController) {
16728
- logger$14.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
16729
- 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);
17046
+ logger$15.debug("[WebRTCManager] Waiting for call to be accepted or ended before sending answer");
17047
+ 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);
16730
17048
  if (vertoByeOrAccepted === null) {
16731
- logger$14.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
17049
+ logger$15.debug("[WebRTCManager] Destroyed while waiting for call acceptance");
16732
17050
  return;
16733
17051
  }
16734
17052
  if (isVertoByeMessage(vertoByeOrAccepted)) {
16735
- logger$14.info("[WebRTCManager] Call ended before answer was sent.");
17053
+ logger$15.info("[WebRTCManager] Call ended before answer was sent.");
16736
17054
  this.callSession?.destroy();
16737
17055
  } else if (!vertoByeOrAccepted) {
16738
- logger$14.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
17056
+ logger$15.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
16739
17057
  try {
16740
17058
  await this.bye("USER_BUSY");
16741
17059
  } finally {
@@ -16743,14 +17061,14 @@ var WebRTCVertoManager = class extends VertoManager {
16743
17061
  this.callSession?.destroy();
16744
17062
  }
16745
17063
  } else {
16746
- logger$14.debug("[WebRTCManager] Call accepted, sending answer");
17064
+ logger$15.debug("[WebRTCManager] Call accepted, sending answer");
16747
17065
  try {
16748
17066
  this._signalingStatus$.next("connecting");
16749
17067
  await this.sendLocalDescription(vertoMessageRequest, rtcPeerConnectionController);
16750
17068
  await rtcPeerConnectionController.updateAnswerStatus({ status: "sent" });
16751
17069
  await this.attachManager.attach(this.buildAttachableCall());
16752
17070
  } catch (error) {
16753
- logger$14.error("[WebRTCManager] Error sending Verto answer:", error);
17071
+ logger$15.error("[WebRTCManager] Error sending Verto answer:", error);
16754
17072
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16755
17073
  await rtcPeerConnectionController.updateAnswerStatus({ status: "failed" });
16756
17074
  }
@@ -16791,6 +17109,14 @@ var WebRTCVertoManager = class extends VertoManager {
16791
17109
  async unmuteMainVideoInputDevice() {
16792
17110
  return this.mainPeerConnection.restoreTrackSender("video");
16793
17111
  }
17112
+ /** Get or lazily create the local audio pipeline for the main peer connection. */
17113
+ ensureLocalAudioPipeline() {
17114
+ return this.mainPeerConnection.ensureLocalAudioPipeline();
17115
+ }
17116
+ /** The currently-active local audio pipeline, or null if it hasn't been created. */
17117
+ get localAudioPipeline() {
17118
+ return this.mainPeerConnection.localAudioPipeline;
17119
+ }
16794
17120
  async addInputDevice(options = {
16795
17121
  audio: false,
16796
17122
  video: true
@@ -16839,12 +17165,12 @@ var WebRTCVertoManager = class extends VertoManager {
16839
17165
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
16840
17166
  this.onError?.(error);
16841
17167
  });
16842
- 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$)));
17168
+ 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$)));
16843
17169
  this._screenShareStatus$.next("started");
16844
- logger$14.info("[WebRTCManager] Screen share started successfully.");
17170
+ logger$15.info("[WebRTCManager] Screen share started successfully.");
16845
17171
  return rtcPeerConnController.id;
16846
17172
  } catch (error) {
16847
- logger$14.warn("[WebRTCManager] Error initializing additional peer connection:", error);
17173
+ logger$15.warn("[WebRTCManager] Error initializing additional peer connection:", error);
16848
17174
  this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
16849
17175
  if (rtcPeerConnController) rtcPeerConnController.destroy();
16850
17176
  this._screenShareStatus$.next("none");
@@ -16863,9 +17189,9 @@ var WebRTCVertoManager = class extends VertoManager {
16863
17189
  if (removeTrack) return this.mainPeerConnection.stopTrackSender(removeTrack, { updateTransceiverDirection: true });
16864
17190
  }
16865
17191
  async removeScreenMedia() {
16866
- if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$14.warn("[WebRTCManager] No active screen share to stop.");
17192
+ if (!["starting", "started"].includes(this._screenShareStatus$.value)) logger$15.warn("[WebRTCManager] No active screen share to stop.");
16867
17193
  if (!this._screenShareId) {
16868
- logger$14.debug("[WebRTCManager] No screen share peer connection found.");
17194
+ logger$15.debug("[WebRTCManager] No screen share peer connection found.");
16869
17195
  return;
16870
17196
  }
16871
17197
  this._screenShareStatus$.next("stopping");
@@ -16894,7 +17220,7 @@ var WebRTCVertoManager = class extends VertoManager {
16894
17220
  dialogParams: this.dialogParams(rtcPeerConnController)
16895
17221
  }));
16896
17222
  } catch (error) {
16897
- logger$14.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
17223
+ logger$15.warn("[WebRTCManager] Call might already be disconnected, error sending Verto bye:", error);
16898
17224
  throw error;
16899
17225
  }
16900
17226
  }
@@ -16912,7 +17238,7 @@ var WebRTCVertoManager = class extends VertoManager {
16912
17238
  try {
16913
17239
  await this.executeVerto(vertoInfoMessage);
16914
17240
  } catch (error) {
16915
- logger$14.warn("[WebRTCManager] Error sending DTMF digits:", error);
17241
+ logger$15.warn("[WebRTCManager] Error sending DTMF digits:", error);
16916
17242
  throw error;
16917
17243
  }
16918
17244
  }
@@ -16923,10 +17249,10 @@ var WebRTCVertoManager = class extends VertoManager {
16923
17249
  action: "transfer"
16924
17250
  });
16925
17251
  try {
16926
- logger$14.debug("[WebRTCManager] Transferring call with options:", options);
17252
+ logger$15.debug("[WebRTCManager] Transferring call with options:", options);
16927
17253
  await this.executeVerto(message);
16928
17254
  } catch (error) {
16929
- logger$14.error("[WebRTCManager] Error transferring call:", error);
17255
+ logger$15.error("[WebRTCManager] Error transferring call:", error);
16930
17256
  throw error;
16931
17257
  }
16932
17258
  }
@@ -16940,6 +17266,77 @@ var WebRTCVertoManager = class extends VertoManager {
16940
17266
  }
16941
17267
  };
16942
17268
 
17269
+ //#endregion
17270
+ //#region src/controllers/RemoteAudioMeter.ts
17271
+ var import_cjs$14 = require_cjs();
17272
+ const logger$14 = getLogger();
17273
+ /**
17274
+ * Read-only audio level meter for a remote MediaStream. Attaches an
17275
+ * AnalyserNode to a MediaStreamAudioSourceNode so it observes the stream
17276
+ * without affecting the caller's playback path (no GainNode, no destination).
17277
+ *
17278
+ * The server delivers all remote audio as a single mixed stream — there is
17279
+ * no per-participant demux — so this meter reports the aggregate remote
17280
+ * level, not per-member.
17281
+ */
17282
+ var RemoteAudioMeter = class extends Destroyable {
17283
+ constructor(options = {}) {
17284
+ super();
17285
+ this._source = null;
17286
+ this._stream = null;
17287
+ this._audioContext = (options.audioContextFactory ?? (() => new AudioContext()))();
17288
+ this._analyser = this._audioContext.createAnalyser();
17289
+ this._analyser.fftSize = 2048;
17290
+ this._analyser.smoothingTimeConstant = .3;
17291
+ this._analyserBuffer = new Uint8Array(new ArrayBuffer(this._analyser.fftSize));
17292
+ this._pollIntervalMs = options.pollIntervalMs ?? AUDIO_LEVEL_POLL_INTERVAL_MS;
17293
+ }
17294
+ /** RMS level of the remote audio, 0..1. 0 when no stream is attached. */
17295
+ get level$() {
17296
+ return this.deferEmission((0, import_cjs$14.interval)(this._pollIntervalMs, import_cjs$14.animationFrameScheduler).pipe((0, import_cjs$14.map)(() => this.computeLevel())));
17297
+ }
17298
+ /**
17299
+ * Attach (or replace) the MediaStream whose audio track is being metered.
17300
+ * Pass null to detach without destroying the meter.
17301
+ */
17302
+ setStream(stream) {
17303
+ if (this._source) {
17304
+ try {
17305
+ this._source.disconnect();
17306
+ } catch (error) {
17307
+ logger$14.debug("[RemoteAudioMeter] source disconnect warning:", error);
17308
+ }
17309
+ this._source = null;
17310
+ this._stream = null;
17311
+ }
17312
+ if (!stream || stream.getAudioTracks().length === 0) return;
17313
+ this._stream = new MediaStream(stream.getAudioTracks());
17314
+ this._source = this._audioContext.createMediaStreamSource(this._stream);
17315
+ }
17316
+ destroy() {
17317
+ if (this._source) {
17318
+ try {
17319
+ this._source.disconnect();
17320
+ } catch {}
17321
+ this._source = null;
17322
+ }
17323
+ this._audioContext.close().catch((error) => {
17324
+ logger$14.debug("[RemoteAudioMeter] audio context close warning:", error);
17325
+ });
17326
+ super.destroy();
17327
+ }
17328
+ computeLevel() {
17329
+ if (!this._source) return 0;
17330
+ this._analyser.getByteTimeDomainData(this._analyserBuffer);
17331
+ let sum = 0;
17332
+ for (const sample$1 of this._analyserBuffer) {
17333
+ const normalized = (sample$1 - 128) / 128;
17334
+ sum += normalized * normalized;
17335
+ }
17336
+ return Math.sqrt(sum / this._analyserBuffer.length);
17337
+ }
17338
+ };
17339
+
16943
17340
  //#endregion
16944
17341
  //#region src/controllers/RTCStatsMonitor.ts
16945
17342
  var import_cjs$13 = require_cjs();
@@ -17097,11 +17494,11 @@ var RTCStatsMonitor = class extends Destroyable {
17097
17494
  let availableOutgoingBitrate;
17098
17495
  report.forEach((stat) => {
17099
17496
  if (isInboundRtpStat(stat)) if (stat.kind === "audio") {
17100
- audioPacketsReceived += stat.packetsReceived ?? this.lastAudioPacketsReceived;
17497
+ audioPacketsReceived += stat.packetsReceived ?? 0;
17101
17498
  audioPacketsLost += stat.packetsLost ?? 0;
17102
17499
  audioJitter = Math.max(audioJitter, (stat.jitter ?? 0) * 1e3);
17103
17500
  } else {
17104
- videoPacketsReceived += stat.packetsReceived ?? this.lastVideoPacketsReceived;
17501
+ videoPacketsReceived += stat.packetsReceived ?? 0;
17105
17502
  videoPacketsLost += stat.packetsLost ?? 0;
17106
17503
  }
17107
17504
  if (isCandidatePairStat(stat) && stat.state === "succeeded" && stat.nominated) {
@@ -17753,6 +18150,8 @@ var WebRTCCall = class extends Destroyable {
17753
18150
  this._bandwidthConstrained$ = this.createBehaviorSubject(false);
17754
18151
  this._mediaParamsUpdated$ = this.createSubject();
17755
18152
  this._customSubscriptions = /* @__PURE__ */ new Map();
18153
+ this._pushToTalkEnabled = false;
18154
+ this._remoteAudioMeter = null;
17756
18155
  this.id = options.callId ?? v4_default();
17757
18156
  this.to = options.to;
17758
18157
  this._userVariables$.next({
@@ -18151,10 +18550,10 @@ var WebRTCCall = class extends Destroyable {
18151
18550
  try {
18152
18551
  if (this.vertoManager.requestIceRestartAll) await this.vertoManager.requestIceRestartAll(relayOnly);
18153
18552
  else await this.vertoManager.requestIceRestart?.(relayOnly);
18154
- return true;
18155
18553
  } catch {
18156
18554
  return false;
18157
18555
  }
18556
+ return this.waitForPeerConnectionConnected();
18158
18557
  },
18159
18558
  disableVideo: () => {
18160
18559
  try {
@@ -18246,6 +18645,27 @@ var WebRTCCall = class extends Destroyable {
18246
18645
  }
18247
18646
  }
18248
18647
  /**
18648
+ * Wait for the underlying RTCPeerConnection to reach 'connected' after
18649
+ * triggering an ICE restart. Resolves true on success, false on failure
18650
+ * or if the state doesn't transition within the configured timeout.
18651
+ *
18652
+ * Polls connectionState directly because the recovery manager already
18653
+ * wraps this call in its own withTimeout(); a separate listener-based
18654
+ * implementation would race the outer timeout in subtle ways.
18655
+ */
18656
+ async waitForPeerConnectionConnected() {
18657
+ const pc = this.rtcPeerConnection;
18658
+ if (!pc) return false;
18659
+ const deadline = Date.now() + PEER_CONNECTION_RECOVERY_WAIT_MS;
18660
+ for (;;) {
18661
+ const state = pc.connectionState;
18662
+ if (state === "connected") return true;
18663
+ if (state === "failed" || state === "closed") return false;
18664
+ if (Date.now() >= deadline) return false;
18665
+ await new Promise((resolve) => setTimeout(resolve, PEER_CONNECTION_RECOVERY_POLL_MS));
18666
+ }
18667
+ }
18668
+ /**
18249
18669
  * @internal Stop and destroy resilience subsystems (on disconnect/destroy).
18250
18670
  * Clears references so they can be re-created on reconnect.
18251
18671
  */
@@ -18382,8 +18802,13 @@ var WebRTCCall = class extends Destroyable {
18382
18802
  const cached = this._customSubscriptions.get(eventType);
18383
18803
  if (cached) return cached;
18384
18804
  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$));
18805
+ this._sendVertoSubscribe(eventType).then(() => {
18806
+ this._customSubscriptions.set(eventType, filtered$);
18807
+ }, (error) => {
18808
+ this._customSubscriptions.delete(eventType);
18809
+ logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed, not caching:`, error);
18810
+ });
18385
18811
  this._customSubscriptions.set(eventType, filtered$);
18386
- this._sendVertoSubscribe(eventType);
18387
18812
  return filtered$;
18388
18813
  }
18389
18814
  get webrtcMessages$() {
@@ -18498,37 +18923,156 @@ var WebRTCCall = class extends Destroyable {
18498
18923
  async transfer(options) {
18499
18924
  return this.vertoManager.transfer(options);
18500
18925
  }
18926
+ /**
18927
+ * Set the local microphone gain as a percentage applied before transmission.
18928
+ *
18929
+ * - `0` = silent
18930
+ * - `100` = unity (no change, default)
18931
+ * - `200` = 2× digital boost (max; expect clipping / noise amplification)
18932
+ *
18933
+ * Values are clamped to [0, 200]. Engages the local audio pipeline on
18934
+ * first use (one-time cost).
18935
+ *
18936
+ * Note: this is a **digital** multiplier applied in a Web Audio GainNode
18937
+ * between your mic track and the RTCRtpSender — it does not change the
18938
+ * physical mic's hardware sensitivity. Browsers' autoGainControl can
18939
+ * fight the setting; call {@link setAutoGainControl}(false) for
18940
+ * predictable behaviour.
18941
+ *
18942
+ * @param value - Gain percentage (0..200; 100 = unity).
18943
+ */
18944
+ setLocalMicrophoneGain(value) {
18945
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18946
+ if (!pipeline) {
18947
+ logger$11.warn("[Call] setLocalMicrophoneGain: audio pipeline unavailable");
18948
+ return;
18949
+ }
18950
+ const percent = Math.max(0, Math.min(200, value));
18951
+ pipeline.setGain(percent / 100);
18952
+ }
18953
+ /** Observable of the current local microphone gain (0..200, where 100 = unity). */
18954
+ get localMicrophoneGain$() {
18955
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18956
+ if (!pipeline) return (0, import_cjs$11.of)(100).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18957
+ return this.publicCachedObservable("localMicrophoneGain$", () => pipeline.gain$.pipe((0, import_cjs$11.map)((multiplier) => multiplier * 100), (0, import_cjs$11.takeUntil)(this._destroyed$)));
18958
+ }
18959
+ /**
18960
+ * Observable of the RMS audio level of the local microphone, 0..1.
18961
+ * Emits at ~30fps while a mic track is active. Engages the local audio
18962
+ * pipeline on first subscription.
18963
+ */
18964
+ get localAudioLevel$() {
18965
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18966
+ if (!pipeline) return (0, import_cjs$11.of)(0).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18967
+ return this.publicCachedObservable("localAudioLevel$", () => pipeline.level$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$), (0, import_cjs$11.share)()));
18968
+ }
18969
+ /**
18970
+ * Observable that is `true` while the local participant is speaking
18971
+ * (RMS level above the VAD threshold, with hold time to avoid flicker).
18972
+ */
18973
+ get localSpeaking$() {
18974
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18975
+ if (!pipeline) return (0, import_cjs$11.of)(false).pipe((0, import_cjs$11.takeUntil)(this._destroyed$));
18976
+ return this.publicCachedObservable("localSpeaking$", () => pipeline.speaking$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$), (0, import_cjs$11.share)()));
18977
+ }
18978
+ /**
18979
+ * Enable push-to-talk: while {@link setPushToTalkActive} has been called
18980
+ * with `false`, the microphone gain is forced to 0; calling
18981
+ * {@link setPushToTalkActive} with `true` restores the configured gain.
18982
+ * Use this instead of mute/unmute for instant talk/silence transitions
18983
+ * because it doesn't rebuild the track.
18984
+ *
18985
+ * This method installs the pipeline but does not attach any keyboard
18986
+ * listener — consumers bind the key themselves and call
18987
+ * {@link setPushToTalkActive} on keydown/keyup.
18988
+ */
18989
+ enablePushToTalk() {
18990
+ const pipeline = this.vertoManager.ensureLocalAudioPipeline();
18991
+ if (!pipeline) {
18992
+ logger$11.warn("[Call] enablePushToTalk: audio pipeline unavailable");
18993
+ return;
18994
+ }
18995
+ pipeline.setPTTActive(false);
18996
+ this._pushToTalkEnabled = true;
18997
+ }
18998
+ /** Disable push-to-talk; mic gain returns to the configured value. */
18999
+ disablePushToTalk() {
19000
+ this.vertoManager.localAudioPipeline?.setPTTActive(true);
19001
+ this._pushToTalkEnabled = false;
19002
+ }
19003
+ /**
19004
+ * While push-to-talk is enabled, sets the talk state. `true` = transmitting,
19005
+ * `false` = silent. No-op if push-to-talk has not been enabled.
19006
+ */
19007
+ setPushToTalkActive(active) {
19008
+ if (!this._pushToTalkEnabled) return;
19009
+ this.vertoManager.localAudioPipeline?.setPTTActive(active);
19010
+ }
19011
+ /**
19012
+ * Toggle echo cancellation on the local mic at runtime. Applied via
19013
+ * `track.applyConstraints`; browsers that don't honour runtime constraints
19014
+ * (notably iOS Safari) fall back to re-acquiring the track with the new
19015
+ * constraint set and plumbing the replacement through the local audio
19016
+ * pipeline if one is active.
19017
+ */
19018
+ async setEchoCancellation(enabled) {
19019
+ await this.vertoManager.updateMediaConstraints({ audio: { echoCancellation: enabled } });
19020
+ }
19021
+ /** Toggle browser noise suppression on the local mic at runtime. */
19022
+ async setNoiseSuppression(enabled) {
19023
+ await this.vertoManager.updateMediaConstraints({ audio: { noiseSuppression: enabled } });
19024
+ }
19025
+ /** Toggle browser automatic gain control on the local mic at runtime. */
19026
+ async setAutoGainControl(enabled) {
19027
+ await this.vertoManager.updateMediaConstraints({ audio: { autoGainControl: enabled } });
19028
+ }
19029
+ /**
19030
+ * Observable of the aggregate remote audio level, 0..1 RMS. The server
19031
+ * delivers a single mixed audio stream for all remote participants — this
19032
+ * meter reports that mix. Per-participant audio is not available client-side.
19033
+ *
19034
+ * Engages a shared AudioContext on first subscription (cheap — one
19035
+ * AnalyserNode, no GainNode, no destination) so it does not affect the
19036
+ * caller's audio element playback.
19037
+ */
19038
+ get remoteAudioLevel$() {
19039
+ return this.publicCachedObservable("remoteAudioLevel$", () => {
19040
+ this._remoteAudioMeter ??= new RemoteAudioMeter();
19041
+ const meter = this._remoteAudioMeter;
19042
+ this.subscribeTo(this.vertoManager.remoteStream$, (stream) => {
19043
+ meter.setStream(stream);
19044
+ });
19045
+ return meter.level$.pipe((0, import_cjs$11.takeUntil)(this._destroyed$), (0, import_cjs$11.share)());
19046
+ });
19047
+ }
18501
19048
  /** Destroys the call, releasing all resources and subscriptions. */
18502
19049
  destroy() {
18503
19050
  if (this._status$.value === "destroyed") return;
18504
19051
  this._status$.next("destroyed");
18505
19052
  this.stopResilienceSubsystems();
19053
+ this._remoteAudioMeter?.destroy();
19054
+ this._remoteAudioMeter = null;
18506
19055
  this.vertoManager.destroy();
18507
19056
  this.callEventsManager.destroy();
18508
19057
  super.destroy();
18509
19058
  }
18510
19059
  /**
18511
19060
  * @internal Send a verto.subscribe message to add an event type to the
18512
- * server's subscription list for this call. Best-effort failures are
18513
- * logged but don't prevent the filtered observable from being returned.
19061
+ * server's subscription list for this call. Returns the underlying RPC
19062
+ * promise so callers can decide whether to cache the observable on success
19063
+ * or retry on failure.
18514
19064
  */
18515
- _sendVertoSubscribe(eventType) {
18516
- try {
18517
- const message = VertoSubscribe({
18518
- sessid: this.id,
18519
- eventChannel: [eventType]
18520
- });
18521
- const params = {
18522
- callID: this.id,
18523
- node_id: this.vertoManager.nodeId ?? "",
18524
- message
18525
- };
18526
- this.clientSession.execute(WebrtcVerto(params)).catch((error) => {
18527
- logger$11.warn(`[Call] verto.subscribe for '${eventType}' failed (non-fatal):`, error);
18528
- });
18529
- } catch (error) {
18530
- logger$11.warn(`[Call] Failed to send verto.subscribe for '${eventType}':`, error);
18531
- }
19065
+ async _sendVertoSubscribe(eventType) {
19066
+ const message = VertoSubscribe({
19067
+ sessid: this.id,
19068
+ eventChannel: [eventType]
19069
+ });
19070
+ const params = {
19071
+ callID: this.id,
19072
+ node_id: this.vertoManager.nodeId ?? "",
19073
+ message
19074
+ };
19075
+ await this.clientSession.execute(WebrtcVerto(params));
18532
19076
  }
18533
19077
  };
18534
19078
 
@@ -18545,11 +19089,21 @@ function inferCallErrorKind(error) {
18545
19089
  if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError) return "network";
18546
19090
  return "internal";
18547
19091
  }
19092
+ /** JSON-RPC error codes that ClientSessionManager treats as recoverable at the
19093
+ * session level. Surfacing one of these against an in-flight call should not
19094
+ * destroy the call, because the session will reauthenticate and any pending
19095
+ * RPC can then be retried. */
19096
+ const RECOVERABLE_RPC_CODES = new Set([
19097
+ RPC_ERROR_REQUESTER_VALIDATION_FAILED,
19098
+ RPC_ERROR_AUTHENTICATION_FAILED,
19099
+ RPC_ERROR_INVALID_PARAMS
19100
+ ]);
18548
19101
  /** Determines whether an error should be fatal (destroy the call). */
18549
19102
  function isFatalError(error) {
18550
19103
  if (error instanceof VertoPongError) return false;
18551
19104
  if (error instanceof MediaTrackError) return false;
18552
19105
  if (error instanceof RPCTimeoutError) return false;
19106
+ if (error instanceof JSONRPCError && RECOVERABLE_RPC_CODES.has(error.code)) return false;
18553
19107
  return true;
18554
19108
  }
18555
19109
  /**
@@ -18987,10 +19541,10 @@ var PendingRPC = class PendingRPC {
18987
19541
  }
18988
19542
  let isSettled = false;
18989
19543
  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) => {
18990
- const timer$3 = setTimeout(() => {
19544
+ const timer$4 = setTimeout(() => {
18991
19545
  subscriber.error(new RPCTimeoutError(request.id, timeoutMs));
18992
19546
  }, timeoutMs);
18993
- return () => clearTimeout(timer$3);
19547
+ return () => clearTimeout(timer$4);
18994
19548
  }), signal ? new import_cjs$8.Observable((subscriber) => {
18995
19549
  const abortHandler = () => {
18996
19550
  subscriber.error(new DOMException("The operation was aborted", "AbortError"));
@@ -19056,7 +19610,7 @@ var ClientSessionManager = class extends Destroyable {
19056
19610
  this.attachManager = attachManager;
19057
19611
  this.dpopManager = dpopManager;
19058
19612
  this.callCreateTimeout = 6e3;
19059
- this.agent = `signalwire-typescript-sdk/1.0.0`;
19613
+ this.agent = `signalwire-js/4.0.0`;
19060
19614
  this.eventAcks = true;
19061
19615
  this.authorizationState$ = this.createReplaySubject(1);
19062
19616
  this.connectVersion = {
@@ -19068,7 +19622,7 @@ var ClientSessionManager = class extends Destroyable {
19068
19622
  this._errors$ = this.createReplaySubject(1);
19069
19623
  this._authState$ = this.createBehaviorSubject({ kind: "unauthenticated" });
19070
19624
  this._wasClientBound = false;
19071
- this._subscriberInfo$ = this.createBehaviorSubject(null);
19625
+ this._userInfo$ = this.createBehaviorSubject(null);
19072
19626
  this._calls$ = this.createBehaviorSubject({});
19073
19627
  this._iceServers$ = this.createBehaviorSubject([]);
19074
19628
  attachManager.setSession(this);
@@ -19081,11 +19635,11 @@ var ClientSessionManager = class extends Destroyable {
19081
19635
  get incomingCalls() {
19082
19636
  return Object.values(this._calls$.value).filter((call) => call.direction === "inbound");
19083
19637
  }
19084
- get subscriberInfo$() {
19085
- return this._subscriberInfo$.asObservable();
19638
+ get userInfo$() {
19639
+ return this._userInfo$.asObservable();
19086
19640
  }
19087
- get subscriberInfo() {
19088
- return this._subscriberInfo$.value;
19641
+ get userInfo() {
19642
+ return this._userInfo$.value;
19089
19643
  }
19090
19644
  get calls$() {
19091
19645
  return this.cachedObservable("calls$", () => this._calls$.pipe((0, import_cjs$7.map)((calls) => Object.values(calls))));
@@ -19390,7 +19944,6 @@ var ClientSessionManager = class extends Destroyable {
19390
19944
  displayDirection: invite.display_direction,
19391
19945
  userVariables: invite.userVariables
19392
19946
  });
19393
- await (0, import_cjs$7.firstValueFrom)(callSession.status$);
19394
19947
  this._calls$.next({
19395
19948
  [`${callSession.id}`]: callSession,
19396
19949
  ...this._calls$.value
@@ -19411,7 +19964,7 @@ var ClientSessionManager = class extends Destroyable {
19411
19964
  logger$8.debug(`[Session] Verto attach for existing call ${callID}, deferring to per-call handler`);
19412
19965
  return;
19413
19966
  }
19414
- const storedOptions = this.attachManager.consumePendingAttachment(callID);
19967
+ const storedOptions = await this.attachManager.consumePendingAttachment(callID);
19415
19968
  logger$8.debug(`[Session] Creating reattached call for callID: ${callID}`);
19416
19969
  const callSession = await this.createCall({
19417
19970
  nodeId: attach.node_id,
@@ -19423,7 +19976,6 @@ var ClientSessionManager = class extends Destroyable {
19423
19976
  reattach: true,
19424
19977
  ...storedOptions
19425
19978
  });
19426
- await (0, import_cjs$7.firstValueFrom)(callSession.status$);
19427
19979
  this._calls$.next({
19428
19980
  [`${callSession.id}`]: callSession,
19429
19981
  ...this._calls$.value
@@ -19533,22 +20085,22 @@ var ConversationMessageCollection = class extends EntityCollection {
19533
20085
  }
19534
20086
  };
19535
20087
  var ConversationsManager = class {
19536
- constructor(clientSession, http, getSubscriberAddressId, onError) {
20088
+ constructor(clientSession, http, getUserAddressId, onError) {
19537
20089
  this.clientSession = clientSession;
19538
20090
  this.http = http;
19539
- this.getSubscriberAddressId = getSubscriberAddressId;
20091
+ this.getUserAddressId = getUserAddressId;
19540
20092
  this.onError = onError;
19541
20093
  this.groupIds = /* @__PURE__ */ new Map();
19542
20094
  }
19543
20095
  async join(addressId) {
19544
- const subscriberFromAddressId = this.getSubscriberAddressId();
20096
+ const userFromAddressId = this.getUserAddressId();
19545
20097
  try {
19546
20098
  const response = await this.http.request({
19547
20099
  ...POST_PARAMS,
19548
20100
  url: `/api/fabric/conversations/join`,
19549
20101
  body: JSON.stringify({
19550
- from_fabric_address_id: subscriberFromAddressId,
19551
- fabric_address_ids: [addressId, subscriberFromAddressId]
20102
+ from_fabric_address_id: userFromAddressId,
20103
+ fabric_address_ids: [addressId, userFromAddressId]
19552
20104
  })
19553
20105
  });
19554
20106
  if (response.ok && !!response.body) {
@@ -19570,14 +20122,14 @@ var ConversationsManager = class {
19570
20122
  }
19571
20123
  async sendText(text, destinationAddressId) {
19572
20124
  const groupId = this.groupIds.get(destinationAddressId) ?? await this.join(destinationAddressId);
19573
- const subscriberFromAddressId = this.getSubscriberAddressId();
20125
+ const userFromAddressId = this.getUserAddressId();
19574
20126
  try {
19575
20127
  if ((await this.http.request({
19576
20128
  ...POST_PARAMS,
19577
20129
  url: "/api/fabric/messages",
19578
20130
  body: JSON.stringify({
19579
20131
  group_id: groupId,
19580
- from_fabric_address_id: subscriberFromAddressId,
20132
+ from_fabric_address_id: userFromAddressId,
19581
20133
  text
19582
20134
  })
19583
20135
  })).ok) return;
@@ -19650,17 +20202,17 @@ var DeviceTokenManager = class extends Destroyable {
19650
20202
  return this._effectiveExpireIn;
19651
20203
  }
19652
20204
  /**
19653
- * Activates the Client Bound SAT flow when the subscriber's token has
20205
+ * Activates the Client Bound SAT flow when the user's token has
19654
20206
  * `sat:refresh` scope.
19655
20207
  *
19656
20208
  * Steps:
19657
- * 1. Check subscriber's `sat_claims` for `sat:refresh` scope
20209
+ * 1. Check user's `sat_claims` for `sat:refresh` scope
19658
20210
  * 2. Call `/api/fabric/subscriber/devices/token` with a DPoP proof
19659
20211
  * 3. Reauthenticate the session with the Client Bound SAT + DPoP proof
19660
20212
  * 4. Emit token to trigger the reactive refresh pipeline
19661
20213
  */
19662
- async activate(subscriber, session, updateCredential) {
19663
- const { satClaims } = subscriber;
20214
+ async activate(user, session, updateCredential) {
20215
+ const { satClaims } = user;
19664
20216
  if (!satClaims?.scope?.includes(SAT_REFRESH_SCOPE)) {
19665
20217
  logger$6.debug("[DeviceToken] No sat:refresh scope, skipping Client Bound SAT activation");
19666
20218
  return;
@@ -19943,9 +20495,9 @@ const isEmptyArray = (a) => {
19943
20495
  };
19944
20496
 
19945
20497
  //#endregion
19946
- //#region src/utils/warnup.ts
20498
+ //#region src/utils/warmup.ts
19947
20499
  var import_cjs$4 = require_cjs();
19948
- const warnup = (observable) => {
20500
+ const warmup = (observable) => {
19949
20501
  observable.pipe((0, import_cjs$4.take)(1)).subscribe();
19950
20502
  };
19951
20503
 
@@ -19994,7 +20546,7 @@ var DirectoryManager = class extends Destroyable {
19994
20546
  return address;
19995
20547
  }));
19996
20548
  if (observable) {
19997
- warnup(observable);
20549
+ warmup(observable);
19998
20550
  this._observableRegistry.set(id, observable);
19999
20551
  }
20000
20552
  this._addressesInstances.set(id, address);
@@ -20146,10 +20698,9 @@ var WebSocketController = class WebSocketController extends Destroyable {
20146
20698
  else this._status$.next("disconnected");
20147
20699
  }
20148
20700
  reconnect() {
20149
- if (this.shouldReconnect) {
20150
- this._status$.next("reconnecting");
20151
- this.scheduleReconnection();
20152
- } else this._status$.next("disconnected");
20701
+ this.shouldReconnect = true;
20702
+ this._status$.next("reconnecting");
20703
+ this.scheduleReconnection();
20153
20704
  }
20154
20705
  send(data) {
20155
20706
  if (this._status$.value === "connected" && this.socket?.readyState === 1) {
@@ -20508,7 +21059,7 @@ var SignalWire = class extends Destroyable {
20508
21059
  constructor(credentialProvider, options = {}) {
20509
21060
  super();
20510
21061
  this.preferences = new ClientPreferences();
20511
- this._subscriber$ = this.createBehaviorSubject(void 0);
21062
+ this._user$ = this.createBehaviorSubject(void 0);
20512
21063
  this._directory$ = this.createBehaviorSubject(void 0);
20513
21064
  this._isConnected$ = this.createBehaviorSubject(false);
20514
21065
  this._isRegistered$ = this.createBehaviorSubject(false);
@@ -20648,7 +21199,7 @@ var SignalWire = class extends Destroyable {
20648
21199
  if (this._deps.persistSession) this._deps.storage.setItem("sw:cached_credential", credential, "local");
20649
21200
  }
20650
21201
  async init() {
20651
- this._subscriber$.next(new Subscriber(this._deps.http));
21202
+ this._user$.next(new User(this._deps.http));
20652
21203
  if (!this._options.skipConnection) await this.connect();
20653
21204
  if (!this._options.reconnectAttachedCalls && this._attachManager) await this._attachManager.flush();
20654
21205
  if (!this._options.skipRegister) try {
@@ -20710,14 +21261,15 @@ var SignalWire = class extends Destroyable {
20710
21261
  * `'reconnecting'`, `'disconnecting'`, or `'disconnected'`.
20711
21262
  */
20712
21263
  async connect() {
21264
+ await this.teardownTransportAndSession();
20713
21265
  try {
20714
- const subscriber = this._subscriber$.value;
20715
- if (!subscriber) throw new UnexpectedError("Subscriber not initialized before connect");
20716
- if (!await (0, import_cjs$1.firstValueFrom)(subscriber.fetched$)) throw new UnexpectedError("Failed to fetch subscriber information - fetched$ emitted false");
20717
- this._deps.subscriber = subscriber;
21266
+ const user = this._user$.value;
21267
+ if (!user) throw new UnexpectedError("User not initialized before connect");
21268
+ if (!await (0, import_cjs$1.firstValueFrom)(user.fetched$)) throw new UnexpectedError("Failed to fetch user information - fetched$ emitted false");
21269
+ this._deps.user = user;
20718
21270
  } catch (error) {
20719
- 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.`);
20720
- throw new UnexpectedError("Error fetching subscriber information", { cause: error });
21271
+ 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.`);
21272
+ throw new UnexpectedError("Error fetching user information", { cause: error });
20721
21273
  }
20722
21274
  const errorHandler = (error) => {
20723
21275
  this._errors$.next(error);
@@ -20751,7 +21303,7 @@ var SignalWire = class extends Destroyable {
20751
21303
  logger$1.debug("[SignalWire] Developer refresh disabled — Client Bound SAT activation starting");
20752
21304
  }
20753
21305
  this._deviceTokenManager = new DeviceTokenManager(this._dpopManager, this._deps.http, (error) => this._errors$.next(error), () => this._deps.credential);
20754
- await this._deviceTokenManager.activate(this._deps.subscriber, this._clientSession, (cred) => {
21306
+ await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
20755
21307
  this._deps.credential = {
20756
21308
  ...this._deps.credential,
20757
21309
  ...cred
@@ -20761,7 +21313,7 @@ var SignalWire = class extends Destroyable {
20761
21313
  this.subscribeTo(this._clientSession.authenticated$.pipe((0, import_cjs$1.skip)(1), (0, import_cjs$1.filter)(Boolean)), async () => {
20762
21314
  try {
20763
21315
  if (this._deviceTokenManager) {
20764
- await this._deviceTokenManager.activate(this._deps.subscriber, this._clientSession, (cred) => {
21316
+ await this._deviceTokenManager.activate(this._deps.user, this._clientSession, (cred) => {
20765
21317
  this._deps.credential = {
20766
21318
  ...this._deps.credential,
20767
21319
  ...cred
@@ -20774,15 +21326,15 @@ var SignalWire = class extends Destroyable {
20774
21326
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20775
21327
  }
20776
21328
  try {
20777
- logger$1.debug("[SignalWire] Re-registering subscriber after reconnect");
21329
+ logger$1.debug("[SignalWire] Re-registering user after reconnect");
20778
21330
  await this.register();
20779
- logger$1.debug("[SignalWire] Subscriber re-registered successfully after reconnect");
21331
+ logger$1.debug("[SignalWire] User re-registered successfully after reconnect");
20780
21332
  } catch (error) {
20781
21333
  logger$1.error("[SignalWire] Re-registration failed after reconnect:", error);
20782
21334
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20783
21335
  }
20784
21336
  });
20785
- const conversationManager = new ConversationsManager(this._clientSession, this._deps.http, () => this._deps.getSubscriberFromAddressId(), errorHandler);
21337
+ const conversationManager = new ConversationsManager(this._clientSession, this._deps.http, () => this._deps.getUserFromAddressId(), errorHandler);
20786
21338
  const directory = new DirectoryManager(this._deps.http, this._clientSession, conversationManager, errorHandler);
20787
21339
  this._directory$.next(directory);
20788
21340
  this._clientSession.setDirectory(directory);
@@ -20793,22 +21345,22 @@ var SignalWire = class extends Destroyable {
20793
21345
  });
20794
21346
  }
20795
21347
  /**
20796
- * Observable that emits the {@link Subscriber} profile once fetched,
21348
+ * Observable that emits the {@link User} profile once fetched,
20797
21349
  * or `undefined` before authentication completes.
20798
21350
  *
20799
21351
  * @example
20800
21352
  * ```ts
20801
- * client.subscriber$.subscribe(sub => {
20802
- * if (sub) console.log('Logged in as', sub.email);
21353
+ * client.user$.subscribe(u => {
21354
+ * if (u) console.log('Logged in as', u.email);
20803
21355
  * });
20804
21356
  * ```
20805
21357
  */
20806
- get subscriber$() {
20807
- return this.deferEmission(this._subscriber$.asObservable());
21358
+ get user$() {
21359
+ return this.deferEmission(this._user$.asObservable());
20808
21360
  }
20809
- /** Current subscriber snapshot, or `undefined` if not yet authenticated. */
20810
- get subscriber() {
20811
- return this._subscriber$.value;
21361
+ /** Current user snapshot, or `undefined` if not yet authenticated. */
21362
+ get user() {
21363
+ return this._user$.value;
20812
21364
  }
20813
21365
  /**
20814
21366
  * Observable that emits the {@link Directory} instance once the client is connected,
@@ -20832,11 +21384,11 @@ var SignalWire = class extends Destroyable {
20832
21384
  get directory() {
20833
21385
  return this._directory$.value;
20834
21386
  }
20835
- /** Observable that emits when the subscriber registration state changes. */
21387
+ /** Observable that emits when the user registration state changes. */
20836
21388
  get isRegistered$() {
20837
21389
  return this.deferEmission(this._isRegistered$.asObservable());
20838
21390
  }
20839
- /** Whether the subscriber is currently registered. */
21391
+ /** Whether the user is currently registered. */
20840
21392
  get isRegistered() {
20841
21393
  return this._isRegistered$.value;
20842
21394
  }
@@ -20941,15 +21493,35 @@ var SignalWire = class extends Destroyable {
20941
21493
  this._refreshTimerId = void 0;
20942
21494
  }
20943
21495
  this._diagnosticsCollector?.record("connection", "disconnected");
20944
- await this._clientSession.disconnect();
20945
- this._clientSession.destroy();
21496
+ await this.teardownTransportAndSession();
20946
21497
  this._isConnected$.next(false);
20947
21498
  }
21499
+ /**
21500
+ * Tear down the current transport / session / attach manager. Safe to call
21501
+ * when nothing has been initialized yet (e.g. first connect()).
21502
+ */
21503
+ async teardownTransportAndSession() {
21504
+ const session = this._clientSession;
21505
+ const transport = this._transport;
21506
+ if (session) {
21507
+ try {
21508
+ await session.disconnect();
21509
+ } catch (error) {
21510
+ logger$1.warn("[SignalWire] Error disconnecting previous session:", error);
21511
+ }
21512
+ session.destroy();
21513
+ }
21514
+ if (transport) transport.destroy();
21515
+ this._clientSession = void 0;
21516
+ this._publicSession = void 0;
21517
+ this._transport = void 0;
21518
+ this._attachManager = void 0;
21519
+ }
20948
21520
  async waitAuthentication() {
20949
21521
  await (0, import_cjs$1.firstValueFrom)(this.ready$.pipe((0, import_cjs$1.filter)((ready$1) => ready$1 === true)));
20950
21522
  }
20951
21523
  /**
20952
- * Registers the subscriber as online to receive inbound calls and events.
21524
+ * Registers the user as online to receive inbound calls and events.
20953
21525
  *
20954
21526
  * Waits for authentication to complete before sending the registration.
20955
21527
  * If the initial attempt fails, reauthentication is attempted automatically.
@@ -20964,26 +21536,31 @@ var SignalWire = class extends Destroyable {
20964
21536
  params: {}
20965
21537
  }));
20966
21538
  this._isRegistered$.next(true);
21539
+ return;
20967
21540
  } catch (error) {
20968
- logger$1.debug("[SignalWire] Failed to register subscriber, trying reauthentication...");
20969
- if (this._deps.credential.token) this._clientSession.reauthenticate(this._deps.credential.token).then(async () => {
21541
+ if (!this._deps.credential.token) {
21542
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21543
+ throw error;
21544
+ }
21545
+ logger$1.debug("[SignalWire] Failed to register user, trying reauthentication...");
21546
+ try {
21547
+ await this._clientSession.reauthenticate(this._deps.credential.token);
20970
21548
  logger$1.debug("[SignalWire] Reauthentication successful, retrying register()");
20971
21549
  await this._transport.execute(RPCExecute({
20972
21550
  method: "subscriber.online",
20973
21551
  params: {}
20974
21552
  }));
20975
21553
  this._isRegistered$.next(true);
20976
- }).catch((reauthError) => {
21554
+ } catch (reauthError) {
20977
21555
  logger$1.error("[SignalWire] Reauthentication failed during register():", reauthError);
20978
- 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 }) });
21556
+ 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 }) });
20979
21557
  this._errors$.next(registerError);
20980
- });
20981
- this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
20982
- throw error;
21558
+ throw registerError;
21559
+ }
20983
21560
  }
20984
21561
  }
20985
21562
  /**
20986
- * Unregisters the subscriber, going offline for inbound calls.
21563
+ * Unregisters the user, going offline for inbound calls.
20987
21564
  *
20988
21565
  * The WebSocket connection remains open; use {@link disconnect} to fully close it.
20989
21566
  */
@@ -20995,7 +21572,7 @@ var SignalWire = class extends Destroyable {
20995
21572
  }));
20996
21573
  this._isRegistered$.next(false);
20997
21574
  } catch (error) {
20998
- logger$1.error("[SignalWire] Failed to unregister subscriber:", error);
21575
+ logger$1.error("[SignalWire] Failed to unregister user:", error);
20999
21576
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
21000
21577
  throw error;
21001
21578
  }
@@ -21135,6 +21712,36 @@ var SignalWire = class extends Destroyable {
21135
21712
  selectAudioOutputDevice(device) {
21136
21713
  this._deviceController.selectAudioOutputDevice(device);
21137
21714
  }
21715
+ /**
21716
+ * Apply the currently selected audio output device to an HTMLMediaElement
21717
+ * (e.g. the `<audio>` or `<video>` element the consumer attached the
21718
+ * remote stream to). Uses `HTMLMediaElement.setSinkId` under the hood.
21719
+ * Returns a `Promise<boolean>`: `true` if the sink was applied,
21720
+ * `false` if the browser doesn't support `setSinkId` or no device is
21721
+ * selected.
21722
+ *
21723
+ * @example
21724
+ * ```ts
21725
+ * audioEl.srcObject = call.remoteStream;
21726
+ * await client.applySelectedAudioOutputDevice(audioEl);
21727
+ * ```
21728
+ */
21729
+ async applySelectedAudioOutputDevice(element) {
21730
+ const device = this._deviceController.selectedAudioOutputDevice;
21731
+ if (!device?.deviceId) return false;
21732
+ const withSink = element;
21733
+ if (typeof withSink.setSinkId !== "function") {
21734
+ logger$1.warn("[SignalWire] setSinkId not supported on this element / browser");
21735
+ return false;
21736
+ }
21737
+ try {
21738
+ await withSink.setSinkId(device.deviceId);
21739
+ return true;
21740
+ } catch (error) {
21741
+ logger$1.warn("[SignalWire] Failed to apply audio output device:", error);
21742
+ return false;
21743
+ }
21744
+ }
21138
21745
  /** Starts monitoring for media device changes (connect/disconnect). */
21139
21746
  enableDeviceMonitoring() {
21140
21747
  this._deviceController.enableDeviceMonitoring();
@@ -21315,6 +21922,7 @@ var EmbedTokenCredentialProvider = class {
21315
21922
  try {
21316
21923
  const response = await fetch(url, {
21317
21924
  method: "POST",
21925
+ headers: { "Content-Type": "application/json" },
21318
21926
  body: JSON.stringify({ token: this.embedToken }),
21319
21927
  signal: controller.signal
21320
21928
  });
@@ -21445,6 +22053,7 @@ exports.ClientPreferences = ClientPreferences;
21445
22053
  exports.CollectionFetchError = CollectionFetchError;
21446
22054
  exports.DPoPInitError = DPoPInitError;
21447
22055
  exports.DeviceTokenError = DeviceTokenError;
22056
+ exports.EmbedTokenCredentialProvider = EmbedTokenCredentialProvider;
21448
22057
  exports.InvalidCredentialsError = InvalidCredentialsError;
21449
22058
  exports.MediaTrackError = MediaTrackError;
21450
22059
  exports.MessageParseError = MessageParseError;
@@ -21456,12 +22065,13 @@ exports.SelfCapabilities = SelfCapabilities;
21456
22065
  exports.SelfParticipant = SelfParticipant;
21457
22066
  exports.SignalWire = SignalWire;
21458
22067
  exports.StaticCredentialProvider = StaticCredentialProvider;
21459
- exports.Subscriber = Subscriber;
21460
22068
  exports.TokenRefreshError = TokenRefreshError;
21461
22069
  exports.UnexpectedError = UnexpectedError;
22070
+ exports.User = User;
21462
22071
  exports.VertoPongError = VertoPongError;
21463
22072
  exports.WebRTCCall = WebRTCCall;
21464
22073
  exports.embeddableCall = embeddableCall;
22074
+ exports.getLogger = getLogger;
21465
22075
  exports.isSelfParticipant = isSelfParticipant;
21466
22076
  exports.ready = ready;
21467
22077
  exports.setDebugOptions = setDebugOptions;