@wazo/sdk 0.37.30 → 0.37.34

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.
Files changed (80) hide show
  1. package/dist/wazo-sdk.js +4 -4
  2. package/esm/__tests__/api-client.test.js +30 -0
  3. package/esm/__tests__/web-rtc-client.test.js +57 -6
  4. package/esm/api/calld.js +30 -0
  5. package/esm/api-client.js +30 -0
  6. package/esm/checker/Checker.js +57 -6
  7. package/esm/checker/checks/aor.js +30 -0
  8. package/esm/checker/checks/api.js +30 -0
  9. package/esm/checker/checks/index.js +57 -6
  10. package/esm/checker/checks/webrtc-transport.js +57 -6
  11. package/esm/checker/checks/webrtc.js +57 -6
  12. package/esm/domain/AdHocAPIConference.js +30 -0
  13. package/esm/domain/CallSession.js +9 -1
  14. package/esm/domain/Features.js +30 -0
  15. package/esm/domain/MeetingStatus.js +29 -0
  16. package/esm/domain/Phone/CTIPhone.js +39 -1
  17. package/esm/domain/Phone/WebRTCPhone.js +43 -8
  18. package/esm/domain/__tests__/CallSession.test.js +41 -5
  19. package/esm/domain/__tests__/IndirectTransfer.test.js +9 -1
  20. package/esm/domain/__tests__/Room.test.js +9 -1
  21. package/esm/index.js +100 -14
  22. package/esm/service/CallApi.js +30 -0
  23. package/esm/service/getApiClient.js +30 -0
  24. package/esm/simple/Auth.js +100 -14
  25. package/esm/simple/Configuration.js +100 -14
  26. package/esm/simple/Directory.js +30 -0
  27. package/esm/simple/Phone.js +100 -14
  28. package/esm/simple/index.js +100 -14
  29. package/esm/simple/room/LocalParticipant.js +100 -14
  30. package/esm/simple/room/Participant.js +100 -14
  31. package/esm/simple/room/RemoteParticipant.js +100 -14
  32. package/esm/simple/room/Room.js +100 -14
  33. package/esm/simple/room/SipRoom.js +100 -14
  34. package/esm/simple/room/Stream.js +100 -14
  35. package/esm/simple/room/__tests__/RemoteParticipant.test.js +100 -14
  36. package/esm/simple/utils.js +100 -14
  37. package/esm/web-rtc-client.js +57 -6
  38. package/lib/__tests__/api-client.test.js +30 -0
  39. package/lib/__tests__/web-rtc-client.test.js +57 -6
  40. package/lib/api/calld.js +30 -0
  41. package/lib/api-client.js +30 -0
  42. package/lib/checker/Checker.js +57 -6
  43. package/lib/checker/checks/aor.js +30 -0
  44. package/lib/checker/checks/api.js +30 -0
  45. package/lib/checker/checks/index.js +57 -6
  46. package/lib/checker/checks/webrtc-transport.js +57 -6
  47. package/lib/checker/checks/webrtc.js +57 -6
  48. package/lib/domain/AdHocAPIConference.js +30 -0
  49. package/lib/domain/CallSession.js +9 -1
  50. package/lib/domain/Features.js +30 -0
  51. package/lib/domain/MeetingStatus.js +31 -0
  52. package/lib/domain/Phone/CTIPhone.js +39 -1
  53. package/lib/domain/Phone/WebRTCPhone.js +43 -8
  54. package/lib/domain/__tests__/CallSession.test.js +41 -5
  55. package/lib/domain/__tests__/IndirectTransfer.test.js +9 -1
  56. package/lib/domain/__tests__/Room.test.js +9 -1
  57. package/lib/index.js +100 -14
  58. package/lib/service/CallApi.js +30 -0
  59. package/lib/service/getApiClient.js +30 -0
  60. package/lib/simple/Auth.js +100 -14
  61. package/lib/simple/Configuration.js +100 -14
  62. package/lib/simple/Directory.js +30 -0
  63. package/lib/simple/Phone.js +100 -14
  64. package/lib/simple/index.js +100 -14
  65. package/lib/simple/room/LocalParticipant.js +100 -14
  66. package/lib/simple/room/Participant.js +100 -14
  67. package/lib/simple/room/RemoteParticipant.js +100 -14
  68. package/lib/simple/room/Room.js +100 -14
  69. package/lib/simple/room/SipRoom.js +100 -14
  70. package/lib/simple/room/Stream.js +100 -14
  71. package/lib/simple/room/__tests__/RemoteParticipant.test.js +100 -14
  72. package/lib/simple/utils.js +100 -14
  73. package/lib/web-rtc-client.js +57 -6
  74. package/package.json +1 -1
  75. package/src/api/calld.js +3 -0
  76. package/src/domain/CallSession.js +9 -1
  77. package/src/domain/MeetingStatus.js +27 -0
  78. package/src/domain/Phone/WebRTCPhone.js +34 -7
  79. package/src/domain/__tests__/CallSession.test.js +32 -4
  80. package/src/web-rtc-client.js +27 -6
@@ -3584,13 +3584,21 @@ class CallSession {
3584
3584
 
3585
3585
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
3586
3586
  set answered(value ) {
3587
- this.answerTime = value ? new Date() : null;
3587
+ this.answerTime = value ? this.answerTime || new Date() : null;
3588
3588
  }
3589
3589
 
3590
3590
  get answered() {
3591
3591
  return !!this.answerTime;
3592
3592
  }
3593
3593
 
3594
+ toJSON() {
3595
+ const jsonObj = { ...this };
3596
+ // $FlowFixMe
3597
+ jsonObj.answered = this.answered;
3598
+
3599
+ return jsonObj;
3600
+ }
3601
+
3594
3602
  static parseCall(call ) {
3595
3603
  return new CallSession({
3596
3604
  callId: call.id,
@@ -4842,6 +4850,34 @@ var chatdMethods = (client , baseUrl ) => ({
4842
4850
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4843
4851
  });
4844
4852
 
4853
+ //
4854
+
4855
+
4856
+
4857
+
4858
+
4859
+ class MeetingStatus {
4860
+
4861
+
4862
+
4863
+
4864
+ constructor({
4865
+ full,
4866
+ } = {}) {
4867
+ this.full = full;
4868
+
4869
+ // Useful to compare instead of instanceof with minified code
4870
+ this.type = 'MeetingStatus';
4871
+ }
4872
+
4873
+ static parse(plain ) {
4874
+ return new MeetingStatus({
4875
+ full: plain.full,
4876
+ });
4877
+ }
4878
+
4879
+ }
4880
+
4845
4881
  /* */
4846
4882
 
4847
4883
 
@@ -5036,6 +5072,8 @@ var calldMethods = (client , baseUrl ) => ({
5036
5072
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
5037
5073
  ApiRequester.successResponseParser),
5038
5074
 
5075
+ guestGetMeetingStatus: (meetingUuid ) =>
5076
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
5039
5077
  });
5040
5078
 
5041
5079
  //
@@ -6361,7 +6399,7 @@ const events$1 = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
6361
6399
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
6362
6400
  class CanceledCallError extends Error {}
6363
6401
 
6364
- const MAX_REGISTER_TRIES = 10;
6402
+ const MAX_REGISTER_TRIES = 5;
6365
6403
 
6366
6404
 
6367
6405
 
@@ -6415,6 +6453,7 @@ class WebRTCClient extends Emitter {
6415
6453
 
6416
6454
 
6417
6455
 
6456
+
6418
6457
 
6419
6458
  // sugar
6420
6459
 
@@ -6477,6 +6516,7 @@ class WebRTCClient extends Emitter {
6477
6516
  this.conferences = {};
6478
6517
  this.networkMonitoringInterval = {};
6479
6518
  this.sessionNetworkStats = {};
6519
+ this.forceClosed = false;
6480
6520
 
6481
6521
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
6482
6522
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -6598,6 +6638,8 @@ class WebRTCClient extends Emitter {
6598
6638
  skipRegister: this.skipRegister,
6599
6639
  };
6600
6640
 
6641
+ this.forceClosed = false;
6642
+
6601
6643
  if (this.skipRegister) {
6602
6644
  logger$9.info('sdk webrtc skip register...', logInfo);
6603
6645
  return Promise.resolve();
@@ -6622,7 +6664,15 @@ class WebRTCClient extends Emitter {
6622
6664
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
6623
6665
 
6624
6666
  const onRegisterFailed = () => {
6625
- logger$9.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
6667
+ logger$9.info('sdk webrtc registering failed', {
6668
+ tries,
6669
+ registerer: !!this.registerer,
6670
+ forceClosed: this.forceClosed,
6671
+ });
6672
+ if (this.forceClosed) {
6673
+ return;
6674
+ }
6675
+
6626
6676
  this.connectionPromise = null;
6627
6677
  if (this.registerer && this.registerer.waiting) {
6628
6678
  this.registerer.waitingToggle(false);
@@ -6900,8 +6950,9 @@ class WebRTCClient extends Emitter {
6900
6950
  }
6901
6951
  }
6902
6952
 
6903
- async close() {
6904
- logger$9.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
6953
+ async close(force = false) {
6954
+ logger$9.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
6955
+ this.forceClosed = force;
6905
6956
  this._cleanupMedia();
6906
6957
  this.connectionPromise = null;
6907
6958
 
@@ -6920,7 +6971,7 @@ class WebRTCClient extends Emitter {
6920
6971
  this.userAgent.delegate = null;
6921
6972
  this.userAgent.stateChange.removeAllListeners();
6922
6973
 
6923
- await this._disconnectTransport();
6974
+ await this._disconnectTransport(force);
6924
6975
 
6925
6976
  this._cleanupRegister();
6926
6977
 
@@ -8356,7 +8407,15 @@ class WebRTCClient extends Emitter {
8356
8407
  return UserAgent.makeURI(`sip:${target}@${this.config.host}`);
8357
8408
  }
8358
8409
 
8359
- async _disconnectTransport() {
8410
+ async _disconnectTransport(force = false) {
8411
+ if (force) {
8412
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
8413
+ this.userAgent.transport.disconnectResolve = () => {};
8414
+ // eslint-disable-next-line
8415
+ this.userAgent.transport._ws.close(1000);
8416
+ return;
8417
+ }
8418
+
8360
8419
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
8361
8420
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
8362
8421
  try {
@@ -9093,6 +9152,7 @@ class WebRTCPhone extends Emitter {
9093
9152
  }
9094
9153
 
9095
9154
  callSession.answerTime = new Date();
9155
+ this._updateCallSession(callSession);
9096
9156
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
9097
9157
 
9098
9158
  return callSession;
@@ -9302,6 +9362,12 @@ class WebRTCPhone extends Emitter {
9302
9362
  return Promise.resolve(null);
9303
9363
  }
9304
9364
 
9365
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
9366
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
9367
+ logger$8.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
9368
+ return Promise.resolve(null);
9369
+ }
9370
+
9305
9371
  if (callSession.getId() in this.acceptedSessions) {
9306
9372
  logger$8.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
9307
9373
  return Promise.resolve(callSession.sipCallId);
@@ -9309,6 +9375,7 @@ class WebRTCPhone extends Emitter {
9309
9375
 
9310
9376
  this.shouldSendReinvite = false;
9311
9377
  callSession.answerTime = new Date();
9378
+ this._updateCallSession(callSession);
9312
9379
  this.acceptedSessions[callSession.getId()] = true;
9313
9380
 
9314
9381
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -9408,7 +9475,7 @@ class WebRTCPhone extends Emitter {
9408
9475
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
9409
9476
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
9410
9477
  logger$8.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
9411
- this.holdSipSession(this.currentSipSession, callSession);
9478
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
9412
9479
  }
9413
9480
 
9414
9481
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -9619,6 +9686,7 @@ class WebRTCPhone extends Emitter {
9619
9686
  }
9620
9687
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
9621
9688
  callSession.creationTime = new Date();
9689
+ this._updateCallSession(callSession);
9622
9690
 
9623
9691
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
9624
9692
 
@@ -9760,7 +9828,7 @@ class WebRTCPhone extends Emitter {
9760
9828
  return this.client && this.client.isConference(callSession.sipCallId);
9761
9829
  }
9762
9830
 
9763
- async close() {
9831
+ async close(force = false) {
9764
9832
  logger$8.info('WebRTC close');
9765
9833
 
9766
9834
  try {
@@ -9772,7 +9840,7 @@ class WebRTCPhone extends Emitter {
9772
9840
  logger$8.error('WebRTC close, unregister error', e);
9773
9841
  }
9774
9842
 
9775
- this.client.close();
9843
+ this.client.close(force);
9776
9844
  this.unbind();
9777
9845
 
9778
9846
  this.incomingSessions = [];
@@ -9898,6 +9966,8 @@ class WebRTCPhone extends Emitter {
9898
9966
  }
9899
9967
 
9900
9968
  callSession.creationTime = new Date();
9969
+ this._updateCallSession(callSession);
9970
+
9901
9971
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
9902
9972
  });
9903
9973
 
@@ -9922,9 +9992,17 @@ class WebRTCPhone extends Emitter {
9922
9992
  });
9923
9993
 
9924
9994
  this.client.on(this.client.ACCEPTED, (sipSession ) => {
9925
- logger$8.info('WebRTC call accepted', { sipId: sipSession.id });
9995
+ const sessionId = this.getSipSessionId(sipSession);
9996
+ const hasSession = sessionId in this.callSessions;
9926
9997
 
9927
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
9998
+ logger$8.info('WebRTC call accepted', { sipId: sessionId, hasSession });
9999
+
10000
+ if (!hasSession) {
10001
+ logger$8.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
10002
+ return;
10003
+ }
10004
+
10005
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
9928
10006
 
9929
10007
  if (this.audioOutputDeviceId) {
9930
10008
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -9941,6 +10019,8 @@ class WebRTCPhone extends Emitter {
9941
10019
  logger$8.info('WebRTC call rejected', session.id);
9942
10020
  const callSession = this._createCallSession(session);
9943
10021
  callSession.endTime = new Date();
10022
+ this._updateCallSession(callSession);
10023
+
9944
10024
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
9945
10025
  });
9946
10026
 
@@ -10058,9 +10138,14 @@ class WebRTCPhone extends Emitter {
10058
10138
  return this.client.getSipSessionId(sipSession);
10059
10139
  }
10060
10140
 
10061
- _updateCallSession(callSession ) {
10141
+ getSipSessionIdFromCallSession(callSession ) {
10062
10142
  const sipSession = this.findSipSession(callSession);
10063
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
10143
+
10144
+ return sipSession ? this.getSipSessionId(sipSession) : null;
10145
+ }
10146
+
10147
+ _updateCallSession(callSession ) {
10148
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
10064
10149
 
10065
10150
  if (sipSessionId) {
10066
10151
  this.callSessions[sipSessionId] = callSession;
@@ -10165,6 +10250,7 @@ class WebRTCPhone extends Emitter {
10165
10250
  displayName: identity ? identity.displayName || number : number,
10166
10251
  startTime: fromSession ? fromSession.startTime : new Date(),
10167
10252
  creationTime: fromSession ? fromSession.creationTime : null,
10253
+ answerTime: fromSession ? fromSession.answerTime : null,
10168
10254
  endTime: fromSession ? fromSession.endTime : null,
10169
10255
  answered: state === SessionState.Established,
10170
10256
  paused: this.client.isCallHeld(sipSession),
@@ -4016,6 +4016,34 @@ var chatdMethods = (client , baseUrl ) => ({
4016
4016
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4017
4017
  });
4018
4018
 
4019
+ //
4020
+
4021
+
4022
+
4023
+
4024
+
4025
+ class MeetingStatus {
4026
+
4027
+
4028
+
4029
+
4030
+ constructor({
4031
+ full,
4032
+ } = {}) {
4033
+ this.full = full;
4034
+
4035
+ // Useful to compare instead of instanceof with minified code
4036
+ this.type = 'MeetingStatus';
4037
+ }
4038
+
4039
+ static parse(plain ) {
4040
+ return new MeetingStatus({
4041
+ full: plain.full,
4042
+ });
4043
+ }
4044
+
4045
+ }
4046
+
4019
4047
  /* */
4020
4048
 
4021
4049
 
@@ -4210,6 +4238,8 @@ var calldMethods = (client , baseUrl ) => ({
4210
4238
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
4211
4239
  ApiRequester.successResponseParser),
4212
4240
 
4241
+ guestGetMeetingStatus: (meetingUuid ) =>
4242
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
4213
4243
  });
4214
4244
 
4215
4245
  //
@@ -584,13 +584,21 @@ class CallSession {
584
584
 
585
585
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
586
586
  set answered(value ) {
587
- this.answerTime = value ? new Date() : null;
587
+ this.answerTime = value ? this.answerTime || new Date() : null;
588
588
  }
589
589
 
590
590
  get answered() {
591
591
  return !!this.answerTime;
592
592
  }
593
593
 
594
+ toJSON() {
595
+ const jsonObj = { ...this };
596
+ // $FlowFixMe
597
+ jsonObj.answered = this.answered;
598
+
599
+ return jsonObj;
600
+ }
601
+
594
602
  static parseCall(call ) {
595
603
  return new CallSession({
596
604
  callId: call.id,
@@ -4842,6 +4850,34 @@ var chatdMethods = (client , baseUrl ) => ({
4842
4850
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4843
4851
  });
4844
4852
 
4853
+ //
4854
+
4855
+
4856
+
4857
+
4858
+
4859
+ class MeetingStatus {
4860
+
4861
+
4862
+
4863
+
4864
+ constructor({
4865
+ full,
4866
+ } = {}) {
4867
+ this.full = full;
4868
+
4869
+ // Useful to compare instead of instanceof with minified code
4870
+ this.type = 'MeetingStatus';
4871
+ }
4872
+
4873
+ static parse(plain ) {
4874
+ return new MeetingStatus({
4875
+ full: plain.full,
4876
+ });
4877
+ }
4878
+
4879
+ }
4880
+
4845
4881
  /* */
4846
4882
 
4847
4883
 
@@ -5036,6 +5072,8 @@ var calldMethods = (client , baseUrl ) => ({
5036
5072
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
5037
5073
  ApiRequester.successResponseParser),
5038
5074
 
5075
+ guestGetMeetingStatus: (meetingUuid ) =>
5076
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
5039
5077
  });
5040
5078
 
5041
5079
  //
@@ -6361,7 +6399,7 @@ const events$1 = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
6361
6399
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
6362
6400
  class CanceledCallError extends Error {}
6363
6401
 
6364
- const MAX_REGISTER_TRIES = 10;
6402
+ const MAX_REGISTER_TRIES = 5;
6365
6403
 
6366
6404
 
6367
6405
 
@@ -6415,6 +6453,7 @@ class WebRTCClient extends Emitter {
6415
6453
 
6416
6454
 
6417
6455
 
6456
+
6418
6457
 
6419
6458
  // sugar
6420
6459
 
@@ -6477,6 +6516,7 @@ class WebRTCClient extends Emitter {
6477
6516
  this.conferences = {};
6478
6517
  this.networkMonitoringInterval = {};
6479
6518
  this.sessionNetworkStats = {};
6519
+ this.forceClosed = false;
6480
6520
 
6481
6521
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
6482
6522
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -6598,6 +6638,8 @@ class WebRTCClient extends Emitter {
6598
6638
  skipRegister: this.skipRegister,
6599
6639
  };
6600
6640
 
6641
+ this.forceClosed = false;
6642
+
6601
6643
  if (this.skipRegister) {
6602
6644
  logger$9.info('sdk webrtc skip register...', logInfo);
6603
6645
  return Promise.resolve();
@@ -6622,7 +6664,15 @@ class WebRTCClient extends Emitter {
6622
6664
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
6623
6665
 
6624
6666
  const onRegisterFailed = () => {
6625
- logger$9.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
6667
+ logger$9.info('sdk webrtc registering failed', {
6668
+ tries,
6669
+ registerer: !!this.registerer,
6670
+ forceClosed: this.forceClosed,
6671
+ });
6672
+ if (this.forceClosed) {
6673
+ return;
6674
+ }
6675
+
6626
6676
  this.connectionPromise = null;
6627
6677
  if (this.registerer && this.registerer.waiting) {
6628
6678
  this.registerer.waitingToggle(false);
@@ -6900,8 +6950,9 @@ class WebRTCClient extends Emitter {
6900
6950
  }
6901
6951
  }
6902
6952
 
6903
- async close() {
6904
- logger$9.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
6953
+ async close(force = false) {
6954
+ logger$9.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
6955
+ this.forceClosed = force;
6905
6956
  this._cleanupMedia();
6906
6957
  this.connectionPromise = null;
6907
6958
 
@@ -6920,7 +6971,7 @@ class WebRTCClient extends Emitter {
6920
6971
  this.userAgent.delegate = null;
6921
6972
  this.userAgent.stateChange.removeAllListeners();
6922
6973
 
6923
- await this._disconnectTransport();
6974
+ await this._disconnectTransport(force);
6924
6975
 
6925
6976
  this._cleanupRegister();
6926
6977
 
@@ -8356,7 +8407,15 @@ class WebRTCClient extends Emitter {
8356
8407
  return UserAgent.makeURI(`sip:${target}@${this.config.host}`);
8357
8408
  }
8358
8409
 
8359
- async _disconnectTransport() {
8410
+ async _disconnectTransport(force = false) {
8411
+ if (force) {
8412
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
8413
+ this.userAgent.transport.disconnectResolve = () => {};
8414
+ // eslint-disable-next-line
8415
+ this.userAgent.transport._ws.close(1000);
8416
+ return;
8417
+ }
8418
+
8360
8419
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
8361
8420
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
8362
8421
  try {
@@ -9093,6 +9152,7 @@ class WebRTCPhone extends Emitter {
9093
9152
  }
9094
9153
 
9095
9154
  callSession.answerTime = new Date();
9155
+ this._updateCallSession(callSession);
9096
9156
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
9097
9157
 
9098
9158
  return callSession;
@@ -9302,6 +9362,12 @@ class WebRTCPhone extends Emitter {
9302
9362
  return Promise.resolve(null);
9303
9363
  }
9304
9364
 
9365
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
9366
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
9367
+ logger$8.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
9368
+ return Promise.resolve(null);
9369
+ }
9370
+
9305
9371
  if (callSession.getId() in this.acceptedSessions) {
9306
9372
  logger$8.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
9307
9373
  return Promise.resolve(callSession.sipCallId);
@@ -9309,6 +9375,7 @@ class WebRTCPhone extends Emitter {
9309
9375
 
9310
9376
  this.shouldSendReinvite = false;
9311
9377
  callSession.answerTime = new Date();
9378
+ this._updateCallSession(callSession);
9312
9379
  this.acceptedSessions[callSession.getId()] = true;
9313
9380
 
9314
9381
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -9408,7 +9475,7 @@ class WebRTCPhone extends Emitter {
9408
9475
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
9409
9476
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
9410
9477
  logger$8.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
9411
- this.holdSipSession(this.currentSipSession, callSession);
9478
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
9412
9479
  }
9413
9480
 
9414
9481
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -9619,6 +9686,7 @@ class WebRTCPhone extends Emitter {
9619
9686
  }
9620
9687
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
9621
9688
  callSession.creationTime = new Date();
9689
+ this._updateCallSession(callSession);
9622
9690
 
9623
9691
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
9624
9692
 
@@ -9760,7 +9828,7 @@ class WebRTCPhone extends Emitter {
9760
9828
  return this.client && this.client.isConference(callSession.sipCallId);
9761
9829
  }
9762
9830
 
9763
- async close() {
9831
+ async close(force = false) {
9764
9832
  logger$8.info('WebRTC close');
9765
9833
 
9766
9834
  try {
@@ -9772,7 +9840,7 @@ class WebRTCPhone extends Emitter {
9772
9840
  logger$8.error('WebRTC close, unregister error', e);
9773
9841
  }
9774
9842
 
9775
- this.client.close();
9843
+ this.client.close(force);
9776
9844
  this.unbind();
9777
9845
 
9778
9846
  this.incomingSessions = [];
@@ -9898,6 +9966,8 @@ class WebRTCPhone extends Emitter {
9898
9966
  }
9899
9967
 
9900
9968
  callSession.creationTime = new Date();
9969
+ this._updateCallSession(callSession);
9970
+
9901
9971
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
9902
9972
  });
9903
9973
 
@@ -9922,9 +9992,17 @@ class WebRTCPhone extends Emitter {
9922
9992
  });
9923
9993
 
9924
9994
  this.client.on(this.client.ACCEPTED, (sipSession ) => {
9925
- logger$8.info('WebRTC call accepted', { sipId: sipSession.id });
9995
+ const sessionId = this.getSipSessionId(sipSession);
9996
+ const hasSession = sessionId in this.callSessions;
9926
9997
 
9927
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
9998
+ logger$8.info('WebRTC call accepted', { sipId: sessionId, hasSession });
9999
+
10000
+ if (!hasSession) {
10001
+ logger$8.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
10002
+ return;
10003
+ }
10004
+
10005
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
9928
10006
 
9929
10007
  if (this.audioOutputDeviceId) {
9930
10008
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -9941,6 +10019,8 @@ class WebRTCPhone extends Emitter {
9941
10019
  logger$8.info('WebRTC call rejected', session.id);
9942
10020
  const callSession = this._createCallSession(session);
9943
10021
  callSession.endTime = new Date();
10022
+ this._updateCallSession(callSession);
10023
+
9944
10024
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
9945
10025
  });
9946
10026
 
@@ -10058,9 +10138,14 @@ class WebRTCPhone extends Emitter {
10058
10138
  return this.client.getSipSessionId(sipSession);
10059
10139
  }
10060
10140
 
10061
- _updateCallSession(callSession ) {
10141
+ getSipSessionIdFromCallSession(callSession ) {
10062
10142
  const sipSession = this.findSipSession(callSession);
10063
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
10143
+
10144
+ return sipSession ? this.getSipSessionId(sipSession) : null;
10145
+ }
10146
+
10147
+ _updateCallSession(callSession ) {
10148
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
10064
10149
 
10065
10150
  if (sipSessionId) {
10066
10151
  this.callSessions[sipSessionId] = callSession;
@@ -10165,6 +10250,7 @@ class WebRTCPhone extends Emitter {
10165
10250
  displayName: identity ? identity.displayName || number : number,
10166
10251
  startTime: fromSession ? fromSession.startTime : new Date(),
10167
10252
  creationTime: fromSession ? fromSession.creationTime : null,
10253
+ answerTime: fromSession ? fromSession.answerTime : null,
10168
10254
  endTime: fromSession ? fromSession.endTime : null,
10169
10255
  answered: state === SessionState.Established,
10170
10256
  paused: this.client.isCallHeld(sipSession),