@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.
package/dist/browser.mjs CHANGED
@@ -2879,7 +2879,7 @@ var require_observeOn = /* @__PURE__ */ __commonJSMin(((exports) => {
2879
2879
  var executeSchedule_1$7 = require_executeSchedule();
2880
2880
  var lift_1$67 = require_lift();
2881
2881
  var OperatorSubscriber_1$56 = require_OperatorSubscriber();
2882
- function observeOn(scheduler, delay$1) {
2882
+ function observeOn$1(scheduler, delay$1) {
2883
2883
  if (delay$1 === void 0) delay$1 = 0;
2884
2884
  return lift_1$67.operate(function(source, subscriber) {
2885
2885
  source.subscribe(OperatorSubscriber_1$56.createOperatorSubscriber(subscriber, function(value) {
@@ -2897,7 +2897,7 @@ var require_observeOn = /* @__PURE__ */ __commonJSMin(((exports) => {
2897
2897
  }));
2898
2898
  });
2899
2899
  }
2900
- exports.observeOn = observeOn;
2900
+ exports.observeOn = observeOn$1;
2901
2901
  }));
2902
2902
 
2903
2903
  //#endregion
@@ -3107,7 +3107,7 @@ var require_throwError = /* @__PURE__ */ __commonJSMin(((exports) => {
3107
3107
  exports.throwError = void 0;
3108
3108
  var Observable_1$18 = require_Observable();
3109
3109
  var isFunction_1$14 = require_isFunction();
3110
- function throwError(errorOrErrorFactory, scheduler) {
3110
+ function throwError$1(errorOrErrorFactory, scheduler) {
3111
3111
  var errorFactory = isFunction_1$14.isFunction(errorOrErrorFactory) ? errorOrErrorFactory : function() {
3112
3112
  return errorOrErrorFactory;
3113
3113
  };
@@ -3118,7 +3118,7 @@ var require_throwError = /* @__PURE__ */ __commonJSMin(((exports) => {
3118
3118
  return scheduler.schedule(init, 0, subscriber);
3119
3119
  } : init);
3120
3120
  }
3121
- exports.throwError = throwError;
3121
+ exports.throwError = throwError$1;
3122
3122
  }));
3123
3123
 
3124
3124
  //#endregion
@@ -4327,13 +4327,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
4327
4327
  var innerFrom_1$28 = require_innerFrom();
4328
4328
  var argsOrArgArray_1$4 = require_argsOrArgArray();
4329
4329
  var OperatorSubscriber_1$48 = require_OperatorSubscriber();
4330
- function race$3() {
4330
+ function race$4() {
4331
4331
  var sources = [];
4332
4332
  for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
4333
4333
  sources = argsOrArgArray_1$4.argsOrArgArray(sources);
4334
4334
  return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
4335
4335
  }
4336
- exports.race = race$3;
4336
+ exports.race = race$4;
4337
4337
  function raceInit(sources) {
4338
4338
  return function(subscriber) {
4339
4339
  var subscriptions = [];
@@ -5470,7 +5470,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5470
5470
  var identity_1$10 = require_identity();
5471
5471
  var lift_1$42 = require_lift();
5472
5472
  var OperatorSubscriber_1$31 = require_OperatorSubscriber();
5473
- function distinctUntilChanged$5(comparator, keySelector) {
5473
+ function distinctUntilChanged$6(comparator, keySelector) {
5474
5474
  if (keySelector === void 0) keySelector = identity_1$10.identity;
5475
5475
  comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
5476
5476
  return lift_1$42.operate(function(source, subscriber) {
@@ -5486,7 +5486,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5486
5486
  }));
5487
5487
  });
5488
5488
  }
5489
- exports.distinctUntilChanged = distinctUntilChanged$5;
5489
+ exports.distinctUntilChanged = distinctUntilChanged$6;
5490
5490
  function defaultCompare(a, b) {
5491
5491
  return a === b;
5492
5492
  }
@@ -6685,7 +6685,7 @@ var require_share = /* @__PURE__ */ __commonJSMin(((exports) => {
6685
6685
  var Subject_1$6 = require_Subject();
6686
6686
  var Subscriber_1$1 = require_Subscriber();
6687
6687
  var lift_1$19 = require_lift();
6688
- function share$1(options) {
6688
+ function share$3(options) {
6689
6689
  if (options === void 0) options = {};
6690
6690
  var _a = options.connector, connector = _a === void 0 ? function() {
6691
6691
  return new Subject_1$6.Subject();
@@ -6743,7 +6743,7 @@ var require_share = /* @__PURE__ */ __commonJSMin(((exports) => {
6743
6743
  })(wrapperSource);
6744
6744
  };
6745
6745
  }
6746
- exports.share = share$1;
6746
+ exports.share = share$3;
6747
6747
  function handleReset(reset, on) {
6748
6748
  var args = [];
6749
6749
  for (var _i = 2; _i < arguments.length; _i++) args[_i - 2] = arguments[_i];
@@ -6915,7 +6915,7 @@ var require_startWith = /* @__PURE__ */ __commonJSMin(((exports) => {
6915
6915
  var concat_1$2 = require_concat$1();
6916
6916
  var args_1$2 = require_args();
6917
6917
  var lift_1$14 = require_lift();
6918
- function startWith() {
6918
+ function startWith$1() {
6919
6919
  var values = [];
6920
6920
  for (var _i = 0; _i < arguments.length; _i++) values[_i] = arguments[_i];
6921
6921
  var scheduler = args_1$2.popScheduler(values);
@@ -6923,7 +6923,7 @@ var require_startWith = /* @__PURE__ */ __commonJSMin(((exports) => {
6923
6923
  (scheduler ? concat_1$2.concat(values, source, scheduler) : concat_1$2.concat(values, source)).subscribe(subscriber);
6924
6924
  });
6925
6925
  }
6926
- exports.startWith = startWith;
6926
+ exports.startWith = startWith$1;
6927
6927
  }));
6928
6928
 
6929
6929
  //#endregion
@@ -6934,7 +6934,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
6934
6934
  var innerFrom_1$6 = require_innerFrom();
6935
6935
  var lift_1$13 = require_lift();
6936
6936
  var OperatorSubscriber_1$11 = require_OperatorSubscriber();
6937
- function switchMap$2(project, resultSelector) {
6937
+ function switchMap$4(project, resultSelector) {
6938
6938
  return lift_1$13.operate(function(source, subscriber) {
6939
6939
  var innerSubscriber = null;
6940
6940
  var index = 0;
@@ -6958,7 +6958,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
6958
6958
  }));
6959
6959
  });
6960
6960
  }
6961
- exports.switchMap = switchMap$2;
6961
+ exports.switchMap = switchMap$4;
6962
6962
  }));
6963
6963
 
6964
6964
  //#endregion
@@ -8941,11 +8941,55 @@ var Destroyable = class {
8941
8941
  this._destroyed$ = new import_cjs$22.Subject();
8942
8942
  }
8943
8943
  destroy() {
8944
+ this._observableCache?.clear();
8944
8945
  this.subscriptions.forEach((sub) => sub.unsubscribe());
8945
8946
  this.subjects.forEach((subject) => subject.complete());
8946
8947
  this._destroyed$.next();
8947
8948
  this._destroyed$.complete();
8948
8949
  }
8950
+ cachedObservable(key, factory) {
8951
+ this._observableCache ??= /* @__PURE__ */ new Map();
8952
+ let cached = this._observableCache.get(key);
8953
+ if (!cached) {
8954
+ cached = factory();
8955
+ this._observableCache.set(key, cached);
8956
+ }
8957
+ return cached;
8958
+ }
8959
+ /**
8960
+ * Like `cachedObservable`, but defers emissions to the microtask queue
8961
+ * via `observeOn(asapScheduler)`.
8962
+ *
8963
+ * Use ONLY for public-facing observable getters that external consumers
8964
+ * subscribe to. Prevents a class of bugs where `BehaviorSubject` or
8965
+ * `ReplaySubject` replays synchronously during `subscribe()`, before
8966
+ * the subscription variable is assigned in the caller's scope.
8967
+ *
8968
+ * Do NOT use for observables consumed internally by the SDK — internal
8969
+ * code using `subscribeTo()`, `firstValueFrom()`, or `withLatestFrom()`
8970
+ * depends on synchronous emission delivery.
8971
+ */
8972
+ publicCachedObservable(key, factory) {
8973
+ const publicKey = `public:${key}`;
8974
+ this._observableCache ??= /* @__PURE__ */ new Map();
8975
+ let cached = this._observableCache.get(publicKey);
8976
+ if (!cached) {
8977
+ cached = factory().pipe((0, import_cjs$22.observeOn)(import_cjs$22.asapScheduler));
8978
+ this._observableCache.set(publicKey, cached);
8979
+ }
8980
+ return cached;
8981
+ }
8982
+ /**
8983
+ * Wraps an observable so emissions are deferred to the microtask queue.
8984
+ *
8985
+ * Use ONLY for public-facing getters that expose a subject via
8986
+ * `.asObservable()` without going through `cachedObservable`.
8987
+ *
8988
+ * Do NOT use for observables consumed internally by the SDK.
8989
+ */
8990
+ deferEmission(observable) {
8991
+ return observable.pipe((0, import_cjs$22.observeOn)(import_cjs$22.asapScheduler));
8992
+ }
8949
8993
  subscribeTo(observable, observerOrNext) {
8950
8994
  const subscription = observable.subscribe(observerOrNext);
8951
8995
  this.subscriptions.push(subscription);
@@ -8966,7 +9010,7 @@ var Destroyable = class {
8966
9010
  return subject;
8967
9011
  }
8968
9012
  get $() {
8969
- 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));
9013
+ 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)));
8970
9014
  }
8971
9015
  /**
8972
9016
  * Observable that emits when the instance is destroyed
@@ -9094,21 +9138,23 @@ var DependencyError = class extends Error {
9094
9138
  }
9095
9139
  };
9096
9140
  var CallCreateError = class extends Error {
9097
- constructor(message, error = null, options) {
9141
+ constructor(message, error = null, direction = "outbound", options) {
9098
9142
  super(message, {
9099
9143
  ...options,
9100
9144
  cause: options?.cause ?? (error instanceof Error ? error : void 0)
9101
9145
  });
9102
9146
  this.message = message;
9103
9147
  this.error = error;
9148
+ this.direction = direction;
9104
9149
  this.name = "CallCreateError";
9105
9150
  }
9106
9151
  };
9107
9152
  var JSONRPCError = class extends Error {
9108
- constructor(code, message, data, options) {
9153
+ constructor(code, message, data, options, requestId) {
9109
9154
  super(message, options);
9110
9155
  this.code = code;
9111
9156
  this.data = data;
9157
+ this.requestId = requestId;
9112
9158
  this.name = "JSONRPCError";
9113
9159
  }
9114
9160
  };
@@ -9650,7 +9696,7 @@ var PreferencesContainer = class PreferencesContainer {
9650
9696
  skipDeviceMonitoring: false,
9651
9697
  savePreferences: false
9652
9698
  };
9653
- this.receiveVideo = true;
9699
+ this.receiveVideo = false;
9654
9700
  this.receiveAudio = true;
9655
9701
  this.preferredAudioInput = null;
9656
9702
  this.preferredAudioOutput = null;
@@ -9954,15 +10000,16 @@ const selectDevice = (devices = [], selected, preferred) => {
9954
10000
  return selected;
9955
10001
  };
9956
10002
  var NavigatorDeviceController = class extends Destroyable {
9957
- constructor() {
10003
+ constructor(webRTCApiProvider) {
9958
10004
  super();
10005
+ this.webRTCApiProvider = webRTCApiProvider;
9959
10006
  this.deviceChangeHandler = () => {
9960
10007
  logger$19.debug("[DeviceController] Device change detected");
9961
10008
  this.enumerateDevices();
9962
10009
  };
9963
10010
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
9964
10011
  this._selectedDevicesState$ = this.createBehaviorSubject(initialSelectedDevicesState);
9965
- this._errors$ = this.createSubject();
10012
+ this._errors$ = this.createReplaySubject(1);
9966
10013
  this.init();
9967
10014
  }
9968
10015
  get selectedAudioInputDeviceConstraints() {
@@ -9979,25 +10026,25 @@ var NavigatorDeviceController = class extends Destroyable {
9979
10026
  return {};
9980
10027
  }
9981
10028
  get errors$() {
9982
- return this._errors$.asObservable().pipe((0, import_cjs$20.takeUntil)(this.destroyed$));
10029
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$20.takeUntil)(this.destroyed$)));
9983
10030
  }
9984
10031
  get audioInputDevices$() {
9985
- return this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.audioinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$));
10032
+ 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$)));
9986
10033
  }
9987
10034
  get audioOutputDevices$() {
9988
- return this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.audiooutput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$));
10035
+ 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$)));
9989
10036
  }
9990
10037
  get videoInputDevices$() {
9991
- return this._devicesState$.pipe((0, import_cjs$20.map)((state) => state.videoinput), (0, import_cjs$20.distinctUntilChanged)(), (0, import_cjs$20.takeUntil)(this.destroyed$));
10038
+ 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$)));
9992
10039
  }
9993
10040
  get selectedAudioInputDevice$() {
9994
- 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)));
10041
+ 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))));
9995
10042
  }
9996
10043
  get selectedAudioOutputDevice$() {
9997
- 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)));
10044
+ 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))));
9998
10045
  }
9999
10046
  get selectedVideoInputDevice$() {
10000
- 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)));
10047
+ 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))));
10001
10048
  }
10002
10049
  get selectedAudioInputDevice() {
10003
10050
  return this._selectedDevicesState$.value.audioinput;
@@ -10037,24 +10084,22 @@ var NavigatorDeviceController = class extends Destroyable {
10037
10084
  });
10038
10085
  }
10039
10086
  init() {
10040
- if (navigator.mediaDevices) {
10041
- this.subscribeTo(this._devicesState$.pipe((0, import_cjs$20.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10042
- const currentSelected = this._selectedDevicesState$.value;
10043
- const newAudioInput = selectDevice(devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10044
- const newAudioOutput = selectDevice(devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
10045
- const newVideoInput = selectDevice(devicesState.videoinput, currentSelected.videoinput, PreferencesContainer.instance.preferredVideoInput);
10046
- if (newAudioInput !== currentSelected.audioinput || newAudioOutput !== currentSelected.audiooutput || newVideoInput !== currentSelected.videoinput) this._selectedDevicesState$.next({
10047
- audioinput: newAudioInput,
10048
- audiooutput: newAudioOutput,
10049
- videoinput: newVideoInput
10050
- });
10087
+ this.subscribeTo(this._devicesState$.pipe((0, import_cjs$20.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
10088
+ const currentSelected = this._selectedDevicesState$.value;
10089
+ const newAudioInput = selectDevice(devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
10090
+ const newAudioOutput = selectDevice(devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
10091
+ const newVideoInput = selectDevice(devicesState.videoinput, currentSelected.videoinput, PreferencesContainer.instance.preferredVideoInput);
10092
+ if (newAudioInput !== currentSelected.audioinput || newAudioOutput !== currentSelected.audiooutput || newVideoInput !== currentSelected.videoinput) this._selectedDevicesState$.next({
10093
+ audioinput: newAudioInput,
10094
+ audiooutput: newAudioOutput,
10095
+ videoinput: newVideoInput
10051
10096
  });
10052
- this.enumerateDevices();
10053
- }
10097
+ });
10098
+ this.enumerateDevices();
10054
10099
  }
10055
10100
  enableDeviceMonitoring() {
10056
10101
  this.disableDeviceMonitoring();
10057
- navigator.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
10102
+ this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
10058
10103
  if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, import_cjs$20.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
10059
10104
  logger$19.debug("[DeviceController] Polling devices due to interval");
10060
10105
  this.enumerateDevices();
@@ -10062,7 +10107,7 @@ var NavigatorDeviceController = class extends Destroyable {
10062
10107
  this.enumerateDevices();
10063
10108
  }
10064
10109
  disableDeviceMonitoring() {
10065
- navigator.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler);
10110
+ this.webRTCApiProvider.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler);
10066
10111
  if (this._devicesPoolingSubscription) {
10067
10112
  this._devicesPoolingSubscription.unsubscribe();
10068
10113
  this._devicesPoolingSubscription = void 0;
@@ -10070,7 +10115,7 @@ var NavigatorDeviceController = class extends Destroyable {
10070
10115
  }
10071
10116
  async enumerateDevices() {
10072
10117
  try {
10073
- const devicesByKind = (await navigator.mediaDevices.enumerateDevices()).reduce((acc, device) => {
10118
+ const devicesByKind = (await this.webRTCApiProvider.mediaDevices.enumerateDevices()).reduce((acc, device) => {
10074
10119
  acc[device.kind].push(device);
10075
10120
  return acc;
10076
10121
  }, {
@@ -10093,7 +10138,7 @@ var NavigatorDeviceController = class extends Destroyable {
10093
10138
  if (deviceInfo.kind === "audiooutput") return null;
10094
10139
  try {
10095
10140
  const constraints = this.deviceInfoToConstraints(deviceInfo);
10096
- const stream = await navigator.mediaDevices.getUserMedia({
10141
+ const stream = await this.webRTCApiProvider.mediaDevices.getUserMedia({
10097
10142
  audio: deviceInfo.kind === "audioinput" ? constraints : false,
10098
10143
  video: deviceInfo.kind === "videoinput" ? constraints : false
10099
10144
  });
@@ -10265,9 +10310,23 @@ var DependencyContainer = class {
10265
10310
  this._webSocketConstructor = WebSocketConstructor;
10266
10311
  }
10267
10312
  get deviceController() {
10268
- this._deviceController ??= new NavigatorDeviceController();
10313
+ this._deviceController ??= new NavigatorDeviceController(this.webRTCApiProvider);
10269
10314
  return this._deviceController;
10270
10315
  }
10316
+ get webRTCApiProvider() {
10317
+ if (!this._webRTCApiProvider) {
10318
+ 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.");
10319
+ this._webRTCApiProvider = {
10320
+ RTCPeerConnection,
10321
+ mediaDevices: navigator.mediaDevices
10322
+ };
10323
+ }
10324
+ return this._webRTCApiProvider;
10325
+ }
10326
+ set webRTCApiProvider(webRTCApiProvider) {
10327
+ this._webRTCApiProvider = webRTCApiProvider;
10328
+ this._deviceController = void 0;
10329
+ }
10271
10330
  get authorizationStateKey() {
10272
10331
  return `sw:${this.subscriberId}:as`;
10273
10332
  }
@@ -10448,6 +10507,15 @@ const RPCConnect = (params) => {
10448
10507
  });
10449
10508
  };
10450
10509
 
10510
+ //#endregion
10511
+ //#region src/core/RPCMessages/RPCReauthenticate.ts
10512
+ const RPCReauthenticate = (authentication) => {
10513
+ return buildRPCRequest({
10514
+ method: "signalwire.reauthenticate",
10515
+ params: { authentication }
10516
+ });
10517
+ };
10518
+
10451
10519
  //#endregion
10452
10520
  //#region src/core/RPCMessages/RPCPing.ts
10453
10521
  const RPCPingResponse = (id, timestamp$1) => {
@@ -10468,7 +10536,7 @@ const RPCExecute = ({ method, params }) => {
10468
10536
 
10469
10537
  //#endregion
10470
10538
  //#region src/core/RPCMessages/VertoMessages.ts
10471
- const tmpMap = {
10539
+ const SDK_TO_VERTO_FIELD_MAP = {
10472
10540
  id: "callID",
10473
10541
  destinationNumber: "destination_number",
10474
10542
  remoteCallerName: "remote_caller_id_name",
@@ -10477,19 +10545,31 @@ const tmpMap = {
10477
10545
  callerNumber: "caller_id_number",
10478
10546
  fromCallAddressId: "from_fabric_address_id"
10479
10547
  };
10548
+ const EXCLUDED_DIALOG_PARAMS = new Set([
10549
+ "remoteSdp",
10550
+ "localStream",
10551
+ "remoteStream"
10552
+ ]);
10480
10553
  /**
10481
- * Translate SDK fields into verto variables
10554
+ * Translate SDK fields into verto variables.
10555
+ * Returns a new object — the input is never mutated.
10482
10556
  */
10557
+ /** @internal Exported for testing only. */
10483
10558
  const filterVertoParams = (params) => {
10484
- if (Object.prototype.hasOwnProperty.call(params, "dialogParams")) {
10485
- const { remoteSdp, localStream, remoteStream, ...dialogParams } = params.dialogParams;
10486
- for (const key in tmpMap) if (key && Object.prototype.hasOwnProperty.call(dialogParams, key)) {
10487
- dialogParams[tmpMap[key]] = dialogParams[key];
10488
- delete dialogParams[key];
10489
- }
10490
- params.dialogParams = dialogParams;
10491
- }
10492
- return params;
10559
+ if (!Object.prototype.hasOwnProperty.call(params, "dialogParams")) return params;
10560
+ const sourceDialogParams = params.dialogParams;
10561
+ const filteredDialogParams = Object.entries(sourceDialogParams).reduce((acc, [key, value]) => {
10562
+ if (EXCLUDED_DIALOG_PARAMS.has(key)) return acc;
10563
+ const mappedKey = SDK_TO_VERTO_FIELD_MAP[key] ?? key;
10564
+ return {
10565
+ ...acc,
10566
+ [mappedKey]: value
10567
+ };
10568
+ }, {});
10569
+ return {
10570
+ ...params,
10571
+ dialogParams: filteredDialogParams
10572
+ };
10493
10573
  };
10494
10574
  const buildVertoRPCMessage = (method) => {
10495
10575
  return (params = {}) => {
@@ -10600,17 +10680,21 @@ var AttachManager = class {
10600
10680
  buildCallOptions(attachment) {
10601
10681
  const { audio: audioDirection, video: videoDirection } = attachment.mediaDirections;
10602
10682
  const { audioInputDevice, videoInputDevice } = attachment;
10683
+ const receiveAudio = audioDirection.includes("recv");
10684
+ const receiveVideo = videoDirection.includes("recv");
10685
+ const sendAudio = audioDirection.includes("send");
10686
+ const sendVideo = videoDirection.includes("send");
10603
10687
  return {
10604
- receiveAudio: audioDirection.includes("recv"),
10605
- receiveVideo: videoDirection.includes("recv"),
10606
- inputAudioDeviceConstraints: {
10607
- audio: audioDirection.includes("send"),
10688
+ receiveAudio,
10689
+ receiveVideo,
10690
+ inputAudioDeviceConstraints: sendAudio ? {
10691
+ audio: true,
10608
10692
  ...this.deviceController.deviceInfoToConstraints(audioInputDevice)
10609
- },
10610
- inputVideoDeviceConstraints: {
10611
- video: videoDirection.includes("send"),
10693
+ } : void 0,
10694
+ inputVideoDeviceConstraints: sendVideo ? {
10695
+ video: true,
10612
10696
  ...this.deviceController.deviceInfoToConstraints(videoInputDevice)
10613
- },
10697
+ } : void 0,
10614
10698
  reattach: true
10615
10699
  };
10616
10700
  }
@@ -10671,12 +10755,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
10671
10755
  exports.race = void 0;
10672
10756
  var argsOrArgArray_1 = require_argsOrArgArray();
10673
10757
  var raceWith_1$1 = require_raceWith();
10674
- function race$2() {
10758
+ function race$3() {
10675
10759
  var args = [];
10676
10760
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
10677
10761
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
10678
10762
  }
10679
- exports.race = race$2;
10763
+ exports.race = race$3;
10680
10764
  }));
10681
10765
 
10682
10766
  //#endregion
@@ -11658,7 +11742,7 @@ var SelfCapabilities = class extends Destroyable {
11658
11742
  }
11659
11743
  /** Observable for self member capabilities */
11660
11744
  get self$() {
11661
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.self), (0, import_cjs$17.distinctUntilChanged)());
11745
+ return this.cachedObservable("self$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.self), (0, import_cjs$17.distinctUntilChanged)()));
11662
11746
  }
11663
11747
  /** Current self member capabilities */
11664
11748
  get self() {
@@ -11666,7 +11750,7 @@ var SelfCapabilities = class extends Destroyable {
11666
11750
  }
11667
11751
  /** Observable for other member capabilities */
11668
11752
  get member$() {
11669
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.member), (0, import_cjs$17.distinctUntilChanged)());
11753
+ return this.cachedObservable("member$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.member), (0, import_cjs$17.distinctUntilChanged)()));
11670
11754
  }
11671
11755
  /** Current other member capabilities */
11672
11756
  get member() {
@@ -11674,7 +11758,7 @@ var SelfCapabilities = class extends Destroyable {
11674
11758
  }
11675
11759
  /** Observable for end call capability */
11676
11760
  get end$() {
11677
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.end), (0, import_cjs$17.distinctUntilChanged)());
11761
+ return this.cachedObservable("end$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.end), (0, import_cjs$17.distinctUntilChanged)()));
11678
11762
  }
11679
11763
  /** Current end call capability */
11680
11764
  get end() {
@@ -11682,7 +11766,7 @@ var SelfCapabilities = class extends Destroyable {
11682
11766
  }
11683
11767
  /** Observable for set layout capability */
11684
11768
  get setLayout$() {
11685
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.setLayout), (0, import_cjs$17.distinctUntilChanged)());
11769
+ return this.cachedObservable("setLayout$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.setLayout), (0, import_cjs$17.distinctUntilChanged)()));
11686
11770
  }
11687
11771
  /** Current set layout capability */
11688
11772
  get setLayout() {
@@ -11690,7 +11774,7 @@ var SelfCapabilities = class extends Destroyable {
11690
11774
  }
11691
11775
  /** Observable for send digit capability */
11692
11776
  get sendDigit$() {
11693
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.sendDigit), (0, import_cjs$17.distinctUntilChanged)());
11777
+ return this.cachedObservable("sendDigit$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.sendDigit), (0, import_cjs$17.distinctUntilChanged)()));
11694
11778
  }
11695
11779
  /** Current send digit capability */
11696
11780
  get sendDigit() {
@@ -11698,7 +11782,7 @@ var SelfCapabilities = class extends Destroyable {
11698
11782
  }
11699
11783
  /** Observable for vmuted hide capability */
11700
11784
  get vmutedHide$() {
11701
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.vmutedHide), (0, import_cjs$17.distinctUntilChanged)());
11785
+ return this.cachedObservable("vmutedHide$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.vmutedHide), (0, import_cjs$17.distinctUntilChanged)()));
11702
11786
  }
11703
11787
  /** Current vmuted hide capability */
11704
11788
  get vmutedHide() {
@@ -11706,7 +11790,7 @@ var SelfCapabilities = class extends Destroyable {
11706
11790
  }
11707
11791
  /** Observable for lock capability */
11708
11792
  get lock$() {
11709
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.lock), (0, import_cjs$17.distinctUntilChanged)());
11793
+ return this.cachedObservable("lock$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.lock), (0, import_cjs$17.distinctUntilChanged)()));
11710
11794
  }
11711
11795
  /** Current lock capability */
11712
11796
  get lock() {
@@ -11714,7 +11798,7 @@ var SelfCapabilities = class extends Destroyable {
11714
11798
  }
11715
11799
  /** Observable for device capability */
11716
11800
  get device$() {
11717
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.device), (0, import_cjs$17.distinctUntilChanged)());
11801
+ return this.cachedObservable("device$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.device), (0, import_cjs$17.distinctUntilChanged)()));
11718
11802
  }
11719
11803
  /** Current device capability */
11720
11804
  get device() {
@@ -11722,7 +11806,7 @@ var SelfCapabilities = class extends Destroyable {
11722
11806
  }
11723
11807
  /** Observable for screenshare capability */
11724
11808
  get screenshare$() {
11725
- return this._state$.pipe((0, import_cjs$17.map)((state) => state.screenshare), (0, import_cjs$17.distinctUntilChanged)());
11809
+ return this.cachedObservable("screenshare$", () => this._state$.pipe((0, import_cjs$17.map)((state) => state.screenshare), (0, import_cjs$17.distinctUntilChanged)()));
11726
11810
  }
11727
11811
  /** Current screenshare capability */
11728
11812
  get screenshare() {
@@ -11775,83 +11859,83 @@ var Participant = class extends Destroyable {
11775
11859
  }
11776
11860
  /** Observable of the participant's display name. */
11777
11861
  get name$() {
11778
- return this._state$.pipe((0, import_operators$1.map)((state) => state.name), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11862
+ return this.cachedObservable("name$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.name), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11779
11863
  }
11780
11864
  /** Observable of the participant type (e.g. `'member'`, `'screen'`). */
11781
11865
  get type$() {
11782
- return this._state$.pipe((0, import_operators$1.map)((state) => state.type), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11866
+ return this.cachedObservable("type$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.type), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11783
11867
  }
11784
11868
  /** Observable indicating whether the participant has raised their hand. */
11785
11869
  get handraised$() {
11786
- return this._state$.pipe((0, import_operators$1.map)((state) => state.handraised), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11870
+ return this.cachedObservable("handraised$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.handraised), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11787
11871
  }
11788
11872
  /** Observable indicating whether the participant is visible in the layout. */
11789
11873
  get visible$() {
11790
- return this._state$.pipe((0, import_operators$1.map)((state) => state.visible), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11874
+ return this.cachedObservable("visible$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.visible), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11791
11875
  }
11792
11876
  /** Observable indicating whether the participant's audio is muted. */
11793
11877
  get audioMuted$() {
11794
- return this._state$.pipe((0, import_operators$1.map)((state) => state.audio_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11878
+ return this.cachedObservable("audioMuted$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.audio_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11795
11879
  }
11796
11880
  /** Observable indicating whether the participant's video is muted. */
11797
11881
  get videoMuted$() {
11798
- return this._state$.pipe((0, import_operators$1.map)((state) => state.video_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11882
+ return this.cachedObservable("videoMuted$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.video_muted), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11799
11883
  }
11800
11884
  /** Observable indicating whether the participant is deafened. */
11801
11885
  get deaf$() {
11802
- return this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11886
+ return this.cachedObservable("deaf$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.deaf), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11803
11887
  }
11804
11888
  /** Observable of the participant's microphone input volume. */
11805
11889
  get inputVolume$() {
11806
- return this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11890
+ return this.cachedObservable("inputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11807
11891
  }
11808
11892
  /** Observable of the participant's speaker output volume. */
11809
11893
  get outputVolume$() {
11810
- return this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11894
+ return this.cachedObservable("outputVolume$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.output_volume), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11811
11895
  }
11812
11896
  /** Observable of the microphone input sensitivity level. */
11813
11897
  get inputSensitivity$() {
11814
- return this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11898
+ return this.cachedObservable("inputSensitivity$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.input_sensitivity), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11815
11899
  }
11816
11900
  /** Observable indicating whether echo cancellation is enabled. */
11817
11901
  get echoCancellation$() {
11818
- return this._state$.pipe((0, import_operators$1.map)((state) => state.echo_cancellation), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11902
+ return this.cachedObservable("echoCancellation$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.echo_cancellation), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11819
11903
  }
11820
11904
  /** Observable indicating whether auto-gain control is enabled. */
11821
11905
  get autoGain$() {
11822
- return this._state$.pipe((0, import_operators$1.map)((state) => state.auto_gain), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11906
+ return this.cachedObservable("autoGain$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.auto_gain), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11823
11907
  }
11824
11908
  /** Observable indicating whether noise suppression is enabled. */
11825
11909
  get noiseSuppression$() {
11826
- return this._state$.pipe((0, import_operators$1.map)((state) => state.noise_suppression), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11910
+ return this.cachedObservable("noiseSuppression$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.noise_suppression), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11827
11911
  }
11828
11912
  /** Observable indicating whether low-bitrate mode is active. */
11829
11913
  get lowbitrate$() {
11830
- return this._state$.pipe((0, import_operators$1.map)((state) => state.lowbitrate), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11914
+ return this.cachedObservable("lowbitrate$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.lowbitrate), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11831
11915
  }
11832
11916
  /** Observable indicating whether noise reduction is active. */
11833
11917
  get denoise$() {
11834
- return this._state$.pipe((0, import_operators$1.map)((state) => state.denoise), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11918
+ return this.cachedObservable("denoise$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.denoise), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11835
11919
  }
11836
11920
  /** Observable of custom metadata for this participant. */
11837
11921
  get meta$() {
11838
- return this._state$.pipe((0, import_operators$1.map)((state) => state.meta), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11922
+ return this.cachedObservable("meta$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.meta), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11839
11923
  }
11840
11924
  /** Observable of the participant's subscriber ID. */
11841
11925
  get subscriberId$() {
11842
- return this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11926
+ return this.cachedObservable("subscriberId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.subscriber_id), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11843
11927
  }
11844
11928
  /** Observable of the participant's address ID. */
11845
11929
  get addressId$() {
11846
- return this._state$.pipe((0, import_operators$1.map)((state) => state.address_id), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11930
+ return this.cachedObservable("addressId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.address_id), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11847
11931
  }
11848
11932
  /** Observable of the server node ID for this participant. */
11849
11933
  get nodeId$() {
11850
- return this._state$.pipe((0, import_operators$1.map)((state) => state.node_id), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11934
+ return this.cachedObservable("nodeId$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.node_id), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11851
11935
  }
11852
11936
  /** Observable indicating whether the participant is currently speaking. */
11853
11937
  get isTalking$() {
11854
- return this._state$.pipe((0, import_operators$1.map)((state) => state.talking), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11938
+ return this.cachedObservable("isTalking$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.talking), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11855
11939
  }
11856
11940
  /** Whether the participant is currently speaking. */
11857
11941
  get isTalking() {
@@ -11859,7 +11943,7 @@ var Participant = class extends Destroyable {
11859
11943
  }
11860
11944
  /** Observable of the participant's layout position. */
11861
11945
  get position$() {
11862
- return this._state$.pipe((0, import_operators$1.map)((state) => state.position), (0, import_operators$1.distinctUntilChanged)(), filterNull());
11946
+ return this.cachedObservable("position$", () => this._state$.pipe((0, import_operators$1.map)((state) => state.position), (0, import_operators$1.distinctUntilChanged)(), filterNull()));
11863
11947
  }
11864
11948
  /** Current layout position. */
11865
11949
  get position() {
@@ -12010,7 +12094,13 @@ var Participant = class extends Destroyable {
12010
12094
  }
12011
12095
  /** Removes this participant from the call. */
12012
12096
  async remove() {
12013
- await this.executeMethod(this.id, "call.member.remove", {});
12097
+ const state = this._state$.value;
12098
+ const target = {
12099
+ member_id: this.id,
12100
+ call_id: state.call_id ?? "",
12101
+ node_id: state.node_id ?? ""
12102
+ };
12103
+ await this.executeMethod(target, "call.member.remove", {});
12014
12104
  }
12015
12105
  /** Ends the call for this participant. */
12016
12106
  async end() {
@@ -12178,7 +12268,10 @@ function isJSONRPCRequest(value) {
12178
12268
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && hasProperty(value, "method") && typeof value.method === "string";
12179
12269
  }
12180
12270
  function isJSONRPCResponse(value) {
12181
- return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && hasProperty(value, "result");
12271
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && (hasProperty(value, "result") || hasProperty(value, "error"));
12272
+ }
12273
+ function isJSONRPCErrorResponse(value) {
12274
+ 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"));
12182
12275
  }
12183
12276
 
12184
12277
  //#endregion
@@ -12369,23 +12462,19 @@ var CallEventsManager = class extends Destroyable {
12369
12462
  this.options = options;
12370
12463
  this.callIds = /* @__PURE__ */ new Set();
12371
12464
  this.roomSessionIds = /* @__PURE__ */ new Set();
12372
- this._status$ = this.createBehaviorSubject("trying");
12373
12465
  this._participants$ = this.createBehaviorSubject({});
12374
12466
  this._self$ = this.createBehaviorSubject(null);
12375
12467
  this._sessionState$ = this.createBehaviorSubject(initialSessionState);
12376
12468
  this.initSubscriptions();
12377
12469
  }
12378
12470
  get participants$() {
12379
- return this._participants$.asObservable().pipe((0, import_cjs$14.map)((participantsRecord) => Object.values(participantsRecord)));
12471
+ return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$14.map)((participantsRecord) => Object.values(participantsRecord))));
12380
12472
  }
12381
- get self$() {
12382
- return this._self$.asObservable().pipe(filterNull());
12383
- }
12384
- get status$() {
12385
- return this._status$.asObservable();
12473
+ get participants() {
12474
+ return Object.values(this._participants$.value);
12386
12475
  }
12387
- get status() {
12388
- return this._status$.value;
12476
+ get self$() {
12477
+ return this.cachedObservable("self$", () => this._self$.asObservable().pipe(filterNull()));
12389
12478
  }
12390
12479
  isRoomSessionIdValid(roomSessionId) {
12391
12480
  return this.roomSessionIds.has(roomSessionId);
@@ -12397,40 +12486,40 @@ var CallEventsManager = class extends Destroyable {
12397
12486
  return this.callIds.has(callId);
12398
12487
  }
12399
12488
  get recording$() {
12400
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recording), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12489
+ return this.cachedObservable("recording$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recording), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12401
12490
  }
12402
12491
  get recordings$() {
12403
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recordings), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12492
+ return this.cachedObservable("recordings$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.recordings), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12404
12493
  }
12405
12494
  get streaming$() {
12406
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streaming), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12495
+ return this.cachedObservable("streaming$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streaming), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12407
12496
  }
12408
12497
  get streams$() {
12409
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streams), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12498
+ return this.cachedObservable("streams$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.streams), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12410
12499
  }
12411
12500
  get playbacks$() {
12412
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.playbacks), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12501
+ return this.cachedObservable("playbacks$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.playbacks), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12413
12502
  }
12414
12503
  get raiseHandPriority$() {
12415
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.prioritize_handraise), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12504
+ return this.cachedObservable("raiseHandPriority$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.prioritize_handraise), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12416
12505
  }
12417
12506
  get locked$() {
12418
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.locked), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12507
+ return this.cachedObservable("locked$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.locked), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12419
12508
  }
12420
12509
  get meta$() {
12421
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.meta), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12510
+ return this.cachedObservable("meta$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.meta), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12422
12511
  }
12423
12512
  get capabilities$() {
12424
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.capabilities), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12513
+ return this.cachedObservable("capabilities$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.capabilities), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12425
12514
  }
12426
12515
  get layout$() {
12427
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_name), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12516
+ return this.cachedObservable("layout$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_name), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12428
12517
  }
12429
12518
  get layouts$() {
12430
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layouts), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12519
+ return this.cachedObservable("layouts$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layouts), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12431
12520
  }
12432
12521
  get layoutLayers$() {
12433
- return this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_layers), (0, import_cjs$14.distinctUntilChanged)(), filterNull());
12522
+ return this.cachedObservable("layoutLayers$", () => this._sessionState$.pipe((0, import_cjs$14.map)((state) => state.layout_layers), (0, import_cjs$14.distinctUntilChanged)(), filterNull()));
12434
12523
  }
12435
12524
  get self() {
12436
12525
  return this._self$.value;
@@ -12471,7 +12560,6 @@ var CallEventsManager = class extends Destroyable {
12471
12560
  callId: callJoinedEvent.call_id,
12472
12561
  roomSessionId: callJoinedEvent.room_session_id
12473
12562
  });
12474
- this._status$.next("connected");
12475
12563
  const sessionState = callJoinedEvent.room_session;
12476
12564
  const { capabilities } = callJoinedEvent;
12477
12565
  this.selfId = this.selfId ?? callJoinedEvent.member_id;
@@ -12492,7 +12580,7 @@ var CallEventsManager = class extends Destroyable {
12492
12580
  });
12493
12581
  this.updateParticipants(sessionState.members);
12494
12582
  this._self$.value?.capabilities.updateFromRaw(capabilities);
12495
- this.updateLayouts();
12583
+ if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
12496
12584
  });
12497
12585
  this.subscribeTo(this.memberUpdates$, (member) => {
12498
12586
  logger$15.debug("[CallEventsManager] Handling member update event for member ID:", member);
@@ -12566,19 +12654,19 @@ var CallEventsManager = class extends Destroyable {
12566
12654
  this._participants$.next(this._participants$.value);
12567
12655
  }
12568
12656
  get callJoinedEvent$() {
12569
- return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.tap)((event) => {
12657
+ return this.cachedObservable("callJoinedEvent$", () => this.webRtcCallSession.callEvent$.pipe((0, import_cjs$14.filter)(isCallJoinedPayload), (0, import_cjs$14.tap)((event) => {
12570
12658
  logger$15.debug("[CallEventsManager] Call joined event:", event);
12571
- }));
12659
+ })));
12572
12660
  }
12573
12661
  get layoutChangedEvent$() {
12574
- return this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$14.tap)((event) => {
12662
+ return this.cachedObservable("layoutChangedEvent$", () => this.webRtcCallSession.callEvent$.pipe(filterAs(isLayoutChangedPayload, "layout"), (0, import_cjs$14.tap)((event) => {
12575
12663
  logger$15.debug("[CallEventsManager] Layout changed event:", event);
12576
- }));
12664
+ })));
12577
12665
  }
12578
12666
  get memberUpdates$() {
12579
- 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) => {
12667
+ 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) => {
12580
12668
  logger$15.debug("[CallEventsManager] Member update event:", event);
12581
- }));
12669
+ })));
12582
12670
  }
12583
12671
  destroy() {
12584
12672
  Object.values(this._participants$.value).forEach((participant) => {
@@ -12598,12 +12686,53 @@ var CallEventsManager = class extends Destroyable {
12598
12686
 
12599
12687
  //#endregion
12600
12688
  //#region src/helpers/SDPHelper.ts
12689
+ /** Valid SDP direction attribute values. */
12690
+ const SDP_DIRECTIONS = new Set([
12691
+ "sendrecv",
12692
+ "sendonly",
12693
+ "recvonly",
12694
+ "inactive"
12695
+ ]);
12601
12696
  /**
12602
- * SDPHelper - Utility functions for SDP (Session Description Protocol) parsing and validation.
12697
+ * Extracts the media directions (audio/video) from an SDP string.
12603
12698
  *
12604
- * This module provides helper functions to analyze and validate SDP content,
12605
- * particularly for ICE candidate validation in WebRTC connections.
12699
+ * Parses each media section (`m=audio` / `m=video`) and reads the `a=` direction
12700
+ * attribute (`sendrecv`, `sendonly`, `recvonly`, `inactive`).
12701
+ * If no explicit direction attribute is found for a media section, defaults to `sendrecv`
12702
+ * per RFC 4566.
12703
+ *
12704
+ * @param sdp - The SDP string to parse
12705
+ * @returns The extracted audio and video directions
12706
+ *
12707
+ * @example
12708
+ * ```typescript
12709
+ * 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`;
12710
+ * extractMediaDirectionsFromSDP(sdp);
12711
+ * // { audio: 'sendrecv', video: 'recvonly' }
12712
+ * ```
12606
12713
  */
12714
+ function extractMediaDirectionsFromSDP(sdp) {
12715
+ const result = {
12716
+ audio: "inactive",
12717
+ video: "inactive"
12718
+ };
12719
+ if (!sdp) return result;
12720
+ const lines = sdp.split(/\r?\n/);
12721
+ let currentMediaKind = null;
12722
+ let currentDirection = null;
12723
+ for (const line of lines) if (line.startsWith("m=")) {
12724
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12725
+ if (line.startsWith("m=audio")) currentMediaKind = "audio";
12726
+ else if (line.startsWith("m=video")) currentMediaKind = "video";
12727
+ else currentMediaKind = null;
12728
+ currentDirection = null;
12729
+ } else if (currentMediaKind && line.startsWith("a=")) {
12730
+ const attr = line.substring(2).trim();
12731
+ if (SDP_DIRECTIONS.has(attr)) currentDirection = attr;
12732
+ }
12733
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12734
+ return result;
12735
+ }
12607
12736
  /**
12608
12737
  * Validates that an SDP string has at least one non-host ICE candidate
12609
12738
  * for each media section (m= line).
@@ -12906,6 +13035,15 @@ var LocalStreamController = class extends Destroyable {
12906
13035
  track.addEventListener("ended", this.mediaTrackEndedHandler);
12907
13036
  }
12908
13037
  /**
13038
+ * Update the controller options (e.g., when media overrides are applied).
13039
+ */
13040
+ updateOptions(options) {
13041
+ this.options = {
13042
+ ...this.options,
13043
+ ...options
13044
+ };
13045
+ }
13046
+ /**
12909
13047
  * Stop all local tracks and clean up.
12910
13048
  */
12911
13049
  stopAllTracks() {
@@ -13220,7 +13358,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13220
13358
  logger$11.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
13221
13359
  return;
13222
13360
  }
13223
- const streamTrack = (await navigator.mediaDevices.getUserMedia({ [kind]: {
13361
+ const streamTrack = (await this.getUserMedia({ [kind]: {
13224
13362
  ...track.getConstraints(),
13225
13363
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
13226
13364
  } })).getTracks().find((t) => t.kind === kind);
@@ -13244,11 +13382,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13244
13382
  this._connectionState$ = this.createReplaySubject(1);
13245
13383
  this._signalingState$ = this.createReplaySubject(1);
13246
13384
  this._iceGatheringState$ = this.createReplaySubject(1);
13247
- this._errors$ = this.createSubject();
13385
+ this._errors$ = this.createReplaySubject(1);
13248
13386
  this._iceCandidates$ = this.createReplaySubject(1);
13249
13387
  this._initialized$ = this.createReplaySubject(1);
13250
13388
  this._remoteDescription$ = this.createReplaySubject(1);
13251
13389
  this._remoteStream$ = this.createBehaviorSubject(null);
13390
+ this._remoteOfferMediaDirections = null;
13252
13391
  this.deviceController = deviceController ?? {};
13253
13392
  this.id = options.callId ?? v4_default();
13254
13393
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -13256,10 +13395,19 @@ var RTCPeerConnectionController = class extends Destroyable {
13256
13395
  type: "offer",
13257
13396
  sdp: remoteSessionDescription
13258
13397
  } : void 0;
13398
+ this._remoteOfferMediaDirections = remoteSessionDescription ? extractMediaDirectionsFromSDP(remoteSessionDescription) : null;
13399
+ const offerDefaults = this._remoteOfferMediaDirections ? {
13400
+ audio: this._remoteOfferMediaDirections.audio.includes("recv"),
13401
+ video: this._remoteOfferMediaDirections.video.includes("recv"),
13402
+ receiveAudio: this._remoteOfferMediaDirections.audio.includes("send"),
13403
+ receiveVideo: this._remoteOfferMediaDirections.video.includes("send")
13404
+ } : {};
13259
13405
  this.options = {
13260
- receiveAudio: options.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13261
- receiveVideo: options.receiveVideo ?? PreferencesContainer.instance.receiveVideo,
13262
- ...options
13406
+ ...options,
13407
+ audio: options.audio ?? offerDefaults.audio,
13408
+ video: options.video ?? offerDefaults.video,
13409
+ receiveAudio: options.receiveAudio ?? offerDefaults.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13410
+ receiveVideo: options.receiveVideo ?? offerDefaults.receiveVideo ?? PreferencesContainer.instance.receiveVideo
13263
13411
  };
13264
13412
  this.localStreamController = new LocalStreamController({
13265
13413
  propose: this.propose,
@@ -13309,43 +13457,43 @@ var RTCPeerConnectionController = class extends Destroyable {
13309
13457
  };
13310
13458
  }
13311
13459
  get iceGatheringState$() {
13312
- return this._iceGatheringState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13460
+ return this.cachedObservable("iceGatheringState$", () => this._iceGatheringState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13313
13461
  }
13314
13462
  get mediaTrackEnded$() {
13315
- return this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13463
+ return this.cachedObservable("mediaTrackEnded$", () => this.localStreamController.mediaTrackEnded$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13316
13464
  }
13317
13465
  get errors$() {
13318
- return this._errors$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13466
+ return this.cachedObservable("errors$", () => this._errors$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13319
13467
  }
13320
13468
  get iceCandidates$() {
13321
- return this._iceCandidates$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13469
+ return this.cachedObservable("iceCandidates$", () => this._iceCandidates$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13322
13470
  }
13323
13471
  get initialized$() {
13324
- return this._initialized$.asObservable().pipe((0, import_cjs$11.filter)((initialized) => initialized), (0, import_cjs$11.takeUntil)(this.destroyed$));
13472
+ return this.cachedObservable("initialized$", () => this._initialized$.asObservable().pipe((0, import_cjs$11.filter)((initialized) => initialized), (0, import_cjs$11.takeUntil)(this.destroyed$)));
13325
13473
  }
13326
13474
  get remoteDescription$() {
13327
- return this._remoteDescription$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13475
+ return this.cachedObservable("remoteDescription$", () => this._remoteDescription$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13328
13476
  }
13329
13477
  get localStream$() {
13330
- return this.localStreamController.localStream$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13478
+ return this.cachedObservable("localStream$", () => this.localStreamController.localStream$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13331
13479
  }
13332
13480
  get remoteStream$() {
13333
- return this._remoteStream$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13481
+ return this.cachedObservable("remoteStream$", () => this._remoteStream$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13334
13482
  }
13335
13483
  get localAudioTracks$() {
13336
- return this.localStreamController.localAudioTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13484
+ return this.cachedObservable("localAudioTracks$", () => this.localStreamController.localAudioTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13337
13485
  }
13338
13486
  get localVideoTracks$() {
13339
- return this.localStreamController.localVideoTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13487
+ return this.cachedObservable("localVideoTracks$", () => this.localStreamController.localVideoTracks$.pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13340
13488
  }
13341
13489
  get iceConnectionState$() {
13342
- return this._iceConnectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13490
+ return this.cachedObservable("iceConnectionState$", () => this._iceConnectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13343
13491
  }
13344
13492
  get connectionState$() {
13345
- return this._connectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13493
+ return this.cachedObservable("connectionState$", () => this._connectionState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13346
13494
  }
13347
13495
  get signalingState$() {
13348
- return this._signalingState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$));
13496
+ return this.cachedObservable("signalingState$", () => this._signalingState$.asObservable().pipe((0, import_cjs$11.takeUntil)(this.destroyed$)));
13349
13497
  }
13350
13498
  get type() {
13351
13499
  return this._type;
@@ -13404,14 +13552,14 @@ var RTCPeerConnectionController = class extends Destroyable {
13404
13552
  };
13405
13553
  }
13406
13554
  get inputVideoDeviceConstraints() {
13407
- if (this.options.video === false && !this.options.inputVideoDeviceConstraints) return false;
13555
+ if (!this.options.video && !this.options.inputVideoDeviceConstraints) return false;
13408
13556
  return {
13409
13557
  ...this.options.inputVideoDeviceConstraints,
13410
13558
  ...this.deviceController.selectedVideoInputDeviceConstraints
13411
13559
  };
13412
13560
  }
13413
13561
  get WebRTCPeerConnectionConstructor() {
13414
- return this.options.WebRTCPeerConnectionConstructor ?? RTCPeerConnection;
13562
+ return this.options.webRTCApiProvider?.RTCPeerConnection ?? RTCPeerConnection;
13415
13563
  }
13416
13564
  get offerOptions() {
13417
13565
  const options = { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
@@ -13426,12 +13574,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13426
13574
  default: return {
13427
13575
  ...options,
13428
13576
  offerToReceiveAudio: true,
13429
- offerToReceiveVideo: Boolean(this.inputVideoDeviceConstraints)
13577
+ offerToReceiveVideo: this.options.receiveVideo ?? Boolean(this.inputVideoDeviceConstraints)
13430
13578
  };
13431
13579
  }
13432
13580
  }
13433
13581
  get answerOptions() {
13434
- return {};
13582
+ return { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
13435
13583
  }
13436
13584
  /**
13437
13585
  * Initialize the RTCPeerConnection and setup event listeners.
@@ -13466,11 +13614,15 @@ var RTCPeerConnectionController = class extends Destroyable {
13466
13614
  });
13467
13615
  await this.updateSelectedInputDevice(kind, deviceInfo);
13468
13616
  });
13469
- await this.setupTrackHandling();
13470
- this._initialized$.next(true);
13471
13617
  if (this.type === "answer" && this.sdpInit) {
13618
+ await this.setupRemoteTracks();
13619
+ this._initialized$.next(true);
13472
13620
  this.setupEventListeners();
13473
- await this.handleOfferReceived();
13621
+ this._isNegotiating$.next(true);
13622
+ await this._setRemoteDescription(this.sdpInit);
13623
+ } else {
13624
+ await this.setupTrackHandling();
13625
+ this._initialized$.next(true);
13474
13626
  }
13475
13627
  } catch (error) {
13476
13628
  logger$11.error("[RTCPeerConnectionController] Initialization error:", error);
@@ -13568,6 +13720,35 @@ var RTCPeerConnectionController = class extends Destroyable {
13568
13720
  default:
13569
13721
  }
13570
13722
  }
13723
+ /**
13724
+ * Accept an inbound call by creating the SDP answer.
13725
+ * Optionally override media options before the answer is generated.
13726
+ * Must be called after initialization for inbound (answer-type) connections.
13727
+ */
13728
+ async acceptInbound(mediaOverrides) {
13729
+ if (mediaOverrides) {
13730
+ const { audio, video, receiveAudio, receiveVideo } = mediaOverrides;
13731
+ this.options = {
13732
+ ...this.options,
13733
+ ...audio !== void 0 ? { audio } : {},
13734
+ ...video !== void 0 ? { video } : {},
13735
+ ...receiveAudio !== void 0 ? { receiveAudio } : {},
13736
+ ...receiveVideo !== void 0 ? { receiveVideo } : {}
13737
+ };
13738
+ this.transceiverController?.updateOptions({
13739
+ receiveAudio: this.receiveAudio,
13740
+ receiveVideo: this.receiveVideo
13741
+ });
13742
+ this.localStreamController.updateOptions({
13743
+ inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,
13744
+ inputVideoDeviceConstraints: this.inputVideoDeviceConstraints
13745
+ });
13746
+ }
13747
+ await this.setupLocalTracks();
13748
+ const { answerOptions } = this;
13749
+ logger$11.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
13750
+ await this.createAnswer(answerOptions);
13751
+ }
13571
13752
  async handleOfferReceived() {
13572
13753
  if (!this.sdpInit) throw new DependencyError("SDP initialization parameters are not set");
13573
13754
  this._isNegotiating$.next(true);
@@ -13642,52 +13823,51 @@ var RTCPeerConnectionController = class extends Destroyable {
13642
13823
  }
13643
13824
  async setupLocalTracks() {
13644
13825
  logger$11.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
13645
- let { localStream } = this;
13646
- if (!localStream) try {
13647
- localStream = await this.localStreamController.buildLocalStream();
13648
- } catch (error) {
13649
- logger$11.error("[RTCPeerConnectionController] Error building local stream:", error);
13650
- this._errors$.next(error);
13651
- }
13652
- if (localStream) {
13653
- if (this.transceiverController?.useAddStream ?? false) {
13654
- logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13655
- this.peerConnection?.addStream(localStream);
13656
- if (!this.isNegotiating) {
13657
- logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13658
- this.negotiationNeeded$.next();
13659
- }
13660
- return;
13826
+ const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
13827
+ if (this.transceiverController?.useAddStream ?? false) {
13828
+ logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13829
+ this.peerConnection?.addStream(localStream);
13830
+ if (!this.isNegotiating) {
13831
+ logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13832
+ this.negotiationNeeded$.next();
13661
13833
  }
13662
- for (const kind of ["audio", "video"]) {
13663
- const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13664
- index,
13665
- track
13666
- }));
13667
- for (const { index, track } of tracks) {
13668
- this.localStreamController.addTrackEndedListener(track);
13669
- if (this.transceiverController?.useAddTransceivers ?? false) {
13670
- const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13671
- await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13672
- } else {
13673
- logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13674
- this.peerConnection?.addTrack(track, localStream);
13675
- }
13834
+ return;
13835
+ }
13836
+ for (const kind of ["audio", "video"]) {
13837
+ const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13838
+ index,
13839
+ track
13840
+ }));
13841
+ for (const { index, track } of tracks) {
13842
+ this.localStreamController.addTrackEndedListener(track);
13843
+ if (this.transceiverController?.useAddTransceivers ?? false) {
13844
+ const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13845
+ await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13846
+ } else {
13847
+ logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13848
+ this.peerConnection?.addTrack(track, localStream);
13676
13849
  }
13677
13850
  }
13678
13851
  }
13679
13852
  }
13680
13853
  async getUserMedia(constraints) {
13681
- return this.options.getUserMedia?.(constraints) ?? navigator.mediaDevices.getUserMedia(constraints);
13854
+ return (this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices).getUserMedia(constraints);
13682
13855
  }
13683
13856
  async getDisplayMedia(options) {
13684
- return this.options.getDisplayMedia?.(options) ?? navigator.mediaDevices.getDisplayMedia(options);
13857
+ const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;
13858
+ if (!mediaDevices.getDisplayMedia) throw new DependencyError("getDisplayMedia is not supported by the current WebRTC provider");
13859
+ return mediaDevices.getDisplayMedia(options);
13685
13860
  }
13686
13861
  async setupRemoteTracks() {
13687
13862
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
13688
13863
  this.peerConnection.ontrack = (event) => {
13689
13864
  logger$11.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
13690
- this._remoteStream$.next(event.streams[0]);
13865
+ if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
13866
+ else {
13867
+ const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
13868
+ const newStream = new MediaStream([...existingTracks, event.track]);
13869
+ this._remoteStream$.next(newStream);
13870
+ }
13691
13871
  };
13692
13872
  await this.transceiverController?.setupRemoteTransceivers(this.type);
13693
13873
  }
@@ -13786,7 +13966,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13786
13966
  });
13787
13967
  }
13788
13968
  get mediaDirections() {
13789
- return this.transceiverController?.getMediaDirections() ?? {
13969
+ return this.transceiverController?.getMediaDirections() ?? this._remoteOfferMediaDirections ?? {
13790
13970
  audio: "inactive",
13791
13971
  video: "inactive"
13792
13972
  };
@@ -13822,7 +14002,10 @@ function isVertoAttachMessage(value) {
13822
14002
  return value.method === "verto.attach";
13823
14003
  }
13824
14004
  function isVertoAnswerInnerParams(value) {
13825
- 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");
14005
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.answer" && isObject(value.params) && hasProperty(value.params, "callID");
14006
+ }
14007
+ function isVertoMediaInnerParams(value) {
14008
+ 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");
13826
14009
  }
13827
14010
  function isVertoMediaParamsInnerParams(value) {
13828
14011
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.mediaParams" && isObject(value.params) && hasProperty(value.params, "mediaParams");
@@ -13846,11 +14029,12 @@ var VertoManager = class extends Destroyable {
13846
14029
  }
13847
14030
  };
13848
14031
  var WebRTCVertoManager = class extends VertoManager {
13849
- constructor(webRtcCallSession, attachManager, deviceController, options = {}) {
14032
+ constructor(webRtcCallSession, attachManager, deviceController, webRTCApiProvider, options = {}) {
13850
14033
  super(webRtcCallSession);
13851
14034
  this.webRtcCallSession = webRtcCallSession;
13852
14035
  this.attachManager = attachManager;
13853
14036
  this.deviceController = deviceController;
14037
+ this.webRTCApiProvider = webRTCApiProvider;
13854
14038
  this._rtcPeerConnections$ = this.createBehaviorSubject([]);
13855
14039
  this._selfId$ = this.createBehaviorSubject(null);
13856
14040
  this._signalingStatus$ = this.createBehaviorSubject(null);
@@ -13924,17 +14108,32 @@ var WebRTCVertoManager = class extends VertoManager {
13924
14108
  return rtcPeerConnection;
13925
14109
  }
13926
14110
  get signalingStatus$() {
13927
- return (0, import_cjs$10.merge)(this._signalingStatus$.pipe(filterNull()), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$10.filter)((connectionState) => [
14111
+ return this.cachedObservable("signalingStatus$", () => (0, import_cjs$10.merge)(this._signalingStatus$.pipe(filterNull()), this.mainPeerConnection.connectionState$.pipe((0, import_cjs$10.filter)((connectionState) => [
13928
14112
  "connected",
13929
14113
  "disconnected",
13930
14114
  "failed"
13931
- ].includes(connectionState))));
14115
+ ].includes(connectionState)))));
13932
14116
  }
13933
14117
  initSubscriptions() {
14118
+ this.subscribeTo(this.callJoinedEvent$, (event) => {
14119
+ const memberNodeId = event.room_session.members.find((m) => m.call_id === event.call_id)?.node_id;
14120
+ if (memberNodeId) this.setNodeIdIfNull(memberNodeId);
14121
+ if (event.member_id) this.setSelfIdIfNull(event.member_id);
14122
+ });
14123
+ this.subscribeTo(this.vertoMedia$, (event) => {
14124
+ logger$10.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
14125
+ this._signalingStatus$.next("ringing");
14126
+ const { sdp, callID } = event;
14127
+ this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
14128
+ status: "received",
14129
+ sdp
14130
+ });
14131
+ });
13934
14132
  this.subscribeTo(this.vertoAnswer$, (event) => {
13935
14133
  logger$10.debug("[WebRTCManager] Received Verto answer event:", event);
13936
- const { sdp } = event;
13937
- this._rtcPeerConnectionsMap.get(event.callID)?.updateAnswerStatus({
14134
+ this._signalingStatus$.next("connecting");
14135
+ const { sdp, callID } = event;
14136
+ this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
13938
14137
  status: "received",
13939
14138
  sdp
13940
14139
  });
@@ -13952,6 +14151,28 @@ var WebRTCVertoManager = class extends VertoManager {
13952
14151
  this.sendVertoPong(vertoPing);
13953
14152
  });
13954
14153
  }
14154
+ /**
14155
+ * Set node_id/selfId only when the current value is null.
14156
+ *
14157
+ * During reattach, `call.joined` and `verto.answer` events can deliver
14158
+ * these identifiers before the `verto.invite` RPC response (`CALL CREATED`)
14159
+ * arrives. These methods let early events populate them eagerly so that
14160
+ * downstream RPC calls (e.g. `call.layout.list`) don't fail with empty
14161
+ * identifiers. `processInviteResponse()` remains the authoritative source
14162
+ * and always overwrites unconditionally.
14163
+ */
14164
+ setNodeIdIfNull(nodeId) {
14165
+ if (!this._nodeId$.value && nodeId) {
14166
+ logger$10.debug(`[WebRTCManager] Early node_id set: ${nodeId}`);
14167
+ this._nodeId$.next(nodeId);
14168
+ }
14169
+ }
14170
+ setSelfIdIfNull(selfId) {
14171
+ if (!this._selfId$.value && selfId) {
14172
+ logger$10.debug(`[WebRTCManager] Early selfId set: ${selfId}`);
14173
+ this._selfId$.next(selfId);
14174
+ }
14175
+ }
13955
14176
  async sendVertoPong(vertoPing) {
13956
14177
  try {
13957
14178
  const vertoPongMessage = VertoPong({ ...vertoPing });
@@ -13975,20 +14196,26 @@ var WebRTCVertoManager = class extends VertoManager {
13975
14196
  get selfId() {
13976
14197
  return this._selfId$.value;
13977
14198
  }
14199
+ get callJoinedEvent$() {
14200
+ return this.webRtcCallSession.callEvent$.pipe((0, import_cjs$10.filter)(isCallJoinedPayload), (0, import_cjs$10.takeUntil)(this.destroyed$));
14201
+ }
14202
+ get vertoMedia$() {
14203
+ return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14204
+ }
13978
14205
  get vertoAnswer$() {
13979
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14206
+ return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAnswerInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13980
14207
  }
13981
14208
  get vertoMediaParams$() {
13982
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14209
+ return this.cachedObservable("vertoMediaParams$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoMediaParamsInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13983
14210
  }
13984
14211
  get vertoBye$() {
13985
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14212
+ return this.cachedObservable("vertoBye$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoByeMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13986
14213
  }
13987
14214
  get vertoAttach$() {
13988
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14215
+ return this.cachedObservable("vertoAttach$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoAttachMessage, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13989
14216
  }
13990
14217
  get vertoPing$() {
13991
- return this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$));
14218
+ return this.cachedObservable("vertoPing$", () => this.webRtcCallSession.webrtcMessages$.pipe(filterAs(isVertoPingInnerParams, "params"), (0, import_cjs$10.takeUntil)(this.destroyed$)));
13992
14219
  }
13993
14220
  async executeVerto(message, optionals = {}) {
13994
14221
  const webrtcVertoMessage = WebrtcVerto({
@@ -14001,19 +14228,19 @@ var WebRTCVertoManager = class extends VertoManager {
14001
14228
  if (response.error) {
14002
14229
  const error = new JSONRPCError(response.error.code, response.error.message, response.error.data);
14003
14230
  this.onError?.(error);
14004
- throw error;
14231
+ return response;
14005
14232
  }
14006
14233
  const innerResult = getValueFrom(response, "result.result");
14007
14234
  if (innerResult?.error) {
14008
14235
  const error = new JSONRPCError(innerResult.error.code, innerResult.error.message, innerResult.error.data);
14009
14236
  this.onError?.(error);
14010
- throw error;
14237
+ return response;
14011
14238
  }
14012
14239
  return response;
14013
14240
  }
14014
14241
  async sendLocalDescription(message, rtcPeerConnController) {
14015
14242
  const vertoMethod = message.method;
14016
- const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController);
14243
+ const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod);
14017
14244
  try {
14018
14245
  const response = await this.executeVerto(message, optionalsParams);
14019
14246
  switch (vertoMethod) {
@@ -14027,7 +14254,7 @@ var WebRTCVertoManager = class extends VertoManager {
14027
14254
  }
14028
14255
  } catch (error) {
14029
14256
  logger$10.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
14030
- throw error;
14257
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14031
14258
  }
14032
14259
  }
14033
14260
  async processModifyResponse(response, rtcPeerConnController) {
@@ -14041,12 +14268,14 @@ var WebRTCVertoManager = class extends VertoManager {
14041
14268
  });
14042
14269
  } catch (error) {
14043
14270
  logger$10.warn("[WebRTCManager] Error processing modify response:", error);
14044
- this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14271
+ const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
14272
+ this.onError?.(modifyError);
14045
14273
  }
14046
14274
  }
14047
14275
  }
14048
14276
  processInviteResponse(response, rtcPeerConnController) {
14049
14277
  if (!response.error && getValueFrom(response, "result.result.result.message") === "CALL CREATED") {
14278
+ this._signalingStatus$.next("trying");
14050
14279
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
14051
14280
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
14052
14281
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
@@ -14058,7 +14287,6 @@ var WebRTCVertoManager = class extends VertoManager {
14058
14287
  this._selfId$.next(memberId);
14059
14288
  rtcPeerConnController.setMemberId(memberId);
14060
14289
  if (callId) this.webRtcCallSession.addCallId(callId);
14061
- this._signalingStatus$.next("ringing");
14062
14290
  this.attachManager.attach(this.webRtcCallSession);
14063
14291
  logger$10.info("[WebRTCManager] Verto invite successful");
14064
14292
  logger$10.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
@@ -14090,6 +14318,7 @@ var WebRTCVertoManager = class extends VertoManager {
14090
14318
  inputVideoStream: options.inputVideoStream,
14091
14319
  receiveAudio: options.receiveAudio,
14092
14320
  receiveVideo: options.receiveVideo,
14321
+ webRTCApiProvider: this.webRTCApiProvider,
14093
14322
  ...this.RTCPeerConnectionConfig
14094
14323
  }, options.initOffer, this.deviceController);
14095
14324
  this.setupLocalDescriptionHandler(rtcPeerConnController);
@@ -14101,6 +14330,36 @@ var WebRTCVertoManager = class extends VertoManager {
14101
14330
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
14102
14331
  this.onError?.(error);
14103
14332
  });
14333
+ if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
14334
+ }
14335
+ async handleInboundAnswer(rtcPeerConnController) {
14336
+ logger$10.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
14337
+ 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);
14338
+ if (vertoByeOrAccepted === null) {
14339
+ logger$10.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
14340
+ return;
14341
+ }
14342
+ if (isVertoByeMessage(vertoByeOrAccepted)) {
14343
+ logger$10.info("[WebRTCManager] Inbound call ended by remote before answer.");
14344
+ this.callSession?.destroy();
14345
+ } else if (!vertoByeOrAccepted) {
14346
+ logger$10.info("[WebRTCManager] Inbound call rejected by user.");
14347
+ try {
14348
+ await this.bye("USER_BUSY");
14349
+ } finally {
14350
+ this._signalingStatus$.next("disconnected");
14351
+ this.callSession?.destroy();
14352
+ }
14353
+ } else {
14354
+ logger$10.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
14355
+ const answerOptions = this.webRtcCallSession.answerMediaOptions;
14356
+ try {
14357
+ await rtcPeerConnController.acceptInbound(answerOptions);
14358
+ } catch (error) {
14359
+ logger$10.error("[WebRTCManager] Error creating inbound answer:", error);
14360
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14361
+ }
14362
+ }
14104
14363
  }
14105
14364
  setupVertoAttachHandler() {
14106
14365
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
@@ -14118,7 +14377,7 @@ var WebRTCVertoManager = class extends VertoManager {
14118
14377
  });
14119
14378
  }
14120
14379
  initObservables(rtcPeerConnController) {
14121
- 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));
14380
+ 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$));
14122
14381
  this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14123
14382
  this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14124
14383
  }
@@ -14134,7 +14393,6 @@ var WebRTCVertoManager = class extends VertoManager {
14134
14393
  });
14135
14394
  this.sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnController);
14136
14395
  } else if (initial) {
14137
- this._signalingStatus$.next("trying");
14138
14396
  const vertoMessageRequest = VertoInvite({
14139
14397
  dialogParams,
14140
14398
  sdp
@@ -14152,22 +14410,23 @@ var WebRTCVertoManager = class extends VertoManager {
14152
14410
  }
14153
14411
  setupVertoByeHandler() {
14154
14412
  this.subscribeTo(this.vertoBye$, () => {
14413
+ this._signalingStatus$.next("disconnected");
14155
14414
  this.attachManager.detach(this.webRtcCallSession);
14156
14415
  this.callSession?.destroy();
14157
14416
  });
14158
14417
  }
14159
- getSendLocalSDPOptionalParams(rtcPeerConnController) {
14418
+ getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod) {
14160
14419
  let subscribe = void 0;
14161
- const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
14162
- if (initial) {
14420
+ if (!rtcPeerConnController.firstSDPExchangeCompleted) {
14163
14421
  subscribe = [];
14164
14422
  if (rtcPeerConnController.isMainDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeMainDevice);
14165
14423
  else if (rtcPeerConnController.isAdditionalDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);
14166
14424
  else if (rtcPeerConnController.isScreenShare) subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);
14167
14425
  }
14426
+ const isInvite = vertoMethod === "verto.invite";
14168
14427
  return {
14169
14428
  callID: rtcPeerConnController.id,
14170
- node_id: initial ? "" : this._nodeId$.value ?? "",
14429
+ node_id: isInvite ? "" : this._nodeId$.value ?? "",
14171
14430
  subscribe
14172
14431
  };
14173
14432
  }
@@ -14179,7 +14438,12 @@ var WebRTCVertoManager = class extends VertoManager {
14179
14438
  this.callSession?.destroy();
14180
14439
  } else if (!vertoByeOrAccepted) {
14181
14440
  logger$10.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
14182
- await this.bye("USER_BUSY");
14441
+ try {
14442
+ await this.bye("USER_BUSY");
14443
+ } finally {
14444
+ this._signalingStatus$.next("disconnected");
14445
+ this.callSession?.destroy();
14446
+ }
14183
14447
  } else {
14184
14448
  logger$10.debug("[WebRTCManager] Call accepted, sending answer");
14185
14449
  try {
@@ -14209,8 +14473,7 @@ var WebRTCVertoManager = class extends VertoManager {
14209
14473
  userVariables: {
14210
14474
  memberCallId: this.webRtcCallSession.id,
14211
14475
  memberId,
14212
- ...this.webRtcCallSession.options.userVariables,
14213
- ...PreferencesContainer.instance.userVariables
14476
+ ...this.webRtcCallSession.userVariables
14214
14477
  },
14215
14478
  screenShare: rtcPeerConnectionController.isScreenShare,
14216
14479
  additionalDevice: rtcPeerConnectionController.isAdditionalDevice,
@@ -14268,7 +14531,8 @@ var WebRTCVertoManager = class extends VertoManager {
14268
14531
  rtcPeerConnController = new RTCPeerConnectionController({
14269
14532
  ...options,
14270
14533
  ...this.RTCPeerConnectionConfig,
14271
- propose
14534
+ propose,
14535
+ webRTCApiProvider: this.webRTCApiProvider
14272
14536
  }, void 0, this.deviceController);
14273
14537
  this.setupLocalDescriptionHandler(rtcPeerConnController);
14274
14538
  if (propose === "screenshare") this._screenShareId = rtcPeerConnController.id;
@@ -14325,7 +14589,7 @@ var WebRTCVertoManager = class extends VertoManager {
14325
14589
  try {
14326
14590
  const causeParams = cause ? {
14327
14591
  cause,
14328
- cause_code: VertoByeCauseCodes[cause]
14592
+ causeCode: VertoByeCauseCodes[cause]
14329
14593
  } : {};
14330
14594
  await this.executeVerto(VertoBye({
14331
14595
  ...causeParams,
@@ -14408,6 +14672,20 @@ var ParticipantFactory = class {
14408
14672
  //#region src/core/entities/Call.ts
14409
14673
  var import_cjs$9 = require_cjs();
14410
14674
  const logger$9 = getLogger();
14675
+ const fromDestinationParams = (destination) => {
14676
+ if (!destination) return {};
14677
+ try {
14678
+ const url = new URL(`destination:${destination}`);
14679
+ const params = {};
14680
+ url.searchParams.forEach((value, key) => {
14681
+ params[key] = value;
14682
+ });
14683
+ return params;
14684
+ } catch (error) {
14685
+ logger$9.warn(`Failed to parse destination URI: ${destination}`, error);
14686
+ return {};
14687
+ }
14688
+ };
14411
14689
  /**
14412
14690
  * Concrete WebRTC call implementation.
14413
14691
  *
@@ -14421,27 +14699,50 @@ var WebRTCCall = class extends Destroyable {
14421
14699
  this.clientSession = clientSession;
14422
14700
  this.options = options;
14423
14701
  this.address = address;
14424
- this.participantsMap = /* @__PURE__ */ new Map();
14425
- this._errors$ = this.createSubject();
14702
+ this._errors$ = this.createReplaySubject(1);
14703
+ this._lastMergedStatus = "new";
14426
14704
  this._answered$ = this.createReplaySubject();
14427
14705
  this._holdState = false;
14706
+ this._userVariables$ = this.createBehaviorSubject({ ...PreferencesContainer.instance.userVariables });
14428
14707
  this.id = options.callId ?? v4_default();
14429
14708
  this.to = options.to;
14709
+ this._userVariables$.next({
14710
+ ...this._userVariables$.value,
14711
+ ...fromDestinationParams(options.to),
14712
+ ...options.userVariables
14713
+ });
14714
+ this.subscribeTo(this.webrtcMessages$, (message) => {
14715
+ const userVars = getValueFrom(message, "params.userVariables");
14716
+ if (userVars) this._userVariables$.next({
14717
+ ...this._userVariables$.value,
14718
+ ...userVars
14719
+ });
14720
+ });
14430
14721
  const managers = initialization.initializeManagers(this);
14431
14722
  this.vertoManager = managers.vertoManager;
14432
14723
  this.callEventsManager = managers.callEventsManager;
14433
- if (options.initOffer) this._status$ = this.createBehaviorSubject("ringing");
14434
- else this._status$ = this.createBehaviorSubject("new");
14724
+ if (options.initOffer) {
14725
+ this._status$ = this.createBehaviorSubject("ringing");
14726
+ this._lastMergedStatus = "ringing";
14727
+ } else this._status$ = this.createBehaviorSubject("new");
14435
14728
  const { deviceController } = initialization;
14436
14729
  this.participantFactory = new ParticipantFactory(this.executeMethod.bind(this), this.vertoManager, deviceController);
14437
14730
  }
14438
14731
  /** Observable stream of errors from media, signaling, and peer connection layers. */
14439
14732
  get errors$() {
14440
- return this._errors$.asObservable();
14733
+ return this.deferEmission(this._errors$.asObservable());
14441
14734
  }
14442
- /** @internal Push an error to the call's error stream. */
14443
- emitError(error) {
14444
- this._errors$.next(error);
14735
+ /**
14736
+ * @internal Push an error to the call's error stream.
14737
+ * Fatal errors automatically transition the call to `'failed'` and destroy it.
14738
+ */
14739
+ emitError(callError) {
14740
+ if (this._status$.value === "destroyed" || this._status$.value === "failed") return;
14741
+ this._errors$.next(callError);
14742
+ if (callError.fatal) {
14743
+ this._status$.next("failed");
14744
+ this.destroy();
14745
+ }
14445
14746
  }
14446
14747
  /** Whether this call is `'inbound'` or `'outbound'`. */
14447
14748
  get direction() {
@@ -14449,7 +14750,7 @@ var WebRTCCall = class extends Destroyable {
14449
14750
  }
14450
14751
  /** Observable of the address associated with this call. */
14451
14752
  get address$() {
14452
- return (0, import_cjs$9.from)([this.address]);
14753
+ return this.deferEmission((0, import_cjs$9.from)([this.address])).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14453
14754
  }
14454
14755
  /** Display name of the caller. */
14455
14756
  get fromName() {
@@ -14481,7 +14782,7 @@ var WebRTCCall = class extends Destroyable {
14481
14782
  }
14482
14783
  /** Current snapshot of all participants in the call. */
14483
14784
  get participants() {
14484
- return Array.from(this.participantsMap.values());
14785
+ return this.callEventsManager.participants;
14485
14786
  }
14486
14787
  /** The local participant, or `null` if not yet joined. */
14487
14788
  get self() {
@@ -14490,7 +14791,6 @@ var WebRTCCall = class extends Destroyable {
14490
14791
  async toggleLock() {
14491
14792
  const method = this.locked ? "call.unlock" : "call.lock";
14492
14793
  await this.executeMethod(this.selfId ?? "", method, {});
14493
- throw new UnimplementedError();
14494
14794
  }
14495
14795
  async toggleHold() {
14496
14796
  if (this._holdState) await this.vertoManager.unhold();
@@ -14511,7 +14811,7 @@ var WebRTCCall = class extends Destroyable {
14511
14811
  }
14512
14812
  /** Observable of layout layer positions for all participants. */
14513
14813
  get layoutLayers$() {
14514
- return this.callEventsManager.layoutLayers$;
14814
+ return this.deferEmission(this.callEventsManager.layoutLayers$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14515
14815
  }
14516
14816
  /** Current snapshot of layout layers. */
14517
14817
  get layoutLayers() {
@@ -14525,72 +14825,80 @@ var WebRTCCall = class extends Destroyable {
14525
14825
  params
14526
14826
  });
14527
14827
  try {
14528
- return await this.clientSession.execute(request);
14828
+ const response = await this.clientSession.execute(request);
14829
+ 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);
14830
+ return response;
14529
14831
  } catch (error) {
14530
14832
  logger$9.error(`[Call] Error executing method ${method} with params`, params, error);
14531
14833
  throw error;
14532
14834
  }
14533
14835
  }
14534
14836
  buildMethodParams(target, args) {
14535
- const reference = {
14536
- node_id: this.nodeId,
14537
- call_id: this.id
14837
+ const self = {
14838
+ node_id: this.nodeId ?? "",
14839
+ call_id: this.id,
14840
+ member_id: this.vertoManager.selfId ?? ""
14841
+ };
14842
+ if (typeof target === "object") return {
14843
+ ...args,
14844
+ self,
14845
+ targets: [target]
14538
14846
  };
14539
14847
  return {
14540
14848
  ...args,
14541
- self: {
14542
- ...reference,
14543
- member_id: this.vertoManager.selfId
14544
- },
14849
+ self,
14545
14850
  target: {
14546
- ...reference,
14851
+ node_id: this.nodeId ?? "",
14852
+ call_id: this.id,
14547
14853
  member_id: target
14548
14854
  }
14549
14855
  };
14550
14856
  }
14551
14857
  /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
14552
14858
  get status$() {
14553
- return (0, import_cjs$9.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$);
14859
+ 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) => {
14860
+ this._lastMergedStatus = status;
14861
+ })));
14554
14862
  }
14555
14863
  /** Observable of the participants list, emits on join/leave/update. */
14556
14864
  get participants$() {
14557
- return this.callEventsManager.participants$;
14865
+ return this.deferEmission(this.callEventsManager.participants$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14558
14866
  }
14559
14867
  /** Observable of the local (self) participant. */
14560
14868
  get self$() {
14561
- return this.callEventsManager.self$;
14869
+ return this.deferEmission(this.callEventsManager.self$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14562
14870
  }
14563
14871
  /** Observable indicating whether the call is being recorded. */
14564
14872
  get recording$() {
14565
- return this.callEventsManager.recording$;
14873
+ return this.deferEmission(this.callEventsManager.recording$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14566
14874
  }
14567
14875
  /** Observable indicating whether the call is being streamed. */
14568
14876
  get streaming$() {
14569
- return this.callEventsManager.streaming$;
14877
+ return this.deferEmission(this.callEventsManager.streaming$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14570
14878
  }
14571
14879
  /** Observable indicating whether raise-hand priority is active. */
14572
14880
  get raiseHandPriority$() {
14573
- return this.callEventsManager.raiseHandPriority$;
14881
+ return this.deferEmission(this.callEventsManager.raiseHandPriority$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14574
14882
  }
14575
14883
  /** Observable indicating whether the call room is locked. */
14576
14884
  get locked$() {
14577
- return this.callEventsManager.locked$;
14885
+ return this.deferEmission(this.callEventsManager.locked$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14578
14886
  }
14579
14887
  /** Observable of custom metadata associated with the call. */
14580
14888
  get meta$() {
14581
- return this.callEventsManager.meta$;
14889
+ return this.deferEmission(this.callEventsManager.meta$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14582
14890
  }
14583
14891
  /** Observable of the call's capability flags. */
14584
14892
  get capabilities$() {
14585
- return this.callEventsManager.capabilities$;
14893
+ return this.deferEmission(this.callEventsManager.capabilities$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14586
14894
  }
14587
14895
  /** Observable of the current layout name. */
14588
14896
  get layout$() {
14589
- return this.callEventsManager.layout$;
14897
+ return this.deferEmission(this.callEventsManager.layout$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14590
14898
  }
14591
14899
  /** Current call status. */
14592
14900
  get status() {
14593
- return this._status$.value;
14901
+ return this._lastMergedStatus;
14594
14902
  }
14595
14903
  /** Whether the call is currently being recorded. */
14596
14904
  get recording() {
@@ -14618,7 +14926,7 @@ var WebRTCCall = class extends Destroyable {
14618
14926
  }
14619
14927
  /** Observable of available layout names. */
14620
14928
  get layouts$() {
14621
- return this.callEventsManager.layouts$;
14929
+ return this.deferEmission(this.callEventsManager.layouts$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14622
14930
  }
14623
14931
  /** Current snapshot of available layout names. */
14624
14932
  get layouts() {
@@ -14626,7 +14934,7 @@ var WebRTCCall = class extends Destroyable {
14626
14934
  }
14627
14935
  /** Observable of the local media stream (camera/microphone). */
14628
14936
  get localStream$() {
14629
- return this.vertoManager.localStream$;
14937
+ return this.deferEmission(this.vertoManager.localStream$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14630
14938
  }
14631
14939
  /** Current local media stream, or `null` if not available. */
14632
14940
  get localStream() {
@@ -14634,12 +14942,27 @@ var WebRTCCall = class extends Destroyable {
14634
14942
  }
14635
14943
  /** Observable of the remote media stream from the far end. */
14636
14944
  get remoteStream$() {
14637
- return this.vertoManager.remoteStream$;
14945
+ return this.deferEmission(this.vertoManager.remoteStream$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14638
14946
  }
14639
14947
  /** Current remote media stream, or `null` if not available. */
14640
14948
  get remoteStream() {
14641
14949
  return this.vertoManager.remoteStream;
14642
14950
  }
14951
+ /** Observable of custom user variables associated with the call. */
14952
+ get userVariables$() {
14953
+ return this.deferEmission(this._userVariables$.asObservable());
14954
+ }
14955
+ /** a copy of the current custom user variables of the call. */
14956
+ get userVariables() {
14957
+ return { ...this._userVariables$.value };
14958
+ }
14959
+ /** Merge current custom user variables of the call. */
14960
+ set userVariables(variables) {
14961
+ this._userVariables$.next({
14962
+ ...this._userVariables$.value,
14963
+ ...variables
14964
+ });
14965
+ }
14643
14966
  /** @internal */
14644
14967
  createParticipant(memberId, selfId) {
14645
14968
  if (memberId === (selfId ?? this.vertoManager.selfId)) return this.participantFactory.createSelfParticipant(memberId);
@@ -14647,14 +14970,14 @@ var WebRTCCall = class extends Destroyable {
14647
14970
  }
14648
14971
  /** Observable of the current audio/video send/receive directions. */
14649
14972
  get mediaDirections$() {
14650
- return this.vertoManager.mediaDirections$;
14973
+ return this.deferEmission(this.vertoManager.mediaDirections$).pipe((0, import_cjs$9.takeUntil)(this._destroyed$));
14651
14974
  }
14652
14975
  /** Current audio/video send/receive directions. */
14653
14976
  get mediaDirections() {
14654
14977
  return this.vertoManager.mediaDirections;
14655
14978
  }
14656
14979
  get participantsId$() {
14657
- return this.participants$.pipe((0, import_cjs$9.map)((participants) => participants.map((participant) => participant.id)));
14980
+ return this.cachedObservable("participantsId$", () => this.participants$.pipe((0, import_cjs$9.map)((participants) => participants.map((participant) => participant.id))));
14658
14981
  }
14659
14982
  /** Executes a raw JSON-RPC request on the client session. */
14660
14983
  async execute(request, options) {
@@ -14689,35 +15012,35 @@ var WebRTCCall = class extends Destroyable {
14689
15012
  }
14690
15013
  }
14691
15014
  get callSessionEvents$() {
14692
- 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$));
15015
+ 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)()));
14693
15016
  }
14694
15017
  /** Observable of call-updated events. */
14695
15018
  get callUpdated$() {
14696
- return this.callSessionEvents$.pipe(filterAs(isCallUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15019
+ return this.publicCachedObservable("callUpdated$", () => this.callSessionEvents$.pipe(filterAs(isCallUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14697
15020
  }
14698
15021
  /** Observable of member-joined events. */
14699
15022
  get memberJoined$() {
14700
- return this.callSessionEvents$.pipe(filterAs(isMemberJoinedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15023
+ return this.publicCachedObservable("memberJoined$", () => this.callSessionEvents$.pipe(filterAs(isMemberJoinedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14701
15024
  }
14702
15025
  /** Observable of member-left events. */
14703
15026
  get memberLeft$() {
14704
- return this.callSessionEvents$.pipe(filterAs(isMemberLeftMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15027
+ return this.publicCachedObservable("memberLeft$", () => this.callSessionEvents$.pipe(filterAs(isMemberLeftMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14705
15028
  }
14706
15029
  /** Observable of member-updated events (mute, volume, etc.). */
14707
15030
  get memberUpdated$() {
14708
- return this.callSessionEvents$.pipe(filterAs(isMemberUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15031
+ return this.publicCachedObservable("memberUpdated$", () => this.callSessionEvents$.pipe(filterAs(isMemberUpdatedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14709
15032
  }
14710
15033
  /** Observable of member-talking events (speech start/stop). */
14711
15034
  get memberTalking$() {
14712
- return this.callSessionEvents$.pipe(filterAs(isMemberTalkingMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15035
+ return this.publicCachedObservable("memberTalking$", () => this.callSessionEvents$.pipe(filterAs(isMemberTalkingMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14713
15036
  }
14714
15037
  /** Observable of call state-change events. */
14715
15038
  get callStates$() {
14716
- return this.callSessionEvents$.pipe(filterAs(isCallStateMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15039
+ return this.publicCachedObservable("callStates$", () => this.callSessionEvents$.pipe(filterAs(isCallStateMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14717
15040
  }
14718
15041
  /** Observable of layout-changed events. */
14719
15042
  get layoutUpdates$() {
14720
- return this.callSessionEvents$.pipe(filterAs(isLayoutChangedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$));
15043
+ return this.publicCachedObservable("layoutUpdates$", () => this.callSessionEvents$.pipe(filterAs(isLayoutChangedMetadata, "params"), (0, import_cjs$9.takeUntil)(this.destroyed$)));
14721
15044
  }
14722
15045
  /** Underlying `RTCPeerConnection`, for advanced use cases. */
14723
15046
  get rtcPeerConnection() {
@@ -14725,19 +15048,19 @@ var WebRTCCall = class extends Destroyable {
14725
15048
  }
14726
15049
  /** Observable of raw signaling events as plain objects. */
14727
15050
  get signalingEvent$() {
14728
- return this.callEvent$.pipe((0, import_cjs$9.map)((event) => JSON.parse(JSON.stringify(event))));
15051
+ return this.publicCachedObservable("signalingEvent$", () => this.callEvent$.pipe((0, import_cjs$9.map)((event) => JSON.parse(JSON.stringify(event)))));
14729
15052
  }
14730
15053
  /** Observable of WebRTC-specific signaling messages. */
14731
15054
  get webrtcMessages$() {
14732
- 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$));
15055
+ 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)()));
14733
15056
  }
14734
15057
  /** Observable of call-level signaling events. */
14735
15058
  get callEvent$() {
14736
- 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$));
15059
+ 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)()));
14737
15060
  }
14738
15061
  /** Observable of layout-changed signaling events. */
14739
15062
  get layoutEvent$() {
14740
- return this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params"));
15063
+ return this.cachedObservable("layoutEvent$", () => this.callEvent$.pipe(filterAs(isLayoutChangedMetadata, "params")));
14741
15064
  }
14742
15065
  /** Hangs up the call and releases all resources. */
14743
15066
  async hangup() {
@@ -14745,7 +15068,6 @@ var WebRTCCall = class extends Destroyable {
14745
15068
  try {
14746
15069
  await this.vertoManager.bye();
14747
15070
  } finally {
14748
- this._status$.next("destroyed");
14749
15071
  this.destroy();
14750
15072
  }
14751
15073
  }
@@ -14753,17 +15075,22 @@ var WebRTCCall = class extends Destroyable {
14753
15075
  async sendDigits(dtmf) {
14754
15076
  return this.vertoManager.sendDigits(dtmf);
14755
15077
  }
14756
- /** Accepts an inbound call. */
14757
- answer() {
15078
+ /** Accepts an inbound call, optionally overriding media options for the answer. */
15079
+ answer(options) {
15080
+ this._answerMediaOptions = options;
14758
15081
  this._answered$.next(true);
14759
15082
  }
15083
+ /** Media options provided when answering. Used internally by the VertoManager. */
15084
+ get answerMediaOptions() {
15085
+ return this._answerMediaOptions;
15086
+ }
14760
15087
  /** Rejects an inbound call. */
14761
15088
  reject() {
14762
15089
  this._answered$.next(false);
14763
15090
  }
14764
15091
  /** Observable that emits `true` when answered, `false` when rejected. */
14765
15092
  get answered$() {
14766
- return this._answered$.asObservable();
15093
+ return this.deferEmission(this._answered$.asObservable());
14767
15094
  }
14768
15095
  /**
14769
15096
  * Sets the call layout and participant positions.
@@ -14784,9 +15111,10 @@ var WebRTCCall = class extends Destroyable {
14784
15111
  }
14785
15112
  /** Destroys the call, releasing all resources and subscriptions. */
14786
15113
  destroy() {
15114
+ if (this._status$.value === "destroyed") return;
15115
+ this._status$.next("destroyed");
14787
15116
  this.vertoManager.destroy();
14788
15117
  this.callEventsManager.destroy();
14789
- this.participantsMap.clear();
14790
15118
  super.destroy();
14791
15119
  }
14792
15120
  };
@@ -14794,14 +15122,32 @@ var WebRTCCall = class extends Destroyable {
14794
15122
  //#endregion
14795
15123
  //#region src/managers/CallFactory.ts
14796
15124
  /**
15125
+ * Infers the semantic error category from a raw Error thrown by VertoManager
15126
+ * or an RTCPeerConnection layer.
15127
+ */
15128
+ function inferCallErrorKind(error) {
15129
+ if (error instanceof RPCTimeoutError) return "timeout";
15130
+ if (error instanceof JSONRPCError) return "signaling";
15131
+ if (error instanceof MediaTrackError) return "media";
15132
+ if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError) return "network";
15133
+ return "internal";
15134
+ }
15135
+ /** Determines whether an error should be fatal (destroy the call). */
15136
+ function isFatalError(error) {
15137
+ if (error instanceof VertoPongError) return false;
15138
+ if (error instanceof MediaTrackError) return false;
15139
+ return true;
15140
+ }
15141
+ /**
14797
15142
  * Factory for creating WebRTCCall instances with proper manager wiring.
14798
15143
  * Eliminates circular dependencies by centralizing Call and Manager creation.
14799
15144
  */
14800
15145
  var CallFactory = class {
14801
- constructor(sessionManager, deviceController, attachManager) {
15146
+ constructor(sessionManager, deviceController, attachManager, webRTCApiProvider) {
14802
15147
  this.sessionManager = sessionManager;
14803
15148
  this.deviceController = deviceController;
14804
15149
  this.attachManager = attachManager;
15150
+ this.webRTCApiProvider = webRTCApiProvider;
14805
15151
  }
14806
15152
  /**
14807
15153
  * Create a new WebRTCCall with properly initialized managers
@@ -14810,10 +15156,16 @@ var CallFactory = class {
14810
15156
  return new WebRTCCall(this.sessionManager, options, {
14811
15157
  initializeManagers: (callInstance) => {
14812
15158
  return {
14813
- vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, {
15159
+ vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, this.webRTCApiProvider, {
14814
15160
  nodeId: options.nodeId,
14815
15161
  onError: (error) => {
14816
- callInstance.emitError(error);
15162
+ const callError = {
15163
+ kind: inferCallErrorKind(error),
15164
+ fatal: isFatalError(error),
15165
+ error,
15166
+ callId: callInstance.id
15167
+ };
15168
+ callInstance.emitError(callError);
14817
15169
  }
14818
15170
  }),
14819
15171
  callEventsManager: new CallEventsManager(callInstance)
@@ -14837,7 +15189,10 @@ var Fetcher = class {
14837
15189
  this.nextUrl = `${this.endpoint}?${params}`;
14838
15190
  }
14839
15191
  async next() {
14840
- if (!this.nextUrl) return [];
15192
+ if (!this.nextUrl) {
15193
+ this.hasMore = false;
15194
+ return [];
15195
+ }
14841
15196
  const response = await this.http.request({
14842
15197
  ...GET_PARAMS,
14843
15198
  url: this.nextUrl
@@ -14845,6 +15200,7 @@ var Fetcher = class {
14845
15200
  if (response.ok && !!response.body) {
14846
15201
  const result = JSON.parse(response.body);
14847
15202
  this.nextUrl = result.links.next;
15203
+ this.hasMore = !!this.nextUrl;
14848
15204
  return result.data.filter(this.filter).map(this.mapper);
14849
15205
  }
14850
15206
  logger$8.error("Failed to fetch entity");
@@ -14878,10 +15234,11 @@ var EntityCollection = class extends Destroyable {
14878
15234
  this.observablesRegistry.get(data.id)?.next(updated);
14879
15235
  this.values$.next(Array.from(this.collectionData.values()));
14880
15236
  };
15237
+ this._hasMore$ = this.createBehaviorSubject(true);
14881
15238
  this._destroy$ = new import_cjs$8.Subject();
14882
15239
  this.updateSubscription = this.update$.subscribe(this.upsertData);
14883
15240
  this.loading$.next(false);
14884
- 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$));
15241
+ 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$));
14885
15242
  }
14886
15243
  get loading() {
14887
15244
  return this.loading$.value;
@@ -14890,23 +15247,27 @@ var EntityCollection = class extends Destroyable {
14890
15247
  return this.fetchController.hasMore ?? true;
14891
15248
  }
14892
15249
  get updated$() {
14893
- 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$));
15250
+ 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$)));
14894
15251
  }
14895
15252
  get values() {
14896
15253
  return Array.from(this.collectionData.values());
14897
15254
  }
14898
15255
  async init() {
14899
- if (this.fetchController.hasMore === false) return Promise.resolve(false);
15256
+ if (this.fetchController.hasMore === false) {
15257
+ this._hasMore$.next(false);
15258
+ return;
15259
+ }
14900
15260
  await this.fetchMore();
14901
- return this.fetchController.hasMore ?? true;
14902
15261
  }
14903
15262
  async fetchMore() {
14904
15263
  try {
14905
15264
  this.loading$.next(true);
14906
15265
  (await this.fetchController.next()).forEach(this.upsertData);
15266
+ this._hasMore$.next(this.fetchController.hasMore ?? false);
14907
15267
  this.loading$.next(false);
14908
15268
  } catch (error) {
14909
15269
  logger$8.error(`Failed to fetch initial collection data`, error);
15270
+ this._hasMore$.next(this.fetchController.hasMore ?? false);
14910
15271
  this.loading$.next(false);
14911
15272
  this.onError?.(new CollectionFetchError("fetchMore", error));
14912
15273
  }
@@ -14970,7 +15331,7 @@ var EntityCollectionTransformed = class {
14970
15331
  return this.originalCollection.values.filter(this.filter).map(this.mapper);
14971
15332
  }
14972
15333
  get values$() {
14973
- return this.originalCollection.values$.pipe((0, import_cjs$8.map)((values) => values.filter(this.filter).map(this.mapper)));
15334
+ return this._values$ ??= this.originalCollection.values$.pipe((0, import_cjs$8.map)((values) => values.filter(this.filter).map(this.mapper)));
14974
15335
  }
14975
15336
  get$(id) {
14976
15337
  const original$ = this.originalCollection.get$(id);
@@ -15044,7 +15405,7 @@ var Address = class extends Destroyable {
15044
15405
  }
15045
15406
  /** Observable of the human-readable display name. */
15046
15407
  get displayName$() {
15047
- return this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.display_name), (0, import_cjs$7.takeUntil)(this.destroyed$));
15408
+ return this.cachedObservable("displayName$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.display_name), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15048
15409
  }
15049
15410
  /** Human-readable display name. */
15050
15411
  get displayName() {
@@ -15053,7 +15414,7 @@ var Address = class extends Destroyable {
15053
15414
  }
15054
15415
  /** Observable of the preview image URL. */
15055
15416
  get previewUrl$() {
15056
- return this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.preview_url), (0, import_cjs$7.takeUntil)(this.destroyed$));
15417
+ return this.cachedObservable("previewUrl$", () => this._state$.pipe(filterNull(), (0, import_cjs$7.map)((state) => state.preview_url), (0, import_cjs$7.takeUntil)(this.destroyed$)));
15057
15418
  }
15058
15419
  /** Preview image URL. */
15059
15420
  get previewUrl() {
@@ -15062,7 +15423,7 @@ var Address = class extends Destroyable {
15062
15423
  }
15063
15424
  /** Observable of the cover image URL. */
15064
15425
  get coverUrl$() {
15065
- 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$));
15426
+ 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$)));
15066
15427
  }
15067
15428
  /** Cover image URL. */
15068
15429
  get coverUrl() {
@@ -15071,7 +15432,7 @@ var Address = class extends Destroyable {
15071
15432
  }
15072
15433
  /** Observable of the underlying resource ID. */
15073
15434
  get resourceId$() {
15074
- 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$));
15435
+ 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$)));
15075
15436
  }
15076
15437
  /** Underlying resource ID. */
15077
15438
  get resourceId() {
@@ -15080,7 +15441,7 @@ var Address = class extends Destroyable {
15080
15441
  }
15081
15442
  /** Observable of the resource type (e.g. `'room'`, `'subscriber'`). */
15082
15443
  get type$() {
15083
- 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$));
15444
+ 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$)));
15084
15445
  }
15085
15446
  /** Resource type (e.g. `'room'`, `'subscriber'`). */
15086
15447
  get type() {
@@ -15089,7 +15450,7 @@ var Address = class extends Destroyable {
15089
15450
  }
15090
15451
  /** Observable of available communication channels (audio, video, messaging). */
15091
15452
  get channels$() {
15092
- 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$));
15453
+ 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$)));
15093
15454
  }
15094
15455
  /** Available communication channels. */
15095
15456
  get channels() {
@@ -15103,7 +15464,7 @@ var Address = class extends Destroyable {
15103
15464
  }
15104
15465
  /** Observable indicating whether the address (room) is locked. */
15105
15466
  get locked$() {
15106
- 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$));
15467
+ 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$)));
15107
15468
  }
15108
15469
  /** Sends a text message to this address. */
15109
15470
  async sendText(text) {
@@ -15184,9 +15545,15 @@ var PendingRPC = class PendingRPC {
15184
15545
  return () => signal.removeEventListener("abort", abortHandler);
15185
15546
  }) : import_cjs$6.NEVER).subscribe({
15186
15547
  next: (response) => {
15187
- logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15188
15548
  isSettled = true;
15189
- resolve(response);
15549
+ if (response.error) {
15550
+ const rpcError = new JSONRPCError(response.error.code, response.error.message, response.error.data, void 0, request.id);
15551
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
15552
+ reject(rpcError);
15553
+ } else {
15554
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15555
+ resolve(response);
15556
+ }
15190
15557
  subscription.unsubscribe();
15191
15558
  },
15192
15559
  error: (error) => {
@@ -15226,7 +15593,7 @@ const getAddressSearchURI = (options) => {
15226
15593
  return name;
15227
15594
  };
15228
15595
  var ClientSessionManager = class extends Destroyable {
15229
- constructor(credential, transport, storage, authorizationStateKey, deviceController, attachManager) {
15596
+ constructor(credential, transport, storage, authorizationStateKey, deviceController, attachManager, webRTCApiProvider) {
15230
15597
  super();
15231
15598
  this.credential = credential;
15232
15599
  this.transport = transport;
@@ -15236,24 +15603,24 @@ var ClientSessionManager = class extends Destroyable {
15236
15603
  this.callCreateTimeout = 6e3;
15237
15604
  this.agent = `signalwire-typescript-sdk/1.0.0`;
15238
15605
  this.eventAcks = true;
15239
- this.authorization$ = this.createSubject();
15240
15606
  this.authorizationState$ = this.createReplaySubject(1);
15241
15607
  this.connectVersion = {
15242
15608
  major: 4,
15243
15609
  minor: 0,
15244
15610
  revision: 0
15245
15611
  };
15246
- this._errors$ = this.createSubject();
15612
+ this._authorization$ = this.createBehaviorSubject(void 0);
15613
+ this._errors$ = this.createReplaySubject(1);
15247
15614
  this._authenticated$ = this.createBehaviorSubject(false);
15248
15615
  this._subscriberInfo$ = this.createBehaviorSubject(null);
15249
15616
  this._calls$ = this.createBehaviorSubject({});
15250
15617
  this._iceServers$ = this.createBehaviorSubject([]);
15251
15618
  attachManager.setSession(this);
15252
- this.callFactory = new CallFactory(this, deviceController, attachManager);
15619
+ this.callFactory = new CallFactory(this, deviceController, attachManager, webRTCApiProvider);
15253
15620
  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$));
15254
15621
  }
15255
15622
  get incomingCalls$() {
15256
- return this.calls$.pipe((0, import_cjs$5.map)((calls) => calls.filter((call) => call.direction === "inbound")));
15623
+ return this.cachedObservable("incomingCalls$", () => this.calls$.pipe((0, import_cjs$5.map)((calls) => calls.filter((call) => call.direction === "inbound"))));
15257
15624
  }
15258
15625
  get incomingCalls() {
15259
15626
  return Object.values(this._calls$.value).filter((call) => call.direction === "inbound");
@@ -15265,7 +15632,7 @@ var ClientSessionManager = class extends Destroyable {
15265
15632
  return this._subscriberInfo$.value;
15266
15633
  }
15267
15634
  get calls$() {
15268
- return this._calls$.pipe((0, import_cjs$5.map)((calls) => Object.values(calls)));
15635
+ return this.cachedObservable("calls$", () => this._calls$.pipe((0, import_cjs$5.map)((calls) => Object.values(calls))));
15269
15636
  }
15270
15637
  get calls() {
15271
15638
  return Object.values(this._calls$.value);
@@ -15273,6 +15640,12 @@ var ClientSessionManager = class extends Destroyable {
15273
15640
  get iceServers() {
15274
15641
  return this._iceServers$.value;
15275
15642
  }
15643
+ get authorization$() {
15644
+ return this._authorization$.asObservable();
15645
+ }
15646
+ get authorization() {
15647
+ return this._authorization$.value;
15648
+ }
15276
15649
  get errors$() {
15277
15650
  return this._errors$.asObservable();
15278
15651
  }
@@ -15368,20 +15741,20 @@ var ClientSessionManager = class extends Destroyable {
15368
15741
  }
15369
15742
  }
15370
15743
  get authStateEvent$() {
15371
- return this.signalingEvent$.pipe((0, import_cjs$5.tap)((msg) => {
15744
+ return this.cachedObservable("authStateEvent$", () => this.signalingEvent$.pipe((0, import_cjs$5.tap)((msg) => {
15372
15745
  logger$6.debug("[Session] Received incoming message:", msg);
15373
15746
  }), filterAs(isSignalwireAuthorizationStateMetadata, "params"), (0, import_cjs$5.tap)((event) => {
15374
15747
  logger$6.debug("[Session] Authorization state event received:", event.authorization_state);
15375
- }));
15748
+ })));
15376
15749
  }
15377
15750
  get signalingEvent$() {
15378
- return this.transport.incomingEvent$.pipe(filterAs(isSignalwireRequest, "params"));
15751
+ return this.cachedObservable("signalingEvent$", () => this.transport.incomingEvent$.pipe(filterAs(isSignalwireRequest, "params"), (0, import_cjs$5.share)()));
15379
15752
  }
15380
15753
  get vertoInvite$() {
15381
- 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) => ({
15754
+ 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) => ({
15382
15755
  node_id: event.node_id,
15383
15756
  ...event.params.params
15384
- })));
15757
+ }))));
15385
15758
  }
15386
15759
  get contexts() {
15387
15760
  return [];
@@ -15424,6 +15797,24 @@ var ClientSessionManager = class extends Destroyable {
15424
15797
  this._errors$.next(new AuthStateHandlerError(error));
15425
15798
  }
15426
15799
  }
15800
+ async reauthenticate(token) {
15801
+ logger$6.debug("[Session] Re-authenticating session");
15802
+ try {
15803
+ const request = RPCReauthenticate({
15804
+ project: this._authorization$.value?.project_id ?? "",
15805
+ jwt_token: token
15806
+ });
15807
+ 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) => {
15808
+ logger$6.error("[Session] Re-authentication RPC failed:", err);
15809
+ throw err;
15810
+ })));
15811
+ logger$6.debug("[Session] Re-authentication successful, updating stored auth state");
15812
+ } catch (error) {
15813
+ logger$6.error("[Session] Re-authentication failed:", error);
15814
+ this._errors$.next(new AuthStateHandlerError(error));
15815
+ throw error;
15816
+ }
15817
+ }
15427
15818
  async authenticate() {
15428
15819
  logger$6.debug("[Session] Starting authentication process");
15429
15820
  const params = {
@@ -15447,6 +15838,7 @@ var ClientSessionManager = class extends Destroyable {
15447
15838
  ...params,
15448
15839
  ...persistedParams
15449
15840
  });
15841
+ this.transport.resetSessionEpoch();
15450
15842
  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)(() => {
15451
15843
  logger$6.debug("[Session] Response passed filter, processing authentication result");
15452
15844
  }), (0, import_cjs$5.take)(1), (0, import_cjs$5.catchError)((err) => {
@@ -15459,7 +15851,7 @@ var ClientSessionManager = class extends Destroyable {
15459
15851
  hasIceServers: !!response.ice_servers
15460
15852
  });
15461
15853
  if (response.protocol) await this.transport.setProtocol(response.protocol);
15462
- this.authorization$.next(response.authorization);
15854
+ this._authorization$.next(response.authorization);
15463
15855
  this._iceServers$.next(response.ice_servers ?? []);
15464
15856
  this._authenticated$.next(true);
15465
15857
  logger$6.debug("[Session] Authentication completed successfully");
@@ -15478,7 +15870,8 @@ var ClientSessionManager = class extends Destroyable {
15478
15870
  to: invite.callee_id_number,
15479
15871
  fromName: invite.caller_id_name,
15480
15872
  from: invite.caller_id_number,
15481
- displayDirection: invite.display_direction
15873
+ displayDirection: invite.display_direction,
15874
+ userVariables: invite.userVariables
15482
15875
  });
15483
15876
  await (0, import_cjs$5.firstValueFrom)(callSession.status$);
15484
15877
  this._calls$.next({
@@ -15488,12 +15881,13 @@ var ClientSessionManager = class extends Destroyable {
15488
15881
  }
15489
15882
  async createOutboundCall(destination, options = {}) {
15490
15883
  const destinationURI = destination instanceof Address ? destination.defaultChannel : destination;
15884
+ let callSession;
15491
15885
  try {
15492
- const callSession = await this.createCall({
15886
+ callSession = await this.createCall({
15493
15887
  to: destinationURI,
15494
15888
  ...options
15495
15889
  });
15496
- 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)));
15890
+ 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)))));
15497
15891
  this._calls$.next({
15498
15892
  [`${callSession.id}`]: callSession,
15499
15893
  ...this._calls$.value
@@ -15501,19 +15895,25 @@ var ClientSessionManager = class extends Destroyable {
15501
15895
  return callSession;
15502
15896
  } catch (error) {
15503
15897
  logger$6.error("[Session] Error creating outbound call:", error);
15504
- const callError = new CallCreateError("Call create timeout", error);
15898
+ callSession?.destroy();
15899
+ const callError = new CallCreateError(error instanceof import_cjs$5.TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
15505
15900
  this._errors$.next(callError);
15506
15901
  throw callError;
15507
15902
  }
15508
15903
  }
15509
15904
  async createCall(options = {}) {
15510
15905
  try {
15511
- if (!this._directory) throw new DependencyError("Directory not initialized");
15512
15906
  const addressURI = getAddressSearchURI(options);
15513
- const addressId = await this._directory.findAddressIdByURI(addressURI);
15514
- if (!addressId) throw new DependencyError(`Address name: ${addressURI} not found`);
15515
- const address = this._directory.get(addressId);
15516
- if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
15907
+ let address;
15908
+ try {
15909
+ if (!this._directory) throw new DependencyError("Directory not initialized");
15910
+ const addressId = await this._directory.findAddressIdByURI(addressURI);
15911
+ if (!addressId) throw new DependencyError(`Address name: ${addressURI} not found`);
15912
+ address = this._directory.get(addressId);
15913
+ if (!address) throw new DependencyError(`Address ID: ${addressId} not found`);
15914
+ } catch (error) {
15915
+ logger$6.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
15916
+ }
15517
15917
  const callSession = this.callFactory.createCall(address, { ...options });
15518
15918
  callSession.status$.pipe((0, import_cjs$5.filter)((status) => status === "destroyed"), (0, import_cjs$5.take)(1)).subscribe(() => {
15519
15919
  const { [`${callSession.id}`]: _, ...remainingCalls } = this._calls$.value;
@@ -15522,7 +15922,7 @@ var ClientSessionManager = class extends Destroyable {
15522
15922
  return callSession;
15523
15923
  } catch (error) {
15524
15924
  logger$6.error("[Session] Error creating call session:", error);
15525
- throw new CallCreateError("Call create error", error);
15925
+ throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
15526
15926
  }
15527
15927
  }
15528
15928
  destroy() {
@@ -15543,6 +15943,9 @@ var ClientSessionWrapper = class {
15543
15943
  get signalingEvent$() {
15544
15944
  return this.clientSessionManager.signalingEvent$;
15545
15945
  }
15946
+ get iceServers() {
15947
+ return this.clientSessionManager.iceServers;
15948
+ }
15546
15949
  async execute(request, options) {
15547
15950
  return this.clientSessionManager.execute(request, options);
15548
15951
  }
@@ -15768,7 +16171,7 @@ var WebSocketController = class WebSocketController extends Destroyable {
15768
16171
  this.shouldReconnect = false;
15769
16172
  this._status$ = this.createBehaviorSubject("disconnected");
15770
16173
  this._incomingMessages$ = this.createSubject();
15771
- this._errors$ = this.createSubject();
16174
+ this._errors$ = this.createReplaySubject(1);
15772
16175
  this.reconnectDelayMin = options.reconnectDelayMin ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MIN_MS;
15773
16176
  this.reconnectDelayMax = options.reconnectDelayMax ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MAX_MS;
15774
16177
  this.connectionTimeout = options.connectionTimeout ?? WebSocketController.DEFAULT_CONNECTION_TIMEOUT_MS;
@@ -15919,7 +16322,30 @@ function isSignalwirePingRequest(value) {
15919
16322
  //#region src/managers/TransportManager.ts
15920
16323
  var import_cjs$1 = require_cjs();
15921
16324
  const logger$2 = getLogger();
15922
- var TransportManager = class extends Destroyable {
16325
+ var TransportManager = class TransportManager extends Destroyable {
16326
+ /**
16327
+ * Normalise a server event timestamp to epoch seconds.
16328
+ *
16329
+ * The server uses two formats:
16330
+ * - `webrtc.message`: float epoch seconds (e.g. 1774372099.022817)
16331
+ * - all other events: int epoch microseconds (e.g. 1774372099925857)
16332
+ *
16333
+ * Values above 1e12 are treated as microseconds and divided by 1e6.
16334
+ */
16335
+ static toEpochSeconds(ts) {
16336
+ const n = typeof ts === "string" ? Number(ts) : ts;
16337
+ return n > 0xe8d4a51000 ? n / 1e6 : n;
16338
+ }
16339
+ /**
16340
+ * Extract the event timestamp from a signalwire.event message.
16341
+ * Returns `null` for messages that have no timestamp
16342
+ * (e.g. signalwire.authorization.state, RPC responses).
16343
+ */
16344
+ static extractEventTimestamp(message) {
16345
+ if (!isSignalwireRequest(message)) return null;
16346
+ if (message.params.event_type === "signalwire.authorization.state") return null;
16347
+ return TransportManager.toEpochSeconds(message.params.timestamp);
16348
+ }
15923
16349
  constructor(storage, protocolKey, webSocketConstructor, relayHost, onError) {
15924
16350
  super();
15925
16351
  this.storage = storage;
@@ -15952,6 +16378,23 @@ var TransportManager = class extends Destroyable {
15952
16378
  return true;
15953
16379
  });
15954
16380
  };
16381
+ this.discardStaleEvents = () => {
16382
+ return (0, import_cjs$1.filter)((message) => {
16383
+ const ts = TransportManager.extractEventTimestamp(message);
16384
+ if (ts === null) return true;
16385
+ if (this._sessionEpoch === null) {
16386
+ this._sessionEpoch = ts;
16387
+ return true;
16388
+ }
16389
+ if (ts < this._sessionEpoch) {
16390
+ const eventType = isSignalwireRequest(message) ? message.params.event_type : "unknown";
16391
+ logger$2.warn(`[Transport] Discarding stale event: ${eventType} (timestamp=${ts.toFixed(3)}, sessionEpoch=${this._sessionEpoch.toFixed(3)}, delta=${(this._sessionEpoch - ts).toFixed(3)}s)`);
16392
+ return false;
16393
+ }
16394
+ return true;
16395
+ });
16396
+ };
16397
+ this._sessionEpoch = null;
15955
16398
  this._outgoingMessages$ = this.createSubject();
15956
16399
  this._webSocketConnections = new WebSocketController(webSocketConstructor, relayHost, this._outgoingMessages$.asObservable(), {
15957
16400
  connectionTimeout: PreferencesContainer.instance.connectionTimeout,
@@ -15976,7 +16419,14 @@ var TransportManager = class extends Destroyable {
15976
16419
  return import_cjs$1.EMPTY;
15977
16420
  }), (0, import_cjs$1.share)(), (0, import_cjs$1.takeUntil)(this.destroyed$));
15978
16421
  this._jsonRPCResponse$ = this._jsonRPCMessage$.pipe((0, import_cjs$1.filter)(isJSONRPCResponse));
15979
- 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$));
16422
+ 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$));
16423
+ }
16424
+ /**
16425
+ * Reset the session epoch. Call this before each signalwire.connect
16426
+ * so that the first event after authentication establishes the new baseline.
16427
+ */
16428
+ resetSessionEpoch() {
16429
+ this._sessionEpoch = null;
15980
16430
  }
15981
16431
  async setProtocol(protocol) {
15982
16432
  this.protocol$.next(protocol);
@@ -16095,7 +16545,8 @@ const buildOptionsFromDestination = (destination) => {
16095
16545
  const channel = new URLSearchParams(queryString).get("channel");
16096
16546
  if (channel === "video") return {
16097
16547
  audio: true,
16098
- video: true
16548
+ video: true,
16549
+ receiveVideo: true
16099
16550
  };
16100
16551
  else if (channel === "audio") return {
16101
16552
  audio: true,
@@ -16125,7 +16576,7 @@ var SignalWire = class extends Destroyable {
16125
16576
  this._directory$ = this.createBehaviorSubject(void 0);
16126
16577
  this._isConnected$ = this.createBehaviorSubject(false);
16127
16578
  this._isRegistered$ = this.createBehaviorSubject(false);
16128
- this._errors$ = this.createSubject();
16579
+ this._errors$ = this.createReplaySubject(1);
16129
16580
  this._options = {};
16130
16581
  this._deps = new DependencyContainer();
16131
16582
  this._options = {
@@ -16135,6 +16586,7 @@ var SignalWire = class extends Destroyable {
16135
16586
  if (this._options.storageImplementation) this._deps.storageImpl = this._options.storageImplementation;
16136
16587
  if (this._options.webSocketConstructor) this._deps.WebSocket = this._options.webSocketConstructor;
16137
16588
  if (this._options.savePreferences) this.preferences.enableSavePreferences(this._deps.storage);
16589
+ if (this._options.webRTCApiProvider) this._deps.webRTCApiProvider = this._options.webRTCApiProvider;
16138
16590
  this._deviceController = this._deps.deviceController;
16139
16591
  if (!this._options.skipDeviceMonitoring) this._deviceController.enableDeviceMonitoring();
16140
16592
  this.subscribeTo(this._deviceController.errors$, (error) => {
@@ -16168,7 +16620,10 @@ var SignalWire = class extends Destroyable {
16168
16620
  throw new InvalidCredentialsError("Provided credentials have expired.");
16169
16621
  }
16170
16622
  if (_credentials.expiry_at && credentialProvider.refresh) {
16171
- const refreshFn = credentialProvider.refresh;
16623
+ const refreshFn = async () => {
16624
+ if (!credentialProvider.refresh) throw new InvalidCredentialsError("Credential provider does not support refresh");
16625
+ return credentialProvider.refresh();
16626
+ };
16172
16627
  const refreshInterval = Math.max(_credentials.expiry_at - Date.now() - 5e3, 1e3);
16173
16628
  this._refreshTimerId = setTimeout(async () => {
16174
16629
  try {
@@ -16186,12 +16641,24 @@ var SignalWire = class extends Destroyable {
16186
16641
  }, refreshInterval);
16187
16642
  }
16188
16643
  this._deps.credential = _credentials;
16644
+ if (this.isConnected && this._clientSession.authenticated && _credentials.token) try {
16645
+ await this._clientSession.reauthenticate(_credentials.token);
16646
+ logger$1.info("[SignalWire] Session refreshed with new credentials.");
16647
+ } catch (error) {
16648
+ logger$1.error("[SignalWire] Failed to refresh session with new credentials:", error);
16649
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16650
+ }
16189
16651
  }
16190
16652
  async init() {
16191
16653
  this._subscriber$.next(new Subscriber(this._deps.http));
16192
16654
  if (!this._options.skipConnection) await this.connect();
16193
- if (this._options.skipReconnect && this._attachManager) await this._attachManager.flush();
16194
- if (!this._options.skipRegister) this.register();
16655
+ if (!this._options.reconnectAttachedCalls && this._attachManager) await this._attachManager.flush();
16656
+ if (!this._options.skipRegister) try {
16657
+ await this.register();
16658
+ } catch (error) {
16659
+ logger$1.error("[SignalWire] Registration failed:", error);
16660
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16661
+ }
16195
16662
  this.handleAttachments();
16196
16663
  }
16197
16664
  async handleAttachments() {
@@ -16259,7 +16726,7 @@ var SignalWire = class extends Destroyable {
16259
16726
  };
16260
16727
  this._transport = new TransportManager(this._deps.storage, this._deps.protocolKey, this._deps.WebSocket, PreferencesContainer.instance.relayHost ?? this._deps.relayHost, errorHandler);
16261
16728
  this._attachManager = new AttachManager(this._deps.storage, this._deps.deviceController, PreferencesContainer.instance.reconnectCallsTimeout, this._deps.attachedCallsKey);
16262
- this._clientSession = new ClientSessionManager(this._deps.credential, this._transport, this._deps.storage, this._deps.authorizationStateKey, this._deps.deviceController, this._attachManager);
16729
+ this._clientSession = new ClientSessionManager(this._deps.credential, this._transport, this._deps.storage, this._deps.authorizationStateKey, this._deps.deviceController, this._attachManager, this._deps.webRTCApiProvider);
16263
16730
  this._publicSession = new ClientSessionWrapper(this._clientSession);
16264
16731
  this.subscribeTo(this._clientSession.errors$, (error) => {
16265
16732
  this._errors$.next(error);
@@ -16283,7 +16750,7 @@ var SignalWire = class extends Destroyable {
16283
16750
  * ```
16284
16751
  */
16285
16752
  get subscriber$() {
16286
- return this._subscriber$.asObservable();
16753
+ return this.deferEmission(this._subscriber$.asObservable());
16287
16754
  }
16288
16755
  /** Current subscriber snapshot, or `undefined` if not yet authenticated. */
16289
16756
  get subscriber() {
@@ -16302,7 +16769,7 @@ var SignalWire = class extends Destroyable {
16302
16769
  * ```
16303
16770
  */
16304
16771
  get directory$() {
16305
- return this._directory$.asObservable();
16772
+ return this.deferEmission(this._directory$.asObservable());
16306
16773
  }
16307
16774
  /**
16308
16775
  * Current directory snapshot, or `undefined` if the client is not yet connected.
@@ -16313,7 +16780,7 @@ var SignalWire = class extends Destroyable {
16313
16780
  }
16314
16781
  /** Observable that emits when the subscriber registration state changes. */
16315
16782
  get isRegistered$() {
16316
- return this._isRegistered$.asObservable();
16783
+ return this.deferEmission(this._isRegistered$.asObservable());
16317
16784
  }
16318
16785
  /** Whether the subscriber is currently registered. */
16319
16786
  get isRegistered() {
@@ -16325,15 +16792,15 @@ var SignalWire = class extends Destroyable {
16325
16792
  }
16326
16793
  /** Observable that emits when the connection state changes. */
16327
16794
  get isConnected$() {
16328
- return this._isConnected$.asObservable();
16795
+ return this.deferEmission(this._isConnected$.asObservable());
16329
16796
  }
16330
16797
  /** Observable that emits `true` when the client is both connected and authenticated. */
16331
16798
  get ready$() {
16332
- return this._isConnected$.pipe((0, import_cjs.switchMap)((connected) => connected ? this._clientSession.authenticated$ : (0, import_cjs.of)(false)));
16799
+ return this.publicCachedObservable("ready$", () => this._isConnected$.pipe((0, import_cjs.switchMap)((connected) => connected ? this._clientSession.authenticated$ : (0, import_cjs.of)(false))));
16333
16800
  }
16334
16801
  /** Observable stream of errors from transport, authentication, and devices. */
16335
16802
  get errors$() {
16336
- return this._errors$.asObservable();
16803
+ return this.deferEmission(this._errors$.asObservable());
16337
16804
  }
16338
16805
  /** Disconnects the WebSocket and tears down the session. */
16339
16806
  async disconnect() {
@@ -16354,8 +16821,22 @@ var SignalWire = class extends Destroyable {
16354
16821
  }));
16355
16822
  this._isRegistered$.next(true);
16356
16823
  } catch (error) {
16357
- logger$1.error("[SignalWire] Failed to register subscriber:", error);
16824
+ logger$1.debug("[SignalWire] Failed to register subscriber, trying reauthentication...");
16825
+ if (this._deps.credential.token) this._clientSession.reauthenticate(this._deps.credential.token).then(async () => {
16826
+ logger$1.debug("[SignalWire] Reauthentication successful, retrying register()");
16827
+ await this._transport.execute(RPCExecute({
16828
+ method: "subscriber.online",
16829
+ params: {}
16830
+ }));
16831
+ this._isRegistered$.next(true);
16832
+ }).catch((reauthError) => {
16833
+ logger$1.error("[SignalWire] Reauthentication failed during register():", reauthError);
16834
+ 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 }) });
16835
+ this._errors$.next(registerError);
16836
+ throw registerError;
16837
+ });
16358
16838
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16839
+ throw error;
16359
16840
  }
16360
16841
  }
16361
16842
  /** Unregisters the subscriber, going offline for inbound calls. */
@@ -16394,7 +16875,7 @@ var SignalWire = class extends Destroyable {
16394
16875
  }
16395
16876
  /** Observable list of available audio input (microphone) devices. */
16396
16877
  get audioInputDevices$() {
16397
- return this._deviceController.audioInputDevices$;
16878
+ return this.deferEmission(this._deviceController.audioInputDevices$);
16398
16879
  }
16399
16880
  /** Current snapshot of available audio input devices. */
16400
16881
  get audioInputDevices() {
@@ -16402,7 +16883,7 @@ var SignalWire = class extends Destroyable {
16402
16883
  }
16403
16884
  /** Observable list of available audio output (speaker) devices. */
16404
16885
  get audioOutputDevices$() {
16405
- return this._deviceController.audioOutputDevices$;
16886
+ return this.deferEmission(this._deviceController.audioOutputDevices$);
16406
16887
  }
16407
16888
  /** Current snapshot of available audio output devices. */
16408
16889
  get audioOutputDevices() {
@@ -16410,7 +16891,7 @@ var SignalWire = class extends Destroyable {
16410
16891
  }
16411
16892
  /** Observable list of available video input (camera) devices. */
16412
16893
  get videoInputDevices$() {
16413
- return this._deviceController.videoInputDevices$;
16894
+ return this.deferEmission(this._deviceController.videoInputDevices$);
16414
16895
  }
16415
16896
  /** Current snapshot of available video input devices. */
16416
16897
  get videoInputDevices() {
@@ -16418,15 +16899,15 @@ var SignalWire = class extends Destroyable {
16418
16899
  }
16419
16900
  /** Observable of the currently selected audio input device. */
16420
16901
  get selectedAudioInputDevice$() {
16421
- return this._deviceController.selectedAudioInputDevice$;
16902
+ return this.deferEmission(this._deviceController.selectedAudioInputDevice$);
16422
16903
  }
16423
16904
  /** Observable of the currently selected audio output device. */
16424
16905
  get selectedAudioOutputDevice$() {
16425
- return this._deviceController.selectedAudioOutputDevice$;
16906
+ return this.deferEmission(this._deviceController.selectedAudioOutputDevice$);
16426
16907
  }
16427
16908
  /** Observable of the currently selected video input device. */
16428
16909
  get selectedVideoInputDevice$() {
16429
- return this._deviceController.selectedVideoInputDevice$;
16910
+ return this.deferEmission(this._deviceController.selectedVideoInputDevice$);
16430
16911
  }
16431
16912
  /** Currently selected audio input device, or `null` if none. */
16432
16913
  get selectedAudioInputDevice() {
@@ -16621,5 +17102,5 @@ emitReadyEvent();
16621
17102
  if (typeof process === "undefined") globalThis.process = { env: { NODE_ENV: "production" } };
16622
17103
 
16623
17104
  //#endregion
16624
- export { Address, CallCreateError, ClientPreferences, CollectionFetchError, MediaTrackError, MessageParseError, Participant, SelfParticipant, SignalWire, StaticCredentialProvider, Subscriber, VertoPongError, WebRTCCall, embeddableCall, isSelfParticipant, ready, version };
17105
+ export { Address, CallCreateError, ClientPreferences, CollectionFetchError, InvalidCredentialsError, MediaTrackError, MessageParseError, Participant, SelfCapabilities, SelfParticipant, SignalWire, StaticCredentialProvider, Subscriber, UnexpectedError, VertoPongError, WebRTCCall, embeddableCall, isSelfParticipant, ready, version };
16625
17106
  //# sourceMappingURL=browser.mjs.map