@signalwire/js 4.0.0-beta.7 → 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.
package/dist/browser.mjs CHANGED
@@ -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
  }
@@ -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$3(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$3;
6961
+ exports.switchMap = switchMap$4;
6962
6962
  }));
6963
6963
 
6964
6964
  //#endregion
@@ -9104,13 +9104,14 @@ var DependencyError = class extends Error {
9104
9104
  }
9105
9105
  };
9106
9106
  var CallCreateError = class extends Error {
9107
- constructor(message, error = null, options) {
9107
+ constructor(message, error = null, direction = "outbound", options) {
9108
9108
  super(message, {
9109
9109
  ...options,
9110
9110
  cause: options?.cause ?? (error instanceof Error ? error : void 0)
9111
9111
  });
9112
9112
  this.message = message;
9113
9113
  this.error = error;
9114
+ this.direction = direction;
9114
9115
  this.name = "CallCreateError";
9115
9116
  }
9116
9117
  };
@@ -9122,6 +9123,15 @@ var JSONRPCError = class extends Error {
9122
9123
  this.name = "JSONRPCError";
9123
9124
  }
9124
9125
  };
9126
+ var RPCError = class extends Error {
9127
+ constructor(code, requestId, message, data, options) {
9128
+ super(message, options);
9129
+ this.code = code;
9130
+ this.requestId = requestId;
9131
+ this.data = data;
9132
+ this.name = "RPCError";
9133
+ }
9134
+ };
9125
9135
  var InvalidParams = class extends Error {
9126
9136
  constructor(message, options) {
9127
9137
  super(message, options);
@@ -9660,7 +9670,7 @@ var PreferencesContainer = class PreferencesContainer {
9660
9670
  skipDeviceMonitoring: false,
9661
9671
  savePreferences: false
9662
9672
  };
9663
- this.receiveVideo = true;
9673
+ this.receiveVideo = false;
9664
9674
  this.receiveAudio = true;
9665
9675
  this.preferredAudioInput = null;
9666
9676
  this.preferredAudioOutput = null;
@@ -9973,7 +9983,7 @@ var NavigatorDeviceController = class extends Destroyable {
9973
9983
  };
9974
9984
  this._devicesState$ = this.createBehaviorSubject(initialDevicesState);
9975
9985
  this._selectedDevicesState$ = this.createBehaviorSubject(initialSelectedDevicesState);
9976
- this._errors$ = this.createSubject();
9986
+ this._errors$ = this.createReplaySubject(1);
9977
9987
  this.init();
9978
9988
  }
9979
9989
  get selectedAudioInputDeviceConstraints() {
@@ -10500,7 +10510,7 @@ const RPCExecute = ({ method, params }) => {
10500
10510
 
10501
10511
  //#endregion
10502
10512
  //#region src/core/RPCMessages/VertoMessages.ts
10503
- const tmpMap = {
10513
+ const SDK_TO_VERTO_FIELD_MAP = {
10504
10514
  id: "callID",
10505
10515
  destinationNumber: "destination_number",
10506
10516
  remoteCallerName: "remote_caller_id_name",
@@ -10509,19 +10519,31 @@ const tmpMap = {
10509
10519
  callerNumber: "caller_id_number",
10510
10520
  fromCallAddressId: "from_fabric_address_id"
10511
10521
  };
10522
+ const EXCLUDED_DIALOG_PARAMS = new Set([
10523
+ "remoteSdp",
10524
+ "localStream",
10525
+ "remoteStream"
10526
+ ]);
10512
10527
  /**
10513
- * Translate SDK fields into verto variables
10528
+ * Translate SDK fields into verto variables.
10529
+ * Returns a new object — the input is never mutated.
10514
10530
  */
10531
+ /** @internal Exported for testing only. */
10515
10532
  const filterVertoParams = (params) => {
10516
- if (Object.prototype.hasOwnProperty.call(params, "dialogParams")) {
10517
- const { remoteSdp, localStream, remoteStream, ...dialogParams } = params.dialogParams;
10518
- for (const key in tmpMap) if (key && Object.prototype.hasOwnProperty.call(dialogParams, key)) {
10519
- dialogParams[tmpMap[key]] = dialogParams[key];
10520
- delete dialogParams[key];
10521
- }
10522
- params.dialogParams = dialogParams;
10523
- }
10524
- return params;
10533
+ if (!Object.prototype.hasOwnProperty.call(params, "dialogParams")) return params;
10534
+ const sourceDialogParams = params.dialogParams;
10535
+ const filteredDialogParams = Object.entries(sourceDialogParams).reduce((acc, [key, value]) => {
10536
+ if (EXCLUDED_DIALOG_PARAMS.has(key)) return acc;
10537
+ const mappedKey = SDK_TO_VERTO_FIELD_MAP[key] ?? key;
10538
+ return {
10539
+ ...acc,
10540
+ [mappedKey]: value
10541
+ };
10542
+ }, {});
10543
+ return {
10544
+ ...params,
10545
+ dialogParams: filteredDialogParams
10546
+ };
10525
10547
  };
10526
10548
  const buildVertoRPCMessage = (method) => {
10527
10549
  return (params = {}) => {
@@ -10703,12 +10725,12 @@ var require_race = /* @__PURE__ */ __commonJSMin(((exports) => {
10703
10725
  exports.race = void 0;
10704
10726
  var argsOrArgArray_1 = require_argsOrArgArray();
10705
10727
  var raceWith_1$1 = require_raceWith();
10706
- function race$2() {
10728
+ function race$3() {
10707
10729
  var args = [];
10708
10730
  for (var _i = 0; _i < arguments.length; _i++) args[_i] = arguments[_i];
10709
10731
  return raceWith_1$1.raceWith.apply(void 0, __spreadArray([], __read(argsOrArgArray_1.argsOrArgArray(args))));
10710
10732
  }
10711
- exports.race = race$2;
10733
+ exports.race = race$3;
10712
10734
  }));
10713
10735
 
10714
10736
  //#endregion
@@ -12042,7 +12064,13 @@ var Participant = class extends Destroyable {
12042
12064
  }
12043
12065
  /** Removes this participant from the call. */
12044
12066
  async remove() {
12045
- await this.executeMethod(this.id, "call.member.remove", {});
12067
+ const state = this._state$.value;
12068
+ const target = {
12069
+ member_id: this.id,
12070
+ call_id: state.call_id ?? "",
12071
+ node_id: state.node_id ?? ""
12072
+ };
12073
+ await this.executeMethod(target, "call.member.remove", {});
12046
12074
  }
12047
12075
  /** Ends the call for this participant. */
12048
12076
  async end() {
@@ -12401,7 +12429,6 @@ var CallEventsManager = class extends Destroyable {
12401
12429
  this.options = options;
12402
12430
  this.callIds = /* @__PURE__ */ new Set();
12403
12431
  this.roomSessionIds = /* @__PURE__ */ new Set();
12404
- this._status$ = this.createBehaviorSubject("trying");
12405
12432
  this._participants$ = this.createBehaviorSubject({});
12406
12433
  this._self$ = this.createBehaviorSubject(null);
12407
12434
  this._sessionState$ = this.createBehaviorSubject(initialSessionState);
@@ -12410,15 +12437,12 @@ var CallEventsManager = class extends Destroyable {
12410
12437
  get participants$() {
12411
12438
  return this.cachedObservable("participants$", () => this._participants$.asObservable().pipe((0, import_cjs$14.map)((participantsRecord) => Object.values(participantsRecord))));
12412
12439
  }
12440
+ get participants() {
12441
+ return Object.values(this._participants$.value);
12442
+ }
12413
12443
  get self$() {
12414
12444
  return this.cachedObservable("self$", () => this._self$.asObservable().pipe(filterNull()));
12415
12445
  }
12416
- get status$() {
12417
- return this._status$.asObservable();
12418
- }
12419
- get status() {
12420
- return this._status$.value;
12421
- }
12422
12446
  isRoomSessionIdValid(roomSessionId) {
12423
12447
  return this.roomSessionIds.has(roomSessionId);
12424
12448
  }
@@ -12503,7 +12527,6 @@ var CallEventsManager = class extends Destroyable {
12503
12527
  callId: callJoinedEvent.call_id,
12504
12528
  roomSessionId: callJoinedEvent.room_session_id
12505
12529
  });
12506
- this._status$.next("connected");
12507
12530
  const sessionState = callJoinedEvent.room_session;
12508
12531
  const { capabilities } = callJoinedEvent;
12509
12532
  this.selfId = this.selfId ?? callJoinedEvent.member_id;
@@ -12630,12 +12653,53 @@ var CallEventsManager = class extends Destroyable {
12630
12653
 
12631
12654
  //#endregion
12632
12655
  //#region src/helpers/SDPHelper.ts
12656
+ /** Valid SDP direction attribute values. */
12657
+ const SDP_DIRECTIONS = new Set([
12658
+ "sendrecv",
12659
+ "sendonly",
12660
+ "recvonly",
12661
+ "inactive"
12662
+ ]);
12633
12663
  /**
12634
- * SDPHelper - Utility functions for SDP (Session Description Protocol) parsing and validation.
12664
+ * Extracts the media directions (audio/video) from an SDP string.
12665
+ *
12666
+ * Parses each media section (`m=audio` / `m=video`) and reads the `a=` direction
12667
+ * attribute (`sendrecv`, `sendonly`, `recvonly`, `inactive`).
12668
+ * If no explicit direction attribute is found for a media section, defaults to `sendrecv`
12669
+ * per RFC 4566.
12635
12670
  *
12636
- * This module provides helper functions to analyze and validate SDP content,
12637
- * particularly for ICE candidate validation in WebRTC connections.
12671
+ * @param sdp - The SDP string to parse
12672
+ * @returns The extracted audio and video directions
12673
+ *
12674
+ * @example
12675
+ * ```typescript
12676
+ * 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`;
12677
+ * extractMediaDirectionsFromSDP(sdp);
12678
+ * // { audio: 'sendrecv', video: 'recvonly' }
12679
+ * ```
12638
12680
  */
12681
+ function extractMediaDirectionsFromSDP(sdp) {
12682
+ const result = {
12683
+ audio: "inactive",
12684
+ video: "inactive"
12685
+ };
12686
+ if (!sdp) return result;
12687
+ const lines = sdp.split(/\r?\n/);
12688
+ let currentMediaKind = null;
12689
+ let currentDirection = null;
12690
+ for (const line of lines) if (line.startsWith("m=")) {
12691
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12692
+ if (line.startsWith("m=audio")) currentMediaKind = "audio";
12693
+ else if (line.startsWith("m=video")) currentMediaKind = "video";
12694
+ else currentMediaKind = null;
12695
+ currentDirection = null;
12696
+ } else if (currentMediaKind && line.startsWith("a=")) {
12697
+ const attr = line.substring(2).trim();
12698
+ if (SDP_DIRECTIONS.has(attr)) currentDirection = attr;
12699
+ }
12700
+ if (currentMediaKind) result[currentMediaKind] = currentDirection ?? "sendrecv";
12701
+ return result;
12702
+ }
12639
12703
  /**
12640
12704
  * Validates that an SDP string has at least one non-host ICE candidate
12641
12705
  * for each media section (m= line).
@@ -12938,6 +13002,15 @@ var LocalStreamController = class extends Destroyable {
12938
13002
  track.addEventListener("ended", this.mediaTrackEndedHandler);
12939
13003
  }
12940
13004
  /**
13005
+ * Update the controller options (e.g., when media overrides are applied).
13006
+ */
13007
+ updateOptions(options) {
13008
+ this.options = {
13009
+ ...this.options,
13010
+ ...options
13011
+ };
13012
+ }
13013
+ /**
12941
13014
  * Stop all local tracks and clean up.
12942
13015
  */
12943
13016
  stopAllTracks() {
@@ -13276,11 +13349,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13276
13349
  this._connectionState$ = this.createReplaySubject(1);
13277
13350
  this._signalingState$ = this.createReplaySubject(1);
13278
13351
  this._iceGatheringState$ = this.createReplaySubject(1);
13279
- this._errors$ = this.createSubject();
13352
+ this._errors$ = this.createReplaySubject(1);
13280
13353
  this._iceCandidates$ = this.createReplaySubject(1);
13281
13354
  this._initialized$ = this.createReplaySubject(1);
13282
13355
  this._remoteDescription$ = this.createReplaySubject(1);
13283
13356
  this._remoteStream$ = this.createBehaviorSubject(null);
13357
+ this._remoteOfferMediaDirections = null;
13284
13358
  this.deviceController = deviceController ?? {};
13285
13359
  this.id = options.callId ?? v4_default();
13286
13360
  this._type = remoteSessionDescription ? "answer" : "offer";
@@ -13288,10 +13362,19 @@ var RTCPeerConnectionController = class extends Destroyable {
13288
13362
  type: "offer",
13289
13363
  sdp: remoteSessionDescription
13290
13364
  } : void 0;
13365
+ this._remoteOfferMediaDirections = remoteSessionDescription ? extractMediaDirectionsFromSDP(remoteSessionDescription) : null;
13366
+ const offerDefaults = this._remoteOfferMediaDirections ? {
13367
+ audio: this._remoteOfferMediaDirections.audio.includes("recv"),
13368
+ video: this._remoteOfferMediaDirections.video.includes("recv"),
13369
+ receiveAudio: this._remoteOfferMediaDirections.audio.includes("send"),
13370
+ receiveVideo: this._remoteOfferMediaDirections.video.includes("send")
13371
+ } : {};
13291
13372
  this.options = {
13292
- receiveAudio: options.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13293
- receiveVideo: options.receiveVideo ?? PreferencesContainer.instance.receiveVideo,
13294
- ...options
13373
+ ...options,
13374
+ audio: options.audio ?? offerDefaults.audio,
13375
+ video: options.video ?? offerDefaults.video,
13376
+ receiveAudio: options.receiveAudio ?? offerDefaults.receiveAudio ?? PreferencesContainer.instance.receiveAudio,
13377
+ receiveVideo: options.receiveVideo ?? offerDefaults.receiveVideo ?? PreferencesContainer.instance.receiveVideo
13295
13378
  };
13296
13379
  this.localStreamController = new LocalStreamController({
13297
13380
  propose: this.propose,
@@ -13436,7 +13519,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13436
13519
  };
13437
13520
  }
13438
13521
  get inputVideoDeviceConstraints() {
13439
- if (this.options.video === false && !this.options.inputVideoDeviceConstraints) return false;
13522
+ if (!this.options.video && !this.options.inputVideoDeviceConstraints) return false;
13440
13523
  return {
13441
13524
  ...this.options.inputVideoDeviceConstraints,
13442
13525
  ...this.deviceController.selectedVideoInputDeviceConstraints
@@ -13458,12 +13541,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13458
13541
  default: return {
13459
13542
  ...options,
13460
13543
  offerToReceiveAudio: true,
13461
- offerToReceiveVideo: Boolean(this.inputVideoDeviceConstraints)
13544
+ offerToReceiveVideo: this.options.receiveVideo ?? Boolean(this.inputVideoDeviceConstraints)
13462
13545
  };
13463
13546
  }
13464
13547
  }
13465
13548
  get answerOptions() {
13466
- return {};
13549
+ return { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
13467
13550
  }
13468
13551
  /**
13469
13552
  * Initialize the RTCPeerConnection and setup event listeners.
@@ -13498,11 +13581,15 @@ var RTCPeerConnectionController = class extends Destroyable {
13498
13581
  });
13499
13582
  await this.updateSelectedInputDevice(kind, deviceInfo);
13500
13583
  });
13501
- await this.setupTrackHandling();
13502
- this._initialized$.next(true);
13503
13584
  if (this.type === "answer" && this.sdpInit) {
13585
+ await this.setupRemoteTracks();
13586
+ this._initialized$.next(true);
13504
13587
  this.setupEventListeners();
13505
- await this.handleOfferReceived();
13588
+ this._isNegotiating$.next(true);
13589
+ await this._setRemoteDescription(this.sdpInit);
13590
+ } else {
13591
+ await this.setupTrackHandling();
13592
+ this._initialized$.next(true);
13506
13593
  }
13507
13594
  } catch (error) {
13508
13595
  logger$11.error("[RTCPeerConnectionController] Initialization error:", error);
@@ -13600,6 +13687,35 @@ var RTCPeerConnectionController = class extends Destroyable {
13600
13687
  default:
13601
13688
  }
13602
13689
  }
13690
+ /**
13691
+ * Accept an inbound call by creating the SDP answer.
13692
+ * Optionally override media options before the answer is generated.
13693
+ * Must be called after initialization for inbound (answer-type) connections.
13694
+ */
13695
+ async acceptInbound(mediaOverrides) {
13696
+ if (mediaOverrides) {
13697
+ const { audio, video, receiveAudio, receiveVideo } = mediaOverrides;
13698
+ this.options = {
13699
+ ...this.options,
13700
+ ...audio !== void 0 ? { audio } : {},
13701
+ ...video !== void 0 ? { video } : {},
13702
+ ...receiveAudio !== void 0 ? { receiveAudio } : {},
13703
+ ...receiveVideo !== void 0 ? { receiveVideo } : {}
13704
+ };
13705
+ this.transceiverController?.updateOptions({
13706
+ receiveAudio: this.receiveAudio,
13707
+ receiveVideo: this.receiveVideo
13708
+ });
13709
+ this.localStreamController.updateOptions({
13710
+ inputAudioDeviceConstraints: this.inputAudioDeviceConstraints,
13711
+ inputVideoDeviceConstraints: this.inputVideoDeviceConstraints
13712
+ });
13713
+ }
13714
+ await this.setupLocalTracks();
13715
+ const { answerOptions } = this;
13716
+ logger$11.debug("[RTCPeerConnectionController] Creating inbound answer with options:", answerOptions);
13717
+ await this.createAnswer(answerOptions);
13718
+ }
13603
13719
  async handleOfferReceived() {
13604
13720
  if (!this.sdpInit) throw new DependencyError("SDP initialization parameters are not set");
13605
13721
  this._isNegotiating$.next(true);
@@ -13674,37 +13790,29 @@ var RTCPeerConnectionController = class extends Destroyable {
13674
13790
  }
13675
13791
  async setupLocalTracks() {
13676
13792
  logger$11.debug("[RTCPeerConnectionController] Setting up local tracks/transceivers.");
13677
- let { localStream } = this;
13678
- if (!localStream) try {
13679
- localStream = await this.localStreamController.buildLocalStream();
13680
- } catch (error) {
13681
- logger$11.error("[RTCPeerConnectionController] Error building local stream:", error);
13682
- this._errors$.next(error);
13683
- }
13684
- if (localStream) {
13685
- if (this.transceiverController?.useAddStream ?? false) {
13686
- logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13687
- this.peerConnection?.addStream(localStream);
13688
- if (!this.isNegotiating) {
13689
- logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13690
- this.negotiationNeeded$.next();
13691
- }
13692
- return;
13793
+ const localStream = this.localStream ?? await this.localStreamController.buildLocalStream();
13794
+ if (this.transceiverController?.useAddStream ?? false) {
13795
+ logger$11.warn("[RTCPeerConnectionController] Using deprecated addStream API to add local stream.");
13796
+ this.peerConnection?.addStream(localStream);
13797
+ if (!this.isNegotiating) {
13798
+ logger$11.debug("[RTCPeerConnectionController] Forcing negotiationneeded after local tracks setup.");
13799
+ this.negotiationNeeded$.next();
13693
13800
  }
13694
- for (const kind of ["audio", "video"]) {
13695
- const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13696
- index,
13697
- track
13698
- }));
13699
- for (const { index, track } of tracks) {
13700
- this.localStreamController.addTrackEndedListener(track);
13701
- if (this.transceiverController?.useAddTransceivers ?? false) {
13702
- const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13703
- await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13704
- } else {
13705
- logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13706
- this.peerConnection?.addTrack(track, localStream);
13707
- }
13801
+ return;
13802
+ }
13803
+ for (const kind of ["audio", "video"]) {
13804
+ const tracks = (kind === "audio" ? localStream.getAudioTracks() : localStream.getVideoTracks()).map((track, index) => ({
13805
+ index,
13806
+ track
13807
+ }));
13808
+ for (const { index, track } of tracks) {
13809
+ this.localStreamController.addTrackEndedListener(track);
13810
+ if (this.transceiverController?.useAddTransceivers ?? false) {
13811
+ const transceivers = (kind === "audio" ? this.transceiverController?.audioTransceivers : this.transceiverController?.videoTransceivers) ?? [];
13812
+ await this.transceiverController?.setupTransceiverSender(track, localStream, transceivers[index]);
13813
+ } else {
13814
+ logger$11.debug(`[RTCPeerConnectionController] Using addTrack for local ${kind} track:`, track.id);
13815
+ this.peerConnection?.addTrack(track, localStream);
13708
13816
  }
13709
13817
  }
13710
13818
  }
@@ -13721,7 +13829,12 @@ var RTCPeerConnectionController = class extends Destroyable {
13721
13829
  if (!this.peerConnection) throw new DependencyError("RTCPeerConnection is not initialized");
13722
13830
  this.peerConnection.ontrack = (event) => {
13723
13831
  logger$11.debug("[RTCPeerConnectionController] Remote track received:", event.track.kind);
13724
- this._remoteStream$.next(event.streams[0]);
13832
+ if (event.streams[0]) this._remoteStream$.next(event.streams[0]);
13833
+ else {
13834
+ const existingTracks = this._remoteStream$.value?.getTracks() ?? [];
13835
+ const newStream = new MediaStream([...existingTracks, event.track]);
13836
+ this._remoteStream$.next(newStream);
13837
+ }
13725
13838
  };
13726
13839
  await this.transceiverController?.setupRemoteTransceivers(this.type);
13727
13840
  }
@@ -13820,7 +13933,7 @@ var RTCPeerConnectionController = class extends Destroyable {
13820
13933
  });
13821
13934
  }
13822
13935
  get mediaDirections() {
13823
- return this.transceiverController?.getMediaDirections() ?? {
13936
+ return this.transceiverController?.getMediaDirections() ?? this._remoteOfferMediaDirections ?? {
13824
13937
  audio: "inactive",
13825
13938
  video: "inactive"
13826
13939
  };
@@ -13980,6 +14093,7 @@ var WebRTCVertoManager = class extends VertoManager {
13980
14093
  });
13981
14094
  this.subscribeTo(this.vertoAnswer$, (event) => {
13982
14095
  logger$10.debug("[WebRTCManager] Received Verto answer event:", event);
14096
+ this._signalingStatus$.next("connecting");
13983
14097
  const { sdp, callID } = event;
13984
14098
  this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
13985
14099
  status: "received",
@@ -14051,13 +14165,13 @@ var WebRTCVertoManager = class extends VertoManager {
14051
14165
  if (response.error) {
14052
14166
  const error = new JSONRPCError(response.error.code, response.error.message, response.error.data);
14053
14167
  this.onError?.(error);
14054
- throw error;
14168
+ return response;
14055
14169
  }
14056
14170
  const innerResult = getValueFrom(response, "result.result");
14057
14171
  if (innerResult?.error) {
14058
14172
  const error = new JSONRPCError(innerResult.error.code, innerResult.error.message, innerResult.error.data);
14059
14173
  this.onError?.(error);
14060
- throw error;
14174
+ return response;
14061
14175
  }
14062
14176
  return response;
14063
14177
  }
@@ -14077,7 +14191,7 @@ var WebRTCVertoManager = class extends VertoManager {
14077
14191
  }
14078
14192
  } catch (error) {
14079
14193
  logger$10.error(`[WebRTCManager] Error sending Verto ${vertoMethod}:`, error);
14080
- throw error;
14194
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14081
14195
  }
14082
14196
  }
14083
14197
  async processModifyResponse(response, rtcPeerConnController) {
@@ -14091,12 +14205,14 @@ var WebRTCVertoManager = class extends VertoManager {
14091
14205
  });
14092
14206
  } catch (error) {
14093
14207
  logger$10.warn("[WebRTCManager] Error processing modify response:", error);
14094
- this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14208
+ const modifyError = error instanceof Error ? error : new Error(String(error), { cause: error });
14209
+ this.onError?.(modifyError);
14095
14210
  }
14096
14211
  }
14097
14212
  }
14098
14213
  processInviteResponse(response, rtcPeerConnController) {
14099
14214
  if (!response.error && getValueFrom(response, "result.result.result.message") === "CALL CREATED") {
14215
+ this._signalingStatus$.next("trying");
14100
14216
  this._nodeId$.next(getValueFrom(response, "result.node_id") ?? null);
14101
14217
  const memberId = getValueFrom(response, "result.result.result.memberID") ?? null;
14102
14218
  const callId = getValueFrom(response, "result.result.result.callID") ?? null;
@@ -14151,6 +14267,36 @@ var WebRTCVertoManager = class extends VertoManager {
14151
14267
  this.subscribeTo(rtcPeerConnController.errors$, (error) => {
14152
14268
  this.onError?.(error);
14153
14269
  });
14270
+ if (options.initOffer) this.handleInboundAnswer(rtcPeerConnController);
14271
+ }
14272
+ async handleInboundAnswer(rtcPeerConnController) {
14273
+ logger$10.debug("[WebRTCManager] Waiting for inbound call to be accepted or rejected");
14274
+ 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);
14275
+ if (vertoByeOrAccepted === null) {
14276
+ logger$10.debug("[WebRTCManager] Inbound answer handler aborted (destroyed).");
14277
+ return;
14278
+ }
14279
+ if (isVertoByeMessage(vertoByeOrAccepted)) {
14280
+ logger$10.info("[WebRTCManager] Inbound call ended by remote before answer.");
14281
+ this.callSession?.destroy();
14282
+ } else if (!vertoByeOrAccepted) {
14283
+ logger$10.info("[WebRTCManager] Inbound call rejected by user.");
14284
+ try {
14285
+ await this.bye("USER_BUSY");
14286
+ } finally {
14287
+ this._signalingStatus$.next("disconnected");
14288
+ this.callSession?.destroy();
14289
+ }
14290
+ } else {
14291
+ logger$10.debug("[WebRTCManager] Inbound call accepted, creating SDP answer");
14292
+ const answerOptions = this.webRtcCallSession.answerMediaOptions;
14293
+ try {
14294
+ await rtcPeerConnController.acceptInbound(answerOptions);
14295
+ } catch (error) {
14296
+ logger$10.error("[WebRTCManager] Error creating inbound answer:", error);
14297
+ this.onError?.(error instanceof Error ? error : new Error(String(error), { cause: error }));
14298
+ }
14299
+ }
14154
14300
  }
14155
14301
  setupVertoAttachHandler() {
14156
14302
  this.subscribeTo(this.vertoAttach$, async (vertoAttach) => {
@@ -14168,7 +14314,7 @@ var WebRTCVertoManager = class extends VertoManager {
14168
14314
  });
14169
14315
  }
14170
14316
  initObservables(rtcPeerConnController) {
14171
- 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));
14317
+ 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$));
14172
14318
  this.localStream$ = rtcPeerConnController.localStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14173
14319
  this.remoteStream$ = rtcPeerConnController.remoteStream$.pipe(filterNull(), (0, import_cjs$10.takeUntil)(this.destroyed$));
14174
14320
  }
@@ -14184,7 +14330,6 @@ var WebRTCVertoManager = class extends VertoManager {
14184
14330
  });
14185
14331
  this.sendLocalDescriptionOnceAccepted(vertoMessageRequest, rtcPeerConnController);
14186
14332
  } else if (initial) {
14187
- this._signalingStatus$.next("trying");
14188
14333
  const vertoMessageRequest = VertoInvite({
14189
14334
  dialogParams,
14190
14335
  sdp
@@ -14491,8 +14636,8 @@ var WebRTCCall = class extends Destroyable {
14491
14636
  this.clientSession = clientSession;
14492
14637
  this.options = options;
14493
14638
  this.address = address;
14494
- this.participantsMap = /* @__PURE__ */ new Map();
14495
- this._errors$ = this.createSubject();
14639
+ this._errors$ = this.createReplaySubject(1);
14640
+ this._lastMergedStatus = "new";
14496
14641
  this._answered$ = this.createReplaySubject();
14497
14642
  this._holdState = false;
14498
14643
  this._userVariables$ = this.createBehaviorSubject({ ...PreferencesContainer.instance.userVariables });
@@ -14513,8 +14658,10 @@ var WebRTCCall = class extends Destroyable {
14513
14658
  const managers = initialization.initializeManagers(this);
14514
14659
  this.vertoManager = managers.vertoManager;
14515
14660
  this.callEventsManager = managers.callEventsManager;
14516
- if (options.initOffer) this._status$ = this.createBehaviorSubject("ringing");
14517
- else this._status$ = this.createBehaviorSubject("new");
14661
+ if (options.initOffer) {
14662
+ this._status$ = this.createBehaviorSubject("ringing");
14663
+ this._lastMergedStatus = "ringing";
14664
+ } else this._status$ = this.createBehaviorSubject("new");
14518
14665
  const { deviceController } = initialization;
14519
14666
  this.participantFactory = new ParticipantFactory(this.executeMethod.bind(this), this.vertoManager, deviceController);
14520
14667
  }
@@ -14522,9 +14669,17 @@ var WebRTCCall = class extends Destroyable {
14522
14669
  get errors$() {
14523
14670
  return this._errors$.asObservable();
14524
14671
  }
14525
- /** @internal Push an error to the call's error stream. */
14526
- emitError(error) {
14527
- this._errors$.next(error);
14672
+ /**
14673
+ * @internal Push an error to the call's error stream.
14674
+ * Fatal errors automatically transition the call to `'failed'` and destroy it.
14675
+ */
14676
+ emitError(callError) {
14677
+ if (this._status$.value === "destroyed" || this._status$.value === "failed") return;
14678
+ this._errors$.next(callError);
14679
+ if (callError.fatal) {
14680
+ this._status$.next("failed");
14681
+ this.destroy();
14682
+ }
14528
14683
  }
14529
14684
  /** Whether this call is `'inbound'` or `'outbound'`. */
14530
14685
  get direction() {
@@ -14564,7 +14719,7 @@ var WebRTCCall = class extends Destroyable {
14564
14719
  }
14565
14720
  /** Current snapshot of all participants in the call. */
14566
14721
  get participants() {
14567
- return Array.from(this.participantsMap.values());
14722
+ return this.callEventsManager.participants;
14568
14723
  }
14569
14724
  /** The local participant, or `null` if not yet joined. */
14570
14725
  get self() {
@@ -14573,7 +14728,6 @@ var WebRTCCall = class extends Destroyable {
14573
14728
  async toggleLock() {
14574
14729
  const method = this.locked ? "call.unlock" : "call.lock";
14575
14730
  await this.executeMethod(this.selfId ?? "", method, {});
14576
- throw new UnimplementedError();
14577
14731
  }
14578
14732
  async toggleHold() {
14579
14733
  if (this._holdState) await this.vertoManager.unhold();
@@ -14615,25 +14769,31 @@ var WebRTCCall = class extends Destroyable {
14615
14769
  }
14616
14770
  }
14617
14771
  buildMethodParams(target, args) {
14618
- const reference = {
14619
- node_id: this.nodeId,
14620
- call_id: this.id
14772
+ const self = {
14773
+ node_id: this.nodeId ?? "",
14774
+ call_id: this.id,
14775
+ member_id: this.vertoManager.selfId ?? ""
14776
+ };
14777
+ if (typeof target === "object") return {
14778
+ ...args,
14779
+ self,
14780
+ targets: [target]
14621
14781
  };
14622
14782
  return {
14623
14783
  ...args,
14624
- self: {
14625
- ...reference,
14626
- member_id: this.vertoManager.selfId
14627
- },
14784
+ self,
14628
14785
  target: {
14629
- ...reference,
14786
+ node_id: this.nodeId ?? "",
14787
+ call_id: this.id,
14630
14788
  member_id: target
14631
14789
  }
14632
14790
  };
14633
14791
  }
14634
14792
  /** Observable of the current call status (e.g. `'ringing'`, `'connected'`). */
14635
14793
  get status$() {
14636
- return this.cachedObservable("status$", () => (0, import_cjs$9.merge)(this._status$.asObservable(), this.vertoManager.signalingStatus$));
14794
+ 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) => {
14795
+ this._lastMergedStatus = status;
14796
+ })));
14637
14797
  }
14638
14798
  /** Observable of the participants list, emits on join/leave/update. */
14639
14799
  get participants$() {
@@ -14673,7 +14833,7 @@ var WebRTCCall = class extends Destroyable {
14673
14833
  }
14674
14834
  /** Current call status. */
14675
14835
  get status() {
14676
- return this._status$.value;
14836
+ return this._lastMergedStatus;
14677
14837
  }
14678
14838
  /** Whether the call is currently being recorded. */
14679
14839
  get recording() {
@@ -14850,10 +15010,15 @@ var WebRTCCall = class extends Destroyable {
14850
15010
  async sendDigits(dtmf) {
14851
15011
  return this.vertoManager.sendDigits(dtmf);
14852
15012
  }
14853
- /** Accepts an inbound call. */
14854
- answer() {
15013
+ /** Accepts an inbound call, optionally overriding media options for the answer. */
15014
+ answer(options) {
15015
+ this._answerMediaOptions = options;
14855
15016
  this._answered$.next(true);
14856
15017
  }
15018
+ /** Media options provided when answering. Used internally by the VertoManager. */
15019
+ get answerMediaOptions() {
15020
+ return this._answerMediaOptions;
15021
+ }
14857
15022
  /** Rejects an inbound call. */
14858
15023
  reject() {
14859
15024
  this._answered$.next(false);
@@ -14881,10 +15046,10 @@ var WebRTCCall = class extends Destroyable {
14881
15046
  }
14882
15047
  /** Destroys the call, releasing all resources and subscriptions. */
14883
15048
  destroy() {
15049
+ if (this._status$.value === "destroyed") return;
14884
15050
  this._status$.next("destroyed");
14885
15051
  this.vertoManager.destroy();
14886
15052
  this.callEventsManager.destroy();
14887
- this.participantsMap.clear();
14888
15053
  super.destroy();
14889
15054
  }
14890
15055
  };
@@ -14892,6 +15057,23 @@ var WebRTCCall = class extends Destroyable {
14892
15057
  //#endregion
14893
15058
  //#region src/managers/CallFactory.ts
14894
15059
  /**
15060
+ * Infers the semantic error category from a raw Error thrown by VertoManager
15061
+ * or an RTCPeerConnection layer.
15062
+ */
15063
+ function inferCallErrorKind(error) {
15064
+ if (error instanceof RPCTimeoutError) return "timeout";
15065
+ if (error instanceof JSONRPCError) return "signaling";
15066
+ if (error instanceof MediaTrackError) return "media";
15067
+ if (error instanceof WebSocketConnectionError || error instanceof TransportConnectionError) return "network";
15068
+ return "internal";
15069
+ }
15070
+ /** Determines whether an error should be fatal (destroy the call). */
15071
+ function isFatalError(error) {
15072
+ if (error instanceof VertoPongError) return false;
15073
+ if (error instanceof MediaTrackError) return false;
15074
+ return true;
15075
+ }
15076
+ /**
14895
15077
  * Factory for creating WebRTCCall instances with proper manager wiring.
14896
15078
  * Eliminates circular dependencies by centralizing Call and Manager creation.
14897
15079
  */
@@ -14912,7 +15094,13 @@ var CallFactory = class {
14912
15094
  vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, this.webRTCApiProvider, {
14913
15095
  nodeId: options.nodeId,
14914
15096
  onError: (error) => {
14915
- callInstance.emitError(error);
15097
+ const callError = {
15098
+ kind: inferCallErrorKind(error),
15099
+ fatal: isFatalError(error),
15100
+ error,
15101
+ callId: callInstance.id
15102
+ };
15103
+ callInstance.emitError(callError);
14916
15104
  }
14917
15105
  }),
14918
15106
  callEventsManager: new CallEventsManager(callInstance)
@@ -15292,9 +15480,15 @@ var PendingRPC = class PendingRPC {
15292
15480
  return () => signal.removeEventListener("abort", abortHandler);
15293
15481
  }) : import_cjs$6.NEVER).subscribe({
15294
15482
  next: (response) => {
15295
- logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15296
15483
  isSettled = true;
15297
- resolve(response);
15484
+ if (response.error) {
15485
+ const rpcError = new RPCError(response.error.code, request.id, response.error.message, response.error.data);
15486
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Rejecting promise with RPC error:`, rpcError);
15487
+ reject(rpcError);
15488
+ } else {
15489
+ logger$7.debug(`[PendingRPC(${this.id}) request:${request.id}] Resolving promise with response:`, response);
15490
+ resolve(response);
15491
+ }
15298
15492
  subscription.unsubscribe();
15299
15493
  },
15300
15494
  error: (error) => {
@@ -15351,7 +15545,7 @@ var ClientSessionManager = class extends Destroyable {
15351
15545
  revision: 0
15352
15546
  };
15353
15547
  this._authorization$ = this.createBehaviorSubject(void 0);
15354
- this._errors$ = this.createSubject();
15548
+ this._errors$ = this.createReplaySubject(1);
15355
15549
  this._authenticated$ = this.createBehaviorSubject(false);
15356
15550
  this._subscriberInfo$ = this.createBehaviorSubject(null);
15357
15551
  this._calls$ = this.createBehaviorSubject({});
@@ -15621,12 +15815,13 @@ var ClientSessionManager = class extends Destroyable {
15621
15815
  }
15622
15816
  async createOutboundCall(destination, options = {}) {
15623
15817
  const destinationURI = destination instanceof Address ? destination.defaultChannel : destination;
15818
+ let callSession;
15624
15819
  try {
15625
- const callSession = await this.createCall({
15820
+ callSession = await this.createCall({
15626
15821
  to: destinationURI,
15627
15822
  ...options
15628
15823
  });
15629
- 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)));
15824
+ 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)))));
15630
15825
  this._calls$.next({
15631
15826
  [`${callSession.id}`]: callSession,
15632
15827
  ...this._calls$.value
@@ -15634,7 +15829,8 @@ var ClientSessionManager = class extends Destroyable {
15634
15829
  return callSession;
15635
15830
  } catch (error) {
15636
15831
  logger$6.error("[Session] Error creating outbound call:", error);
15637
- const callError = new CallCreateError("Call create timeout", error);
15832
+ callSession?.destroy();
15833
+ const callError = new CallCreateError(error instanceof import_cjs$5.TimeoutError ? "Call create timeout" : "Call creation failed", error, "outbound");
15638
15834
  this._errors$.next(callError);
15639
15835
  throw callError;
15640
15836
  }
@@ -15660,7 +15856,7 @@ var ClientSessionManager = class extends Destroyable {
15660
15856
  return callSession;
15661
15857
  } catch (error) {
15662
15858
  logger$6.error("[Session] Error creating call session:", error);
15663
- throw new CallCreateError("Call create error", error);
15859
+ throw new CallCreateError("Call create error", error, options.initOffer ? "inbound" : "outbound");
15664
15860
  }
15665
15861
  }
15666
15862
  destroy() {
@@ -15909,7 +16105,7 @@ var WebSocketController = class WebSocketController extends Destroyable {
15909
16105
  this.shouldReconnect = false;
15910
16106
  this._status$ = this.createBehaviorSubject("disconnected");
15911
16107
  this._incomingMessages$ = this.createSubject();
15912
- this._errors$ = this.createSubject();
16108
+ this._errors$ = this.createReplaySubject(1);
15913
16109
  this.reconnectDelayMin = options.reconnectDelayMin ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MIN_MS;
15914
16110
  this.reconnectDelayMax = options.reconnectDelayMax ?? WebSocketController.DEFAULT_RECONNECT_DELAY_MAX_MS;
15915
16111
  this.connectionTimeout = options.connectionTimeout ?? WebSocketController.DEFAULT_CONNECTION_TIMEOUT_MS;
@@ -16236,7 +16432,8 @@ const buildOptionsFromDestination = (destination) => {
16236
16432
  const channel = new URLSearchParams(queryString).get("channel");
16237
16433
  if (channel === "video") return {
16238
16434
  audio: true,
16239
- video: true
16435
+ video: true,
16436
+ receiveVideo: true
16240
16437
  };
16241
16438
  else if (channel === "audio") return {
16242
16439
  audio: true,
@@ -16266,7 +16463,7 @@ var SignalWire = class extends Destroyable {
16266
16463
  this._directory$ = this.createBehaviorSubject(void 0);
16267
16464
  this._isConnected$ = this.createBehaviorSubject(false);
16268
16465
  this._isRegistered$ = this.createBehaviorSubject(false);
16269
- this._errors$ = this.createSubject();
16466
+ this._errors$ = this.createReplaySubject(1);
16270
16467
  this._options = {};
16271
16468
  this._deps = new DependencyContainer();
16272
16469
  this._options = {
@@ -16343,7 +16540,12 @@ var SignalWire = class extends Destroyable {
16343
16540
  this._subscriber$.next(new Subscriber(this._deps.http));
16344
16541
  if (!this._options.skipConnection) await this.connect();
16345
16542
  if (!this._options.reconnectAttachedCalls && this._attachManager) await this._attachManager.flush();
16346
- if (!this._options.skipRegister) this.register();
16543
+ if (!this._options.skipRegister) try {
16544
+ await this.register();
16545
+ } catch (error) {
16546
+ logger$1.error("[SignalWire] Registration failed:", error);
16547
+ this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16548
+ }
16347
16549
  this.handleAttachments();
16348
16550
  }
16349
16551
  async handleAttachments() {
@@ -16506,8 +16708,22 @@ var SignalWire = class extends Destroyable {
16506
16708
  }));
16507
16709
  this._isRegistered$.next(true);
16508
16710
  } catch (error) {
16509
- logger$1.error("[SignalWire] Failed to register subscriber:", error);
16711
+ logger$1.debug("[SignalWire] Failed to register subscriber, trying reauthentication...");
16712
+ if (this._deps.credential.token) this._clientSession.reauthenticate(this._deps.credential.token).then(async () => {
16713
+ logger$1.debug("[SignalWire] Reauthentication successful, retrying register()");
16714
+ await this._transport.execute(RPCExecute({
16715
+ method: "subscriber.online",
16716
+ params: {}
16717
+ }));
16718
+ this._isRegistered$.next(true);
16719
+ }).catch((reauthError) => {
16720
+ logger$1.error("[SignalWire] Reauthentication failed during register():", reauthError);
16721
+ 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 }) });
16722
+ this._errors$.next(registerError);
16723
+ throw registerError;
16724
+ });
16510
16725
  this._errors$.next(error instanceof Error ? error : new Error(String(error), { cause: error }));
16726
+ throw error;
16511
16727
  }
16512
16728
  }
16513
16729
  /** Unregisters the subscriber, going offline for inbound calls. */