@signalwire/js 4.0.0-beta.2 → 4.0.0-beta.4

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/index.cjs CHANGED
@@ -598,8 +598,9 @@ const selectDevice = (devices = [], selected, preferred) => {
598
598
  return selected;
599
599
  };
600
600
  var NavigatorDeviceController = class extends Destroyable {
601
- constructor() {
601
+ constructor(webRTCApiProvider) {
602
602
  super();
603
+ this.webRTCApiProvider = webRTCApiProvider;
603
604
  this.deviceChangeHandler = () => {
604
605
  logger$18.debug("[DeviceController] Device change detected");
605
606
  this.enumerateDevices();
@@ -681,24 +682,22 @@ var NavigatorDeviceController = class extends Destroyable {
681
682
  });
682
683
  }
683
684
  init() {
684
- if (navigator.mediaDevices) {
685
- this.subscribeTo(this._devicesState$.pipe((0, rxjs.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
686
- const currentSelected = this._selectedDevicesState$.value;
687
- const newAudioInput = selectDevice(devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
688
- const newAudioOutput = selectDevice(devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
689
- const newVideoInput = selectDevice(devicesState.videoinput, currentSelected.videoinput, PreferencesContainer.instance.preferredVideoInput);
690
- if (newAudioInput !== currentSelected.audioinput || newAudioOutput !== currentSelected.audiooutput || newVideoInput !== currentSelected.videoinput) this._selectedDevicesState$.next({
691
- audioinput: newAudioInput,
692
- audiooutput: newAudioOutput,
693
- videoinput: newVideoInput
694
- });
685
+ this.subscribeTo(this._devicesState$.pipe((0, rxjs.debounceTime)(PreferencesContainer.instance.deviceDebounceTime)), (devicesState) => {
686
+ const currentSelected = this._selectedDevicesState$.value;
687
+ const newAudioInput = selectDevice(devicesState.audioinput, currentSelected.audioinput, PreferencesContainer.instance.preferredAudioInput);
688
+ const newAudioOutput = selectDevice(devicesState.audiooutput, currentSelected.audiooutput, PreferencesContainer.instance.preferredAudioOutput);
689
+ const newVideoInput = selectDevice(devicesState.videoinput, currentSelected.videoinput, PreferencesContainer.instance.preferredVideoInput);
690
+ if (newAudioInput !== currentSelected.audioinput || newAudioOutput !== currentSelected.audiooutput || newVideoInput !== currentSelected.videoinput) this._selectedDevicesState$.next({
691
+ audioinput: newAudioInput,
692
+ audiooutput: newAudioOutput,
693
+ videoinput: newVideoInput
695
694
  });
696
- this.enumerateDevices();
697
- }
695
+ });
696
+ this.enumerateDevices();
698
697
  }
699
698
  enableDeviceMonitoring() {
700
699
  this.disableDeviceMonitoring();
701
- navigator.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
700
+ this.webRTCApiProvider.mediaDevices.addEventListener("devicechange", this.deviceChangeHandler);
702
701
  if (PreferencesContainer.instance.devicePollingInterval > 0) this._devicesPoolingSubscription = (0, rxjs.interval)(PreferencesContainer.instance.devicePollingInterval).subscribe(() => {
703
702
  logger$18.debug("[DeviceController] Polling devices due to interval");
704
703
  this.enumerateDevices();
@@ -706,7 +705,7 @@ var NavigatorDeviceController = class extends Destroyable {
706
705
  this.enumerateDevices();
707
706
  }
708
707
  disableDeviceMonitoring() {
709
- navigator.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler);
708
+ this.webRTCApiProvider.mediaDevices.removeEventListener("devicechange", this.deviceChangeHandler);
710
709
  if (this._devicesPoolingSubscription) {
711
710
  this._devicesPoolingSubscription.unsubscribe();
712
711
  this._devicesPoolingSubscription = void 0;
@@ -714,7 +713,7 @@ var NavigatorDeviceController = class extends Destroyable {
714
713
  }
715
714
  async enumerateDevices() {
716
715
  try {
717
- const devicesByKind = (await navigator.mediaDevices.enumerateDevices()).reduce((acc, device) => {
716
+ const devicesByKind = (await this.webRTCApiProvider.mediaDevices.enumerateDevices()).reduce((acc, device) => {
718
717
  acc[device.kind].push(device);
719
718
  return acc;
720
719
  }, {
@@ -737,7 +736,7 @@ var NavigatorDeviceController = class extends Destroyable {
737
736
  if (deviceInfo.kind === "audiooutput") return null;
738
737
  try {
739
738
  const constraints = this.deviceInfoToConstraints(deviceInfo);
740
- const stream = await navigator.mediaDevices.getUserMedia({
739
+ const stream = await this.webRTCApiProvider.mediaDevices.getUserMedia({
741
740
  audio: deviceInfo.kind === "audioinput" ? constraints : false,
742
741
  video: deviceInfo.kind === "videoinput" ? constraints : false
743
742
  });
@@ -909,9 +908,23 @@ var DependencyContainer = class {
909
908
  this._webSocketConstructor = WebSocketConstructor;
910
909
  }
911
910
  get deviceController() {
912
- this._deviceController ??= new NavigatorDeviceController();
911
+ this._deviceController ??= new NavigatorDeviceController(this.webRTCApiProvider);
913
912
  return this._deviceController;
914
913
  }
914
+ get webRTCApiProvider() {
915
+ if (!this._webRTCApiProvider) {
916
+ if (typeof RTCPeerConnection === "undefined" || typeof navigator === "undefined") throw new require_operators.DependencyError("WebRTCApiProvider: RTCPeerConnection or navigator.mediaDevices is not available. Please provide a custom webRTCApiProvider in SignalWireOptions.");
917
+ this._webRTCApiProvider = {
918
+ RTCPeerConnection,
919
+ mediaDevices: navigator.mediaDevices
920
+ };
921
+ }
922
+ return this._webRTCApiProvider;
923
+ }
924
+ set webRTCApiProvider(webRTCApiProvider) {
925
+ this._webRTCApiProvider = webRTCApiProvider;
926
+ this._deviceController = void 0;
927
+ }
915
928
  get authorizationStateKey() {
916
929
  return `sw:${this.subscriberId}:as`;
917
930
  }
@@ -1905,7 +1918,7 @@ function isJSONRPCRequest(value) {
1905
1918
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && hasProperty(value, "method") && typeof value.method === "string";
1906
1919
  }
1907
1920
  function isJSONRPCResponse(value) {
1908
- return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && hasProperty(value, "result");
1921
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "id") && typeof value.id === "string" && (hasProperty(value, "result") || hasProperty(value, "error"));
1909
1922
  }
1910
1923
 
1911
1924
  //#endregion
@@ -2102,7 +2115,7 @@ var CallEventsManager = class extends Destroyable {
2102
2115
  });
2103
2116
  this.updateParticipants(sessionState.members);
2104
2117
  this._self$.value?.capabilities.updateFromRaw(capabilities);
2105
- this.updateLayouts();
2118
+ if (this._self$.value?.capabilities.setLayout) this.updateLayouts();
2106
2119
  });
2107
2120
  this.subscribeTo(this.memberUpdates$, (member) => {
2108
2121
  logger$15.debug("[CallEventsManager] Handling member update event for member ID:", member);
@@ -2827,7 +2840,7 @@ var RTCPeerConnectionController = class extends Destroyable {
2827
2840
  logger$11.debug(`[RTCPeerConnectionController] ${kind} input device selected: none`);
2828
2841
  return;
2829
2842
  }
2830
- const streamTrack = (await navigator.mediaDevices.getUserMedia({ [kind]: {
2843
+ const streamTrack = (await this.getUserMedia({ [kind]: {
2831
2844
  ...track.getConstraints(),
2832
2845
  ...this.deviceController.deviceInfoToConstraints(deviceInfo)
2833
2846
  } })).getTracks().find((t) => t.kind === kind);
@@ -3018,7 +3031,7 @@ var RTCPeerConnectionController = class extends Destroyable {
3018
3031
  };
3019
3032
  }
3020
3033
  get WebRTCPeerConnectionConstructor() {
3021
- return this.options.WebRTCPeerConnectionConstructor ?? RTCPeerConnection;
3034
+ return this.options.webRTCApiProvider?.RTCPeerConnection ?? RTCPeerConnection;
3022
3035
  }
3023
3036
  get offerOptions() {
3024
3037
  const options = { iceRestart: this.firstSDPExchangeCompleted ? true : void 0 };
@@ -3285,10 +3298,12 @@ var RTCPeerConnectionController = class extends Destroyable {
3285
3298
  }
3286
3299
  }
3287
3300
  async getUserMedia(constraints) {
3288
- return this.options.getUserMedia?.(constraints) ?? navigator.mediaDevices.getUserMedia(constraints);
3301
+ return (this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices).getUserMedia(constraints);
3289
3302
  }
3290
3303
  async getDisplayMedia(options) {
3291
- return this.options.getDisplayMedia?.(options) ?? navigator.mediaDevices.getDisplayMedia(options);
3304
+ const mediaDevices = this.options.webRTCApiProvider?.mediaDevices ?? navigator.mediaDevices;
3305
+ if (!mediaDevices.getDisplayMedia) throw new require_operators.DependencyError("getDisplayMedia is not supported by the current WebRTC provider");
3306
+ return mediaDevices.getDisplayMedia(options);
3292
3307
  }
3293
3308
  async setupRemoteTracks() {
3294
3309
  if (!this.peerConnection) throw new require_operators.DependencyError("RTCPeerConnection is not initialized");
@@ -3429,7 +3444,10 @@ function isVertoAttachMessage(value) {
3429
3444
  return value.method === "verto.attach";
3430
3445
  }
3431
3446
  function isVertoAnswerInnerParams(value) {
3432
- return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.answer" && isObject(value.params) && hasProperty(value.params, "callID") && hasProperty(value.params, "sdp");
3447
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.answer" && isObject(value.params) && hasProperty(value.params, "callID");
3448
+ }
3449
+ function isVertoMediaInnerParams(value) {
3450
+ return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.media" && isObject(value.params) && hasProperty(value.params, "callID") && hasProperty(value.params, "sdp");
3433
3451
  }
3434
3452
  function isVertoMediaParamsInnerParams(value) {
3435
3453
  return isObject(value) && hasProperty(value, "jsonrpc") && value.jsonrpc === "2.0" && hasProperty(value, "method") && value.method === "verto.mediaParams" && isObject(value.params) && hasProperty(value.params, "mediaParams");
@@ -3452,11 +3470,12 @@ var VertoManager = class extends Destroyable {
3452
3470
  }
3453
3471
  };
3454
3472
  var WebRTCVertoManager = class extends VertoManager {
3455
- constructor(webRtcCallSession, attachManager, deviceController, options = {}) {
3473
+ constructor(webRtcCallSession, attachManager, deviceController, webRTCApiProvider, options = {}) {
3456
3474
  super(webRtcCallSession);
3457
3475
  this.webRtcCallSession = webRtcCallSession;
3458
3476
  this.attachManager = attachManager;
3459
3477
  this.deviceController = deviceController;
3478
+ this.webRTCApiProvider = webRTCApiProvider;
3460
3479
  this._rtcPeerConnections$ = this.createBehaviorSubject([]);
3461
3480
  this._selfId$ = this.createBehaviorSubject(null);
3462
3481
  this._signalingStatus$ = this.createBehaviorSubject(null);
@@ -3537,10 +3556,19 @@ var WebRTCVertoManager = class extends VertoManager {
3537
3556
  ].includes(connectionState)))));
3538
3557
  }
3539
3558
  initSubscriptions() {
3559
+ this.subscribeTo(this.vertoMedia$, (event) => {
3560
+ logger$10.debug("[WebRTCManager] Received Verto media event (early media SDP):", event);
3561
+ this._signalingStatus$.next("ringing");
3562
+ const { sdp, callID } = event;
3563
+ this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
3564
+ status: "received",
3565
+ sdp
3566
+ });
3567
+ });
3540
3568
  this.subscribeTo(this.vertoAnswer$, (event) => {
3541
3569
  logger$10.debug("[WebRTCManager] Received Verto answer event:", event);
3542
- const { sdp } = event;
3543
- this._rtcPeerConnectionsMap.get(event.callID)?.updateAnswerStatus({
3570
+ const { sdp, callID } = event;
3571
+ this._rtcPeerConnectionsMap.get(callID)?.updateAnswerStatus({
3544
3572
  status: "received",
3545
3573
  sdp
3546
3574
  });
@@ -3581,6 +3609,9 @@ var WebRTCVertoManager = class extends VertoManager {
3581
3609
  get selfId() {
3582
3610
  return this._selfId$.value;
3583
3611
  }
3612
+ get vertoMedia$() {
3613
+ return this.webRtcCallSession.webrtcMessages$.pipe(require_operators.filterAs(isVertoMediaInnerParams, "params"), (0, rxjs.takeUntil)(this.destroyed$));
3614
+ }
3584
3615
  get vertoAnswer$() {
3585
3616
  return this.cachedObservable("vertoAnswer$", () => this.webRtcCallSession.webrtcMessages$.pipe(require_operators.filterAs(isVertoAnswerInnerParams, "params"), (0, rxjs.takeUntil)(this.destroyed$)));
3586
3617
  }
@@ -3619,7 +3650,7 @@ var WebRTCVertoManager = class extends VertoManager {
3619
3650
  }
3620
3651
  async sendLocalDescription(message, rtcPeerConnController) {
3621
3652
  const vertoMethod = message.method;
3622
- const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController);
3653
+ const optionalsParams = this.getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod);
3623
3654
  try {
3624
3655
  const response = await this.executeVerto(message, optionalsParams);
3625
3656
  switch (vertoMethod) {
@@ -3664,7 +3695,6 @@ var WebRTCVertoManager = class extends VertoManager {
3664
3695
  this._selfId$.next(memberId);
3665
3696
  rtcPeerConnController.setMemberId(memberId);
3666
3697
  if (callId) this.webRtcCallSession.addCallId(callId);
3667
- this._signalingStatus$.next("ringing");
3668
3698
  this.attachManager.attach(this.webRtcCallSession);
3669
3699
  logger$10.info("[WebRTCManager] Verto invite successful");
3670
3700
  logger$10.debug(`[WebRTCManager] nodeid: ${this._nodeId$.value}, selfId: ${this._selfId$.value}`);
@@ -3696,6 +3726,7 @@ var WebRTCVertoManager = class extends VertoManager {
3696
3726
  inputVideoStream: options.inputVideoStream,
3697
3727
  receiveAudio: options.receiveAudio,
3698
3728
  receiveVideo: options.receiveVideo,
3729
+ webRTCApiProvider: this.webRTCApiProvider,
3699
3730
  ...this.RTCPeerConnectionConfig
3700
3731
  }, options.initOffer, this.deviceController);
3701
3732
  this.setupLocalDescriptionHandler(rtcPeerConnController);
@@ -3758,22 +3789,23 @@ var WebRTCVertoManager = class extends VertoManager {
3758
3789
  }
3759
3790
  setupVertoByeHandler() {
3760
3791
  this.subscribeTo(this.vertoBye$, () => {
3792
+ this._signalingStatus$.next("disconnected");
3761
3793
  this.attachManager.detach(this.webRtcCallSession);
3762
3794
  this.callSession?.destroy();
3763
3795
  });
3764
3796
  }
3765
- getSendLocalSDPOptionalParams(rtcPeerConnController) {
3797
+ getSendLocalSDPOptionalParams(rtcPeerConnController, vertoMethod) {
3766
3798
  let subscribe = void 0;
3767
- const initial = !rtcPeerConnController.firstSDPExchangeCompleted;
3768
- if (initial) {
3799
+ if (!rtcPeerConnController.firstSDPExchangeCompleted) {
3769
3800
  subscribe = [];
3770
3801
  if (rtcPeerConnController.isMainDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeMainDevice);
3771
3802
  else if (rtcPeerConnController.isAdditionalDevice) subscribe.push(...PreferencesContainer.instance.inviteSubscribeAdditionalDevice);
3772
3803
  else if (rtcPeerConnController.isScreenShare) subscribe.push(...PreferencesContainer.instance.inviteSubscribeScreenshare);
3773
3804
  }
3805
+ const isInvite = vertoMethod === "verto.invite";
3774
3806
  return {
3775
3807
  callID: rtcPeerConnController.id,
3776
- node_id: initial ? "" : this._nodeId$.value ?? "",
3808
+ node_id: isInvite ? "" : this._nodeId$.value ?? "",
3777
3809
  subscribe
3778
3810
  };
3779
3811
  }
@@ -3785,7 +3817,12 @@ var WebRTCVertoManager = class extends VertoManager {
3785
3817
  this.callSession?.destroy();
3786
3818
  } else if (!vertoByeOrAccepted) {
3787
3819
  logger$10.info("[WebRTCManager] Call was not accepted, sending verto.bye.");
3788
- await this.bye("USER_BUSY");
3820
+ try {
3821
+ await this.bye("USER_BUSY");
3822
+ } finally {
3823
+ this._signalingStatus$.next("disconnected");
3824
+ this.callSession?.destroy();
3825
+ }
3789
3826
  } else {
3790
3827
  logger$10.debug("[WebRTCManager] Call accepted, sending answer");
3791
3828
  try {
@@ -3815,8 +3852,7 @@ var WebRTCVertoManager = class extends VertoManager {
3815
3852
  userVariables: {
3816
3853
  memberCallId: this.webRtcCallSession.id,
3817
3854
  memberId,
3818
- ...this.webRtcCallSession.options.userVariables,
3819
- ...PreferencesContainer.instance.userVariables
3855
+ ...this.webRtcCallSession.userVariables
3820
3856
  },
3821
3857
  screenShare: rtcPeerConnectionController.isScreenShare,
3822
3858
  additionalDevice: rtcPeerConnectionController.isAdditionalDevice,
@@ -3874,7 +3910,8 @@ var WebRTCVertoManager = class extends VertoManager {
3874
3910
  rtcPeerConnController = new RTCPeerConnectionController({
3875
3911
  ...options,
3876
3912
  ...this.RTCPeerConnectionConfig,
3877
- propose
3913
+ propose,
3914
+ webRTCApiProvider: this.webRTCApiProvider
3878
3915
  }, void 0, this.deviceController);
3879
3916
  this.setupLocalDescriptionHandler(rtcPeerConnController);
3880
3917
  if (propose === "screenshare") this._screenShareId = rtcPeerConnController.id;
@@ -4013,6 +4050,20 @@ var ParticipantFactory = class {
4013
4050
  //#endregion
4014
4051
  //#region src/core/entities/Call.ts
4015
4052
  const logger$9 = require_operators.getLogger();
4053
+ const fromDestinationParams = (destination) => {
4054
+ if (!destination) return {};
4055
+ try {
4056
+ const url = new URL(`destination:${destination}`);
4057
+ const params = {};
4058
+ url.searchParams.forEach((value, key) => {
4059
+ params[key] = value;
4060
+ });
4061
+ return params;
4062
+ } catch (error) {
4063
+ logger$9.warn(`Failed to parse destination URI: ${destination}`, error);
4064
+ return {};
4065
+ }
4066
+ };
4016
4067
  /**
4017
4068
  * Concrete WebRTC call implementation.
4018
4069
  *
@@ -4030,8 +4081,21 @@ var WebRTCCall = class extends Destroyable {
4030
4081
  this._errors$ = this.createSubject();
4031
4082
  this._answered$ = this.createReplaySubject();
4032
4083
  this._holdState = false;
4084
+ this._userVariables$ = this.createBehaviorSubject({ ...PreferencesContainer.instance.userVariables });
4033
4085
  this.id = options.callId ?? (0, uuid.v4)();
4034
4086
  this.to = options.to;
4087
+ this._userVariables$.next({
4088
+ ...this._userVariables$.value,
4089
+ ...fromDestinationParams(options.to),
4090
+ ...options.userVariables
4091
+ });
4092
+ this.subscribeTo(this.webrtcMessages$, (message) => {
4093
+ const userVars = require_operators.getValueFrom(message, "params.userVariables");
4094
+ if (userVars) this._userVariables$.next({
4095
+ ...this._userVariables$.value,
4096
+ ...userVars
4097
+ });
4098
+ });
4035
4099
  const managers = initialization.initializeManagers(this);
4036
4100
  this.vertoManager = managers.vertoManager;
4037
4101
  this.callEventsManager = managers.callEventsManager;
@@ -4245,6 +4309,21 @@ var WebRTCCall = class extends Destroyable {
4245
4309
  get remoteStream() {
4246
4310
  return this.vertoManager.remoteStream;
4247
4311
  }
4312
+ /** Observable of custom user variables associated with the call. */
4313
+ get userVariables$() {
4314
+ return this._userVariables$.asObservable();
4315
+ }
4316
+ /** a copy of the current custom user variables of the call. */
4317
+ get userVariables() {
4318
+ return { ...this._userVariables$.value };
4319
+ }
4320
+ /** Merge current custom user variables of the call. */
4321
+ set userVariables(variables) {
4322
+ this._userVariables$.next({
4323
+ ...this._userVariables$.value,
4324
+ ...variables
4325
+ });
4326
+ }
4248
4327
  /** @internal */
4249
4328
  createParticipant(memberId, selfId) {
4250
4329
  if (memberId === (selfId ?? this.vertoManager.selfId)) return this.participantFactory.createSelfParticipant(memberId);
@@ -4350,7 +4429,6 @@ var WebRTCCall = class extends Destroyable {
4350
4429
  try {
4351
4430
  await this.vertoManager.bye();
4352
4431
  } finally {
4353
- this._status$.next("destroyed");
4354
4432
  this.destroy();
4355
4433
  }
4356
4434
  }
@@ -4389,6 +4467,7 @@ var WebRTCCall = class extends Destroyable {
4389
4467
  }
4390
4468
  /** Destroys the call, releasing all resources and subscriptions. */
4391
4469
  destroy() {
4470
+ this._status$.next("destroyed");
4392
4471
  this.vertoManager.destroy();
4393
4472
  this.callEventsManager.destroy();
4394
4473
  this.participantsMap.clear();
@@ -4403,10 +4482,11 @@ var WebRTCCall = class extends Destroyable {
4403
4482
  * Eliminates circular dependencies by centralizing Call and Manager creation.
4404
4483
  */
4405
4484
  var CallFactory = class {
4406
- constructor(sessionManager, deviceController, attachManager) {
4485
+ constructor(sessionManager, deviceController, attachManager, webRTCApiProvider) {
4407
4486
  this.sessionManager = sessionManager;
4408
4487
  this.deviceController = deviceController;
4409
4488
  this.attachManager = attachManager;
4489
+ this.webRTCApiProvider = webRTCApiProvider;
4410
4490
  }
4411
4491
  /**
4412
4492
  * Create a new WebRTCCall with properly initialized managers
@@ -4415,7 +4495,7 @@ var CallFactory = class {
4415
4495
  return new WebRTCCall(this.sessionManager, options, {
4416
4496
  initializeManagers: (callInstance) => {
4417
4497
  return {
4418
- vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, {
4498
+ vertoManager: new WebRTCVertoManager(callInstance, this.attachManager, this.deviceController, this.webRTCApiProvider, {
4419
4499
  nodeId: options.nodeId,
4420
4500
  onError: (error) => {
4421
4501
  callInstance.emitError(error);
@@ -4441,7 +4521,10 @@ var Fetcher = class {
4441
4521
  this.nextUrl = `${this.endpoint}?${params}`;
4442
4522
  }
4443
4523
  async next() {
4444
- if (!this.nextUrl) return [];
4524
+ if (!this.nextUrl) {
4525
+ this.hasMore = false;
4526
+ return [];
4527
+ }
4445
4528
  const response = await this.http.request({
4446
4529
  ...GET_PARAMS,
4447
4530
  url: this.nextUrl
@@ -4449,6 +4532,7 @@ var Fetcher = class {
4449
4532
  if (response.ok && !!response.body) {
4450
4533
  const result = JSON.parse(response.body);
4451
4534
  this.nextUrl = result.links.next;
4535
+ this.hasMore = !!this.nextUrl;
4452
4536
  return result.data.filter(this.filter).map(this.mapper);
4453
4537
  }
4454
4538
  logger$8.error("Failed to fetch entity");
@@ -4482,10 +4566,11 @@ var EntityCollection = class extends Destroyable {
4482
4566
  this.observablesRegistry.get(data.id)?.next(updated);
4483
4567
  this.values$.next(Array.from(this.collectionData.values()));
4484
4568
  };
4569
+ this._hasMore$ = this.createBehaviorSubject(true);
4485
4570
  this._destroy$ = new rxjs.Subject();
4486
4571
  this.updateSubscription = this.update$.subscribe(this.upsertData);
4487
4572
  this.loading$.next(false);
4488
- this.hasMore$ = (0, rxjs.defer)(() => (0, rxjs.from)(this.init())).pipe((0, rxjs.shareReplay)(1), (0, rxjs.takeUntil)(this._destroy$));
4573
+ this.hasMore$ = (0, rxjs.defer)(() => (0, rxjs.from)(this.init())).pipe((0, rxjs.switchMap)(() => this._hasMore$), (0, rxjs.distinctUntilChanged)(), (0, rxjs.shareReplay)(1), (0, rxjs.takeUntil)(this._destroy$));
4489
4574
  }
4490
4575
  get loading() {
4491
4576
  return this.loading$.value;
@@ -4500,17 +4585,21 @@ var EntityCollection = class extends Destroyable {
4500
4585
  return Array.from(this.collectionData.values());
4501
4586
  }
4502
4587
  async init() {
4503
- if (this.fetchController.hasMore === false) return Promise.resolve(false);
4588
+ if (this.fetchController.hasMore === false) {
4589
+ this._hasMore$.next(false);
4590
+ return;
4591
+ }
4504
4592
  await this.fetchMore();
4505
- return this.fetchController.hasMore ?? true;
4506
4593
  }
4507
4594
  async fetchMore() {
4508
4595
  try {
4509
4596
  this.loading$.next(true);
4510
4597
  (await this.fetchController.next()).forEach(this.upsertData);
4598
+ this._hasMore$.next(this.fetchController.hasMore ?? false);
4511
4599
  this.loading$.next(false);
4512
4600
  } catch (error) {
4513
4601
  logger$8.error(`Failed to fetch initial collection data`, error);
4602
+ this._hasMore$.next(this.fetchController.hasMore ?? false);
4514
4603
  this.loading$.next(false);
4515
4604
  this.onError?.(new require_operators.CollectionFetchError("fetchMore", error));
4516
4605
  }
@@ -4827,7 +4916,7 @@ const getAddressSearchURI = (options) => {
4827
4916
  return name;
4828
4917
  };
4829
4918
  var ClientSessionManager = class extends Destroyable {
4830
- constructor(credential, transport, storage, authorizationStateKey, deviceController, attachManager) {
4919
+ constructor(credential, transport, storage, authorizationStateKey, deviceController, attachManager, webRTCApiProvider) {
4831
4920
  super();
4832
4921
  this.credential = credential;
4833
4922
  this.transport = transport;
@@ -4850,7 +4939,7 @@ var ClientSessionManager = class extends Destroyable {
4850
4939
  this._calls$ = this.createBehaviorSubject({});
4851
4940
  this._iceServers$ = this.createBehaviorSubject([]);
4852
4941
  attachManager.setSession(this);
4853
- this.callFactory = new CallFactory(this, deviceController, attachManager);
4942
+ this.callFactory = new CallFactory(this, deviceController, attachManager, webRTCApiProvider);
4854
4943
  this.initialized$ = (0, rxjs.defer)(() => (0, rxjs.from)(this.init())).pipe((0, rxjs.shareReplay)(1), (0, rxjs.takeUntil)(this.destroyed$));
4855
4944
  }
4856
4945
  get incomingCalls$() {
@@ -5079,7 +5168,8 @@ var ClientSessionManager = class extends Destroyable {
5079
5168
  to: invite.callee_id_number,
5080
5169
  fromName: invite.caller_id_name,
5081
5170
  from: invite.caller_id_number,
5082
- displayDirection: invite.display_direction
5171
+ displayDirection: invite.display_direction,
5172
+ userVariables: invite.userVariables
5083
5173
  });
5084
5174
  await (0, rxjs.firstValueFrom)(callSession.status$);
5085
5175
  this._calls$.next({
@@ -5111,12 +5201,14 @@ var ClientSessionManager = class extends Destroyable {
5111
5201
  try {
5112
5202
  const addressURI = getAddressSearchURI(options);
5113
5203
  let address;
5114
- if (!addressURI.startsWith("+")) {
5204
+ try {
5115
5205
  if (!this._directory) throw new require_operators.DependencyError("Directory not initialized");
5116
5206
  const addressId = await this._directory.findAddressIdByURI(addressURI);
5117
5207
  if (!addressId) throw new require_operators.DependencyError(`Address name: ${addressURI} not found`);
5118
5208
  address = this._directory.get(addressId);
5119
5209
  if (!address) throw new require_operators.DependencyError(`Address ID: ${addressId} not found`);
5210
+ } catch (error) {
5211
+ logger$6.warn(`[Session] Directory lookup failed for ${addressURI}, proceeding with raw URI`);
5120
5212
  }
5121
5213
  const callSession = this.callFactory.createCall(address, { ...options });
5122
5214
  callSession.status$.pipe((0, rxjs.filter)((status) => status === "destroyed"), (0, rxjs.take)(1)).subscribe(() => {
@@ -5737,6 +5829,7 @@ var SignalWire = class extends Destroyable {
5737
5829
  if (this._options.storageImplementation) this._deps.storageImpl = this._options.storageImplementation;
5738
5830
  if (this._options.webSocketConstructor) this._deps.WebSocket = this._options.webSocketConstructor;
5739
5831
  if (this._options.savePreferences) this.preferences.enableSavePreferences(this._deps.storage);
5832
+ if (this._options.webRTCApiProvider) this._deps.webRTCApiProvider = this._options.webRTCApiProvider;
5740
5833
  this._deviceController = this._deps.deviceController;
5741
5834
  if (!this._options.skipDeviceMonitoring) this._deviceController.enableDeviceMonitoring();
5742
5835
  this.subscribeTo(this._deviceController.errors$, (error) => {
@@ -5861,7 +5954,7 @@ var SignalWire = class extends Destroyable {
5861
5954
  };
5862
5955
  this._transport = new TransportManager(this._deps.storage, this._deps.protocolKey, this._deps.WebSocket, PreferencesContainer.instance.relayHost ?? this._deps.relayHost, errorHandler);
5863
5956
  this._attachManager = new AttachManager(this._deps.storage, this._deps.deviceController, PreferencesContainer.instance.reconnectCallsTimeout, this._deps.attachedCallsKey);
5864
- this._clientSession = new ClientSessionManager(this._deps.credential, this._transport, this._deps.storage, this._deps.authorizationStateKey, this._deps.deviceController, this._attachManager);
5957
+ this._clientSession = new ClientSessionManager(this._deps.credential, this._transport, this._deps.storage, this._deps.authorizationStateKey, this._deps.deviceController, this._attachManager, this._deps.webRTCApiProvider);
5865
5958
  this._publicSession = new ClientSessionWrapper(this._clientSession);
5866
5959
  this.subscribeTo(this._clientSession.errors$, (error) => {
5867
5960
  this._errors$.next(error);