@webex/plugin-meetings 3.6.0 → 3.7.0

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 (277) hide show
  1. package/README.md +2 -1
  2. package/dist/breakouts/breakout.js +1 -1
  3. package/dist/breakouts/index.js +1 -1
  4. package/dist/common/errors/webinar-registration-error.js +50 -0
  5. package/dist/common/errors/webinar-registration-error.js.map +1 -0
  6. package/dist/config.js +3 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/constants.js +31 -2
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controls-options-manager/enums.js +1 -0
  11. package/dist/controls-options-manager/enums.js.map +1 -1
  12. package/dist/controls-options-manager/index.js +10 -3
  13. package/dist/controls-options-manager/index.js.map +1 -1
  14. package/dist/controls-options-manager/types.js.map +1 -1
  15. package/dist/controls-options-manager/util.js +12 -0
  16. package/dist/controls-options-manager/util.js.map +1 -1
  17. package/dist/index.js +7 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/interpretation/index.js +1 -1
  20. package/dist/interpretation/siLanguage.js +1 -1
  21. package/dist/locus-info/controlsUtils.js +28 -4
  22. package/dist/locus-info/controlsUtils.js.map +1 -1
  23. package/dist/locus-info/fullState.js +2 -1
  24. package/dist/locus-info/fullState.js.map +1 -1
  25. package/dist/locus-info/index.js +61 -3
  26. package/dist/locus-info/index.js.map +1 -1
  27. package/dist/locus-info/parser.js +5 -1
  28. package/dist/locus-info/parser.js.map +1 -1
  29. package/dist/meeting/in-meeting-actions.js +19 -1
  30. package/dist/meeting/in-meeting-actions.js.map +1 -1
  31. package/dist/meeting/index.js +664 -490
  32. package/dist/meeting/index.js.map +1 -1
  33. package/dist/meeting/muteState.js +5 -2
  34. package/dist/meeting/muteState.js.map +1 -1
  35. package/dist/meeting/util.js +3 -2
  36. package/dist/meeting/util.js.map +1 -1
  37. package/dist/meeting-info/meeting-info-v2.js +68 -17
  38. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  39. package/dist/meetings/index.js +25 -1
  40. package/dist/meetings/index.js.map +1 -1
  41. package/dist/members/index.js +3 -2
  42. package/dist/members/index.js.map +1 -1
  43. package/dist/members/util.js +9 -5
  44. package/dist/members/util.js.map +1 -1
  45. package/dist/metrics/constants.js +2 -1
  46. package/dist/metrics/constants.js.map +1 -1
  47. package/dist/multistream/remoteMedia.js +4 -0
  48. package/dist/multistream/remoteMedia.js.map +1 -1
  49. package/dist/networkQualityMonitor/index.js +19 -13
  50. package/dist/networkQualityMonitor/index.js.map +1 -1
  51. package/dist/reachability/index.js +3 -3
  52. package/dist/reachability/index.js.map +1 -1
  53. package/dist/reachability/request.js +2 -1
  54. package/dist/reachability/request.js.map +1 -1
  55. package/dist/roap/request.js +1 -1
  56. package/dist/roap/request.js.map +1 -1
  57. package/dist/{common/errors/parameter.d.ts → types/common/errors/webinar-registration-error.d.ts} +4 -5
  58. package/dist/types/config.d.ts +2 -0
  59. package/dist/types/constants.d.ts +25 -0
  60. package/dist/types/controls-options-manager/enums.d.ts +2 -1
  61. package/dist/types/controls-options-manager/index.d.ts +2 -1
  62. package/dist/types/controls-options-manager/types.d.ts +2 -0
  63. package/dist/types/index.d.ts +2 -1
  64. package/dist/types/locus-info/index.d.ts +9 -0
  65. package/dist/types/meeting/in-meeting-actions.d.ts +18 -0
  66. package/dist/types/meeting/index.d.ts +12 -1
  67. package/dist/types/meeting/muteState.d.ts +2 -1
  68. package/dist/types/meeting-info/meeting-info-v2.d.ts +23 -0
  69. package/dist/types/meetings/index.d.ts +9 -0
  70. package/dist/types/members/index.d.ts +2 -1
  71. package/dist/types/members/util.d.ts +3 -1
  72. package/dist/types/metrics/constants.d.ts +1 -0
  73. package/dist/types/multistream/remoteMedia.d.ts +1 -0
  74. package/dist/webinar/index.js +32 -19
  75. package/dist/webinar/index.js.map +1 -1
  76. package/package.json +22 -22
  77. package/src/common/errors/webinar-registration-error.ts +27 -0
  78. package/src/config.ts +2 -0
  79. package/src/constants.ts +31 -0
  80. package/src/controls-options-manager/enums.ts +1 -0
  81. package/src/controls-options-manager/index.ts +19 -2
  82. package/src/controls-options-manager/types.ts +2 -0
  83. package/src/controls-options-manager/util.ts +12 -0
  84. package/src/index.ts +2 -0
  85. package/src/locus-info/controlsUtils.ts +46 -2
  86. package/src/locus-info/fullState.ts +1 -0
  87. package/src/locus-info/index.ts +60 -0
  88. package/src/locus-info/parser.ts +8 -1
  89. package/src/meeting/in-meeting-actions.ts +37 -0
  90. package/src/meeting/index.ts +154 -20
  91. package/src/meeting/muteState.ts +6 -2
  92. package/src/meeting/util.ts +6 -2
  93. package/src/meeting-info/meeting-info-v2.ts +51 -0
  94. package/src/meetings/index.ts +68 -40
  95. package/src/members/index.ts +4 -2
  96. package/src/members/util.ts +3 -1
  97. package/src/metrics/constants.ts +1 -0
  98. package/src/multistream/remoteMedia.ts +5 -0
  99. package/src/reachability/index.ts +3 -3
  100. package/src/reachability/request.ts +1 -0
  101. package/src/roap/request.ts +3 -1
  102. package/src/webinar/index.ts +31 -17
  103. package/test/unit/spec/controls-options-manager/index.js +56 -32
  104. package/test/unit/spec/controls-options-manager/util.js +44 -0
  105. package/test/unit/spec/locus-info/controlsUtils.js +80 -4
  106. package/test/unit/spec/locus-info/index.js +88 -2
  107. package/test/unit/spec/meeting/in-meeting-actions.ts +18 -0
  108. package/test/unit/spec/meeting/index.js +263 -64
  109. package/test/unit/spec/meeting/muteState.js +8 -4
  110. package/test/unit/spec/meeting/utils.js +16 -1
  111. package/test/unit/spec/meeting-info/meetinginfov2.js +37 -0
  112. package/test/unit/spec/meetings/index.js +32 -1
  113. package/test/unit/spec/members/index.js +25 -2
  114. package/test/unit/spec/members/request.js +37 -3
  115. package/test/unit/spec/members/utils.js +15 -1
  116. package/test/unit/spec/multistream/remoteMedia.ts +16 -2
  117. package/test/unit/spec/reachability/index.ts +1 -1
  118. package/test/unit/spec/reachability/request.js +13 -8
  119. package/test/unit/spec/webinar/index.ts +82 -16
  120. package/dist/annotation/annotation.types.d.ts +0 -42
  121. package/dist/annotation/constants.d.ts +0 -31
  122. package/dist/annotation/index.d.ts +0 -117
  123. package/dist/breakouts/breakout.d.ts +0 -8
  124. package/dist/breakouts/collection.d.ts +0 -5
  125. package/dist/breakouts/edit-lock-error.d.ts +0 -15
  126. package/dist/breakouts/events.d.ts +0 -8
  127. package/dist/breakouts/index.d.ts +0 -5
  128. package/dist/breakouts/request.d.ts +0 -22
  129. package/dist/breakouts/utils.d.ts +0 -15
  130. package/dist/common/browser-detection.d.ts +0 -9
  131. package/dist/common/collection.d.ts +0 -48
  132. package/dist/common/config.d.ts +0 -2
  133. package/dist/common/errors/captcha-error.d.ts +0 -15
  134. package/dist/common/errors/intent-to-join.d.ts +0 -16
  135. package/dist/common/errors/join-meeting.d.ts +0 -17
  136. package/dist/common/errors/media.d.ts +0 -15
  137. package/dist/common/errors/no-meeting-info.d.ts +0 -14
  138. package/dist/common/errors/password-error.d.ts +0 -15
  139. package/dist/common/errors/permission.d.ts +0 -14
  140. package/dist/common/errors/reclaim-host-role-error.js +0 -149
  141. package/dist/common/errors/reclaim-host-role-error.js.map +0 -1
  142. package/dist/common/errors/reclaim-host-role-errors.d.ts +0 -60
  143. package/dist/common/errors/reconnection-in-progress.d.ts +0 -9
  144. package/dist/common/errors/reconnection-in-progress.js +0 -33
  145. package/dist/common/errors/reconnection-in-progress.js.map +0 -1
  146. package/dist/common/errors/reconnection.d.ts +0 -15
  147. package/dist/common/errors/stats.d.ts +0 -15
  148. package/dist/common/errors/webex-errors.d.ts +0 -93
  149. package/dist/common/errors/webex-meetings-error.d.ts +0 -20
  150. package/dist/common/events/events-scope.d.ts +0 -17
  151. package/dist/common/events/events.d.ts +0 -12
  152. package/dist/common/events/trigger-proxy.d.ts +0 -2
  153. package/dist/common/events/util.d.ts +0 -2
  154. package/dist/common/logs/logger-config.d.ts +0 -2
  155. package/dist/common/logs/logger-proxy.d.ts +0 -2
  156. package/dist/common/logs/request.d.ts +0 -36
  157. package/dist/common/queue.d.ts +0 -34
  158. package/dist/config.d.ts +0 -72
  159. package/dist/constants.d.ts +0 -1088
  160. package/dist/controls-options-manager/constants.d.ts +0 -4
  161. package/dist/controls-options-manager/enums.d.ts +0 -15
  162. package/dist/controls-options-manager/index.d.ts +0 -136
  163. package/dist/controls-options-manager/types.d.ts +0 -43
  164. package/dist/controls-options-manager/util.d.ts +0 -1
  165. package/dist/index.d.ts +0 -7
  166. package/dist/interceptors/index.d.ts +0 -2
  167. package/dist/interceptors/locusRetry.d.ts +0 -27
  168. package/dist/interpretation/collection.d.ts +0 -5
  169. package/dist/interpretation/index.d.ts +0 -5
  170. package/dist/interpretation/siLanguage.d.ts +0 -5
  171. package/dist/locus-info/controlsUtils.d.ts +0 -2
  172. package/dist/locus-info/embeddedAppsUtils.d.ts +0 -2
  173. package/dist/locus-info/fullState.d.ts +0 -2
  174. package/dist/locus-info/hostUtils.d.ts +0 -2
  175. package/dist/locus-info/index.d.ts +0 -322
  176. package/dist/locus-info/infoUtils.d.ts +0 -2
  177. package/dist/locus-info/mediaSharesUtils.d.ts +0 -2
  178. package/dist/locus-info/parser.d.ts +0 -272
  179. package/dist/locus-info/selfUtils.d.ts +0 -2
  180. package/dist/media/index.d.ts +0 -34
  181. package/dist/media/properties.d.ts +0 -93
  182. package/dist/media/util.d.ts +0 -2
  183. package/dist/mediaQualityMetrics/config.d.ts +0 -241
  184. package/dist/mediaQualityMetrics/config.js +0 -502
  185. package/dist/mediaQualityMetrics/config.js.map +0 -1
  186. package/dist/meeting/effectsState.js +0 -260
  187. package/dist/meeting/effectsState.js.map +0 -1
  188. package/dist/meeting/in-meeting-actions.d.ts +0 -167
  189. package/dist/meeting/index.d.ts +0 -1825
  190. package/dist/meeting/locusMediaRequest.d.ts +0 -74
  191. package/dist/meeting/muteState.d.ts +0 -178
  192. package/dist/meeting/request.d.ts +0 -295
  193. package/dist/meeting/request.type.d.ts +0 -11
  194. package/dist/meeting/state.d.ts +0 -9
  195. package/dist/meeting/util.d.ts +0 -119
  196. package/dist/meeting/voicea-meeting.d.ts +0 -16
  197. package/dist/meeting-info/collection.d.ts +0 -20
  198. package/dist/meeting-info/index.d.ts +0 -69
  199. package/dist/meeting-info/meeting-info-v2.d.ts +0 -123
  200. package/dist/meeting-info/request.d.ts +0 -22
  201. package/dist/meeting-info/util.d.ts +0 -2
  202. package/dist/meeting-info/utilv2.d.ts +0 -2
  203. package/dist/meetings/collection.d.ts +0 -40
  204. package/dist/meetings/index.d.ts +0 -390
  205. package/dist/meetings/meetings.types.d.ts +0 -4
  206. package/dist/meetings/request.d.ts +0 -27
  207. package/dist/meetings/util.d.ts +0 -18
  208. package/dist/member/index.d.ts +0 -160
  209. package/dist/member/member.types.js +0 -17
  210. package/dist/member/member.types.js.map +0 -1
  211. package/dist/member/types.d.ts +0 -32
  212. package/dist/member/util.d.ts +0 -2
  213. package/dist/members/collection.d.ts +0 -29
  214. package/dist/members/index.d.ts +0 -353
  215. package/dist/members/request.d.ts +0 -114
  216. package/dist/members/types.d.ts +0 -25
  217. package/dist/members/util.d.ts +0 -215
  218. package/dist/metrics/config.js +0 -276
  219. package/dist/metrics/config.js.map +0 -1
  220. package/dist/metrics/constants.d.ts +0 -70
  221. package/dist/metrics/index.d.ts +0 -45
  222. package/dist/multistream/mediaRequestManager.d.ts +0 -119
  223. package/dist/multistream/receiveSlot.d.ts +0 -68
  224. package/dist/multistream/receiveSlotManager.d.ts +0 -56
  225. package/dist/multistream/remoteMedia.d.ts +0 -72
  226. package/dist/multistream/remoteMediaGroup.d.ts +0 -49
  227. package/dist/multistream/remoteMediaManager.d.ts +0 -300
  228. package/dist/multistream/sendSlotManager.d.ts +0 -69
  229. package/dist/networkQualityMonitor/index.d.ts +0 -70
  230. package/dist/peer-connection-manager/index.js +0 -671
  231. package/dist/peer-connection-manager/index.js.map +0 -1
  232. package/dist/peer-connection-manager/util.js +0 -109
  233. package/dist/peer-connection-manager/util.js.map +0 -1
  234. package/dist/personal-meeting-room/index.d.ts +0 -47
  235. package/dist/personal-meeting-room/request.d.ts +0 -14
  236. package/dist/personal-meeting-room/util.d.ts +0 -2
  237. package/dist/reachability/clusterReachability.d.ts +0 -109
  238. package/dist/reachability/index.d.ts +0 -105
  239. package/dist/reachability/request.d.ts +0 -39
  240. package/dist/reachability/util.d.ts +0 -8
  241. package/dist/reactions/constants.d.ts +0 -3
  242. package/dist/reactions/reactions.d.ts +0 -4
  243. package/dist/reactions/reactions.type.d.ts +0 -52
  244. package/dist/reconnection-manager/index.d.ts +0 -136
  245. package/dist/recording-controller/enums.d.ts +0 -7
  246. package/dist/recording-controller/index.d.ts +0 -207
  247. package/dist/recording-controller/util.d.ts +0 -14
  248. package/dist/roap/collection.js +0 -62
  249. package/dist/roap/collection.js.map +0 -1
  250. package/dist/roap/handler.js +0 -275
  251. package/dist/roap/handler.js.map +0 -1
  252. package/dist/roap/index.d.ts +0 -86
  253. package/dist/roap/request.d.ts +0 -39
  254. package/dist/roap/state.js +0 -126
  255. package/dist/roap/state.js.map +0 -1
  256. package/dist/roap/turnDiscovery.d.ts +0 -155
  257. package/dist/roap/util.js +0 -75
  258. package/dist/roap/util.js.map +0 -1
  259. package/dist/rtcMetrics/constants.d.ts +0 -4
  260. package/dist/rtcMetrics/index.d.ts +0 -61
  261. package/dist/statsAnalyzer/global.d.ts +0 -36
  262. package/dist/statsAnalyzer/global.js +0 -126
  263. package/dist/statsAnalyzer/global.js.map +0 -1
  264. package/dist/statsAnalyzer/index.d.ts +0 -217
  265. package/dist/statsAnalyzer/index.js +0 -1013
  266. package/dist/statsAnalyzer/index.js.map +0 -1
  267. package/dist/statsAnalyzer/mqaUtil.d.ts +0 -48
  268. package/dist/statsAnalyzer/mqaUtil.js +0 -179
  269. package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
  270. package/dist/transcription/index.d.ts +0 -64
  271. package/dist/types/common/errors/reconnection-in-progress.d.ts +0 -9
  272. package/dist/types/mediaQualityMetrics/config.d.ts +0 -241
  273. package/dist/types/statsAnalyzer/global.d.ts +0 -36
  274. package/dist/types/statsAnalyzer/index.d.ts +0 -217
  275. package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
  276. package/dist/webinar/collection.d.ts +0 -16
  277. package/dist/webinar/index.d.ts +0 -5
@@ -91,12 +91,14 @@ import ParameterError from '../../../../src/common/errors/parameter';
91
91
  import PasswordError from '../../../../src/common/errors/password-error';
92
92
  import CaptchaError from '../../../../src/common/errors/captcha-error';
93
93
  import PermissionError from '../../../../src/common/errors/permission';
94
+ import WebinarRegistrationError from '../../../../src/common/errors/webinar-registration-error';
94
95
  import IntentToJoinError from '../../../../src/common/errors/intent-to-join';
95
96
  import testUtils from '../../../utils/testUtils';
96
97
  import {
97
98
  MeetingInfoV2CaptchaError,
98
99
  MeetingInfoV2PasswordError,
99
100
  MeetingInfoV2PolicyError,
101
+ MeetingInfoV2WebinarRegistrationError,
100
102
  } from '../../../../src/meeting-info/meeting-info-v2';
101
103
  import {
102
104
  DTLS_HANDSHAKE_FAILED_CLIENT_CODE,
@@ -375,7 +377,10 @@ describe('plugin-meetings', () => {
375
377
  }
376
378
  );
377
379
  assert.equal(newMeeting.correlationId, newMeeting.id);
378
- assert.deepEqual(newMeeting.callStateForMetrics, {correlationId: newMeeting.id, sessionCorrelationId: ''});
380
+ assert.deepEqual(newMeeting.callStateForMetrics, {
381
+ correlationId: newMeeting.id,
382
+ sessionCorrelationId: '',
383
+ });
379
384
  });
380
385
 
381
386
  it('correlationId can be provided in callStateForMetrics', () => {
@@ -3440,47 +3445,60 @@ describe('plugin-meetings', () => {
3440
3445
  });
3441
3446
  });
3442
3447
 
3443
- it('should pass bundlePolicy to createMediaConnection', async () => {
3448
+ describe('bundlePolicy', () => {
3444
3449
  const FAKE_TURN_URL = 'turns:webex.com:3478';
3445
3450
  const FAKE_TURN_USER = 'some-turn-username';
3446
3451
  const FAKE_TURN_PASSWORD = 'some-password';
3447
3452
 
3448
- meeting.meetingState = 'ACTIVE';
3449
- Media.createMediaConnection.resetHistory();
3450
-
3451
- meeting.roap.doTurnDiscovery = sinon.stub().resolves({
3452
- turnServerInfo: {
3453
- url: FAKE_TURN_URL,
3454
- username: FAKE_TURN_USER,
3455
- password: FAKE_TURN_PASSWORD,
3456
- },
3457
- turnDiscoverySkippedReason: undefined,
3458
- });
3459
- const media = meeting.addMedia({
3460
- mediaSettings: {},
3461
- bundlePolicy: 'bundlePolicy-value',
3462
- });
3453
+ beforeEach(() => {
3454
+ meeting.meetingState = 'ACTIVE';
3455
+ Media.createMediaConnection.resetHistory();
3463
3456
 
3464
- assert.exists(media);
3465
- await media;
3466
- assert.calledOnce(meeting.roap.doTurnDiscovery);
3467
- assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
3468
- assert.calledOnce(Media.createMediaConnection);
3469
- assert.calledWith(
3470
- Media.createMediaConnection,
3471
- false,
3472
- meeting.getMediaConnectionDebugId(),
3473
- meeting.id,
3474
- sinon.match({
3457
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({
3475
3458
  turnServerInfo: {
3476
3459
  url: FAKE_TURN_URL,
3477
3460
  username: FAKE_TURN_USER,
3478
3461
  password: FAKE_TURN_PASSWORD,
3479
3462
  },
3480
- bundlePolicy: 'bundlePolicy-value',
3481
- })
3482
- );
3483
- assert.calledOnce(fakeMediaConnection.initiateOffer);
3463
+ turnDiscoverySkippedReason: undefined,
3464
+ });
3465
+ });
3466
+
3467
+ const runCheck = async (bundlePolicy, expectedValue) => {
3468
+ const media = meeting.addMedia({
3469
+ mediaSettings: {},
3470
+ bundlePolicy,
3471
+ });
3472
+
3473
+ assert.exists(media);
3474
+ await media;
3475
+ assert.calledOnce(meeting.roap.doTurnDiscovery);
3476
+ assert.calledWith(meeting.roap.doTurnDiscovery, meeting, false);
3477
+ assert.calledOnce(Media.createMediaConnection);
3478
+ assert.calledWith(
3479
+ Media.createMediaConnection,
3480
+ false,
3481
+ meeting.getMediaConnectionDebugId(),
3482
+ meeting.id,
3483
+ sinon.match({
3484
+ turnServerInfo: {
3485
+ url: FAKE_TURN_URL,
3486
+ username: FAKE_TURN_USER,
3487
+ password: FAKE_TURN_PASSWORD,
3488
+ },
3489
+ bundlePolicy: expectedValue,
3490
+ })
3491
+ );
3492
+ assert.calledOnce(fakeMediaConnection.initiateOffer);
3493
+ };
3494
+
3495
+ it('should pass bundlePolicy to createMediaConnection', async () => {
3496
+ await runCheck('max-compat', 'max-compat');
3497
+ });
3498
+
3499
+ it('should pass max-bundle to createMediaConnection if bundlePolicy is not provided', async () => {
3500
+ await runCheck(undefined, 'max-bundle');
3501
+ });
3484
3502
  });
3485
3503
 
3486
3504
  it('succeeds even if getDevices() throws', async () => {
@@ -3793,12 +3811,12 @@ describe('plugin-meetings', () => {
3793
3811
  id: 'fake locus from mocked join request',
3794
3812
  locusUrl: 'fake locus url',
3795
3813
  mediaId: 'fake media id',
3796
- })
3814
+ });
3797
3815
  sinon.stub(meeting.meetingRequest, 'joinMeeting').resolves({
3798
3816
  headers: {
3799
3817
  trackingid: 'fake tracking id',
3800
- }
3801
- })
3818
+ },
3819
+ });
3802
3820
  await meeting.join({enableMultistream: isMultistream});
3803
3821
  });
3804
3822
 
@@ -3991,6 +4009,10 @@ describe('plugin-meetings', () => {
3991
4009
  assert.notCalled(
3992
4010
  meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream
3993
4011
  );
4012
+ assert.throws(
4013
+ meeting.publishStreams(localStreams),
4014
+ `Attempted to publish microphone stream with ended readyState, correlationId=${meeting.correlationId}`
4015
+ );
3994
4016
  } else {
3995
4017
  assert.calledOnceWithExactly(
3996
4018
  meeting.sendSlotManager.getSlot(MediaType.AudioMain).publishStream,
@@ -4003,6 +4025,10 @@ describe('plugin-meetings', () => {
4003
4025
  assert.notCalled(
4004
4026
  meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream
4005
4027
  );
4028
+ assert.throws(
4029
+ meeting.publishStreams(localStreams),
4030
+ `Attempted to publish camera stream with ended readyState, correlationId=${meeting.correlationId}`
4031
+ );
4006
4032
  } else {
4007
4033
  assert.calledOnceWithExactly(
4008
4034
  meeting.sendSlotManager.getSlot(MediaType.VideoMain).publishStream,
@@ -4015,6 +4041,10 @@ describe('plugin-meetings', () => {
4015
4041
  assert.notCalled(
4016
4042
  meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream
4017
4043
  );
4044
+ assert.throws(
4045
+ meeting.publishStreams(localStreams),
4046
+ `Attempted to publish screenShare audio stream with ended readyState, correlationId=${meeting.correlationId}`
4047
+ );
4018
4048
  } else {
4019
4049
  assert.calledOnceWithExactly(
4020
4050
  meeting.sendSlotManager.getSlot(MediaType.AudioSlides).publishStream,
@@ -4027,6 +4057,10 @@ describe('plugin-meetings', () => {
4027
4057
  assert.notCalled(
4028
4058
  meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream
4029
4059
  );
4060
+ assert.throws(
4061
+ meeting.publishStreams(localStreams),
4062
+ `Attempted to publish screenShare video stream with ended readyState, correlationId=${meeting.correlationId}`
4063
+ );
4030
4064
  } else {
4031
4065
  assert.calledOnceWithExactly(
4032
4066
  meeting.sendSlotManager.getSlot(MediaType.VideoSlides).publishStream,
@@ -4321,14 +4355,14 @@ describe('plugin-meetings', () => {
4321
4355
  const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4322
4356
  await meeting.addMedia({audioEnabled: false});
4323
4357
  //calling handleDeviceLogging with audioEnaled as true adn videoEnabled as false
4324
- assert.calledWith(handleDeviceLoggingSpy,false,true);
4358
+ assert.calledWith(handleDeviceLoggingSpy, false, true);
4325
4359
  });
4326
4360
 
4327
4361
  it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
4328
4362
  const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4329
4363
  await meeting.addMedia({videoEnabled: false});
4330
4364
  //calling handleDeviceLogging audioEnabled as true videoEnabled as false
4331
- assert.calledWith(handleDeviceLoggingSpy,true,false);
4365
+ assert.calledWith(handleDeviceLoggingSpy, true, false);
4332
4366
  });
4333
4367
 
4334
4368
  it('addMedia() works correctly when video is disabled with no streams to publish', async () => {
@@ -4397,12 +4431,11 @@ describe('plugin-meetings', () => {
4397
4431
  assert.calledTwice(locusMediaRequestStub);
4398
4432
  });
4399
4433
 
4400
-
4401
4434
  it('addMedia() works correctly when both shareAudio and shareVideo is disabled with no streams publish', async () => {
4402
4435
  const handleDeviceLoggingSpy = sinon.spy(Meeting, 'handleDeviceLogging');
4403
4436
  await meeting.addMedia({shareAudioEnabled: false, shareVideoEnabled: false});
4404
4437
  //calling handleDeviceLogging with audioEnabled true and videoEnabled as true
4405
- assert.calledWith(handleDeviceLoggingSpy,true,true);
4438
+ assert.calledWith(handleDeviceLoggingSpy, true, true);
4406
4439
  });
4407
4440
 
4408
4441
  describe('publishStreams()/unpublishStreams() calls', () => {
@@ -6250,6 +6283,30 @@ describe('plugin-meetings', () => {
6250
6283
 
6251
6284
  assert.equal(meeting.fetchMeetingInfoTimeoutId, undefined);
6252
6285
  });
6286
+
6287
+ it('handles meetingInfoProvider webinar need registration error', async () => {
6288
+ meeting.destination = FAKE_DESTINATION;
6289
+ meeting.destinationType = FAKE_TYPE;
6290
+ meeting.attrs.meetingInfoProvider = {
6291
+ fetchMeetingInfo: sinon
6292
+ .stub()
6293
+ .throws(
6294
+ new MeetingInfoV2WebinarRegistrationError(403021, FAKE_MEETING_INFO, 'a message')
6295
+ ),
6296
+ };
6297
+
6298
+ await assert.isRejected(
6299
+ meeting.fetchMeetingInfo({sendCAevents: true}),
6300
+ WebinarRegistrationError
6301
+ );
6302
+
6303
+ assert.deepEqual(meeting.meetingInfo, FAKE_MEETING_INFO);
6304
+ assert.equal(meeting.meetingInfoFailureCode, 403021);
6305
+ assert.equal(
6306
+ meeting.meetingInfoFailureReason,
6307
+ MEETING_INFO_FAILURE_REASON.WEBINAR_REGISTRATION
6308
+ );
6309
+ });
6253
6310
  });
6254
6311
 
6255
6312
  describe('#refreshPermissionToken', () => {
@@ -6963,7 +7020,10 @@ describe('plugin-meetings', () => {
6963
7020
  assert.deepEqual(meeting.callStateForMetrics, {correlationId, sessionCorrelationId: ''});
6964
7021
  meeting.setCorrelationId(uuid1);
6965
7022
  assert.equal(meeting.correlationId, uuid1);
6966
- assert.deepEqual(meeting.callStateForMetrics, {correlationId: uuid1, sessionCorrelationId: ''});
7023
+ assert.deepEqual(meeting.callStateForMetrics, {
7024
+ correlationId: uuid1,
7025
+ sessionCorrelationId: '',
7026
+ });
6967
7027
  });
6968
7028
  });
6969
7029
 
@@ -7635,11 +7695,11 @@ describe('plugin-meetings', () => {
7635
7695
  id: 'stream',
7636
7696
  getTracks: () => [{id: 'track', addEventListener: sinon.stub()}],
7637
7697
  };
7638
- const simulateConnectionStateChange = (newState) => {
7698
+ const simulateConnectionStateChange = async (newState) => {
7639
7699
  meeting.mediaProperties.webrtcMediaConnection.getConnectionState = sinon
7640
7700
  .stub()
7641
7701
  .returns(newState);
7642
- eventListeners[MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED]();
7702
+ await eventListeners[MediaConnectionEventNames.PEER_CONNECTION_STATE_CHANGED]();
7643
7703
  };
7644
7704
 
7645
7705
  beforeEach(() => {
@@ -7709,11 +7769,17 @@ describe('plugin-meetings', () => {
7709
7769
  });
7710
7770
 
7711
7771
  it('should collect ice candidates', () => {
7712
- eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: 'candidate'});
7772
+ eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: {candidate: 'candidate'}});
7713
7773
 
7714
7774
  assert.equal(meeting.iceCandidatesCount, 1);
7715
7775
  });
7716
7776
 
7777
+ it('should not collect empty ice candidates', () => {
7778
+ eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: {candidate: ''}});
7779
+
7780
+ assert.equal(meeting.iceCandidatesCount, 0);
7781
+ });
7782
+
7717
7783
  it('should not collect null ice candidates', () => {
7718
7784
  eventListeners[MediaConnectionEventNames.ICE_CANDIDATE]({candidate: null});
7719
7785
 
@@ -7895,7 +7961,7 @@ describe('plugin-meetings', () => {
7895
7961
  meeting.reconnectionManager = new ReconnectionManager(meeting);
7896
7962
  meeting.reconnectionManager.iceReconnected = sinon.stub().returns(undefined);
7897
7963
  meeting.setNetworkStatus = sinon.stub().returns(undefined);
7898
- meeting.statsAnalyzer = {startAnalyzer: sinon.stub()};
7964
+ meeting.statsAnalyzer = {startAnalyzer: sinon.stub(), stopAnalyzer: sinon.stub()};
7899
7965
  meeting.mediaProperties.webrtcMediaConnection = {
7900
7966
  // mock the on() method and store all the listeners
7901
7967
  on: sinon.stub().callsFake((event, listener) => {
@@ -7970,10 +8036,10 @@ describe('plugin-meetings', () => {
7970
8036
  });
7971
8037
 
7972
8038
  describe('CONNECTION_STATE_CHANGED event when state = "Failed"', () => {
7973
- const mockFailedEvent = () => {
8039
+ const mockFailedEvent = async () => {
7974
8040
  meeting.setupMediaConnectionListeners();
7975
8041
 
7976
- simulateConnectionStateChange(ConnectionState.Failed);
8042
+ await simulateConnectionStateChange(ConnectionState.Failed);
7977
8043
  };
7978
8044
 
7979
8045
  const checkBehavioralMetricSent = (hasMediaConnectionConnectedAtLeastOnce = false) => {
@@ -8003,6 +8069,22 @@ describe('plugin-meetings', () => {
8003
8069
  assert.notCalled(webex.internal.newMetrics.submitClientEvent);
8004
8070
  checkBehavioralMetricSent(true);
8005
8071
  });
8072
+
8073
+ it('stop stats analyzer during reconnection ', async () => {
8074
+ meeting.hasMediaConnectionConnectedAtLeastOnce = true;
8075
+ meeting.statsAnalyzer.stopAnalyzer = sinon.stub().resolves();
8076
+ meeting.reconnectionManager = {
8077
+ reconnect: sinon.stub().resolves(),
8078
+ resetReconnectionTimer: () => {}
8079
+ };
8080
+ meeting.currentMediaStatus = {
8081
+ video: true
8082
+ };
8083
+
8084
+ await mockFailedEvent();
8085
+
8086
+ assert.calledOnce(meeting.statsAnalyzer.stopAnalyzer);
8087
+ });
8006
8088
  });
8007
8089
 
8008
8090
  describe('should send correct metrics for ROAP_FAILURE event', () => {
@@ -8863,6 +8945,78 @@ describe('plugin-meetings', () => {
8863
8945
  );
8864
8946
  });
8865
8947
 
8948
+ it('listens to MEETING_CONTROLS_WEBCAST_UPDATED', async () => {
8949
+ const state = {example: 'value'};
8950
+
8951
+ await meeting.locusInfo.emitScoped(
8952
+ {function: 'test', file: 'test'},
8953
+ LOCUSINFO.EVENTS.CONTROLS_WEBCAST_CHANGED,
8954
+ {state}
8955
+ );
8956
+
8957
+ assert.calledWith(
8958
+ TriggerProxy.trigger,
8959
+ meeting,
8960
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
8961
+ EVENT_TRIGGERS.MEETING_CONTROLS_WEBCAST_UPDATED,
8962
+ {state}
8963
+ );
8964
+ });
8965
+
8966
+ it('listens to MEETING_CONTROLS_MEETING_FULL_UPDATED', async () => {
8967
+ const state = {example: 'value'};
8968
+
8969
+ await meeting.locusInfo.emitScoped(
8970
+ {function: 'test', file: 'test'},
8971
+ LOCUSINFO.EVENTS.CONTROLS_MEETING_FULL_CHANGED,
8972
+ {state}
8973
+ );
8974
+
8975
+ assert.calledWith(
8976
+ TriggerProxy.trigger,
8977
+ meeting,
8978
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
8979
+ EVENT_TRIGGERS.MEETING_CONTROLS_MEETING_FULL_UPDATED,
8980
+ {state}
8981
+ );
8982
+ });
8983
+
8984
+ it('listens to MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED', async () => {
8985
+ const state = {example: 'value'};
8986
+
8987
+ await meeting.locusInfo.emitScoped(
8988
+ {function: 'test', file: 'test'},
8989
+ LOCUSINFO.EVENTS.CONTROLS_PRACTICE_SESSION_STATUS_UPDATED,
8990
+ {state}
8991
+ );
8992
+
8993
+ assert.calledWith(
8994
+ TriggerProxy.trigger,
8995
+ meeting,
8996
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
8997
+ EVENT_TRIGGERS.MEETING_CONTROLS_PRACTICE_SESSION_STATUS_UPDATED,
8998
+ {state}
8999
+ );
9000
+ });
9001
+
9002
+ it('listens to MEETING_CONTROLS_STAGE_VIEW_UPDATED', async () => {
9003
+ const state = {example: 'value'};
9004
+
9005
+ await meeting.locusInfo.emitScoped(
9006
+ {function: 'test', file: 'test'},
9007
+ LOCUSINFO.EVENTS.CONTROLS_STAGE_VIEW_UPDATED,
9008
+ {state}
9009
+ );
9010
+
9011
+ assert.calledWith(
9012
+ TriggerProxy.trigger,
9013
+ meeting,
9014
+ {file: 'meeting/index', function: 'setupLocusControlsListener'},
9015
+ EVENT_TRIGGERS.MEETING_CONTROLS_STAGE_VIEW_UPDATED,
9016
+ {state}
9017
+ );
9018
+ });
9019
+
8866
9020
  it('listens to MEETING_CONTROLS_VIDEO_UPDATED', async () => {
8867
9021
  const state = {example: 'value'};
8868
9022
 
@@ -8976,12 +9130,6 @@ describe('plugin-meetings', () => {
8976
9130
  approval: {
8977
9131
  url: 'url',
8978
9132
  },
8979
- webcast: {
8980
- url: 'url',
8981
- },
8982
- webinarAttendeesSearching: {
8983
- url: 'url',
8984
- },
8985
9133
  },
8986
9134
  };
8987
9135
 
@@ -8995,10 +9143,6 @@ describe('plugin-meetings', () => {
8995
9143
  meeting.simultaneousInterpretation = {
8996
9144
  approvalUrlUpdate: sinon.stub().returns(undefined),
8997
9145
  };
8998
- meeting.webinar = {
8999
- webcastUrlUpdate: sinon.stub().returns(undefined),
9000
- webinarAttendeesSearchingUrlUpdate: sinon.stub().returns(undefined),
9001
- };
9002
9146
 
9003
9147
  meeting.locusInfo.emit(
9004
9148
  {function: 'test', file: 'test'},
@@ -9018,19 +9162,37 @@ describe('plugin-meetings', () => {
9018
9162
  meeting.simultaneousInterpretation.approvalUrlUpdate,
9019
9163
  newLocusServices.services.approval.url
9020
9164
  );
9021
- assert.calledWith(
9022
- meeting.webinar.webcastUrlUpdate,
9023
- newLocusServices.services.webcast.url
9024
- );
9025
- assert.calledWith(
9026
- meeting.webinar.webinarAttendeesSearchingUrlUpdate,
9027
- newLocusServices.services.webinarAttendeesSearching.url
9028
- );
9029
9165
  assert.calledOnce(meeting.recordingController.setSessionId);
9030
9166
  done();
9031
9167
  });
9032
9168
  });
9033
9169
 
9170
+ describe('#setUpLocusResourcesListener', () => {
9171
+ it('listens to the locus resources update event', (done) => {
9172
+ const newLocusResources = {
9173
+ resources: {
9174
+ webcastInstance: {
9175
+ url: 'url',
9176
+ },
9177
+ },
9178
+ };
9179
+
9180
+ meeting.webinar = {
9181
+ updateWebcastUrl: sinon.stub().returns(undefined),
9182
+ };
9183
+
9184
+ meeting.locusInfo.emit(
9185
+ {function: 'test', file: 'test'},
9186
+ 'LINKS_RESOURCES',
9187
+ newLocusResources
9188
+ );
9189
+
9190
+ assert.calledWith(meeting.webinar.updateWebcastUrl, newLocusResources);
9191
+
9192
+ done();
9193
+ });
9194
+ });
9195
+
9034
9196
  describe('#setUpLocusInfoMediaInactiveListener', () => {
9035
9197
  it('listens to disconnect due to un activity ', (done) => {
9036
9198
  TriggerProxy.trigger.reset();
@@ -12178,6 +12340,43 @@ describe('plugin-meetings', () => {
12178
12340
  await testEmit(false);
12179
12341
  });
12180
12342
  });
12343
+
12344
+ describe('LOCAL_UNMUTE_REQUIRED locus event', () => {
12345
+ const testEmit = async (unmuteAllowed) => {
12346
+ meeting.audio = {
12347
+ handleServerLocalUnmuteRequired: sinon.stub(),
12348
+ };
12349
+ await meeting.locusInfo.emitScoped({}, LOCUSINFO.EVENTS.LOCAL_UNMUTE_REQUIRED, {
12350
+ unmuteAllowed,
12351
+ });
12352
+
12353
+ assert.calledWith(
12354
+ TriggerProxy.trigger,
12355
+ sinon.match.instanceOf(Meeting),
12356
+ {
12357
+ file: 'meeting/index',
12358
+ function: 'setUpLocusInfoSelfListener',
12359
+ },
12360
+ EVENT_TRIGGERS.MEETING_SELF_UNMUTED_BY_OTHERS,
12361
+ {
12362
+ payload: {
12363
+ unmuteAllowed,
12364
+ },
12365
+ }
12366
+ );
12367
+ assert.calledOnceWithExactly(
12368
+ meeting.audio.handleServerLocalUnmuteRequired,
12369
+ meeting,
12370
+ unmuteAllowed
12371
+ );
12372
+ };
12373
+
12374
+ [true, false].forEach((unmuteAllowed) => {
12375
+ it(`emits the expected event and calls handleServerLocalUnmuteRequired() when unmuteAllowed=${unmuteAllowed}`, async () => {
12376
+ await testEmit(unmuteAllowed);
12377
+ });
12378
+ });
12379
+ });
12181
12380
  });
12182
12381
  });
12183
12382
 
@@ -151,7 +151,7 @@ describe('plugin-meetings', () => {
151
151
  meeting.mediaProperties.audioStream.setServerMuted = sinon.stub().callsFake((muted) => {
152
152
  meeting.mediaProperties.audioStream.userMuted = muted;
153
153
  });
154
- audio.handleServerLocalUnmuteRequired(meeting);
154
+ audio.handleServerLocalUnmuteRequired(meeting, true);
155
155
 
156
156
  await testUtils.flushPromises();
157
157
 
@@ -161,6 +161,8 @@ describe('plugin-meetings', () => {
161
161
  false,
162
162
  'localUnmuteRequired'
163
163
  );
164
+ // and unmuteAllowed was updated
165
+ assert.calledWith(meeting.mediaProperties.audioStream.setUnmuteAllowed, true);
164
166
 
165
167
  // and local unmute was sent to server
166
168
  assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
@@ -184,7 +186,7 @@ describe('plugin-meetings', () => {
184
186
  meeting.mediaProperties.audioStream.setServerMuted = sinon.stub().callsFake((muted) => {
185
187
  meeting.mediaProperties.audioStream.userMuted = muted;
186
188
  });
187
- audio.handleServerLocalUnmuteRequired(meeting);
189
+ audio.handleServerLocalUnmuteRequired(meeting, true);
188
190
 
189
191
  await testUtils.flushPromises();
190
192
 
@@ -215,7 +217,7 @@ describe('plugin-meetings', () => {
215
217
  meeting.mediaProperties.videoStream.setServerMuted = sinon.stub().callsFake((muted) => {
216
218
  meeting.mediaProperties.videoStream.userMuted = muted;
217
219
  });
218
- video.handleServerLocalUnmuteRequired(meeting);
220
+ video.handleServerLocalUnmuteRequired(meeting, true);
219
221
 
220
222
  await testUtils.flushPromises();
221
223
 
@@ -225,6 +227,8 @@ describe('plugin-meetings', () => {
225
227
  false,
226
228
  'localUnmuteRequired'
227
229
  );
230
+ // and unmuteAllowed was updated
231
+ assert.calledWith(meeting.mediaProperties.videoStream.setUnmuteAllowed, true);
228
232
 
229
233
  // and local unmute was sent to server
230
234
  assert.calledOnce(MeetingUtil.remoteUpdateAudioVideo);
@@ -248,7 +252,7 @@ describe('plugin-meetings', () => {
248
252
  meeting.mediaProperties.videoStream.setServerMuted = sinon.stub().callsFake((muted) => {
249
253
  meeting.mediaProperties.videoStream.userMuted = muted;
250
254
  });
251
- video.handleServerLocalUnmuteRequired(meeting);
255
+ video.handleServerLocalUnmuteRequired(meeting, true);
252
256
 
253
257
  await testUtils.flushPromises();
254
258
 
@@ -1013,7 +1013,7 @@ describe('plugin-meetings', () => {
1013
1013
  assert.equal(MeetingUtil.getIpVersion(webex), expectedOutput);
1014
1014
  });
1015
1015
 
1016
- it(`returns undefined when supportsIpV4=${supportsIpV4} and supportsIpV6=${supportsIpV6} and browser is firefox`, () => {
1016
+ it(`returns ${expectedOutput} when supportsIpV4=${supportsIpV4} and supportsIpV6=${supportsIpV6} for Firefox if config is enabled`, () => {
1017
1017
  sinon
1018
1018
  .stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
1019
1019
  .get(() => supportsIpV4);
@@ -1021,6 +1021,21 @@ describe('plugin-meetings', () => {
1021
1021
  .stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
1022
1022
  .get(() => supportsIpV6);
1023
1023
 
1024
+ webex.config.meetings.backendIpv6NativeSupport = true;
1025
+ isBrowserStub.callsFake((name) => name === 'firefox');
1026
+
1027
+ assert.equal(MeetingUtil.getIpVersion(webex), expectedOutput);
1028
+ });
1029
+
1030
+ it(`returns undefined when supportsIpV4=${supportsIpV4} and supportsIpV6=${supportsIpV6}, config disabled and browser is firefox`, () => {
1031
+ sinon
1032
+ .stub(webex.internal.device.ipNetworkDetector, 'supportsIpV4')
1033
+ .get(() => supportsIpV4);
1034
+ sinon
1035
+ .stub(webex.internal.device.ipNetworkDetector, 'supportsIpV6')
1036
+ .get(() => supportsIpV6);
1037
+
1038
+ webex.config.meetings.backendIpv6NativeSupport = false;
1024
1039
  isBrowserStub.callsFake((name) => name === 'firefox');
1025
1040
 
1026
1041
  assert.equal(MeetingUtil.getIpVersion(webex), undefined);
@@ -18,6 +18,7 @@ import MeetingInfo, {
18
18
  MeetingInfoV2CaptchaError,
19
19
  MeetingInfoV2AdhocMeetingError,
20
20
  MeetingInfoV2PolicyError,
21
+ MeetingInfoV2WebinarRegistrationError,
21
22
  } from '@webex/plugin-meetings/src/meeting-info/meeting-info-v2';
22
23
  import MeetingInfoUtil from '@webex/plugin-meetings/src/meeting-info/utilv2';
23
24
  import Metrics from '@webex/plugin-meetings/src/metrics';
@@ -888,6 +889,42 @@ describe('plugin-meetings', () => {
888
889
  });
889
890
  }
890
891
  );
892
+
893
+ forEach(
894
+ [
895
+ {errorCode: 403021},
896
+ {errorCode: 403022},
897
+ {errorCode: 403024},
898
+ ],
899
+ ({errorCode}) => {
900
+ it(`should throw a MeetingInfoV2WebinarRegistrationError for error code ${errorCode}`, async () => {
901
+ const message = 'a message';
902
+ const meetingInfoData = {meetingInfo: {registrationUrl: 'registrationUrl'}};
903
+
904
+ webex.request = sinon.stub().rejects({
905
+ statusCode: 403,
906
+ body: {message, code: errorCode, data: {meetingInfo: meetingInfoData}},
907
+ });
908
+ try {
909
+ await meetingInfo.createAdhocSpaceMeeting(conversationUrl, installedOrgID);
910
+ assert.fail('createAdhocSpaceMeeting should have thrown, but has not done that');
911
+ } catch (err) {
912
+ assert.instanceOf(err, MeetingInfoV2WebinarRegistrationError);
913
+ assert.deepEqual(err.message, `${message}, code=${errorCode}`);
914
+ assert.equal(err.wbxAppApiCode, errorCode);
915
+ assert.deepEqual(err.meetingInfo, meetingInfoData);
916
+
917
+ assert(Metrics.sendBehavioralMetric.calledOnce);
918
+ assert.calledWith(
919
+ Metrics.sendBehavioralMetric,
920
+ BEHAVIORAL_METRICS.WEBINAR_REGISTRATION_ERROR,
921
+ {code: errorCode}
922
+ );
923
+
924
+ }
925
+ });
926
+ }
927
+ );
891
928
  });
892
929
  });
893
930
  });