@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
@@ -3620,13 +3620,21 @@ class CallSession {
3620
3620
 
3621
3621
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
3622
3622
  set answered(value ) {
3623
- this.answerTime = value ? new Date() : null;
3623
+ this.answerTime = value ? this.answerTime || new Date() : null;
3624
3624
  }
3625
3625
 
3626
3626
  get answered() {
3627
3627
  return !!this.answerTime;
3628
3628
  }
3629
3629
 
3630
+ toJSON() {
3631
+ const jsonObj = { ...this };
3632
+ // $FlowFixMe
3633
+ jsonObj.answered = this.answered;
3634
+
3635
+ return jsonObj;
3636
+ }
3637
+
3630
3638
  static parseCall(call ) {
3631
3639
  return new CallSession({
3632
3640
  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),
@@ -3620,13 +3620,21 @@ class CallSession {
3620
3620
 
3621
3621
  // Retro-compatibility: `answered` was a boolean before. We can reproduce the behaviour with a getter/setter
3622
3622
  set answered(value ) {
3623
- this.answerTime = value ? new Date() : null;
3623
+ this.answerTime = value ? this.answerTime || new Date() : null;
3624
3624
  }
3625
3625
 
3626
3626
  get answered() {
3627
3627
  return !!this.answerTime;
3628
3628
  }
3629
3629
 
3630
+ toJSON() {
3631
+ const jsonObj = { ...this };
3632
+ // $FlowFixMe
3633
+ jsonObj.answered = this.answered;
3634
+
3635
+ return jsonObj;
3636
+ }
3637
+
3630
3638
  static parseCall(call ) {
3631
3639
  return new CallSession({
3632
3640
  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),