@stream-io/video-client 1.28.1 → 1.29.0

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,17 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.29.0](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.28.1...@stream-io/video-client-1.29.0) (2025-09-09)
6
+
7
+ ### Features
8
+
9
+ - opt-out from optimistic updates ([#1904](https://github.com/GetStream/stream-video-js/issues/1904)) ([45dba34](https://github.com/GetStream/stream-video-js/commit/45dba34d38dc64f456e37b593e38e420426529f5))
10
+
11
+ ### Bug Fixes
12
+
13
+ - capabilities and call grants ([#1899](https://github.com/GetStream/stream-video-js/issues/1899)) ([5725dfa](https://github.com/GetStream/stream-video-js/commit/5725dfa29b1e5fdb6fe4e26825ce7b268664d2fa))
14
+ - graceful Axios request config overrides ([#1913](https://github.com/GetStream/stream-video-js/issues/1913)) ([a124099](https://github.com/GetStream/stream-video-js/commit/a124099f984a592750d66ac440ef6c27ae7a02d9))
15
+
5
16
  ## [1.28.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.28.0...@stream-io/video-client-1.28.1) (2025-08-22)
6
17
 
7
18
  ### Bug Fixes
@@ -4,7 +4,7 @@ import { ServiceType, stackIntercept, RpcError } from '@protobuf-ts/runtime-rpc'
4
4
  import axios from 'axios';
5
5
  export { AxiosError } from 'axios';
6
6
  import { TwirpFetchTransport, TwirpErrorCode } from '@protobuf-ts/twirp-transport';
7
- import { ReplaySubject, combineLatest, BehaviorSubject, shareReplay, map, distinctUntilChanged, takeWhile, distinctUntilKeyChanged, fromEventPattern, startWith, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, of } from 'rxjs';
7
+ import { ReplaySubject, combineLatest, BehaviorSubject, shareReplay, map, distinctUntilChanged, startWith, takeWhile, distinctUntilKeyChanged, fromEventPattern, concatMap, merge, from, fromEvent, tap, debounceTime, pairwise, of } from 'rxjs';
8
8
  import { UAParser } from 'ua-parser-js';
9
9
  import { parse, write } from 'sdp-transform';
10
10
 
@@ -4649,6 +4649,7 @@ class CallState {
4649
4649
  // This happens when the participantJoined event hasn't been received yet.
4650
4650
  // We keep these tracks around until we can associate them with a participant.
4651
4651
  this.orphanedTracks = [];
4652
+ this.callGrantsSubject = new ReplaySubject(1);
4652
4653
  this.logger = getLogger(['CallState']);
4653
4654
  /**
4654
4655
  * A list of comparators that are used to sort the participants.
@@ -4776,6 +4777,15 @@ class CallState {
4776
4777
  this.setOwnCapabilities = (capabilities) => {
4777
4778
  return this.setCurrentValue(this.ownCapabilitiesSubject, capabilities);
4778
4779
  };
4780
+ /**
4781
+ * Sets the call grants (used for own capabilities).
4782
+ *
4783
+ * @internal
4784
+ * @param grants the grants to set.
4785
+ */
4786
+ this.setCallGrants = (grants) => {
4787
+ return this.setCurrentValue(this.callGrantsSubject, grants);
4788
+ };
4779
4789
  /**
4780
4790
  * Sets the backstage state.
4781
4791
  * @param backstage the backstage state.
@@ -5207,7 +5217,7 @@ class CallState {
5207
5217
  };
5208
5218
  this.updateOwnCapabilities = (event) => {
5209
5219
  if (event.user.id === this.localParticipant?.userId) {
5210
- this.setCurrentValue(this.ownCapabilitiesSubject, event.own_capabilities);
5220
+ this.setOwnCapabilities(event.own_capabilities);
5211
5221
  }
5212
5222
  };
5213
5223
  this.updateFromClosedCaptions = (event) => {
@@ -5276,25 +5286,53 @@ class CallState {
5276
5286
  const isShallowEqual = (a, b) => {
5277
5287
  if (a.length !== b.length)
5278
5288
  return false;
5279
- for (const item of a)
5289
+ for (const item of a) {
5280
5290
  if (!b.includes(item))
5281
5291
  return false;
5282
- for (const item of b)
5292
+ }
5293
+ for (const item of b) {
5283
5294
  if (!a.includes(item))
5284
5295
  return false;
5296
+ }
5285
5297
  return true;
5286
5298
  };
5287
5299
  /**
5288
5300
  * Creates an Observable from the given subject by piping to the
5289
5301
  * `distinctUntilChanged()` operator.
5290
5302
  */
5291
- const duc = (subject, comparator) => subject.asObservable().pipe(distinctUntilChanged(comparator));
5303
+ const duc = (subject, comparator) => subject.pipe(distinctUntilChanged(comparator));
5292
5304
  // primitive values should only emit once the value they hold changes
5293
5305
  this.anonymousParticipantCount$ = duc(this.anonymousParticipantCountSubject);
5294
5306
  this.blockedUserIds$ = duc(this.blockedUserIdsSubject, isShallowEqual);
5295
5307
  this.backstage$ = duc(this.backstageSubject);
5296
5308
  this.callingState$ = duc(this.callingStateSubject);
5297
- this.ownCapabilities$ = duc(this.ownCapabilitiesSubject, isShallowEqual);
5309
+ this.ownCapabilities$ = combineLatest([
5310
+ this.ownCapabilitiesSubject,
5311
+ this.callGrantsSubject.pipe(startWith(undefined)),
5312
+ ]).pipe(map(([capabilities, grants]) => {
5313
+ if (!grants)
5314
+ return capabilities;
5315
+ const { canPublishAudio, canPublishVideo, canScreenshare } = grants;
5316
+ const update = {
5317
+ [OwnCapability.SEND_AUDIO]: canPublishAudio,
5318
+ [OwnCapability.SEND_VIDEO]: canPublishVideo,
5319
+ [OwnCapability.SCREENSHARE]: canScreenshare,
5320
+ };
5321
+ const nextCapabilities = [...capabilities];
5322
+ for (const _capability in update) {
5323
+ const capability = _capability;
5324
+ const allowed = update[capability];
5325
+ // grants take precedence over capabilities, reconstruct the capabilities
5326
+ if (allowed && !nextCapabilities.includes(capability)) {
5327
+ nextCapabilities.push(capability);
5328
+ }
5329
+ else if (!allowed && nextCapabilities.includes(capability)) {
5330
+ const index = nextCapabilities.indexOf(capability);
5331
+ nextCapabilities.splice(index, 1);
5332
+ }
5333
+ }
5334
+ return nextCapabilities;
5335
+ }), distinctUntilChanged(isShallowEqual), shareReplay({ bufferSize: 1, refCount: true }));
5298
5336
  this.participantCount$ = duc(this.participantCountSubject);
5299
5337
  this.recording$ = duc(this.recordingSubject);
5300
5338
  this.transcribing$ = duc(this.transcribingSubject);
@@ -5653,7 +5691,7 @@ const getSdkVersion = (sdk) => {
5653
5691
  return sdk ? `${sdk.major}.${sdk.minor}.${sdk.patch}` : '0.0.0-development';
5654
5692
  };
5655
5693
 
5656
- const version = "1.28.1";
5694
+ const version = "1.29.0";
5657
5695
  const [major, minor, patch] = version.split('.');
5658
5696
  let sdkInfo = {
5659
5697
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -8387,21 +8425,9 @@ const watchSfuCallEnded = (call) => {
8387
8425
  const watchCallGrantsUpdated = (state) => {
8388
8426
  return function onCallGrantsUpdated(event) {
8389
8427
  const { currentGrants } = event;
8390
- if (currentGrants) {
8391
- const { canPublishAudio, canPublishVideo, canScreenshare } = currentGrants;
8392
- const update = {
8393
- [OwnCapability.SEND_AUDIO]: canPublishAudio,
8394
- [OwnCapability.SEND_VIDEO]: canPublishVideo,
8395
- [OwnCapability.SCREENSHARE]: canScreenshare,
8396
- };
8397
- const nextCapabilities = state.ownCapabilities.filter((capability) => update[capability] !== false);
8398
- Object.entries(update).forEach(([capability, value]) => {
8399
- if (value && !nextCapabilities.includes(capability)) {
8400
- nextCapabilities.push(capability);
8401
- }
8402
- });
8403
- state.setOwnCapabilities(nextCapabilities);
8404
- }
8428
+ if (!currentGrants)
8429
+ return;
8430
+ state.setCallGrants(currentGrants);
8405
8431
  };
8406
8432
  };
8407
8433
 
@@ -9996,11 +10022,10 @@ class InputMediaDeviceManager {
9996
10022
  }
9997
10023
  });
9998
10024
  }
9999
- /**
10000
- * Stops or pauses the stream based on state.disableMode
10001
- * @param {boolean} [forceStop=false] when true, stops the tracks regardless of the state.disableMode
10002
- */
10003
- async disable(forceStop = false) {
10025
+ async disable(forceStopOrOptions) {
10026
+ const forceStop = typeof forceStopOrOptions === 'boolean'
10027
+ ? forceStopOrOptions
10028
+ : (forceStopOrOptions?.forceStop ?? false);
10004
10029
  this.state.prevStatus = this.state.optimisticStatus;
10005
10030
  if (!forceStop && this.state.optimisticStatus === 'disabled') {
10006
10031
  return;
@@ -14569,7 +14594,7 @@ class StreamClient {
14569
14594
  this.getUserAgent = () => {
14570
14595
  if (!this.cachedUserAgent) {
14571
14596
  const { clientAppIdentifier = {} } = this.options;
14572
- const { sdkName = 'js', sdkVersion = "1.28.1", ...extras } = clientAppIdentifier;
14597
+ const { sdkName = 'js', sdkVersion = "1.29.0", ...extras } = clientAppIdentifier;
14573
14598
  this.cachedUserAgent = [
14574
14599
  `stream-video-${sdkName}-v${sdkVersion}`,
14575
14600
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
@@ -14591,12 +14616,14 @@ class StreamClient {
14591
14616
  'x-client-request-id': generateUUIDv4(),
14592
14617
  };
14593
14618
  }
14619
+ const { params: axiosConfigParams, headers: axiosConfigHeaders, ...axiosRequestConfig } = this.options.axiosRequestConfig || {};
14594
14620
  return {
14595
14621
  params: {
14596
14622
  user_id: this.userID,
14597
14623
  connection_id: this._getConnectionID(),
14598
14624
  api_key: this.key,
14599
14625
  ...options.params,
14626
+ ...axiosConfigParams,
14600
14627
  },
14601
14628
  headers: {
14602
14629
  ...authorization,
@@ -14605,9 +14632,10 @@ class StreamClient {
14605
14632
  : this.getAuthType(),
14606
14633
  'X-Stream-Client': this.getUserAgent(),
14607
14634
  ...options.headers,
14635
+ ...axiosConfigHeaders,
14608
14636
  },
14609
14637
  ...options.config,
14610
- ...this.options.axiosRequestConfig,
14638
+ ...axiosRequestConfig,
14611
14639
  };
14612
14640
  };
14613
14641
  this._getToken = () => {