@livedigital/client 2.4.0-beta.2 → 2.4.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.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "2.4.0-beta.2",
5
+ "version": "2.4.0",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -26,7 +26,6 @@ import { GetNodeRequest } from '../types/network';
26
26
  import VideoTrack from './media/tracks/VideoTrack';
27
27
  import AudioTrack from './media/tracks/AudioTrack';
28
28
  import PeerTrack from './media/tracks/PeerTrack';
29
- import { retryAsync } from '../helpers/retry';
30
29
 
31
30
  type EngineParams = {
32
31
  clientEventEmitter: EnhancedEventEmitter,
@@ -119,7 +118,7 @@ class Engine {
119
118
  this.network.socket.observer.on('state', ({ state }: { state: SocketIOEvents }) => {
120
119
  this.clientEventEmitter.emit(state);
121
120
 
122
- if (state === SocketIOEvents.Reconnecting) {
121
+ if (state === SocketIOEvents.Reconnected) {
123
122
  this.network.socket.disconnect();
124
123
  this.clientEventEmitter.emit(CLIENT_EVENTS.channelRejoinRequired);
125
124
  }
@@ -146,7 +145,12 @@ class Engine {
146
145
  try {
147
146
  this.logger.debug('join()', { params });
148
147
  this.isRoomJoining = true;
149
- await this.connectToSocketServerWithRetry(params);
148
+ const { webSocketUrl } = await this.getAvailableNode({
149
+ channelId: params.channelId,
150
+ role: params.role,
151
+ });
152
+ this.network.socket.connect(webSocketUrl);
153
+ await this.waitForSocketConnection();
150
154
  await this.performJoin(params);
151
155
  } catch (error) {
152
156
  this.logger.error('join()', { error });
@@ -420,37 +424,6 @@ class Engine {
420
424
  return this.app;
421
425
  }
422
426
 
423
- private async connectToSocketServerWithRetry(params: { channelId: string, role: Role }): Promise<void> {
424
- const connectToSocketServerAction = async () => this.connectToSocketServer(params);
425
- return retryAsync(connectToSocketServerAction, {
426
- maxRetries: 15,
427
- minBackoffDelayMs: 150,
428
- maxBackoffDelayMs: 5_000,
429
- actionName: 'connectToSocketServer',
430
- });
431
- }
432
-
433
- private async connectToSocketServer(params: { channelId: string, role: Role }): Promise<void> {
434
- const { webSocketUrl } = await this.getAvailableNode({
435
- channelId: params.channelId,
436
- role: params.role,
437
- });
438
- this.network.socket.connect(webSocketUrl);
439
-
440
- try {
441
- await this.waitForSocketConnection();
442
- } catch (error: unknown) {
443
- this.logger.error('Failed to connect to socket server', {
444
- error,
445
- role: params.role,
446
- channelId: params.channelId,
447
- });
448
-
449
- this.network.socket.disconnect();
450
- throw error;
451
- }
452
- }
453
-
454
427
  private async getAvailableNode(params: GetNodeRequest): Promise<{ webSocketUrl: string }> {
455
428
  try {
456
429
  const response = await this.network.loadBalancerClient.getNode(params);
@@ -465,17 +438,14 @@ class Engine {
465
438
  return new Promise((resolve, reject) => {
466
439
  const onSocketStateChange = async (data: { state: SocketIOEvents, error?: string }) => {
467
440
  const { error, state } = data;
468
- const stopListening = () => this.network.socket.observer.removeListener('state', onSocketStateChange);
469
- const isStateNotExpected = [SocketIOEvents.Disconnected, SocketIOEvents.Reconnecting].includes(state);
470
-
471
- if (error || (this.isRoomJoining && isStateNotExpected)) {
472
- stopListening();
473
- reject(error || 'Not expected socket state for new connection');
441
+ if (error) {
442
+ this.network.socket.observer.removeListener('state', onSocketStateChange);
443
+ reject(error);
474
444
  return;
475
445
  }
476
446
 
477
- if (this.isRoomJoining && [SocketIOEvents.Connected, SocketIOEvents.Reconnected].includes(state)) {
478
- stopListening();
447
+ if (this.isRoomJoining && state === SocketIOEvents.Connected) {
448
+ this.network.socket.observer.removeListener('state', onSocketStateChange);
479
449
  resolve();
480
450
  }
481
451
  };
@@ -486,7 +456,7 @@ class Engine {
486
456
 
487
457
  private async performJoin(params: JoinChannelParams): Promise<void> {
488
458
  try {
489
- await this.sendJoinChannelRequestWithRetry(params);
459
+ await this.network.socket.request(CHANNEL_EVENTS.channelJoin, params);
490
460
  await this.initialize();
491
461
  this.channelEventsHandler.subscribeToEvents();
492
462
  this.mediaSoupEventsHandler.subscribeToEvents();
@@ -500,19 +470,6 @@ class Engine {
500
470
  }
501
471
  }
502
472
 
503
- private async sendJoinChannelRequestWithRetry(params: JoinChannelParams): Promise<SocketResponse> {
504
- const joinChannelAction = async () => this.sendJoinChannelRequest(params);
505
- return retryAsync(joinChannelAction, {
506
- maxRetries: 3,
507
- minBackoffDelayMs: 300,
508
- actionName: 'sendJoinChannelRequest',
509
- });
510
- }
511
-
512
- private async sendJoinChannelRequest(params: JoinChannelParams): Promise<SocketResponse> {
513
- return this.network.socket.request(CHANNEL_EVENTS.channelJoin, params);
514
- }
515
-
516
473
  get cahPublish(): boolean {
517
474
  return this.peers.find((item) => item.isMe)?.role === 'host';
518
475
  }
@@ -45,7 +45,7 @@ class SocketIO {
45
45
  : SocketIOEvents.Reconnected;
46
46
 
47
47
  this.isConnected = true;
48
- this.setReconnectingAttempt(0);
48
+ this.reconnectingAttempt = 0;
49
49
 
50
50
  this.logger.debug('connection.on(`connect`)');
51
51
  this.observer.safeEmit('state', { state });
@@ -59,14 +59,14 @@ class SocketIO {
59
59
 
60
60
  connection.on('disconnect', ((reason: string) => {
61
61
  this.isConnected = false;
62
- this.setReconnectingAttempt(0);
62
+ this.reconnectingAttempt = 0;
63
63
  this.disconnectReason = reason;
64
- this.logger.warn('connection.on(`disconnect`)', reason);
64
+ this.logger.error('connection.on(`disconnect`)', reason);
65
65
  this.observer.safeEmit('state', { state: SocketIOEvents.Disconnected });
66
66
  }));
67
67
 
68
68
  connection.io.on('reconnect_attempt', (attempt: number) => {
69
- this.setReconnectingAttempt(attempt);
69
+ this.reconnectingAttempt = attempt;
70
70
  this.logger.warn('connection.on(`reconnect_attempt`)', attempt);
71
71
  this.observer.safeEmit('state', { state: SocketIOEvents.Reconnecting });
72
72
  });
@@ -82,8 +82,6 @@ class SocketIO {
82
82
  this.connection.offAny();
83
83
  this.connection.volatile.offAny();
84
84
  this.connection.disconnect();
85
- // we need to reset this counter in order not to get Reconnected event on retry
86
- this.setReconnectingAttempt(0);
87
85
  }
88
86
  }
89
87
 
@@ -105,10 +103,6 @@ class SocketIO {
105
103
  });
106
104
  });
107
105
  }
108
-
109
- private setReconnectingAttempt(attemptNo: number): void {
110
- this.reconnectingAttempt = attemptNo;
111
- }
112
106
  }
113
107
 
114
108
  export default SocketIO;
@@ -1,5 +0,0 @@
1
- declare class DefaultTimeProvider {
2
- sleepMs(ms: number): Promise<void>;
3
- now(): Date;
4
- }
5
- export default DefaultTimeProvider;
@@ -1,10 +0,0 @@
1
- export declare type RetryOpts = {
2
- maxRetries?: number;
3
- minBackoffDelayMs?: number;
4
- maxBackoffDelayMs?: number;
5
- actionName?: string;
6
- };
7
- declare const retryAsync: <ReturnType_1>(action: () => Promise<ReturnType_1>, opts?: RetryOpts) => Promise<ReturnType_1>;
8
- declare const retry: <ReturnType_1>(action: () => ReturnType_1, opts?: RetryOpts) => Promise<ReturnType_1>;
9
- export { retryAsync };
10
- export default retry;
@@ -1,13 +0,0 @@
1
- class DefaultTimeProvider {
2
- // eslint-disable-next-line class-methods-use-this
3
- async sleepMs(ms: number): Promise<void> {
4
- return new Promise((resolve) => setTimeout(resolve, ms));
5
- }
6
-
7
- // eslint-disable-next-line class-methods-use-this
8
- now(): Date {
9
- return new Date(Date.now());
10
- }
11
- }
12
-
13
- export default DefaultTimeProvider;
@@ -1,50 +0,0 @@
1
- import DefaultTimeProvider from './datetime';
2
- import Logger from '../engine/Logger';
3
-
4
- export type RetryOpts = {
5
- maxRetries?: number;
6
- minBackoffDelayMs?: number;
7
- maxBackoffDelayMs?: number;
8
- actionName?: string; // human-readable action name for debug purposes
9
- };
10
-
11
- const logger = new Logger('Retry');
12
- const timeProvider = new DefaultTimeProvider();
13
-
14
- const retryAsync = async <ReturnType>(
15
- action: () => Promise<ReturnType>,
16
- opts: RetryOpts = {},
17
- ): Promise<ReturnType> => {
18
- const maxRetries = opts.maxRetries || 2;
19
- const minBackoffDelayMs = opts.minBackoffDelayMs || 50;
20
- let currentAttempt = 0;
21
- let lastError;
22
-
23
- while (currentAttempt < maxRetries) {
24
- currentAttempt += 1;
25
-
26
- try {
27
- // eslint-disable-next-line no-await-in-loop
28
- return await action();
29
- } catch (err: unknown) {
30
- lastError = err;
31
- logger.warn('Action retry failed', currentAttempt, opts.actionName, err);
32
-
33
- if (currentAttempt < maxRetries) {
34
- const delay = 2 ** (currentAttempt - 1) * minBackoffDelayMs;
35
- // eslint-disable-next-line no-await-in-loop
36
- await timeProvider.sleepMs(Math.min(delay, opts.maxBackoffDelayMs ?? delay));
37
- }
38
- }
39
- }
40
-
41
- throw lastError;
42
- };
43
-
44
- const retry = async <ReturnType>(
45
- action: () => ReturnType,
46
- opts: RetryOpts = {},
47
- ): Promise<ReturnType> => retryAsync(async () => action(), opts);
48
-
49
- export { retryAsync };
50
- export default retry;