@stream-io/video-client 1.27.1 → 1.27.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 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.27.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.27.1...@stream-io/video-client-1.27.2) (2025-08-05)
6
+
7
+ ### Bug Fixes
8
+
9
+ - improved logging and tracing ([#1874](https://github.com/GetStream/stream-video-js/issues/1874)) ([e450ce2](https://github.com/GetStream/stream-video-js/commit/e450ce2a294d6f80480fcc709591c13d9ede79e4))
10
+
5
11
  ## [1.27.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-client-1.27.0...@stream-io/video-client-1.27.1) (2025-07-25)
6
12
 
7
13
  ### Bug Fixes
@@ -5914,7 +5914,7 @@ const aggregate = (stats) => {
5914
5914
  return report;
5915
5915
  };
5916
5916
 
5917
- const version = "1.27.1";
5917
+ const version = "1.27.2";
5918
5918
  const [major, minor, patch] = version.split('.');
5919
5919
  let sdkInfo = {
5920
5920
  type: SdkType.PLAIN_JAVASCRIPT,
@@ -8069,7 +8069,6 @@ class StreamSfuClient {
8069
8069
  const current = this.joinResponseTask;
8070
8070
  let timeoutId = undefined;
8071
8071
  const unsubscribe = this.dispatcher.on('joinResponse', (joinResponse) => {
8072
- this.logger('debug', 'Received joinResponse', joinResponse);
8073
8072
  clearTimeout(timeoutId);
8074
8073
  unsubscribe();
8075
8074
  this.keepAlive();
@@ -8077,7 +8076,9 @@ class StreamSfuClient {
8077
8076
  });
8078
8077
  timeoutId = setTimeout(() => {
8079
8078
  unsubscribe();
8080
- current.reject(new Error('Waiting for "joinResponse" has timed out'));
8079
+ const message = `Waiting for "joinResponse" has timed out after ${this.joinResponseTimeout}ms`;
8080
+ this.tracer?.trace('joinRequestTimeout', message);
8081
+ current.reject(new Error(message));
8081
8082
  }, this.joinResponseTimeout);
8082
8083
  const joinRequest = SfuRequest.create({
8083
8084
  requestPayload: {
@@ -9421,6 +9422,25 @@ const CallTypes = new CallTypesRegistry([
9421
9422
  }),
9422
9423
  ]);
9423
9424
 
9425
+ /**
9426
+ * Deactivates MediaStream (stops and removes tracks) to be later garbage collected
9427
+ *
9428
+ * @param stream MediaStream
9429
+ * @returns void
9430
+ */
9431
+ const disposeOfMediaStream = (stream) => {
9432
+ if (!stream.active)
9433
+ return;
9434
+ stream.getTracks().forEach((track) => {
9435
+ track.stop();
9436
+ });
9437
+ // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
9438
+ if (typeof stream.release === 'function') {
9439
+ // @ts-expect-error - release() is present in react-native-webrtc
9440
+ stream.release();
9441
+ }
9442
+ };
9443
+
9424
9444
  class BrowserPermission {
9425
9445
  constructor(permission) {
9426
9446
  this.permission = permission;
@@ -9818,24 +9838,6 @@ const deviceIds$ = typeof navigator !== 'undefined' &&
9818
9838
  typeof navigator.mediaDevices !== 'undefined'
9819
9839
  ? getDeviceChangeObserver().pipe(startWith(undefined), concatMap(() => navigator.mediaDevices.enumerateDevices()), shareReplay(1))
9820
9840
  : undefined;
9821
- /**
9822
- * Deactivates MediaStream (stops and removes tracks) to be later garbage collected
9823
- *
9824
- * @param stream MediaStream
9825
- * @returns void
9826
- */
9827
- const disposeOfMediaStream = (stream) => {
9828
- if (!stream.active)
9829
- return;
9830
- stream.getTracks().forEach((track) => {
9831
- track.stop();
9832
- });
9833
- // @ts-expect-error release() is present in react-native-webrtc and must be called to dispose the stream
9834
- if (typeof stream.release === 'function') {
9835
- // @ts-expect-error - release() is present in react-native-webrtc
9836
- stream.release();
9837
- }
9838
- };
9839
9841
  /**
9840
9842
  * Resolves `default` device id into the real device id. Some browsers (notably,
9841
9843
  * Chromium-based) report device with id `default` among audio input and output
@@ -11849,6 +11851,12 @@ class Call {
11849
11851
  }
11850
11852
  catch (err) {
11851
11853
  this.logger('warn', `Failed to join call (${attempt})`, this.cid);
11854
+ if (err instanceof ErrorFromResponse && err.unrecoverable) {
11855
+ // if the error is unrecoverable, we should not retry as that signals
11856
+ // that connectivity is good, but the coordinator doesn't allow the user
11857
+ // to join the call due to some reason (e.g., ended call, expired token...)
11858
+ throw err;
11859
+ }
11852
11860
  const sfuId = this.credentials?.server.edge_name || '';
11853
11861
  const failures = (sfuJoinFailures.get(sfuId) || 0) + 1;
11854
11862
  sfuJoinFailures.set(sfuId, failures);
@@ -14360,12 +14368,6 @@ class StreamClient {
14360
14368
  response,
14361
14369
  });
14362
14370
  };
14363
- this._logApiError = (type, url, error) => {
14364
- this.logger('error', `client:${type} - Error - url: ${url}`, {
14365
- url,
14366
- error,
14367
- });
14368
- };
14369
14371
  this.doAxiosRequest = async (type, url, data, options = {}) => {
14370
14372
  if (!options.publicEndpoint) {
14371
14373
  await Promise.all([
@@ -14412,27 +14414,36 @@ class StreamClient {
14412
14414
  }
14413
14415
  this._logApiResponse(type, url, response);
14414
14416
  this.consecutiveFailures = 0;
14415
- return this.handleResponse(response);
14417
+ return response.data;
14416
14418
  }
14417
14419
  catch (e /**TODO: generalize error types */) {
14418
14420
  e.client_request_id = requestConfig.headers?.['x-client-request-id'];
14419
14421
  this.consecutiveFailures += 1;
14420
- if (e.response) {
14421
- this._logApiError(type, url, e.response);
14422
- /** connection_fallback depends on this token expiration logic */
14423
- if (e.response.data.code === KnownCodes.TOKEN_EXPIRED &&
14424
- !this.tokenManager.isStatic()) {
14425
- if (this.consecutiveFailures > 1) {
14426
- await sleep(retryInterval(this.consecutiveFailures));
14427
- }
14428
- await this.tokenManager.loadToken();
14429
- return await this.doAxiosRequest(type, url, data, options);
14422
+ const { response } = e;
14423
+ if (!response || !isErrorResponse(response)) {
14424
+ this.logger('error', `client:${type} url: ${url}`, e);
14425
+ throw e;
14426
+ }
14427
+ const { data: responseData, status } = response;
14428
+ const isTokenExpired = responseData.code === KnownCodes.TOKEN_EXPIRED;
14429
+ if (isTokenExpired && !this.tokenManager.isStatic()) {
14430
+ this.logger('warn', `client:${type}: url: ${url}`, response);
14431
+ if (this.consecutiveFailures > 1) {
14432
+ await sleep(retryInterval(this.consecutiveFailures));
14430
14433
  }
14431
- return this.handleResponse(e.response);
14434
+ // refresh and retry the request
14435
+ await this.tokenManager.loadToken();
14436
+ return await this.doAxiosRequest(type, url, data, options);
14432
14437
  }
14433
14438
  else {
14434
- this._logApiError(type, url, e);
14435
- throw e;
14439
+ this.logger('error', `client:${type} url: ${url}`, response);
14440
+ throw new ErrorFromResponse({
14441
+ message: `Stream error code ${responseData.code}: ${responseData.message}`,
14442
+ code: responseData.code ?? null,
14443
+ unrecoverable: responseData.unrecoverable ?? null,
14444
+ response: response,
14445
+ status: status,
14446
+ });
14436
14447
  }
14437
14448
  }
14438
14449
  };
@@ -14455,23 +14466,6 @@ class StreamClient {
14455
14466
  params,
14456
14467
  });
14457
14468
  };
14458
- this.errorFromResponse = (response) => {
14459
- const { data, status } = response;
14460
- return new ErrorFromResponse({
14461
- message: `Stream error code ${data.code}: ${data.message}`,
14462
- code: data.code ?? null,
14463
- unrecoverable: data.unrecoverable ?? null,
14464
- response: response,
14465
- status: status,
14466
- });
14467
- };
14468
- this.handleResponse = (response) => {
14469
- const data = response.data;
14470
- if (isErrorResponse(response)) {
14471
- throw this.errorFromResponse(response);
14472
- }
14473
- return data;
14474
- };
14475
14469
  this.dispatchEvent = (event) => {
14476
14470
  this.logger('debug', `Dispatching event: ${event.type}`, event);
14477
14471
  if (!this.listeners)
@@ -14504,7 +14498,7 @@ class StreamClient {
14504
14498
  this.getUserAgent = () => {
14505
14499
  if (!this.cachedUserAgent) {
14506
14500
  const { clientAppIdentifier = {} } = this.options;
14507
- const { sdkName = 'js', sdkVersion = "1.27.1", ...extras } = clientAppIdentifier;
14501
+ const { sdkName = 'js', sdkVersion = "1.27.2", ...extras } = clientAppIdentifier;
14508
14502
  this.cachedUserAgent = [
14509
14503
  `stream-video-${sdkName}-v${sdkVersion}`,
14510
14504
  ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),