@stream-io/video-client 1.42.1 → 1.42.2
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 +6 -0
- package/dist/index.browser.es.js +116 -48
- package/dist/index.browser.es.js.map +1 -1
- package/dist/index.cjs.js +116 -48
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +116 -48
- package/dist/index.es.js.map +1 -1
- package/dist/src/StreamSfuClient.d.ts +1 -6
- package/dist/src/errors/SfuJoinError.d.ts +7 -0
- package/dist/src/errors/index.d.ts +1 -0
- package/dist/src/gen/coordinator/index.d.ts +6 -0
- package/dist/src/rtc/BasePeerConnection.d.ts +1 -0
- package/dist/src/rtc/Dispatcher.d.ts +36 -2
- package/index.ts +1 -0
- package/package.json +1 -1
- package/src/Call.ts +22 -5
- package/src/StreamSfuClient.ts +36 -37
- package/src/errors/SfuJoinError.ts +26 -0
- package/src/errors/index.ts +1 -0
- package/src/events/internal.ts +4 -4
- package/src/events/speaker.ts +2 -2
- package/src/gen/coordinator/index.ts +6 -0
- package/src/rtc/BasePeerConnection.ts +3 -1
- package/src/rtc/Dispatcher.ts +55 -13
- package/src/rtc/__tests__/Dispatcher.test.ts +28 -0
- package/src/rtc/__tests__/Publisher.test.ts +3 -0
- package/src/rtc/__tests__/Subscriber.test.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.42.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.42.1...@stream-io/video-client-1.42.2) (2026-02-13)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- improve the handling of join errors and prevent cross-socket event leaking ([#2121](https://github.com/GetStream/stream-video-js/issues/2121)) ([72d0834](https://github.com/GetStream/stream-video-js/commit/72d08343243990f14f29103734eea6f7cb6092c9))
|
|
10
|
+
|
|
5
11
|
## [1.42.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.42.0...@stream-io/video-client-1.42.1) (2026-02-10)
|
|
6
12
|
|
|
7
13
|
### Bug Fixes
|
package/dist/index.browser.es.js
CHANGED
|
@@ -4271,6 +4271,12 @@ const sfuEventKinds = {
|
|
|
4271
4271
|
changePublishOptions: undefined,
|
|
4272
4272
|
inboundStateNotification: undefined,
|
|
4273
4273
|
};
|
|
4274
|
+
/**
|
|
4275
|
+
* Determines if a given event name belongs to the category of SFU events.
|
|
4276
|
+
*
|
|
4277
|
+
* @param eventName the name of the event to check.
|
|
4278
|
+
* @returns true if the event name is an SFU event, otherwise false.
|
|
4279
|
+
*/
|
|
4274
4280
|
const isSfuEvent = (eventName) => {
|
|
4275
4281
|
return Object.prototype.hasOwnProperty.call(sfuEventKinds, eventName);
|
|
4276
4282
|
};
|
|
@@ -4278,33 +4284,70 @@ class Dispatcher {
|
|
|
4278
4284
|
constructor() {
|
|
4279
4285
|
this.logger = videoLoggerSystem.getLogger('Dispatcher');
|
|
4280
4286
|
this.subscribers = {};
|
|
4281
|
-
|
|
4287
|
+
/**
|
|
4288
|
+
* Dispatch an event to all subscribers.
|
|
4289
|
+
*
|
|
4290
|
+
* @param message the event payload to dispatch.
|
|
4291
|
+
* @param tag for scoping events to a specific tag. Use `*` dispatch to every tag.
|
|
4292
|
+
*/
|
|
4293
|
+
this.dispatch = (message, tag = '*') => {
|
|
4282
4294
|
const eventKind = message.eventPayload.oneofKind;
|
|
4283
4295
|
if (!eventKind)
|
|
4284
4296
|
return;
|
|
4285
4297
|
const payload = message.eventPayload[eventKind];
|
|
4286
4298
|
this.logger.debug(`Dispatching ${eventKind}, tag=${tag}`, payload);
|
|
4287
|
-
const
|
|
4288
|
-
if (!
|
|
4299
|
+
const handlers = this.subscribers[eventKind];
|
|
4300
|
+
if (!handlers)
|
|
4289
4301
|
return;
|
|
4290
|
-
|
|
4302
|
+
this.emit(payload, handlers[tag]);
|
|
4303
|
+
if (tag !== '*')
|
|
4304
|
+
this.emit(payload, handlers['*']);
|
|
4305
|
+
};
|
|
4306
|
+
/**
|
|
4307
|
+
* Emit an event to a list of listeners.
|
|
4308
|
+
*
|
|
4309
|
+
* @param payload the event payload to emit.
|
|
4310
|
+
* @param listeners the list of listeners to emit the event to.
|
|
4311
|
+
*/
|
|
4312
|
+
this.emit = (payload, listeners = []) => {
|
|
4313
|
+
for (const listener of listeners) {
|
|
4291
4314
|
try {
|
|
4292
|
-
|
|
4315
|
+
listener(payload);
|
|
4293
4316
|
}
|
|
4294
4317
|
catch (e) {
|
|
4295
4318
|
this.logger.warn('Listener failed with error', e);
|
|
4296
4319
|
}
|
|
4297
4320
|
}
|
|
4298
4321
|
};
|
|
4299
|
-
|
|
4322
|
+
/**
|
|
4323
|
+
* Subscribe to an event.
|
|
4324
|
+
*
|
|
4325
|
+
* @param eventName the name of the event to subscribe to.
|
|
4326
|
+
* @param tag for scoping events to a specific tag. Use `*` dispatch to every tag.
|
|
4327
|
+
* @param fn the callback function to invoke when the event is emitted.
|
|
4328
|
+
* @returns a function that can be called to unsubscribe from the event.
|
|
4329
|
+
*/
|
|
4330
|
+
this.on = (eventName, tag, fn) => {
|
|
4300
4331
|
var _a;
|
|
4301
|
-
((_a = this.subscribers)[eventName] ?? (_a[eventName] =
|
|
4332
|
+
const bucket = ((_a = this.subscribers)[eventName] ?? (_a[eventName] = {}));
|
|
4333
|
+
(bucket[tag] ?? (bucket[tag] = [])).push(fn);
|
|
4302
4334
|
return () => {
|
|
4303
|
-
this.off(eventName, fn);
|
|
4335
|
+
this.off(eventName, tag, fn);
|
|
4304
4336
|
};
|
|
4305
4337
|
};
|
|
4306
|
-
|
|
4307
|
-
|
|
4338
|
+
/**
|
|
4339
|
+
* Unsubscribe from an event.
|
|
4340
|
+
*
|
|
4341
|
+
* @param eventName the name of the event to unsubscribe from.
|
|
4342
|
+
* @param tag for scoping events to a specific tag. Use `*` dispatch to every tag.
|
|
4343
|
+
* @param fn the callback function to remove from the event listeners.
|
|
4344
|
+
*/
|
|
4345
|
+
this.off = (eventName, tag, fn) => {
|
|
4346
|
+
const bucket = this.subscribers[eventName];
|
|
4347
|
+
const listeners = bucket?.[tag];
|
|
4348
|
+
if (!listeners)
|
|
4349
|
+
return;
|
|
4350
|
+
bucket[tag] = listeners.filter((f) => f !== fn);
|
|
4308
4351
|
};
|
|
4309
4352
|
}
|
|
4310
4353
|
}
|
|
@@ -6188,7 +6231,7 @@ const getSdkVersion = (sdk) => {
|
|
|
6188
6231
|
return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
|
|
6189
6232
|
};
|
|
6190
6233
|
|
|
6191
|
-
const version = "1.42.
|
|
6234
|
+
const version = "1.42.2";
|
|
6192
6235
|
const [major, minor, patch] = version.split('.');
|
|
6193
6236
|
let sdkInfo = {
|
|
6194
6237
|
type: SdkType.PLAIN_JAVASCRIPT,
|
|
@@ -7255,7 +7298,7 @@ class BasePeerConnection {
|
|
|
7255
7298
|
* Consecutive events are queued and executed one after the other.
|
|
7256
7299
|
*/
|
|
7257
7300
|
this.on = (event, fn) => {
|
|
7258
|
-
this.subscriptions.push(this.dispatcher.on(event, (e) => {
|
|
7301
|
+
this.subscriptions.push(this.dispatcher.on(event, this.tag, (e) => {
|
|
7259
7302
|
const lockKey = `pc.${this.lock}.${event}`;
|
|
7260
7303
|
withoutConcurrency(lockKey, async () => fn(e)).catch((err) => {
|
|
7261
7304
|
if (this.isDisposed)
|
|
@@ -7440,6 +7483,7 @@ class BasePeerConnection {
|
|
|
7440
7483
|
this.dispatcher = dispatcher;
|
|
7441
7484
|
this.iceRestartDelay = iceRestartDelay;
|
|
7442
7485
|
this.clientPublishOptions = clientPublishOptions;
|
|
7486
|
+
this.tag = tag;
|
|
7443
7487
|
this.onReconnectionNeeded = onReconnectionNeeded;
|
|
7444
7488
|
this.logger = videoLoggerSystem.getLogger(peerType === PeerType.SUBSCRIBER ? 'Subscriber' : 'Publisher', { tags: [tag] });
|
|
7445
7489
|
this.pc = this.createPeerConnection(connectionConfig);
|
|
@@ -8442,6 +8486,21 @@ const getTimers = lazy(() => {
|
|
|
8442
8486
|
return new WorkerTimer({ useWorker: timerWorkerEnabled });
|
|
8443
8487
|
});
|
|
8444
8488
|
|
|
8489
|
+
class SfuJoinError extends Error {
|
|
8490
|
+
constructor(event) {
|
|
8491
|
+
super(event.error?.message || 'Join Error');
|
|
8492
|
+
this.errorEvent = event;
|
|
8493
|
+
this.unrecoverable =
|
|
8494
|
+
event.reconnectStrategy === WebsocketReconnectStrategy.DISCONNECT;
|
|
8495
|
+
}
|
|
8496
|
+
static isJoinErrorCode(event) {
|
|
8497
|
+
const code = event.error?.code;
|
|
8498
|
+
return (code === ErrorCode.SFU_FULL ||
|
|
8499
|
+
code === ErrorCode.SFU_SHUTTING_DOWN ||
|
|
8500
|
+
code === ErrorCode.CALL_PARTICIPANT_LIMIT_REACHED);
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8503
|
+
|
|
8445
8504
|
/**
|
|
8446
8505
|
* The client used for exchanging information with the SFU.
|
|
8447
8506
|
*/
|
|
@@ -8618,7 +8677,7 @@ class StreamSfuClient {
|
|
|
8618
8677
|
const { timeout = 7 * 1000 } = opts;
|
|
8619
8678
|
this.migrationTask?.reject(new Error('Cancelled previous migration'));
|
|
8620
8679
|
const task = (this.migrationTask = promiseWithResolvers());
|
|
8621
|
-
const unsubscribe = this.dispatcher.on('participantMigrationComplete', () => {
|
|
8680
|
+
const unsubscribe = this.dispatcher.on('participantMigrationComplete', this.tag, () => {
|
|
8622
8681
|
unsubscribe();
|
|
8623
8682
|
clearTimeout(this.migrateAwayTimeout);
|
|
8624
8683
|
task.resolve();
|
|
@@ -8644,27 +8703,29 @@ class StreamSfuClient {
|
|
|
8644
8703
|
// be replaced with a new one in case a second join request is made
|
|
8645
8704
|
const current = this.joinResponseTask;
|
|
8646
8705
|
let timeoutId = undefined;
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
|
|
8653
|
-
|
|
8654
|
-
|
|
8706
|
+
let unsubscribeJoinResponse = undefined;
|
|
8707
|
+
let unsubscribeJoinErrorEvents = undefined;
|
|
8708
|
+
const cleanupJoinSubscriptions = () => {
|
|
8709
|
+
clearTimeout(timeoutId);
|
|
8710
|
+
timeoutId = undefined;
|
|
8711
|
+
unsubscribeJoinErrorEvents?.();
|
|
8712
|
+
unsubscribeJoinErrorEvents = undefined;
|
|
8713
|
+
unsubscribeJoinResponse?.();
|
|
8714
|
+
unsubscribeJoinResponse = undefined;
|
|
8715
|
+
};
|
|
8716
|
+
unsubscribeJoinErrorEvents = this.dispatcher.on('error', this.tag, (event) => {
|
|
8717
|
+
if (SfuJoinError.isJoinErrorCode(event)) {
|
|
8718
|
+
cleanupJoinSubscriptions();
|
|
8655
8719
|
current.reject(new SfuJoinError(event));
|
|
8656
8720
|
}
|
|
8657
8721
|
});
|
|
8658
|
-
|
|
8659
|
-
|
|
8660
|
-
unsubscribe();
|
|
8661
|
-
unsubscribeJoinErrorEvents();
|
|
8722
|
+
unsubscribeJoinResponse = this.dispatcher.on('joinResponse', this.tag, (joinResponse) => {
|
|
8723
|
+
cleanupJoinSubscriptions();
|
|
8662
8724
|
this.keepAlive();
|
|
8663
8725
|
current.resolve(joinResponse);
|
|
8664
8726
|
});
|
|
8665
8727
|
timeoutId = setTimeout(() => {
|
|
8666
|
-
|
|
8667
|
-
unsubscribeJoinErrorEvents();
|
|
8728
|
+
cleanupJoinSubscriptions();
|
|
8668
8729
|
const message = `Waiting for "joinResponse" has timed out after ${this.joinResponseTimeout}ms`;
|
|
8669
8730
|
this.tracer?.trace('joinRequestTimeout', message);
|
|
8670
8731
|
current.reject(new Error(message));
|
|
@@ -8759,8 +8820,8 @@ class StreamSfuClient {
|
|
|
8759
8820
|
// In that case, those events (ICE candidates) need to be buffered
|
|
8760
8821
|
// and later added to the appropriate PeerConnection
|
|
8761
8822
|
// once the remoteDescription is known and set.
|
|
8762
|
-
this.unsubscribeIceTrickle = dispatcher.on('iceTrickle', (
|
|
8763
|
-
this.iceTrickleBuffer.push(
|
|
8823
|
+
this.unsubscribeIceTrickle = dispatcher.on('iceTrickle', tag, (t) => {
|
|
8824
|
+
this.iceTrickleBuffer.push(t);
|
|
8764
8825
|
});
|
|
8765
8826
|
// listen to network changes to handle offline state
|
|
8766
8827
|
// we shouldn't attempt to recover websocket connection when offline
|
|
@@ -8809,14 +8870,6 @@ StreamSfuClient.DISPOSE_OLD_SOCKET = 4100;
|
|
|
8809
8870
|
* The close code used when the client fails to join the call (on the SFU).
|
|
8810
8871
|
*/
|
|
8811
8872
|
StreamSfuClient.JOIN_FAILED = 4101;
|
|
8812
|
-
class SfuJoinError extends Error {
|
|
8813
|
-
constructor(event) {
|
|
8814
|
-
super(event.error?.message || 'Join Error');
|
|
8815
|
-
this.errorEvent = event;
|
|
8816
|
-
this.unrecoverable =
|
|
8817
|
-
event.reconnectStrategy === WebsocketReconnectStrategy.DISCONNECT;
|
|
8818
|
-
}
|
|
8819
|
-
}
|
|
8820
8873
|
|
|
8821
8874
|
/**
|
|
8822
8875
|
* Event handler that watched the delivery of `call.accepted`.
|
|
@@ -8965,7 +9018,7 @@ const removeFromIfPresent = (arr, ...values) => {
|
|
|
8965
9018
|
};
|
|
8966
9019
|
|
|
8967
9020
|
const watchConnectionQualityChanged = (dispatcher, state) => {
|
|
8968
|
-
return dispatcher.on('connectionQualityChanged', (e) => {
|
|
9021
|
+
return dispatcher.on('connectionQualityChanged', '*', (e) => {
|
|
8969
9022
|
const { connectionQualityUpdates } = e;
|
|
8970
9023
|
if (!connectionQualityUpdates)
|
|
8971
9024
|
return;
|
|
@@ -8983,7 +9036,7 @@ const watchConnectionQualityChanged = (dispatcher, state) => {
|
|
|
8983
9036
|
* health check events that our SFU sends.
|
|
8984
9037
|
*/
|
|
8985
9038
|
const watchParticipantCountChanged = (dispatcher, state) => {
|
|
8986
|
-
return dispatcher.on('healthCheckResponse', (e) => {
|
|
9039
|
+
return dispatcher.on('healthCheckResponse', '*', (e) => {
|
|
8987
9040
|
const { participantCount } = e;
|
|
8988
9041
|
if (participantCount) {
|
|
8989
9042
|
state.setParticipantCount(participantCount.total);
|
|
@@ -8992,7 +9045,7 @@ const watchParticipantCountChanged = (dispatcher, state) => {
|
|
|
8992
9045
|
});
|
|
8993
9046
|
};
|
|
8994
9047
|
const watchLiveEnded = (dispatcher, call) => {
|
|
8995
|
-
return dispatcher.on('error', (e) => {
|
|
9048
|
+
return dispatcher.on('error', '*', (e) => {
|
|
8996
9049
|
if (e.error && e.error.code !== ErrorCode.LIVE_ENDED)
|
|
8997
9050
|
return;
|
|
8998
9051
|
call.state.setBackstage(true);
|
|
@@ -9007,7 +9060,7 @@ const watchLiveEnded = (dispatcher, call) => {
|
|
|
9007
9060
|
* Watches and logs the errors reported by the currently connected SFU.
|
|
9008
9061
|
*/
|
|
9009
9062
|
const watchSfuErrorReports = (dispatcher) => {
|
|
9010
|
-
return dispatcher.on('error', (e) => {
|
|
9063
|
+
return dispatcher.on('error', '*', (e) => {
|
|
9011
9064
|
if (!e.error)
|
|
9012
9065
|
return;
|
|
9013
9066
|
const logger = videoLoggerSystem.getLogger('SfuClient');
|
|
@@ -9206,7 +9259,7 @@ const reconcileOrphanedTracks = (state, participant) => {
|
|
|
9206
9259
|
* Watches for `dominantSpeakerChanged` events.
|
|
9207
9260
|
*/
|
|
9208
9261
|
const watchDominantSpeakerChanged = (dispatcher, state) => {
|
|
9209
|
-
return dispatcher.on('dominantSpeakerChanged', (e) => {
|
|
9262
|
+
return dispatcher.on('dominantSpeakerChanged', '*', (e) => {
|
|
9210
9263
|
const { sessionId } = e;
|
|
9211
9264
|
if (sessionId === state.dominantSpeaker?.sessionId)
|
|
9212
9265
|
return;
|
|
@@ -9233,7 +9286,7 @@ const watchDominantSpeakerChanged = (dispatcher, state) => {
|
|
|
9233
9286
|
* Watches for `audioLevelChanged` events.
|
|
9234
9287
|
*/
|
|
9235
9288
|
const watchAudioLevelChanged = (dispatcher, state) => {
|
|
9236
|
-
return dispatcher.on('audioLevelChanged', (e) => {
|
|
9289
|
+
return dispatcher.on('audioLevelChanged', '*', (e) => {
|
|
9237
9290
|
const { audioLevels } = e;
|
|
9238
9291
|
state.updateParticipants(audioLevels.reduce((patches, current) => {
|
|
9239
9292
|
patches[current.sessionId] = {
|
|
@@ -12589,7 +12642,7 @@ class Call {
|
|
|
12589
12642
|
*/
|
|
12590
12643
|
this.on = (eventName, fn) => {
|
|
12591
12644
|
if (isSfuEvent(eventName)) {
|
|
12592
|
-
return this.dispatcher.on(eventName, fn);
|
|
12645
|
+
return this.dispatcher.on(eventName, '*', fn);
|
|
12593
12646
|
}
|
|
12594
12647
|
const offHandler = this.streamClient.on(eventName, (e) => {
|
|
12595
12648
|
const event = e;
|
|
@@ -12611,7 +12664,7 @@ class Call {
|
|
|
12611
12664
|
*/
|
|
12612
12665
|
this.off = (eventName, fn) => {
|
|
12613
12666
|
if (isSfuEvent(eventName)) {
|
|
12614
|
-
return this.dispatcher.off(eventName, fn);
|
|
12667
|
+
return this.dispatcher.off(eventName, '*', fn);
|
|
12615
12668
|
}
|
|
12616
12669
|
// unsubscribe from the stream client event by using the 'off' reference
|
|
12617
12670
|
const registeredOffHandler = this.streamClientEventHandlers.get(fn);
|
|
@@ -12847,6 +12900,7 @@ class Call {
|
|
|
12847
12900
|
this.logger.trace(`Joining call (${attempt})`, this.cid);
|
|
12848
12901
|
await this.doJoin(data);
|
|
12849
12902
|
delete joinData.migrating_from;
|
|
12903
|
+
delete joinData.migrating_from_list;
|
|
12850
12904
|
break;
|
|
12851
12905
|
}
|
|
12852
12906
|
catch (err) {
|
|
@@ -12858,11 +12912,15 @@ class Call {
|
|
|
12858
12912
|
// to join the call due to some reason (e.g., ended call, expired token...)
|
|
12859
12913
|
throw err;
|
|
12860
12914
|
}
|
|
12915
|
+
// immediately switch to a different SFU in case of recoverable join error
|
|
12916
|
+
const switchSfu = err instanceof SfuJoinError &&
|
|
12917
|
+
SfuJoinError.isJoinErrorCode(err.errorEvent);
|
|
12861
12918
|
const sfuId = this.credentials?.server.edge_name || '';
|
|
12862
12919
|
const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
|
|
12863
12920
|
sfuJoinFailures.set(sfuId, failures);
|
|
12864
|
-
if (failures >= 2) {
|
|
12921
|
+
if (switchSfu || failures >= 2) {
|
|
12865
12922
|
joinData.migrating_from = sfuId;
|
|
12923
|
+
joinData.migrating_from_list = Array.from(sfuJoinFailures.keys());
|
|
12866
12924
|
}
|
|
12867
12925
|
if (attempt === maxJoinRetries - 1) {
|
|
12868
12926
|
throw err;
|
|
@@ -13390,12 +13448,17 @@ class Call {
|
|
|
13390
13448
|
const migrationTask = makeSafePromise(currentSfuClient.enterMigration());
|
|
13391
13449
|
try {
|
|
13392
13450
|
const currentSfu = currentSfuClient.edgeName;
|
|
13393
|
-
await this.doJoin({
|
|
13451
|
+
await this.doJoin({
|
|
13452
|
+
...this.joinCallData,
|
|
13453
|
+
migrating_from: currentSfu,
|
|
13454
|
+
migrating_from_list: [currentSfu],
|
|
13455
|
+
});
|
|
13394
13456
|
}
|
|
13395
13457
|
finally {
|
|
13396
13458
|
// cleanup the migration_from field after the migration is complete or failed
|
|
13397
13459
|
// as we don't want to keep dirty data in the join call data
|
|
13398
13460
|
delete this.joinCallData?.migrating_from;
|
|
13461
|
+
delete this.joinCallData?.migrating_from_list;
|
|
13399
13462
|
}
|
|
13400
13463
|
await this.restorePublishedTracks();
|
|
13401
13464
|
this.restoreSubscribedTracks();
|
|
@@ -13430,6 +13493,11 @@ class Call {
|
|
|
13430
13493
|
// handles the "error" event, through which the SFU can request a reconnect
|
|
13431
13494
|
const unregisterOnError = this.on('error', (e) => {
|
|
13432
13495
|
const { reconnectStrategy: strategy, error } = e;
|
|
13496
|
+
// SFU_FULL is a join error, and when emitted, although it specifies a
|
|
13497
|
+
// `migrate` strategy, we should actually perform a REJOIN to a new SFU.
|
|
13498
|
+
// This is now handled separately in the `call.join()` method.
|
|
13499
|
+
if (SfuJoinError.isJoinErrorCode(e))
|
|
13500
|
+
return;
|
|
13433
13501
|
if (strategy === WebsocketReconnectStrategy.UNSPECIFIED)
|
|
13434
13502
|
return;
|
|
13435
13503
|
if (strategy === WebsocketReconnectStrategy.DISCONNECT) {
|
|
@@ -15528,7 +15596,7 @@ class StreamClient {
|
|
|
15528
15596
|
this.getUserAgent = () => {
|
|
15529
15597
|
if (!this.cachedUserAgent) {
|
|
15530
15598
|
const { clientAppIdentifier = {} } = this.options;
|
|
15531
|
-
const { sdkName = 'js', sdkVersion = "1.42.
|
|
15599
|
+
const { sdkName = 'js', sdkVersion = "1.42.2", ...extras } = clientAppIdentifier;
|
|
15532
15600
|
this.cachedUserAgent = [
|
|
15533
15601
|
`stream-video-${sdkName}-v${sdkVersion}`,
|
|
15534
15602
|
...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
|