@stream-io/video-client 1.11.6 → 1.11.7

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 (31) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.browser.es.js +68 -562
  3. package/dist/index.browser.es.js.map +1 -1
  4. package/dist/index.cjs.js +53 -547
  5. package/dist/index.cjs.js.map +1 -1
  6. package/dist/index.es.js +68 -562
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/src/coordinator/connection/client.d.ts +0 -18
  9. package/dist/src/coordinator/connection/connection.d.ts +4 -12
  10. package/dist/src/coordinator/connection/signing.d.ts +1 -7
  11. package/dist/src/coordinator/connection/token_manager.d.ts +0 -2
  12. package/dist/src/coordinator/connection/types.d.ts +5 -6
  13. package/dist/src/coordinator/connection/utils.d.ts +6 -8
  14. package/package.json +6 -10
  15. package/src/__tests__/Call.test.ts +3 -2
  16. package/src/coordinator/connection/client.ts +12 -149
  17. package/src/coordinator/connection/connection.ts +40 -109
  18. package/src/coordinator/connection/signing.ts +31 -17
  19. package/src/coordinator/connection/token_manager.ts +3 -9
  20. package/src/coordinator/connection/types.ts +5 -9
  21. package/src/coordinator/connection/utils.ts +18 -50
  22. package/src/devices/__tests__/InputMediaDeviceManagerState.test.ts +13 -8
  23. package/src/devices/__tests__/mocks.ts +0 -4
  24. package/dist/src/coordinator/connection/base64.d.ts +0 -2
  25. package/dist/src/coordinator/connection/connection_fallback.d.ts +0 -39
  26. package/dist/src/coordinator/connection/errors.d.ts +0 -16
  27. package/dist/src/coordinator/connection/insights.d.ts +0 -57
  28. package/src/coordinator/connection/base64.ts +0 -80
  29. package/src/coordinator/connection/connection_fallback.ts +0 -242
  30. package/src/coordinator/connection/errors.ts +0 -80
  31. package/src/coordinator/connection/insights.ts +0 -88
package/dist/index.cjs.js CHANGED
@@ -9,8 +9,6 @@ var uaParserJs = require('ua-parser-js');
9
9
  var rxjs = require('rxjs');
10
10
  var SDP = require('sdp-transform');
11
11
  var https = require('https');
12
- var WebSocket$1 = require('isomorphic-ws');
13
- var base64Js = require('base64-js');
14
12
 
15
13
  function _interopNamespaceDefault(e) {
16
14
  var n = Object.create(null);
@@ -3195,42 +3193,6 @@ function getRandomBytes(length) {
3195
3193
  getRandomValues(bytes);
3196
3194
  return bytes;
3197
3195
  }
3198
- function convertErrorToJson(err) {
3199
- const jsonObj = {};
3200
- if (!err)
3201
- return jsonObj;
3202
- try {
3203
- Object.getOwnPropertyNames(err).forEach((key) => {
3204
- jsonObj[key] = Object.getOwnPropertyDescriptor(err, key);
3205
- });
3206
- }
3207
- catch (_) {
3208
- return {
3209
- error: 'failed to serialize the error',
3210
- };
3211
- }
3212
- return jsonObj;
3213
- }
3214
- /**
3215
- * isOnline safely return the navigator.online value for browser env
3216
- * if navigator is not in global object, it always return true
3217
- */
3218
- function isOnline(logger) {
3219
- const nav = typeof navigator !== 'undefined'
3220
- ? navigator
3221
- : typeof window !== 'undefined' && window.navigator
3222
- ? window.navigator
3223
- : undefined;
3224
- if (!nav) {
3225
- logger('warn', 'isOnline failed to access window.navigator and assume browser is online');
3226
- return true;
3227
- }
3228
- // RN navigator has undefined for onLine
3229
- if (typeof nav.onLine !== 'boolean') {
3230
- return true;
3231
- }
3232
- return nav.onLine;
3233
- }
3234
3196
  /**
3235
3197
  * listenForConnectionChanges - Adds an event listener fired on browser going online or offline
3236
3198
  */
@@ -3246,6 +3208,13 @@ function removeConnectionEventListeners(cb) {
3246
3208
  window.removeEventListener('online', cb);
3247
3209
  }
3248
3210
  }
3211
+ function isErrorResponse(res) {
3212
+ return !res.status || res.status < 200 || 300 <= res.status;
3213
+ }
3214
+ // Type guards to check WebSocket error type
3215
+ function isCloseEvent(res) {
3216
+ return res.code !== undefined;
3217
+ }
3249
3218
 
3250
3219
  /**
3251
3220
  * Checks whether we are using React Native
@@ -3349,7 +3318,7 @@ const retryable = async (rpc, signal) => {
3349
3318
  return result;
3350
3319
  };
3351
3320
 
3352
- const version = "1.11.6";
3321
+ const version = "1.11.7";
3353
3322
  const [major, minor, patch] = version.split('.');
3354
3323
  let sdkInfo = {
3355
3324
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -11521,68 +11490,6 @@ class Call {
11521
11490
  }
11522
11491
  }
11523
11492
 
11524
- class InsightMetrics {
11525
- constructor() {
11526
- this.connectionStartTimestamp = null;
11527
- this.wsTotalFailures = 0;
11528
- this.wsConsecutiveFailures = 0;
11529
- this.instanceClientId = randomId();
11530
- }
11531
- }
11532
- /**
11533
- * postInsights is not supposed to be used by end users directly within chat application, and thus is kept isolated
11534
- * from all the client/connection code/logic.
11535
- *
11536
- * @param insightType
11537
- * @param insights
11538
- */
11539
- const postInsights = async (insightType, insights) => {
11540
- const maxAttempts = 3;
11541
- for (let i = 0; i < maxAttempts; i++) {
11542
- try {
11543
- await axios.post(`https://chat-insights.getstream.io/insights/${insightType}`, insights);
11544
- }
11545
- catch (e) {
11546
- await sleep((i + 1) * 3000);
11547
- continue;
11548
- }
11549
- break;
11550
- }
11551
- };
11552
- function buildWsFatalInsight(connection, event) {
11553
- return {
11554
- ...event,
11555
- ...buildWsBaseInsight(connection),
11556
- };
11557
- }
11558
- function buildWsBaseInsight(connection) {
11559
- const { client } = connection;
11560
- return {
11561
- ready_state: connection.ws?.readyState,
11562
- url: connection._buildUrl(),
11563
- api_key: client.key,
11564
- start_ts: client.insightMetrics.connectionStartTimestamp,
11565
- end_ts: new Date().getTime(),
11566
- auth_type: client.getAuthType(),
11567
- token: client.tokenManager.token,
11568
- user_id: client.userID,
11569
- user_details: client._user,
11570
- // device: client.options.device,
11571
- device: 'browser',
11572
- client_id: connection.connectionID,
11573
- ws_details: connection.ws,
11574
- ws_consecutive_failures: client.insightMetrics.wsConsecutiveFailures,
11575
- ws_total_failures: client.insightMetrics.wsTotalFailures,
11576
- request_id: connection.requestID,
11577
- online: typeof navigator !== 'undefined' ? navigator?.onLine : null,
11578
- user_agent: typeof navigator !== 'undefined' ? navigator?.userAgent : null,
11579
- instance_client_id: client.insightMetrics.instanceClientId,
11580
- };
11581
- }
11582
- function buildWsSuccessAfterFailureInsight(connection) {
11583
- return buildWsBaseInsight(connection);
11584
- }
11585
-
11586
11493
  /**
11587
11494
  * Saving a long-lived reference to a promise that can reject can be unsafe,
11588
11495
  * since rejecting the promise causes an unhandled rejection error (even if the
@@ -11610,9 +11517,6 @@ function makeSafePromise(promise) {
11610
11517
  return unwrapPromise;
11611
11518
  }
11612
11519
 
11613
- // Type guards to check WebSocket error type
11614
- const isCloseEvent = (res) => res.code !== undefined;
11615
- const isErrorEvent = (res) => res.error !== undefined;
11616
11520
  /**
11617
11521
  * StableWSConnection - A WS connection that reconnects upon failure.
11618
11522
  * - the browser will sometimes report that you're online or offline
@@ -11647,12 +11551,9 @@ class StableWSConnection {
11647
11551
  */
11648
11552
  this._buildUrl = () => {
11649
11553
  const params = new URLSearchParams();
11650
- // const qs = encodeURIComponent(this.client._buildWSPayload(this.requestID));
11651
- // params.set('json', qs);
11652
11554
  params.set('api_key', this.client.key);
11653
11555
  params.set('stream-auth-type', this.client.getAuthType());
11654
11556
  params.set('X-Stream-Client', this.client.getUserAgent());
11655
- // params.append('authorization', this.client._getToken()!);
11656
11557
  return `${this.client.wsBaseURL}/connect?${params.toString()}`;
11657
11558
  };
11658
11559
  /**
@@ -11701,7 +11602,6 @@ class StableWSConnection {
11701
11602
  custom: user.custom,
11702
11603
  },
11703
11604
  };
11704
- this.authenticationSent = true;
11705
11605
  this.ws?.send(JSON.stringify(authMessage));
11706
11606
  this._log('onopen() - onopen callback', { wsID });
11707
11607
  };
@@ -11718,7 +11618,6 @@ class StableWSConnection {
11718
11618
  if (!this.isResolved && data && data.type === 'connection.error') {
11719
11619
  this.isResolved = true;
11720
11620
  if (data.error) {
11721
- // @ts-expect-error - the types of _errorFromWSEvent are incorrect
11722
11621
  this.rejectPromise?.(this._errorFromWSEvent(data, false));
11723
11622
  return;
11724
11623
  }
@@ -11760,9 +11659,13 @@ class StableWSConnection {
11760
11659
  // this is a permanent error raised by stream..
11761
11660
  // usually caused by invalid auth details
11762
11661
  const error = new Error(`WS connection reject with error ${event.reason}`);
11662
+ // @ts-expect-error
11763
11663
  error.reason = event.reason;
11664
+ // @ts-expect-error
11764
11665
  error.code = event.code;
11666
+ // @ts-expect-error
11765
11667
  error.wasClean = event.wasClean;
11668
+ // @ts-expect-error
11766
11669
  error.target = event.target;
11767
11670
  this.rejectPromise?.(error);
11768
11671
  this._log(`onclose() - WS connection reject with error ${event.reason}`, {
@@ -11789,7 +11692,7 @@ class StableWSConnection {
11789
11692
  this.totalFailures += 1;
11790
11693
  this._setHealth(false);
11791
11694
  this.isConnecting = false;
11792
- this.rejectPromise?.(this._errorFromWSEvent(event));
11695
+ this.rejectPromise?.(new Error(`WebSocket error: ${event}`));
11793
11696
  this._log(`onerror() - WS connection resulted into error`, { event });
11794
11697
  this._reconnect();
11795
11698
  };
@@ -11799,7 +11702,6 @@ class StableWSConnection {
11799
11702
  *
11800
11703
  * @param {boolean} healthy boolean indicating if the connection is healthy or not
11801
11704
  * @param {boolean} dispatchImmediately boolean indicating to dispatch event immediately even if the connection is unhealthy
11802
- *
11803
11705
  */
11804
11706
  this._setHealth = (healthy, dispatchImmediately = false) => {
11805
11707
  if (healthy === this.isHealthy)
@@ -11824,7 +11726,6 @@ class StableWSConnection {
11824
11726
  };
11825
11727
  /**
11826
11728
  * _errorFromWSEvent - Creates an error object for the WS event
11827
- *
11828
11729
  */
11829
11730
  this._errorFromWSEvent = (event, isWSFailure = true) => {
11830
11731
  let code;
@@ -11832,17 +11733,18 @@ class StableWSConnection {
11832
11733
  let message;
11833
11734
  if (isCloseEvent(event)) {
11834
11735
  code = event.code;
11835
- statusCode = 'unknown';
11836
11736
  message = event.reason;
11737
+ statusCode = 0;
11837
11738
  }
11838
- if (isErrorEvent(event)) {
11839
- code = event.error.code;
11840
- statusCode = event.error.StatusCode;
11841
- message = event.error.message;
11842
- }
11843
- // Keeping this `warn` level log, to avoid cluttering of error logs from ws failures.
11844
- this._log(`_errorFromWSEvent() - WS failed with code ${code}`, { event }, 'warn');
11845
- const error = new Error(`WS failed with code ${code} and reason - ${message}`);
11739
+ else {
11740
+ const { error } = event;
11741
+ code = error.code;
11742
+ message = error.message;
11743
+ statusCode = error.StatusCode;
11744
+ }
11745
+ const msg = `WS failed with code: ${code} and reason: ${message}`;
11746
+ this._log(msg, { event }, 'warn');
11747
+ const error = new Error(msg);
11846
11748
  error.code = code;
11847
11749
  /**
11848
11750
  * StatusCode does not exist on any event types but has been left
@@ -11867,10 +11769,8 @@ class StableWSConnection {
11867
11769
  * Schedules a next health check ping for websocket.
11868
11770
  */
11869
11771
  this.scheduleNextPing = () => {
11870
- if (this.healthCheckTimeoutRef) {
11871
- clearTimeout(this.healthCheckTimeoutRef);
11872
- }
11873
11772
  // 30 seconds is the recommended interval (messenger uses this)
11773
+ clearTimeout(this.healthCheckTimeoutRef);
11874
11774
  this.healthCheckTimeoutRef = setTimeout(() => {
11875
11775
  // send the healthcheck..., server replies with a health check event
11876
11776
  const data = [{ type: 'health.check', client_id: this.client.clientID }];
@@ -11889,9 +11789,7 @@ class StableWSConnection {
11889
11789
  * to be reconnected.
11890
11790
  */
11891
11791
  this.scheduleConnectionCheck = () => {
11892
- if (this.connectionCheckTimeoutRef) {
11893
- clearTimeout(this.connectionCheckTimeoutRef);
11894
- }
11792
+ clearTimeout(this.connectionCheckTimeoutRef);
11895
11793
  this.connectionCheckTimeoutRef = setTimeout(() => {
11896
11794
  const now = new Date();
11897
11795
  if (this.lastEvent &&
@@ -11909,8 +11807,6 @@ class StableWSConnection {
11909
11807
  this.totalFailures = 0;
11910
11808
  /** We only make 1 attempt to reconnect at the same time.. */
11911
11809
  this.isConnecting = false;
11912
- /** True after the auth payload is sent to the server */
11913
- this.authenticationSent = false;
11914
11810
  /** To avoid reconnect if client is disconnected */
11915
11811
  this.isDisconnected = false;
11916
11812
  /** Boolean that indicates if the connection promise is resolved */
@@ -12018,18 +11914,10 @@ class StableWSConnection {
12018
11914
  this.isConnecting = false;
12019
11915
  this.isDisconnected = true;
12020
11916
  // start by removing all the listeners
12021
- if (this.healthCheckTimeoutRef) {
12022
- clearInterval(this.healthCheckTimeoutRef);
12023
- }
12024
- if (this.connectionCheckTimeoutRef) {
12025
- clearInterval(this.connectionCheckTimeoutRef);
12026
- }
11917
+ clearInterval(this.healthCheckTimeoutRef);
11918
+ clearInterval(this.connectionCheckTimeoutRef);
12027
11919
  removeConnectionEventListeners(this.onlineStatusChanged);
12028
11920
  this.isHealthy = false;
12029
- // remove ws handlers...
12030
- if (this.ws && this.ws.removeAllListeners) {
12031
- this.ws.removeAllListeners();
12032
- }
12033
11921
  let isClosedPromise;
12034
11922
  // and finally close...
12035
11923
  // Assigning to local here because we will remove it from this before the
@@ -12062,12 +11950,10 @@ class StableWSConnection {
12062
11950
  * @return {ConnectAPIResponse<ConnectedEvent>} Promise that completes once the first health check message is received
12063
11951
  */
12064
11952
  async _connect() {
12065
- if (this.isConnecting ||
12066
- (this.isDisconnected && this.client.options.enableWSFallback))
11953
+ if (this.isConnecting)
12067
11954
  return; // simply ignore _connect if it's currently trying to connect
12068
11955
  this.isConnecting = true;
12069
11956
  this.requestID = randomId();
12070
- this.client.insightMetrics.connectionStartTimestamp = new Date().getTime();
12071
11957
  let isTokenReady = false;
12072
11958
  try {
12073
11959
  this._log(`_connect() - waiting for token`);
@@ -12091,7 +11977,8 @@ class StableWSConnection {
12091
11977
  wsURL,
12092
11978
  requestID: this.requestID,
12093
11979
  });
12094
- this.ws = new WebSocket$1(wsURL);
11980
+ const WS = this.client.options.WebSocketImpl ?? WebSocket;
11981
+ this.ws = new WS(wsURL);
12095
11982
  this.ws.onopen = this.onopen.bind(this, this.wsID);
12096
11983
  this.ws.onclose = this.onclose.bind(this, this.wsID);
12097
11984
  this.ws.onerror = this.onerror.bind(this, this.wsID);
@@ -12101,11 +11988,6 @@ class StableWSConnection {
12101
11988
  if (response) {
12102
11989
  this.connectionID = response.connection_id;
12103
11990
  this.client.resolveConnectionId?.(this.connectionID);
12104
- if (this.client.insightMetrics.wsConsecutiveFailures > 0 &&
12105
- this.client.options.enableInsights) {
12106
- postInsights('ws_success_after_failure', buildWsSuccessAfterFailureInsight(this));
12107
- this.client.insightMetrics.wsConsecutiveFailures = 0;
12108
- }
12109
11991
  return response;
12110
11992
  }
12111
11993
  }
@@ -12114,12 +11996,6 @@ class StableWSConnection {
12114
11996
  this.isConnecting = false;
12115
11997
  // @ts-ignore
12116
11998
  this._log(`_connect() - Error - `, err);
12117
- if (this.client.options.enableInsights) {
12118
- this.client.insightMetrics.wsConsecutiveFailures++;
12119
- this.client.insightMetrics.wsTotalFailures++;
12120
- const insights = buildWsFatalInsight(this, convertErrorToJson(err));
12121
- postInsights?.('ws_fatal', insights);
12122
- }
12123
11999
  this.client.rejectConnectionId?.(err);
12124
12000
  throw err;
12125
12001
  }
@@ -12153,7 +12029,7 @@ class StableWSConnection {
12153
12029
  this._log('_reconnect() - Abort (2) since already connecting or healthy');
12154
12030
  return;
12155
12031
  }
12156
- if (this.isDisconnected && this.client.options.enableWSFallback) {
12032
+ if (this.isDisconnected) {
12157
12033
  this._log('_reconnect() - Abort (3) since disconnect() is called');
12158
12034
  return;
12159
12035
  }
@@ -12195,7 +12071,6 @@ class StableWSConnection {
12195
12071
  // ws connection from now on.
12196
12072
  this.wsID += 1;
12197
12073
  try {
12198
- this?.ws?.removeAllListeners();
12199
12074
  this?.ws?.close();
12200
12075
  }
12201
12076
  catch (e) {
@@ -12207,36 +12082,16 @@ class StableWSConnection {
12207
12082
  }
12208
12083
  }
12209
12084
 
12210
- function isString(arrayOrString) {
12211
- return typeof arrayOrString === 'string';
12212
- }
12213
- function isMapStringCallback(arrayOrString, callback) {
12214
- return !!callback && isString(arrayOrString);
12215
- }
12216
- function map(arrayOrString, callback) {
12217
- const res = [];
12218
- if (isString(arrayOrString) && isMapStringCallback(arrayOrString, callback)) {
12219
- for (let k = 0, len = arrayOrString.length; k < len; k++) {
12220
- if (arrayOrString.charAt(k)) {
12221
- const kValue = arrayOrString.charAt(k);
12222
- const mappedValue = callback(kValue, k, arrayOrString);
12223
- res[k] = mappedValue;
12224
- }
12225
- }
12226
- }
12227
- else if (!isString(arrayOrString) &&
12228
- !isMapStringCallback(arrayOrString, callback)) {
12229
- for (let k = 0, len = arrayOrString.length; k < len; k++) {
12230
- if (k in arrayOrString) {
12231
- const kValue = arrayOrString[k];
12232
- const mappedValue = callback(kValue, k, arrayOrString);
12233
- res[k] = mappedValue;
12234
- }
12235
- }
12085
+ function getUserFromToken(token) {
12086
+ const fragments = token.split('.');
12087
+ if (fragments.length !== 3) {
12088
+ return '';
12236
12089
  }
12237
- return res;
12090
+ const b64Payload = fragments[1];
12091
+ const payload = decodeBase64(b64Payload);
12092
+ const data = JSON.parse(payload);
12093
+ return data.user_id;
12238
12094
  }
12239
- const encodeBase64 = (data) => base64Js.fromByteArray(new Uint8Array(map(data, (char) => char.charCodeAt(0))));
12240
12095
  // base-64 decoder throws exception if encoded string is not padded by '=' to make string length
12241
12096
  // in multiples of 4. So gonna use our own method for this purpose to keep backwards compatibility
12242
12097
  // https://github.com/beatgammit/base64-js/blob/master/index.js#L26
@@ -12258,29 +12113,6 @@ const decodeBase64 = (s) => {
12258
12113
  return r;
12259
12114
  };
12260
12115
 
12261
- /**
12262
- *
12263
- * @param {string} userId the id of the user
12264
- * @return {string}
12265
- */
12266
- function DevToken(userId) {
12267
- return [
12268
- 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', //{"alg": "HS256", "typ": "JWT"}
12269
- encodeBase64(JSON.stringify({ user_id: userId })),
12270
- 'devtoken', // hardcoded signature
12271
- ].join('.');
12272
- }
12273
- function UserFromToken(token) {
12274
- const fragments = token.split('.');
12275
- if (fragments.length !== 3) {
12276
- return '';
12277
- }
12278
- const b64Payload = fragments[1];
12279
- const payload = decodeBase64(b64Payload);
12280
- const data = JSON.parse(payload);
12281
- return data.user_id;
12282
- }
12283
-
12284
12116
  /**
12285
12117
  * TokenManager
12286
12118
  *
@@ -12289,8 +12121,6 @@ function UserFromToken(token) {
12289
12121
  class TokenManager {
12290
12122
  /**
12291
12123
  * Constructor
12292
- *
12293
- * @param {Secret} secret
12294
12124
  */
12295
12125
  constructor(secret) {
12296
12126
  /**
@@ -12343,7 +12173,7 @@ class TokenManager {
12343
12173
  // Allow empty token for anonymous users
12344
12174
  if (isAnonymous && tokenOrProvider === '')
12345
12175
  return;
12346
- const tokenUserId = UserFromToken(tokenOrProvider);
12176
+ const tokenUserId = getUserFromToken(tokenOrProvider);
12347
12177
  if (tokenOrProvider != null &&
12348
12178
  (tokenUserId == null ||
12349
12179
  tokenUserId === '' ||
@@ -12358,7 +12188,6 @@ class TokenManager {
12358
12188
  // Fetches a token from tokenProvider function and sets in tokenManager.
12359
12189
  // In case of static token, it will simply resolve to static token.
12360
12190
  this.loadToken = () => {
12361
- // eslint-disable-next-line no-async-promise-executor
12362
12191
  this.loadTokenPromise = new Promise(async (resolve, reject) => {
12363
12192
  if (this.type === 'static') {
12364
12193
  return resolve(this.token);
@@ -12387,236 +12216,11 @@ class TokenManager {
12387
12216
  };
12388
12217
  this.isStatic = () => this.type === 'static';
12389
12218
  this.loadTokenPromise = null;
12390
- if (secret) {
12391
- this.secret = secret;
12392
- }
12219
+ this.secret = secret;
12393
12220
  this.type = 'static';
12394
12221
  }
12395
12222
  }
12396
12223
 
12397
- const APIErrorCodes = {
12398
- '-1': { name: 'InternalSystemError', retryable: true },
12399
- '2': { name: 'AccessKeyError', retryable: false },
12400
- '3': { name: 'AuthenticationFailedError', retryable: true },
12401
- '4': { name: 'InputError', retryable: false },
12402
- '6': { name: 'DuplicateUsernameError', retryable: false },
12403
- '9': { name: 'RateLimitError', retryable: true },
12404
- '16': { name: 'DoesNotExistError', retryable: false },
12405
- '17': { name: 'NotAllowedError', retryable: false },
12406
- '18': { name: 'EventNotSupportedError', retryable: false },
12407
- '19': { name: 'ChannelFeatureNotSupportedError', retryable: false },
12408
- '20': { name: 'MessageTooLongError', retryable: false },
12409
- '21': { name: 'MultipleNestingLevelError', retryable: false },
12410
- '22': { name: 'PayloadTooBigError', retryable: false },
12411
- '23': { name: 'RequestTimeoutError', retryable: true },
12412
- '24': { name: 'MaxHeaderSizeExceededError', retryable: false },
12413
- '40': { name: 'AuthErrorTokenExpired', retryable: false },
12414
- '41': { name: 'AuthErrorTokenNotValidYet', retryable: false },
12415
- '42': { name: 'AuthErrorTokenUsedBeforeIssuedAt', retryable: false },
12416
- '43': { name: 'AuthErrorTokenSignatureInvalid', retryable: false },
12417
- '44': { name: 'CustomCommandEndpointMissingError', retryable: false },
12418
- '45': { name: 'CustomCommandEndpointCallError', retryable: true },
12419
- '46': { name: 'ConnectionIDNotFoundError', retryable: false },
12420
- '60': { name: 'CoolDownError', retryable: true },
12421
- '69': { name: 'ErrWrongRegion', retryable: false },
12422
- '70': { name: 'ErrQueryChannelPermissions', retryable: false },
12423
- '71': { name: 'ErrTooManyConnections', retryable: true },
12424
- '99': { name: 'AppSuspendedError', retryable: false },
12425
- };
12426
- function isAPIError(error) {
12427
- return error.code !== undefined;
12428
- }
12429
- function isErrorRetryable(error) {
12430
- if (!error.code)
12431
- return false;
12432
- const err = APIErrorCodes[`${error.code}`];
12433
- if (!err)
12434
- return false;
12435
- return err.retryable;
12436
- }
12437
- function isConnectionIDError(error) {
12438
- return error.code === 46; // ConnectionIDNotFoundError
12439
- }
12440
- function isWSFailure(err) {
12441
- if (typeof err.isWSFailure === 'boolean') {
12442
- return err.isWSFailure;
12443
- }
12444
- try {
12445
- return JSON.parse(err.message).isWSFailure;
12446
- }
12447
- catch (_) {
12448
- return false;
12449
- }
12450
- }
12451
- function isErrorResponse(res) {
12452
- return !res.status || res.status < 200 || 300 <= res.status;
12453
- }
12454
-
12455
- var ConnectionState;
12456
- (function (ConnectionState) {
12457
- ConnectionState["Closed"] = "CLOSED";
12458
- ConnectionState["Connected"] = "CONNECTED";
12459
- ConnectionState["Connecting"] = "CONNECTING";
12460
- ConnectionState["Disconnected"] = "DISCONNECTED";
12461
- ConnectionState["Init"] = "INIT";
12462
- })(ConnectionState || (ConnectionState = {}));
12463
- class WSConnectionFallback {
12464
- constructor(client) {
12465
- /** @private */
12466
- this._onlineStatusChanged = (event) => {
12467
- this._log(`_onlineStatusChanged() - ${event.type}`);
12468
- if (event.type === 'offline') {
12469
- this._setState(ConnectionState.Closed);
12470
- this.cancelToken?.cancel('disconnect() is called');
12471
- this.cancelToken = undefined;
12472
- return;
12473
- }
12474
- if (event.type === 'online' && this.state === ConnectionState.Closed) {
12475
- this.connect(true);
12476
- }
12477
- };
12478
- /** @private */
12479
- this._req = async (params, config, retry) => {
12480
- if (!this.cancelToken && !params.close) {
12481
- this.cancelToken = axios.CancelToken.source();
12482
- }
12483
- try {
12484
- const res = await this.client.doAxiosRequest('get', this.client.baseURL.replace(':3030', ':8900') + '/longpoll', // replace port if present for testing with local API
12485
- undefined, {
12486
- config: { ...config, cancelToken: this.cancelToken?.token },
12487
- params,
12488
- publicEndpoint: true,
12489
- });
12490
- this.consecutiveFailures = 0; // always reset in case of no error
12491
- return res;
12492
- }
12493
- catch (err) {
12494
- this.consecutiveFailures += 1;
12495
- // @ts-ignore
12496
- if (retry && isErrorRetryable(err)) {
12497
- this._log(`_req() - Retryable error, retrying request`);
12498
- await sleep(retryInterval(this.consecutiveFailures));
12499
- return this._req(params, config, retry);
12500
- }
12501
- throw err;
12502
- }
12503
- };
12504
- /** @private */
12505
- this._poll = async () => {
12506
- while (this.state === ConnectionState.Connected) {
12507
- try {
12508
- const data = await this._req({}, {
12509
- timeout: 30000,
12510
- }, true); // 30s => API responds in 20s if there is no event
12511
- if (data.events?.length) {
12512
- for (let i = 0; i < data.events.length; i++) {
12513
- this.client.dispatchEvent(data.events[i]);
12514
- }
12515
- }
12516
- }
12517
- catch (err) {
12518
- if (axios.isCancel(err)) {
12519
- this._log(`_poll() - axios canceled request`);
12520
- return;
12521
- }
12522
- /** client.doAxiosRequest will take care of TOKEN_EXPIRED error */
12523
- // @ts-ignore
12524
- if (isConnectionIDError(err)) {
12525
- this._log(`_poll() - ConnectionID error, connecting without ID...`);
12526
- this._setState(ConnectionState.Disconnected);
12527
- this.connect(true);
12528
- return;
12529
- }
12530
- // @ts-ignore
12531
- if (isAPIError(err) && !isErrorRetryable(err)) {
12532
- this._setState(ConnectionState.Closed);
12533
- return;
12534
- }
12535
- await sleep(retryInterval(this.consecutiveFailures));
12536
- }
12537
- }
12538
- };
12539
- /**
12540
- * connect try to open a longpoll request
12541
- * @param reconnect should be false for first call and true for subsequent calls to keep the connection alive and call recoverState
12542
- */
12543
- this.connect = async (reconnect = false) => {
12544
- if (this.state === ConnectionState.Connecting) {
12545
- this._log('connect() - connecting already in progress', { reconnect }, 'warn');
12546
- return;
12547
- }
12548
- if (this.state === ConnectionState.Connected) {
12549
- this._log('connect() - already connected and polling', { reconnect }, 'warn');
12550
- return;
12551
- }
12552
- this._setState(ConnectionState.Connecting);
12553
- this.connectionID = undefined; // connect should be sent with empty connection_id so API creates one
12554
- try {
12555
- const { event } = await this._req({ json: this.client._buildWSPayload() }, {
12556
- timeout: 8000, // 8s
12557
- }, reconnect);
12558
- this._setState(ConnectionState.Connected);
12559
- this.connectionID = event.connection_id;
12560
- this.client.resolveConnectionId?.();
12561
- // @ts-expect-error
12562
- this.client.dispatchEvent(event);
12563
- this._poll();
12564
- return event;
12565
- }
12566
- catch (err) {
12567
- this._setState(ConnectionState.Closed);
12568
- this.client.rejectConnectionId?.();
12569
- throw err;
12570
- }
12571
- };
12572
- /**
12573
- * isHealthy checks if there is a connectionID and connection is in Connected state
12574
- */
12575
- this.isHealthy = () => {
12576
- return !!this.connectionID && this.state === ConnectionState.Connected;
12577
- };
12578
- this.disconnect = async (timeout = 2000) => {
12579
- removeConnectionEventListeners(this._onlineStatusChanged);
12580
- this._setState(ConnectionState.Disconnected);
12581
- this.cancelToken?.cancel('disconnect() is called');
12582
- this.cancelToken = undefined;
12583
- const connection_id = this.connectionID;
12584
- this.connectionID = undefined;
12585
- try {
12586
- await this._req({ close: true, connection_id }, {
12587
- timeout,
12588
- }, false);
12589
- this._log(`disconnect() - Closed connectionID`);
12590
- }
12591
- catch (err) {
12592
- this._log(`disconnect() - Failed`, { err }, 'error');
12593
- }
12594
- };
12595
- this.client = client;
12596
- this.state = ConnectionState.Init;
12597
- this.consecutiveFailures = 0;
12598
- addConnectionEventListeners(this._onlineStatusChanged);
12599
- }
12600
- _log(msg, extra = {}, level = 'info') {
12601
- this.client.logger(level, 'WSConnectionFallback:' + msg, {
12602
- ...extra,
12603
- });
12604
- }
12605
- _setState(state) {
12606
- this._log(`_setState() - ${state}`);
12607
- // transition from connecting => connected
12608
- if (this.state === ConnectionState.Connecting &&
12609
- state === ConnectionState.Connected) {
12610
- this.client.dispatchEvent({ type: 'connection.changed', online: true });
12611
- }
12612
- if (state === ConnectionState.Closed ||
12613
- state === ConnectionState.Disconnected) {
12614
- this.client.dispatchEvent({ type: 'connection.changed', online: false });
12615
- }
12616
- this.state = state;
12617
- }
12618
- }
12619
-
12620
12224
  const getLocationHint = async (hintUrl = `https://hint.stream-io-video.com/`, timeout = 2000, maxAttempts = 3) => {
12621
12225
  const logger = getLogger(['location-hint']);
12622
12226
  let attempt = 0;
@@ -12659,9 +12263,6 @@ class StreamClient {
12659
12263
  */
12660
12264
  constructor(key, options) {
12661
12265
  this.listeners = {};
12662
- this.devToken = (userID) => {
12663
- return DevToken(userID);
12664
- };
12665
12266
  this.getAuthType = () => {
12666
12267
  return this.anonymous ? 'anonymous' : 'jwt';
12667
12268
  };
@@ -12679,7 +12280,7 @@ class StreamClient {
12679
12280
  }
12680
12281
  return hint;
12681
12282
  };
12682
- this._getConnectionID = () => this.wsConnection?.connectionID || this.wsFallback?.connectionID;
12283
+ this._getConnectionID = () => this.wsConnection?.connectionID;
12683
12284
  this._hasConnectionID = () => Boolean(this._getConnectionID());
12684
12285
  /**
12685
12286
  * connectUser - Set the current user and open a WebSocket connection
@@ -12755,11 +12356,7 @@ class StreamClient {
12755
12356
  * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
12756
12357
  */
12757
12358
  this.closeConnection = async (timeout) => {
12758
- await Promise.all([
12759
- this.wsConnection?.disconnect(timeout),
12760
- this.wsFallback?.disconnect(timeout),
12761
- ]);
12762
- return Promise.resolve();
12359
+ await this.wsConnection?.disconnect(timeout);
12763
12360
  };
12764
12361
  /**
12765
12362
  * Creates a new WebSocket connection with the current user. Returns empty promise, if there is an active connection
@@ -12773,12 +12370,11 @@ class StreamClient {
12773
12370
  this.logger('info', 'client:openConnection() - connection already in progress');
12774
12371
  return await wsPromise;
12775
12372
  }
12776
- if ((this.wsConnection?.isHealthy || this.wsFallback?.isHealthy()) &&
12777
- this._hasConnectionID()) {
12373
+ if (this.wsConnection?.isHealthy && this._hasConnectionID()) {
12778
12374
  this.logger('info', 'client:openConnection() - openConnection called twice, healthy connection already exists');
12779
12375
  return;
12780
12376
  }
12781
- this._setupConnectionIdPromise();
12377
+ await this._setupConnectionIdPromise();
12782
12378
  this.clientID = `${this.userID}--${randomId()}`;
12783
12379
  const newWsPromise = this.connect();
12784
12380
  this.wsPromiseSafe = makeSafePromise(newWsPromise);
@@ -12805,11 +12401,7 @@ class StreamClient {
12805
12401
  this.resolveConnectionId = undefined;
12806
12402
  };
12807
12403
  this.connectGuestUser = async (user) => {
12808
- this.guestUserCreatePromise = this.doAxiosRequest('post', '/guest', {
12809
- user: {
12810
- ...user,
12811
- },
12812
- }, { publicEndpoint: true });
12404
+ this.guestUserCreatePromise = this.doAxiosRequest('post', '/guest', { user }, { publicEndpoint: true });
12813
12405
  const response = await this.guestUserCreatePromise;
12814
12406
  this.guestUserCreatePromise.finally(() => (this.guestUserCreatePromise = undefined));
12815
12407
  return this.connectUser(response.user, response.access_token);
@@ -12819,7 +12411,7 @@ class StreamClient {
12819
12411
  */
12820
12412
  this.connectAnonymousUser = async (user, tokenOrProvider) => {
12821
12413
  addConnectionEventListeners(this.updateNetworkConnectionStatus);
12822
- this._setupConnectionIdPromise();
12414
+ await this._setupConnectionIdPromise();
12823
12415
  this.anonymous = true;
12824
12416
  await this._setToken(user, tokenOrProvider, this.anonymous);
12825
12417
  this._setUser(user);
@@ -13013,78 +12605,17 @@ class StreamClient {
13013
12605
  if (!this.userID || !this._user) {
13014
12606
  throw Error('Call connectUser or connectAnonymousUser before starting the connection');
13015
12607
  }
13016
- if (!this.wsBaseURL) {
12608
+ if (!this.wsBaseURL)
13017
12609
  throw Error('Websocket base url not set');
13018
- }
13019
- if (!this.clientID) {
12610
+ if (!this.clientID)
13020
12611
  throw Error('clientID is not set');
13021
- }
13022
- if (!this.wsConnection &&
13023
- (this.options.warmUp || this.options.enableInsights)) {
13024
- this._sayHi();
13025
- }
13026
12612
  // The StableWSConnection handles all the reconnection logic.
13027
- if (this.options.wsConnection && this.node) {
13028
- // Intentionally avoiding adding ts generics on wsConnection in options since its only useful for unit test purpose.
13029
- this.options.wsConnection.setClient(this);
13030
- this.wsConnection = this.options
13031
- .wsConnection;
13032
- }
13033
- else {
13034
- this.wsConnection = new StableWSConnection(this);
13035
- }
13036
- try {
13037
- // if fallback is used before, continue using it instead of waiting for WS to fail
13038
- if (this.wsFallback) {
13039
- return await this.wsFallback.connect();
13040
- }
13041
- this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
13042
- // if WSFallback is enabled, ws connect should timeout faster so fallback can try
13043
- return await this.wsConnection.connect(this.options.enableWSFallback
13044
- ? this.defaultWSTimeoutWithFallback
13045
- : this.defaultWSTimeout);
13046
- }
13047
- catch (err) {
13048
- // run fallback only if it's WS/Network error and not a normal API error
13049
- // make sure browser is online before even trying the longpoll
13050
- if (this.options.enableWSFallback &&
13051
- // @ts-ignore
13052
- isWSFailure(err) &&
13053
- isOnline(this.logger)) {
13054
- this.logger('warn', 'client:connect() - WS failed, fallback to longpoll');
13055
- this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' });
13056
- this.wsConnection._destroyCurrentWSConnection();
13057
- this.wsConnection.disconnect().then(); // close WS so no retry
13058
- this.wsFallback = new WSConnectionFallback(this);
13059
- return await this.wsFallback.connect();
13060
- }
13061
- throw err;
13062
- }
13063
- };
13064
- /**
13065
- * Check the connectivity with server for warmup purpose.
13066
- *
13067
- * @private
13068
- */
13069
- this._sayHi = () => {
13070
- const client_request_id = randomId();
13071
- const opts = {
13072
- headers: axios.AxiosHeaders.from({
13073
- 'x-client-request-id': client_request_id,
13074
- }),
13075
- };
13076
- this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
13077
- if (this.options.enableInsights) {
13078
- postInsights('http_hi_failed', {
13079
- api_key: this.key,
13080
- err: e,
13081
- client_request_id,
13082
- });
13083
- }
13084
- });
12613
+ this.wsConnection = new StableWSConnection(this);
12614
+ this.logger('info', 'StreamClient.connect: this.wsConnection.connect()');
12615
+ return await this.wsConnection.connect(this.defaultWSTimeout);
13085
12616
  };
13086
12617
  this.getUserAgent = () => {
13087
- const version = "1.11.6";
12618
+ const version = "1.11.7";
13088
12619
  return (this.userAgent ||
13089
12620
  `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${version}`);
13090
12621
  };
@@ -13132,18 +12663,6 @@ class StreamClient {
13132
12663
  return null;
13133
12664
  return this.tokenManager.getToken();
13134
12665
  };
13135
- /**
13136
- * encode ws url payload
13137
- * @private
13138
- * @returns json string
13139
- */
13140
- this._buildWSPayload = (client_request_id) => {
13141
- return JSON.stringify({
13142
- user_id: this.userID,
13143
- user_details: this._user,
13144
- client_request_id,
13145
- });
13146
- };
13147
12666
  this.updateNetworkConnectionStatus = (event) => {
13148
12667
  if (event.type === 'offline') {
13149
12668
  this.logger('debug', 'device went offline');
@@ -13172,7 +12691,6 @@ class StreamClient {
13172
12691
  this.options = {
13173
12692
  timeout: 5000,
13174
12693
  withCredentials: false, // making sure cookies are not sent
13175
- warmUp: false,
13176
12694
  ...inputOptions,
13177
12695
  };
13178
12696
  if (this.node && !this.options.httpsAgent) {
@@ -13182,16 +12700,6 @@ class StreamClient {
13182
12700
  });
13183
12701
  }
13184
12702
  this.setBaseURL(this.options.baseURL || 'https://video.stream-io-api.com/video');
13185
- if (typeof process !== 'undefined' &&
13186
- 'env' in process &&
13187
- process.env.STREAM_LOCAL_TEST_RUN) {
13188
- this.setBaseURL('http://localhost:3030/video');
13189
- }
13190
- if (typeof process !== 'undefined' &&
13191
- 'env' in process &&
13192
- process.env.STREAM_LOCAL_TEST_HOST) {
13193
- this.setBaseURL(`http://${process.env.STREAM_LOCAL_TEST_HOST}/video`);
13194
- }
13195
12703
  this.axiosInstance = axios.create({
13196
12704
  ...this.options,
13197
12705
  baseURL: this.baseURL,
@@ -13208,8 +12716,6 @@ class StreamClient {
13208
12716
  // generated from secret.
13209
12717
  this.tokenManager = new TokenManager(this.secret);
13210
12718
  this.consecutiveFailures = 0;
13211
- this.insightMetrics = new InsightMetrics();
13212
- this.defaultWSTimeoutWithFallback = 6000;
13213
12719
  this.defaultWSTimeout = 15000;
13214
12720
  this.logger = isFunction(inputOptions.logger)
13215
12721
  ? inputOptions.logger