@signalwire/js 4.0.0-beta.0 → 4.0.0-beta.10

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.
@@ -2883,7 +2883,7 @@ var require_observeOn = /* @__PURE__ */ __commonJSMin(((exports) => {
2883
2883
  var executeSchedule_1$7 = require_executeSchedule();
2884
2884
  var lift_1$67 = require_lift();
2885
2885
  var OperatorSubscriber_1$56 = require_OperatorSubscriber();
2886
- function observeOn(scheduler, delay$1) {
2886
+ function observeOn$1(scheduler, delay$1) {
2887
2887
  if (delay$1 === void 0) delay$1 = 0;
2888
2888
  return lift_1$67.operate(function(source, subscriber) {
2889
2889
  source.subscribe(OperatorSubscriber_1$56.createOperatorSubscriber(subscriber, function(value) {
@@ -2901,7 +2901,7 @@ var require_observeOn = /* @__PURE__ */ __commonJSMin(((exports) => {
2901
2901
  }));
2902
2902
  });
2903
2903
  }
2904
- exports.observeOn = observeOn;
2904
+ exports.observeOn = observeOn$1;
2905
2905
  }));
2906
2906
 
2907
2907
  //#endregion
@@ -3111,7 +3111,7 @@ var require_throwError = /* @__PURE__ */ __commonJSMin(((exports) => {
3111
3111
  exports.throwError = void 0;
3112
3112
  var Observable_1$18 = require_Observable();
3113
3113
  var isFunction_1$14 = require_isFunction();
3114
- function throwError(errorOrErrorFactory, scheduler) {
3114
+ function throwError$1(errorOrErrorFactory, scheduler) {
3115
3115
  var errorFactory = isFunction_1$14.isFunction(errorOrErrorFactory) ? errorOrErrorFactory : function() {
3116
3116
  return errorOrErrorFactory;
3117
3117
  };
@@ -3122,7 +3122,7 @@ var require_throwError = /* @__PURE__ */ __commonJSMin(((exports) => {
3122
3122
  return scheduler.schedule(init, 0, subscriber);
3123
3123
  } : init);
3124
3124
  }
3125
- exports.throwError = throwError;
3125
+ exports.throwError = throwError$1;
3126
3126
  }));
3127
3127
 
3128
3128
  //#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$3() {
4334
+ function race$4() {
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$3;
4340
+ exports.race = race$4;
4341
4341
  function raceInit(sources) {
4342
4342
  return function(subscriber) {
4343
4343
  var subscriptions = [];
@@ -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$5(comparator, keySelector) {
5477
+ function distinctUntilChanged$6(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$5;
5493
+ exports.distinctUntilChanged = distinctUntilChanged$6;
5494
5494
  function defaultCompare(a, b) {
5495
5495
  return a === b;
5496
5496
  }
@@ -6689,7 +6689,7 @@ var require_share = /* @__PURE__ */ __commonJSMin(((exports) => {
6689
6689
  var Subject_1$6 = require_Subject();
6690
6690
  var Subscriber_1$1 = require_Subscriber();
6691
6691
  var lift_1$19 = require_lift();
6692
- function share$1(options) {
6692
+ function share$3(options) {
6693
6693
  if (options === void 0) options = {};
6694
6694
  var _a = options.connector, connector = _a === void 0 ? function() {
6695
6695
  return new Subject_1$6.Subject();
@@ -6747,7 +6747,7 @@ var require_share = /* @__PURE__ */ __commonJSMin(((exports) => {
6747
6747
  })(wrapperSource);
6748
6748
  };
6749
6749
  }
6750
- exports.share = share$1;
6750
+ exports.share = share$3;
6751
6751
  function handleReset(reset, on) {
6752
6752
  var args = [];
6753
6753
  for (var _i = 2; _i < arguments.length; _i++) args[_i - 2] = arguments[_i];
@@ -6919,7 +6919,7 @@ var require_startWith = /* @__PURE__ */ __commonJSMin(((exports) => {
6919
6919
  var concat_1$2 = require_concat$1();
6920
6920
  var args_1$2 = require_args();
6921
6921
  var lift_1$14 = require_lift();
6922
- function startWith() {
6922
+ function startWith$1() {
6923
6923
  var values = [];
6924
6924
  for (var _i = 0; _i < arguments.length; _i++) values[_i] = arguments[_i];
6925
6925
  var scheduler = args_1$2.popScheduler(values);
@@ -6927,7 +6927,7 @@ var require_startWith = /* @__PURE__ */ __commonJSMin(((exports) => {
6927
6927
  (scheduler ? concat_1$2.concat(values, source, scheduler) : concat_1$2.concat(values, source)).subscribe(subscriber);
6928
6928
  });
6929
6929
  }
6930
- exports.startWith = startWith;
6930
+ exports.startWith = startWith$1;
6931
6931
  }));
6932
6932
 
6933
6933
  //#endregion
@@ -6938,7 +6938,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
6938
6938
  var innerFrom_1$6 = require_innerFrom();
6939
6939
  var lift_1$13 = require_lift();
6940
6940
  var OperatorSubscriber_1$11 = require_OperatorSubscriber();
6941
- function switchMap$2(project, resultSelector) {
6941
+ function switchMap$4(project, resultSelector) {
6942
6942
  return lift_1$13.operate(function(source, subscriber) {
6943
6943
  var innerSubscriber = null;
6944
6944
  var index = 0;
@@ -6962,7 +6962,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
6962
6962
  }));
6963
6963
  });
6964
6964
  }
6965
- exports.switchMap = switchMap$2;
6965
+ exports.switchMap = switchMap$4;
6966
6966
  }));
6967
6967
 
6968
6968
  //#endregion
@@ -8945,11 +8945,55 @@ var Destroyable = class {
8945
8945
  this._destroyed$ = new import_cjs$22.Subject();
8946
8946
  }
8947
8947
  destroy() {
8948
+ this._observableCache?.clear();
8948
8949
  this.subscriptions.forEach((sub) => sub.unsubscribe());
8949
8950
  this.subjects.forEach((subject) => subject.complete());
8950
8951
  this._destroyed$.next();
8951
8952
  this._destroyed$.complete();
8952
8953
  }
8954
+ cachedObservable(key, factory) {
8955
+ this._observableCache ??= /* @__PURE__ */ new Map();
8956
+ let cached = this._observableCache.get(key);
8957
+ if (!cached) {
8958
+ cached = factory();
8959
+ this._observableCache.set(key, cached);
8960
+ }
8961
+ return cached;
8962
+ }
8963
+ /**
8964
+ * Like `cachedObservable`, but defers emissions to the microtask queue
8965
+ * via `observeOn(asapScheduler)`.
8966
+ *
8967
+ * Use ONLY for public-facing observable getters that external consumers
8968
+ * subscribe to. Prevents a class of bugs where `BehaviorSubject` or
8969
+ * `ReplaySubject` replays synchronously during `subscribe()`, before
8970
+ * the subscription variable is assigned in the caller's scope.
8971
+ *
8972
+ * Do NOT use for observables consumed internally by the SDK — internal
8973
+ * code using `subscribeTo()`, `firstValueFrom()`, or `withLatestFrom()`
8974
+ * depends on synchronous emission delivery.
8975
+ */
8976
+ publicCachedObservable(key, factory) {
8977
+ const publicKey = `public:${key}`;
8978
+ this._observableCache ??= /* @__PURE__ */ new Map();
8979
+ let cached = this._observableCache.get(publicKey);
8980
+ if (!cached) {
8981
+ cached = factory().pipe((0, import_cjs$22.observeOn)(import_cjs$22.asapScheduler));
8982
+ this._observableCache.set(publicKey, cached);
8983
+ }
8984
+ return cached;
8985
+ }
8986
+ /**
8987
+ * Wraps an observable so emissions are deferred to the microtask queue.
8988
+ *
8989
+ * Use ONLY for public-facing getters that expose a subject via
8990
+ * `.asObservable()` without going through `cachedObservable`.
8991
+ *
8992
+ * Do NOT use for observables consumed internally by the SDK.
8993
+ */
8994
+ deferEmission(observable) {
8995
+ return observable.pipe((0, import_cjs$22.observeOn)(import_cjs$22.asapScheduler));
8996
+ }
8953
8997
  subscribeTo(observable, observerOrNext) {
8954
8998
  const subscription = observable.subscribe(observerOrNext);
8955
8999
  this.subscriptions.push(subscription);
@@ -8970,7 +9014,7 @@ var Destroyable = class {
8970
9014
  return subject;
8971
9015
  }
8972
9016
  get $() {
8973
- return (0, import_cjs$22.merge)(...this.subjects.map((s) => s instanceof import_cjs$22.BehaviorSubject ? s.pipe((0, import_cjs$22.skip)(1)) : s)).pipe((0, import_cjs$22.map)((_) => this));
9017
+ return this.cachedObservable("$", () => (0, import_cjs$22.merge)(...this.subjects.map((s) => s instanceof import_cjs$22.BehaviorSubject ? s.pipe((0, import_cjs$22.skip)(1)) : s)).pipe((0, import_cjs$22.map)((_) => this)));
8974
9018
  }
8975
9019
  /**
8976
9020
  * Observable that emits when the instance is destroyed
@@ -9098,21 +9142,23 @@ var DependencyError = class extends Error {
9098
9142
  }
9099
9143
  };
9100
9144
  var CallCreateError = class extends Error {
9101
- constructor(message, error = null, options) {
9145
+ constructor(message, error = null, direction = "outbound", options) {
9102
9146
  super(message, {
9103
9147
  ...options,
9104
9148
  cause: options?.cause ?? (error instanceof Error ? error : void 0)
9105
9149
  });
9106
9150
  this.message = message;
9107
9151
  this.error = error;
9152
+ this.direction = direction;
9108
9153
  this.name = "CallCreateError";
9109
9154
  }
9110
9155
  };
9111
9156
  var JSONRPCError = class extends Error {
9112
- constructor(code, message, data, options) {
9157
+ constructor(code, message, data, options, requestId) {
9113
9158
  super(message, options);
9114
9159
  this.code = code;
9115
9160
  this.data = data;
9161
+ this.requestId = requestId;
9116
9162
  this.name = "JSONRPCError";
9117
9163
  }
9118
9164
  };
@@ -9654,7 +9700,7 @@ var PreferencesContainer = class PreferencesContainer {
9654
9700
  skipDeviceMonitoring: false,
9655
9701
  savePreferences: false
9656
9702
  };
9657
- this.receiveVideo = true;
9703
+ this.receiveVideo = false;
9658
9704
  this.receiveAudio = true;
9659
9705
  this.preferredAudioInput = null;
9660
9706
  this.preferredAudioOutput = null;
@@ -9958,15 +10004,16 @@ const selectDevice = (devices = [], selected, preferred) => {
9958
10004
  return selected;
9959
10005
  };
9960
10006
  var NavigatorDeviceController = class extends Destroyable {
9961
- constructor() {
10007
+ constructor(webRTCApiProvider) {
9962
10008
  super();
10009
+ this.webRTCApiProvider = webRTCApiProvider;
9963
10010
  this.deviceChangeHandler = () => {
9964
10011
  logger$19.debug("[DeviceController] Device change detected");
9965
10012
  this.enumerateDevices();
9966
10013
  };
9967
10014
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
9968
10015
  this._selectedDevicesState$ = this.createBehaviorSubject(initialSelectedDevicesState);
9969
- this._errors$ = this.createSubject();
10016
+ this._errors$ = this.createReplaySubject(1);
9970
10017
  this.init();
9971
10018
  }
9972
10019
  get selectedAudioInputDeviceConstraints() {
@@ -9983,25 +10030,25 @@ var NavigatorDeviceController = class extends Destroyable {
9983
10030
  return {};
9984
10031
  }
9985
10032
  get errors$() {
9986
- return this._errors$.asObservable().pipe((0, import_cjs$20.takeUntil)(this.destroyed$));
10033
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$20.takeUntil)(this.destroyed$)));
9987
10034
  }
9988
10035
  get audioInputDevices$() {
9989
- return this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.audioinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$));
10036
+ return this.cachedObservable("audioInputDevices$", () => this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.audioinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$)));
9990
10037
  }
9991
10038
  get audioOutputDevices$() {
9992
- return this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.audiooutput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$));
10039
+ return this.cachedObservable("audioOutputDevices$", () => this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.audiooutput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$)));
9993
10040
  }
9994
10041
  get videoInputDevices$() {
9995
- return this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.videoinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$));
10042
+ return this.cachedObservable("videoInputDevices$", () => this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.videoinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$)));
9996
10043
  }
9997
10044
  get selectedAudioInputDevice$() {
9998
- return this._selectedDevicesState$.asObservable().pipe((0, import_cjs$20.map)((state) => state.audioinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$), (0, import_cjs$20.tap)((info) => logger$19.debug("[DeviceController] Selected audio input device changed:", info)));
10045
+ return this.cachedObservable("selectedAudioInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$20.map)((state) => state.audioinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$), (0, import_cjs$20.tap)((info) => logger$19.debug("[DeviceController] Selected audio input device changed:", info))));
9999
10046
  }
10000
10047
  get selectedAudioOutputDevice$() {
10001
- return this._selectedDevicesState$.asObservable().pipe((0, import_cjs$20.map)((state) => state.audiooutput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$), (0, import_cjs$20.tap)((info) => logger$19.debug("[DeviceController] Selected audio output device changed:", info)));
10048
+ return this.cachedObservable("selectedAudioOutputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$20.map)((state) => state.audiooutput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$), (0, import_cjs$20.tap)((info) => logger$19.debug("[DeviceController] Selected audio output device changed:", info))));
10002
10049
  }
10003
10050
  get selectedVideoInputDevice$() {
10004
- return this._selectedDevicesState$.asObservable().pipe((0, import_cjs$20.map)((state) => state.videoinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$), (0, import_cjs$20.tap)((info) => logger$19.debug("[DeviceController] Selected video input device changed:", info)));
10051
+ return this.cachedObservable("selectedVideoInputDevice$", () => this._selectedDevicesState$.asObservable().pipe((0, import_cjs$20.map)((state) => state.videoinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$), (0, import_cjs$20.tap)((info) => logger$19.debug("[DeviceController] Selected video input device changed:", info))));
10005
10052
  }
10006
10053
  get selectedAudioInputDevice() {
10007
10054
  return this._selectedDevicesState$.value.audioinput;
@@ -10041,24 +10088,22 @@ var NavigatorDeviceController = class extends Destroyable {
10041
10088
  });
10042
10089
  }
10043
10090
  init() {
10044
- if (navigator.mediaDevices) {
10045
- this.subscribeTo(this._devicesState$.pipe((0, import_cjs$20.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10046
- const currentSelected = this._selectedDevicesState$.value;
10047
- const newAudioInput = selectDevice(devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10048
- const newAudioOutput = selectDevice(devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
10049
- const newVideoInput = selectDevice(devicesState.videoinput, currentSelected.videoinput, PreferencesContainer.instance.preferredVideoInput);
10050
- if (newAudioInput !== currentSelected.audioinput || newAudioOutput !== currentSelected.audiooutput || newVideoInput !== currentSelected.videoinput) this._selectedDevicesState$.next({
10051
- audioinput: newAudioInput,
10052
- audiooutput: newAudioOutput,
10053
- videoinput: newVideoInput
10054
- });
10091
+ this.subscribeTo(this._devicesState$.pipe((0, import_cjs$20.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10092
+ const currentSelected = this._selectedDevicesState$.value;
10093
+ const newAudioInput = selectDevice(devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10094
+ const newAudioOutput = selectDevice(devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
10095
+ const newVideoInput = selectDevice(devicesState.videoinput, currentSelected.videoinput, PreferencesContainer.instance.preferredVideoInput);
10096
+ if (newAudioInput !== currentSelected.audioinput || newAudioOutput !== currentSelected.audiooutput || newVideoInput !== currentSelected.videoinput) this._selectedDevicesState$.next({
10097
+ audioinput: newAudioInput,
10098
+ audiooutput: newAudioOutput,
10099
+ videoinput: newVideoInput
10055
10100
  });
10056
- this.enumerateDevices();
10057
- }
10101
+ });
10102
+ this.enumerateDevices();
10058
10103
  }
10059
10104
  enableDeviceMonitoring() {
10060
10105
  this.disableDeviceMonitoring();
10061
- navigator.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
10106
+ this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
10062
10107
  if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$20.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
10063
10108
  logger$19.debug("[DeviceController] Polling devices due to interval");
10064
10109
  this.enumerateDevices();
@@ -10066,7 +10111,7 @@ var NavigatorDeviceController = class extends Destroyable {
10066
10111
  this.enumerateDevices();
10067
10112
  }
10068
10113
  disableDeviceMonitoring() {
10069
- navigator.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler);
10114
+ this.webRTCApiProvider.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler);
10070
10115
  if (this._devicesPoolingSubscription) {
10071
10116
  this._devicesPoolingSubscription.unsubscribe();
10072
10117
  this._devicesPoolingSubscription = void 0;
@@ -10074,7 +10119,7 @@ var NavigatorDeviceController = class extends Destroyable {
10074
10119
  }
10075
10120
  async enumerateDevices() {
10076
10121
  try {
10077
- const devicesByKind = (await navigator.mediaDevices.enumerateDevices()).reduce((acc, device) => {
10122
+ const devicesByKind = (await this.webRTCApiProvider.mediaDevices.enumerateDevices()).reduce((acc, device) => {
10078
10123
  acc[device.kind].push(device);
10079
10124
  return acc;
10080
10125
  }, {
@@ -10097,7 +10142,7 @@ var NavigatorDeviceController = class extends Destroyable {
10097
10142
  if (deviceInfo.kind === "audiooutput") return null;
10098
10143
  try {
10099
10144
  const constraints = this.deviceInfoToConstraints(deviceInfo);
10100
- const stream = await navigator.mediaDevices.getUserMedia({
10145
+ const stream = await this.webRTCApiProvider.mediaDevices.getUserMedia({
10101
10146
  audio: deviceInfo.kind === "audioinput" ? constraints : false,
10102
10147
  video: deviceInfo.kind === "videoinput" ? constraints : false
10103
10148
  });
@@ -10269,9 +10314,23 @@ var DependencyContainer = class {
10269
10314
  this._webSocketConstructor = WebSocketConstructor;
10270
10315
  }
10271
10316
  get deviceController() {
10272
- this._deviceController ??= new NavigatorDeviceController();
10317
+ this._deviceController ??= new NavigatorDeviceController(this.webRTCApiProvider);
10273
10318
  return this._deviceController;
10274
10319
  }
10320
+ get webRTCApiProvider() {
10321
+ if (!this._webRTCApiProvider) {
10322
+ if (typeof RTCPeerConnection === "undefined" || typeof navigator === "undefined") throw new DependencyError("WebRTCApiProvider: RTCPeerConnection or navigator.mediaDevices is not available. Please provide a custom webRTCApiProvider in SignalWireOptions.");
10323
+ this._webRTCApiProvider = {
10324
+ RTCPeerConnection,
10325
+ mediaDevices: navigator.mediaDevices
10326
+ };
10327
+ }
10328
+ return this._webRTCApiProvider;
10329
+ }
10330
+ set webRTCApiProvider(webRTCApiProvider) {
10331
+ this._webRTCApiProvider = webRTCApiProvider;
10332
+ this._deviceController = void 0;
10333
+ }
10275
10334
  get authorizationStateKey() {
10276
10335
  return `sw:${this.subscriberId}:as`;
10277
10336
  }
@@ -10452,6 +10511,15 @@ const RPCConnect = (params) => {
10452
10511
  });
10453
10512
  };
10454
10513
 
10514
+ //#endregion
10515
+ //#region src/core/RPCMessages/RPCReauthenticate.ts
10516
+ const RPCReauthenticate = (authentication) => {
10517
+ return buildRPCRequest({
10518
+ method: "signalwire.reauthenticate",
10519
+ params: { authentication }
10520
+ });
10521
+ };
10522
+
10455
10523
  //#endregion
10456
10524
  //#region src/core/RPCMessages/RPCPing.ts
10457
10525
  const RPCPingResponse = (id, timestamp$1) => {
@@ -10472,7 +10540,7 @@ const RPCExecute = ({ method, params }) => {
10472
10540
 
10473
10541
  //#endregion
10474
10542
  //#region src/core/RPCMessages/VertoMessages.ts
10475
- const tmpMap = {
10543
+ const SDK_TO_VERTO_FIELD_MAP = {
10476
10544
  id: "callID",
10477
10545
  destinationNumber: "destination_number",
10478
10546
  remoteCallerName: "remote_caller_id_name",
@@ -10481,19 +10549,31 @@ const tmpMap = {
10481
10549
  callerNumber: "caller_id_number",
10482
10550
  fromCallAddressId: "from_fabric_address_id"
10483
10551
  };
10552
+ const EXCLUDED_DIALOG_PARAMS = new Set([
10553
+ "remoteSdp",
10554
+ "localStream",
10555
+ "remoteStream"
10556
+ ]);
10484
10557
  /**
10485
- * Translate SDK fields into verto variables
10558
+ * Translate SDK fields into verto variables.
10559
+ * Returns a new object — the input is never mutated.
10486
10560
  */
10561
+ /** @internal Exported for testing only. */
10487
10562
  const filterVertoParams = (params) => {
10488
- if (Object.prototype.hasOwnProperty.call(params, "dialogParams")) {
10489
- const { remoteSdp, localStream, remoteStream, ...dialogParams } = params.dialogParams;
10490
- for (const key in tmpMap) if (key && Object.prototype.hasOwnProperty.call(dialogParams, key)) {
10491
- dialogParams[tmpMap[key]] = dialogParams[key];
10492
- delete dialogParams[key];
10493
- }
10494
- params.dialogParams = dialogParams;
10495
- }
10496
- return params;
10563
+ if (!Object.prototype.hasOwnProperty.call(params, "dialogParams")) return params;
10564
+ const sourceDialogParams = params.dialogParams;
10565
+ const filteredDialogParams = Object.entries(sourceDialogParams).reduce((acc, [key, value]) => {
10566
+ if (EXCLUDED_DIALOG_PARAMS.has(key)) return acc;
10567
+ const mappedKey = SDK_TO_VERTO_FIELD_MAP[key] ?? key;
10568
+ return {
10569
+ ...acc,
10570
+ [mappedKey]: value
10571
+ };
10572
+ }, {});
10573
+ return {
10574
+ ...params,
10575
+ dialogParams: filteredDialogParams
10576
+ };
10497
10577
  };
10498
10578
  const buildVertoRPCMessage = (method) => {
10499
10579
  return (params = {}) => {
@@ -10604,17 +10684,21 @@ var AttachManager = class {
10604
10684
  buildCallOptions(attachment) {
10605
10685
  const { audio: audioDirection, video: videoDirection } = attachment.mediaDirections;
10606
10686
  const { audioInputDevice, videoInputDevice } = attachment;
10687
+ const receiveAudio = audioDirection.includes("recv");
10688
+ const receiveVideo = videoDirection.includes("recv");
10689
+ const sendAudio = audioDirection.includes("send");
10690
+ const sendVideo = videoDirection.includes("send");
10607
10691
  return {
10608
- receiveAudio: audioDirection.includes("recv"),
10609
- receiveVideo: videoDirection.includes("recv"),
10610
- inputAudioDeviceConstraints: {
10611
- audio: audioDirection.includes("send"),
10692
+ receiveAudio,
10693
+ receiveVideo,
10694
+ inputAudioDeviceConstraints: sendAudio ? {
10695
+ audio: true,
10612
10696
  ...this.deviceController.deviceInfoToConstraints(audioInputDevice)
10613
- },
10614
- inputVideoDeviceConstraints: {
10615
- video: videoDirection.includes("send"),
10697
+ } : void 0,
10698
+ inputVideoDeviceConstraints: sendVideo ? {
10699
+ video: true,
10616
10700
  ...this.deviceController.deviceInfoToConstraints(videoInputDevice)
10617
- },
10701
+ } : void 0,
10618
10702
  reattach: true
10619
10703
  };
10620
10704
  }
@@ -10675,12 +10759,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
10675
10759
  exports.race = void 0;
10676
10760
  var argsOrArgArray_1 = require_argsOrArgArray();
10677
10761
  var raceWith_1$1 = require_raceWith();
10678
- function race$2() {
10762
+ function race$3() {
10679
10763
  var args = [];
10680
10764
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
10681
10765
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
10682
10766
  }
10683
- exports.race = race$2;
10767
+ exports.race = race$3;
10684
10768
  }));
10685
10769
 
10686
10770
  //#endregion
@@ -11662,7 +11746,7 @@ var SelfCapabilities = class extends Destroyable {
11662
11746
  }
11663
11747
  /** Observable for self member capabilities */
11664
11748
  get self$() {
11665
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.self), (0, import_cjs$17.distinctUntilChanged)());
11749
+ return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.self), (0, import_cjs$17.distinctUntilChanged)()));
11666
11750
  }
11667
11751
  /** Current self member capabilities */
11668
11752
  get self() {
@@ -11670,7 +11754,7 @@ var SelfCapabilities = class extends Destroyable {
11670
11754
  }
11671
11755
  /** Observable for other member capabilities */
11672
11756
  get member$() {
11673
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.member), (0, import_cjs$17.distinctUntilChanged)());
11757
+ return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.member), (0, import_cjs$17.distinctUntilChanged)()));
11674
11758
  }
11675
11759
  /** Current other member capabilities */
11676
11760
  get member() {
@@ -11678,7 +11762,7 @@ var SelfCapabilities = class extends Destroyable {
11678
11762
  }
11679
11763
  /** Observable for end call capability */
11680
11764
  get end$() {
11681
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.end), (0, import_cjs$17.distinctUntilChanged)());
11765
+ return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.end), (0, import_cjs$17.distinctUntilChanged)()));
11682
11766
  }
11683
11767
  /** Current end call capability */
11684
11768
  get end() {
@@ -11686,7 +11770,7 @@ var SelfCapabilities = class extends Destroyable {
11686
11770
  }
11687
11771
  /** Observable for set layout capability */
11688
11772
  get setLayout$() {
11689
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.setLayout), (0, import_cjs$17.distinctUntilChanged)());
11773
+ return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.setLayout), (0, import_cjs$17.distinctUntilChanged)()));
11690
11774
  }
11691
11775
  /** Current set layout capability */
11692
11776
  get setLayout() {
@@ -11694,7 +11778,7 @@ var SelfCapabilities = class extends Destroyable {
11694
11778
  }
11695
11779
  /** Observable for send digit capability */
11696
11780
  get sendDigit$() {
11697
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.sendDigit), (0, import_cjs$17.distinctUntilChanged)());
11781
+ return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.sendDigit), (0, import_cjs$17.distinctUntilChanged)()));
11698
11782
  }
11699
11783
  /** Current send digit capability */
11700
11784
  get sendDigit() {
@@ -11702,7 +11786,7 @@ var SelfCapabilities = class extends Destroyable {
11702
11786
  }
11703
11787
  /** Observable for vmuted hide capability */
11704
11788
  get vmutedHide$() {
11705
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.vmutedHide), (0, import_cjs$17.distinctUntilChanged)());
11789
+ return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.vmutedHide), (0, import_cjs$17.distinctUntilChanged)()));
11706
11790
  }
11707
11791
  /** Current vmuted hide capability */
11708
11792
  get vmutedHide() {
@@ -11710,7 +11794,7 @@ var SelfCapabilities = class extends Destroyable {
11710
11794
  }
11711
11795
  /** Observable for lock capability */
11712
11796
  get lock$() {
11713
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.lock), (0, import_cjs$17.distinctUntilChanged)());
11797
+ return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.lock), (0, import_cjs$17.distinctUntilChanged)()));
11714
11798
  }
11715
11799
  /** Current lock capability */
11716
11800
  get lock() {
@@ -11718,7 +11802,7 @@ var SelfCapabilities = class extends Destroyable {
11718
11802
  }
11719
11803
  /** Observable for device capability */
11720
11804
  get device$() {
11721
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.device), (0, import_cjs$17.distinctUntilChanged)());
11805
+ return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.device), (0, import_cjs$17.distinctUntilChanged)()));
11722
11806
  }
11723
11807
  /** Current device capability */
11724
11808
  get device() {
@@ -11726,7 +11810,7 @@ var SelfCapabilities = class extends Destroyable {
11726
11810
  }
11727
11811
  /** Observable for screenshare capability */
11728
11812
  get screenshare$() {
11729
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.screenshare), (0, import_cjs$17.distinctUntilChanged)());
11813
+ return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.screenshare), (0, import_cjs$17.distinctUntilChanged)()));
11730
11814
  }
11731
11815
  /** Current screenshare capability */
11732
11816
  get screenshare() {
@@ -11779,83 +11863,83 @@ var Participant = class extends Destroyable {
11779
11863
  }
11780
11864
  /** Observable of the participant's display name. */
11781
11865
  get name$() {
11782
- return this._state$.pipe((0, import_operators$1.map)((state) => state.name), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11866
+ return this.cachedObservable("name$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.name), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11783
11867
  }
11784
11868
  /** Observable of the participant type (e.g. `'member'`, `'screen'`). */
11785
11869
  get type$() {
11786
- return this._state$.pipe((0, import_operators$1.map)((state) => state.type), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11870
+ return this.cachedObservable("type$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.type), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11787
11871
  }
11788
11872
  /** Observable indicating whether the participant has raised their hand. */
11789
11873
  get handraised$() {
11790
- return this._state$.pipe((0, import_operators$1.map)((state) => state.handraised), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11874
+ return this.cachedObservable("handraised$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.handraised), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11791
11875
  }
11792
11876
  /** Observable indicating whether the participant is visible in the layout. */
11793
11877
  get visible$() {
11794
- return this._state$.pipe((0, import_operators$1.map)((state) => state.visible), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11878
+ return this.cachedObservable("visible$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.visible), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11795
11879
  }
11796
11880
  /** Observable indicating whether the participant's audio is muted. */
11797
11881
  get audioMuted$() {
11798
- return this._state$.pipe((0, import_operators$1.map)((state) => state.audio_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11882
+ return this.cachedObservable("audioMuted$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.audio_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11799
11883
  }
11800
11884
  /** Observable indicating whether the participant's video is muted. */
11801
11885
  get videoMuted$() {
11802
- return this._state$.pipe((0, import_operators$1.map)((state) => state.video_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11886
+ return this.cachedObservable("videoMuted$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.video_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11803
11887
  }
11804
11888
  /** Observable indicating whether the participant is deafened. */
11805
11889
  get deaf$() {
11806
- return this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11890
+ return this.cachedObservable("deaf$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11807
11891
  }
11808
11892
  /** Observable of the participant's microphone input volume. */
11809
11893
  get inputVolume$() {
11810
- return this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11894
+ return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11811
11895
  }
11812
11896
  /** Observable of the participant's speaker output volume. */
11813
11897
  get outputVolume$() {
11814
- return this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11898
+ return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11815
11899
  }
11816
11900
  /** Observable of the microphone input sensitivity level. */
11817
11901
  get inputSensitivity$() {
11818
- return this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11902
+ return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11819
11903
  }
11820
11904
  /** Observable indicating whether echo cancellation is enabled. */
11821
11905
  get echoCancellation$() {
11822
- return this._state$.pipe((0, import_operators$1.map)((state) => state.echo_cancellation), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11906
+ return this.cachedObservable("echoCancellation$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.echo_cancellation), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11823
11907
  }
11824
11908
  /** Observable indicating whether auto-gain control is enabled. */
11825
11909
  get autoGain$() {
11826
- return this._state$.pipe((0, import_operators$1.map)((state) => state.auto_gain), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11910
+ return this.cachedObservable("autoGain$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.auto_gain), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11827
11911
  }
11828
11912
  /** Observable indicating whether noise suppression is enabled. */
11829
11913
  get noiseSuppression$() {
11830
- return this._state$.pipe((0, import_operators$1.map)((state) => state.noise_suppression), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11914
+ return this.cachedObservable("noiseSuppression$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.noise_suppression), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11831
11915
  }
11832
11916
  /** Observable indicating whether low-bitrate mode is active. */
11833
11917
  get lowbitrate$() {
11834
- return this._state$.pipe((0, import_operators$1.map)((state) => state.lowbitrate), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11918
+ return this.cachedObservable("lowbitrate$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.lowbitrate), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11835
11919
  }
11836
11920
  /** Observable indicating whether noise reduction is active. */
11837
11921
  get denoise$() {
11838
- return this._state$.pipe((0, import_operators$1.map)((state) => state.denoise), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11922
+ return this.cachedObservable("denoise$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.denoise), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11839
11923
  }
11840
11924
  /** Observable of custom metadata for this participant. */
11841
11925
  get meta$() {
11842
- return this._state$.pipe((0, import_operators$1.map)((state) => state.meta), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11926
+ return this.cachedObservable("meta$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.meta), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11843
11927
  }
11844
11928
  /** Observable of the participant's subscriber ID. */
11845
11929
  get subscriberId$() {
11846
- return this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11930
+ return this.cachedObservable("subscriberId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11847
11931
  }
11848
11932
  /** Observable of the participant's address ID. */
11849
11933
  get addressId$() {
11850
- return this._state$.pipe((0, import_operators$1.map)((state) => state.address_id), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11934
+ return this.cachedObservable("addressId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.address_id), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11851
11935
  }
11852
11936
  /** Observable of the server node ID for this participant. */
11853
11937
  get nodeId$() {
11854
- return this._state$.pipe((0, import_operators$1.map)((state) => state.node_id), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11938
+ return this.cachedObservable("nodeId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.node_id), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11855
11939
  }
11856
11940
  /** Observable indicating whether the participant is currently speaking. */
11857
11941
  get isTalking$() {
11858
- return this._state$.pipe((0, import_operators$1.map)((state) => state.talking), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11942
+ return this.cachedObservable("isTalking$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.talking), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11859
11943
  }
11860
11944
  /** Whether the participant is currently speaking. */
11861
11945
  get isTalking() {
@@ -11863,7 +11947,7 @@ var Participant = class extends Destroyable {
11863
11947
  }
11864
11948
  /** Observable of the participant's layout position. */
11865
11949
  get position$() {
11866
- return this._state$.pipe((0, import_operators$1.map)((state) => state.position), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11950
+ return this.cachedObservable("position$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.position), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11867
11951
  }
11868
11952
  /** Current layout position. */
11869
11953
  get position() {
@@ -12014,7 +12098,13 @@ var Participant = class extends Destroyable {
12014
12098
  }
12015
12099
  /** Removes this participant from the call. */
12016
12100
  async remove() {
12017
- await this.executeMethod(this.id, "call.member.remove", {});
12101
+ const state = this._state$.value;
12102
+ const target = {
12103
+ member_id: this.id,
12104
+ call_id: state.call_id ?? "",
12105
+ node_id: state.node_id ?? ""
12106
+ };
12107
+ await this.executeMethod(target, "call.member.remove", {});
12018
12108
  }
12019
12109
  /** Ends the call for this participant. */
12020
12110
  async end() {
@@ -12182,7 +12272,10 @@ function isJSONRPCRequest(value) {
12182
12272
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && hasProperty(value, "method") && typeof value.method === "string";
12183
12273
  }
12184
12274
  function isJSONRPCResponse(value) {
12185
- return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && hasProperty(value, "result");
12275
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && (hasProperty(value, "result") || hasProperty(value, "error"));
12276
+ }
12277
+ function isJSONRPCErrorResponse(value) {
12278
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && (hasProperty(value, "error") && isObject(value.error) && hasProperty(value.error, "code") && hasProperty(value.error, "message") || hasProperty(value, "result") && isObject(value.result) && hasProperty(value.result, "code") && value.result.code !== "200" && hasProperty(value.result, "message"));
12186
12279
  }
12187
12280
 
12188
12281
  //#endregion
@@ -12373,23 +12466,19 @@ var CallEventsManager = class extends Destroyable {
12373
12466
  this.options = options;
12374
12467
  this.callIds = /* @__PURE__ */ new Set();
12375
12468
  this.roomSessionIds = /* @__PURE__ */ new Set();
12376
- this._status$ = this.createBehaviorSubject("trying");
12377
12469
  this._participants$ = this.createBehaviorSubject({});
12378
12470
  this._self$ = this.createBehaviorSubject(null);
12379
12471
  this._sessionState$ = this.createBehaviorSubject(initialSessionState);
12380
12472
  this.initSubscriptions();
12381
12473
  }
12382
12474
  get participants$() {
12383
- return this._participants$.asObservable().pipe((0, import_cjs$14.map)((participantsRecord) => Object.values(participantsRecord)));
12475
+ return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$14.map)((participantsRecord) => Object.values(participantsRecord))));
12384
12476
  }
12385
- get self$() {
12386
- return this._self$.asObservable().pipe(filterNull());
12387
- }
12388
- get status$() {
12389
- return this._status$.asObservable();
12477
+ get participants() {
12478
+ return Object.values(this._participants$.value);
12390
12479
  }
12391
- get status() {
12392
- return this._status$.value;
12480
+ get self$() {
12481
+ return this.cachedObservable("self$", () => this._self$.asObservable().pipe(filterNull()));
12393
12482
  }
12394
12483
  isRoomSessionIdValid(roomSessionId) {
12395
12484
  return this.roomSessionIds.has(roomSessionId);
@@ -12401,40 +12490,40 @@ var CallEventsManager = class extends Destroyable {
12401
12490
  return this.callIds.has(callId);
12402
12491
  }
12403
12492
  get recording$() {
12404
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recording), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12493
+ return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recording), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12405
12494
  }
12406
12495
  get recordings$() {
12407
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recordings), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12496
+ return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recordings), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12408
12497
  }
12409
12498
  get streaming$() {
12410
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streaming), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12499
+ return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streaming), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12411
12500
  }
12412
12501
  get streams$() {
12413
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streams), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12502
+ return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streams), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12414
12503
  }
12415
12504
  get playbacks$() {
12416
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.playbacks), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12505
+ return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.playbacks), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12417
12506
  }
12418
12507
  get raiseHandPriority$() {
12419
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.prioritize_handraise), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12508
+ return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.prioritize_handraise), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12420
12509
  }
12421
12510
  get locked$() {
12422
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.locked), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12511
+ return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.locked), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12423
12512
  }
12424
12513
  get meta$() {
12425
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.meta), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12514
+ return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.meta), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12426
12515
  }
12427
12516
  get capabilities$() {
12428
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.capabilities), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12517
+ return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.capabilities), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12429
12518
  }
12430
12519
  get layout$() {
12431
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_name), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12520
+ return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_name), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12432
12521
  }
12433
12522
  get layouts$() {
12434
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layouts), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12523
+ return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layouts), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12435
12524
  }
12436
12525
  get layoutLayers$() {
12437
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_layers), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12526
+ return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_layers), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12438
12527
  }
12439
12528
  get self() {
12440
12529
  return this._self$.value;
@@ -12475,7 +12564,6 @@ var CallEventsManager = class extends Destroyable {
12475
12564
  callId: callJoinedEvent.call_id,
12476
12565
  roomSessionId: callJoinedEvent.room_session_id
12477
12566
  });
12478
- this._status$.next("connected");
12479
12567
  const sessionState = callJoinedEvent.room_session;
12480
12568
  const { capabilities } = callJoinedEvent;
12481
12569
  this.selfId = this.selfId ?? callJoinedEvent.member_id;
@@ -12496,7 +12584,7 @@ var CallEventsManager = class extends Destroyable {
12496
12584
  });
12497
12585
  this.updateParticipants(sessionState.members);
12498
12586
  this._self$.value?.capabilities.updateFromRaw(capabilities);
12499
- this.updateLayouts();
12587
+ if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
12500
12588
  });
12501
12589
  this.subscribeTo(this.memberUpdates$, (member) => {
12502
12590
  logger$15.debug("[CallEventsManager] Handling member update event for member ID:", member);
@@ -12570,19 +12658,19 @@ var CallEventsManager = class extends Destroyable {
12570
12658
  this._participants$.next(this._participants$.value);
12571
12659
  }
12572
12660
  get callJoinedEvent$() {
12573
- return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.tap)((event) => {
12661
+ return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.tap)((event) => {
12574
12662
  logger$15.debug("[CallEventsManager] Call joined event:", event);
12575
- }));
12663
+ })));
12576
12664
  }
12577
12665
  get layoutChangedEvent$() {
12578
- return this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$14.tap)((event) => {
12666
+ return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$14.tap)((event) => {
12579
12667
  logger$15.debug("[CallEventsManager] Layout changed event:", event);
12580
- }));
12668
+ })));
12581
12669
  }
12582
12670
  get memberUpdates$() {
12583
- return (0, import_cjs$14.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$14.map)((event) => event.member), (0, import_cjs$14.tap)((event) => {
12671
+ return this.cachedObservable("memberUpdates$", () => (0, import_cjs$14.merge)(this.webRtcCallSession.memberJoined$, this.webRtcCallSession.memberUpdated$, this.webRtcCallSession.memberTalking$).pipe((0, import_cjs$14.map)((event) => event.member), (0, import_cjs$14.tap)((event) => {
12584
12672
  logger$15.debug("[CallEventsManager] Member update event:", event);
12585
- }));
12673
+ })));
12586
12674
  }
12587
12675
  destroy() {
12588
12676
  Object.values(this._participants$.value).forEach((participant) => {
@@ -12602,12 +12690,53 @@ var CallEventsManager = class extends Destroyable {
12602
12690
 
12603
12691
  //#endregion
12604
12692
  //#region src/helpers/SDPHelper.ts
12693
+ /** Valid SDP direction attribute values. */
12694
+ const SDP_DIRECTIONS = new Set([
12695
+ "sendrecv",
12696
+ "sendonly",
12697
+ "recvonly",
12698
+ "inactive"
12699
+ ]);
12605
12700
  /**
12606
- * SDPHelper - Utility functions for SDP (Session Description Protocol) parsing and validation.
12701
+ * Extracts the media directions (audio/video) from an SDP string.
12607
12702
  *
12608
- * This module provides helper functions to analyze and validate SDP content,
12609
- * particularly for ICE candidate validation in WebRTC connections.
12703
+ * Parses each media section (`m=audio` / `m=video`) and reads the `a=` direction
12704
+ * attribute (`sendrecv`, `sendonly`, `recvonly`, `inactive`).
12705
+ * If no explicit direction attribute is found for a media section, defaults to `sendrecv`
12706
+ * per RFC 4566.
12707
+ *
12708
+ * @param sdp - The SDP string to parse
12709
+ * @returns The extracted audio and video directions
12710
+ *
12711
+ * @example
12712
+ * ```typescript
12713
+ * const sdp = `v=0\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111\r\na=sendrecv\r\nm=video 9 UDP/TLS/RTP/SAVPF 96\r\na=recvonly`;
12714
+ * extractMediaDirectionsFromSDP(sdp);
12715
+ * // { audio: 'sendrecv', video: 'recvonly' }
12716
+ * ```
12610
12717
  */
12718
+ function extractMediaDirectionsFromSDP(sdp) {
12719
+ const result = {
12720
+ audio: "inactive",
12721
+ video: "inactive"
12722
+ };
12723
+ if (!sdp) return result;
12724
+ const lines = sdp.split(/\r?\n/);
12725
+ let currentMediaKind = null;
12726
+ let currentDirection = null;
12727
+ for (const line of lines) if (line.startsWith("m=")) {
12728
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12729
+ if (line.startsWith("m=audio")) currentMediaKind = "audio";
12730
+ else if (line.startsWith("m=video")) currentMediaKind = "video";
12731
+ else currentMediaKind = null;
12732
+ currentDirection = null;
12733
+ } else if (currentMediaKind && line.startsWith("a=")) {
12734
+ const attr = line.substring(2).trim();
12735
+ if (SDP_DIRECTIONS.has(attr)) currentDirection = attr;
12736
+ }
12737
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12738
+ return result;
12739
+ }
12611
12740
  /**
12612
12741
  * Validates that an SDP string has at least one non-host ICE candidate
12613
12742
  * for each media section (m= line).
@@ -12910,6 +13039,15 @@ var LocalStreamController = class extends Destroyable {
12910
13039
  track.addEventListener("ended", this.mediaTrackEndedHandler);
12911
13040
  }
12912
13041
  /**
13042
+ * Update the controller options (e.g., when media overrides are applied).
13043
+ */
13044
+ updateOptions(options) {
13045
+ this.options = {
13046
+ ...this.options,
13047
+ ...options
13048
+ };
13049
+ }
13050
+ /**
12913
13051
  * Stop all local tracks and clean up.
12914
13052
  */
12915
13053
  stopAllTracks() {
@@ -13224,7 +13362,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13224
13362
  logger$11.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
13225
13363
  return;
13226
13364
  }
13227
- const streamTrack = (await navigator.mediaDevices.getUserMedia({ [kind]: {
13365
+ const streamTrack = (await this.getUserMedia({ [kind]: {
13228
13366
  ...track.getConstraints(),
13229
13367
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
13230
13368
  } })).getTracks().find((t) => t.kind === kind);
@@ -13248,11 +13386,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13248
13386
  this._connectionState$ = this.createReplaySubject(1);
13249
13387
  this._signalingState$ = this.createReplaySubject(1);
13250
13388
  this._iceGatheringState$ = this.createReplaySubject(1);
13251
- this._errors$ = this.createSubject();
13389
+ this._errors$ = this.createReplaySubject(1);
13252
13390
  this._iceCandidates$ = this.createReplaySubject(1);
13253
13391
  this._initialized$ = this.createReplaySubject(1);
13254
13392
  this._remoteDescription$ = this.createReplaySubject(1);
13255
13393
  this._remoteStream$ = this.createBehaviorSubject(null);
13394
+ this._remoteOfferMediaDirections = null;
13256
13395
  this.deviceController = deviceController ?? {};
13257
13396
  this.id = options.callId ?? v4_default();
13258
13397
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -13260,10 +13399,19 @@ var RTCPeerConnectionController = class extends Destroyable {
13260
13399
  type: "offer",
13261
13400
  sdp: remoteSessionDescription
13262
13401
  } : void 0;
13402
+ this._remoteOfferMediaDirections = remoteSessionDescription ? extractMediaDirectionsFromSDP(remoteSessionDescription) : null;
13403
+ const offerDefaults = this._remoteOfferMediaDirections ? {
13404
+ audio: this._remoteOfferMediaDirections.audio.includes("recv"),
13405
+ video: this._remoteOfferMediaDirections.video.includes("recv"),
13406
+ receiveAudio: this._remoteOfferMediaDirections.audio.includes("send"),
13407
+ receiveVideo: this._remoteOfferMediaDirections.video.includes("send")
13408
+ } : {};
13263
13409
  this.options = {
13264
- receiveAudio: options.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13265
- receiveVideo: options.receiveVideo ?? PreferencesContainer.instance.receiveVideo,
13266
- ...options
13410
+ ...options,
13411
+ audio: options.audio ?? offerDefaults.audio,
13412
+ video: options.video ?? offerDefaults.video,
13413
+ receiveAudio: options.receiveAudio ?? offerDefaults.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13414
+ receiveVideo: options.receiveVideo ?? offerDefaults.receiveVideo ?? PreferencesContainer.instance.receiveVideo
13267
13415
  };
13268
13416
  this.localStreamController = new LocalStreamController({
13269
13417
  propose: this.propose,
@@ -13313,43 +13461,43 @@ var RTCPeerConnectionController = class extends Destroyable {
13313
13461
  };
13314
13462
  }
13315
13463
  get iceGatheringState$() {
13316
- return this._iceGatheringState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13464
+ return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13317
13465
  }
13318
13466
  get mediaTrackEnded$() {
13319
- return this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13467
+ return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13320
13468
  }
13321
13469
  get errors$() {
13322
- return this._errors$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13470
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13323
13471
  }
13324
13472
  get iceCandidates$() {
13325
- return this._iceCandidates$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13473
+ return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13326
13474
  }
13327
13475
  get initialized$() {
13328
- return this._initialized$.asObservable().pipe((0, import_cjs$11.filter)((initialized) => initialized), (0, import_cjs$11.takeUntil)(this.destroyed$));
13476
+ return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$11.filter)((initialized) => initialized), (0, import_cjs$11.takeUntil)(this.destroyed$)));
13329
13477
  }
13330
13478
  get remoteDescription$() {
13331
- return this._remoteDescription$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13479
+ return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13332
13480
  }
13333
13481
  get localStream$() {
13334
- return this.localStreamController.localStream$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13482
+ return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13335
13483
  }
13336
13484
  get remoteStream$() {
13337
- return this._remoteStream$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13485
+ return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13338
13486
  }
13339
13487
  get localAudioTracks$() {
13340
- return this.localStreamController.localAudioTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13488
+ return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13341
13489
  }
13342
13490
  get localVideoTracks$() {
13343
- return this.localStreamController.localVideoTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13491
+ return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13344
13492
  }
13345
13493
  get iceConnectionState$() {
13346
- return this._iceConnectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13494
+ return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13347
13495
  }
13348
13496
  get connectionState$() {
13349
- return this._connectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13497
+ return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13350
13498
  }
13351
13499
  get signalingState$() {
13352
- return this._signalingState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13500
+ return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13353
13501
  }
13354
13502
  get type() {
13355
13503
  return this._type;
@@ -13408,14 +13556,14 @@ var RTCPeerConnectionController = class extends Destroyable {
13408
13556
  };
13409
13557
  }
13410
13558
  get inputVideoDeviceConstraints() {
13411
- if (this.options.video === false && !this.options.inputVideoDeviceConstraints) return false;
13559
+ if (!this.options.video && !this.options.inputVideoDeviceConstraints) return false;
13412
13560
  return {
13413
13561
  ...this.options.inputVideoDeviceConstraints,
13414
13562
  ...this.deviceController.selectedVideoInputDeviceConstraints
13415
13563
  };
13416
13564
  }
13417
13565
  get WebRTCPeerConnectionConstructor() {
13418
- return this.options.WebRTCPeerConnectionConstructor ?? RTCPeerConnection;
13566
+ return this.options.webRTCApiProvider?.RTCPeerConnection ?? RTCPeerConnection;
13419
13567
  }
13420
13568
  get offerOptions() {
13421
13569
  const options = { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
@@ -13430,12 +13578,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13430
13578
  default: return {
13431
13579
  ...options,
13432
13580
  offerToReceiveAudio: true,
13433
- offerToReceiveVideo: Boolean(this.inputVideoDeviceConstraints)
13581
+ offerToReceiveVideo: this.options.receiveVideo ?? Boolean(this.inputVideoDeviceConstraints)
13434
13582
  };
13435
13583
  }
13436
13584
  }
13437
13585
  get answerOptions() {
13438
- return {};
13586
+ return { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
13439
13587
  }
13440
13588
  /**
13441
13589
  * Initialize the RTCPeerConnection and setup event listeners.
@@ -13470,11 +13618,15 @@ var RTCPeerConnectionController = class extends Destroyable {
13470
13618
  });
13471
13619
  await this.updateSelectedInputDevice(kind, deviceInfo);
13472
13620
  });
13473
- await this.setupTrackHandling();
13474
- this._initialized$.next(true);
13475
13621
  if (this.type === "answer" && this.sdpInit) {
13622
+ await this.setupRemoteTracks();
13623
+ this._initialized$.next(true);
13476
13624
  this.setupEventListeners();
13477
- await this.handleOfferReceived();
13625
+ this._isNegotiating$.next(true);
13626
+ await this._setRemoteDescription(this.sdpInit);
13627
+ } else {
13628
+ await this.setupTrackHandling();
13629
+ this._initialized$.next(true);
13478
13630
  }
13479
13631
  } catch (error) {
13480
13632
  logger$11.error("[RTCPeerConnectionController] Initialization error:", error);
@@ -13572,6 +13724,35 @@ var RTCPeerConnectionController = class extends Destroyable {
13572
13724
  default:
13573
13725
  }
13574
13726
  }
13727
+ /**
13728
+ * Accept an inbound call by creating the SDP answer.
13729
+ * Optionally override media options before the answer is generated.
13730
+ * Must be called after initialization for inbound (answer-type) connections.
13731
+ */
13732
+ async acceptInbound(mediaOverrides) {
13733
+ if (mediaOverrides) {
13734
+ const { audio, video, receiveAudio, receiveVideo } = mediaOverrides;
13735
+ this.options = {
13736
+ ...this.options,
13737
+ ...audio !== void 0 ? { audio } : {},
13738
+ ...video !== void 0 ? { video } : {},
13739
+ ...receiveAudio !== void 0 ? { receiveAudio } : {},
13740
+ ...receiveVideo !== void 0 ? { receiveVideo } : {}
13741
+ };
13742
+ this.transceiverController?.updateOptions({
13743
+ receiveAudio: this.receiveAudio,
13744
+ receiveVideo: this.receiveVideo
13745
+ });
13746
+ this.localStreamController.updateOptions({
13747
+ inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,
13748
+ inputVideoDeviceConstraints: this.inputVideoDeviceConstraints
13749
+ });
13750
+ }
13751
+ await this.setupLocalTracks();
13752
+ const { answerOptions } = this;
13753
+ logger$11.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
13754
+ await this.createAnswer(answerOptions);
13755
+ }
13575
13756
  async handleOfferReceived() {
13576
13757
  if (!this.sdpInit) throw new DependencyError("SDP initialization parameters are not set");
13577
13758
  this._isNegotiating$.next(true);
@@ -13646,52 +13827,51 @@ var RTCPeerConnectionController = class extends Destroyable {
13646
13827
  }
13647
13828
  async setupLocalTracks() {
13648
13829
  logger$11.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
13649
- let { localStream } = this;
13650
- if (!localStream) try {
13651
- localStream = await this.localStreamController.buildLocalStream();
13652
- } catch (error) {
13653
- logger$11.error("[RTCPeerConnectionController] Error building local stream:", error);
13654
- this._errors$.next(error);
13655
- }
13656
- if (localStream) {
13657
- if (this.transceiverController?.useAddStream ?? false) {
13658
- logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13659
- this.peerConnection?.addStream(localStream);
13660
- if (!this.isNegotiating) {
13661
- logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13662
- this.negotiationNeeded$.next();
13663
- }
13664
- return;
13830
+ const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
13831
+ if (this.transceiverController?.useAddStream ?? false) {
13832
+ logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13833
+ this.peerConnection?.addStream(localStream);
13834
+ if (!this.isNegotiating) {
13835
+ logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13836
+ this.negotiationNeeded$.next();
13665
13837
  }
13666
- for (const kind of ["audio", "video"]) {
13667
- const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13668
- index,
13669
- track
13670
- }));
13671
- for (const { index, track } of tracks) {
13672
- this.localStreamController.addTrackEndedListener(track);
13673
- if (this.transceiverController?.useAddTransceivers ?? false) {
13674
- const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13675
- await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13676
- } else {
13677
- logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13678
- this.peerConnection?.addTrack(track, localStream);
13679
- }
13838
+ return;
13839
+ }
13840
+ for (const kind of ["audio", "video"]) {
13841
+ const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13842
+ index,
13843
+ track
13844
+ }));
13845
+ for (const { index, track } of tracks) {
13846
+ this.localStreamController.addTrackEndedListener(track);
13847
+ if (this.transceiverController?.useAddTransceivers ?? false) {
13848
+ const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13849
+ await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13850
+ } else {
13851
+ logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13852
+ this.peerConnection?.addTrack(track, localStream);
13680
13853
  }
13681
13854
  }
13682
13855
  }
13683
13856
  }
13684
13857
  async getUserMedia(constraints) {
13685
- return this.options.getUserMedia?.(constraints) ?? navigator.mediaDevices.getUserMedia(constraints);
13858
+ return (this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices).getUserMedia(constraints);
13686
13859
  }
13687
13860
  async getDisplayMedia(options) {
13688
- return this.options.getDisplayMedia?.(options) ?? navigator.mediaDevices.getDisplayMedia(options);
13861
+ const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;
13862
+ if (!mediaDevices.getDisplayMedia) throw new DependencyError("getDisplayMedia is not supported by the current WebRTC provider");
13863
+ return mediaDevices.getDisplayMedia(options);
13689
13864
  }
13690
13865
  async setupRemoteTracks() {
13691
13866
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
13692
13867
  this.peerConnection.ontrack = (event) => {
13693
13868
  logger$11.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
13694
- this._remoteStream$.next(event.streams[0]);
13869
+ if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
13870
+ else {
13871
+ const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
13872
+ const newStream = new MediaStream([...existingTracks, event.track]);
13873
+ this._remoteStream$.next(newStream);
13874
+ }
13695
13875
  };
13696
13876
  await this.transceiverController?.setupRemoteTransceivers(this.type);
13697
13877
  }
@@ -13790,7 +13970,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13790
13970
  });
13791
13971
  }
13792
13972
  get mediaDirections() {
13793
- return this.transceiverController?.getMediaDirections() ?? {
13973
+ return this.transceiverController?.getMediaDirections() ?? this._remoteOfferMediaDirections ?? {
13794
13974
  audio: "inactive",
13795
13975
  video: "inactive"
13796
13976
  };
@@ -13826,7 +14006,10 @@ function isVertoAttachMessage(value) {
13826
14006
  return value.method === "verto.attach";
13827
14007
  }
13828
14008
  function isVertoAnswerInnerParams(value) {
13829
- return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.answer" && isObject(value.params) && hasProperty(value.params, "callID") && hasProperty(value.params, "sdp");
14009
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.answer" && isObject(value.params) && hasProperty(value.params, "callID");
14010
+ }
14011
+ function isVertoMediaInnerParams(value) {
14012
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.media" && isObject(value.params) && hasProperty(value.params, "callID") && hasProperty(value.params, "sdp");
13830
14013
  }
13831
14014
  function isVertoMediaParamsInnerParams(value) {
13832
14015
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.mediaParams" && isObject(value.params) && hasProperty(value.params, "mediaParams");
@@ -13850,11 +14033,12 @@ var VertoManager = class extends Destroyable {
13850
14033
  }
13851
14034
  };
13852
14035
  var WebRTCVertoManager = class extends VertoManager {
13853
- constructor(webRtcCallSession, attachManager, deviceController, options = {}) {
14036
+ constructor(webRtcCallSession, attachManager, deviceController, webRTCApiProvider, options = {}) {
13854
14037
  super(webRtcCallSession);
13855
14038
  this.webRtcCallSession = webRtcCallSession;
13856
14039
  this.attachManager = attachManager;
13857
14040
  this.deviceController = deviceController;
14041
+ this.webRTCApiProvider = webRTCApiProvider;
13858
14042
  this._rtcPeerConnections$ = this.createBehaviorSubject([]);
13859
14043
  this._selfId$ = this.createBehaviorSubject(null);
13860
14044
  this._signalingStatus$ = this.createBehaviorSubject(null);
@@ -13928,17 +14112,32 @@ var WebRTCVertoManager = class extends VertoManager {
13928
14112
  return rtcPeerConnection;
13929
14113
  }
13930
14114
  get signalingStatus$() {
13931
- return (0, import_cjs$10.merge)(this._signalingStatus$.pipe(filterNull()), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$10.filter)((connectionState) => [
14115
+ return this.cachedObservable("signalingStatus$", () => (0, import_cjs$10.merge)(this._signalingStatus$.pipe(filterNull()), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$10.filter)((connectionState) => [
13932
14116
  "connected",
13933
14117
  "disconnected",
13934
14118
  "failed"
13935
- ].includes(connectionState))));
14119
+ ].includes(connectionState)))));
13936
14120
  }
13937
14121
  initSubscriptions() {
14122
+ this.subscribeTo(this.callJoinedEvent$, (event) => {
14123
+ const memberNodeId = event.room_session.members.find((m) => m.call_id === event.call_id)?.node_id;
14124
+ if (memberNodeId) this.setNodeIdIfNull(memberNodeId);
14125
+ if (event.member_id) this.setSelfIdIfNull(event.member_id);
14126
+ });
14127
+ this.subscribeTo(this.vertoMedia$, (event) => {
14128
+ logger$10.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
14129
+ this._signalingStatus$.next("ringing");
14130
+ const { sdp, callID } = event;
14131
+ this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
14132
+ status: "received",
14133
+ sdp
14134
+ });
14135
+ });
13938
14136
  this.subscribeTo(this.vertoAnswer$, (event) => {
13939
14137
  logger$10.debug("[WebRTCManager] Received Verto answer event:", event);
13940
- const { sdp } = event;
13941
- this._rtcPeerConnectionsMap.get(event.callID)?.updateAnswerStatus({
14138
+ this._signalingStatus$.next("connecting");
14139
+ const { sdp, callID } = event;
14140
+ this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
13942
14141
  status: "received",
13943
14142
  sdp
13944
14143
  });
@@ -13956,6 +14155,28 @@ var WebRTCVertoManager = class extends VertoManager {
13956
14155
  this.sendVertoPong(vertoPing);
13957
14156
  });
13958
14157
  }
14158
+ /**
14159
+ * Set node_id/selfId only when the current value is null.
14160
+ *
14161
+ * During reattach, `call.joined` and `verto.answer` events can deliver
14162
+ * these identifiers before the `verto.invite` RPC response (`CALL CREATED`)
14163
+ * arrives. These methods let early events populate them eagerly so that
14164
+ * downstream RPC calls (e.g. `call.layout.list`) don't fail with empty
14165
+ * identifiers. `processInviteResponse()` remains the authoritative source
14166
+ * and always overwrites unconditionally.
14167
+ */
14168
+ setNodeIdIfNull(nodeId) {
14169
+ if (!this._nodeId$.value && nodeId) {
14170
+ logger$10.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
14171
+ this._nodeId$.next(nodeId);
14172
+ }
14173
+ }
14174
+ setSelfIdIfNull(selfId) {
14175
+ if (!this._selfId$.value && selfId) {
14176
+ logger$10.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
14177
+ this._selfId$.next(selfId);
14178
+ }
14179
+ }
13959
14180
  async sendVertoPong(vertoPing) {
13960
14181
  try {
13961
14182
  const vertoPongMessage = VertoPong({ ...vertoPing });
@@ -13979,20 +14200,26 @@ var WebRTCVertoManager = class extends VertoManager {
13979
14200
  get selfId() {
13980
14201
  return this._selfId$.value;
13981
14202
  }
14203
+ get callJoinedEvent$() {
14204
+ return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$10.filter)(isCallJoinedPayload), (0, import_cjs$10.takeUntil)(this.destroyed$));
14205
+ }
14206
+ get vertoMedia$() {
14207
+ return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14208
+ }
13982
14209
  get vertoAnswer$() {
13983
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14210
+ return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13984
14211
  }
13985
14212
  get vertoMediaParams$() {
13986
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14213
+ return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13987
14214
  }
13988
14215
  get vertoBye$() {
13989
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14216
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13990
14217
  }
13991
14218
  get vertoAttach$() {
13992
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14219
+ return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13993
14220
  }
13994
14221
  get vertoPing$() {
13995
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14222
+ return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13996
14223
  }
13997
14224
  async executeVerto(message, optionals = {}) {
13998
14225
  const webrtcVertoMessage = WebrtcVerto({
@@ -14005,19 +14232,19 @@ var WebRTCVertoManager = class extends VertoManager {
14005
14232
  if (response.error) {
14006
14233
  const error = new JSONRPCError(response.error.code, response.error.message, response.error.data);
14007
14234
  this.onError?.(error);
14008
- throw error;
14235
+ return response;
14009
14236
  }
14010
14237
  const innerResult = getValueFrom(response, "result.result");
14011
14238
  if (innerResult?.error) {
14012
14239
  const error = new JSONRPCError(innerResult.error.code, innerResult.error.message, innerResult.error.data);
14013
14240
  this.onError?.(error);
14014
- throw error;
14241
+ return response;
14015
14242
  }
14016
14243
  return response;
14017
14244
  }
14018
14245
  async sendLocalDescription(message, rtcPeerConnController) {
14019
14246
  const vertoMethod = message.method;
14020
- const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController);
14247
+ const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod);
14021
14248
  try {
14022
14249
  const response = await this.executeVerto(message, optionalsParams);
14023
14250
  switch (vertoMethod) {
@@ -14031,7 +14258,7 @@ var WebRTCVertoManager = class extends VertoManager {
14031
14258
  }
14032
14259
  } catch (error) {
14033
14260
  logger$10.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
14034
- throw error;
14261
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14035
14262
  }
14036
14263
  }
14037
14264
  async processModifyResponse(response, rtcPeerConnController) {
@@ -14045,12 +14272,14 @@ var WebRTCVertoManager = class extends VertoManager {
14045
14272
  });
14046
14273
  } catch (error) {
14047
14274
  logger$10.warn("[WebRTCManager] Error processing modify response:", error);
14048
- this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14275
+ const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
14276
+ this.onError?.(modifyError);
14049
14277
  }
14050
14278
  }
14051
14279
  }
14052
14280
  processInviteResponse(response, rtcPeerConnController) {
14053
14281
  if (!response.error && getValueFrom(response, "result.result.result.message") === "CALL CREATED") {
14282
+ this._signalingStatus$.next("trying");
14054
14283
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
14055
14284
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
14056
14285
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
@@ -14062,7 +14291,6 @@ var WebRTCVertoManager = class extends VertoManager {
14062
14291
  this._selfId$.next(memberId);
14063
14292
  rtcPeerConnController.setMemberId(memberId);
14064
14293
  if (callId) this.webRtcCallSession.addCallId(callId);
14065
- this._signalingStatus$.next("ringing");
14066
14294
  this.attachManager.attach(this.webRtcCallSession);
14067
14295
  logger$10.info("[WebRTCManager] Verto invite successful");
14068
14296
  logger$10.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
@@ -14094,6 +14322,7 @@ var WebRTCVertoManager = class extends VertoManager {
14094
14322
  inputVideoStream: options.inputVideoStream,
14095
14323
  receiveAudio: options.receiveAudio,
14096
14324
  receiveVideo: options.receiveVideo,
14325
+ webRTCApiProvider: this.webRTCApiProvider,
14097
14326
  ...this.RTCPeerConnectionConfig
14098
14327
  }, options.initOffer, this.deviceController);
14099
14328
  this.setupLocalDescriptionHandler(rtcPeerConnController);
@@ -14105,6 +14334,36 @@ var WebRTCVertoManager = class extends VertoManager {
14105
14334
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
14106
14335
  this.onError?.(error);
14107
14336
  });
14337
+ if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
14338
+ }
14339
+ async handleInboundAnswer(rtcPeerConnController) {
14340
+ logger$10.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
14341
+ const vertoByeOrAccepted = await (0, import_cjs$10.firstValueFrom)((0, import_cjs$10.race)(this.vertoBye$, this.webRtcCallSession.answered$).pipe((0, import_cjs$10.takeUntil)(this.destroyed$))).catch(() => null);
14342
+ if (vertoByeOrAccepted === null) {
14343
+ logger$10.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
14344
+ return;
14345
+ }
14346
+ if (isVertoByeMessage(vertoByeOrAccepted)) {
14347
+ logger$10.info("[WebRTCManager] Inbound call ended by remote before answer.");
14348
+ this.callSession?.destroy();
14349
+ } else if (!vertoByeOrAccepted) {
14350
+ logger$10.info("[WebRTCManager] Inbound call rejected by user.");
14351
+ try {
14352
+ await this.bye("USER_BUSY");
14353
+ } finally {
14354
+ this._signalingStatus$.next("disconnected");
14355
+ this.callSession?.destroy();
14356
+ }
14357
+ } else {
14358
+ logger$10.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
14359
+ const answerOptions = this.webRtcCallSession.answerMediaOptions;
14360
+ try {
14361
+ await rtcPeerConnController.acceptInbound(answerOptions);
14362
+ } catch (error) {
14363
+ logger$10.error("[WebRTCManager] Error creating inbound answer:", error);
14364
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14365
+ }
14366
+ }
14108
14367
  }
14109
14368
  setupVertoAttachHandler() {
14110
14369
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
@@ -14122,7 +14381,7 @@ var WebRTCVertoManager = class extends VertoManager {
14122
14381
  });
14123
14382
  }
14124
14383
  initObservables(rtcPeerConnController) {
14125
- this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe((0, import_cjs$10.filter)((state) => state === "connected"), (0, import_cjs$10.takeUntil)(this.destroyed$), (0, import_cjs$10.map)(() => rtcPeerConnController.mediaDirections));
14384
+ this.mediaDirections$ = rtcPeerConnController.connectionState$.pipe((0, import_cjs$10.filter)((state) => state === "connected"), (0, import_cjs$10.map)(() => rtcPeerConnController.mediaDirections), (0, import_cjs$10.startWith)(rtcPeerConnController.mediaDirections), (0, import_cjs$10.takeUntil)(this.destroyed$));
14126
14385
  this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14127
14386
  this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14128
14387
  }
@@ -14138,7 +14397,6 @@ var WebRTCVertoManager = class extends VertoManager {
14138
14397
  });
14139
14398
  this.sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnController);
14140
14399
  } else if (initial) {
14141
- this._signalingStatus$.next("trying");
14142
14400
  const vertoMessageRequest = VertoInvite({
14143
14401
  dialogParams,
14144
14402
  sdp
@@ -14156,22 +14414,23 @@ var WebRTCVertoManager = class extends VertoManager {
14156
14414
  }
14157
14415
  setupVertoByeHandler() {
14158
14416
  this.subscribeTo(this.vertoBye$, () => {
14417
+ this._signalingStatus$.next("disconnected");
14159
14418
  this.attachManager.detach(this.webRtcCallSession);
14160
14419
  this.callSession?.destroy();
14161
14420
  });
14162
14421
  }
14163
- getSendLocalSDPOptionalParams(rtcPeerConnController) {
14422
+ getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod) {
14164
14423
  let subscribe = void 0;
14165
- const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
14166
- if (initial) {
14424
+ if (!rtcPeerConnController.firstSDPExchangeCompleted) {
14167
14425
  subscribe = [];
14168
14426
  if (rtcPeerConnController.isMainDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeMainDevice);
14169
14427
  else if (rtcPeerConnController.isAdditionalDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);
14170
14428
  else if (rtcPeerConnController.isScreenShare) subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);
14171
14429
  }
14430
+ const isInvite = vertoMethod === "verto.invite";
14172
14431
  return {
14173
14432
  callID: rtcPeerConnController.id,
14174
- node_id: initial ? "" : this._nodeId$.value ?? "",
14433
+ node_id: isInvite ? "" : this._nodeId$.value ?? "",
14175
14434
  subscribe
14176
14435
  };
14177
14436
  }
@@ -14183,7 +14442,12 @@ var WebRTCVertoManager = class extends VertoManager {
14183
14442
  this.callSession?.destroy();
14184
14443
  } else if (!vertoByeOrAccepted) {
14185
14444
  logger$10.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
14186
- await this.bye("USER_BUSY");
14445
+ try {
14446
+ await this.bye("USER_BUSY");
14447
+ } finally {
14448
+ this._signalingStatus$.next("disconnected");
14449
+ this.callSession?.destroy();
14450
+ }
14187
14451
  } else {
14188
14452
  logger$10.debug("[WebRTCManager] Call accepted, sending answer");
14189
14453
  try {
@@ -14213,8 +14477,7 @@ var WebRTCVertoManager = class extends VertoManager {
14213
14477
  userVariables: {
14214
14478
  memberCallId: this.webRtcCallSession.id,
14215
14479
  memberId,
14216
- ...this.webRtcCallSession.options.userVariables,
14217
- ...PreferencesContainer.instance.userVariables
14480
+ ...this.webRtcCallSession.userVariables
14218
14481
  },
14219
14482
  screenShare: rtcPeerConnectionController.isScreenShare,
14220
14483
  additionalDevice: rtcPeerConnectionController.isAdditionalDevice,
@@ -14272,7 +14535,8 @@ var WebRTCVertoManager = class extends VertoManager {
14272
14535
  rtcPeerConnController = new RTCPeerConnectionController({
14273
14536
  ...options,
14274
14537
  ...this.RTCPeerConnectionConfig,
14275
- propose
14538
+ propose,
14539
+ webRTCApiProvider: this.webRTCApiProvider
14276
14540
  }, void 0, this.deviceController);
14277
14541
  this.setupLocalDescriptionHandler(rtcPeerConnController);
14278
14542
  if (propose === "screenshare") this._screenShareId = rtcPeerConnController.id;
@@ -14329,7 +14593,7 @@ var WebRTCVertoManager = class extends VertoManager {
14329
14593
  try {
14330
14594
  const causeParams = cause ? {
14331
14595
  cause,
14332
- cause_code: VertoByeCauseCodes[cause]
14596
+ causeCode: VertoByeCauseCodes[cause]
14333
14597
  } : {};
14334
14598
  await this.executeVerto(VertoBye({
14335
14599
  ...causeParams,
@@ -14412,6 +14676,20 @@ var ParticipantFactory = class {
14412
14676
  //#region src/core/entities/Call.ts
14413
14677
  var import_cjs$9 = require_cjs();
14414
14678
  const logger$9 = getLogger();
14679
+ const fromDestinationParams = (destination) => {
14680
+ if (!destination) return {};
14681
+ try {
14682
+ const url = new URL(`destination:${destination}`);
14683
+ const params = {};
14684
+ url.searchParams.forEach((value, key) => {
14685
+ params[key] = value;
14686
+ });
14687
+ return params;
14688
+ } catch (error) {
14689
+ logger$9.warn(`Failed to parse destination URI: ${destination}`, error);
14690
+ return {};
14691
+ }
14692
+ };
14415
14693
  /**
14416
14694
  * Concrete WebRTC call implementation.
14417
14695
  *
@@ -14425,27 +14703,50 @@ var WebRTCCall = class extends Destroyable {
14425
14703
  this.clientSession = clientSession;
14426
14704
  this.options = options;
14427
14705
  this.address = address;
14428
- this.participantsMap = /* @__PURE__ */ new Map();
14429
- this._errors$ = this.createSubject();
14706
+ this._errors$ = this.createReplaySubject(1);
14707
+ this._lastMergedStatus = "new";
14430
14708
  this._answered$ = this.createReplaySubject();
14431
14709
  this._holdState = false;
14710
+ this._userVariables$ = this.createBehaviorSubject({ ...PreferencesContainer.instance.userVariables });
14432
14711
  this.id = options.callId ?? v4_default();
14433
14712
  this.to = options.to;
14713
+ this._userVariables$.next({
14714
+ ...this._userVariables$.value,
14715
+ ...fromDestinationParams(options.to),
14716
+ ...options.userVariables
14717
+ });
14718
+ this.subscribeTo(this.webrtcMessages$, (message) => {
14719
+ const userVars = getValueFrom(message, "params.userVariables");
14720
+ if (userVars) this._userVariables$.next({
14721
+ ...this._userVariables$.value,
14722
+ ...userVars
14723
+ });
14724
+ });
14434
14725
  const managers = initialization.initializeManagers(this);
14435
14726
  this.vertoManager = managers.vertoManager;
14436
14727
  this.callEventsManager = managers.callEventsManager;
14437
- if (options.initOffer) this._status$ = this.createBehaviorSubject("ringing");
14438
- else this._status$ = this.createBehaviorSubject("new");
14728
+ if (options.initOffer) {
14729
+ this._status$ = this.createBehaviorSubject("ringing");
14730
+ this._lastMergedStatus = "ringing";
14731
+ } else this._status$ = this.createBehaviorSubject("new");
14439
14732
  const { deviceController } = initialization;
14440
14733
  this.participantFactory = new ParticipantFactory(this.executeMethod.bind(this), this.vertoManager, deviceController);
14441
14734
  }
14442
14735
  /** Observable stream of errors from media, signaling, and peer connection layers. */
14443
14736
  get errors$() {
14444
- return this._errors$.asObservable();
14737
+ return this.deferEmission(this._errors$.asObservable());
14445
14738
  }
14446
- /** @internal Push an error to the call's error stream. */
14447
- emitError(error) {
14448
- this._errors$.next(error);
14739
+ /**
14740
+ * @internal Push an error to the call's error stream.
14741
+ * Fatal errors automatically transition the call to `'failed'` and destroy it.
14742
+ */
14743
+ emitError(callError) {
14744
+ if (this._status$.value === "destroyed" || this._status$.value === "failed") return;
14745
+ this._errors$.next(callError);
14746
+ if (callError.fatal) {
14747
+ this._status$.next("failed");
14748
+ this.destroy();
14749
+ }
14449
14750
  }
14450
14751
  /** Whether this call is `'inbound'` or `'outbound'`. */
14451
14752
  get direction() {
@@ -14453,7 +14754,7 @@ var WebRTCCall = class extends Destroyable {
14453
14754
  }
14454
14755
  /** Observable of the address associated with this call. */
14455
14756
  get address$() {
14456
- return (0, import_cjs$9.from)([this.address]);
14757
+ return this.deferEmission((0, import_cjs$9.from)([this.address])).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14457
14758
  }
14458
14759
  /** Display name of the caller. */
14459
14760
  get fromName() {
@@ -14485,7 +14786,7 @@ var WebRTCCall = class extends Destroyable {
14485
14786
  }
14486
14787
  /** Current snapshot of all participants in the call. */
14487
14788
  get participants() {
14488
- return Array.from(this.participantsMap.values());
14789
+ return this.callEventsManager.participants;
14489
14790
  }
14490
14791
  /** The local participant, or `null` if not yet joined. */
14491
14792
  get self() {
@@ -14494,7 +14795,6 @@ var WebRTCCall = class extends Destroyable {
14494
14795
  async toggleLock() {
14495
14796
  const method = this.locked ? "call.unlock" : "call.lock";
14496
14797
  await this.executeMethod(this.selfId ?? "", method, {});
14497
- throw new UnimplementedError();
14498
14798
  }
14499
14799
  async toggleHold() {
14500
14800
  if (this._holdState) await this.vertoManager.unhold();
@@ -14515,7 +14815,7 @@ var WebRTCCall = class extends Destroyable {
14515
14815
  }
14516
14816
  /** Observable of layout layer positions for all participants. */
14517
14817
  get layoutLayers$() {
14518
- return this.callEventsManager.layoutLayers$;
14818
+ return this.deferEmission(this.callEventsManager.layoutLayers$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14519
14819
  }
14520
14820
  /** Current snapshot of layout layers. */
14521
14821
  get layoutLayers() {
@@ -14529,72 +14829,80 @@ var WebRTCCall = class extends Destroyable {
14529
14829
  params
14530
14830
  });
14531
14831
  try {
14532
- return await this.clientSession.execute(request);
14832
+ const response = await this.clientSession.execute(request);
14833
+ if (isJSONRPCErrorResponse(response)) throw new JSONRPCError(parseInt(response.result?.code ?? "0"), `Error response from method ${method}: ${response.result?.code} ${response.result?.message}`, void 0, void 0, request.id);
14834
+ return response;
14533
14835
  } catch (error) {
14534
14836
  logger$9.error(`[Call] Error executing method ${method} with params`, params, error);
14535
14837
  throw error;
14536
14838
  }
14537
14839
  }
14538
14840
  buildMethodParams(target, args) {
14539
- const reference = {
14540
- node_id: this.nodeId,
14541
- call_id: this.id
14841
+ const self = {
14842
+ node_id: this.nodeId ?? "",
14843
+ call_id: this.id,
14844
+ member_id: this.vertoManager.selfId ?? ""
14845
+ };
14846
+ if (typeof target === "object") return {
14847
+ ...args,
14848
+ self,
14849
+ targets: [target]
14542
14850
  };
14543
14851
  return {
14544
14852
  ...args,
14545
- self: {
14546
- ...reference,
14547
- member_id: this.vertoManager.selfId
14548
- },
14853
+ self,
14549
14854
  target: {
14550
- ...reference,
14855
+ node_id: this.nodeId ?? "",
14856
+ call_id: this.id,
14551
14857
  member_id: target
14552
14858
  }
14553
14859
  };
14554
14860
  }
14555
14861
  /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
14556
14862
  get status$() {
14557
- return (0, import_cjs$9.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$);
14863
+ return this.publicCachedObservable("status$", () => (0, import_cjs$9.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe((0, import_cjs$9.distinctUntilChanged)(), (0, import_cjs$9.tap)((status) => {
14864
+ this._lastMergedStatus = status;
14865
+ })));
14558
14866
  }
14559
14867
  /** Observable of the participants list, emits on join/leave/update. */
14560
14868
  get participants$() {
14561
- return this.callEventsManager.participants$;
14869
+ return this.deferEmission(this.callEventsManager.participants$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14562
14870
  }
14563
14871
  /** Observable of the local (self) participant. */
14564
14872
  get self$() {
14565
- return this.callEventsManager.self$;
14873
+ return this.deferEmission(this.callEventsManager.self$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14566
14874
  }
14567
14875
  /** Observable indicating whether the call is being recorded. */
14568
14876
  get recording$() {
14569
- return this.callEventsManager.recording$;
14877
+ return this.deferEmission(this.callEventsManager.recording$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14570
14878
  }
14571
14879
  /** Observable indicating whether the call is being streamed. */
14572
14880
  get streaming$() {
14573
- return this.callEventsManager.streaming$;
14881
+ return this.deferEmission(this.callEventsManager.streaming$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14574
14882
  }
14575
14883
  /** Observable indicating whether raise-hand priority is active. */
14576
14884
  get raiseHandPriority$() {
14577
- return this.callEventsManager.raiseHandPriority$;
14885
+ return this.deferEmission(this.callEventsManager.raiseHandPriority$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14578
14886
  }
14579
14887
  /** Observable indicating whether the call room is locked. */
14580
14888
  get locked$() {
14581
- return this.callEventsManager.locked$;
14889
+ return this.deferEmission(this.callEventsManager.locked$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14582
14890
  }
14583
14891
  /** Observable of custom metadata associated with the call. */
14584
14892
  get meta$() {
14585
- return this.callEventsManager.meta$;
14893
+ return this.deferEmission(this.callEventsManager.meta$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14586
14894
  }
14587
14895
  /** Observable of the call's capability flags. */
14588
14896
  get capabilities$() {
14589
- return this.callEventsManager.capabilities$;
14897
+ return this.deferEmission(this.callEventsManager.capabilities$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14590
14898
  }
14591
14899
  /** Observable of the current layout name. */
14592
14900
  get layout$() {
14593
- return this.callEventsManager.layout$;
14901
+ return this.deferEmission(this.callEventsManager.layout$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14594
14902
  }
14595
14903
  /** Current call status. */
14596
14904
  get status() {
14597
- return this._status$.value;
14905
+ return this._lastMergedStatus;
14598
14906
  }
14599
14907
  /** Whether the call is currently being recorded. */
14600
14908
  get recording() {
@@ -14622,7 +14930,7 @@ var WebRTCCall = class extends Destroyable {
14622
14930
  }
14623
14931
  /** Observable of available layout names. */
14624
14932
  get layouts$() {
14625
- return this.callEventsManager.layouts$;
14933
+ return this.deferEmission(this.callEventsManager.layouts$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14626
14934
  }
14627
14935
  /** Current snapshot of available layout names. */
14628
14936
  get layouts() {
@@ -14630,7 +14938,7 @@ var WebRTCCall = class extends Destroyable {
14630
14938
  }
14631
14939
  /** Observable of the local media stream (camera/microphone). */
14632
14940
  get localStream$() {
14633
- return this.vertoManager.localStream$;
14941
+ return this.deferEmission(this.vertoManager.localStream$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14634
14942
  }
14635
14943
  /** Current local media stream, or `null` if not available. */
14636
14944
  get localStream() {
@@ -14638,12 +14946,27 @@ var WebRTCCall = class extends Destroyable {
14638
14946
  }
14639
14947
  /** Observable of the remote media stream from the far end. */
14640
14948
  get remoteStream$() {
14641
- return this.vertoManager.remoteStream$;
14949
+ return this.deferEmission(this.vertoManager.remoteStream$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14642
14950
  }
14643
14951
  /** Current remote media stream, or `null` if not available. */
14644
14952
  get remoteStream() {
14645
14953
  return this.vertoManager.remoteStream;
14646
14954
  }
14955
+ /** Observable of custom user variables associated with the call. */
14956
+ get userVariables$() {
14957
+ return this.deferEmission(this._userVariables$.asObservable());
14958
+ }
14959
+ /** a copy of the current custom user variables of the call. */
14960
+ get userVariables() {
14961
+ return { ...this._userVariables$.value };
14962
+ }
14963
+ /** Merge current custom user variables of the call. */
14964
+ set userVariables(variables) {
14965
+ this._userVariables$.next({
14966
+ ...this._userVariables$.value,
14967
+ ...variables
14968
+ });
14969
+ }
14647
14970
  /** @internal */
14648
14971
  createParticipant(memberId, selfId) {
14649
14972
  if (memberId === (selfId ?? this.vertoManager.selfId)) return this.participantFactory.createSelfParticipant(memberId);
@@ -14651,14 +14974,14 @@ var WebRTCCall = class extends Destroyable {
14651
14974
  }
14652
14975
  /** Observable of the current audio/video send/receive directions. */
14653
14976
  get mediaDirections$() {
14654
- return this.vertoManager.mediaDirections$;
14977
+ return this.deferEmission(this.vertoManager.mediaDirections$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14655
14978
  }
14656
14979
  /** Current audio/video send/receive directions. */
14657
14980
  get mediaDirections() {
14658
14981
  return this.vertoManager.mediaDirections;
14659
14982
  }
14660
14983
  get participantsId$() {
14661
- return this.participants$.pipe((0, import_cjs$9.map)((participants) => participants.map((participant) => participant.id)));
14984
+ return this.cachedObservable("participantsId$", () => this.participants$.pipe((0, import_cjs$9.map)((participants) => participants.map((participant) => participant.id))));
14662
14985
  }
14663
14986
  /** Executes a raw JSON-RPC request on the client session. */
14664
14987
  async execute(request, options) {
@@ -14693,35 +15016,35 @@ var WebRTCCall = class extends Destroyable {
14693
15016
  }
14694
15017
  }
14695
15018
  get callSessionEvents$() {
14696
- return this.clientSession.signalingEvent$.pipe((0, import_cjs$9.filter)((event) => this.isCallSessionEvent(event)), (0, import_cjs$9.tap)((event) => logger$9.debug("[Call] Received call session event:", event)), (0, import_cjs$9.takeUntil)(this.destroyed$));
15019
+ return this.cachedObservable("callSessionEvents$", () => this.clientSession.signalingEvent$.pipe((0, import_cjs$9.filter)((event) => this.isCallSessionEvent(event)), (0, import_cjs$9.tap)((event) => logger$9.debug("[Call] Received call session event:", event)), (0, import_cjs$9.takeUntil)(this.destroyed$), (0, import_cjs$9.share)()));
14697
15020
  }
14698
15021
  /** Observable of call-updated events. */
14699
15022
  get callUpdated$() {
14700
- return this.callSessionEvents$.pipe(filterAs(isCallUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15023
+ return this.publicCachedObservable("callUpdated$", () => this.callSessionEvents$.pipe(filterAs(isCallUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14701
15024
  }
14702
15025
  /** Observable of member-joined events. */
14703
15026
  get memberJoined$() {
14704
- return this.callSessionEvents$.pipe(filterAs(isMemberJoinedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15027
+ return this.publicCachedObservable("memberJoined$", () => this.callSessionEvents$.pipe(filterAs(isMemberJoinedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14705
15028
  }
14706
15029
  /** Observable of member-left events. */
14707
15030
  get memberLeft$() {
14708
- return this.callSessionEvents$.pipe(filterAs(isMemberLeftMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15031
+ return this.publicCachedObservable("memberLeft$", () => this.callSessionEvents$.pipe(filterAs(isMemberLeftMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14709
15032
  }
14710
15033
  /** Observable of member-updated events (mute, volume, etc.). */
14711
15034
  get memberUpdated$() {
14712
- return this.callSessionEvents$.pipe(filterAs(isMemberUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15035
+ return this.publicCachedObservable("memberUpdated$", () => this.callSessionEvents$.pipe(filterAs(isMemberUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14713
15036
  }
14714
15037
  /** Observable of member-talking events (speech start/stop). */
14715
15038
  get memberTalking$() {
14716
- return this.callSessionEvents$.pipe(filterAs(isMemberTalkingMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15039
+ return this.publicCachedObservable("memberTalking$", () => this.callSessionEvents$.pipe(filterAs(isMemberTalkingMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14717
15040
  }
14718
15041
  /** Observable of call state-change events. */
14719
15042
  get callStates$() {
14720
- return this.callSessionEvents$.pipe(filterAs(isCallStateMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15043
+ return this.publicCachedObservable("callStates$", () => this.callSessionEvents$.pipe(filterAs(isCallStateMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14721
15044
  }
14722
15045
  /** Observable of layout-changed events. */
14723
15046
  get layoutUpdates$() {
14724
- return this.callSessionEvents$.pipe(filterAs(isLayoutChangedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15047
+ return this.publicCachedObservable("layoutUpdates$", () => this.callSessionEvents$.pipe(filterAs(isLayoutChangedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14725
15048
  }
14726
15049
  /** Underlying `RTCPeerConnection`, for advanced use cases. */
14727
15050
  get rtcPeerConnection() {
@@ -14729,19 +15052,19 @@ var WebRTCCall = class extends Destroyable {
14729
15052
  }
14730
15053
  /** Observable of raw signaling events as plain objects. */
14731
15054
  get signalingEvent$() {
14732
- return this.callEvent$.pipe((0, import_cjs$9.map)((event) => JSON.parse(JSON.stringify(event))));
15055
+ return this.publicCachedObservable("signalingEvent$", () => this.callEvent$.pipe((0, import_cjs$9.map)((event) => JSON.parse(JSON.stringify(event)))));
14733
15056
  }
14734
15057
  /** Observable of WebRTC-specific signaling messages. */
14735
15058
  get webrtcMessages$() {
14736
- return this.callSessionEvents$.pipe(filterAs(isWebrtcMessageMetadata, "params"), (0, import_cjs$9.tap)((event) => logger$9.debug("[Call] Event is a WebRTC message event:", event)), (0, import_cjs$9.takeUntil)(this.destroyed$));
15059
+ return this.cachedObservable("webrtcMessages$", () => this.callSessionEvents$.pipe(filterAs(isWebrtcMessageMetadata, "params"), (0, import_cjs$9.tap)((event) => logger$9.debug("[Call] Event is a WebRTC message event:", event)), (0, import_cjs$9.takeUntil)(this.destroyed$), (0, import_cjs$9.share)()));
14737
15060
  }
14738
15061
  /** Observable of call-level signaling events. */
14739
15062
  get callEvent$() {
14740
- return this.callSessionEvents$.pipe(filterAs(isSignalwireCallMetadata, "params"), (0, import_cjs$9.tap)((event) => logger$9.debug("[Call] Event is a call event:", event)), (0, import_cjs$9.takeUntil)(this.destroyed$));
15063
+ return this.cachedObservable("callEvent$", () => this.callSessionEvents$.pipe(filterAs(isSignalwireCallMetadata, "params"), (0, import_cjs$9.tap)((event) => logger$9.debug("[Call] Event is a call event:", event)), (0, import_cjs$9.takeUntil)(this.destroyed$), (0, import_cjs$9.share)()));
14741
15064
  }
14742
15065
  /** Observable of layout-changed signaling events. */
14743
15066
  get layoutEvent$() {
14744
- return this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params"));
15067
+ return this.cachedObservable("layoutEvent$", () => this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params")));
14745
15068
  }
14746
15069
  /** Hangs up the call and releases all resources. */
14747
15070
  async hangup() {
@@ -14749,7 +15072,6 @@ var WebRTCCall = class extends Destroyable {
14749
15072
  try {
14750
15073
  await this.vertoManager.bye();
14751
15074
  } finally {
14752
- this._status$.next("destroyed");
14753
15075
  this.destroy();
14754
15076
  }
14755
15077
  }
@@ -14757,17 +15079,22 @@ var WebRTCCall = class extends Destroyable {
14757
15079
  async sendDigits(dtmf) {
14758
15080
  return this.vertoManager.sendDigits(dtmf);
14759
15081
  }
14760
- /** Accepts an inbound call. */
14761
- answer() {
15082
+ /** Accepts an inbound call, optionally overriding media options for the answer. */
15083
+ answer(options) {
15084
+ this._answerMediaOptions = options;
14762
15085
  this._answered$.next(true);
14763
15086
  }
15087
+ /** Media options provided when answering. Used internally by the VertoManager. */
15088
+ get answerMediaOptions() {
15089
+ return this._answerMediaOptions;
15090
+ }
14764
15091
  /** Rejects an inbound call. */
14765
15092
  reject() {
14766
15093
  this._answered$.next(false);
14767
15094
  }
14768
15095
  /** Observable that emits `true` when answered, `false` when rejected. */
14769
15096
  get answered$() {
14770
- return this._answered$.asObservable();
15097
+ return this.deferEmission(this._answered$.asObservable());
14771
15098
  }
14772
15099
  /**
14773
15100
  * Sets the call layout and participant positions.
@@ -14788,9 +15115,10 @@ var WebRTCCall = class extends Destroyable {
14788
15115
  }
14789
15116
  /** Destroys the call, releasing all resources and subscriptions. */
14790
15117
  destroy() {
15118
+ if (this._status$.value === "destroyed") return;
15119
+ this._status$.next("destroyed");
14791
15120
  this.vertoManager.destroy();
14792
15121
  this.callEventsManager.destroy();
14793
- this.participantsMap.clear();
14794
15122
  super.destroy();
14795
15123
  }
14796
15124
  };
@@ -14798,14 +15126,32 @@ var WebRTCCall = class extends Destroyable {
14798
15126
  //#endregion
14799
15127
  //#region src/managers/CallFactory.ts
14800
15128
  /**
15129
+ * Infers the semantic error category from a raw Error thrown by VertoManager
15130
+ * or an RTCPeerConnection layer.
15131
+ */
15132
+ function inferCallErrorKind(error) {
15133
+ if (error instanceof RPCTimeoutError) return "timeout";
15134
+ if (error instanceof JSONRPCError) return "signaling";
15135
+ if (error instanceof MediaTrackError) return "media";
15136
+ if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError) return "network";
15137
+ return "internal";
15138
+ }
15139
+ /** Determines whether an error should be fatal (destroy the call). */
15140
+ function isFatalError(error) {
15141
+ if (error instanceof VertoPongError) return false;
15142
+ if (error instanceof MediaTrackError) return false;
15143
+ return true;
15144
+ }
15145
+ /**
14801
15146
  * Factory for creating WebRTCCall instances with proper manager wiring.
14802
15147
  * Eliminates circular dependencies by centralizing Call and Manager creation.
14803
15148
  */
14804
15149
  var CallFactory = class {
14805
- constructor(sessionManager, deviceController, attachManager) {
15150
+ constructor(sessionManager, deviceController, attachManager, webRTCApiProvider) {
14806
15151
  this.sessionManager = sessionManager;
14807
15152
  this.deviceController = deviceController;
14808
15153
  this.attachManager = attachManager;
15154
+ this.webRTCApiProvider = webRTCApiProvider;
14809
15155
  }
14810
15156
  /**
14811
15157
  * Create a new WebRTCCall with properly initialized managers
@@ -14814,10 +15160,16 @@ var CallFactory = class {
14814
15160
  return new WebRTCCall(this.sessionManager, options, {
14815
15161
  initializeManagers: (callInstance) => {
14816
15162
  return {
14817
- vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, {
15163
+ vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, this.webRTCApiProvider, {
14818
15164
  nodeId: options.nodeId,
14819
15165
  onError: (error) => {
14820
- callInstance.emitError(error);
15166
+ const callError = {
15167
+ kind: inferCallErrorKind(error),
15168
+ fatal: isFatalError(error),
15169
+ error,
15170
+ callId: callInstance.id
15171
+ };
15172
+ callInstance.emitError(callError);
14821
15173
  }
14822
15174
  }),
14823
15175
  callEventsManager: new CallEventsManager(callInstance)
@@ -14841,7 +15193,10 @@ var Fetcher = class {
14841
15193
  this.nextUrl = `${this.endpoint}?${params}`;
14842
15194
  }
14843
15195
  async next() {
14844
- if (!this.nextUrl) return [];
15196
+ if (!this.nextUrl) {
15197
+ this.hasMore = false;
15198
+ return [];
15199
+ }
14845
15200
  const response = await this.http.request({
14846
15201
  ...GET_PARAMS,
14847
15202
  url: this.nextUrl
@@ -14849,6 +15204,7 @@ var Fetcher = class {
14849
15204
  if (response.ok && !!response.body) {
14850
15205
  const result = JSON.parse(response.body);
14851
15206
  this.nextUrl = result.links.next;
15207
+ this.hasMore = !!this.nextUrl;
14852
15208
  return result.data.filter(this.filter).map(this.mapper);
14853
15209
  }
14854
15210
  logger$8.error("Failed to fetch entity");
@@ -14882,10 +15238,11 @@ var EntityCollection = class extends Destroyable {
14882
15238
  this.observablesRegistry.get(data.id)?.next(updated);
14883
15239
  this.values$.next(Array.from(this.collectionData.values()));
14884
15240
  };
15241
+ this._hasMore$ = this.createBehaviorSubject(true);
14885
15242
  this._destroy$ = new import_cjs$8.Subject();
14886
15243
  this.updateSubscription = this.update$.subscribe(this.upsertData);
14887
15244
  this.loading$.next(false);
14888
- this.hasMore$ = (0, import_cjs$8.defer)(() => (0, import_cjs$8.from)(this.init())).pipe((0, import_cjs$8.shareReplay)(1), (0, import_cjs$8.takeUntil)(this._destroy$));
15245
+ this.hasMore$ = (0, import_cjs$8.defer)(() => (0, import_cjs$8.from)(this.init())).pipe((0, import_cjs$8.switchMap)(() => this._hasMore$), (0, import_cjs$8.distinctUntilChanged)(), (0, import_cjs$8.shareReplay)(1), (0, import_cjs$8.takeUntil)(this._destroy$));
14889
15246
  }
14890
15247
  get loading() {
14891
15248
  return this.loading$.value;
@@ -14894,23 +15251,27 @@ var EntityCollection = class extends Destroyable {
14894
15251
  return this.fetchController.hasMore ?? true;
14895
15252
  }
14896
15253
  get updated$() {
14897
- return this.loading$.pipe((0, import_cjs$8.distinctUntilChanged)(), (0, import_cjs$8.skip)(1), (0, import_cjs$8.filter)((loading) => !loading), (0, import_cjs$8.map)(() => void 0), (0, import_cjs$8.takeUntil)(this._destroy$));
15254
+ return this.cachedObservable("updated$", () => this.loading$.pipe((0, import_cjs$8.distinctUntilChanged)(), (0, import_cjs$8.skip)(1), (0, import_cjs$8.filter)((loading) => !loading), (0, import_cjs$8.map)(() => void 0), (0, import_cjs$8.takeUntil)(this._destroy$)));
14898
15255
  }
14899
15256
  get values() {
14900
15257
  return Array.from(this.collectionData.values());
14901
15258
  }
14902
15259
  async init() {
14903
- if (this.fetchController.hasMore === false) return Promise.resolve(false);
15260
+ if (this.fetchController.hasMore === false) {
15261
+ this._hasMore$.next(false);
15262
+ return;
15263
+ }
14904
15264
  await this.fetchMore();
14905
- return this.fetchController.hasMore ?? true;
14906
15265
  }
14907
15266
  async fetchMore() {
14908
15267
  try {
14909
15268
  this.loading$.next(true);
14910
15269
  (await this.fetchController.next()).forEach(this.upsertData);
15270
+ this._hasMore$.next(this.fetchController.hasMore ?? false);
14911
15271
  this.loading$.next(false);
14912
15272
  } catch (error) {
14913
15273
  logger$8.error(`Failed to fetch initial collection data`, error);
15274
+ this._hasMore$.next(this.fetchController.hasMore ?? false);
14914
15275
  this.loading$.next(false);
14915
15276
  this.onError?.(new CollectionFetchError("fetchMore", error));
14916
15277
  }
@@ -14974,7 +15335,7 @@ var EntityCollectionTransformed = class {
14974
15335
  return this.originalCollection.values.filter(this.filter).map(this.mapper);
14975
15336
  }
14976
15337
  get values$() {
14977
- return this.originalCollection.values$.pipe((0, import_cjs$8.map)((values) => values.filter(this.filter).map(this.mapper)));
15338
+ return this._values$ ??= this.originalCollection.values$.pipe((0, import_cjs$8.map)((values) => values.filter(this.filter).map(this.mapper)));
14978
15339
  }
14979
15340
  get$(id) {
14980
15341
  const original$ = this.originalCollection.get$(id);
@@ -15048,7 +15409,7 @@ var Address = class extends Destroyable {
15048
15409
  }
15049
15410
  /** Observable of the human-readable display name. */
15050
15411
  get displayName$() {
15051
- return this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.display_name), (0, import_cjs$7.takeUntil)(this.destroyed$));
15412
+ return this.cachedObservable("displayName$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.display_name), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15052
15413
  }
15053
15414
  /** Human-readable display name. */
15054
15415
  get displayName() {
@@ -15057,7 +15418,7 @@ var Address = class extends Destroyable {
15057
15418
  }
15058
15419
  /** Observable of the preview image URL. */
15059
15420
  get previewUrl$() {
15060
- return this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.preview_url), (0, import_cjs$7.takeUntil)(this.destroyed$));
15421
+ return this.cachedObservable("previewUrl$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.preview_url), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15061
15422
  }
15062
15423
  /** Preview image URL. */
15063
15424
  get previewUrl() {
@@ -15066,7 +15427,7 @@ var Address = class extends Destroyable {
15066
15427
  }
15067
15428
  /** Observable of the cover image URL. */
15068
15429
  get coverUrl$() {
15069
- return this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.cover_url), (0, import_cjs$7.takeUntil)(this.destroyed$));
15430
+ return this.cachedObservable("coverUrl$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.cover_url), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15070
15431
  }
15071
15432
  /** Cover image URL. */
15072
15433
  get coverUrl() {
@@ -15075,7 +15436,7 @@ var Address = class extends Destroyable {
15075
15436
  }
15076
15437
  /** Observable of the underlying resource ID. */
15077
15438
  get resourceId$() {
15078
- return this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.resource_id), (0, import_cjs$7.takeUntil)(this.destroyed$));
15439
+ return this.cachedObservable("resourceId$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.resource_id), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15079
15440
  }
15080
15441
  /** Underlying resource ID. */
15081
15442
  get resourceId() {
@@ -15084,7 +15445,7 @@ var Address = class extends Destroyable {
15084
15445
  }
15085
15446
  /** Observable of the resource type (e.g. `'room'`, `'subscriber'`). */
15086
15447
  get type$() {
15087
- return this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.type), (0, import_cjs$7.takeUntil)(this.destroyed$));
15448
+ return this.cachedObservable("type$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.type), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15088
15449
  }
15089
15450
  /** Resource type (e.g. `'room'`, `'subscriber'`). */
15090
15451
  get type() {
@@ -15093,7 +15454,7 @@ var Address = class extends Destroyable {
15093
15454
  }
15094
15455
  /** Observable of available communication channels (audio, video, messaging). */
15095
15456
  get channels$() {
15096
- return this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.channels), (0, import_cjs$7.takeUntil)(this.destroyed$));
15457
+ return this.cachedObservable("channels$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.channels), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15097
15458
  }
15098
15459
  /** Available communication channels. */
15099
15460
  get channels() {
@@ -15107,7 +15468,7 @@ var Address = class extends Destroyable {
15107
15468
  }
15108
15469
  /** Observable indicating whether the address (room) is locked. */
15109
15470
  get locked$() {
15110
- return this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.locked), (0, import_cjs$7.takeUntil)(this.destroyed$));
15471
+ return this.cachedObservable("locked$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.shareReplay)(1), (0, import_cjs$7.map)((state) => state.locked), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15111
15472
  }
15112
15473
  /** Sends a text message to this address. */
15113
15474
  async sendText(text) {
@@ -15188,9 +15549,15 @@ var PendingRPC = class PendingRPC {
15188
15549
  return () => signal.removeEventListener("abort", abortHandler);
15189
15550
  }) : import_cjs$6.NEVER).subscribe({
15190
15551
  next: (response) => {
15191
- logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15192
15552
  isSettled = true;
15193
- resolve(response);
15553
+ if (response.error) {
15554
+ const rpcError = new JSONRPCError(response.error.code, response.error.message, response.error.data, void 0, request.id);
15555
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
15556
+ reject(rpcError);
15557
+ } else {
15558
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15559
+ resolve(response);
15560
+ }
15194
15561
  subscription.unsubscribe();
15195
15562
  },
15196
15563
  error: (error) => {
@@ -15230,7 +15597,7 @@ const getAddressSearchURI = (options) => {
15230
15597
  return name;
15231
15598
  };
15232
15599
  var ClientSessionManager = class extends Destroyable {
15233
- constructor(credential, transport, storage, authorizationStateKey, deviceController, attachManager) {
15600
+ constructor(credential, transport, storage, authorizationStateKey, deviceController, attachManager, webRTCApiProvider) {
15234
15601
  super();
15235
15602
  this.credential = credential;
15236
15603
  this.transport = transport;
@@ -15240,24 +15607,24 @@ var ClientSessionManager = class extends Destroyable {
15240
15607
  this.callCreateTimeout = 6e3;
15241
15608
  this.agent = `signalwire-typescript-sdk/1.0.0`;
15242
15609
  this.eventAcks = true;
15243
- this.authorization$ = this.createSubject();
15244
15610
  this.authorizationState$ = this.createReplaySubject(1);
15245
15611
  this.connectVersion = {
15246
15612
  major: 4,
15247
15613
  minor: 0,
15248
15614
  revision: 0
15249
15615
  };
15250
- this._errors$ = this.createSubject();
15616
+ this._authorization$ = this.createBehaviorSubject(void 0);
15617
+ this._errors$ = this.createReplaySubject(1);
15251
15618
  this._authenticated$ = this.createBehaviorSubject(false);
15252
15619
  this._subscriberInfo$ = this.createBehaviorSubject(null);
15253
15620
  this._calls$ = this.createBehaviorSubject({});
15254
15621
  this._iceServers$ = this.createBehaviorSubject([]);
15255
15622
  attachManager.setSession(this);
15256
- this.callFactory = new CallFactory(this, deviceController, attachManager);
15623
+ this.callFactory = new CallFactory(this, deviceController, attachManager, webRTCApiProvider);
15257
15624
  this.initialized$ = (0, import_cjs$5.defer)(() => (0, import_cjs$5.from)(this.init())).pipe((0, import_cjs$5.shareReplay)(1), (0, import_cjs$5.takeUntil)(this.destroyed$));
15258
15625
  }
15259
15626
  get incomingCalls$() {
15260
- return this.calls$.pipe((0, import_cjs$5.map)((calls) => calls.filter((call) => call.direction === "inbound")));
15627
+ return this.cachedObservable("incomingCalls$", () => this.calls$.pipe((0, import_cjs$5.map)((calls) => calls.filter((call) => call.direction === "inbound"))));
15261
15628
  }
15262
15629
  get incomingCalls() {
15263
15630
  return Object.values(this._calls$.value).filter((call) => call.direction === "inbound");
@@ -15269,7 +15636,7 @@ var ClientSessionManager = class extends Destroyable {
15269
15636
  return this._subscriberInfo$.value;
15270
15637
  }
15271
15638
  get calls$() {
15272
- return this._calls$.pipe((0, import_cjs$5.map)((calls) => Object.values(calls)));
15639
+ return this.cachedObservable("calls$", () => this._calls$.pipe((0, import_cjs$5.map)((calls) => Object.values(calls))));
15273
15640
  }
15274
15641
  get calls() {
15275
15642
  return Object.values(this._calls$.value);
@@ -15277,6 +15644,12 @@ var ClientSessionManager = class extends Destroyable {
15277
15644
  get iceServers() {
15278
15645
  return this._iceServers$.value;
15279
15646
  }
15647
+ get authorization$() {
15648
+ return this._authorization$.asObservable();
15649
+ }
15650
+ get authorization() {
15651
+ return this._authorization$.value;
15652
+ }
15280
15653
  get errors$() {
15281
15654
  return this._errors$.asObservable();
15282
15655
  }
@@ -15372,20 +15745,20 @@ var ClientSessionManager = class extends Destroyable {
15372
15745
  }
15373
15746
  }
15374
15747
  get authStateEvent$() {
15375
- return this.signalingEvent$.pipe((0, import_cjs$5.tap)((msg) => {
15748
+ return this.cachedObservable("authStateEvent$", () => this.signalingEvent$.pipe((0, import_cjs$5.tap)((msg) => {
15376
15749
  logger$6.debug("[Session] Received incoming message:", msg);
15377
15750
  }), filterAs(isSignalwireAuthorizationStateMetadata, "params"), (0, import_cjs$5.tap)((event) => {
15378
15751
  logger$6.debug("[Session] Authorization state event received:", event.authorization_state);
15379
- }));
15752
+ })));
15380
15753
  }
15381
15754
  get signalingEvent$() {
15382
- return this.transport.incomingEvent$.pipe(filterAs(isSignalwireRequest, "params"));
15755
+ return this.cachedObservable("signalingEvent$", () => this.transport.incomingEvent$.pipe(filterAs(isSignalwireRequest, "params"), (0, import_cjs$5.share)()));
15383
15756
  }
15384
15757
  get vertoInvite$() {
15385
- return this.signalingEvent$.pipe((0, import_cjs$5.filter)(isWebrtcMessageMetadata), (0, import_cjs$5.filter)((event) => isVertoInviteMessage(event.params)), (0, import_cjs$5.map)((event) => ({
15758
+ return this.cachedObservable("vertoInvite$", () => this.signalingEvent$.pipe((0, import_cjs$5.filter)(isWebrtcMessageMetadata), (0, import_cjs$5.filter)((event) => isVertoInviteMessage(event.params)), (0, import_cjs$5.map)((event) => ({
15386
15759
  node_id: event.node_id,
15387
15760
  ...event.params.params
15388
- })));
15761
+ }))));
15389
15762
  }
15390
15763
  get contexts() {
15391
15764
  return [];
@@ -15428,6 +15801,24 @@ var ClientSessionManager = class extends Destroyable {
15428
15801
  this._errors$.next(new AuthStateHandlerError(error));
15429
15802
  }
15430
15803
  }
15804
+ async reauthenticate(token) {
15805
+ logger$6.debug("[Session] Re-authenticating session");
15806
+ try {
15807
+ const request = RPCReauthenticate({
15808
+ project: this._authorization$.value?.project_id ?? "",
15809
+ jwt_token: token
15810
+ });
15811
+ await (0, import_cjs$5.lastValueFrom)((0, import_cjs$5.from)(this.transport.execute(request)).pipe(throwOnRPCError(), (0, import_cjs$5.take)(1), (0, import_cjs$5.catchError)((err) => {
15812
+ logger$6.error("[Session] Re-authentication RPC failed:", err);
15813
+ throw err;
15814
+ })));
15815
+ logger$6.debug("[Session] Re-authentication successful, updating stored auth state");
15816
+ } catch (error) {
15817
+ logger$6.error("[Session] Re-authentication failed:", error);
15818
+ this._errors$.next(new AuthStateHandlerError(error));
15819
+ throw error;
15820
+ }
15821
+ }
15431
15822
  async authenticate() {
15432
15823
  logger$6.debug("[Session] Starting authentication process");
15433
15824
  const params = {
@@ -15451,6 +15842,7 @@ var ClientSessionManager = class extends Destroyable {
15451
15842
  ...params,
15452
15843
  ...persistedParams
15453
15844
  });
15845
+ this.transport.resetSessionEpoch();
15454
15846
  const response = await (0, import_cjs$5.lastValueFrom)((0, import_cjs$5.from)(this.transport.execute(rpcConnectRequest)).pipe(throwOnRPCError(), (0, import_cjs$5.map)((res) => res.result), (0, import_cjs$5.filter)(isRPCConnectResult), (0, import_cjs$5.tap)(() => {
15455
15847
  logger$6.debug("[Session] Response passed filter, processing authentication result");
15456
15848
  }), (0, import_cjs$5.take)(1), (0, import_cjs$5.catchError)((err) => {
@@ -15463,7 +15855,7 @@ var ClientSessionManager = class extends Destroyable {
15463
15855
  hasIceServers: !!response.ice_servers
15464
15856
  });
15465
15857
  if (response.protocol) await this.transport.setProtocol(response.protocol);
15466
- this.authorization$.next(response.authorization);
15858
+ this._authorization$.next(response.authorization);
15467
15859
  this._iceServers$.next(response.ice_servers ?? []);
15468
15860
  this._authenticated$.next(true);
15469
15861
  logger$6.debug("[Session] Authentication completed successfully");
@@ -15482,7 +15874,8 @@ var ClientSessionManager = class extends Destroyable {
15482
15874
  to: invite.callee_id_number,
15483
15875
  fromName: invite.caller_id_name,
15484
15876
  from: invite.caller_id_number,
15485
- displayDirection: invite.display_direction
15877
+ displayDirection: invite.display_direction,
15878
+ userVariables: invite.userVariables
15486
15879
  });
15487
15880
  await (0, import_cjs$5.firstValueFrom)(callSession.status$);
15488
15881
  this._calls$.next({
@@ -15492,12 +15885,13 @@ var ClientSessionManager = class extends Destroyable {
15492
15885
  }
15493
15886
  async createOutboundCall(destination, options = {}) {
15494
15887
  const destinationURI = destination instanceof Address ? destination.defaultChannel : destination;
15888
+ let callSession;
15495
15889
  try {
15496
- const callSession = await this.createCall({
15890
+ callSession = await this.createCall({
15497
15891
  to: destinationURI,
15498
15892
  ...options
15499
15893
  });
15500
- await (0, import_cjs$5.firstValueFrom)(callSession.selfId$.pipe((0, import_cjs$5.filter)((id) => Boolean(id)), (0, import_cjs$5.take)(1), (0, import_cjs$5.timeout)(this.callCreateTimeout)));
15894
+ await (0, import_cjs$5.firstValueFrom)((0, import_cjs$5.race)(callSession.selfId$.pipe((0, import_cjs$5.filter)((id) => Boolean(id)), (0, import_cjs$5.take)(1), (0, import_cjs$5.timeout)(this.callCreateTimeout)), callSession.errors$.pipe((0, import_cjs$5.take)(1), (0, import_cjs$5.switchMap)((callError) => (0, import_cjs$5.throwError)(() => callError.error)))));
15501
15895
  this._calls$.next({
15502
15896
  [`${callSession.id}`]: callSession,
15503
15897
  ...this._calls$.value
@@ -15505,19 +15899,25 @@ var ClientSessionManager = class extends Destroyable {
15505
15899
  return callSession;
15506
15900
  } catch (error) {
15507
15901
  logger$6.error("[Session] Error creating outbound call:", error);
15508
- const callError = new CallCreateError("Call create timeout", error);
15902
+ callSession?.destroy();
15903
+ const callError = new CallCreateError(error instanceof import_cjs$5.TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
15509
15904
  this._errors$.next(callError);
15510
15905
  throw callError;
15511
15906
  }
15512
15907
  }
15513
15908
  async createCall(options = {}) {
15514
15909
  try {
15515
- if (!this._directory) throw new DependencyError("Directory not initialized");
15516
15910
  const addressURI = getAddressSearchURI(options);
15517
- const addressId = await this._directory.findAddressIdByURI(addressURI);
15518
- if (!addressId) throw new DependencyError(`Address name: ${addressURI} not found`);
15519
- const address = this._directory.get(addressId);
15520
- if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
15911
+ let address;
15912
+ try {
15913
+ if (!this._directory) throw new DependencyError("Directory not initialized");
15914
+ const addressId = await this._directory.findAddressIdByURI(addressURI);
15915
+ if (!addressId) throw new DependencyError(`Address name: ${addressURI} not found`);
15916
+ address = this._directory.get(addressId);
15917
+ if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
15918
+ } catch (error) {
15919
+ logger$6.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
15920
+ }
15521
15921
  const callSession = this.callFactory.createCall(address, { ...options });
15522
15922
  callSession.status$.pipe((0, import_cjs$5.filter)((status) => status === "destroyed"), (0, import_cjs$5.take)(1)).subscribe(() => {
15523
15923
  const { [`${callSession.id}`]: _, ...remainingCalls } = this._calls$.value;
@@ -15526,7 +15926,7 @@ var ClientSessionManager = class extends Destroyable {
15526
15926
  return callSession;
15527
15927
  } catch (error) {
15528
15928
  logger$6.error("[Session] Error creating call session:", error);
15529
- throw new CallCreateError("Call create error", error);
15929
+ throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
15530
15930
  }
15531
15931
  }
15532
15932
  destroy() {
@@ -15547,6 +15947,9 @@ var ClientSessionWrapper = class {
15547
15947
  get signalingEvent$() {
15548
15948
  return this.clientSessionManager.signalingEvent$;
15549
15949
  }
15950
+ get iceServers() {
15951
+ return this.clientSessionManager.iceServers;
15952
+ }
15550
15953
  async execute(request, options) {
15551
15954
  return this.clientSessionManager.execute(request, options);
15552
15955
  }
@@ -15772,7 +16175,7 @@ var WebSocketController = class WebSocketController extends Destroyable {
15772
16175
  this.shouldReconnect = false;
15773
16176
  this._status$ = this.createBehaviorSubject("disconnected");
15774
16177
  this._incomingMessages$ = this.createSubject();
15775
- this._errors$ = this.createSubject();
16178
+ this._errors$ = this.createReplaySubject(1);
15776
16179
  this.reconnectDelayMin = options.reconnectDelayMin ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MIN_MS;
15777
16180
  this.reconnectDelayMax = options.reconnectDelayMax ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MAX_MS;
15778
16181
  this.connectionTimeout = options.connectionTimeout ?? WebSocketController.DEFAULT_CONNECTION_TIMEOUT_MS;
@@ -15923,7 +16326,30 @@ function isSignalwirePingRequest(value) {
15923
16326
  //#region src/managers/TransportManager.ts
15924
16327
  var import_cjs$1 = require_cjs();
15925
16328
  const logger$2 = getLogger();
15926
- var TransportManager = class extends Destroyable {
16329
+ var TransportManager = class TransportManager extends Destroyable {
16330
+ /**
16331
+ * Normalise a server event timestamp to epoch seconds.
16332
+ *
16333
+ * The server uses two formats:
16334
+ * - `webrtc.message`: float epoch seconds (e.g. 1774372099.022817)
16335
+ * - all other events: int epoch microseconds (e.g. 1774372099925857)
16336
+ *
16337
+ * Values above 1e12 are treated as microseconds and divided by 1e6.
16338
+ */
16339
+ static toEpochSeconds(ts) {
16340
+ const n = typeof ts === "string" ? Number(ts) : ts;
16341
+ return n > 0xe8d4a51000 ? n / 1e6 : n;
16342
+ }
16343
+ /**
16344
+ * Extract the event timestamp from a signalwire.event message.
16345
+ * Returns `null` for messages that have no timestamp
16346
+ * (e.g. signalwire.authorization.state, RPC responses).
16347
+ */
16348
+ static extractEventTimestamp(message) {
16349
+ if (!isSignalwireRequest(message)) return null;
16350
+ if (message.params.event_type === "signalwire.authorization.state") return null;
16351
+ return TransportManager.toEpochSeconds(message.params.timestamp);
16352
+ }
15927
16353
  constructor(storage, protocolKey, webSocketConstructor, relayHost, onError) {
15928
16354
  super();
15929
16355
  this.storage = storage;
@@ -15956,6 +16382,23 @@ var TransportManager = class extends Destroyable {
15956
16382
  return true;
15957
16383
  });
15958
16384
  };
16385
+ this.discardStaleEvents = () => {
16386
+ return (0, import_cjs$1.filter)((message) => {
16387
+ const ts = TransportManager.extractEventTimestamp(message);
16388
+ if (ts === null) return true;
16389
+ if (this._sessionEpoch === null) {
16390
+ this._sessionEpoch = ts;
16391
+ return true;
16392
+ }
16393
+ if (ts < this._sessionEpoch) {
16394
+ const eventType = isSignalwireRequest(message) ? message.params.event_type : "unknown";
16395
+ logger$2.warn(`[Transport] Discarding stale event: ${eventType} (timestamp=${ts.toFixed(3)}, sessionEpoch=${this._sessionEpoch.toFixed(3)}, delta=${(this._sessionEpoch - ts).toFixed(3)}s)`);
16396
+ return false;
16397
+ }
16398
+ return true;
16399
+ });
16400
+ };
16401
+ this._sessionEpoch = null;
15959
16402
  this._outgoingMessages$ = this.createSubject();
15960
16403
  this._webSocketConnections = new WebSocketController(webSocketConstructor, relayHost, this._outgoingMessages$.asObservable(), {
15961
16404
  connectionTimeout: PreferencesContainer.instance.connectionTimeout,
@@ -15980,7 +16423,14 @@ var TransportManager = class extends Destroyable {
15980
16423
  return import_cjs$1.EMPTY;
15981
16424
  }), (0, import_cjs$1.share)(), (0, import_cjs$1.takeUntil)(this.destroyed$));
15982
16425
  this._jsonRPCResponse$ = this._jsonRPCMessage$.pipe((0, import_cjs$1.filter)(isJSONRPCResponse));
15983
- this._incomingEvent$ = this._jsonRPCMessage$.pipe(this.ackEvent(), this.replySignalwirePing(), (0, import_cjs$1.filter)((message) => !isJSONRPCResponse(message)), (0, import_cjs$1.share)(), (0, import_cjs$1.takeUntil)(this.destroyed$));
16426
+ this._incomingEvent$ = this._jsonRPCMessage$.pipe(this.ackEvent(), this.replySignalwirePing(), (0, import_cjs$1.filter)((message) => !isJSONRPCResponse(message)), this.discardStaleEvents(), (0, import_cjs$1.share)(), (0, import_cjs$1.takeUntil)(this.destroyed$));
16427
+ }
16428
+ /**
16429
+ * Reset the session epoch. Call this before each signalwire.connect
16430
+ * so that the first event after authentication establishes the new baseline.
16431
+ */
16432
+ resetSessionEpoch() {
16433
+ this._sessionEpoch = null;
15984
16434
  }
15985
16435
  async setProtocol(protocol) {
15986
16436
  this.protocol$.next(protocol);
@@ -16099,7 +16549,8 @@ const buildOptionsFromDestination = (destination) => {
16099
16549
  const channel = new URLSearchParams(queryString).get("channel");
16100
16550
  if (channel === "video") return {
16101
16551
  audio: true,
16102
- video: true
16552
+ video: true,
16553
+ receiveVideo: true
16103
16554
  };
16104
16555
  else if (channel === "audio") return {
16105
16556
  audio: true,
@@ -16129,7 +16580,7 @@ var SignalWire = class extends Destroyable {
16129
16580
  this._directory$ = this.createBehaviorSubject(void 0);
16130
16581
  this._isConnected$ = this.createBehaviorSubject(false);
16131
16582
  this._isRegistered$ = this.createBehaviorSubject(false);
16132
- this._errors$ = this.createSubject();
16583
+ this._errors$ = this.createReplaySubject(1);
16133
16584
  this._options = {};
16134
16585
  this._deps = new DependencyContainer();
16135
16586
  this._options = {
@@ -16139,6 +16590,7 @@ var SignalWire = class extends Destroyable {
16139
16590
  if (this._options.storageImplementation) this._deps.storageImpl = this._options.storageImplementation;
16140
16591
  if (this._options.webSocketConstructor) this._deps.WebSocket = this._options.webSocketConstructor;
16141
16592
  if (this._options.savePreferences) this.preferences.enableSavePreferences(this._deps.storage);
16593
+ if (this._options.webRTCApiProvider) this._deps.webRTCApiProvider = this._options.webRTCApiProvider;
16142
16594
  this._deviceController = this._deps.deviceController;
16143
16595
  if (!this._options.skipDeviceMonitoring) this._deviceController.enableDeviceMonitoring();
16144
16596
  this.subscribeTo(this._deviceController.errors$, (error) => {
@@ -16172,7 +16624,10 @@ var SignalWire = class extends Destroyable {
16172
16624
  throw new InvalidCredentialsError("Provided credentials have expired.");
16173
16625
  }
16174
16626
  if (_credentials.expiry_at && credentialProvider.refresh) {
16175
- const refreshFn = credentialProvider.refresh;
16627
+ const refreshFn = async () => {
16628
+ if (!credentialProvider.refresh) throw new InvalidCredentialsError("Credential provider does not support refresh");
16629
+ return credentialProvider.refresh();
16630
+ };
16176
16631
  const refreshInterval = Math.max(_credentials.expiry_at - Date.now() - 5e3, 1e3);
16177
16632
  this._refreshTimerId = setTimeout(async () => {
16178
16633
  try {
@@ -16190,12 +16645,24 @@ var SignalWire = class extends Destroyable {
16190
16645
  }, refreshInterval);
16191
16646
  }
16192
16647
  this._deps.credential = _credentials;
16648
+ if (this.isConnected && this._clientSession.authenticated && _credentials.token) try {
16649
+ await this._clientSession.reauthenticate(_credentials.token);
16650
+ logger$1.info("[SignalWire] Session refreshed with new credentials.");
16651
+ } catch (error) {
16652
+ logger$1.error("[SignalWire] Failed to refresh session with new credentials:", error);
16653
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16654
+ }
16193
16655
  }
16194
16656
  async init() {
16195
16657
  this._subscriber$.next(new Subscriber(this._deps.http));
16196
16658
  if (!this._options.skipConnection) await this.connect();
16197
- if (this._options.skipReconnect && this._attachManager) await this._attachManager.flush();
16198
- if (!this._options.skipRegister) this.register();
16659
+ if (!this._options.reconnectAttachedCalls && this._attachManager) await this._attachManager.flush();
16660
+ if (!this._options.skipRegister) try {
16661
+ await this.register();
16662
+ } catch (error) {
16663
+ logger$1.error("[SignalWire] Registration failed:", error);
16664
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16665
+ }
16199
16666
  this.handleAttachments();
16200
16667
  }
16201
16668
  async handleAttachments() {
@@ -16263,7 +16730,7 @@ var SignalWire = class extends Destroyable {
16263
16730
  };
16264
16731
  this._transport = new TransportManager(this._deps.storage, this._deps.protocolKey, this._deps.WebSocket, PreferencesContainer.instance.relayHost ?? this._deps.relayHost, errorHandler);
16265
16732
  this._attachManager = new AttachManager(this._deps.storage, this._deps.deviceController, PreferencesContainer.instance.reconnectCallsTimeout, this._deps.attachedCallsKey);
16266
- this._clientSession = new ClientSessionManager(this._deps.credential, this._transport, this._deps.storage, this._deps.authorizationStateKey, this._deps.deviceController, this._attachManager);
16733
+ this._clientSession = new ClientSessionManager(this._deps.credential, this._transport, this._deps.storage, this._deps.authorizationStateKey, this._deps.deviceController, this._attachManager, this._deps.webRTCApiProvider);
16267
16734
  this._publicSession = new ClientSessionWrapper(this._clientSession);
16268
16735
  this.subscribeTo(this._clientSession.errors$, (error) => {
16269
16736
  this._errors$.next(error);
@@ -16287,7 +16754,7 @@ var SignalWire = class extends Destroyable {
16287
16754
  * ```
16288
16755
  */
16289
16756
  get subscriber$() {
16290
- return this._subscriber$.asObservable();
16757
+ return this.deferEmission(this._subscriber$.asObservable());
16291
16758
  }
16292
16759
  /** Current subscriber snapshot, or `undefined` if not yet authenticated. */
16293
16760
  get subscriber() {
@@ -16306,7 +16773,7 @@ var SignalWire = class extends Destroyable {
16306
16773
  * ```
16307
16774
  */
16308
16775
  get directory$() {
16309
- return this._directory$.asObservable();
16776
+ return this.deferEmission(this._directory$.asObservable());
16310
16777
  }
16311
16778
  /**
16312
16779
  * Current directory snapshot, or `undefined` if the client is not yet connected.
@@ -16317,7 +16784,7 @@ var SignalWire = class extends Destroyable {
16317
16784
  }
16318
16785
  /** Observable that emits when the subscriber registration state changes. */
16319
16786
  get isRegistered$() {
16320
- return this._isRegistered$.asObservable();
16787
+ return this.deferEmission(this._isRegistered$.asObservable());
16321
16788
  }
16322
16789
  /** Whether the subscriber is currently registered. */
16323
16790
  get isRegistered() {
@@ -16329,15 +16796,15 @@ var SignalWire = class extends Destroyable {
16329
16796
  }
16330
16797
  /** Observable that emits when the connection state changes. */
16331
16798
  get isConnected$() {
16332
- return this._isConnected$.asObservable();
16799
+ return this.deferEmission(this._isConnected$.asObservable());
16333
16800
  }
16334
16801
  /** Observable that emits `true` when the client is both connected and authenticated. */
16335
16802
  get ready$() {
16336
- return this._isConnected$.pipe((0, import_cjs.switchMap)((connected) => connected ? this._clientSession.authenticated$ : (0, import_cjs.of)(false)));
16803
+ return this.publicCachedObservable("ready$", () => this._isConnected$.pipe((0, import_cjs.switchMap)((connected) => connected ? this._clientSession.authenticated$ : (0, import_cjs.of)(false))));
16337
16804
  }
16338
16805
  /** Observable stream of errors from transport, authentication, and devices. */
16339
16806
  get errors$() {
16340
- return this._errors$.asObservable();
16807
+ return this.deferEmission(this._errors$.asObservable());
16341
16808
  }
16342
16809
  /** Disconnects the WebSocket and tears down the session. */
16343
16810
  async disconnect() {
@@ -16358,8 +16825,22 @@ var SignalWire = class extends Destroyable {
16358
16825
  }));
16359
16826
  this._isRegistered$.next(true);
16360
16827
  } catch (error) {
16361
- logger$1.error("[SignalWire] Failed to register subscriber:", error);
16828
+ logger$1.debug("[SignalWire] Failed to register subscriber, trying reauthentication...");
16829
+ if (this._deps.credential.token) this._clientSession.reauthenticate(this._deps.credential.token).then(async () => {
16830
+ logger$1.debug("[SignalWire] Reauthentication successful, retrying register()");
16831
+ await this._transport.execute(RPCExecute({
16832
+ method: "subscriber.online",
16833
+ params: {}
16834
+ }));
16835
+ this._isRegistered$.next(true);
16836
+ }).catch((reauthError) => {
16837
+ logger$1.error("[SignalWire] Reauthentication failed during register():", reauthError);
16838
+ 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 }) });
16839
+ this._errors$.next(registerError);
16840
+ throw registerError;
16841
+ });
16362
16842
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16843
+ throw error;
16363
16844
  }
16364
16845
  }
16365
16846
  /** Unregisters the subscriber, going offline for inbound calls. */
@@ -16398,7 +16879,7 @@ var SignalWire = class extends Destroyable {
16398
16879
  }
16399
16880
  /** Observable list of available audio input (microphone) devices. */
16400
16881
  get audioInputDevices$() {
16401
- return this._deviceController.audioInputDevices$;
16882
+ return this.deferEmission(this._deviceController.audioInputDevices$);
16402
16883
  }
16403
16884
  /** Current snapshot of available audio input devices. */
16404
16885
  get audioInputDevices() {
@@ -16406,7 +16887,7 @@ var SignalWire = class extends Destroyable {
16406
16887
  }
16407
16888
  /** Observable list of available audio output (speaker) devices. */
16408
16889
  get audioOutputDevices$() {
16409
- return this._deviceController.audioOutputDevices$;
16890
+ return this.deferEmission(this._deviceController.audioOutputDevices$);
16410
16891
  }
16411
16892
  /** Current snapshot of available audio output devices. */
16412
16893
  get audioOutputDevices() {
@@ -16414,7 +16895,7 @@ var SignalWire = class extends Destroyable {
16414
16895
  }
16415
16896
  /** Observable list of available video input (camera) devices. */
16416
16897
  get videoInputDevices$() {
16417
- return this._deviceController.videoInputDevices$;
16898
+ return this.deferEmission(this._deviceController.videoInputDevices$);
16418
16899
  }
16419
16900
  /** Current snapshot of available video input devices. */
16420
16901
  get videoInputDevices() {
@@ -16422,15 +16903,15 @@ var SignalWire = class extends Destroyable {
16422
16903
  }
16423
16904
  /** Observable of the currently selected audio input device. */
16424
16905
  get selectedAudioInputDevice$() {
16425
- return this._deviceController.selectedAudioInputDevice$;
16906
+ return this.deferEmission(this._deviceController.selectedAudioInputDevice$);
16426
16907
  }
16427
16908
  /** Observable of the currently selected audio output device. */
16428
16909
  get selectedAudioOutputDevice$() {
16429
- return this._deviceController.selectedAudioOutputDevice$;
16910
+ return this.deferEmission(this._deviceController.selectedAudioOutputDevice$);
16430
16911
  }
16431
16912
  /** Observable of the currently selected video input device. */
16432
16913
  get selectedVideoInputDevice$() {
16433
- return this._deviceController.selectedVideoInputDevice$;
16914
+ return this.deferEmission(this._deviceController.selectedVideoInputDevice$);
16434
16915
  }
16435
16916
  /** Currently selected audio input device, or `null` if none. */
16436
16917
  get selectedAudioInputDevice() {
@@ -16629,13 +17110,16 @@ exports.Address = Address;
16629
17110
  exports.CallCreateError = CallCreateError;
16630
17111
  exports.ClientPreferences = ClientPreferences;
16631
17112
  exports.CollectionFetchError = CollectionFetchError;
17113
+ exports.InvalidCredentialsError = InvalidCredentialsError;
16632
17114
  exports.MediaTrackError = MediaTrackError;
16633
17115
  exports.MessageParseError = MessageParseError;
16634
17116
  exports.Participant = Participant;
17117
+ exports.SelfCapabilities = SelfCapabilities;
16635
17118
  exports.SelfParticipant = SelfParticipant;
16636
17119
  exports.SignalWire = SignalWire;
16637
17120
  exports.StaticCredentialProvider = StaticCredentialProvider;
16638
17121
  exports.Subscriber = Subscriber;
17122
+ exports.UnexpectedError = UnexpectedError;
16639
17123
  exports.VertoPongError = VertoPongError;
16640
17124
  exports.WebRTCCall = WebRTCCall;
16641
17125
  exports.embeddableCall = embeddableCall;