@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.
- package/CHANGELOG.md +14 -0
- package/dist/index.browser.es.js +123 -114
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +123 -114
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +123 -114
- package/dist/index.es.js.map +1 -1
- package/dist/src/StreamVideoClient.d.ts +5 -3
- package/dist/src/coordinator/connection/client.d.ts +3 -4
- package/dist/src/coordinator/connection/connection.d.ts +3 -4
- package/dist/src/coordinator/connection/types.d.ts +14 -0
- package/dist/src/coordinator/connection/utils.d.ts +0 -7
- package/package.json +1 -1
- package/src/StreamVideoClient.ts +85 -43
- package/src/__tests__/StreamVideoClient.test.ts +82 -6
- package/src/coordinator/connection/client.ts +17 -37
- package/src/coordinator/connection/connection.ts +26 -28
- package/src/coordinator/connection/token_manager.ts +3 -9
- package/src/coordinator/connection/types.ts +17 -0
- package/src/coordinator/connection/utils.ts +15 -45
- package/src/devices/CameraManager.ts +2 -3
- package/src/rtc/Publisher.ts +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.18.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.3...@stream-io/video-client-1.18.4) (2025-03-10)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* retryable client.connectUser() ([#1710](https://github.com/GetStream/stream-video-js/issues/1710)) ([10b6860](https://github.com/GetStream/stream-video-js/commit/10b6860e1d65c38d8eb0ba7d7ea18f0ca30f5abc))
|
|
11
|
+
|
|
12
|
+
## [1.18.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.2...@stream-io/video-client-1.18.3) (2025-03-05)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* revert the release of cloned track on publisher dispose ([556fb61](https://github.com/GetStream/stream-video-js/commit/556fb610ae1c9a1965f38fc07e995683b5052544))
|
|
18
|
+
|
|
5
19
|
## [1.18.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.18.1...@stream-io/video-client-1.18.2) (2025-03-04)
|
|
6
20
|
|
|
7
21
|
|
package/dist/index.browser.es.js
CHANGED
|
@@ -3302,7 +3302,8 @@ function isFunction(value) {
|
|
|
3302
3302
|
*/
|
|
3303
3303
|
const KnownCodes = {
|
|
3304
3304
|
TOKEN_EXPIRED: 40,
|
|
3305
|
-
WS_CLOSED_SUCCESS: 1000
|
|
3305
|
+
WS_CLOSED_SUCCESS: 1000,
|
|
3306
|
+
};
|
|
3306
3307
|
/**
|
|
3307
3308
|
* retryInterval - A retry interval which increases acc to number of failures
|
|
3308
3309
|
*
|
|
@@ -3314,9 +3315,6 @@ function retryInterval(numberOfFailures) {
|
|
|
3314
3315
|
const min = Math.min(Math.max(250, (numberOfFailures - 1) * 2000), 5000);
|
|
3315
3316
|
return Math.floor(Math.random() * (max - min) + min);
|
|
3316
3317
|
}
|
|
3317
|
-
function randomId() {
|
|
3318
|
-
return generateUUIDv4();
|
|
3319
|
-
}
|
|
3320
3318
|
function hex(bytes) {
|
|
3321
3319
|
let s = '';
|
|
3322
3320
|
for (let i = 0; i < bytes.length; i++) {
|
|
@@ -3329,33 +3327,24 @@ function generateUUIDv4() {
|
|
|
3329
3327
|
const bytes = getRandomBytes(16);
|
|
3330
3328
|
bytes[6] = (bytes[6] & 0x0f) | 0x40; // version
|
|
3331
3329
|
bytes[8] = (bytes[8] & 0xbf) | 0x80; // variant
|
|
3332
|
-
return
|
|
3333
|
-
|
|
3334
|
-
hex(bytes.subarray(4, 6))
|
|
3335
|
-
|
|
3336
|
-
hex(bytes.subarray(
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
'-' +
|
|
3340
|
-
hex(bytes.subarray(10, 16)));
|
|
3341
|
-
}
|
|
3342
|
-
function getRandomValuesWithMathRandom(bytes) {
|
|
3343
|
-
const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
|
|
3344
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
3345
|
-
bytes[i] = Math.random() * max;
|
|
3346
|
-
}
|
|
3330
|
+
return [
|
|
3331
|
+
hex(bytes.subarray(0, 4)),
|
|
3332
|
+
hex(bytes.subarray(4, 6)),
|
|
3333
|
+
hex(bytes.subarray(6, 8)),
|
|
3334
|
+
hex(bytes.subarray(8, 10)),
|
|
3335
|
+
hex(bytes.subarray(10, 16)),
|
|
3336
|
+
].join('-');
|
|
3347
3337
|
}
|
|
3348
3338
|
const getRandomValues = (() => {
|
|
3349
|
-
if (typeof crypto !== 'undefined' &&
|
|
3350
|
-
typeof crypto?.getRandomValues !== 'undefined') {
|
|
3339
|
+
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
|
3351
3340
|
return crypto.getRandomValues.bind(crypto);
|
|
3352
3341
|
}
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
}
|
|
3342
|
+
return function getRandomValuesWithMathRandom(bytes) {
|
|
3343
|
+
const max = Math.pow(2, (8 * bytes.byteLength) / bytes.length);
|
|
3344
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
3345
|
+
bytes[i] = Math.random() * max;
|
|
3346
|
+
}
|
|
3347
|
+
};
|
|
3359
3348
|
})();
|
|
3360
3349
|
function getRandomBytes(length) {
|
|
3361
3350
|
const bytes = new Uint8Array(length);
|
|
@@ -5813,11 +5802,6 @@ class Publisher extends BasePeerConnection {
|
|
|
5813
5802
|
}
|
|
5814
5803
|
for (const track of this.clonedTracks) {
|
|
5815
5804
|
this.stopTrack(track);
|
|
5816
|
-
// @ts-expect-error release() is present in react-native-webrtc
|
|
5817
|
-
if (track && typeof track.release === 'function') {
|
|
5818
|
-
// @ts-expect-error
|
|
5819
|
-
track.release();
|
|
5820
|
-
}
|
|
5821
5805
|
}
|
|
5822
5806
|
};
|
|
5823
5807
|
this.changePublishQuality = async (videoSender) => {
|
|
@@ -7470,7 +7454,7 @@ const aggregate = (stats) => {
|
|
|
7470
7454
|
return report;
|
|
7471
7455
|
};
|
|
7472
7456
|
|
|
7473
|
-
const version = "1.18.
|
|
7457
|
+
const version = "1.18.4";
|
|
7474
7458
|
const [major, minor, patch] = version.split('.');
|
|
7475
7459
|
let sdkInfo = {
|
|
7476
7460
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -9349,10 +9333,9 @@ class CameraManager extends InputMediaDeviceManager {
|
|
|
9349
9333
|
this.logger('warn', 'No video track found to do direction selection');
|
|
9350
9334
|
return;
|
|
9351
9335
|
}
|
|
9352
|
-
|
|
9336
|
+
await videoTrack.applyConstraints({
|
|
9353
9337
|
facingMode: direction === 'front' ? 'user' : 'environment',
|
|
9354
|
-
};
|
|
9355
|
-
await videoTrack.applyConstraints(constraints);
|
|
9338
|
+
});
|
|
9356
9339
|
this.state.setDirection(direction);
|
|
9357
9340
|
this.state.setDevice(undefined);
|
|
9358
9341
|
}
|
|
@@ -12016,9 +11999,7 @@ var https = null;
|
|
|
12016
11999
|
class StableWSConnection {
|
|
12017
12000
|
constructor(client) {
|
|
12018
12001
|
this._log = (msg, extra = {}, level = 'info') => {
|
|
12019
|
-
this.client.logger(level,
|
|
12020
|
-
...extra,
|
|
12021
|
-
});
|
|
12002
|
+
this.client.logger(level, `connection:${msg}`, extra);
|
|
12022
12003
|
};
|
|
12023
12004
|
this.setClient = (client) => {
|
|
12024
12005
|
this.client = client;
|
|
@@ -12072,7 +12053,7 @@ class StableWSConnection {
|
|
|
12072
12053
|
this.client.logger('error', `Token not set, can't connect authenticate`);
|
|
12073
12054
|
return;
|
|
12074
12055
|
}
|
|
12075
|
-
const authMessage = {
|
|
12056
|
+
const authMessage = JSON.stringify({
|
|
12076
12057
|
token,
|
|
12077
12058
|
user_details: {
|
|
12078
12059
|
id: user.id,
|
|
@@ -12080,8 +12061,9 @@ class StableWSConnection {
|
|
|
12080
12061
|
image: user.image,
|
|
12081
12062
|
custom: user.custom,
|
|
12082
12063
|
},
|
|
12083
|
-
};
|
|
12084
|
-
this.
|
|
12064
|
+
});
|
|
12065
|
+
this._log(`onopen() - Sending auth message ${authMessage}`, {}, 'trace');
|
|
12066
|
+
this.ws?.send(authMessage);
|
|
12085
12067
|
this._log('onopen() - onopen callback', { wsID });
|
|
12086
12068
|
};
|
|
12087
12069
|
this.onmessage = (wsID, event) => {
|
|
@@ -12094,10 +12076,12 @@ class StableWSConnection {
|
|
|
12094
12076
|
// we wait till the first message before we consider the connection open.
|
|
12095
12077
|
// the reason for this is that auth errors and similar errors trigger a ws.onopen and immediately
|
|
12096
12078
|
// after that a ws.onclose.
|
|
12097
|
-
if (!this.
|
|
12098
|
-
|
|
12079
|
+
if (!this.isConnectionOpenResolved &&
|
|
12080
|
+
data &&
|
|
12081
|
+
data.type === 'connection.error') {
|
|
12082
|
+
this.isConnectionOpenResolved = true;
|
|
12099
12083
|
if (data.error) {
|
|
12100
|
-
this.
|
|
12084
|
+
this.rejectConnectionOpen?.(this._errorFromWSEvent(data, false));
|
|
12101
12085
|
return;
|
|
12102
12086
|
}
|
|
12103
12087
|
}
|
|
@@ -12109,7 +12093,7 @@ class StableWSConnection {
|
|
|
12109
12093
|
this.scheduleNextPing();
|
|
12110
12094
|
}
|
|
12111
12095
|
if (data && data.type === 'connection.ok') {
|
|
12112
|
-
this.
|
|
12096
|
+
this.resolveConnectionOpen?.(data);
|
|
12113
12097
|
this._setHealth(true);
|
|
12114
12098
|
}
|
|
12115
12099
|
if (data && data.type === 'connection.error' && data.error) {
|
|
@@ -12146,7 +12130,7 @@ class StableWSConnection {
|
|
|
12146
12130
|
error.wasClean = event.wasClean;
|
|
12147
12131
|
// @ts-expect-error
|
|
12148
12132
|
error.target = event.target;
|
|
12149
|
-
this.
|
|
12133
|
+
this.rejectConnectionOpen?.(error);
|
|
12150
12134
|
this._log(`onclose() - WS connection reject with error ${event.reason}`, {
|
|
12151
12135
|
event,
|
|
12152
12136
|
});
|
|
@@ -12156,7 +12140,7 @@ class StableWSConnection {
|
|
|
12156
12140
|
this.totalFailures += 1;
|
|
12157
12141
|
this._setHealth(false);
|
|
12158
12142
|
this.isConnecting = false;
|
|
12159
|
-
this.
|
|
12143
|
+
this.rejectConnectionOpen?.(this._errorFromWSEvent(event));
|
|
12160
12144
|
this._log(`onclose() - WS connection closed. Calling reconnect ...`, {
|
|
12161
12145
|
event,
|
|
12162
12146
|
});
|
|
@@ -12171,7 +12155,7 @@ class StableWSConnection {
|
|
|
12171
12155
|
this.totalFailures += 1;
|
|
12172
12156
|
this._setHealth(false);
|
|
12173
12157
|
this.isConnecting = false;
|
|
12174
|
-
this.
|
|
12158
|
+
this.rejectConnectionOpen?.(new Error(`WebSocket error: ${event}`));
|
|
12175
12159
|
this._log(`onerror() - WS connection resulted into error`, { event });
|
|
12176
12160
|
this._reconnect();
|
|
12177
12161
|
};
|
|
@@ -12237,11 +12221,11 @@ class StableWSConnection {
|
|
|
12237
12221
|
* _setupPromise - sets up the this.connectOpen promise
|
|
12238
12222
|
*/
|
|
12239
12223
|
this._setupConnectionPromise = () => {
|
|
12240
|
-
this.
|
|
12224
|
+
this.isConnectionOpenResolved = false;
|
|
12241
12225
|
/** a promise that is resolved once ws.open is called */
|
|
12242
12226
|
this.connectionOpenSafe = makeSafePromise(new Promise((resolve, reject) => {
|
|
12243
|
-
this.
|
|
12244
|
-
this.
|
|
12227
|
+
this.resolveConnectionOpen = resolve;
|
|
12228
|
+
this.rejectConnectionOpen = reject;
|
|
12245
12229
|
}));
|
|
12246
12230
|
};
|
|
12247
12231
|
/**
|
|
@@ -12292,7 +12276,7 @@ class StableWSConnection {
|
|
|
12292
12276
|
/** To avoid reconnect if client is disconnected */
|
|
12293
12277
|
this.isDisconnected = false;
|
|
12294
12278
|
/** Boolean that indicates if the connection promise is resolved */
|
|
12295
|
-
this.
|
|
12279
|
+
this.isConnectionOpenResolved = false;
|
|
12296
12280
|
/** Boolean that indicates if we have a working connection to the server */
|
|
12297
12281
|
this.isHealthy = false;
|
|
12298
12282
|
/** Incremented when a new WS connection is made */
|
|
@@ -12437,9 +12421,8 @@ class StableWSConnection {
|
|
|
12437
12421
|
*/
|
|
12438
12422
|
async _connect() {
|
|
12439
12423
|
if (this.isConnecting)
|
|
12440
|
-
return; //
|
|
12424
|
+
return; // ignore _connect if it's currently trying to connect
|
|
12441
12425
|
this.isConnecting = true;
|
|
12442
|
-
this.requestID = randomId();
|
|
12443
12426
|
let isTokenReady = false;
|
|
12444
12427
|
try {
|
|
12445
12428
|
this._log(`_connect() - waiting for token`);
|
|
@@ -12459,10 +12442,7 @@ class StableWSConnection {
|
|
|
12459
12442
|
}
|
|
12460
12443
|
this._setupConnectionPromise();
|
|
12461
12444
|
const wsURL = this._buildUrl();
|
|
12462
|
-
this._log(`_connect() - Connecting to ${wsURL}
|
|
12463
|
-
wsURL,
|
|
12464
|
-
requestID: this.requestID,
|
|
12465
|
-
});
|
|
12445
|
+
this._log(`_connect() - Connecting to ${wsURL}`);
|
|
12466
12446
|
const WS = this.client.options.WebSocketImpl ?? WebSocket;
|
|
12467
12447
|
this.ws = new WS(wsURL);
|
|
12468
12448
|
this.ws.onopen = this.onopen.bind(this, this.wsID);
|
|
@@ -12606,6 +12586,8 @@ const decodeBase64 = (s) => {
|
|
|
12606
12586
|
*/
|
|
12607
12587
|
class TokenManager {
|
|
12608
12588
|
constructor(secret) {
|
|
12589
|
+
this.loadTokenPromise = null;
|
|
12590
|
+
this.type = 'static';
|
|
12609
12591
|
/**
|
|
12610
12592
|
* Set the static string token or token provider.
|
|
12611
12593
|
* Token provider should return a token string or a promise which resolves to string token.
|
|
@@ -12648,9 +12630,7 @@ class TokenManager {
|
|
|
12648
12630
|
if (!this.secret && !tokenOrProvider) {
|
|
12649
12631
|
throw new Error('User token can not be empty');
|
|
12650
12632
|
}
|
|
12651
|
-
if (tokenOrProvider &&
|
|
12652
|
-
typeof tokenOrProvider !== 'string' &&
|
|
12653
|
-
!isFunction(tokenOrProvider)) {
|
|
12633
|
+
if (typeof tokenOrProvider !== 'string' && !isFunction(tokenOrProvider)) {
|
|
12654
12634
|
throw new Error('User token should either be a string or a function');
|
|
12655
12635
|
}
|
|
12656
12636
|
if (typeof tokenOrProvider === 'string') {
|
|
@@ -12701,9 +12681,7 @@ class TokenManager {
|
|
|
12701
12681
|
throw new Error(`User token is not set. Either client.connectUser wasn't called or client.disconnect was called`);
|
|
12702
12682
|
};
|
|
12703
12683
|
this.isStatic = () => this.type === 'static';
|
|
12704
|
-
this.loadTokenPromise = null;
|
|
12705
12684
|
this.secret = secret;
|
|
12706
|
-
this.type = 'static';
|
|
12707
12685
|
}
|
|
12708
12686
|
}
|
|
12709
12687
|
|
|
@@ -12772,11 +12750,11 @@ class StreamClient {
|
|
|
12772
12750
|
* connectUser - Set the current user and open a WebSocket connection
|
|
12773
12751
|
*
|
|
12774
12752
|
* @param user Data about this user. IE {name: "john"}
|
|
12775
|
-
* @param {TokenOrProvider}
|
|
12753
|
+
* @param {TokenOrProvider} tokenOrProvider Token or provider
|
|
12776
12754
|
*
|
|
12777
12755
|
* @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup
|
|
12778
12756
|
*/
|
|
12779
|
-
this.connectUser = async (user,
|
|
12757
|
+
this.connectUser = async (user, tokenOrProvider) => {
|
|
12780
12758
|
if (!user.id) {
|
|
12781
12759
|
throw new Error('The "id" field on the user is missing');
|
|
12782
12760
|
}
|
|
@@ -12784,9 +12762,9 @@ class StreamClient {
|
|
|
12784
12762
|
* Calling connectUser multiple times is potentially the result of a bad integration, however,
|
|
12785
12763
|
* If the user id remains the same we don't throw error
|
|
12786
12764
|
*/
|
|
12787
|
-
if (this.userID === user.id && this.
|
|
12765
|
+
if (this.userID === user.id && this.connectUserTask) {
|
|
12788
12766
|
this.logger('warn', 'Consecutive calls to connectUser is detected, ideally you should only call this function once in your app.');
|
|
12789
|
-
return this.
|
|
12767
|
+
return this.connectUserTask;
|
|
12790
12768
|
}
|
|
12791
12769
|
if (this.userID) {
|
|
12792
12770
|
throw new Error('Use client.disconnect() before trying to connect as a different user. connectUser was called twice.');
|
|
@@ -12797,26 +12775,24 @@ class StreamClient {
|
|
|
12797
12775
|
// we generate the client id client side
|
|
12798
12776
|
this.userID = user.id;
|
|
12799
12777
|
this.anonymous = false;
|
|
12800
|
-
|
|
12778
|
+
await this.tokenManager.setTokenOrProvider(tokenOrProvider, user, false);
|
|
12801
12779
|
this._setUser(user);
|
|
12802
|
-
|
|
12803
|
-
this.setUserPromise = Promise.all([setTokenPromise, wsPromise]).then((result) => result[1]);
|
|
12780
|
+
this.connectUserTask = this.openConnection();
|
|
12804
12781
|
try {
|
|
12805
12782
|
addConnectionEventListeners(this.updateNetworkConnectionStatus);
|
|
12806
|
-
return await this.
|
|
12783
|
+
return await this.connectUserTask;
|
|
12807
12784
|
}
|
|
12808
12785
|
catch (err) {
|
|
12809
12786
|
if (this.persistUserOnConnectionFailure) {
|
|
12810
12787
|
// cleanup client to allow the user to retry connectUser again
|
|
12811
|
-
this.closeConnection();
|
|
12788
|
+
await this.closeConnection();
|
|
12812
12789
|
}
|
|
12813
12790
|
else {
|
|
12814
|
-
this.disconnectUser();
|
|
12791
|
+
await this.disconnectUser();
|
|
12815
12792
|
}
|
|
12816
12793
|
throw err;
|
|
12817
12794
|
}
|
|
12818
12795
|
};
|
|
12819
|
-
this._setToken = (user, userTokenOrProvider, isAnonymous) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user, isAnonymous);
|
|
12820
12796
|
this._setUser = (user) => {
|
|
12821
12797
|
/**
|
|
12822
12798
|
* This one is used by the frontend. This is a copy of the current user object stored on backend.
|
|
@@ -12860,7 +12836,7 @@ class StreamClient {
|
|
|
12860
12836
|
return;
|
|
12861
12837
|
}
|
|
12862
12838
|
this._setupConnectionIdPromise();
|
|
12863
|
-
this.clientID = `${this.userID}--${
|
|
12839
|
+
this.clientID = `${this.userID}--${generateUUIDv4()}`;
|
|
12864
12840
|
const newWsPromise = this.connect();
|
|
12865
12841
|
this.wsPromiseSafe = makeSafePromise(newWsPromise);
|
|
12866
12842
|
return await newWsPromise;
|
|
@@ -12898,7 +12874,7 @@ class StreamClient {
|
|
|
12898
12874
|
addConnectionEventListeners(this.updateNetworkConnectionStatus);
|
|
12899
12875
|
this._setupConnectionIdPromise();
|
|
12900
12876
|
this.anonymous = true;
|
|
12901
|
-
await this.
|
|
12877
|
+
await this.tokenManager.setTokenOrProvider(tokenOrProvider, user, true);
|
|
12902
12878
|
this._setUser(user);
|
|
12903
12879
|
// some endpoints require a connection_id to be resolved.
|
|
12904
12880
|
// as anonymous users aren't allowed to open WS connections, we just
|
|
@@ -13106,7 +13082,7 @@ class StreamClient {
|
|
|
13106
13082
|
this.getUserAgent = () => {
|
|
13107
13083
|
if (!this.cachedUserAgent) {
|
|
13108
13084
|
const { clientAppIdentifier = {} } = this.options;
|
|
13109
|
-
const { sdkName = 'js', sdkVersion = "1.18.
|
|
13085
|
+
const { sdkName = 'js', sdkVersion = "1.18.4", ...extras } = clientAppIdentifier;
|
|
13110
13086
|
this.cachedUserAgent = [
|
|
13111
13087
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
13112
13088
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -13125,7 +13101,7 @@ class StreamClient {
|
|
|
13125
13101
|
if (!options.headers?.['x-client-request-id']) {
|
|
13126
13102
|
options.headers = {
|
|
13127
13103
|
...options.headers,
|
|
13128
|
-
'x-client-request-id':
|
|
13104
|
+
'x-client-request-id': generateUUIDv4(),
|
|
13129
13105
|
};
|
|
13130
13106
|
}
|
|
13131
13107
|
return {
|
|
@@ -13196,7 +13172,7 @@ class StreamClient {
|
|
|
13196
13172
|
// WS connection is initialized when setUser is called
|
|
13197
13173
|
this.wsConnection = null;
|
|
13198
13174
|
this.wsPromiseSafe = null;
|
|
13199
|
-
this.
|
|
13175
|
+
this.connectUserTask = null;
|
|
13200
13176
|
// mapping between channel groups and configs
|
|
13201
13177
|
this.anonymous = false;
|
|
13202
13178
|
this.persistUserOnConnectionFailure =
|
|
@@ -13205,7 +13181,7 @@ class StreamClient {
|
|
|
13205
13181
|
// generated from secret.
|
|
13206
13182
|
this.tokenManager = new TokenManager(this.secret);
|
|
13207
13183
|
this.consecutiveFailures = 0;
|
|
13208
|
-
this.defaultWSTimeout = 15000;
|
|
13184
|
+
this.defaultWSTimeout = this.options.defaultWsTimeout ?? 15000;
|
|
13209
13185
|
this.logger = isFunction(inputOptions.logger)
|
|
13210
13186
|
? inputOptions.logger
|
|
13211
13187
|
: () => null;
|
|
@@ -13282,6 +13258,7 @@ const createTokenOrProvider = (options) => {
|
|
|
13282
13258
|
*/
|
|
13283
13259
|
class StreamVideoClient {
|
|
13284
13260
|
constructor(apiKeyOrArgs, opts) {
|
|
13261
|
+
this.effectsRegistered = false;
|
|
13285
13262
|
this.eventHandlersToUnregister = [];
|
|
13286
13263
|
this.connectionConcurrencyTag = Symbol('connectionConcurrencyTag');
|
|
13287
13264
|
this.registerClientInstance = (apiKey, user) => {
|
|
@@ -13291,27 +13268,9 @@ class StreamVideoClient {
|
|
|
13291
13268
|
}
|
|
13292
13269
|
StreamVideoClient._instances.set(instanceKey, this);
|
|
13293
13270
|
};
|
|
13294
|
-
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
* If the connection is successful, the connected user [state variable](#readonlystatestore) will be updated accordingly.
|
|
13298
|
-
*
|
|
13299
|
-
* @param user the user to connect.
|
|
13300
|
-
* @param token a token or a function that returns a token.
|
|
13301
|
-
*/
|
|
13302
|
-
this.connectUser = async (user, token) => {
|
|
13303
|
-
if (user.type === 'anonymous') {
|
|
13304
|
-
user.id = '!anon';
|
|
13305
|
-
return this.connectAnonymousUser(user, token);
|
|
13306
|
-
}
|
|
13307
|
-
const connectUser = user.type === 'guest'
|
|
13308
|
-
? () => this.streamClient.connectGuestUser(user)
|
|
13309
|
-
: () => this.streamClient.connectUser(user, token);
|
|
13310
|
-
const connectUserResponse = await withoutConcurrency(this.connectionConcurrencyTag, () => connectUser());
|
|
13311
|
-
// connectUserResponse will be void if connectUser called twice for the same user
|
|
13312
|
-
if (connectUserResponse?.me) {
|
|
13313
|
-
this.writeableStateStore.setConnectedUser(connectUserResponse.me);
|
|
13314
|
-
}
|
|
13271
|
+
this.registerEffects = () => {
|
|
13272
|
+
if (this.effectsRegistered)
|
|
13273
|
+
return;
|
|
13315
13274
|
this.eventHandlersToUnregister.push(this.on('connection.changed', (event) => {
|
|
13316
13275
|
if (!event.online)
|
|
13317
13276
|
return;
|
|
@@ -13331,7 +13290,7 @@ class StreamVideoClient {
|
|
|
13331
13290
|
}));
|
|
13332
13291
|
this.eventHandlersToUnregister.push(this.on('call.created', (event) => {
|
|
13333
13292
|
const { call, members } = event;
|
|
13334
|
-
if (
|
|
13293
|
+
if (this.state.connectedUser?.id === call.created_by.id) {
|
|
13335
13294
|
this.logger('warn', 'Received `call.created` sent by the current user');
|
|
13336
13295
|
return;
|
|
13337
13296
|
}
|
|
@@ -13348,7 +13307,7 @@ class StreamVideoClient {
|
|
|
13348
13307
|
}));
|
|
13349
13308
|
this.eventHandlersToUnregister.push(this.on('call.ring', async (event) => {
|
|
13350
13309
|
const { call, members } = event;
|
|
13351
|
-
if (
|
|
13310
|
+
if (this.state.connectedUser?.id === call.created_by.id) {
|
|
13352
13311
|
this.logger('debug', 'Received `call.ring` sent by the current user so ignoring the event');
|
|
13353
13312
|
return;
|
|
13354
13313
|
}
|
|
@@ -13372,6 +13331,53 @@ class StreamVideoClient {
|
|
|
13372
13331
|
await newCallInstance.get();
|
|
13373
13332
|
}
|
|
13374
13333
|
}));
|
|
13334
|
+
this.effectsRegistered = true;
|
|
13335
|
+
};
|
|
13336
|
+
/**
|
|
13337
|
+
* Connects the given user to the client.
|
|
13338
|
+
* Only one user can connect at a time, if you want to change users, call `disconnectUser` before connecting a new user.
|
|
13339
|
+
* If the connection is successful, the connected user [state variable](#readonlystatestore) will be updated accordingly.
|
|
13340
|
+
*
|
|
13341
|
+
* @param user the user to connect.
|
|
13342
|
+
* @param tokenOrProvider a token or a function that returns a token.
|
|
13343
|
+
*/
|
|
13344
|
+
this.connectUser = async (user, tokenOrProvider) => {
|
|
13345
|
+
if (user.type === 'anonymous') {
|
|
13346
|
+
user.id = '!anon';
|
|
13347
|
+
return this.connectAnonymousUser(user, tokenOrProvider);
|
|
13348
|
+
}
|
|
13349
|
+
const connectUserResponse = await withoutConcurrency(this.connectionConcurrencyTag, async () => {
|
|
13350
|
+
const client = this.streamClient;
|
|
13351
|
+
const { maxConnectUserRetries = 5, onConnectUserError, persistUserOnConnectionFailure, } = client.options;
|
|
13352
|
+
const errorQueue = [];
|
|
13353
|
+
for (let attempt = 0; attempt < maxConnectUserRetries; attempt++) {
|
|
13354
|
+
try {
|
|
13355
|
+
this.logger('trace', `Connecting user (${attempt})`, user);
|
|
13356
|
+
return user.type === 'guest'
|
|
13357
|
+
? await client.connectGuestUser(user)
|
|
13358
|
+
: await client.connectUser(user, tokenOrProvider);
|
|
13359
|
+
}
|
|
13360
|
+
catch (err) {
|
|
13361
|
+
this.logger('warn', `Failed to connect a user (${attempt})`, err);
|
|
13362
|
+
errorQueue.push(err);
|
|
13363
|
+
if (attempt === maxConnectUserRetries - 1) {
|
|
13364
|
+
onConnectUserError?.(err, errorQueue);
|
|
13365
|
+
throw err;
|
|
13366
|
+
}
|
|
13367
|
+
// we need to force to disconnect the user if the client is
|
|
13368
|
+
// configured to persist the user on connection failure
|
|
13369
|
+
if (persistUserOnConnectionFailure) {
|
|
13370
|
+
await client.disconnectUser();
|
|
13371
|
+
}
|
|
13372
|
+
await sleep(retryInterval(attempt));
|
|
13373
|
+
}
|
|
13374
|
+
}
|
|
13375
|
+
});
|
|
13376
|
+
// connectUserResponse will be void if connectUser called twice for the same user
|
|
13377
|
+
if (connectUserResponse?.me) {
|
|
13378
|
+
this.writeableStateStore.setConnectedUser(connectUserResponse.me);
|
|
13379
|
+
}
|
|
13380
|
+
this.registerEffects();
|
|
13375
13381
|
return connectUserResponse;
|
|
13376
13382
|
};
|
|
13377
13383
|
/**
|
|
@@ -13383,16 +13389,19 @@ class StreamVideoClient {
|
|
|
13383
13389
|
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
|
13384
13390
|
*/
|
|
13385
13391
|
this.disconnectUser = async (timeout) => {
|
|
13386
|
-
|
|
13387
|
-
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13392
|
+
await withoutConcurrency(this.connectionConcurrencyTag, async () => {
|
|
13393
|
+
const { user, key } = this.streamClient;
|
|
13394
|
+
if (!user)
|
|
13395
|
+
return;
|
|
13396
|
+
await this.streamClient.disconnectUser(timeout);
|
|
13397
|
+
if (user.id) {
|
|
13398
|
+
StreamVideoClient._instances.delete(getInstanceKey(key, user));
|
|
13399
|
+
}
|
|
13400
|
+
this.eventHandlersToUnregister.forEach((unregister) => unregister());
|
|
13401
|
+
this.eventHandlersToUnregister = [];
|
|
13402
|
+
this.effectsRegistered = false;
|
|
13403
|
+
this.writeableStateStore.setConnectedUser(undefined);
|
|
13404
|
+
});
|
|
13396
13405
|
};
|
|
13397
13406
|
/**
|
|
13398
13407
|
* You can subscribe to WebSocket events provided by the API.
|