@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
@@ -594,13 +594,21 @@ class CallSession {
594
594
 
595
595
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
596
596
  set answered(value ) {
597
- this.answerTime = value ? new Date() : null;
597
+ this.answerTime = value ? this.answerTime || new Date() : null;
598
598
  }
599
599
 
600
600
  get answered() {
601
601
  return !!this.answerTime;
602
602
  }
603
603
 
604
+ toJSON() {
605
+ const jsonObj = { ...this };
606
+ // $FlowFixMe
607
+ jsonObj.answered = this.answered;
608
+
609
+ return jsonObj;
610
+ }
611
+
604
612
  static parseCall(call ) {
605
613
  return new CallSession({
606
614
  callId: call.id,
@@ -4852,6 +4860,34 @@ var chatdMethods = (client , baseUrl ) => ({
4852
4860
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4853
4861
  });
4854
4862
 
4863
+ //
4864
+
4865
+
4866
+
4867
+
4868
+
4869
+ class MeetingStatus {
4870
+
4871
+
4872
+
4873
+
4874
+ constructor({
4875
+ full,
4876
+ } = {}) {
4877
+ this.full = full;
4878
+
4879
+ // Useful to compare instead of instanceof with minified code
4880
+ this.type = 'MeetingStatus';
4881
+ }
4882
+
4883
+ static parse(plain ) {
4884
+ return new MeetingStatus({
4885
+ full: plain.full,
4886
+ });
4887
+ }
4888
+
4889
+ }
4890
+
4855
4891
  /* */
4856
4892
 
4857
4893
 
@@ -5046,6 +5082,8 @@ var calldMethods = (client , baseUrl ) => ({
5046
5082
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
5047
5083
  ApiRequester.successResponseParser),
5048
5084
 
5085
+ guestGetMeetingStatus: (meetingUuid ) =>
5086
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
5049
5087
  });
5050
5088
 
5051
5089
  //
@@ -6371,7 +6409,7 @@ const events$1 = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
6371
6409
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
6372
6410
  class CanceledCallError extends Error {}
6373
6411
 
6374
- const MAX_REGISTER_TRIES = 10;
6412
+ const MAX_REGISTER_TRIES = 5;
6375
6413
 
6376
6414
 
6377
6415
 
@@ -6425,6 +6463,7 @@ class WebRTCClient extends Emitter {
6425
6463
 
6426
6464
 
6427
6465
 
6466
+
6428
6467
 
6429
6468
  // sugar
6430
6469
 
@@ -6487,6 +6526,7 @@ class WebRTCClient extends Emitter {
6487
6526
  this.conferences = {};
6488
6527
  this.networkMonitoringInterval = {};
6489
6528
  this.sessionNetworkStats = {};
6529
+ this.forceClosed = false;
6490
6530
 
6491
6531
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
6492
6532
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -6608,6 +6648,8 @@ class WebRTCClient extends Emitter {
6608
6648
  skipRegister: this.skipRegister,
6609
6649
  };
6610
6650
 
6651
+ this.forceClosed = false;
6652
+
6611
6653
  if (this.skipRegister) {
6612
6654
  logger$9.info('sdk webrtc skip register...', logInfo);
6613
6655
  return Promise.resolve();
@@ -6632,7 +6674,15 @@ class WebRTCClient extends Emitter {
6632
6674
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
6633
6675
 
6634
6676
  const onRegisterFailed = () => {
6635
- logger$9.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
6677
+ logger$9.info('sdk webrtc registering failed', {
6678
+ tries,
6679
+ registerer: !!this.registerer,
6680
+ forceClosed: this.forceClosed,
6681
+ });
6682
+ if (this.forceClosed) {
6683
+ return;
6684
+ }
6685
+
6636
6686
  this.connectionPromise = null;
6637
6687
  if (this.registerer && this.registerer.waiting) {
6638
6688
  this.registerer.waitingToggle(false);
@@ -6910,8 +6960,9 @@ class WebRTCClient extends Emitter {
6910
6960
  }
6911
6961
  }
6912
6962
 
6913
- async close() {
6914
- logger$9.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
6963
+ async close(force = false) {
6964
+ logger$9.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
6965
+ this.forceClosed = force;
6915
6966
  this._cleanupMedia();
6916
6967
  this.connectionPromise = null;
6917
6968
 
@@ -6930,7 +6981,7 @@ class WebRTCClient extends Emitter {
6930
6981
  this.userAgent.delegate = null;
6931
6982
  this.userAgent.stateChange.removeAllListeners();
6932
6983
 
6933
- await this._disconnectTransport();
6984
+ await this._disconnectTransport(force);
6934
6985
 
6935
6986
  this._cleanupRegister();
6936
6987
 
@@ -8366,7 +8417,15 @@ class WebRTCClient extends Emitter {
8366
8417
  return userAgent.UserAgent.makeURI(`sip:${target}@${this.config.host}`);
8367
8418
  }
8368
8419
 
8369
- async _disconnectTransport() {
8420
+ async _disconnectTransport(force = false) {
8421
+ if (force) {
8422
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
8423
+ this.userAgent.transport.disconnectResolve = () => {};
8424
+ // eslint-disable-next-line
8425
+ this.userAgent.transport._ws.close(1000);
8426
+ return;
8427
+ }
8428
+
8370
8429
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
8371
8430
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
8372
8431
  try {
@@ -9103,6 +9162,7 @@ class WebRTCPhone extends Emitter {
9103
9162
  }
9104
9163
 
9105
9164
  callSession.answerTime = new Date();
9165
+ this._updateCallSession(callSession);
9106
9166
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
9107
9167
 
9108
9168
  return callSession;
@@ -9312,6 +9372,12 @@ class WebRTCPhone extends Emitter {
9312
9372
  return Promise.resolve(null);
9313
9373
  }
9314
9374
 
9375
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
9376
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
9377
+ logger$8.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
9378
+ return Promise.resolve(null);
9379
+ }
9380
+
9315
9381
  if (callSession.getId() in this.acceptedSessions) {
9316
9382
  logger$8.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
9317
9383
  return Promise.resolve(callSession.sipCallId);
@@ -9319,6 +9385,7 @@ class WebRTCPhone extends Emitter {
9319
9385
 
9320
9386
  this.shouldSendReinvite = false;
9321
9387
  callSession.answerTime = new Date();
9388
+ this._updateCallSession(callSession);
9322
9389
  this.acceptedSessions[callSession.getId()] = true;
9323
9390
 
9324
9391
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -9418,7 +9485,7 @@ class WebRTCPhone extends Emitter {
9418
9485
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
9419
9486
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
9420
9487
  logger$8.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
9421
- this.holdSipSession(this.currentSipSession, callSession);
9488
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
9422
9489
  }
9423
9490
 
9424
9491
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -9629,6 +9696,7 @@ class WebRTCPhone extends Emitter {
9629
9696
  }
9630
9697
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
9631
9698
  callSession.creationTime = new Date();
9699
+ this._updateCallSession(callSession);
9632
9700
 
9633
9701
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
9634
9702
 
@@ -9770,7 +9838,7 @@ class WebRTCPhone extends Emitter {
9770
9838
  return this.client && this.client.isConference(callSession.sipCallId);
9771
9839
  }
9772
9840
 
9773
- async close() {
9841
+ async close(force = false) {
9774
9842
  logger$8.info('WebRTC close');
9775
9843
 
9776
9844
  try {
@@ -9782,7 +9850,7 @@ class WebRTCPhone extends Emitter {
9782
9850
  logger$8.error('WebRTC close, unregister error', e);
9783
9851
  }
9784
9852
 
9785
- this.client.close();
9853
+ this.client.close(force);
9786
9854
  this.unbind();
9787
9855
 
9788
9856
  this.incomingSessions = [];
@@ -9908,6 +9976,8 @@ class WebRTCPhone extends Emitter {
9908
9976
  }
9909
9977
 
9910
9978
  callSession.creationTime = new Date();
9979
+ this._updateCallSession(callSession);
9980
+
9911
9981
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
9912
9982
  });
9913
9983
 
@@ -9932,9 +10002,17 @@ class WebRTCPhone extends Emitter {
9932
10002
  });
9933
10003
 
9934
10004
  this.client.on(this.client.ACCEPTED, (sipSession ) => {
9935
- logger$8.info('WebRTC call accepted', { sipId: sipSession.id });
10005
+ const sessionId = this.getSipSessionId(sipSession);
10006
+ const hasSession = sessionId in this.callSessions;
9936
10007
 
9937
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
10008
+ logger$8.info('WebRTC call accepted', { sipId: sessionId, hasSession });
10009
+
10010
+ if (!hasSession) {
10011
+ logger$8.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
10012
+ return;
10013
+ }
10014
+
10015
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
9938
10016
 
9939
10017
  if (this.audioOutputDeviceId) {
9940
10018
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -9951,6 +10029,8 @@ class WebRTCPhone extends Emitter {
9951
10029
  logger$8.info('WebRTC call rejected', session.id);
9952
10030
  const callSession = this._createCallSession(session);
9953
10031
  callSession.endTime = new Date();
10032
+ this._updateCallSession(callSession);
10033
+
9954
10034
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
9955
10035
  });
9956
10036
 
@@ -10068,9 +10148,14 @@ class WebRTCPhone extends Emitter {
10068
10148
  return this.client.getSipSessionId(sipSession);
10069
10149
  }
10070
10150
 
10071
- _updateCallSession(callSession ) {
10151
+ getSipSessionIdFromCallSession(callSession ) {
10072
10152
  const sipSession = this.findSipSession(callSession);
10073
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
10153
+
10154
+ return sipSession ? this.getSipSessionId(sipSession) : null;
10155
+ }
10156
+
10157
+ _updateCallSession(callSession ) {
10158
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
10074
10159
 
10075
10160
  if (sipSessionId) {
10076
10161
  this.callSessions[sipSessionId] = callSession;
@@ -10175,6 +10260,7 @@ class WebRTCPhone extends Emitter {
10175
10260
  displayName: identity ? identity.displayName || number : number,
10176
10261
  startTime: fromSession ? fromSession.startTime : new Date(),
10177
10262
  creationTime: fromSession ? fromSession.creationTime : null,
10263
+ answerTime: fromSession ? fromSession.answerTime : null,
10178
10264
  endTime: fromSession ? fromSession.endTime : null,
10179
10265
  answered: state === sessionState.SessionState.Established,
10180
10266
  paused: this.client.isCallHeld(sipSession),
@@ -3594,13 +3594,21 @@ class CallSession {
3594
3594
 
3595
3595
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
3596
3596
  set answered(value ) {
3597
- this.answerTime = value ? new Date() : null;
3597
+ this.answerTime = value ? this.answerTime || new Date() : null;
3598
3598
  }
3599
3599
 
3600
3600
  get answered() {
3601
3601
  return !!this.answerTime;
3602
3602
  }
3603
3603
 
3604
+ toJSON() {
3605
+ const jsonObj = { ...this };
3606
+ // $FlowFixMe
3607
+ jsonObj.answered = this.answered;
3608
+
3609
+ return jsonObj;
3610
+ }
3611
+
3604
3612
  static parseCall(call ) {
3605
3613
  return new CallSession({
3606
3614
  callId: call.id,
@@ -4852,6 +4860,34 @@ var chatdMethods = (client , baseUrl ) => ({
4852
4860
  client.get(`${baseUrl}/users/me/rooms/messages`, options),
4853
4861
  });
4854
4862
 
4863
+ //
4864
+
4865
+
4866
+
4867
+
4868
+
4869
+ class MeetingStatus {
4870
+
4871
+
4872
+
4873
+
4874
+ constructor({
4875
+ full,
4876
+ } = {}) {
4877
+ this.full = full;
4878
+
4879
+ // Useful to compare instead of instanceof with minified code
4880
+ this.type = 'MeetingStatus';
4881
+ }
4882
+
4883
+ static parse(plain ) {
4884
+ return new MeetingStatus({
4885
+ full: plain.full,
4886
+ });
4887
+ }
4888
+
4889
+ }
4890
+
4855
4891
  /* */
4856
4892
 
4857
4893
 
@@ -5046,6 +5082,8 @@ var calldMethods = (client , baseUrl ) => ({
5046
5082
  client.put(`${baseUrl}/users/me/calls/${callId}/record/resume`, null, null,
5047
5083
  ApiRequester.successResponseParser),
5048
5084
 
5085
+ guestGetMeetingStatus: (meetingUuid ) =>
5086
+ client.get(`${baseUrl}/guests/me/meetings/${meetingUuid}/status`).then(MeetingStatus.parse),
5049
5087
  });
5050
5088
 
5051
5089
  //
@@ -6635,7 +6673,7 @@ const events$1 = [REGISTERED, UNREGISTERED, REGISTRATION_FAILED, INVITE];
6635
6673
  const transportEvents = [CONNECTED, DISCONNECTED, TRANSPORT_ERROR, MESSAGE];
6636
6674
  class CanceledCallError extends Error {}
6637
6675
 
6638
- const MAX_REGISTER_TRIES = 10;
6676
+ const MAX_REGISTER_TRIES = 5;
6639
6677
 
6640
6678
 
6641
6679
 
@@ -6689,6 +6727,7 @@ class WebRTCClient extends Emitter {
6689
6727
 
6690
6728
 
6691
6729
 
6730
+
6692
6731
 
6693
6732
  // sugar
6694
6733
 
@@ -6751,6 +6790,7 @@ class WebRTCClient extends Emitter {
6751
6790
  this.conferences = {};
6752
6791
  this.networkMonitoringInterval = {};
6753
6792
  this.sessionNetworkStats = {};
6793
+ this.forceClosed = false;
6754
6794
 
6755
6795
  this._boundOnHeartbeat = this._onHeartbeat.bind(this);
6756
6796
  this.heartbeat = new Heartbeat(config.heartbeatDelay, config.heartbeatTimeout, config.maxHeartbeats);
@@ -6872,6 +6912,8 @@ class WebRTCClient extends Emitter {
6872
6912
  skipRegister: this.skipRegister,
6873
6913
  };
6874
6914
 
6915
+ this.forceClosed = false;
6916
+
6875
6917
  if (this.skipRegister) {
6876
6918
  logger$8.info('sdk webrtc skip register...', logInfo);
6877
6919
  return Promise.resolve();
@@ -6896,7 +6938,15 @@ class WebRTCClient extends Emitter {
6896
6938
  const registerOptions = this._isWeb() ? {} : { extraContactHeaderParams: ['mobility=mobile'] };
6897
6939
 
6898
6940
  const onRegisterFailed = () => {
6899
- logger$8.info('sdk webrtc registering failed', { tries, registerer: !!this.registerer });
6941
+ logger$8.info('sdk webrtc registering failed', {
6942
+ tries,
6943
+ registerer: !!this.registerer,
6944
+ forceClosed: this.forceClosed,
6945
+ });
6946
+ if (this.forceClosed) {
6947
+ return;
6948
+ }
6949
+
6900
6950
  this.connectionPromise = null;
6901
6951
  if (this.registerer && this.registerer.waiting) {
6902
6952
  this.registerer.waitingToggle(false);
@@ -7174,8 +7224,9 @@ class WebRTCClient extends Emitter {
7174
7224
  }
7175
7225
  }
7176
7226
 
7177
- async close() {
7178
- logger$8.info('sdk webrtc closing client', { userAgent: !!this.userAgent });
7227
+ async close(force = false) {
7228
+ logger$8.info('sdk webrtc closing client', { userAgent: !!this.userAgent, force });
7229
+ this.forceClosed = force;
7179
7230
  this._cleanupMedia();
7180
7231
  this.connectionPromise = null;
7181
7232
 
@@ -7194,7 +7245,7 @@ class WebRTCClient extends Emitter {
7194
7245
  this.userAgent.delegate = null;
7195
7246
  this.userAgent.stateChange.removeAllListeners();
7196
7247
 
7197
- await this._disconnectTransport();
7248
+ await this._disconnectTransport(force);
7198
7249
 
7199
7250
  this._cleanupRegister();
7200
7251
 
@@ -8630,7 +8681,15 @@ class WebRTCClient extends Emitter {
8630
8681
  return userAgent.UserAgent.makeURI(`sip:${target}@${this.config.host}`);
8631
8682
  }
8632
8683
 
8633
- async _disconnectTransport() {
8684
+ async _disconnectTransport(force = false) {
8685
+ if (force) {
8686
+ // Bypass sip.js state machine that prevent to close WS with the state `Connecting`
8687
+ this.userAgent.transport.disconnectResolve = () => {};
8688
+ // eslint-disable-next-line
8689
+ this.userAgent.transport._ws.close(1000);
8690
+ return;
8691
+ }
8692
+
8634
8693
  // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors.
8635
8694
  if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) {
8636
8695
  try {
@@ -9367,6 +9426,7 @@ class WebRTCPhone extends Emitter {
9367
9426
  }
9368
9427
 
9369
9428
  callSession.answerTime = new Date();
9429
+ this._updateCallSession(callSession);
9370
9430
  this.eventEmitter.emit(ON_CALL_ACCEPTED, callSession, cameraEnabled);
9371
9431
 
9372
9432
  return callSession;
@@ -9576,6 +9636,12 @@ class WebRTCPhone extends Emitter {
9576
9636
  return Promise.resolve(null);
9577
9637
  }
9578
9638
 
9639
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
9640
+ if (sipSessionId && !(sipSessionId in this.callSessions)) {
9641
+ logger$7.warn('Call session already ended or not found, can\'t accept it.', { sipSessionId });
9642
+ return Promise.resolve(null);
9643
+ }
9644
+
9579
9645
  if (callSession.getId() in this.acceptedSessions) {
9580
9646
  logger$7.warn('CallSession already accepted.', { id: callSession ? callSession.getId() : 'n/a' });
9581
9647
  return Promise.resolve(callSession.sipCallId);
@@ -9583,6 +9649,7 @@ class WebRTCPhone extends Emitter {
9583
9649
 
9584
9650
  this.shouldSendReinvite = false;
9585
9651
  callSession.answerTime = new Date();
9652
+ this._updateCallSession(callSession);
9586
9653
  this.acceptedSessions[callSession.getId()] = true;
9587
9654
 
9588
9655
  this.eventEmitter.emit(ON_CALL_ANSWERED, callSession);
@@ -9682,7 +9749,7 @@ class WebRTCPhone extends Emitter {
9682
9749
  // Hold current session if different from the current one (we don't want 2 sessions active at the same time).
9683
9750
  if (this.currentSipSession && this.currentSipSession.id !== sipSession.id) {
9684
9751
  logger$7.info('WebRTC hold call after resume', { id: this.currentSipSession.id });
9685
- this.holdSipSession(this.currentSipSession, callSession);
9752
+ this.holdSipSession(this.currentSipSession, this.currentCallSession);
9686
9753
  }
9687
9754
 
9688
9755
  const promise = this.unholdSipSession(sipSession, callSession, withEvent, isConference);
@@ -9893,6 +9960,7 @@ class WebRTCPhone extends Emitter {
9893
9960
  }
9894
9961
  const callSession = this._createOutgoingCallSession(sipSession, cameraEnabled || false);
9895
9962
  callSession.creationTime = new Date();
9963
+ this._updateCallSession(callSession);
9896
9964
 
9897
9965
  this.eventEmitter.emit(ON_PLAY_PROGRESS_SOUND, this.audioOutputDeviceId, this.audioOutputVolume);
9898
9966
 
@@ -10034,7 +10102,7 @@ class WebRTCPhone extends Emitter {
10034
10102
  return this.client && this.client.isConference(callSession.sipCallId);
10035
10103
  }
10036
10104
 
10037
- async close() {
10105
+ async close(force = false) {
10038
10106
  logger$7.info('WebRTC close');
10039
10107
 
10040
10108
  try {
@@ -10046,7 +10114,7 @@ class WebRTCPhone extends Emitter {
10046
10114
  logger$7.error('WebRTC close, unregister error', e);
10047
10115
  }
10048
10116
 
10049
- this.client.close();
10117
+ this.client.close(force);
10050
10118
  this.unbind();
10051
10119
 
10052
10120
  this.incomingSessions = [];
@@ -10172,6 +10240,8 @@ class WebRTCPhone extends Emitter {
10172
10240
  }
10173
10241
 
10174
10242
  callSession.creationTime = new Date();
10243
+ this._updateCallSession(callSession);
10244
+
10175
10245
  this.eventEmitter.emit(ON_CALL_INCOMING, callSession, wantsToDoVideo);
10176
10246
  });
10177
10247
 
@@ -10196,9 +10266,17 @@ class WebRTCPhone extends Emitter {
10196
10266
  });
10197
10267
 
10198
10268
  this.client.on(this.client.ACCEPTED, (sipSession ) => {
10199
- logger$7.info('WebRTC call accepted', { sipId: sipSession.id });
10269
+ const sessionId = this.getSipSessionId(sipSession);
10270
+ const hasSession = sessionId in this.callSessions;
10200
10271
 
10201
- this._onCallAccepted(sipSession, this.client.hasVideo(this.getSipSessionId(sipSession)));
10272
+ logger$7.info('WebRTC call accepted', { sipId: sessionId, hasSession });
10273
+
10274
+ if (!hasSession) {
10275
+ logger$7.warn('Call accepted ignored, session is no longer present in the WebRtcPhone', { sessionId });
10276
+ return;
10277
+ }
10278
+
10279
+ this._onCallAccepted(sipSession, this.client.hasVideo(sessionId));
10202
10280
 
10203
10281
  if (this.audioOutputDeviceId) {
10204
10282
  this.client.changeAudioOutputDevice(this.audioOutputDeviceId);
@@ -10215,6 +10293,8 @@ class WebRTCPhone extends Emitter {
10215
10293
  logger$7.info('WebRTC call rejected', session.id);
10216
10294
  const callSession = this._createCallSession(session);
10217
10295
  callSession.endTime = new Date();
10296
+ this._updateCallSession(callSession);
10297
+
10218
10298
  this.eventEmitter.emit(ON_CALL_REJECTED, callSession);
10219
10299
  });
10220
10300
 
@@ -10332,9 +10412,14 @@ class WebRTCPhone extends Emitter {
10332
10412
  return this.client.getSipSessionId(sipSession);
10333
10413
  }
10334
10414
 
10335
- _updateCallSession(callSession ) {
10415
+ getSipSessionIdFromCallSession(callSession ) {
10336
10416
  const sipSession = this.findSipSession(callSession);
10337
- const sipSessionId = sipSession ? this.getSipSessionId(sipSession) : null;
10417
+
10418
+ return sipSession ? this.getSipSessionId(sipSession) : null;
10419
+ }
10420
+
10421
+ _updateCallSession(callSession ) {
10422
+ const sipSessionId = this.getSipSessionIdFromCallSession(callSession);
10338
10423
 
10339
10424
  if (sipSessionId) {
10340
10425
  this.callSessions[sipSessionId] = callSession;
@@ -10439,6 +10524,7 @@ class WebRTCPhone extends Emitter {
10439
10524
  displayName: identity ? identity.displayName || number : number,
10440
10525
  startTime: fromSession ? fromSession.startTime : new Date(),
10441
10526
  creationTime: fromSession ? fromSession.creationTime : null,
10527
+ answerTime: fromSession ? fromSession.answerTime : null,
10442
10528
  endTime: fromSession ? fromSession.endTime : null,
10443
10529
  answered: state === sessionState.SessionState.Established,
10444
10530
  paused: this.client.isCallHeld(sipSession),