@stream-io/video-client 1.44.2 → 1.44.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 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.44.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.44.3...@stream-io/video-client-1.44.4) (2026-03-20)
6
+
7
+ - trace device permission state transitions ([#2168](https://github.com/GetStream/stream-video-js/issues/2168)) ([e4203a3](https://github.com/GetStream/stream-video-js/commit/e4203a34cad1c90d1bc5612fc379dd1f0f0ebe5d))
8
+
9
+ ### Bug Fixes
10
+
11
+ - **react:** remove default broken microphone notification from call controls ([#2158](https://github.com/GetStream/stream-video-js/issues/2158)) ([4a95b9c](https://github.com/GetStream/stream-video-js/commit/4a95b9c29e9d2728ae7eea764f07ec8507aa0f5a))
12
+
13
+ ## [1.44.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.44.2...@stream-io/video-client-1.44.3) (2026-03-06)
14
+
15
+ ### Bug Fixes
16
+
17
+ - **client:** prevent concurrent SFU updateSubscriptions during reconnects ([#2155](https://github.com/GetStream/stream-video-js/issues/2155)) ([1ac32d2](https://github.com/GetStream/stream-video-js/commit/1ac32d261c9a54aa8e3636a60e3c8f3e1407ae16))
18
+
5
19
  ## [1.44.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.44.1...@stream-io/video-client-1.44.2) (2026-03-06)
6
20
 
7
21
  ### Bug Fixes
@@ -4005,6 +4005,8 @@ const retryable = async (rpc, signal, maxRetries = Number.POSITIVE_INFINITY) =>
4005
4005
  do {
4006
4006
  if (attempt > 0)
4007
4007
  await sleep(retryInterval(attempt));
4008
+ if (signal?.aborted)
4009
+ throw new Error(signal.reason);
4008
4010
  try {
4009
4011
  result = await rpc({ attempt });
4010
4012
  }
@@ -6281,7 +6283,7 @@ const getSdkVersion = (sdk) => {
6281
6283
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
6282
6284
  };
6283
6285
 
6284
- const version = "1.44.2";
6286
+ const version = "1.44.4";
6285
6287
  const [major, minor, patch] = version.split('.');
6286
6288
  let sdkInfo = {
6287
6289
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -8582,6 +8584,7 @@ class StreamSfuClient {
8582
8584
  this.isClosingClean = false;
8583
8585
  this.pingIntervalInMs = 5 * 1000;
8584
8586
  this.unhealthyTimeoutInMs = 15 * 1000;
8587
+ this.subscriptionsConcurrencyTag = Symbol('subscriptionsConcurrencyTag');
8585
8588
  /**
8586
8589
  * Promise that resolves when the JoinResponse is received.
8587
8590
  * Rejects after a certain threshold if the response is not received.
@@ -8689,8 +8692,10 @@ class StreamSfuClient {
8689
8692
  this.close(StreamSfuClient.NORMAL_CLOSURE, reason.substring(0, 115));
8690
8693
  };
8691
8694
  this.updateSubscriptions = async (tracks) => {
8692
- await this.joinTask;
8693
- return retryable((invocationMeta) => this.rpc.updateSubscriptions({ sessionId: this.sessionId, tracks }, { invocationMeta }), this.abortController.signal);
8695
+ return withoutConcurrency(this.subscriptionsConcurrencyTag, async () => {
8696
+ await this.joinTask;
8697
+ return retryable((invocationMeta) => this.rpc.updateSubscriptions({ sessionId: this.sessionId, tracks }, { invocationMeta }), this.abortController.signal);
8698
+ });
8694
8699
  };
8695
8700
  this.setPublisher = async (data) => {
8696
8701
  await this.joinTask;
@@ -10293,6 +10298,9 @@ class BrowserPermission {
10293
10298
  }
10294
10299
  setState(state) {
10295
10300
  if (this.state !== state) {
10301
+ const { tracer, queryName } = this.permission;
10302
+ const traceKey = `navigator.mediaDevices.${queryName}.permission`;
10303
+ tracer?.trace(traceKey, { previous: this.state, state });
10296
10304
  this.state = state;
10297
10305
  this.listeners.forEach((listener) => listener(state));
10298
10306
  }
@@ -10363,17 +10371,19 @@ const videoDeviceConstraints = {
10363
10371
  * Keeps track of the browser permission to use microphone. This permission also
10364
10372
  * affects an ability to enumerate audio devices.
10365
10373
  */
10366
- const getAudioBrowserPermission = lazy(() => new BrowserPermission({
10374
+ const getAudioBrowserPermission = lazy((tracer) => new BrowserPermission({
10367
10375
  constraints: audioDeviceConstraints,
10368
10376
  queryName: 'microphone',
10377
+ tracer,
10369
10378
  }));
10370
10379
  /**
10371
10380
  * Keeps track of the browser permission to use camera. This permission also
10372
10381
  * affects an ability to enumerate video devices.
10373
10382
  */
10374
- const getVideoBrowserPermission = lazy(() => new BrowserPermission({
10383
+ const getVideoBrowserPermission = lazy((tracer) => new BrowserPermission({
10375
10384
  constraints: videoDeviceConstraints,
10376
10385
  queryName: 'camera',
10386
+ tracer,
10377
10387
  }));
10378
10388
  const getDeviceChangeObserver = lazy((tracer) => {
10379
10389
  // 'addEventListener' is not available in React Native, returning
@@ -10389,7 +10399,7 @@ const getDeviceChangeObserver = lazy((tracer) => {
10389
10399
  * the observable errors.
10390
10400
  */
10391
10401
  const getAudioDevices = lazy((tracer) => {
10392
- return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission().asObservable()).pipe(startWith([]), concatMap(() => getDevices(getAudioBrowserPermission(), 'audioinput', tracer)), shareReplay(1));
10402
+ return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission(tracer).asObservable()).pipe(startWith([]), concatMap(() => getDevices(getAudioBrowserPermission(tracer), 'audioinput', tracer)), shareReplay(1));
10393
10403
  });
10394
10404
  /**
10395
10405
  * Prompts the user for a permission to use video devices (if not already granted
@@ -10398,7 +10408,7 @@ const getAudioDevices = lazy((tracer) => {
10398
10408
  * the observable errors.
10399
10409
  */
10400
10410
  const getVideoDevices = lazy((tracer) => {
10401
- return merge(getDeviceChangeObserver(tracer), getVideoBrowserPermission().asObservable()).pipe(startWith([]), concatMap(() => getDevices(getVideoBrowserPermission(), 'videoinput', tracer)), shareReplay(1));
10411
+ return merge(getDeviceChangeObserver(tracer), getVideoBrowserPermission(tracer).asObservable()).pipe(startWith([]), concatMap(() => getDevices(getVideoBrowserPermission(tracer), 'videoinput', tracer)), shareReplay(1));
10402
10412
  });
10403
10413
  /**
10404
10414
  * Prompts the user for a permission to use video devices (if not already granted
@@ -10407,7 +10417,7 @@ const getVideoDevices = lazy((tracer) => {
10407
10417
  * the observable errors.
10408
10418
  */
10409
10419
  const getAudioOutputDevices = lazy((tracer) => {
10410
- return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission().asObservable()).pipe(startWith([]), concatMap(() => getDevices(getAudioBrowserPermission(), 'audiooutput', tracer)), shareReplay(1));
10420
+ return merge(getDeviceChangeObserver(tracer), getAudioBrowserPermission(tracer).asObservable()).pipe(startWith([]), concatMap(() => getDevices(getAudioBrowserPermission(tracer), 'audiooutput', tracer)), shareReplay(1));
10411
10421
  });
10412
10422
  let getUserMediaExecId = 0;
10413
10423
  const getStream = async (constraints, tracer) => {
@@ -10473,25 +10483,21 @@ const getAudioStream = async (trackConstraints, tracer) => {
10473
10483
  },
10474
10484
  };
10475
10485
  try {
10476
- await getAudioBrowserPermission().prompt({
10486
+ await getAudioBrowserPermission(tracer).prompt({
10477
10487
  throwOnNotAllowed: true,
10478
10488
  forcePrompt: true,
10479
10489
  });
10480
10490
  return await getStream(constraints, tracer);
10481
10491
  }
10482
10492
  catch (error) {
10493
+ const logger = videoLoggerSystem.getLogger('devices');
10483
10494
  if (isNotFoundOrOverconstrainedError(error) && trackConstraints?.deviceId) {
10484
10495
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
10485
10496
  const { deviceId, ...relaxedConstraints } = trackConstraints;
10486
- videoLoggerSystem
10487
- .getLogger('devices')
10488
- .warn('Failed to get audio stream, will try again with relaxed constraints', { error, constraints, relaxedConstraints });
10497
+ logger.warn('Failed to get audio stream, will try again with relaxed constraints', { error, constraints, relaxedConstraints });
10489
10498
  return getAudioStream(relaxedConstraints, tracer);
10490
10499
  }
10491
- videoLoggerSystem.getLogger('devices').error('Failed to get audio stream', {
10492
- error,
10493
- constraints,
10494
- });
10500
+ logger.error('Failed to get audio stream', { error, constraints });
10495
10501
  throw error;
10496
10502
  }
10497
10503
  };
@@ -10511,25 +10517,21 @@ const getVideoStream = async (trackConstraints, tracer) => {
10511
10517
  },
10512
10518
  };
10513
10519
  try {
10514
- await getVideoBrowserPermission().prompt({
10520
+ await getVideoBrowserPermission(tracer).prompt({
10515
10521
  throwOnNotAllowed: true,
10516
10522
  forcePrompt: true,
10517
10523
  });
10518
10524
  return await getStream(constraints, tracer);
10519
10525
  }
10520
10526
  catch (error) {
10527
+ const logger = videoLoggerSystem.getLogger('devices');
10521
10528
  if (isNotFoundOrOverconstrainedError(error) && trackConstraints?.deviceId) {
10522
10529
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
10523
10530
  const { deviceId, ...relaxedConstraints } = trackConstraints;
10524
- videoLoggerSystem
10525
- .getLogger('devices')
10526
- .warn('Failed to get video stream, will try again with relaxed constraints', { error, constraints, relaxedConstraints });
10527
- return getVideoStream(relaxedConstraints);
10531
+ logger.warn('Failed to get video stream, will try again with relaxed constraints', { error, constraints, relaxedConstraints });
10532
+ return getVideoStream(relaxedConstraints, tracer);
10528
10533
  }
10529
- videoLoggerSystem.getLogger('devices').error('Failed to get video stream', {
10530
- error,
10531
- constraints,
10532
- });
10534
+ logger.error('Failed to get video stream', { error, constraints });
10533
10535
  throw error;
10534
10536
  }
10535
10537
  };
@@ -11300,8 +11302,8 @@ class DeviceManagerState {
11300
11302
  }
11301
11303
 
11302
11304
  class CameraManagerState extends DeviceManagerState {
11303
- constructor() {
11304
- super('stop-tracks', getVideoBrowserPermission());
11305
+ constructor(tracer) {
11306
+ super('stop-tracks', getVideoBrowserPermission(tracer));
11305
11307
  this.directionSubject = new BehaviorSubject(undefined);
11306
11308
  /**
11307
11309
  * Observable that emits the preferred camera direction
@@ -11355,7 +11357,7 @@ class CameraManager extends DeviceManager {
11355
11357
  * @param devicePersistence the device persistence preferences to use.
11356
11358
  */
11357
11359
  constructor(call, devicePersistence) {
11358
- super(call, new CameraManagerState(), TrackType.VIDEO, devicePersistence);
11360
+ super(call, new CameraManagerState(call.tracer), TrackType.VIDEO, devicePersistence);
11359
11361
  this.targetResolution = {
11360
11362
  width: 1280,
11361
11363
  height: 720,
@@ -11566,8 +11568,8 @@ class AudioDeviceManagerState extends DeviceManagerState {
11566
11568
  }
11567
11569
 
11568
11570
  class MicrophoneManagerState extends AudioDeviceManagerState {
11569
- constructor(disableMode) {
11570
- super(disableMode, getAudioBrowserPermission(), AudioBitrateProfile.VOICE_STANDARD_UNSPECIFIED);
11571
+ constructor(disableMode, tracer) {
11572
+ super(disableMode, getAudioBrowserPermission(tracer), AudioBitrateProfile.VOICE_STANDARD_UNSPECIFIED);
11571
11573
  this.speakingWhileMutedSubject = new BehaviorSubject(false);
11572
11574
  /**
11573
11575
  * An Observable that emits `true` if the user's microphone is muted, but they're speaking.
@@ -11907,7 +11909,7 @@ class RNSpeechDetector {
11907
11909
 
11908
11910
  class MicrophoneManager extends AudioDeviceManager {
11909
11911
  constructor(call, devicePersistence, disableMode = 'stop-tracks') {
11910
- super(call, new MicrophoneManagerState(disableMode), TrackType.AUDIO, devicePersistence);
11912
+ super(call, new MicrophoneManagerState(disableMode, call.tracer), TrackType.AUDIO, devicePersistence);
11911
11913
  this.speakingWhileMutedNotificationEnabled = true;
11912
11914
  this.soundDetectorConcurrencyTag = Symbol('soundDetectorConcurrencyTag');
11913
11915
  this.silenceThresholdMs = 5000;
@@ -12009,7 +12011,6 @@ class MicrophoneManager extends AudioDeviceManager {
12009
12011
  deviceId,
12010
12012
  label,
12011
12013
  };
12012
- console.log(event);
12013
12014
  this.call.tracer.trace('mic.capture_report', event);
12014
12015
  this.call.streamClient.dispatchEvent(event);
12015
12016
  },
@@ -15885,7 +15886,7 @@ class StreamClient {
15885
15886
  this.getUserAgent = () => {
15886
15887
  if (!this.cachedUserAgent) {
15887
15888
  const { clientAppIdentifier = {} } = this.options;
15888
- const { sdkName = 'js', sdkVersion = "1.44.2", ...extras } = clientAppIdentifier;
15889
+ const { sdkName = 'js', sdkVersion = "1.44.4", ...extras } = clientAppIdentifier;
15889
15890
  this.cachedUserAgent = [
15890
15891
  `stream-video-${sdkName}-v${sdkVersion}`,
15891
15892
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),