@stream-io/video-client 1.17.0 → 1.18.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.
@@ -21,7 +21,7 @@ export declare class StreamClient {
21
21
  setUserPromise: ConnectAPIResponse | null;
22
22
  tokenManager: TokenManager;
23
23
  user?: UserWithId;
24
- userAgent?: string;
24
+ private cachedUserAgent?;
25
25
  userID?: string;
26
26
  wsBaseURL?: string;
27
27
  wsConnection: StableWSConnection | null;
@@ -142,11 +142,6 @@ export declare class StreamClient {
142
142
  */
143
143
  connect: () => Promise<ConnectedEvent | undefined>;
144
144
  getUserAgent: () => string;
145
- setUserAgent: (userAgent: string) => void;
146
- /**
147
- * _isUsingServerAuth - Returns true if we're using server side auth
148
- */
149
- _isUsingServerAuth: () => boolean;
150
145
  _enrichAxiosOptions: (options?: AxiosRequestConfig & {
151
146
  config?: AxiosRequestConfig;
152
147
  } & {
@@ -124,6 +124,18 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
124
124
  * timer throttling issues in inactive browser tabs.
125
125
  */
126
126
  enableTimerWorker?: boolean;
127
+ /**
128
+ * The client app identifier.
129
+ */
130
+ clientAppIdentifier?: ClientAppIdentifier;
131
+ };
132
+ export type ClientAppIdentifier = {
133
+ sdkName?: 'react' | 'react-native' | 'plain-javascript' | (string & {});
134
+ sdkVersion?: string;
135
+ app?: string;
136
+ app_version?: string;
137
+ os?: string;
138
+ device_model?: string;
127
139
  };
128
140
  export type TokenProvider = () => Promise<string>;
129
141
  export type TokenOrProvider = null | string | TokenProvider | undefined;
@@ -51,8 +51,6 @@ export declare class DynascaleManager {
51
51
  }>;
52
52
  /**
53
53
  * Creates a new DynascaleManager instance.
54
- *
55
- * @param call the call to manage.
56
54
  */
57
55
  constructor(callState: CallState, speaker: SpeakerManager);
58
56
  setSfuClient(sfuClient: StreamSfuClient | undefined): void;
@@ -5,14 +5,9 @@ type WebRTCInfoType = {
5
5
  export declare const setSdkInfo: (info: Sdk) => void;
6
6
  export declare const getSdkInfo: () => Sdk | undefined;
7
7
  export declare const setOSInfo: (info: OS) => void;
8
- export declare const getOSInfo: () => OS | undefined;
9
8
  export declare const setDeviceInfo: (info: Device) => void;
10
- export declare const getDeviceInfo: () => Device | undefined;
11
9
  export declare const getWebRTCInfo: () => WebRTCInfoType | undefined;
12
10
  export declare const setWebRTCInfo: (info: WebRTCInfoType) => void;
13
- export type LocalClientDetailsType = ClientDetails & {
14
- webRTCInfo?: WebRTCInfoType;
15
- };
16
11
  export declare const setThermalState: (state: string) => void;
17
12
  export declare const setPowerState: (powerMode: boolean) => void;
18
13
  export declare const getDeviceState: () => {
@@ -24,5 +19,5 @@ export declare const getDeviceState: () => {
24
19
  } | {
25
20
  oneofKind: undefined;
26
21
  };
27
- export declare const getClientDetails: () => LocalClientDetailsType;
22
+ export declare const getClientDetails: () => Promise<ClientDetails>;
28
23
  export {};
@@ -1,4 +1,4 @@
1
- import { StreamClientOptions, TokenOrProvider, User } from '../coordinator/connection/types';
1
+ import type { StreamClientOptions, TokenOrProvider, User } from '../coordinator/connection/types';
2
2
  import { StreamClient } from '../coordinator/connection/client';
3
3
  import type { StreamVideoClientOptions } from '../StreamVideoClient';
4
4
  /**
@@ -1,13 +1,12 @@
1
1
  import { StreamSfuClient } from '../StreamSfuClient';
2
2
  import { StatsOptions } from '../gen/coordinator';
3
3
  import { Publisher, Subscriber } from '../rtc';
4
- import { LocalClientDetailsType } from '../helpers/client-details';
5
- import { WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
4
+ import { ClientDetails, WebsocketReconnectStrategy } from '../gen/video/sfu/models/models';
6
5
  import { CameraManager, MicrophoneManager } from '../devices';
7
6
  import { CallState } from '../store';
8
7
  export type SfuStatsReporterOptions = {
9
8
  options: StatsOptions;
10
- clientDetails: LocalClientDetailsType;
9
+ clientDetails: ClientDetails;
11
10
  subscriber: Subscriber;
12
11
  publisher?: Publisher;
13
12
  microphone: MicrophoneManager;
@@ -1,18 +1,14 @@
1
- import { type LocalClientDetailsType } from '../helpers/client-details';
2
- import { Sdk } from '../gen/video/sfu/models/models';
1
+ import { ClientDetails, Sdk } from '../gen/video/sfu/models/models';
3
2
  /**
4
3
  * Flatten the stats report into an array of stats objects.
5
4
  *
6
5
  * @param report the report to flatten.
7
6
  */
8
7
  export declare const flatten: (report: RTCStatsReport) => RTCStats[];
9
- export declare const getSdkSignature: (clientDetails: LocalClientDetailsType) => {
8
+ export declare const getSdkSignature: (clientDetails: ClientDetails) => {
10
9
  os?: import("../gen/video/sfu/models/models").OS;
11
10
  browser?: import("../gen/video/sfu/models/models").Browser;
12
11
  device?: import("../gen/video/sfu/models/models").Device;
13
- webRTCInfo?: {
14
- version: string;
15
- };
16
12
  sdkName: string;
17
13
  sdkVersion: string;
18
14
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-client",
3
- "version": "1.17.0",
3
+ "version": "1.18.0",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.es.js",
@@ -31,7 +31,7 @@
31
31
  "@protobuf-ts/runtime": "^2.9.4",
32
32
  "@protobuf-ts/runtime-rpc": "^2.9.4",
33
33
  "@protobuf-ts/twirp-transport": "^2.9.4",
34
- "axios": "^1.7.9",
34
+ "axios": "^1.8.1",
35
35
  "rxjs": "~7.8.1",
36
36
  "sdp-transform": "^2.15.0",
37
37
  "ua-parser-js": "^1.0.40",
package/src/Call.ts CHANGED
@@ -601,6 +601,7 @@ export class Call {
601
601
  this.leaveCallHooks.forEach((hook) => hook());
602
602
  this.initialized = false;
603
603
  this.hasJoinedOnce = false;
604
+ this.ringingSubject.next(false);
604
605
  this.clientStore.unregisterCall(this);
605
606
 
606
607
  this.camera.dispose();
@@ -692,7 +693,7 @@ export class Call {
692
693
  this.state.setMembers(response.members);
693
694
  this.state.setOwnCapabilities(response.own_capabilities);
694
695
 
695
- if (params?.ring || this.ringing) {
696
+ if (params?.ring) {
696
697
  // the call response can indicate where the call is still ringing or not
697
698
  this.ringingSubject.next(true);
698
699
  }
@@ -723,7 +724,7 @@ export class Call {
723
724
  this.state.setMembers(response.members);
724
725
  this.state.setOwnCapabilities(response.own_capabilities);
725
726
 
726
- if (data?.ring || this.ringing) {
727
+ if (data?.ring) {
727
728
  // the call response can indicate where the call is still ringing or not
728
729
  this.ringingSubject.next(true);
729
730
  }
@@ -866,7 +867,7 @@ export class Call {
866
867
  this.sfuClient = sfuClient;
867
868
  this.dynascaleManager.setSfuClient(sfuClient);
868
869
 
869
- const clientDetails = getClientDetails();
870
+ const clientDetails = await getClientDetails();
870
871
  // we don't need to send JoinRequest if we are re-using an existing healthy SFU client
871
872
  if (previousSfuClient !== sfuClient) {
872
873
  // prepare a generic SDP and send it to the SFU.
@@ -1192,7 +1193,7 @@ export class Call {
1192
1193
  this.state.setMembers(joinResponse.members);
1193
1194
  this.state.setOwnCapabilities(joinResponse.own_capabilities);
1194
1195
 
1195
- if (data?.ring && !this.ringing) {
1196
+ if (data?.ring) {
1196
1197
  this.ringingSubject.next(true);
1197
1198
  }
1198
1199
 
@@ -2308,8 +2309,9 @@ export class Call {
2308
2309
  custom,
2309
2310
  }: Pick<CollectUserFeedbackRequest, 'reason' | 'custom'> = {},
2310
2311
  ): Promise<CollectUserFeedbackResponse> => {
2311
- const { sdkName, sdkVersion, ...platform } =
2312
- getSdkSignature(getClientDetails());
2312
+ const { sdkName, sdkVersion, ...platform } = getSdkSignature(
2313
+ await getClientDetails(),
2314
+ );
2313
2315
  return this.streamClient.post<
2314
2316
  CollectUserFeedbackResponse,
2315
2317
  CollectUserFeedbackRequest
@@ -38,6 +38,7 @@ import {
38
38
  CreateGuestResponse,
39
39
  } from '../../gen/coordinator';
40
40
  import { makeSafePromise, type SafePromise } from '../../helpers/promise';
41
+ import { getLogLevel } from '../../logger';
41
42
 
42
43
  export class StreamClient {
43
44
  _user?: UserWithId;
@@ -61,7 +62,7 @@ export class StreamClient {
61
62
  setUserPromise: ConnectAPIResponse | null;
62
63
  tokenManager: TokenManager;
63
64
  user?: UserWithId;
64
- userAgent?: string;
65
+ private cachedUserAgent?: string;
65
66
  userID?: string;
66
67
  wsBaseURL?: string;
67
68
  wsConnection: StableWSConnection | null;
@@ -218,10 +219,7 @@ export class StreamClient {
218
219
  );
219
220
  }
220
221
 
221
- if (
222
- (this._isUsingServerAuth() || this.node) &&
223
- !this.options.allowServerSideConnect
224
- ) {
222
+ if ((this.secret || this.node) && !this.options.allowServerSideConnect) {
225
223
  this.logger(
226
224
  'warn',
227
225
  'Please do not use connectUser server side. Use our @stream-io/node-sdk instead: https://getstream.io/video/docs/api/',
@@ -326,7 +324,7 @@ export class StreamClient {
326
324
  return;
327
325
  }
328
326
 
329
- await this._setupConnectionIdPromise();
327
+ this._setupConnectionIdPromise();
330
328
 
331
329
  this.clientID = `${this.userID}--${randomId()}`;
332
330
  const newWsPromise = this.connect();
@@ -382,7 +380,7 @@ export class StreamClient {
382
380
  tokenOrProvider: TokenOrProvider,
383
381
  ) => {
384
382
  addConnectionEventListeners(this.updateNetworkConnectionStatus);
385
- await this._setupConnectionIdPromise();
383
+ this._setupConnectionIdPromise();
386
384
 
387
385
  this.anonymous = true;
388
386
  await this._setToken(user, tokenOrProvider, this.anonymous);
@@ -469,6 +467,7 @@ export class StreamClient {
469
467
  config?: AxiosRequestConfig & { maxBodyLength?: number };
470
468
  },
471
469
  ) => {
470
+ if (getLogLevel() !== 'trace') return;
472
471
  this.logger('trace', `client: ${type} - Request - ${url}`, {
473
472
  payload: data,
474
473
  config,
@@ -480,6 +479,7 @@ export class StreamClient {
480
479
  url: string,
481
480
  response: AxiosResponse<T>,
482
481
  ) => {
482
+ if (getLogLevel() !== 'trace') return;
483
483
  this.logger(
484
484
  'trace',
485
485
  `client:${type} - Response - url: ${url} > status ${response.status}`,
@@ -666,25 +666,25 @@ export class StreamClient {
666
666
  return await this.wsConnection.connect(this.defaultWSTimeout);
667
667
  };
668
668
 
669
- getUserAgent = () => {
670
- const version = process.env.PKG_VERSION || '0.0.0-development';
671
- return (
672
- this.userAgent ||
673
- `stream-video-javascript-client-${
674
- this.node ? 'node' : 'browser'
675
- }-${version}`
676
- );
677
- };
669
+ getUserAgent = (): string => {
670
+ if (!this.cachedUserAgent) {
671
+ const { clientAppIdentifier = {} } = this.options;
672
+ const {
673
+ sdkName = 'js',
674
+ sdkVersion = process.env.PKG_VERSION || '0.0.0',
675
+ ...extras
676
+ } = clientAppIdentifier;
677
+
678
+ this.cachedUserAgent = [
679
+ `stream-video-${sdkName}-v${sdkVersion}`,
680
+ ...Object.entries(extras).map(([key, value]) => `${key}=${value}`),
681
+ `client_bundle=${process.env.CLIENT_BUNDLE || (this.node ? 'node' : 'browser')}`,
682
+ ].join('|');
683
+ }
678
684
 
679
- setUserAgent = (userAgent: string) => {
680
- this.userAgent = userAgent;
685
+ return this.cachedUserAgent;
681
686
  };
682
687
 
683
- /**
684
- * _isUsingServerAuth - Returns true if we're using server side auth
685
- */
686
- _isUsingServerAuth = () => !!this.secret;
687
-
688
688
  _enrichAxiosOptions = (
689
689
  options: AxiosRequestConfig & { config?: AxiosRequestConfig } & {
690
690
  publicEndpoint?: boolean;
@@ -329,7 +329,7 @@ export class StableWSConnection {
329
329
  return response;
330
330
  }
331
331
  } catch (err) {
332
- await this.client._setupConnectionIdPromise();
332
+ this.client._setupConnectionIdPromise();
333
333
  this.isConnecting = false;
334
334
  // @ts-ignore
335
335
  this._log(`_connect() - Error - `, err);
@@ -152,6 +152,20 @@ export type StreamClientOptions = Partial<AxiosRequestConfig> & {
152
152
  * timer throttling issues in inactive browser tabs.
153
153
  */
154
154
  enableTimerWorker?: boolean;
155
+
156
+ /**
157
+ * The client app identifier.
158
+ */
159
+ clientAppIdentifier?: ClientAppIdentifier;
160
+ };
161
+
162
+ export type ClientAppIdentifier = {
163
+ sdkName?: 'react' | 'react-native' | 'plain-javascript' | (string & {});
164
+ sdkVersion?: string;
165
+ app?: string;
166
+ app_version?: string;
167
+ os?: string;
168
+ device_model?: string;
155
169
  };
156
170
 
157
171
  export type TokenProvider = () => Promise<string>;
@@ -112,8 +112,6 @@ export class DynascaleManager {
112
112
 
113
113
  /**
114
114
  * Creates a new DynascaleManager instance.
115
- *
116
- * @param call the call to manage.
117
115
  */
118
116
  constructor(callState: CallState, speaker: SpeakerManager) {
119
117
  this.callState = callState;
@@ -5,6 +5,7 @@ import {
5
5
  getInstanceKey,
6
6
  } from '../clientUtils';
7
7
  import { TokenProvider } from '../../coordinator/connection/types';
8
+ import { getSdkInfo, setSdkInfo } from '../client-details';
8
9
 
9
10
  describe('clientUtils', () => {
10
11
  describe('getInstanceKey', () => {
@@ -46,14 +47,39 @@ describe('clientUtils', () => {
46
47
 
47
48
  describe('createCoordinatorClient', () => {
48
49
  it('should create a coordinator client', () => {
49
- const client = createCoordinatorClient('apiKey', { timeout: 1000 });
50
+ const client = createCoordinatorClient('apiKey', {
51
+ timeout: 1000,
52
+ clientAppIdentifier: {
53
+ app: 'vitest',
54
+ app_version: '1.0.0',
55
+ device_model: 'iPhone',
56
+ os: 'iOS 18.4',
57
+ },
58
+ });
50
59
  expect(client).toBeDefined();
51
- expect(client.userAgent).toBe(
52
- 'stream-video-javascript-client-node-0.0.0-development-video-plain_javascript-sdk-0.0.0',
60
+ expect(client.getUserAgent()).toBe(
61
+ 'stream-video-plain_javascript-v0.0.0' +
62
+ '|app=vitest' +
63
+ '|app_version=1.0.0' +
64
+ '|device_model=iPhone' +
65
+ '|os=iOS 18.4' +
66
+ '|client_bundle=node',
53
67
  );
54
68
  expect(client.logger).toBeDefined();
55
69
  expect(client.options.persistUserOnConnectionFailure).toBe(true);
56
70
  expect(client.options.timeout).toBe(1000);
57
71
  });
72
+
73
+ it('should use default x-stream-client header', () => {
74
+ const sdk = getSdkInfo();
75
+ // @ts-expect-error
76
+ setSdkInfo(undefined);
77
+ const client = createCoordinatorClient('apiKey', {});
78
+ expect(client).toBeDefined();
79
+ expect(client.getUserAgent()).toBe(
80
+ `stream-video-js-v0.0.0|client_bundle=node`,
81
+ );
82
+ setSdkInfo(sdk!);
83
+ });
58
84
  });
59
85
  });
@@ -42,18 +42,10 @@ export const setOSInfo = (info: OS) => {
42
42
  osInfo = info;
43
43
  };
44
44
 
45
- export const getOSInfo = () => {
46
- return osInfo;
47
- };
48
-
49
45
  export const setDeviceInfo = (info: Device) => {
50
46
  deviceInfo = info;
51
47
  };
52
48
 
53
- export const getDeviceInfo = () => {
54
- return deviceInfo;
55
- };
56
-
57
49
  export const getWebRTCInfo = () => {
58
50
  return webRtcInfo;
59
51
  };
@@ -62,10 +54,6 @@ export const setWebRTCInfo = (info: WebRTCInfoType) => {
62
54
  webRtcInfo = info;
63
55
  };
64
56
 
65
- export type LocalClientDetailsType = ClientDetails & {
66
- webRTCInfo?: WebRTCInfoType;
67
- };
68
-
69
57
  export const setThermalState = (state: string) => {
70
58
  if (!osInfo) {
71
59
  deviceState = { oneofKind: undefined };
@@ -142,27 +130,43 @@ export const getDeviceState = () => {
142
130
  return deviceState;
143
131
  };
144
132
 
145
- export const getClientDetails = (): LocalClientDetailsType => {
133
+ export const getClientDetails = async (): Promise<ClientDetails> => {
146
134
  if (isReactNative()) {
147
135
  // Since RN doesn't support web, sharing browser info is not required
148
136
  return {
149
- sdk: getSdkInfo(),
150
- os: getOSInfo(),
151
- device: getDeviceInfo(),
137
+ sdk: sdkInfo,
138
+ os: osInfo,
139
+ device: deviceInfo,
152
140
  };
153
141
  }
154
142
 
143
+ // @ts-expect-error - userAgentData is not yet in the TS types
144
+ const userAgentDataApi = navigator.userAgentData;
145
+ let userAgentData:
146
+ | { platform?: string; platformVersion?: string }
147
+ | undefined;
148
+ if (userAgentDataApi && userAgentDataApi.getHighEntropyValues) {
149
+ try {
150
+ userAgentData = await userAgentDataApi.getHighEntropyValues([
151
+ 'platform',
152
+ 'platformVersion',
153
+ ]);
154
+ } catch (e) {
155
+ // Ignore the error
156
+ }
157
+ }
158
+
155
159
  const userAgent = new UAParser(navigator.userAgent);
156
160
  const { browser, os, device, cpu } = userAgent.getResult();
157
161
  return {
158
- sdk: getSdkInfo(),
162
+ sdk: sdkInfo,
159
163
  browser: {
160
164
  name: browser.name || navigator.userAgent,
161
165
  version: browser.version || '',
162
166
  },
163
167
  os: {
164
- name: os.name || '',
165
- version: os.version || '',
168
+ name: userAgentData?.platform || os.name || '',
169
+ version: userAgentData?.platformVersion || os.version || '',
166
170
  architecture: cpu.architecture || '',
167
171
  },
168
172
  device: {
@@ -1,4 +1,5 @@
1
- import {
1
+ import type {
2
+ ClientAppIdentifier,
2
3
  StreamClientOptions,
3
4
  TokenOrProvider,
4
5
  User,
@@ -16,6 +17,23 @@ export const getInstanceKey = (apiKey: string, user: User) => {
16
17
  return `${apiKey}/${user.id}`;
17
18
  };
18
19
 
20
+ /**
21
+ * Utility function to get the client app identifier.
22
+ */
23
+ const getClientAppIdentifier = (
24
+ options?: StreamClientOptions,
25
+ ): ClientAppIdentifier => {
26
+ const appId = options?.clientAppIdentifier || {};
27
+ const sdkInfo = getSdkInfo();
28
+ if (sdkInfo) {
29
+ // ensure the sdk name and version are set correctly,
30
+ // overriding any user-provided values
31
+ appId.sdkName = SdkType[sdkInfo.type].toLowerCase();
32
+ appId.sdkVersion = `${sdkInfo.major}.${sdkInfo.minor}.${sdkInfo.patch}`;
33
+ }
34
+ return appId;
35
+ };
36
+
19
37
  /**
20
38
  * Creates a coordinator client.
21
39
  */
@@ -23,22 +41,14 @@ export const createCoordinatorClient = (
23
41
  apiKey: string,
24
42
  options: StreamClientOptions | undefined,
25
43
  ) => {
44
+ const clientAppIdentifier = getClientAppIdentifier(options);
26
45
  const coordinatorLogger = getLogger(['coordinator']);
27
- const streamClient = new StreamClient(apiKey, {
46
+ return new StreamClient(apiKey, {
28
47
  persistUserOnConnectionFailure: true,
29
48
  ...options,
49
+ clientAppIdentifier,
30
50
  logger: coordinatorLogger,
31
51
  });
32
- const sdkInfo = getSdkInfo();
33
- if (sdkInfo) {
34
- const sdkName = SdkType[sdkInfo.type].toLowerCase();
35
- const sdkVersion = `${sdkInfo.major}.${sdkInfo.minor}.${sdkInfo.patch}`;
36
- const userAgent = streamClient.getUserAgent();
37
- streamClient.setUserAgent(
38
- `${userAgent}-video-${sdkName}-sdk-${sdkVersion}`,
39
- );
40
- }
41
- return streamClient;
42
52
  };
43
53
 
44
54
  /**
@@ -206,6 +206,7 @@ export abstract class BasePeerConnection {
206
206
  const state = this.pc.iceConnectionState;
207
207
  this.logger('debug', `ICE connection state changed`, state);
208
208
 
209
+ if (this.state.callingState === CallingState.OFFLINE) return;
209
210
  if (this.state.callingState === CallingState.RECONNECTING) return;
210
211
 
211
212
  // do nothing when ICE is restarting
@@ -4,12 +4,9 @@ import { OwnCapability, StatsOptions } from '../gen/coordinator';
4
4
  import { getLogger } from '../logger';
5
5
  import { Publisher, Subscriber } from '../rtc';
6
6
  import { flatten, getSdkName, getSdkVersion } from './utils';
7
+ import { getDeviceState, getWebRTCInfo } from '../helpers/client-details';
7
8
  import {
8
- getDeviceState,
9
- getWebRTCInfo,
10
- LocalClientDetailsType,
11
- } from '../helpers/client-details';
12
- import {
9
+ ClientDetails,
13
10
  InputDevices,
14
11
  WebsocketReconnectStrategy,
15
12
  } from '../gen/video/sfu/models/models';
@@ -20,7 +17,7 @@ import { Telemetry } from '../gen/video/sfu/signal_rpc/signal';
20
17
 
21
18
  export type SfuStatsReporterOptions = {
22
19
  options: StatsOptions;
23
- clientDetails: LocalClientDetailsType;
20
+ clientDetails: ClientDetails;
24
21
  subscriber: Subscriber;
25
22
  publisher?: Publisher;
26
23
  microphone: MicrophoneManager;
@@ -1,5 +1,4 @@
1
- import { type LocalClientDetailsType } from '../helpers/client-details';
2
- import { Sdk, SdkType } from '../gen/video/sfu/models/models';
1
+ import { ClientDetails, Sdk, SdkType } from '../gen/video/sfu/models/models';
3
2
 
4
3
  /**
5
4
  * Flatten the stats report into an array of stats objects.
@@ -14,7 +13,7 @@ export const flatten = (report: RTCStatsReport) => {
14
13
  return stats;
15
14
  };
16
15
 
17
- export const getSdkSignature = (clientDetails: LocalClientDetailsType) => {
16
+ export const getSdkSignature = (clientDetails: ClientDetails) => {
18
17
  const { sdk, ...platform } = clientDetails;
19
18
  const sdkName = getSdkName(sdk);
20
19
  const sdkVersion = getSdkVersion(sdk);