@signalwire/js 4.0.0-beta.6 → 4.0.0-beta.8

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.
@@ -3111,7 +3111,7 @@ var require_throwError = /* @__PURE__ */ __commonJSMin(((exports) => {
3111
3111
  exports.throwError = void 0;
3112
3112
  var Observable_1$18 = require_Observable();
3113
3113
  var isFunction_1$14 = require_isFunction();
3114
- function throwError(errorOrErrorFactory, scheduler) {
3114
+ function throwError$1(errorOrErrorFactory, scheduler) {
3115
3115
  var errorFactory = isFunction_1$14.isFunction(errorOrErrorFactory) ? errorOrErrorFactory : function() {
3116
3116
  return errorOrErrorFactory;
3117
3117
  };
@@ -3122,7 +3122,7 @@ var require_throwError = /* @__PURE__ */ __commonJSMin(((exports) => {
3122
3122
  return scheduler.schedule(init, 0, subscriber);
3123
3123
  } : init);
3124
3124
  }
3125
- exports.throwError = throwError;
3125
+ exports.throwError = throwError$1;
3126
3126
  }));
3127
3127
 
3128
3128
  //#endregion
@@ -4331,13 +4331,13 @@ var require_race$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
4331
4331
  var innerFrom_1$28 = require_innerFrom();
4332
4332
  var argsOrArgArray_1$4 = require_argsOrArgArray();
4333
4333
  var OperatorSubscriber_1$48 = require_OperatorSubscriber();
4334
- function race$3() {
4334
+ function race$4() {
4335
4335
  var sources = [];
4336
4336
  for (var _i = 0; _i < arguments.length; _i++) sources[_i] = arguments[_i];
4337
4337
  sources = argsOrArgArray_1$4.argsOrArgArray(sources);
4338
4338
  return sources.length === 1 ? innerFrom_1$28.innerFrom(sources[0]) : new Observable_1$6.Observable(raceInit(sources));
4339
4339
  }
4340
- exports.race = race$3;
4340
+ exports.race = race$4;
4341
4341
  function raceInit(sources) {
4342
4342
  return function(subscriber) {
4343
4343
  var subscriptions = [];
@@ -5474,7 +5474,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5474
5474
  var identity_1$10 = require_identity();
5475
5475
  var lift_1$42 = require_lift();
5476
5476
  var OperatorSubscriber_1$31 = require_OperatorSubscriber();
5477
- function distinctUntilChanged$5(comparator, keySelector) {
5477
+ function distinctUntilChanged$6(comparator, keySelector) {
5478
5478
  if (keySelector === void 0) keySelector = identity_1$10.identity;
5479
5479
  comparator = comparator !== null && comparator !== void 0 ? comparator : defaultCompare;
5480
5480
  return lift_1$42.operate(function(source, subscriber) {
@@ -5490,7 +5490,7 @@ var require_distinctUntilChanged = /* @__PURE__ */ __commonJSMin(((exports) => {
5490
5490
  }));
5491
5491
  });
5492
5492
  }
5493
- exports.distinctUntilChanged = distinctUntilChanged$5;
5493
+ exports.distinctUntilChanged = distinctUntilChanged$6;
5494
5494
  function defaultCompare(a, b) {
5495
5495
  return a === b;
5496
5496
  }
@@ -6919,7 +6919,7 @@ var require_startWith = /* @__PURE__ */ __commonJSMin(((exports) => {
6919
6919
  var concat_1$2 = require_concat$1();
6920
6920
  var args_1$2 = require_args();
6921
6921
  var lift_1$14 = require_lift();
6922
- function startWith() {
6922
+ function startWith$1() {
6923
6923
  var values = [];
6924
6924
  for (var _i = 0; _i < arguments.length; _i++) values[_i] = arguments[_i];
6925
6925
  var scheduler = args_1$2.popScheduler(values);
@@ -6927,7 +6927,7 @@ var require_startWith = /* @__PURE__ */ __commonJSMin(((exports) => {
6927
6927
  (scheduler ? concat_1$2.concat(values, source, scheduler) : concat_1$2.concat(values, source)).subscribe(subscriber);
6928
6928
  });
6929
6929
  }
6930
- exports.startWith = startWith;
6930
+ exports.startWith = startWith$1;
6931
6931
  }));
6932
6932
 
6933
6933
  //#endregion
@@ -6938,7 +6938,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
6938
6938
  var innerFrom_1$6 = require_innerFrom();
6939
6939
  var lift_1$13 = require_lift();
6940
6940
  var OperatorSubscriber_1$11 = require_OperatorSubscriber();
6941
- function switchMap$3(project, resultSelector) {
6941
+ function switchMap$4(project, resultSelector) {
6942
6942
  return lift_1$13.operate(function(source, subscriber) {
6943
6943
  var innerSubscriber = null;
6944
6944
  var index = 0;
@@ -6962,7 +6962,7 @@ var require_switchMap = /* @__PURE__ */ __commonJSMin(((exports) => {
6962
6962
  }));
6963
6963
  });
6964
6964
  }
6965
- exports.switchMap = switchMap$3;
6965
+ exports.switchMap = switchMap$4;
6966
6966
  }));
6967
6967
 
6968
6968
  //#endregion
@@ -9108,13 +9108,14 @@ var DependencyError = class extends Error {
9108
9108
  }
9109
9109
  };
9110
9110
  var CallCreateError = class extends Error {
9111
- constructor(message, error = null, options) {
9111
+ constructor(message, error = null, direction = "outbound", options) {
9112
9112
  super(message, {
9113
9113
  ...options,
9114
9114
  cause: options?.cause ?? (error instanceof Error ? error : void 0)
9115
9115
  });
9116
9116
  this.message = message;
9117
9117
  this.error = error;
9118
+ this.direction = direction;
9118
9119
  this.name = "CallCreateError";
9119
9120
  }
9120
9121
  };
@@ -9126,6 +9127,15 @@ var JSONRPCError = class extends Error {
9126
9127
  this.name = "JSONRPCError";
9127
9128
  }
9128
9129
  };
9130
+ var RPCError = class extends Error {
9131
+ constructor(code, requestId, message, data, options) {
9132
+ super(message, options);
9133
+ this.code = code;
9134
+ this.requestId = requestId;
9135
+ this.data = data;
9136
+ this.name = "RPCError";
9137
+ }
9138
+ };
9129
9139
  var InvalidParams = class extends Error {
9130
9140
  constructor(message, options) {
9131
9141
  super(message, options);
@@ -9664,7 +9674,7 @@ var PreferencesContainer = class PreferencesContainer {
9664
9674
  skipDeviceMonitoring: false,
9665
9675
  savePreferences: false
9666
9676
  };
9667
- this.receiveVideo = true;
9677
+ this.receiveVideo = false;
9668
9678
  this.receiveAudio = true;
9669
9679
  this.preferredAudioInput = null;
9670
9680
  this.preferredAudioOutput = null;
@@ -9977,7 +9987,7 @@ var NavigatorDeviceController = class extends Destroyable {
9977
9987
  };
9978
9988
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
9979
9989
  this._selectedDevicesState$ = this.createBehaviorSubject(initialSelectedDevicesState);
9980
- this._errors$ = this.createSubject();
9990
+ this._errors$ = this.createReplaySubject(1);
9981
9991
  this.init();
9982
9992
  }
9983
9993
  get selectedAudioInputDeviceConstraints() {
@@ -10504,7 +10514,7 @@ const RPCExecute = ({ method, params }) => {
10504
10514
 
10505
10515
  //#endregion
10506
10516
  //#region src/core/RPCMessages/VertoMessages.ts
10507
- const tmpMap = {
10517
+ const SDK_TO_VERTO_FIELD_MAP = {
10508
10518
  id: "callID",
10509
10519
  destinationNumber: "destination_number",
10510
10520
  remoteCallerName: "remote_caller_id_name",
@@ -10513,19 +10523,31 @@ const tmpMap = {
10513
10523
  callerNumber: "caller_id_number",
10514
10524
  fromCallAddressId: "from_fabric_address_id"
10515
10525
  };
10526
+ const EXCLUDED_DIALOG_PARAMS = new Set([
10527
+ "remoteSdp",
10528
+ "localStream",
10529
+ "remoteStream"
10530
+ ]);
10516
10531
  /**
10517
- * Translate SDK fields into verto variables
10532
+ * Translate SDK fields into verto variables.
10533
+ * Returns a new object — the input is never mutated.
10518
10534
  */
10535
+ /** @internal Exported for testing only. */
10519
10536
  const filterVertoParams = (params) => {
10520
- if (Object.prototype.hasOwnProperty.call(params, "dialogParams")) {
10521
- const { remoteSdp, localStream, remoteStream, ...dialogParams } = params.dialogParams;
10522
- for (const key in tmpMap) if (key && Object.prototype.hasOwnProperty.call(dialogParams, key)) {
10523
- dialogParams[tmpMap[key]] = dialogParams[key];
10524
- delete dialogParams[key];
10525
- }
10526
- params.dialogParams = dialogParams;
10527
- }
10528
- return params;
10537
+ if (!Object.prototype.hasOwnProperty.call(params, "dialogParams")) return params;
10538
+ const sourceDialogParams = params.dialogParams;
10539
+ const filteredDialogParams = Object.entries(sourceDialogParams).reduce((acc, [key, value]) => {
10540
+ if (EXCLUDED_DIALOG_PARAMS.has(key)) return acc;
10541
+ const mappedKey = SDK_TO_VERTO_FIELD_MAP[key] ?? key;
10542
+ return {
10543
+ ...acc,
10544
+ [mappedKey]: value
10545
+ };
10546
+ }, {});
10547
+ return {
10548
+ ...params,
10549
+ dialogParams: filteredDialogParams
10550
+ };
10529
10551
  };
10530
10552
  const buildVertoRPCMessage = (method) => {
10531
10553
  return (params = {}) => {
@@ -10707,12 +10729,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
10707
10729
  exports.race = void 0;
10708
10730
  var argsOrArgArray_1 = require_argsOrArgArray();
10709
10731
  var raceWith_1$1 = require_raceWith();
10710
- function race$2() {
10732
+ function race$3() {
10711
10733
  var args = [];
10712
10734
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
10713
10735
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
10714
10736
  }
10715
- exports.race = race$2;
10737
+ exports.race = race$3;
10716
10738
  }));
10717
10739
 
10718
10740
  //#endregion
@@ -12046,7 +12068,13 @@ var Participant = class extends Destroyable {
12046
12068
  }
12047
12069
  /** Removes this participant from the call. */
12048
12070
  async remove() {
12049
- await this.executeMethod(this.id, "call.member.remove", {});
12071
+ const state = this._state$.value;
12072
+ const target = {
12073
+ member_id: this.id,
12074
+ call_id: state.call_id ?? "",
12075
+ node_id: state.node_id ?? ""
12076
+ };
12077
+ await this.executeMethod(target, "call.member.remove", {});
12050
12078
  }
12051
12079
  /** Ends the call for this participant. */
12052
12080
  async end() {
@@ -12405,7 +12433,6 @@ var CallEventsManager = class extends Destroyable {
12405
12433
  this.options = options;
12406
12434
  this.callIds = /* @__PURE__ */ new Set();
12407
12435
  this.roomSessionIds = /* @__PURE__ */ new Set();
12408
- this._status$ = this.createBehaviorSubject("trying");
12409
12436
  this._participants$ = this.createBehaviorSubject({});
12410
12437
  this._self$ = this.createBehaviorSubject(null);
12411
12438
  this._sessionState$ = this.createBehaviorSubject(initialSessionState);
@@ -12414,15 +12441,12 @@ var CallEventsManager = class extends Destroyable {
12414
12441
  get participants$() {
12415
12442
  return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$14.map)((participantsRecord) => Object.values(participantsRecord))));
12416
12443
  }
12444
+ get participants() {
12445
+ return Object.values(this._participants$.value);
12446
+ }
12417
12447
  get self$() {
12418
12448
  return this.cachedObservable("self$", () => this._self$.asObservable().pipe(filterNull()));
12419
12449
  }
12420
- get status$() {
12421
- return this._status$.asObservable();
12422
- }
12423
- get status() {
12424
- return this._status$.value;
12425
- }
12426
12450
  isRoomSessionIdValid(roomSessionId) {
12427
12451
  return this.roomSessionIds.has(roomSessionId);
12428
12452
  }
@@ -12507,7 +12531,6 @@ var CallEventsManager = class extends Destroyable {
12507
12531
  callId: callJoinedEvent.call_id,
12508
12532
  roomSessionId: callJoinedEvent.room_session_id
12509
12533
  });
12510
- this._status$.next("connected");
12511
12534
  const sessionState = callJoinedEvent.room_session;
12512
12535
  const { capabilities } = callJoinedEvent;
12513
12536
  this.selfId = this.selfId ?? callJoinedEvent.member_id;
@@ -12634,12 +12657,53 @@ var CallEventsManager = class extends Destroyable {
12634
12657
 
12635
12658
  //#endregion
12636
12659
  //#region src/helpers/SDPHelper.ts
12660
+ /** Valid SDP direction attribute values. */
12661
+ const SDP_DIRECTIONS = new Set([
12662
+ "sendrecv",
12663
+ "sendonly",
12664
+ "recvonly",
12665
+ "inactive"
12666
+ ]);
12637
12667
  /**
12638
- * SDPHelper - Utility functions for SDP (Session Description Protocol) parsing and validation.
12668
+ * Extracts the media directions (audio/video) from an SDP string.
12669
+ *
12670
+ * Parses each media section (`m=audio` / `m=video`) and reads the `a=` direction
12671
+ * attribute (`sendrecv`, `sendonly`, `recvonly`, `inactive`).
12672
+ * If no explicit direction attribute is found for a media section, defaults to `sendrecv`
12673
+ * per RFC 4566.
12639
12674
  *
12640
- * This module provides helper functions to analyze and validate SDP content,
12641
- * particularly for ICE candidate validation in WebRTC connections.
12675
+ * @param sdp - The SDP string to parse
12676
+ * @returns The extracted audio and video directions
12677
+ *
12678
+ * @example
12679
+ * ```typescript
12680
+ * 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`;
12681
+ * extractMediaDirectionsFromSDP(sdp);
12682
+ * // { audio: 'sendrecv', video: 'recvonly' }
12683
+ * ```
12642
12684
  */
12685
+ function extractMediaDirectionsFromSDP(sdp) {
12686
+ const result = {
12687
+ audio: "inactive",
12688
+ video: "inactive"
12689
+ };
12690
+ if (!sdp) return result;
12691
+ const lines = sdp.split(/\r?\n/);
12692
+ let currentMediaKind = null;
12693
+ let currentDirection = null;
12694
+ for (const line of lines) if (line.startsWith("m=")) {
12695
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12696
+ if (line.startsWith("m=audio")) currentMediaKind = "audio";
12697
+ else if (line.startsWith("m=video")) currentMediaKind = "video";
12698
+ else currentMediaKind = null;
12699
+ currentDirection = null;
12700
+ } else if (currentMediaKind && line.startsWith("a=")) {
12701
+ const attr = line.substring(2).trim();
12702
+ if (SDP_DIRECTIONS.has(attr)) currentDirection = attr;
12703
+ }
12704
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12705
+ return result;
12706
+ }
12643
12707
  /**
12644
12708
  * Validates that an SDP string has at least one non-host ICE candidate
12645
12709
  * for each media section (m= line).
@@ -12942,6 +13006,15 @@ var LocalStreamController = class extends Destroyable {
12942
13006
  track.addEventListener("ended", this.mediaTrackEndedHandler);
12943
13007
  }
12944
13008
  /**
13009
+ * Update the controller options (e.g., when media overrides are applied).
13010
+ */
13011
+ updateOptions(options) {
13012
+ this.options = {
13013
+ ...this.options,
13014
+ ...options
13015
+ };
13016
+ }
13017
+ /**
12945
13018
  * Stop all local tracks and clean up.
12946
13019
  */
12947
13020
  stopAllTracks() {
@@ -13280,11 +13353,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13280
13353
  this._connectionState$ = this.createReplaySubject(1);
13281
13354
  this._signalingState$ = this.createReplaySubject(1);
13282
13355
  this._iceGatheringState$ = this.createReplaySubject(1);
13283
- this._errors$ = this.createSubject();
13356
+ this._errors$ = this.createReplaySubject(1);
13284
13357
  this._iceCandidates$ = this.createReplaySubject(1);
13285
13358
  this._initialized$ = this.createReplaySubject(1);
13286
13359
  this._remoteDescription$ = this.createReplaySubject(1);
13287
13360
  this._remoteStream$ = this.createBehaviorSubject(null);
13361
+ this._remoteOfferMediaDirections = null;
13288
13362
  this.deviceController = deviceController ?? {};
13289
13363
  this.id = options.callId ?? v4_default();
13290
13364
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -13292,10 +13366,19 @@ var RTCPeerConnectionController = class extends Destroyable {
13292
13366
  type: "offer",
13293
13367
  sdp: remoteSessionDescription
13294
13368
  } : void 0;
13369
+ this._remoteOfferMediaDirections = remoteSessionDescription ? extractMediaDirectionsFromSDP(remoteSessionDescription) : null;
13370
+ const offerDefaults = this._remoteOfferMediaDirections ? {
13371
+ audio: this._remoteOfferMediaDirections.audio.includes("recv"),
13372
+ video: this._remoteOfferMediaDirections.video.includes("recv"),
13373
+ receiveAudio: this._remoteOfferMediaDirections.audio.includes("send"),
13374
+ receiveVideo: this._remoteOfferMediaDirections.video.includes("send")
13375
+ } : {};
13295
13376
  this.options = {
13296
- receiveAudio: options.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13297
- receiveVideo: options.receiveVideo ?? PreferencesContainer.instance.receiveVideo,
13298
- ...options
13377
+ ...options,
13378
+ audio: options.audio ?? offerDefaults.audio,
13379
+ video: options.video ?? offerDefaults.video,
13380
+ receiveAudio: options.receiveAudio ?? offerDefaults.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13381
+ receiveVideo: options.receiveVideo ?? offerDefaults.receiveVideo ?? PreferencesContainer.instance.receiveVideo
13299
13382
  };
13300
13383
  this.localStreamController = new LocalStreamController({
13301
13384
  propose: this.propose,
@@ -13440,7 +13523,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13440
13523
  };
13441
13524
  }
13442
13525
  get inputVideoDeviceConstraints() {
13443
- if (this.options.video === false && !this.options.inputVideoDeviceConstraints) return false;
13526
+ if (!this.options.video && !this.options.inputVideoDeviceConstraints) return false;
13444
13527
  return {
13445
13528
  ...this.options.inputVideoDeviceConstraints,
13446
13529
  ...this.deviceController.selectedVideoInputDeviceConstraints
@@ -13462,12 +13545,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13462
13545
  default: return {
13463
13546
  ...options,
13464
13547
  offerToReceiveAudio: true,
13465
- offerToReceiveVideo: Boolean(this.inputVideoDeviceConstraints)
13548
+ offerToReceiveVideo: this.options.receiveVideo ?? Boolean(this.inputVideoDeviceConstraints)
13466
13549
  };
13467
13550
  }
13468
13551
  }
13469
13552
  get answerOptions() {
13470
- return {};
13553
+ return { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
13471
13554
  }
13472
13555
  /**
13473
13556
  * Initialize the RTCPeerConnection and setup event listeners.
@@ -13502,11 +13585,15 @@ var RTCPeerConnectionController = class extends Destroyable {
13502
13585
  });
13503
13586
  await this.updateSelectedInputDevice(kind, deviceInfo);
13504
13587
  });
13505
- await this.setupTrackHandling();
13506
- this._initialized$.next(true);
13507
13588
  if (this.type === "answer" && this.sdpInit) {
13589
+ await this.setupRemoteTracks();
13590
+ this._initialized$.next(true);
13508
13591
  this.setupEventListeners();
13509
- await this.handleOfferReceived();
13592
+ this._isNegotiating$.next(true);
13593
+ await this._setRemoteDescription(this.sdpInit);
13594
+ } else {
13595
+ await this.setupTrackHandling();
13596
+ this._initialized$.next(true);
13510
13597
  }
13511
13598
  } catch (error) {
13512
13599
  logger$11.error("[RTCPeerConnectionController] Initialization error:", error);
@@ -13604,6 +13691,35 @@ var RTCPeerConnectionController = class extends Destroyable {
13604
13691
  default:
13605
13692
  }
13606
13693
  }
13694
+ /**
13695
+ * Accept an inbound call by creating the SDP answer.
13696
+ * Optionally override media options before the answer is generated.
13697
+ * Must be called after initialization for inbound (answer-type) connections.
13698
+ */
13699
+ async acceptInbound(mediaOverrides) {
13700
+ if (mediaOverrides) {
13701
+ const { audio, video, receiveAudio, receiveVideo } = mediaOverrides;
13702
+ this.options = {
13703
+ ...this.options,
13704
+ ...audio !== void 0 ? { audio } : {},
13705
+ ...video !== void 0 ? { video } : {},
13706
+ ...receiveAudio !== void 0 ? { receiveAudio } : {},
13707
+ ...receiveVideo !== void 0 ? { receiveVideo } : {}
13708
+ };
13709
+ this.transceiverController?.updateOptions({
13710
+ receiveAudio: this.receiveAudio,
13711
+ receiveVideo: this.receiveVideo
13712
+ });
13713
+ this.localStreamController.updateOptions({
13714
+ inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,
13715
+ inputVideoDeviceConstraints: this.inputVideoDeviceConstraints
13716
+ });
13717
+ }
13718
+ await this.setupLocalTracks();
13719
+ const { answerOptions } = this;
13720
+ logger$11.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
13721
+ await this.createAnswer(answerOptions);
13722
+ }
13607
13723
  async handleOfferReceived() {
13608
13724
  if (!this.sdpInit) throw new DependencyError("SDP initialization parameters are not set");
13609
13725
  this._isNegotiating$.next(true);
@@ -13678,37 +13794,29 @@ var RTCPeerConnectionController = class extends Destroyable {
13678
13794
  }
13679
13795
  async setupLocalTracks() {
13680
13796
  logger$11.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
13681
- let { localStream } = this;
13682
- if (!localStream) try {
13683
- localStream = await this.localStreamController.buildLocalStream();
13684
- } catch (error) {
13685
- logger$11.error("[RTCPeerConnectionController] Error building local stream:", error);
13686
- this._errors$.next(error);
13687
- }
13688
- if (localStream) {
13689
- if (this.transceiverController?.useAddStream ?? false) {
13690
- logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13691
- this.peerConnection?.addStream(localStream);
13692
- if (!this.isNegotiating) {
13693
- logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13694
- this.negotiationNeeded$.next();
13695
- }
13696
- return;
13797
+ const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
13798
+ if (this.transceiverController?.useAddStream ?? false) {
13799
+ logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13800
+ this.peerConnection?.addStream(localStream);
13801
+ if (!this.isNegotiating) {
13802
+ logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13803
+ this.negotiationNeeded$.next();
13697
13804
  }
13698
- for (const kind of ["audio", "video"]) {
13699
- const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13700
- index,
13701
- track
13702
- }));
13703
- for (const { index, track } of tracks) {
13704
- this.localStreamController.addTrackEndedListener(track);
13705
- if (this.transceiverController?.useAddTransceivers ?? false) {
13706
- const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13707
- await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13708
- } else {
13709
- logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13710
- this.peerConnection?.addTrack(track, localStream);
13711
- }
13805
+ return;
13806
+ }
13807
+ for (const kind of ["audio", "video"]) {
13808
+ const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13809
+ index,
13810
+ track
13811
+ }));
13812
+ for (const { index, track } of tracks) {
13813
+ this.localStreamController.addTrackEndedListener(track);
13814
+ if (this.transceiverController?.useAddTransceivers ?? false) {
13815
+ const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13816
+ await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13817
+ } else {
13818
+ logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13819
+ this.peerConnection?.addTrack(track, localStream);
13712
13820
  }
13713
13821
  }
13714
13822
  }
@@ -13725,7 +13833,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13725
13833
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
13726
13834
  this.peerConnection.ontrack = (event) => {
13727
13835
  logger$11.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
13728
- this._remoteStream$.next(event.streams[0]);
13836
+ if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
13837
+ else {
13838
+ const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
13839
+ const newStream = new MediaStream([...existingTracks, event.track]);
13840
+ this._remoteStream$.next(newStream);
13841
+ }
13729
13842
  };
13730
13843
  await this.transceiverController?.setupRemoteTransceivers(this.type);
13731
13844
  }
@@ -13824,7 +13937,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13824
13937
  });
13825
13938
  }
13826
13939
  get mediaDirections() {
13827
- return this.transceiverController?.getMediaDirections() ?? {
13940
+ return this.transceiverController?.getMediaDirections() ?? this._remoteOfferMediaDirections ?? {
13828
13941
  audio: "inactive",
13829
13942
  video: "inactive"
13830
13943
  };
@@ -13984,6 +14097,7 @@ var WebRTCVertoManager = class extends VertoManager {
13984
14097
  });
13985
14098
  this.subscribeTo(this.vertoAnswer$, (event) => {
13986
14099
  logger$10.debug("[WebRTCManager] Received Verto answer event:", event);
14100
+ this._signalingStatus$.next("connecting");
13987
14101
  const { sdp, callID } = event;
13988
14102
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
13989
14103
  status: "received",
@@ -14055,13 +14169,13 @@ var WebRTCVertoManager = class extends VertoManager {
14055
14169
  if (response.error) {
14056
14170
  const error = new JSONRPCError(response.error.code, response.error.message, response.error.data);
14057
14171
  this.onError?.(error);
14058
- throw error;
14172
+ return response;
14059
14173
  }
14060
14174
  const innerResult = getValueFrom(response, "result.result");
14061
14175
  if (innerResult?.error) {
14062
14176
  const error = new JSONRPCError(innerResult.error.code, innerResult.error.message, innerResult.error.data);
14063
14177
  this.onError?.(error);
14064
- throw error;
14178
+ return response;
14065
14179
  }
14066
14180
  return response;
14067
14181
  }
@@ -14081,7 +14195,7 @@ var WebRTCVertoManager = class extends VertoManager {
14081
14195
  }
14082
14196
  } catch (error) {
14083
14197
  logger$10.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
14084
- throw error;
14198
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14085
14199
  }
14086
14200
  }
14087
14201
  async processModifyResponse(response, rtcPeerConnController) {
@@ -14095,12 +14209,14 @@ var WebRTCVertoManager = class extends VertoManager {
14095
14209
  });
14096
14210
  } catch (error) {
14097
14211
  logger$10.warn("[WebRTCManager] Error processing modify response:", error);
14098
- this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14212
+ const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
14213
+ this.onError?.(modifyError);
14099
14214
  }
14100
14215
  }
14101
14216
  }
14102
14217
  processInviteResponse(response, rtcPeerConnController) {
14103
14218
  if (!response.error && getValueFrom(response, "result.result.result.message") === "CALL CREATED") {
14219
+ this._signalingStatus$.next("trying");
14104
14220
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
14105
14221
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
14106
14222
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
@@ -14155,6 +14271,36 @@ var WebRTCVertoManager = class extends VertoManager {
14155
14271
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
14156
14272
  this.onError?.(error);
14157
14273
  });
14274
+ if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
14275
+ }
14276
+ async handleInboundAnswer(rtcPeerConnController) {
14277
+ logger$10.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
14278
+ 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);
14279
+ if (vertoByeOrAccepted === null) {
14280
+ logger$10.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
14281
+ return;
14282
+ }
14283
+ if (isVertoByeMessage(vertoByeOrAccepted)) {
14284
+ logger$10.info("[WebRTCManager] Inbound call ended by remote before answer.");
14285
+ this.callSession?.destroy();
14286
+ } else if (!vertoByeOrAccepted) {
14287
+ logger$10.info("[WebRTCManager] Inbound call rejected by user.");
14288
+ try {
14289
+ await this.bye("USER_BUSY");
14290
+ } finally {
14291
+ this._signalingStatus$.next("disconnected");
14292
+ this.callSession?.destroy();
14293
+ }
14294
+ } else {
14295
+ logger$10.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
14296
+ const answerOptions = this.webRtcCallSession.answerMediaOptions;
14297
+ try {
14298
+ await rtcPeerConnController.acceptInbound(answerOptions);
14299
+ } catch (error) {
14300
+ logger$10.error("[WebRTCManager] Error creating inbound answer:", error);
14301
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14302
+ }
14303
+ }
14158
14304
  }
14159
14305
  setupVertoAttachHandler() {
14160
14306
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
@@ -14172,7 +14318,7 @@ var WebRTCVertoManager = class extends VertoManager {
14172
14318
  });
14173
14319
  }
14174
14320
  initObservables(rtcPeerConnController) {
14175
- 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));
14321
+ 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$));
14176
14322
  this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14177
14323
  this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14178
14324
  }
@@ -14188,7 +14334,6 @@ var WebRTCVertoManager = class extends VertoManager {
14188
14334
  });
14189
14335
  this.sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnController);
14190
14336
  } else if (initial) {
14191
- this._signalingStatus$.next("trying");
14192
14337
  const vertoMessageRequest = VertoInvite({
14193
14338
  dialogParams,
14194
14339
  sdp
@@ -14495,8 +14640,8 @@ var WebRTCCall = class extends Destroyable {
14495
14640
  this.clientSession = clientSession;
14496
14641
  this.options = options;
14497
14642
  this.address = address;
14498
- this.participantsMap = /* @__PURE__ */ new Map();
14499
- this._errors$ = this.createSubject();
14643
+ this._errors$ = this.createReplaySubject(1);
14644
+ this._lastMergedStatus = "new";
14500
14645
  this._answered$ = this.createReplaySubject();
14501
14646
  this._holdState = false;
14502
14647
  this._userVariables$ = this.createBehaviorSubject({ ...PreferencesContainer.instance.userVariables });
@@ -14517,8 +14662,10 @@ var WebRTCCall = class extends Destroyable {
14517
14662
  const managers = initialization.initializeManagers(this);
14518
14663
  this.vertoManager = managers.vertoManager;
14519
14664
  this.callEventsManager = managers.callEventsManager;
14520
- if (options.initOffer) this._status$ = this.createBehaviorSubject("ringing");
14521
- else this._status$ = this.createBehaviorSubject("new");
14665
+ if (options.initOffer) {
14666
+ this._status$ = this.createBehaviorSubject("ringing");
14667
+ this._lastMergedStatus = "ringing";
14668
+ } else this._status$ = this.createBehaviorSubject("new");
14522
14669
  const { deviceController } = initialization;
14523
14670
  this.participantFactory = new ParticipantFactory(this.executeMethod.bind(this), this.vertoManager, deviceController);
14524
14671
  }
@@ -14526,9 +14673,17 @@ var WebRTCCall = class extends Destroyable {
14526
14673
  get errors$() {
14527
14674
  return this._errors$.asObservable();
14528
14675
  }
14529
- /** @internal Push an error to the call's error stream. */
14530
- emitError(error) {
14531
- this._errors$.next(error);
14676
+ /**
14677
+ * @internal Push an error to the call's error stream.
14678
+ * Fatal errors automatically transition the call to `'failed'` and destroy it.
14679
+ */
14680
+ emitError(callError) {
14681
+ if (this._status$.value === "destroyed" || this._status$.value === "failed") return;
14682
+ this._errors$.next(callError);
14683
+ if (callError.fatal) {
14684
+ this._status$.next("failed");
14685
+ this.destroy();
14686
+ }
14532
14687
  }
14533
14688
  /** Whether this call is `'inbound'` or `'outbound'`. */
14534
14689
  get direction() {
@@ -14568,7 +14723,7 @@ var WebRTCCall = class extends Destroyable {
14568
14723
  }
14569
14724
  /** Current snapshot of all participants in the call. */
14570
14725
  get participants() {
14571
- return Array.from(this.participantsMap.values());
14726
+ return this.callEventsManager.participants;
14572
14727
  }
14573
14728
  /** The local participant, or `null` if not yet joined. */
14574
14729
  get self() {
@@ -14577,7 +14732,6 @@ var WebRTCCall = class extends Destroyable {
14577
14732
  async toggleLock() {
14578
14733
  const method = this.locked ? "call.unlock" : "call.lock";
14579
14734
  await this.executeMethod(this.selfId ?? "", method, {});
14580
- throw new UnimplementedError();
14581
14735
  }
14582
14736
  async toggleHold() {
14583
14737
  if (this._holdState) await this.vertoManager.unhold();
@@ -14619,25 +14773,31 @@ var WebRTCCall = class extends Destroyable {
14619
14773
  }
14620
14774
  }
14621
14775
  buildMethodParams(target, args) {
14622
- const reference = {
14623
- node_id: this.nodeId,
14624
- call_id: this.id
14776
+ const self = {
14777
+ node_id: this.nodeId ?? "",
14778
+ call_id: this.id,
14779
+ member_id: this.vertoManager.selfId ?? ""
14780
+ };
14781
+ if (typeof target === "object") return {
14782
+ ...args,
14783
+ self,
14784
+ targets: [target]
14625
14785
  };
14626
14786
  return {
14627
14787
  ...args,
14628
- self: {
14629
- ...reference,
14630
- member_id: this.vertoManager.selfId
14631
- },
14788
+ self,
14632
14789
  target: {
14633
- ...reference,
14790
+ node_id: this.nodeId ?? "",
14791
+ call_id: this.id,
14634
14792
  member_id: target
14635
14793
  }
14636
14794
  };
14637
14795
  }
14638
14796
  /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
14639
14797
  get status$() {
14640
- return this.cachedObservable("status$", () => (0, import_cjs$9.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$));
14798
+ return this.cachedObservable("status$", () => (0, import_cjs$9.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$).pipe((0, import_cjs$9.distinctUntilChanged)(), (0, import_cjs$9.tap)((status) => {
14799
+ this._lastMergedStatus = status;
14800
+ })));
14641
14801
  }
14642
14802
  /** Observable of the participants list, emits on join/leave/update. */
14643
14803
  get participants$() {
@@ -14677,7 +14837,7 @@ var WebRTCCall = class extends Destroyable {
14677
14837
  }
14678
14838
  /** Current call status. */
14679
14839
  get status() {
14680
- return this._status$.value;
14840
+ return this._lastMergedStatus;
14681
14841
  }
14682
14842
  /** Whether the call is currently being recorded. */
14683
14843
  get recording() {
@@ -14854,10 +15014,15 @@ var WebRTCCall = class extends Destroyable {
14854
15014
  async sendDigits(dtmf) {
14855
15015
  return this.vertoManager.sendDigits(dtmf);
14856
15016
  }
14857
- /** Accepts an inbound call. */
14858
- answer() {
15017
+ /** Accepts an inbound call, optionally overriding media options for the answer. */
15018
+ answer(options) {
15019
+ this._answerMediaOptions = options;
14859
15020
  this._answered$.next(true);
14860
15021
  }
15022
+ /** Media options provided when answering. Used internally by the VertoManager. */
15023
+ get answerMediaOptions() {
15024
+ return this._answerMediaOptions;
15025
+ }
14861
15026
  /** Rejects an inbound call. */
14862
15027
  reject() {
14863
15028
  this._answered$.next(false);
@@ -14885,10 +15050,10 @@ var WebRTCCall = class extends Destroyable {
14885
15050
  }
14886
15051
  /** Destroys the call, releasing all resources and subscriptions. */
14887
15052
  destroy() {
15053
+ if (this._status$.value === "destroyed") return;
14888
15054
  this._status$.next("destroyed");
14889
15055
  this.vertoManager.destroy();
14890
15056
  this.callEventsManager.destroy();
14891
- this.participantsMap.clear();
14892
15057
  super.destroy();
14893
15058
  }
14894
15059
  };
@@ -14896,6 +15061,23 @@ var WebRTCCall = class extends Destroyable {
14896
15061
  //#endregion
14897
15062
  //#region src/managers/CallFactory.ts
14898
15063
  /**
15064
+ * Infers the semantic error category from a raw Error thrown by VertoManager
15065
+ * or an RTCPeerConnection layer.
15066
+ */
15067
+ function inferCallErrorKind(error) {
15068
+ if (error instanceof RPCTimeoutError) return "timeout";
15069
+ if (error instanceof JSONRPCError) return "signaling";
15070
+ if (error instanceof MediaTrackError) return "media";
15071
+ if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError) return "network";
15072
+ return "internal";
15073
+ }
15074
+ /** Determines whether an error should be fatal (destroy the call). */
15075
+ function isFatalError(error) {
15076
+ if (error instanceof VertoPongError) return false;
15077
+ if (error instanceof MediaTrackError) return false;
15078
+ return true;
15079
+ }
15080
+ /**
14899
15081
  * Factory for creating WebRTCCall instances with proper manager wiring.
14900
15082
  * Eliminates circular dependencies by centralizing Call and Manager creation.
14901
15083
  */
@@ -14916,7 +15098,13 @@ var CallFactory = class {
14916
15098
  vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, this.webRTCApiProvider, {
14917
15099
  nodeId: options.nodeId,
14918
15100
  onError: (error) => {
14919
- callInstance.emitError(error);
15101
+ const callError = {
15102
+ kind: inferCallErrorKind(error),
15103
+ fatal: isFatalError(error),
15104
+ error,
15105
+ callId: callInstance.id
15106
+ };
15107
+ callInstance.emitError(callError);
14920
15108
  }
14921
15109
  }),
14922
15110
  callEventsManager: new CallEventsManager(callInstance)
@@ -15296,9 +15484,15 @@ var PendingRPC = class PendingRPC {
15296
15484
  return () => signal.removeEventListener("abort", abortHandler);
15297
15485
  }) : import_cjs$6.NEVER).subscribe({
15298
15486
  next: (response) => {
15299
- logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15300
15487
  isSettled = true;
15301
- resolve(response);
15488
+ if (response.error) {
15489
+ const rpcError = new RPCError(response.error.code, request.id, response.error.message, response.error.data);
15490
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
15491
+ reject(rpcError);
15492
+ } else {
15493
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15494
+ resolve(response);
15495
+ }
15302
15496
  subscription.unsubscribe();
15303
15497
  },
15304
15498
  error: (error) => {
@@ -15355,7 +15549,7 @@ var ClientSessionManager = class extends Destroyable {
15355
15549
  revision: 0
15356
15550
  };
15357
15551
  this._authorization$ = this.createBehaviorSubject(void 0);
15358
- this._errors$ = this.createSubject();
15552
+ this._errors$ = this.createReplaySubject(1);
15359
15553
  this._authenticated$ = this.createBehaviorSubject(false);
15360
15554
  this._subscriberInfo$ = this.createBehaviorSubject(null);
15361
15555
  this._calls$ = this.createBehaviorSubject({});
@@ -15625,12 +15819,13 @@ var ClientSessionManager = class extends Destroyable {
15625
15819
  }
15626
15820
  async createOutboundCall(destination, options = {}) {
15627
15821
  const destinationURI = destination instanceof Address ? destination.defaultChannel : destination;
15822
+ let callSession;
15628
15823
  try {
15629
- const callSession = await this.createCall({
15824
+ callSession = await this.createCall({
15630
15825
  to: destinationURI,
15631
15826
  ...options
15632
15827
  });
15633
- 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)));
15828
+ 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)))));
15634
15829
  this._calls$.next({
15635
15830
  [`${callSession.id}`]: callSession,
15636
15831
  ...this._calls$.value
@@ -15638,7 +15833,8 @@ var ClientSessionManager = class extends Destroyable {
15638
15833
  return callSession;
15639
15834
  } catch (error) {
15640
15835
  logger$6.error("[Session] Error creating outbound call:", error);
15641
- const callError = new CallCreateError("Call create timeout", error);
15836
+ callSession?.destroy();
15837
+ const callError = new CallCreateError(error instanceof import_cjs$5.TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
15642
15838
  this._errors$.next(callError);
15643
15839
  throw callError;
15644
15840
  }
@@ -15664,7 +15860,7 @@ var ClientSessionManager = class extends Destroyable {
15664
15860
  return callSession;
15665
15861
  } catch (error) {
15666
15862
  logger$6.error("[Session] Error creating call session:", error);
15667
- throw new CallCreateError("Call create error", error);
15863
+ throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
15668
15864
  }
15669
15865
  }
15670
15866
  destroy() {
@@ -15913,7 +16109,7 @@ var WebSocketController = class WebSocketController extends Destroyable {
15913
16109
  this.shouldReconnect = false;
15914
16110
  this._status$ = this.createBehaviorSubject("disconnected");
15915
16111
  this._incomingMessages$ = this.createSubject();
15916
- this._errors$ = this.createSubject();
16112
+ this._errors$ = this.createReplaySubject(1);
15917
16113
  this.reconnectDelayMin = options.reconnectDelayMin ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MIN_MS;
15918
16114
  this.reconnectDelayMax = options.reconnectDelayMax ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MAX_MS;
15919
16115
  this.connectionTimeout = options.connectionTimeout ?? WebSocketController.DEFAULT_CONNECTION_TIMEOUT_MS;
@@ -16240,7 +16436,8 @@ const buildOptionsFromDestination = (destination) => {
16240
16436
  const channel = new URLSearchParams(queryString).get("channel");
16241
16437
  if (channel === "video") return {
16242
16438
  audio: true,
16243
- video: true
16439
+ video: true,
16440
+ receiveVideo: true
16244
16441
  };
16245
16442
  else if (channel === "audio") return {
16246
16443
  audio: true,
@@ -16270,7 +16467,7 @@ var SignalWire = class extends Destroyable {
16270
16467
  this._directory$ = this.createBehaviorSubject(void 0);
16271
16468
  this._isConnected$ = this.createBehaviorSubject(false);
16272
16469
  this._isRegistered$ = this.createBehaviorSubject(false);
16273
- this._errors$ = this.createSubject();
16470
+ this._errors$ = this.createReplaySubject(1);
16274
16471
  this._options = {};
16275
16472
  this._deps = new DependencyContainer();
16276
16473
  this._options = {
@@ -16347,7 +16544,12 @@ var SignalWire = class extends Destroyable {
16347
16544
  this._subscriber$.next(new Subscriber(this._deps.http));
16348
16545
  if (!this._options.skipConnection) await this.connect();
16349
16546
  if (!this._options.reconnectAttachedCalls && this._attachManager) await this._attachManager.flush();
16350
- if (!this._options.skipRegister) this.register();
16547
+ if (!this._options.skipRegister) try {
16548
+ await this.register();
16549
+ } catch (error) {
16550
+ logger$1.error("[SignalWire] Registration failed:", error);
16551
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16552
+ }
16351
16553
  this.handleAttachments();
16352
16554
  }
16353
16555
  async handleAttachments() {
@@ -16510,8 +16712,22 @@ var SignalWire = class extends Destroyable {
16510
16712
  }));
16511
16713
  this._isRegistered$.next(true);
16512
16714
  } catch (error) {
16513
- logger$1.error("[SignalWire] Failed to register subscriber:", error);
16715
+ logger$1.debug("[SignalWire] Failed to register subscriber, trying reauthentication...");
16716
+ if (this._deps.credential.token) this._clientSession.reauthenticate(this._deps.credential.token).then(async () => {
16717
+ logger$1.debug("[SignalWire] Reauthentication successful, retrying register()");
16718
+ await this._transport.execute(RPCExecute({
16719
+ method: "subscriber.online",
16720
+ params: {}
16721
+ }));
16722
+ this._isRegistered$.next(true);
16723
+ }).catch((reauthError) => {
16724
+ logger$1.error("[SignalWire] Reauthentication failed during register():", reauthError);
16725
+ 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 }) });
16726
+ this._errors$.next(registerError);
16727
+ throw registerError;
16728
+ });
16514
16729
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16730
+ throw error;
16515
16731
  }
16516
16732
  }
16517
16733
  /** Unregisters the subscriber, going offline for inbound calls. */