@webex/plugin-meetings 3.10.0-webex-services-ready.1 → 3.10.0-webex-services-ready.3

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.
@@ -6,9 +6,11 @@ export declare const ObjectType: {
6
6
  readonly mediaShare: "mediashare";
7
7
  readonly info: "info";
8
8
  readonly fullState: "fullstate";
9
+ readonly links: "links";
9
10
  };
10
11
  export type ObjectType = Enum<typeof ObjectType>;
11
12
  export declare const ObjectTypeToLocusKeyMap: {
13
+ links: string;
12
14
  info: string;
13
15
  fullstate: string;
14
16
  self: string;
@@ -1,7 +1,7 @@
1
1
  import EventsScope from '../common/events/events-scope';
2
2
  import { LOCUSEVENT } from '../constants';
3
3
  import HashTreeParser, { DataSet, HashTreeMessage, HashTreeObject } from '../hashTree/hashTreeParser';
4
- import { LocusDTO } from './types';
4
+ import { Links, LocusDTO } from './types';
5
5
  export type LocusLLMEvent = {
6
6
  data: {
7
7
  eventType: typeof LOCUSEVENT.HASH_TREE_DATA_UPDATED;
@@ -29,10 +29,7 @@ export default class LocusInfo extends EventsScope {
29
29
  aclUrl: any;
30
30
  baseSequence: any;
31
31
  created: any;
32
- identities: any;
33
- membership: any;
34
32
  participants: any;
35
- participantsUrl: any;
36
33
  replaces: any;
37
34
  scheduledMeeting: any;
38
35
  sequence: any;
@@ -44,10 +41,8 @@ export default class LocusInfo extends EventsScope {
44
41
  info: any;
45
42
  roles: any;
46
43
  mediaShares: any;
47
- replace: any;
48
44
  url: any;
49
- services: any;
50
- resources: any;
45
+ links?: Links;
51
46
  mainSessionLocusCache: any;
52
47
  self: any;
53
48
  hashTreeParser?: HashTreeParser;
@@ -278,21 +273,12 @@ export default class LocusInfo extends EventsScope {
278
273
  */
279
274
  updateCreated(created: object): void;
280
275
  /**
281
- * @param {Object} services
276
+ * Updates links and emits appropriate events if services or resources have changed
277
+ * @param {Object} links
282
278
  * @returns {undefined}
283
279
  * @memberof LocusInfo
284
280
  */
285
- updateServices(services: Record<'breakout' | 'record', {
286
- url: string;
287
- }>): void;
288
- /**
289
- * @param {Object} resources
290
- * @returns {undefined}
291
- * @memberof LocusInfo
292
- */
293
- updateResources(resources: Record<'webcastInstance', {
294
- url: string;
295
- }>): void;
281
+ updateLinks(links?: Links): void;
296
282
  /**
297
283
  * @param {Object} fullState
298
284
  * @returns {undefined}
@@ -330,17 +316,11 @@ export default class LocusInfo extends EventsScope {
330
316
  */
331
317
  updateMediaShares(mediaShares: object, forceUpdate?: boolean): void;
332
318
  /**
333
- * @param {String} participantsUrl
334
- * @returns {undefined}
335
- * @memberof LocusInfo
336
- */
337
- updateParticipantsUrl(participantsUrl: string): void;
338
- /**
339
- * @param {Object} replace
319
+ * @param {Object} replaces
340
320
  * @returns {undefined}
341
321
  * @memberof LocusInfo
342
322
  */
343
- updateReplace(replace: object): void;
323
+ updateReplaces(replaces: object): void;
344
324
  /**
345
325
  * handles when the locus.self is updated
346
326
  * @param {Object} self the new locus.self
@@ -375,18 +355,6 @@ export default class LocusInfo extends EventsScope {
375
355
  * @memberof LocusInfo
376
356
  */
377
357
  updateSequence(sequence: number): void;
378
- /**
379
- * @param {Object} membership
380
- * @returns {undefined}
381
- * @memberof LocusInfo
382
- */
383
- updateMemberShip(membership: object): void;
384
- /**
385
- * @param {Array} identities
386
- * @returns {undefined}
387
- * @memberof LocusInfo
388
- */
389
- updateIdentifiers(identities: Array<any>): void;
390
358
  /**
391
359
  * check the locus is main session's one or not, if is main session's, update main session cache
392
360
  * @param {Object} locus
@@ -10,6 +10,14 @@ export type LocusFullState = {
10
10
  state: string;
11
11
  type: string;
12
12
  };
13
+ export type Links = {
14
+ services: Record<'breakout' | 'record', {
15
+ url: string;
16
+ }>;
17
+ resources: Record<'webcastInstance' | 'visibleDataSets', {
18
+ url: string;
19
+ }>;
20
+ };
13
21
  export type LocusDTO = {
14
22
  controls?: any;
15
23
  fullState?: LocusFullState;
@@ -25,7 +33,7 @@ export type LocusDTO = {
25
33
  jsSdkMeta?: {
26
34
  removedParticipantIds: string[];
27
35
  };
28
- links?: any;
36
+ links?: Links;
29
37
  mediaShares?: any[];
30
38
  meetings?: any[];
31
39
  participants: any[];
@@ -448,7 +448,7 @@ var Webinar = _webexCore.WebexPlugin.extend({
448
448
  }, _callee7);
449
449
  }))();
450
450
  },
451
- version: "3.10.0-webex-services-ready.1"
451
+ version: "3.10.0-webex-services-ready.3"
452
452
  });
453
453
  var _default = exports.default = Webinar;
454
454
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -43,12 +43,12 @@
43
43
  "@webex/eslint-config-legacy": "0.0.0",
44
44
  "@webex/jest-config-legacy": "0.0.0",
45
45
  "@webex/legacy-tools": "0.0.0",
46
- "@webex/plugin-rooms": "3.10.0-webex-services-ready.1",
47
- "@webex/test-helper-chai": "3.10.0-webex-services-ready.0",
48
- "@webex/test-helper-mocha": "3.10.0-webex-services-ready.0",
49
- "@webex/test-helper-mock-webex": "3.10.0-webex-services-ready.0",
50
- "@webex/test-helper-retry": "3.10.0-webex-services-ready.0",
51
- "@webex/test-helper-test-users": "3.10.0-webex-services-ready.0",
46
+ "@webex/plugin-rooms": "3.10.0-webex-services-ready.3",
47
+ "@webex/test-helper-chai": "3.10.0-webex-services-ready.2",
48
+ "@webex/test-helper-mocha": "3.10.0-webex-services-ready.2",
49
+ "@webex/test-helper-mock-webex": "3.10.0-webex-services-ready.2",
50
+ "@webex/test-helper-retry": "3.10.0-webex-services-ready.2",
51
+ "@webex/test-helper-test-users": "3.10.0-webex-services-ready.2",
52
52
  "chai": "^4.3.4",
53
53
  "chai-as-promised": "^7.1.1",
54
54
  "eslint": "^8.24.0",
@@ -60,23 +60,23 @@
60
60
  "typescript": "^4.7.4"
61
61
  },
62
62
  "dependencies": {
63
- "@webex/common": "3.10.0-webex-services-ready.0",
63
+ "@webex/common": "3.10.0-webex-services-ready.2",
64
64
  "@webex/event-dictionary-ts": "^1.0.1930",
65
65
  "@webex/internal-media-core": "2.20.3",
66
- "@webex/internal-plugin-conversation": "3.10.0-webex-services-ready.1",
67
- "@webex/internal-plugin-device": "3.10.0-webex-services-ready.1",
68
- "@webex/internal-plugin-llm": "3.10.0-webex-services-ready.1",
69
- "@webex/internal-plugin-mercury": "3.10.0-webex-services-ready.1",
70
- "@webex/internal-plugin-metrics": "3.10.0-webex-services-ready.1",
71
- "@webex/internal-plugin-support": "3.10.0-webex-services-ready.1",
72
- "@webex/internal-plugin-user": "3.10.0-webex-services-ready.1",
73
- "@webex/internal-plugin-voicea": "3.10.0-webex-services-ready.1",
74
- "@webex/media-helpers": "3.10.0-webex-services-ready.0",
75
- "@webex/plugin-people": "3.10.0-webex-services-ready.1",
76
- "@webex/plugin-rooms": "3.10.0-webex-services-ready.1",
66
+ "@webex/internal-plugin-conversation": "3.10.0-webex-services-ready.3",
67
+ "@webex/internal-plugin-device": "3.10.0-webex-services-ready.3",
68
+ "@webex/internal-plugin-llm": "3.10.0-webex-services-ready.3",
69
+ "@webex/internal-plugin-mercury": "3.10.0-webex-services-ready.3",
70
+ "@webex/internal-plugin-metrics": "3.10.0-webex-services-ready.3",
71
+ "@webex/internal-plugin-support": "3.10.0-webex-services-ready.3",
72
+ "@webex/internal-plugin-user": "3.10.0-webex-services-ready.3",
73
+ "@webex/internal-plugin-voicea": "3.10.0-webex-services-ready.3",
74
+ "@webex/media-helpers": "3.10.0-webex-services-ready.2",
75
+ "@webex/plugin-people": "3.10.0-webex-services-ready.3",
76
+ "@webex/plugin-rooms": "3.10.0-webex-services-ready.3",
77
77
  "@webex/ts-sdp": "^1.8.1",
78
78
  "@webex/web-capabilities": "^1.7.1",
79
- "@webex/webex-core": "3.10.0-webex-services-ready.1",
79
+ "@webex/webex-core": "3.10.0-webex-services-ready.3",
80
80
  "ampersand-collection": "^2.0.2",
81
81
  "bowser": "^2.11.0",
82
82
  "btoa": "^1.2.1",
@@ -93,5 +93,5 @@
93
93
  "//": [
94
94
  "TODO: upgrade jwt-decode when moving to node 18"
95
95
  ],
96
- "version": "3.10.0-webex-services-ready.1"
96
+ "version": "3.10.0-webex-services-ready.3"
97
97
  }
@@ -1,4 +1,4 @@
1
- import {XXH3_128} from 'xxh3-ts';
1
+ import {XXH3_128} from 'xxh3-ts/xxh3';
2
2
  import {EMPTY_HASH} from './constants';
3
3
  import {ObjectType} from './types';
4
4
 
@@ -8,12 +8,14 @@ export const ObjectType = {
8
8
  mediaShare: 'mediashare',
9
9
  info: 'info',
10
10
  fullState: 'fullstate',
11
+ links: 'links',
11
12
  } as const;
12
13
 
13
14
  export type ObjectType = Enum<typeof ObjectType>;
14
15
 
15
16
  // mapping from ObjectType to top level LocusDTO keys
16
17
  export const ObjectTypeToLocusKeyMap = {
18
+ [ObjectType.links]: 'links',
17
19
  [ObjectType.info]: 'info',
18
20
  [ObjectType.fullState]: 'fullState',
19
21
  [ObjectType.self]: 'self',
@@ -18,6 +18,7 @@ import {
18
18
  CALL_REMOVED_REASON,
19
19
  RECORDING_STATE,
20
20
  Enum,
21
+ SELF_ROLES,
21
22
  } from '../constants';
22
23
 
23
24
  import InfoUtils from './infoUtils';
@@ -38,7 +39,7 @@ import HashTreeParser, {
38
39
  LocusInfoUpdateType,
39
40
  } from '../hashTree/hashTreeParser';
40
41
  import {ObjectType, ObjectTypeToLocusKeyMap} from '../hashTree/types';
41
- import {LocusDTO, LocusFullState} from './types';
42
+ import {Links, LocusDTO, LocusFullState} from './types';
42
43
 
43
44
  export type LocusLLMEvent = {
44
45
  data: {
@@ -96,10 +97,7 @@ export default class LocusInfo extends EventsScope {
96
97
  aclUrl: any;
97
98
  baseSequence: any;
98
99
  created: any;
99
- identities: any;
100
- membership: any;
101
100
  participants: any;
102
- participantsUrl: any;
103
101
  replaces: any;
104
102
  scheduledMeeting: any;
105
103
  sequence: any;
@@ -111,10 +109,8 @@ export default class LocusInfo extends EventsScope {
111
109
  info: any;
112
110
  roles: any;
113
111
  mediaShares: any;
114
- replace: any;
115
112
  url: any;
116
- services: any;
117
- resources: any;
113
+ links?: Links;
118
114
  mainSessionLocusCache: any;
119
115
  self: any;
120
116
  hashTreeParser?: HashTreeParser;
@@ -328,13 +324,10 @@ export default class LocusInfo extends EventsScope {
328
324
  init(locus: any = {}) {
329
325
  this.created = locus.created || null;
330
326
  this.scheduledMeeting = locus.meeting || null;
331
- this.participantsUrl = locus.participantsUrl || null;
332
327
  this.replaces = locus.replaces || null;
333
328
  this.aclUrl = locus.aclUrl || null;
334
329
  this.baseSequence = locus.baseSequence || null;
335
330
  this.sequence = locus.sequence || null;
336
- this.membership = locus.membership || null;
337
- this.identities = locus.identities || null;
338
331
  this.participants = locus.participants || null;
339
332
 
340
333
  /**
@@ -360,8 +353,7 @@ export default class LocusInfo extends EventsScope {
360
353
  this.updateSelf(locus.self);
361
354
  this.updateHostInfo(locus.host);
362
355
  this.updateMediaShares(locus.mediaShares);
363
- this.updateServices(locus.links?.services);
364
- this.updateResources(locus.links?.resources);
356
+ this.updateLinks(locus.links);
365
357
  }
366
358
 
367
359
  /**
@@ -514,6 +506,14 @@ export default class LocusInfo extends EventsScope {
514
506
  updateLocusFromHashTreeObject(object: HashTreeObject, locus: LocusDTO): LocusDTO {
515
507
  const type = object.htMeta.elementId.type.toLowerCase();
516
508
 
509
+ const addParticipantObject = (obj: HashTreeObject) => {
510
+ if (!locus.participants) {
511
+ locus.participants = [];
512
+ }
513
+ locus.participants.push(obj.data);
514
+ this.hashTreeObjectId2ParticipantId.set(obj.htMeta.elementId.id, obj.data.id);
515
+ };
516
+
517
517
  switch (type) {
518
518
  case ObjectType.locus: {
519
519
  if (!object.data) {
@@ -579,13 +579,7 @@ export default class LocusInfo extends EventsScope {
579
579
  } ${object.data ? 'updated' : 'removed'} version=${object.htMeta.elementId.version}`
580
580
  );
581
581
  if (object.data) {
582
- if (!locus.participants) {
583
- locus.participants = [];
584
- }
585
- const participantObject = object.data;
586
- participantObject.htMeta = object.htMeta;
587
- locus.participants.push(participantObject);
588
- this.hashTreeObjectId2ParticipantId.set(object.htMeta.elementId.id, participantObject.id);
582
+ addParticipantObject(object);
589
583
  } else {
590
584
  const participantId = this.hashTreeObjectId2ParticipantId.get(object.htMeta.elementId.id);
591
585
 
@@ -596,12 +590,13 @@ export default class LocusInfo extends EventsScope {
596
590
  this.hashTreeObjectId2ParticipantId.delete(object.htMeta.elementId.id);
597
591
  }
598
592
  break;
593
+ case ObjectType.links:
599
594
  case ObjectType.info:
600
595
  case ObjectType.fullState:
601
596
  case ObjectType.self:
602
597
  if (!object.data) {
603
598
  // self without data is handled inside HashTreeParser and results in LocusInfoUpdateType.MEETING_ENDED, so we should never get here
604
- // other types like info or fullstate - Locus should never send them without data
599
+ // all other types info, fullstate, etc - Locus should never send them without data
605
600
  LoggerProxy.logger.warn(
606
601
  `Locus-info:index#updateLocusFromHashTreeObject --> received ${type} object without data, this is not expected! version=${object.htMeta.elementId.version}`
607
602
  );
@@ -611,6 +606,24 @@ export default class LocusInfo extends EventsScope {
611
606
  );
612
607
  const locusDtoKey = ObjectTypeToLocusKeyMap[type];
613
608
  locus[locusDtoKey] = object.data;
609
+
610
+ /* Hash tree based webinar attendees don't receive a Participant object for themselves from Locus,
611
+ but a lot of existing code in SDK and web app expects a member object for self to exist,
612
+ so whenever SELF changes for a webinar attendee, we copy it into a participant object.
613
+ We can do it, because SELF has always all the same properties as a participant object.
614
+ */
615
+ if (
616
+ type === ObjectType.self &&
617
+ locus.info?.isWebinar &&
618
+ object.data.controls?.role?.roles?.find(
619
+ (r) => r.type === SELF_ROLES.ATTENDEE && r.hasRole
620
+ )
621
+ ) {
622
+ LoggerProxy.logger.info(
623
+ `Locus-info:index#updateLocusFromHashTreeObject --> webinar attendee: creating participant object from self`
624
+ );
625
+ addParticipantObject(object);
626
+ }
614
627
  }
615
628
  break;
616
629
  default:
@@ -1023,17 +1036,13 @@ export default class LocusInfo extends EventsScope {
1023
1036
  this.updateLocusUrl(locus.url, ControlsUtils.isMainSessionDTO(locus));
1024
1037
  this.updateMeetingInfo(locus.info, locus.self);
1025
1038
  this.updateMediaShares(locus.mediaShares);
1026
- this.updateParticipantsUrl(locus.participantsUrl);
1027
- this.updateReplace(locus.replace);
1039
+ this.updateReplaces(locus.replaces);
1028
1040
  this.updateSelf(locus.self);
1029
1041
  this.updateAclUrl(locus.aclUrl);
1030
1042
  this.updateBasequence(locus.baseSequence);
1031
1043
  this.updateSequence(locus.sequence);
1032
- this.updateMemberShip(locus.membership);
1033
- this.updateIdentifiers(locus.identities);
1034
1044
  this.updateEmbeddedApps(locus.embeddedApps);
1035
- this.updateServices(locus.links?.services);
1036
- this.updateResources(locus.links?.resources);
1045
+ this.updateLinks(locus.links);
1037
1046
  this.compareAndUpdate();
1038
1047
  // update which required to compare different objects from locus
1039
1048
  }
@@ -1685,17 +1694,19 @@ export default class LocusInfo extends EventsScope {
1685
1694
  }
1686
1695
 
1687
1696
  /**
1688
- * @param {Object} services
1697
+ * Updates links and emits appropriate events if services or resources have changed
1698
+ * @param {Object} links
1689
1699
  * @returns {undefined}
1690
1700
  * @memberof LocusInfo
1691
1701
  */
1692
- updateServices(services: Record<'breakout' | 'record', {url: string}>) {
1693
- if (services && !isEqual(this.services, services)) {
1694
- this.services = services;
1702
+ updateLinks(links?: Links) {
1703
+ const {services, resources} = links || {};
1704
+
1705
+ if (services && !isEqual(this.links?.services, services)) {
1695
1706
  this.emitScoped(
1696
1707
  {
1697
1708
  file: 'locus-info',
1698
- function: 'updateServices',
1709
+ function: 'updateLinks',
1699
1710
  },
1700
1711
  LOCUSINFO.EVENTS.LINKS_SERVICES,
1701
1712
  {
@@ -1703,20 +1714,12 @@ export default class LocusInfo extends EventsScope {
1703
1714
  }
1704
1715
  );
1705
1716
  }
1706
- }
1707
1717
 
1708
- /**
1709
- * @param {Object} resources
1710
- * @returns {undefined}
1711
- * @memberof LocusInfo
1712
- */
1713
- updateResources(resources: Record<'webcastInstance', {url: string}>) {
1714
- if (resources && !isEqual(this.resources, resources)) {
1715
- this.resources = resources;
1718
+ if (resources && !isEqual(this.links?.resources, resources)) {
1716
1719
  this.emitScoped(
1717
1720
  {
1718
1721
  file: 'locus-info',
1719
- function: 'updateResources',
1722
+ function: 'updateLinks',
1720
1723
  },
1721
1724
  LOCUSINFO.EVENTS.LINKS_RESOURCES,
1722
1725
  {
@@ -1724,6 +1727,8 @@ export default class LocusInfo extends EventsScope {
1724
1727
  }
1725
1728
  );
1726
1729
  }
1730
+
1731
+ this.links = links;
1727
1732
  }
1728
1733
 
1729
1734
  /**
@@ -1910,24 +1915,13 @@ export default class LocusInfo extends EventsScope {
1910
1915
  }
1911
1916
 
1912
1917
  /**
1913
- * @param {String} participantsUrl
1914
- * @returns {undefined}
1915
- * @memberof LocusInfo
1916
- */
1917
- updateParticipantsUrl(participantsUrl: string) {
1918
- if (participantsUrl && !isEqual(this.participantsUrl, participantsUrl)) {
1919
- this.participantsUrl = participantsUrl;
1920
- }
1921
- }
1922
-
1923
- /**
1924
- * @param {Object} replace
1918
+ * @param {Object} replaces
1925
1919
  * @returns {undefined}
1926
1920
  * @memberof LocusInfo
1927
1921
  */
1928
- updateReplace(replace: object) {
1929
- if (replace && !isEqual(this.replace, replace)) {
1930
- this.replace = replace;
1922
+ updateReplaces(replaces: object) {
1923
+ if (replaces && !isEqual(this.replaces, replaces)) {
1924
+ this.replaces = replaces;
1931
1925
  }
1932
1926
  }
1933
1927
 
@@ -2264,28 +2258,6 @@ export default class LocusInfo extends EventsScope {
2264
2258
  }
2265
2259
  }
2266
2260
 
2267
- /**
2268
- * @param {Object} membership
2269
- * @returns {undefined}
2270
- * @memberof LocusInfo
2271
- */
2272
- updateMemberShip(membership: object) {
2273
- if (membership && !isEqual(this.membership, membership)) {
2274
- this.membership = membership;
2275
- }
2276
- }
2277
-
2278
- /**
2279
- * @param {Array} identities
2280
- * @returns {undefined}
2281
- * @memberof LocusInfo
2282
- */
2283
- updateIdentifiers(identities: Array<any>) {
2284
- if (identities && !isEqual(this.identities, identities)) {
2285
- this.identities = identities;
2286
- }
2287
- }
2288
-
2289
2261
  /**
2290
2262
  * check the locus is main session's one or not, if is main session's, update main session cache
2291
2263
  * @param {Object} locus
@@ -12,6 +12,11 @@ export type LocusFullState = {
12
12
  type: string;
13
13
  };
14
14
 
15
+ export type Links = {
16
+ services: Record<'breakout' | 'record', {url: string}>; // there exist also other services, but these are the ones we currently use
17
+ resources: Record<'webcastInstance' | 'visibleDataSets', {url: string}>; // there exist also other resources, but these are the ones we currently use
18
+ };
19
+
15
20
  export type LocusDTO = {
16
21
  controls?: any;
17
22
  fullState?: LocusFullState;
@@ -27,7 +32,7 @@ export type LocusDTO = {
27
32
  jsSdkMeta?: {
28
33
  removedParticipantIds: string[]; // list of ids of participants that are removed in the last update
29
34
  };
30
- links?: any;
35
+ links?: Links;
31
36
  mediaShares?: any[];
32
37
  meetings?: any[];
33
38
  participants: any[];
@@ -1,5 +1,5 @@
1
1
  import uuid from 'uuid';
2
- import {cloneDeep, isEqual, isEmpty} from 'lodash';
2
+ import {cloneDeep, isEqual, isEmpty, merge} from 'lodash';
3
3
  import jwtDecode from 'jwt-decode';
4
4
  // @ts-ignore - Fix this
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
@@ -50,6 +50,11 @@ import {
50
50
  type MeetingTranscriptPayload,
51
51
  } from '@webex/internal-plugin-voicea';
52
52
 
53
+ import {
54
+ getBrowserMediaErrorCode,
55
+ isBrowserMediaError,
56
+ isBrowserMediaErrorName,
57
+ } from '@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util';
53
58
  import {processNewCaptions} from './voicea-meeting';
54
59
 
55
60
  import {
@@ -5440,6 +5445,20 @@ export default class Meeting extends StatelessWebexPlugin {
5440
5445
  shouldRetry = false;
5441
5446
  }
5442
5447
 
5448
+ if (CallDiagnosticUtils.isBrowserMediaError(error)) {
5449
+ shouldRetry = false;
5450
+ // eslint-disable-next-line no-ex-assign
5451
+ error = merge({
5452
+ error: {
5453
+ body: {
5454
+ errorCode: CallDiagnosticUtils.getBrowserMediaErrorCode(error),
5455
+ message: error?.message,
5456
+ name: error?.name,
5457
+ },
5458
+ },
5459
+ });
5460
+ }
5461
+
5443
5462
  // we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
5444
5463
  if (joined && (isRetry || !shouldRetry)) {
5445
5464
  try {