@stream-io/video-client 1.44.1-beta.0 → 1.44.1

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 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.44.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.44.0...@stream-io/video-client-1.44.1) (2026-03-04)
6
+
7
+ ### Bug Fixes
8
+
9
+ - **client:** handle SFU tag changes during reconnect ([#2149](https://github.com/GetStream/stream-video-js/issues/2149)) ([5aa89d3](https://github.com/GetStream/stream-video-js/commit/5aa89d378a73d33d8e46a6eb40e688bd0f50cca9)), closes [#2121](https://github.com/GetStream/stream-video-js/issues/2121)
10
+
5
11
  ## [1.44.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.43.0...@stream-io/video-client-1.44.0) (2026-02-27)
6
12
 
7
13
  - update agent instructions [skip ci] ([9cec4c6](https://github.com/GetStream/stream-video-js/commit/9cec4c6431ff51549fcfc870a0df935b0b8aa850))
@@ -4283,7 +4283,7 @@ const isSfuEvent = (eventName) => {
4283
4283
  class Dispatcher {
4284
4284
  constructor() {
4285
4285
  this.logger = videoLoggerSystem.getLogger('Dispatcher');
4286
- this.subscribers = {};
4286
+ this.subscribers = new Map();
4287
4287
  /**
4288
4288
  * Dispatch an event to all subscribers.
4289
4289
  *
@@ -4296,12 +4296,14 @@ class Dispatcher {
4296
4296
  return;
4297
4297
  const payload = message.eventPayload[eventKind];
4298
4298
  this.logger.debug(`Dispatching ${eventKind}, tag=${tag}`, payload);
4299
- const handlers = this.subscribers[eventKind];
4299
+ const handlers = this.subscribers.get(eventKind);
4300
4300
  if (!handlers)
4301
4301
  return;
4302
- this.emit(payload, handlers[tag]);
4302
+ const { byTag, dynamic } = handlers;
4303
+ this.emit(payload, byTag.get(tag));
4303
4304
  if (tag !== '*')
4304
- this.emit(payload, handlers['*']);
4305
+ this.emit(payload, byTag.get('*'));
4306
+ this.emitDynamic(payload, tag, dynamic);
4305
4307
  };
4306
4308
  /**
4307
4309
  * Emit an event to a list of listeners.
@@ -4311,26 +4313,54 @@ class Dispatcher {
4311
4313
  */
4312
4314
  this.emit = (payload, listeners = []) => {
4313
4315
  for (const listener of listeners) {
4314
- try {
4315
- listener(payload);
4316
- }
4317
- catch (e) {
4318
- this.logger.warn('Listener failed with error', e);
4316
+ this.emitOne(payload, listener);
4317
+ }
4318
+ };
4319
+ /**
4320
+ * Emit an event to a list of listeners.
4321
+ *
4322
+ */
4323
+ this.emitDynamic = (payload, tag, dynamic) => {
4324
+ for (const { tagSelector, listener } of dynamic) {
4325
+ const dynamicTag = tagSelector();
4326
+ if (dynamicTag === tag || (tag !== '*' && dynamicTag === '*')) {
4327
+ this.emitOne(payload, listener);
4319
4328
  }
4320
4329
  }
4321
4330
  };
4331
+ /**
4332
+ * Emit an event to a single listener.
4333
+ * @param payload the event payload to emit.
4334
+ * @param listener the listener to emit the event to.
4335
+ */
4336
+ this.emitOne = (payload, listener) => {
4337
+ try {
4338
+ listener(payload);
4339
+ }
4340
+ catch (e) {
4341
+ this.logger.warn('Listener failed with error', e);
4342
+ }
4343
+ };
4322
4344
  /**
4323
4345
  * Subscribe to an event.
4324
4346
  *
4325
4347
  * @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.
4348
+ * @param tag for scoping events to a specific tag. Can be a static tag
4349
+ * string or a function that resolves the tag dynamically.
4327
4350
  * @param fn the callback function to invoke when the event is emitted.
4328
4351
  * @returns a function that can be called to unsubscribe from the event.
4329
4352
  */
4330
4353
  this.on = (eventName, tag, fn) => {
4331
- var _a;
4332
- const bucket = ((_a = this.subscribers)[eventName] ?? (_a[eventName] = {}));
4333
- (bucket[tag] ?? (bucket[tag] = [])).push(fn);
4354
+ const { byTag, dynamic } = this.getHandlers(eventName);
4355
+ const listener = fn;
4356
+ if (typeof tag === 'string') {
4357
+ const listeners = byTag.get(tag) ?? [];
4358
+ listeners.push(listener);
4359
+ byTag.set(tag, listeners);
4360
+ }
4361
+ else {
4362
+ dynamic.push({ tagSelector: tag, listener });
4363
+ }
4334
4364
  return () => {
4335
4365
  this.off(eventName, tag, fn);
4336
4366
  };
@@ -4339,15 +4369,35 @@ class Dispatcher {
4339
4369
  * Unsubscribe from an event.
4340
4370
  *
4341
4371
  * @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.
4372
+ * @param tag the original static/dynamic tag selector used during subscription.
4343
4373
  * @param fn the callback function to remove from the event listeners.
4344
4374
  */
4345
4375
  this.off = (eventName, tag, fn) => {
4346
- const bucket = this.subscribers[eventName];
4347
- const listeners = bucket?.[tag];
4348
- if (!listeners)
4376
+ const bucket = this.subscribers.get(eventName);
4377
+ if (!bucket)
4349
4378
  return;
4350
- bucket[tag] = listeners.filter((f) => f !== fn);
4379
+ const { byTag, dynamic } = bucket;
4380
+ if (typeof tag === 'string') {
4381
+ const listeners = byTag.get(tag) || [];
4382
+ const idx = listeners.indexOf(fn);
4383
+ if (idx >= 0)
4384
+ listeners.splice(idx, 1);
4385
+ }
4386
+ else {
4387
+ const idx = dynamic.findIndex(({ tagSelector, listener }) => {
4388
+ return tagSelector === tag && listener === fn;
4389
+ });
4390
+ if (idx >= 0)
4391
+ dynamic.splice(idx, 1);
4392
+ }
4393
+ };
4394
+ this.getHandlers = (eventName) => {
4395
+ const existing = this.subscribers.get(eventName);
4396
+ if (existing)
4397
+ return existing;
4398
+ const next = { byTag: new Map(), dynamic: [] };
4399
+ this.subscribers.set(eventName, next);
4400
+ return next;
4351
4401
  };
4352
4402
  }
4353
4403
  }
@@ -6231,7 +6281,7 @@ const getSdkVersion = (sdk) => {
6231
6281
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6232
6282
  };
6233
6283
 
6234
- const version = "1.44.1-beta.0";
6284
+ const version = "1.44.1";
6235
6285
  const [major, minor, patch] = version.split('.');
6236
6286
  let sdkInfo = {
6237
6287
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -7298,7 +7348,8 @@ class BasePeerConnection {
7298
7348
  * Consecutive events are queued and executed one after the other.
7299
7349
  */
7300
7350
  this.on = (event, fn) => {
7301
- this.subscriptions.push(this.dispatcher.on(event, this.tag, (e) => {
7351
+ const getTag = () => this.tag;
7352
+ this.subscriptions.push(this.dispatcher.on(event, getTag, (e) => {
7302
7353
  const lockKey = `pc.${this.lock}.${event}`;
7303
7354
  withoutConcurrency(lockKey, async () => fn(e)).catch((err) => {
7304
7355
  if (this.isDisposed)
@@ -7331,6 +7382,7 @@ class BasePeerConnection {
7331
7382
  */
7332
7383
  this.setSfuClient = (sfuClient) => {
7333
7384
  this.sfuClient = sfuClient;
7385
+ this.tag = sfuClient.tag;
7334
7386
  };
7335
7387
  /**
7336
7388
  * Returns the result of the `RTCPeerConnection.getStats()` method
@@ -7521,6 +7573,7 @@ class BasePeerConnection {
7521
7573
  pc.removeEventListener('icegatheringstatechange', this.onIceGatherChange);
7522
7574
  this.unsubscribeIceTrickle?.();
7523
7575
  this.subscriptions.forEach((unsubscribe) => unsubscribe());
7576
+ this.subscriptions = [];
7524
7577
  }
7525
7578
  }
7526
7579
 
@@ -12944,8 +12997,7 @@ class Call {
12944
12997
  // const calls = useCalls().filter((c) => c.ringing);
12945
12998
  const calls = this.clientStore.calls.filter((c) => c.cid !== this.cid);
12946
12999
  this.clientStore.setCalls([this, ...calls]);
12947
- const skipSpeakerApply = isReactNative() && true;
12948
- await this.applyDeviceConfig(settings, false, skipSpeakerApply);
13000
+ await this.applyDeviceConfig(settings, false);
12949
13001
  };
12950
13002
  /**
12951
13003
  * Loads the information about the call.
@@ -12968,10 +13020,7 @@ class Call {
12968
13020
  this.watching = true;
12969
13021
  this.clientStore.registerOrUpdateCall(this);
12970
13022
  }
12971
- const skipSpeakerApply = isReactNative()
12972
- ? (params?.ring ?? this.ringing)
12973
- : false;
12974
- await this.applyDeviceConfig(response.call.settings, false, skipSpeakerApply);
13023
+ await this.applyDeviceConfig(response.call.settings, false);
12975
13024
  return response;
12976
13025
  };
12977
13026
  /**
@@ -12992,10 +13041,7 @@ class Call {
12992
13041
  this.watching = true;
12993
13042
  this.clientStore.registerOrUpdateCall(this);
12994
13043
  }
12995
- const skipSpeakerApply = isReactNative()
12996
- ? (data?.ring ?? this.ringing)
12997
- : false;
12998
- await this.applyDeviceConfig(response.call.settings, false, skipSpeakerApply);
13044
+ await this.applyDeviceConfig(response.call.settings, false);
12999
13045
  return response;
13000
13046
  };
13001
13047
  /**
@@ -14508,10 +14554,8 @@ class Call {
14508
14554
  *
14509
14555
  * @internal
14510
14556
  */
14511
- this.applyDeviceConfig = async (settings, publish, skipSpeakerApply = false) => {
14512
- if (!skipSpeakerApply) {
14513
- this.speaker.apply(settings);
14514
- }
14557
+ this.applyDeviceConfig = async (settings, publish) => {
14558
+ this.speaker.apply(settings);
14515
14559
  await this.camera.apply(settings.video, publish).catch((err) => {
14516
14560
  this.logger.warn('Camera init failed', err);
14517
14561
  });
@@ -15826,7 +15870,7 @@ class StreamClient {
15826
15870
  this.getUserAgent = () => {
15827
15871
  if (!this.cachedUserAgent) {
15828
15872
  const { clientAppIdentifier = {} } = this.options;
15829
- const { sdkName = 'js', sdkVersion = "1.44.1-beta.0", ...extras } = clientAppIdentifier;
15873
+ const { sdkName = 'js', sdkVersion = "1.44.1", ...extras } = clientAppIdentifier;
15830
15874
  this.cachedUserAgent = [
15831
15875
  `stream-video-${sdkName}-v${sdkVersion}`,
15832
15876
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),