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