@stream-io/video-client 1.18.7 → 1.18.8
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 +97 -169
- package/dist/index.browser.es.js +89 -52
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +89 -52
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +89 -52
- package/dist/index.es.js.map +1 -1
- package/dist/src/Call.d.ts +10 -1
- package/dist/src/devices/ScreenShareManager.d.ts +1 -3
- package/dist/src/gen/video/sfu/event/events.d.ts +1 -19
- package/dist/src/gen/video/sfu/signal_rpc/signal.client.d.ts +2 -21
- package/dist/src/gen/video/sfu/signal_rpc/signal.d.ts +1 -9
- package/dist/src/helpers/promise.d.ts +2 -2
- package/package.json +10 -11
- package/src/Call.ts +61 -10
- package/src/StreamSfuClient.ts +9 -3
- package/src/StreamVideoClient.ts +4 -5
- package/src/__tests__/Call.test.ts +1 -1
- package/src/coordinator/connection/client.ts +2 -3
- package/src/coordinator/connection/connection.ts +14 -14
- package/src/coordinator/connection/signing.ts +1 -1
- package/src/devices/BrowserPermission.ts +3 -2
- package/src/devices/ScreenShareManager.ts +1 -3
- package/src/devices/__tests__/InputMediaDeviceManager.test.ts +1 -1
- package/src/devices/__tests__/MicrophoneManager.test.ts +4 -4
- package/src/devices/__tests__/MicrophoneManagerRN.test.ts +4 -4
- package/src/devices/devices.ts +3 -1
- package/src/events/__tests__/call.test.ts +42 -57
- package/src/events/__tests__/internal.test.ts +8 -13
- package/src/events/__tests__/mutes.test.ts +7 -3
- package/src/events/__tests__/participant.test.ts +16 -20
- package/src/events/__tests__/speaker.test.ts +6 -6
- package/src/gen/coordinator/index.ts +1 -1
- package/src/gen/video/sfu/event/events.ts +22 -20
- package/src/gen/video/sfu/models/models.ts +0 -1
- package/src/gen/video/sfu/signal_rpc/signal.client.ts +27 -23
- package/src/gen/video/sfu/signal_rpc/signal.ts +13 -11
- package/src/helpers/RNSpeechDetector.ts +3 -4
- package/src/helpers/__tests__/DynascaleManager.test.ts +27 -26
- package/src/helpers/__tests__/clientUtils.test.ts +0 -1
- package/src/helpers/client-details.ts +1 -1
- package/src/helpers/promise.ts +4 -4
- package/src/rtc/Dispatcher.ts +1 -1
- package/src/rtc/Publisher.ts +2 -2
- package/src/rtc/__tests__/Publisher.test.ts +8 -8
- package/src/rtc/__tests__/Subscriber.test.ts +9 -9
- package/src/rtc/__tests__/mocks/webrtc.mocks.ts +2 -2
- package/src/rtc/helpers/__tests__/sdp.test.ts +3 -3
- package/src/stats/CallStateStatsReporter.ts +2 -3
- package/src/store/__tests__/CallState.test.ts +59 -115
- package/src/timers/worker.ts +0 -4
package/dist/index.cjs.js
CHANGED
|
@@ -10,6 +10,7 @@ var sdpTransform = require('sdp-transform');
|
|
|
10
10
|
var uaParserJs = require('ua-parser-js');
|
|
11
11
|
var https = require('https');
|
|
12
12
|
|
|
13
|
+
/* tslint:disable */
|
|
13
14
|
/**
|
|
14
15
|
* @export
|
|
15
16
|
*/
|
|
@@ -686,7 +687,6 @@ class Timestamp$Type extends runtime.MessageType {
|
|
|
686
687
|
*/
|
|
687
688
|
const Timestamp = new Timestamp$Type();
|
|
688
689
|
|
|
689
|
-
/* eslint-disable */
|
|
690
690
|
// @generated by protobuf-ts 2.9.4 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
|
|
691
691
|
// @generated from protobuf file "video/sfu/models/models.proto" (package "stream.video.sfu.models", syntax proto3)
|
|
692
692
|
// tslint:disable
|
|
@@ -1729,7 +1729,6 @@ var models = /*#__PURE__*/Object.freeze({
|
|
|
1729
1729
|
get WebsocketReconnectStrategy () { return WebsocketReconnectStrategy; }
|
|
1730
1730
|
});
|
|
1731
1731
|
|
|
1732
|
-
/* eslint-disable */
|
|
1733
1732
|
// @generated by protobuf-ts 2.9.4 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
|
|
1734
1733
|
// @generated from protobuf file "video/sfu/signal_rpc/signal.proto" (package "stream.video.sfu.signal", syntax proto3)
|
|
1735
1734
|
// tslint:disable
|
|
@@ -2194,7 +2193,6 @@ const SignalServer = new runtimeRpc.ServiceType('stream.video.sfu.signal.SignalS
|
|
|
2194
2193
|
},
|
|
2195
2194
|
]);
|
|
2196
2195
|
|
|
2197
|
-
/* eslint-disable */
|
|
2198
2196
|
// @generated by protobuf-ts 2.9.4 with parameter long_type_string,client_generic,server_none,eslint_disable,optimize_code_size
|
|
2199
2197
|
// @generated from protobuf file "video/sfu/event/events.proto" (package "stream.video.sfu.event", syntax proto3)
|
|
2200
2198
|
// tslint:disable
|
|
@@ -5825,8 +5823,8 @@ class Publisher extends BasePeerConnection {
|
|
|
5825
5823
|
? // for SVC, we only have one layer (q) and often rid is omitted
|
|
5826
5824
|
enabledLayers[0]
|
|
5827
5825
|
: // for non-SVC, we need to find the layer by rid (simulcast)
|
|
5828
|
-
enabledLayers.find((l) => l.name === encoder.rid) ??
|
|
5829
|
-
(params.encodings.length === 1 ? enabledLayers[0] : undefined);
|
|
5826
|
+
(enabledLayers.find((l) => l.name === encoder.rid) ??
|
|
5827
|
+
(params.encodings.length === 1 ? enabledLayers[0] : undefined));
|
|
5830
5828
|
// flip 'active' flag only when necessary
|
|
5831
5829
|
const shouldActivate = !!layer?.active;
|
|
5832
5830
|
if (shouldActivate !== encoder.active) {
|
|
@@ -6229,8 +6227,8 @@ const promiseWithResolvers = () => {
|
|
|
6229
6227
|
promise,
|
|
6230
6228
|
resolve: resolver,
|
|
6231
6229
|
reject: rejecter,
|
|
6232
|
-
isResolved,
|
|
6233
|
-
isRejected,
|
|
6230
|
+
isResolved: () => isResolved,
|
|
6231
|
+
isRejected: () => isRejected,
|
|
6234
6232
|
};
|
|
6235
6233
|
};
|
|
6236
6234
|
|
|
@@ -6534,7 +6532,8 @@ class StreamSfuClient {
|
|
|
6534
6532
|
this.join = async (data) => {
|
|
6535
6533
|
// wait for the signal web socket to be ready before sending "joinRequest"
|
|
6536
6534
|
await this.signalReady();
|
|
6537
|
-
if (this.joinResponseTask.isResolved ||
|
|
6535
|
+
if (this.joinResponseTask.isResolved() ||
|
|
6536
|
+
this.joinResponseTask.isRejected()) {
|
|
6538
6537
|
// we need to lock the RPC requests until we receive a JoinResponse.
|
|
6539
6538
|
// that's why we have this primitive lock mechanism.
|
|
6540
6539
|
// the client starts with already initialized joinResponseTask,
|
|
@@ -6544,7 +6543,7 @@ class StreamSfuClient {
|
|
|
6544
6543
|
// capture a reference to the current joinResponseTask as it might
|
|
6545
6544
|
// be replaced with a new one in case a second join request is made
|
|
6546
6545
|
const current = this.joinResponseTask;
|
|
6547
|
-
let timeoutId;
|
|
6546
|
+
let timeoutId = undefined;
|
|
6548
6547
|
const unsubscribe = this.dispatcher.on('joinResponse', (joinResponse) => {
|
|
6549
6548
|
this.logger('debug', 'Received joinResponse', joinResponse);
|
|
6550
6549
|
clearTimeout(timeoutId);
|
|
@@ -6657,7 +6656,8 @@ class StreamSfuClient {
|
|
|
6657
6656
|
this.createWebSocket();
|
|
6658
6657
|
}
|
|
6659
6658
|
get isHealthy() {
|
|
6660
|
-
return this.signalWs.readyState === WebSocket.OPEN
|
|
6659
|
+
return (this.signalWs.readyState === WebSocket.OPEN &&
|
|
6660
|
+
this.joinResponseTask.isResolved());
|
|
6661
6661
|
}
|
|
6662
6662
|
get joinTask() {
|
|
6663
6663
|
return this.joinResponseTask.promise;
|
|
@@ -7209,9 +7209,9 @@ const createStatsReporter = ({ subscriber, publisher, state, datacenter, polling
|
|
|
7209
7209
|
for (const track of tracks) {
|
|
7210
7210
|
const report = await pc.getStats(track);
|
|
7211
7211
|
const stats = transform(report, {
|
|
7212
|
-
// @ts-ignore
|
|
7213
7212
|
trackKind: track.kind,
|
|
7214
7213
|
kind,
|
|
7214
|
+
publisher: undefined,
|
|
7215
7215
|
});
|
|
7216
7216
|
statsForStream.push(stats);
|
|
7217
7217
|
}
|
|
@@ -7353,7 +7353,6 @@ const transform = (report, opts) => {
|
|
|
7353
7353
|
jitter: rtcStreamStats.jitter,
|
|
7354
7354
|
kind: rtcStreamStats.kind,
|
|
7355
7355
|
mediaSourceId: rtcStreamStats.mediaSourceId,
|
|
7356
|
-
// @ts-ignore: available in Chrome only, TS doesn't recognize this
|
|
7357
7356
|
qualityLimitationReason: rtcStreamStats.qualityLimitationReason,
|
|
7358
7357
|
rid: rtcStreamStats.rid,
|
|
7359
7358
|
ssrc: rtcStreamStats.ssrc,
|
|
@@ -7434,7 +7433,7 @@ const aggregate = (stats) => {
|
|
|
7434
7433
|
return report;
|
|
7435
7434
|
};
|
|
7436
7435
|
|
|
7437
|
-
const version = "1.18.
|
|
7436
|
+
const version = "1.18.8";
|
|
7438
7437
|
const [major, minor, patch] = version.split('.');
|
|
7439
7438
|
let sdkInfo = {
|
|
7440
7439
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -7544,7 +7543,7 @@ const getClientDetails = async () => {
|
|
|
7544
7543
|
'platformVersion',
|
|
7545
7544
|
]);
|
|
7546
7545
|
}
|
|
7547
|
-
catch
|
|
7546
|
+
catch {
|
|
7548
7547
|
// Ignore the error
|
|
7549
7548
|
}
|
|
7550
7549
|
}
|
|
@@ -8323,7 +8322,7 @@ class BrowserPermission {
|
|
|
8323
8322
|
this.logger = getLogger(['permissions']);
|
|
8324
8323
|
const signal = this.disposeController.signal;
|
|
8325
8324
|
this.ready = (async () => {
|
|
8326
|
-
const assumeGranted = (
|
|
8325
|
+
const assumeGranted = () => {
|
|
8327
8326
|
this.setState('prompt');
|
|
8328
8327
|
};
|
|
8329
8328
|
if (!canQueryPermissions()) {
|
|
@@ -8341,6 +8340,7 @@ class BrowserPermission {
|
|
|
8341
8340
|
}
|
|
8342
8341
|
}
|
|
8343
8342
|
catch (err) {
|
|
8343
|
+
this.logger('debug', 'Failed to query permission status', err);
|
|
8344
8344
|
assumeGranted();
|
|
8345
8345
|
}
|
|
8346
8346
|
})();
|
|
@@ -8582,6 +8582,7 @@ const getAudioStream = async (trackConstraints) => {
|
|
|
8582
8582
|
}
|
|
8583
8583
|
catch (error) {
|
|
8584
8584
|
if (isNotFoundOrOverconstrainedError(error) && trackConstraints?.deviceId) {
|
|
8585
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8585
8586
|
const { deviceId, ...relaxedConstraints } = trackConstraints;
|
|
8586
8587
|
getLogger(['devices'])('warn', 'Failed to get audio stream, will try again with relaxed constraints', { error, constraints, relaxedConstraints });
|
|
8587
8588
|
return getAudioStream(relaxedConstraints);
|
|
@@ -8617,6 +8618,7 @@ const getVideoStream = async (trackConstraints) => {
|
|
|
8617
8618
|
}
|
|
8618
8619
|
catch (error) {
|
|
8619
8620
|
if (isNotFoundOrOverconstrainedError(error) && trackConstraints?.deviceId) {
|
|
8621
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8620
8622
|
const { deviceId, ...relaxedConstraints } = trackConstraints;
|
|
8621
8623
|
getLogger(['devices'])('warn', 'Failed to get video stream, will try again with relaxed constraints', { error, constraints, relaxedConstraints });
|
|
8622
8624
|
return getVideoStream(relaxedConstraints);
|
|
@@ -8678,7 +8680,7 @@ const disposeOfMediaStream = (stream) => {
|
|
|
8678
8680
|
});
|
|
8679
8681
|
// @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
|
|
8680
8682
|
if (typeof stream.release === 'function') {
|
|
8681
|
-
// @ts-expect-error
|
|
8683
|
+
// @ts-expect-error - release() is present in react-native-webrtc
|
|
8682
8684
|
stream.release();
|
|
8683
8685
|
}
|
|
8684
8686
|
};
|
|
@@ -9537,7 +9539,7 @@ class RNSpeechDetector {
|
|
|
9537
9539
|
e.streams[0].getTracks().forEach((track) => {
|
|
9538
9540
|
// In RN, the remote track is automatically added to the audio output device
|
|
9539
9541
|
// so we need to mute it to avoid hearing the audio back
|
|
9540
|
-
// @ts-
|
|
9542
|
+
// @ts-expect-error _setVolume is a private method in react-native-webrtc
|
|
9541
9543
|
track._setVolume(0);
|
|
9542
9544
|
});
|
|
9543
9545
|
});
|
|
@@ -9577,10 +9579,9 @@ class RNSpeechDetector {
|
|
|
9577
9579
|
const initialBaselineNoiseLevel = 0.13;
|
|
9578
9580
|
let baselineNoiseLevel = initialBaselineNoiseLevel;
|
|
9579
9581
|
let speechDetected = false;
|
|
9580
|
-
let intervalId;
|
|
9581
9582
|
let speechTimer;
|
|
9582
9583
|
let silenceTimer;
|
|
9583
|
-
|
|
9584
|
+
const audioLevelHistory = []; // Store recent audio levels for smoother detection
|
|
9584
9585
|
const historyLength = 10;
|
|
9585
9586
|
const silenceThreshold = 1.1;
|
|
9586
9587
|
const resetThreshold = 0.9;
|
|
@@ -9643,7 +9644,7 @@ class RNSpeechDetector {
|
|
|
9643
9644
|
}
|
|
9644
9645
|
};
|
|
9645
9646
|
// Call checkAudioLevel periodically (every 100ms)
|
|
9646
|
-
intervalId = setInterval(checkAudioLevel, 100);
|
|
9647
|
+
const intervalId = setInterval(checkAudioLevel, 100);
|
|
9647
9648
|
return () => {
|
|
9648
9649
|
clearInterval(intervalId);
|
|
9649
9650
|
clearTimeout(speechTimer);
|
|
@@ -9989,10 +9990,8 @@ class ScreenShareManager extends InputMediaDeviceManager {
|
|
|
9989
9990
|
}
|
|
9990
9991
|
/**
|
|
9991
9992
|
* Overrides the default `select` method to throw an error.
|
|
9992
|
-
*
|
|
9993
|
-
* @param deviceId ignored.
|
|
9994
9993
|
*/
|
|
9995
|
-
async select(
|
|
9994
|
+
async select() {
|
|
9996
9995
|
throw new Error('This method is not supported in for Screen Share');
|
|
9997
9996
|
}
|
|
9998
9997
|
}
|
|
@@ -10582,13 +10581,39 @@ class Call {
|
|
|
10582
10581
|
*
|
|
10583
10582
|
* @returns a promise which resolves once the call join-flow has finished.
|
|
10584
10583
|
*/
|
|
10585
|
-
this.join = async (data) => {
|
|
10586
|
-
const connectStartTime = Date.now();
|
|
10584
|
+
this.join = async ({ maxJoinRetries = 3, ...data } = {}) => {
|
|
10587
10585
|
await this.setup();
|
|
10588
10586
|
const callingState = this.state.callingState;
|
|
10589
10587
|
if ([exports.CallingState.JOINED, exports.CallingState.JOINING].includes(callingState)) {
|
|
10590
10588
|
throw new Error(`Illegal State: call.join() shall be called only once`);
|
|
10591
10589
|
}
|
|
10590
|
+
this.state.setCallingState(exports.CallingState.JOINING);
|
|
10591
|
+
maxJoinRetries = Math.max(maxJoinRetries, 1);
|
|
10592
|
+
for (let attempt = 0; attempt < maxJoinRetries; attempt++) {
|
|
10593
|
+
try {
|
|
10594
|
+
this.logger('trace', `Joining call (${attempt})`, this.cid);
|
|
10595
|
+
return await this.doJoin(data);
|
|
10596
|
+
}
|
|
10597
|
+
catch (err) {
|
|
10598
|
+
this.logger('warn', `Failed to join call (${attempt})`, this.cid);
|
|
10599
|
+
if (attempt === maxJoinRetries - 1) {
|
|
10600
|
+
// restore the previous call state if the join-flow fails
|
|
10601
|
+
this.state.setCallingState(callingState);
|
|
10602
|
+
throw err;
|
|
10603
|
+
}
|
|
10604
|
+
}
|
|
10605
|
+
await sleep(retryInterval(attempt));
|
|
10606
|
+
}
|
|
10607
|
+
};
|
|
10608
|
+
/**
|
|
10609
|
+
* Will make a single attempt to watch for call related WebSocket events
|
|
10610
|
+
* and initiate a call session with the server.
|
|
10611
|
+
*
|
|
10612
|
+
* @returns a promise which resolves once the call join-flow has finished.
|
|
10613
|
+
*/
|
|
10614
|
+
this.doJoin = async (data) => {
|
|
10615
|
+
const connectStartTime = Date.now();
|
|
10616
|
+
const callingState = this.state.callingState;
|
|
10592
10617
|
this.joinCallData = data;
|
|
10593
10618
|
this.logger('debug', 'Starting join flow');
|
|
10594
10619
|
this.state.setCallingState(exports.CallingState.JOINING);
|
|
@@ -10669,6 +10694,8 @@ class Call {
|
|
|
10669
10694
|
}
|
|
10670
10695
|
}
|
|
10671
10696
|
catch (error) {
|
|
10697
|
+
this.logger('warn', 'Join SFU request failed', error);
|
|
10698
|
+
sfuClient.close(StreamSfuClient.ERROR_CONNECTION_UNHEALTHY, 'Join request failed, connection considered unhealthy');
|
|
10672
10699
|
// restore the previous call state if the join-flow fails
|
|
10673
10700
|
this.state.setCallingState(callingState);
|
|
10674
10701
|
throw error;
|
|
@@ -10913,9 +10940,18 @@ class Call {
|
|
|
10913
10940
|
*/
|
|
10914
10941
|
this.handleSfuSignalClose = (sfuClient) => {
|
|
10915
10942
|
this.logger('debug', '[Reconnect] SFU signal connection closed');
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10943
|
+
const { callingState } = this.state;
|
|
10944
|
+
if (
|
|
10945
|
+
// SFU WS closed before we finished current join,
|
|
10946
|
+
// no need to schedule reconnecting
|
|
10947
|
+
callingState === exports.CallingState.JOINING ||
|
|
10948
|
+
// we are already in the process of reconnecting,
|
|
10949
|
+
// no need to schedule another one
|
|
10950
|
+
callingState === exports.CallingState.RECONNECTING ||
|
|
10951
|
+
// SFU WS closed as a result of unsuccessful join,
|
|
10952
|
+
// and no further retries need to be made
|
|
10953
|
+
callingState === exports.CallingState.IDLE ||
|
|
10954
|
+
callingState === exports.CallingState.LEFT)
|
|
10919
10955
|
return;
|
|
10920
10956
|
// normal close, no need to reconnect
|
|
10921
10957
|
if (sfuClient.isLeaving)
|
|
@@ -10937,7 +10973,7 @@ class Call {
|
|
|
10937
10973
|
return;
|
|
10938
10974
|
return withoutConcurrency(this.reconnectConcurrencyTag, async () => {
|
|
10939
10975
|
this.logger('info', `[Reconnect] Reconnecting with strategy ${WebsocketReconnectStrategy[strategy]}`);
|
|
10940
|
-
|
|
10976
|
+
const reconnectStartTime = Date.now();
|
|
10941
10977
|
this.reconnectStrategy = strategy;
|
|
10942
10978
|
do {
|
|
10943
10979
|
if (this.disconnectionTimeoutSeconds > 0 &&
|
|
@@ -11004,7 +11040,7 @@ class Call {
|
|
|
11004
11040
|
const reconnectStartTime = Date.now();
|
|
11005
11041
|
this.reconnectStrategy = WebsocketReconnectStrategy.FAST;
|
|
11006
11042
|
this.state.setCallingState(exports.CallingState.RECONNECTING);
|
|
11007
|
-
await this.
|
|
11043
|
+
await this.doJoin(this.joinCallData);
|
|
11008
11044
|
this.sfuStatsReporter?.sendReconnectionTime(WebsocketReconnectStrategy.FAST, (Date.now() - reconnectStartTime) / 1000);
|
|
11009
11045
|
};
|
|
11010
11046
|
/**
|
|
@@ -11015,7 +11051,7 @@ class Call {
|
|
|
11015
11051
|
const reconnectStartTime = Date.now();
|
|
11016
11052
|
this.reconnectStrategy = WebsocketReconnectStrategy.REJOIN;
|
|
11017
11053
|
this.state.setCallingState(exports.CallingState.RECONNECTING);
|
|
11018
|
-
await this.
|
|
11054
|
+
await this.doJoin(this.joinCallData);
|
|
11019
11055
|
await this.restorePublishedTracks();
|
|
11020
11056
|
this.restoreSubscribedTracks();
|
|
11021
11057
|
this.sfuStatsReporter?.sendReconnectionTime(WebsocketReconnectStrategy.REJOIN, (Date.now() - reconnectStartTime) / 1000);
|
|
@@ -11039,7 +11075,7 @@ class Call {
|
|
|
11039
11075
|
const migrationTask = makeSafePromise(currentSfuClient.enterMigration());
|
|
11040
11076
|
try {
|
|
11041
11077
|
const currentSfu = currentSfuClient.edgeName;
|
|
11042
|
-
await this.
|
|
11078
|
+
await this.doJoin({ ...this.joinCallData, migrating_from: currentSfu });
|
|
11043
11079
|
}
|
|
11044
11080
|
finally {
|
|
11045
11081
|
// cleanup the migration_from field after the migration is complete or failed
|
|
@@ -12112,13 +12148,13 @@ class StableWSConnection {
|
|
|
12112
12148
|
// this is a permanent error raised by stream..
|
|
12113
12149
|
// usually caused by invalid auth details
|
|
12114
12150
|
const error = new Error(`WS connection reject with error ${event.reason}`);
|
|
12115
|
-
// @ts-expect-error
|
|
12151
|
+
// @ts-expect-error type issue
|
|
12116
12152
|
error.reason = event.reason;
|
|
12117
|
-
// @ts-expect-error
|
|
12153
|
+
// @ts-expect-error type issue
|
|
12118
12154
|
error.code = event.code;
|
|
12119
|
-
// @ts-expect-error
|
|
12155
|
+
// @ts-expect-error type issue
|
|
12120
12156
|
error.wasClean = event.wasClean;
|
|
12121
|
-
// @ts-expect-error
|
|
12157
|
+
// @ts-expect-error type issue
|
|
12122
12158
|
error.target = event.target;
|
|
12123
12159
|
this.rejectConnectionOpen?.(error);
|
|
12124
12160
|
this._log(`onclose() - WS connection reject with error ${event.reason}`, {
|
|
@@ -12234,7 +12270,7 @@ class StableWSConnection {
|
|
|
12234
12270
|
try {
|
|
12235
12271
|
this.ws?.send(JSON.stringify(data));
|
|
12236
12272
|
}
|
|
12237
|
-
catch
|
|
12273
|
+
catch {
|
|
12238
12274
|
// error will already be detected elsewhere
|
|
12239
12275
|
}
|
|
12240
12276
|
}, this.pingInterval);
|
|
@@ -12297,24 +12333,24 @@ class StableWSConnection {
|
|
|
12297
12333
|
this.isHealthy = false;
|
|
12298
12334
|
this.consecutiveFailures += 1;
|
|
12299
12335
|
if (
|
|
12300
|
-
// @ts-
|
|
12336
|
+
// @ts-expect-error type issue
|
|
12301
12337
|
error.code === KnownCodes.TOKEN_EXPIRED &&
|
|
12302
12338
|
!this.client.tokenManager.isStatic()) {
|
|
12303
12339
|
this._log('connect() - WS failure due to expired token, so going to try to reload token and reconnect');
|
|
12304
12340
|
this._reconnect({ refreshToken: true });
|
|
12305
12341
|
}
|
|
12306
12342
|
else {
|
|
12307
|
-
// @ts-
|
|
12343
|
+
// @ts-expect-error type issue
|
|
12308
12344
|
if (!error.isWSFailure) {
|
|
12309
12345
|
// API rejected the connection and we should not retry
|
|
12310
12346
|
throw new Error(JSON.stringify({
|
|
12311
|
-
// @ts-
|
|
12347
|
+
// @ts-expect-error type issue
|
|
12312
12348
|
code: error.code,
|
|
12313
|
-
// @ts-
|
|
12349
|
+
// @ts-expect-error type issue
|
|
12314
12350
|
StatusCode: error.StatusCode,
|
|
12315
|
-
// @ts-
|
|
12351
|
+
// @ts-expect-error type issue
|
|
12316
12352
|
message: error.message,
|
|
12317
|
-
// @ts-
|
|
12353
|
+
// @ts-expect-error type issue
|
|
12318
12354
|
isWSFailure: error.isWSFailure,
|
|
12319
12355
|
}));
|
|
12320
12356
|
}
|
|
@@ -12419,7 +12455,7 @@ class StableWSConnection {
|
|
|
12419
12455
|
await this.client.tokenManager.tokenReady();
|
|
12420
12456
|
isTokenReady = true;
|
|
12421
12457
|
}
|
|
12422
|
-
catch
|
|
12458
|
+
catch {
|
|
12423
12459
|
// token provider has failed before, so try again
|
|
12424
12460
|
}
|
|
12425
12461
|
try {
|
|
@@ -12450,7 +12486,7 @@ class StableWSConnection {
|
|
|
12450
12486
|
catch (err) {
|
|
12451
12487
|
this.client._setupConnectionIdPromise();
|
|
12452
12488
|
this.isConnecting = false;
|
|
12453
|
-
// @ts-
|
|
12489
|
+
// @ts-expect-error type issue
|
|
12454
12490
|
this._log(`_connect() - Error - `, err);
|
|
12455
12491
|
this.client.rejectConnectionId?.(err);
|
|
12456
12492
|
throw err;
|
|
@@ -12529,7 +12565,7 @@ class StableWSConnection {
|
|
|
12529
12565
|
try {
|
|
12530
12566
|
this?.ws?.close();
|
|
12531
12567
|
}
|
|
12532
|
-
catch
|
|
12568
|
+
catch {
|
|
12533
12569
|
// we don't care
|
|
12534
12570
|
}
|
|
12535
12571
|
}
|
|
@@ -12563,7 +12599,8 @@ const decodeBase64 = (s) => {
|
|
|
12563
12599
|
b = (b << 6) + c;
|
|
12564
12600
|
l += 6;
|
|
12565
12601
|
while (l >= 8) {
|
|
12566
|
-
((a = (b >>> (l -= 8)) & 0xff) || x < L - 2)
|
|
12602
|
+
if ((a = (b >>> (l -= 8)) & 0xff) || x < L - 2)
|
|
12603
|
+
r += w(a);
|
|
12567
12604
|
}
|
|
12568
12605
|
}
|
|
12569
12606
|
return r;
|
|
@@ -12942,7 +12979,7 @@ class StreamClient {
|
|
|
12942
12979
|
try {
|
|
12943
12980
|
await this.connectionIdPromise;
|
|
12944
12981
|
}
|
|
12945
|
-
catch
|
|
12982
|
+
catch {
|
|
12946
12983
|
// in case connection id was rejected
|
|
12947
12984
|
// reconnection maybe in progress
|
|
12948
12985
|
// we can wait for healthy connection to resolve, which rejects when 15s timeout is reached
|
|
@@ -12979,7 +13016,6 @@ class StreamClient {
|
|
|
12979
13016
|
this._logApiResponse(type, url, response);
|
|
12980
13017
|
this.consecutiveFailures = 0;
|
|
12981
13018
|
return this.handleResponse(response);
|
|
12982
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12983
13019
|
}
|
|
12984
13020
|
catch (e /**TODO: generalize error types */) {
|
|
12985
13021
|
e.client_request_id = requestConfig.headers?.['x-client-request-id'];
|
|
@@ -12999,7 +13035,6 @@ class StreamClient {
|
|
|
12999
13035
|
}
|
|
13000
13036
|
else {
|
|
13001
13037
|
this._logApiError(type, url, e);
|
|
13002
|
-
// eslint-disable-next-line no-throw-literal
|
|
13003
13038
|
throw e;
|
|
13004
13039
|
}
|
|
13005
13040
|
}
|
|
@@ -13072,7 +13107,7 @@ class StreamClient {
|
|
|
13072
13107
|
this.getUserAgent = () => {
|
|
13073
13108
|
if (!this.cachedUserAgent) {
|
|
13074
13109
|
const { clientAppIdentifier = {} } = this.options;
|
|
13075
|
-
const { sdkName = 'js', sdkVersion = "1.18.
|
|
13110
|
+
const { sdkName = 'js', sdkVersion = "1.18.8", ...extras } = clientAppIdentifier;
|
|
13076
13111
|
this.cachedUserAgent = [
|
|
13077
13112
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
13078
13113
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|
|
@@ -13338,7 +13373,9 @@ class StreamVideoClient {
|
|
|
13338
13373
|
}
|
|
13339
13374
|
const connectUserResponse = await withoutConcurrency(this.connectionConcurrencyTag, async () => {
|
|
13340
13375
|
const client = this.streamClient;
|
|
13341
|
-
const {
|
|
13376
|
+
const { onConnectUserError, persistUserOnConnectionFailure } = client.options;
|
|
13377
|
+
let { maxConnectUserRetries = 5 } = client.options;
|
|
13378
|
+
maxConnectUserRetries = Math.max(maxConnectUserRetries, 1);
|
|
13342
13379
|
const errorQueue = [];
|
|
13343
13380
|
for (let attempt = 0; attempt < maxConnectUserRetries; attempt++) {
|
|
13344
13381
|
try {
|