@stream-io/video-client 1.18.2 → 1.18.4

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.
@@ -3,7 +3,6 @@ import {
3
3
  addConnectionEventListeners,
4
4
  isCloseEvent,
5
5
  KnownCodes,
6
- randomId,
7
6
  removeConnectionEventListeners,
8
7
  retryInterval,
9
8
  sleep,
@@ -44,19 +43,18 @@ export class StableWSConnection {
44
43
  isConnecting: boolean;
45
44
  isDisconnected: boolean;
46
45
  isHealthy: boolean;
47
- isResolved?: boolean;
46
+ isConnectionOpenResolved?: boolean;
48
47
  lastEvent: Date | null;
49
48
  connectionCheckTimeout: number;
50
49
  connectionCheckTimeoutRef?: NodeJS.Timeout;
51
- rejectPromise?: (
50
+ rejectConnectionOpen?: (
52
51
  reason?: Error & {
53
52
  code?: string | number;
54
53
  isWSFailure?: boolean;
55
54
  StatusCode?: string | number;
56
55
  },
57
56
  ) => void;
58
- requestID: string | undefined;
59
- resolvePromise?: (value: ConnectedEvent) => void;
57
+ resolveConnectionOpen?: (value: ConnectedEvent) => void;
60
58
  totalFailures: number;
61
59
  ws?: WebSocket;
62
60
  wsID: number;
@@ -74,7 +72,7 @@ export class StableWSConnection {
74
72
  /** To avoid reconnect if client is disconnected */
75
73
  this.isDisconnected = false;
76
74
  /** Boolean that indicates if the connection promise is resolved */
77
- this.isResolved = false;
75
+ this.isConnectionOpenResolved = false;
78
76
  /** Boolean that indicates if we have a working connection to the server */
79
77
  this.isHealthy = false;
80
78
  /** Incremented when a new WS connection is made */
@@ -89,9 +87,7 @@ export class StableWSConnection {
89
87
  }
90
88
 
91
89
  _log = (msg: string, extra: UR = {}, level: LogLevel = 'info') => {
92
- this.client.logger(level, 'connection:' + msg, {
93
- ...extra,
94
- });
90
+ this.client.logger(level, `connection:${msg}`, extra);
95
91
  };
96
92
 
97
93
  setClient = (client: StreamClient) => {
@@ -285,9 +281,8 @@ export class StableWSConnection {
285
281
  * @return {ConnectAPIResponse<ConnectedEvent>} Promise that completes once the first health check message is received
286
282
  */
287
283
  async _connect() {
288
- if (this.isConnecting) return; // simply ignore _connect if it's currently trying to connect
284
+ if (this.isConnecting) return; // ignore _connect if it's currently trying to connect
289
285
  this.isConnecting = true;
290
- this.requestID = randomId();
291
286
  let isTokenReady = false;
292
287
  try {
293
288
  this._log(`_connect() - waiting for token`);
@@ -310,10 +305,7 @@ export class StableWSConnection {
310
305
  }
311
306
  this._setupConnectionPromise();
312
307
  const wsURL = this._buildUrl();
313
- this._log(`_connect() - Connecting to ${wsURL}`, {
314
- wsURL,
315
- requestID: this.requestID,
316
- });
308
+ this._log(`_connect() - Connecting to ${wsURL}`);
317
309
  const WS = this.client.options.WebSocketImpl ?? WebSocket;
318
310
  this.ws = new WS(wsURL);
319
311
  this.ws.onopen = this.onopen.bind(this, this.wsID);
@@ -459,7 +451,7 @@ export class StableWSConnection {
459
451
  return;
460
452
  }
461
453
 
462
- const authMessage: WSAuthMessage = {
454
+ const authMessage = JSON.stringify({
463
455
  token,
464
456
  user_details: {
465
457
  id: user.id,
@@ -467,9 +459,11 @@ export class StableWSConnection {
467
459
  image: user.image,
468
460
  custom: user.custom,
469
461
  },
470
- };
462
+ } as WSAuthMessage);
463
+
464
+ this._log(`onopen() - Sending auth message ${authMessage}`, {}, 'trace');
471
465
 
472
- this.ws?.send(JSON.stringify(authMessage));
466
+ this.ws?.send(authMessage);
473
467
  this._log('onopen() - onopen callback', { wsID });
474
468
  };
475
469
 
@@ -485,10 +479,14 @@ export class StableWSConnection {
485
479
  // we wait till the first message before we consider the connection open.
486
480
  // the reason for this is that auth errors and similar errors trigger a ws.onopen and immediately
487
481
  // after that a ws.onclose.
488
- if (!this.isResolved && data && data.type === 'connection.error') {
489
- this.isResolved = true;
482
+ if (
483
+ !this.isConnectionOpenResolved &&
484
+ data &&
485
+ data.type === 'connection.error'
486
+ ) {
487
+ this.isConnectionOpenResolved = true;
490
488
  if (data.error) {
491
- this.rejectPromise?.(this._errorFromWSEvent(data, false));
489
+ this.rejectConnectionOpen?.(this._errorFromWSEvent(data, false));
492
490
  return;
493
491
  }
494
492
  }
@@ -505,7 +503,7 @@ export class StableWSConnection {
505
503
  }
506
504
 
507
505
  if (data && data.type === 'connection.ok') {
508
- this.resolvePromise?.(data);
506
+ this.resolveConnectionOpen?.(data);
509
507
  this._setHealth(true);
510
508
  }
511
509
 
@@ -554,7 +552,7 @@ export class StableWSConnection {
554
552
  // @ts-expect-error
555
553
  error.target = event.target;
556
554
 
557
- this.rejectPromise?.(error);
555
+ this.rejectConnectionOpen?.(error);
558
556
  this._log(`onclose() - WS connection reject with error ${event.reason}`, {
559
557
  event,
560
558
  });
@@ -564,7 +562,7 @@ export class StableWSConnection {
564
562
  this._setHealth(false);
565
563
  this.isConnecting = false;
566
564
 
567
- this.rejectPromise?.(this._errorFromWSEvent(event));
565
+ this.rejectConnectionOpen?.(this._errorFromWSEvent(event));
568
566
 
569
567
  this._log(`onclose() - WS connection closed. Calling reconnect ...`, {
570
568
  event,
@@ -582,7 +580,7 @@ export class StableWSConnection {
582
580
  this.totalFailures += 1;
583
581
  this._setHealth(false);
584
582
  this.isConnecting = false;
585
- this.rejectPromise?.(new Error(`WebSocket error: ${event}`));
583
+ this.rejectConnectionOpen?.(new Error(`WebSocket error: ${event}`));
586
584
  this._log(`onerror() - WS connection resulted into error`, { event });
587
585
 
588
586
  this._reconnect();
@@ -676,12 +674,12 @@ export class StableWSConnection {
676
674
  * _setupPromise - sets up the this.connectOpen promise
677
675
  */
678
676
  _setupConnectionPromise = () => {
679
- this.isResolved = false;
677
+ this.isConnectionOpenResolved = false;
680
678
  /** a promise that is resolved once ws.open is called */
681
679
  this.connectionOpenSafe = makeSafePromise(
682
680
  new Promise<ConnectedEvent>((resolve, reject) => {
683
- this.resolvePromise = resolve;
684
- this.rejectPromise = reject;
681
+ this.resolveConnectionOpen = resolve;
682
+ this.rejectConnectionOpen = reject;
685
683
  }),
686
684
  );
687
685
  };
@@ -8,8 +8,8 @@ import type { TokenOrProvider, UserWithId } from './types';
8
8
  * Handles all the operations around user token.
9
9
  */
10
10
  export class TokenManager {
11
- private loadTokenPromise: Promise<string> | null;
12
- private type: 'static' | 'provider';
11
+ private loadTokenPromise: Promise<string> | null = null;
12
+ private type: 'static' | 'provider' = 'static';
13
13
  private readonly secret?: string;
14
14
  private token?: string;
15
15
  private tokenProvider?: TokenOrProvider;
@@ -17,9 +17,7 @@ export class TokenManager {
17
17
  private isAnonymous?: boolean;
18
18
 
19
19
  constructor(secret?: string) {
20
- this.loadTokenPromise = null;
21
20
  this.secret = secret;
22
- this.type = 'static';
23
21
  }
24
22
 
25
23
  /**
@@ -74,11 +72,7 @@ export class TokenManager {
74
72
  throw new Error('User token can not be empty');
75
73
  }
76
74
 
77
- if (
78
- tokenOrProvider &&
79
- typeof tokenOrProvider !== 'string' &&
80
- !isFunction(tokenOrProvider)
81
- ) {
75
+ if (typeof tokenOrProvider !== 'string' && !isFunction(tokenOrProvider)) {
82
76
  throw new Error('User token should either be a string or a function');
83
77
  }
84
78
 
@@ -157,6 +157,23 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
157
157
  * The client app identifier.
158
158
  */
159
159
  clientAppIdentifier?: ClientAppIdentifier;
160
+
161
+ /**
162
+ * The default timeout for WebSocket connections.
163
+ */
164
+ defaultWsTimeout?: number;
165
+
166
+ /**
167
+ * The maximum number of retries to connect a user.
168
+ */
169
+ maxConnectUserRetries?: number;
170
+
171
+ /**
172
+ * A callback to be called one the maxUserConnectRetries is exhausted.
173
+ * @param lastError the last error.
174
+ * @param allErrors all errors.
175
+ */
176
+ onConnectUserError?: (lastError: Error, allErrors: Error[]) => void;
160
177
  };
161
178
 
162
179
  export type ClientAppIdentifier = {
@@ -19,8 +19,6 @@ export function isFunction<T>(value: Function | T): value is Function {
19
19
  export const KnownCodes = {
20
20
  TOKEN_EXPIRED: 40,
21
21
  WS_CLOSED_SUCCESS: 1000,
22
- WS_CLOSED_ABRUPTLY: 1006,
23
- WS_POLICY_VIOLATION: 1008,
24
22
  };
25
23
 
26
24
  /**
@@ -28,17 +26,13 @@ export const KnownCodes = {
28
26
  *
29
27
  * @return {number} Duration to wait in milliseconds
30
28
  */
31
- export function retryInterval(numberOfFailures: number) {
29
+ export function retryInterval(numberOfFailures: number): number {
32
30
  // try to reconnect in 0.25-5 seconds (random to spread out the load from failures)
33
31
  const max = Math.min(500 + numberOfFailures * 2000, 5000);
34
32
  const min = Math.min(Math.max(250, (numberOfFailures - 1) * 2000), 5000);
35
33
  return Math.floor(Math.random() * (max - min) + min);
36
34
  }
37
35
 
38
- export function randomId() {
39
- return generateUUIDv4();
40
- }
41
-
42
36
  function hex(bytes: Uint8Array): string {
43
37
  let s = '';
44
38
  for (let i = 0; i < bytes.length; i++) {
@@ -53,38 +47,25 @@ export function generateUUIDv4() {
53
47
  bytes[6] = (bytes[6] & 0x0f) | 0x40; // version
54
48
  bytes[8] = (bytes[8] & 0xbf) | 0x80; // variant
55
49
 
56
- return (
57
- hex(bytes.subarray(0, 4)) +
58
- '-' +
59
- hex(bytes.subarray(4, 6)) +
60
- '-' +
61
- hex(bytes.subarray(6, 8)) +
62
- '-' +
63
- hex(bytes.subarray(8, 10)) +
64
- '-' +
65
- hex(bytes.subarray(10, 16))
66
- );
50
+ return [
51
+ hex(bytes.subarray(0, 4)),
52
+ hex(bytes.subarray(4, 6)),
53
+ hex(bytes.subarray(6, 8)),
54
+ hex(bytes.subarray(8, 10)),
55
+ hex(bytes.subarray(10, 16)),
56
+ ].join('-');
67
57
  }
68
58
 
69
- function getRandomValuesWithMathRandom(bytes: Uint8Array): void {
70
- const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
71
- for (let i = 0; i < bytes.length; i++) {
72
- bytes[i] = Math.random() * max;
73
- }
74
- }
75
- declare const msCrypto: Crypto;
76
-
77
59
  const getRandomValues = (() => {
78
- if (
79
- typeof crypto !== 'undefined' &&
80
- typeof crypto?.getRandomValues !== 'undefined'
81
- ) {
60
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
82
61
  return crypto.getRandomValues.bind(crypto);
83
- } else if (typeof msCrypto !== 'undefined') {
84
- return msCrypto.getRandomValues.bind(msCrypto);
85
- } else {
86
- return getRandomValuesWithMathRandom;
87
62
  }
63
+ return function getRandomValuesWithMathRandom(bytes: Uint8Array): void {
64
+ const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
65
+ for (let i = 0; i < bytes.length; i++) {
66
+ bytes[i] = Math.random() * max;
67
+ }
68
+ };
88
69
  })();
89
70
 
90
71
  function getRandomBytes(length: number): Uint8Array {
@@ -93,17 +74,6 @@ function getRandomBytes(length: number): Uint8Array {
93
74
  return bytes;
94
75
  }
95
76
 
96
- /**
97
- * Informs if a promise is yet to be resolved or rejected
98
- */
99
- export async function isPromisePending<T>(promise: Promise<T>) {
100
- const emptyObj = {};
101
- return Promise.race([promise, emptyObj]).then(
102
- (value) => value === emptyObj,
103
- () => false,
104
- );
105
- }
106
-
107
77
  /**
108
78
  * listenForConnectionChanges - Adds an event listener fired on browser going online or offline
109
79
  */
@@ -40,10 +40,9 @@ export class CameraManager extends InputMediaDeviceManager<CameraManagerState> {
40
40
  this.logger('warn', 'No video track found to do direction selection');
41
41
  return;
42
42
  }
43
- const constraints = {
43
+ await videoTrack.applyConstraints({
44
44
  facingMode: direction === 'front' ? 'user' : 'environment',
45
- };
46
- await videoTrack.applyConstraints(constraints);
45
+ });
47
46
  this.state.setDirection(direction);
48
47
  this.state.setDevice(undefined);
49
48
  } else {
@@ -227,11 +227,6 @@ export class Publisher extends BasePeerConnection {
227
227
  }
228
228
  for (const track of this.clonedTracks) {
229
229
  this.stopTrack(track);
230
- // @ts-expect-error release() is present in react-native-webrtc
231
- if (track && typeof track.release === 'function') {
232
- // @ts-expect-error
233
- track.release();
234
- }
235
230
  }
236
231
  };
237
232