stream-chat 4.4.3 → 4.5.0-beta.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.
Files changed (53) hide show
  1. package/README.md +4 -13
  2. package/dist/browser.es.js +1571 -1071
  3. package/dist/browser.es.js.map +1 -1
  4. package/dist/browser.full-bundle.min.js +1 -1
  5. package/dist/browser.full-bundle.min.js.map +1 -1
  6. package/dist/browser.js +1571 -1071
  7. package/dist/browser.js.map +1 -1
  8. package/dist/index.es.js +1571 -1071
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.js +1571 -1071
  11. package/dist/index.js.map +1 -1
  12. package/dist/types/base64.d.ts.map +1 -1
  13. package/dist/types/channel.d.ts +19 -15
  14. package/dist/types/channel.d.ts.map +1 -1
  15. package/dist/types/channel_state.d.ts +2 -2
  16. package/dist/types/channel_state.d.ts.map +1 -1
  17. package/dist/types/client.d.ts +18 -39
  18. package/dist/types/client.d.ts.map +1 -1
  19. package/dist/types/client_state.d.ts +2 -2
  20. package/dist/types/client_state.d.ts.map +1 -1
  21. package/dist/types/connection.d.ts +14 -49
  22. package/dist/types/connection.d.ts.map +1 -1
  23. package/dist/types/connection_fallback.d.ts +42 -0
  24. package/dist/types/connection_fallback.d.ts.map +1 -0
  25. package/dist/types/errors.d.ts +14 -0
  26. package/dist/types/errors.d.ts.map +1 -0
  27. package/dist/types/insights.d.ts +6 -6
  28. package/dist/types/insights.d.ts.map +1 -1
  29. package/dist/types/permissions.d.ts.map +1 -1
  30. package/dist/types/signing.d.ts +3 -3
  31. package/dist/types/signing.d.ts.map +1 -1
  32. package/dist/types/token_manager.d.ts +2 -2
  33. package/dist/types/token_manager.d.ts.map +1 -1
  34. package/dist/types/types.d.ts +94 -80
  35. package/dist/types/types.d.ts.map +1 -1
  36. package/dist/types/utils.d.ts +12 -2
  37. package/dist/types/utils.d.ts.map +1 -1
  38. package/package.json +3 -3
  39. package/src/base64.ts +1 -4
  40. package/src/channel.ts +133 -461
  41. package/src/channel_state.ts +31 -158
  42. package/src/client.ts +277 -674
  43. package/src/client_state.ts +2 -2
  44. package/src/connection.ts +143 -394
  45. package/src/connection_fallback.ts +205 -0
  46. package/src/errors.ts +58 -0
  47. package/src/insights.ts +15 -23
  48. package/src/permissions.ts +3 -24
  49. package/src/signing.ts +6 -17
  50. package/src/token_manager.ts +6 -18
  51. package/src/types.ts +268 -504
  52. package/src/utils.ts +39 -19
  53. package/CHANGELOG.md +0 -844
package/src/client.ts CHANGED
@@ -11,6 +11,8 @@ import { StableWSConnection } from './connection';
11
11
  import { isValidEventType } from './events';
12
12
  import { JWTUserToken, DevToken, CheckSignature } from './signing';
13
13
  import { TokenManager } from './token_manager';
14
+ import { WSConnectionFallback } from './connection_fallback';
15
+ import { isWSFailure } from './errors';
14
16
  import {
15
17
  isFunction,
16
18
  isOwnUserBaseProperty,
@@ -20,6 +22,7 @@ import {
20
22
  randomId,
21
23
  sleep,
22
24
  retryInterval,
25
+ isOnline,
23
26
  } from './utils';
24
27
 
25
28
  import {
@@ -45,7 +48,6 @@ import {
45
48
  CheckSQSResponse,
46
49
  Configs,
47
50
  ConnectAPIResponse,
48
- ConnectionChangeEvent,
49
51
  CreateChannelOptions,
50
52
  CreateChannelResponse,
51
53
  CreateCommandOptions,
@@ -95,7 +97,7 @@ import {
95
97
  TestSQSDataInput,
96
98
  TokenOrProvider,
97
99
  UnBanUserOptions,
98
- UnknownType,
100
+ UR,
99
101
  UpdateChannelOptions,
100
102
  UpdateChannelResponse,
101
103
  UpdateCommandOptions,
@@ -117,6 +119,7 @@ import {
117
119
  DeleteUserOptions,
118
120
  DeleteChannelsResponse,
119
121
  TaskResponse,
122
+ ReservedMessageFields,
120
123
  } from './types';
121
124
  import { InsightMetrics, postInsights } from './insights';
122
125
 
@@ -125,27 +128,19 @@ function isString(x: unknown): x is string {
125
128
  }
126
129
 
127
130
  export class StreamChat<
128
- AttachmentType extends UnknownType = UnknownType,
129
- ChannelType extends UnknownType = UnknownType,
131
+ AttachmentType extends UR = UR,
132
+ ChannelType extends UR = UR,
130
133
  CommandType extends string = LiteralStringForUnion,
131
- EventType extends UnknownType = UnknownType,
132
- MessageType extends UnknownType = UnknownType,
133
- ReactionType extends UnknownType = UnknownType,
134
- UserType extends UnknownType = UnknownType
134
+ EventType extends UR = UR,
135
+ MessageType extends UR = UR,
136
+ ReactionType extends UR = UR,
137
+ UserType extends UR = UR
135
138
  > {
136
139
  private static _instance?: unknown | StreamChat; // type is undefined|StreamChat, unknown is due to TS limitations with statics
137
140
 
138
141
  _user?: OwnUserResponse<ChannelType, CommandType, UserType> | UserResponse<UserType>;
139
142
  activeChannels: {
140
- [key: string]: Channel<
141
- AttachmentType,
142
- ChannelType,
143
- CommandType,
144
- EventType,
145
- MessageType,
146
- ReactionType,
147
- UserType
148
- >;
143
+ [key: string]: Channel<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
149
144
  };
150
145
  anonymous: boolean;
151
146
  axiosInstance: AxiosInstance;
@@ -154,22 +149,10 @@ export class StreamChat<
154
149
  cleaningIntervalRef?: NodeJS.Timeout;
155
150
  clientID?: string;
156
151
  configs: Configs<CommandType>;
157
- connectionID?: string;
158
- failures?: number;
159
152
  key: string;
160
153
  listeners: {
161
154
  [key: string]: Array<
162
- (
163
- event: Event<
164
- AttachmentType,
165
- ChannelType,
166
- CommandType,
167
- EventType,
168
- MessageType,
169
- ReactionType,
170
- UserType
171
- >,
172
- ) => void
155
+ (event: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>) => void
173
156
  >;
174
157
  };
175
158
  logger: Logger;
@@ -195,10 +178,29 @@ export class StreamChat<
195
178
  userAgent?: string;
196
179
  userID?: string;
197
180
  wsBaseURL?: string;
198
- wsConnection: StableWSConnection<ChannelType, CommandType, UserType> | null;
181
+ wsConnection: StableWSConnection<
182
+ ChannelType,
183
+ CommandType,
184
+ UserType,
185
+ AttachmentType,
186
+ EventType,
187
+ MessageType,
188
+ ReactionType
189
+ > | null;
190
+ wsFallback?: WSConnectionFallback<
191
+ AttachmentType,
192
+ ChannelType,
193
+ CommandType,
194
+ EventType,
195
+ MessageType,
196
+ ReactionType,
197
+ UserType
198
+ >;
199
199
  wsPromise: ConnectAPIResponse<ChannelType, CommandType, UserType> | null;
200
200
  consecutiveFailures: number;
201
201
  insightMetrics: InsightMetrics;
202
+ defaultWSTimeoutWithFallback: number;
203
+ defaultWSTimeout: number;
202
204
 
203
205
  /**
204
206
  * Initialize a client
@@ -221,11 +223,7 @@ export class StreamChat<
221
223
  */
222
224
  constructor(key: string, options?: StreamChatOptions);
223
225
  constructor(key: string, secret?: string, options?: StreamChatOptions);
224
- constructor(
225
- key: string,
226
- secretOrOptions?: StreamChatOptions | string,
227
- options?: StreamChatOptions,
228
- ) {
226
+ constructor(key: string, secretOrOptions?: StreamChatOptions | string, options?: StreamChatOptions) {
229
227
  // set the key
230
228
  this.key = key;
231
229
  this.listeners = {};
@@ -240,16 +238,9 @@ export class StreamChat<
240
238
  }
241
239
 
242
240
  // set the options... and figure out defaults...
243
- const inputOptions = options
244
- ? options
245
- : secretOrOptions && !isString(secretOrOptions)
246
- ? secretOrOptions
247
- : {};
248
-
249
- this.browser =
250
- typeof inputOptions.browser !== 'undefined'
251
- ? inputOptions.browser
252
- : typeof window !== 'undefined';
241
+ const inputOptions = options ? options : secretOrOptions && !isString(secretOrOptions) ? secretOrOptions : {};
242
+
243
+ this.browser = typeof inputOptions.browser !== 'undefined' ? inputOptions.browser : typeof window !== 'undefined';
253
244
  this.node = !this.browser;
254
245
 
255
246
  this.options = {
@@ -295,6 +286,9 @@ export class StreamChat<
295
286
  this.consecutiveFailures = 0;
296
287
  this.insightMetrics = new InsightMetrics();
297
288
 
289
+ this.defaultWSTimeoutWithFallback = 6000;
290
+ this.defaultWSTimeout = 15000;
291
+
298
292
  /**
299
293
  * logger function should accept 3 parameters:
300
294
  * @param logLevel string
@@ -370,67 +364,43 @@ export class StreamChat<
370
364
  * StreamChat.getInstance('api_key', "secret", { httpsAgent: customAgent })
371
365
  */
372
366
  public static getInstance<
373
- AttachmentType extends UnknownType = UnknownType,
374
- ChannelType extends UnknownType = UnknownType,
367
+ AttachmentType extends UR = UR,
368
+ ChannelType extends UR = UR,
375
369
  CommandType extends string = LiteralStringForUnion,
376
- EventType extends UnknownType = UnknownType,
377
- MessageType extends UnknownType = UnknownType,
378
- ReactionType extends UnknownType = UnknownType,
379
- UserType extends UnknownType = UnknownType
370
+ EventType extends UR = UR,
371
+ MessageType extends UR = UR,
372
+ ReactionType extends UR = UR,
373
+ UserType extends UR = UR
380
374
  >(
381
375
  key: string,
382
376
  options?: StreamChatOptions,
383
- ): StreamChat<
384
- AttachmentType,
385
- ChannelType,
386
- CommandType,
387
- EventType,
388
- MessageType,
389
- ReactionType,
390
- UserType
391
- >;
377
+ ): StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
392
378
  public static getInstance<
393
- AttachmentType extends UnknownType = UnknownType,
394
- ChannelType extends UnknownType = UnknownType,
379
+ AttachmentType extends UR = UR,
380
+ ChannelType extends UR = UR,
395
381
  CommandType extends string = LiteralStringForUnion,
396
- EventType extends UnknownType = UnknownType,
397
- MessageType extends UnknownType = UnknownType,
398
- ReactionType extends UnknownType = UnknownType,
399
- UserType extends UnknownType = UnknownType
382
+ EventType extends UR = UR,
383
+ MessageType extends UR = UR,
384
+ ReactionType extends UR = UR,
385
+ UserType extends UR = UR
400
386
  >(
401
387
  key: string,
402
388
  secret?: string,
403
389
  options?: StreamChatOptions,
404
- ): StreamChat<
405
- AttachmentType,
406
- ChannelType,
407
- CommandType,
408
- EventType,
409
- MessageType,
410
- ReactionType,
411
- UserType
412
- >;
390
+ ): StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
413
391
  public static getInstance<
414
- AttachmentType extends UnknownType = UnknownType,
415
- ChannelType extends UnknownType = UnknownType,
392
+ AttachmentType extends UR = UR,
393
+ ChannelType extends UR = UR,
416
394
  CommandType extends string = LiteralStringForUnion,
417
- EventType extends UnknownType = UnknownType,
418
- MessageType extends UnknownType = UnknownType,
419
- ReactionType extends UnknownType = UnknownType,
420
- UserType extends UnknownType = UnknownType
395
+ EventType extends UR = UR,
396
+ MessageType extends UR = UR,
397
+ ReactionType extends UR = UR,
398
+ UserType extends UR = UR
421
399
  >(
422
400
  key: string,
423
401
  secretOrOptions?: StreamChatOptions | string,
424
402
  options?: StreamChatOptions,
425
- ): StreamChat<
426
- AttachmentType,
427
- ChannelType,
428
- CommandType,
429
- EventType,
430
- MessageType,
431
- ReactionType,
432
- UserType
433
- > {
403
+ ): StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType> {
434
404
  if (!StreamChat._instance) {
435
405
  if (typeof secretOrOptions === 'string') {
436
406
  StreamChat._instance = new StreamChat<
@@ -479,7 +449,9 @@ export class StreamChat<
479
449
  this.wsBaseURL = this.baseURL.replace('http', 'ws').replace(':3030', ':8800');
480
450
  }
481
451
 
482
- _hasConnectionID = () => Boolean(this.wsConnection?.connectionID);
452
+ _getConnectionID = () => this.wsConnection?.connectionID || this.wsFallback?.connectionID;
453
+
454
+ _hasConnectionID = () => Boolean(this._getConnectionID());
483
455
 
484
456
  /**
485
457
  * connectUser - Set the current user and open a WebSocket connection
@@ -514,10 +486,7 @@ export class StreamChat<
514
486
  );
515
487
  }
516
488
 
517
- if (
518
- (this._isUsingServerAuth() || this.node) &&
519
- !this.options.allowServerSideConnect
520
- ) {
489
+ if ((this._isUsingServerAuth() || this.node) && !this.options.allowServerSideConnect) {
521
490
  console.warn(
522
491
  'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.',
523
492
  );
@@ -560,9 +529,7 @@ export class StreamChat<
560
529
  _setToken = (user: UserResponse<UserType>, userTokenOrProvider: TokenOrProvider) =>
561
530
  this.tokenManager.setTokenOrProvider(userTokenOrProvider, user);
562
531
 
563
- _setUser(
564
- user: OwnUserResponse<ChannelType, CommandType, UserType> | UserResponse<UserType>,
565
- ) {
532
+ _setUser(user: OwnUserResponse<ChannelType, CommandType, UserType> | UserResponse<UserType>) {
566
533
  /**
567
534
  * This one is used by the frontend. This is a copy of the current user object stored on backend.
568
535
  * It contains reserved properties and own user properties which are not present in `this._user`.
@@ -585,17 +552,14 @@ export class StreamChat<
585
552
  * @param timeout Max number of ms, to wait for close event of websocket, before forcefully assuming succesful disconnection.
586
553
  * https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
587
554
  */
588
- closeConnection = (timeout?: number) => {
555
+ closeConnection = async (timeout?: number) => {
589
556
  if (this.cleaningIntervalRef != null) {
590
557
  clearInterval(this.cleaningIntervalRef);
591
558
  this.cleaningIntervalRef = undefined;
592
559
  }
593
560
 
594
- if (!this.wsConnection) {
595
- return Promise.resolve();
596
- }
597
-
598
- return this.wsConnection.disconnect(timeout);
561
+ await Promise.all([this.wsConnection?.disconnect(timeout), this.wsFallback?.disconnect(timeout)]);
562
+ return Promise.resolve();
599
563
  };
600
564
 
601
565
  /**
@@ -603,19 +567,13 @@ export class StreamChat<
603
567
  */
604
568
  openConnection = async () => {
605
569
  if (!this.userID) {
606
- throw Error(
607
- 'User is not set on client, use client.connectUser or client.connectAnonymousUser instead',
608
- );
570
+ throw Error('User is not set on client, use client.connectUser or client.connectAnonymousUser instead');
609
571
  }
610
572
 
611
- if (this.wsConnection?.isHealthy && this._hasConnectionID()) {
612
- this.logger(
613
- 'info',
614
- 'client:openConnection() - openConnection called twice, healthy connection already exists',
615
- {
616
- tags: ['connection', 'client'],
617
- },
618
- );
573
+ if ((this.wsConnection?.isHealthy || this.wsFallback?.isHealthy()) && this._hasConnectionID()) {
574
+ this.logger('info', 'client:openConnection() - openConnection called twice, healthy connection already exists', {
575
+ tags: ['connection', 'client'],
576
+ });
619
577
 
620
578
  return Promise.resolve();
621
579
  }
@@ -661,9 +619,7 @@ export class StreamChat<
661
619
  */
662
620
  async updateAppSettings(options: AppSettings) {
663
621
  if (options.apn_config?.p12_cert) {
664
- options.apn_config.p12_cert = Buffer.from(options.apn_config.p12_cert).toString(
665
- 'base64',
666
- );
622
+ options.apn_config.p12_cert = Buffer.from(options.apn_config.p12_cert).toString('base64');
667
623
  }
668
624
  return await this.patch<APIResponse>(this.baseURL + '/app', options);
669
625
  }
@@ -674,9 +630,7 @@ export class StreamChat<
674
630
  }
675
631
 
676
632
  if (before === '') {
677
- throw new Error(
678
- "Don't pass blank string for since, use null instead if resetting the token revoke",
679
- );
633
+ throw new Error("Don't pass blank string for since, use null instead if resetting the token revoke");
680
634
  }
681
635
 
682
636
  return before;
@@ -748,12 +702,8 @@ export class StreamChat<
748
702
  ...(data.messageID ? { message_id: data.messageID } : {}),
749
703
  ...(data.apnTemplate ? { apn_template: data.apnTemplate } : {}),
750
704
  ...(data.firebaseTemplate ? { firebase_template: data.firebaseTemplate } : {}),
751
- ...(data.firebaseDataTemplate
752
- ? { firebase_data_template: data.firebaseDataTemplate }
753
- : {}),
754
- ...(data.huaweiDataTemplate
755
- ? { huawei_data_template: data.huaweiDataTemplate }
756
- : {}),
705
+ ...(data.firebaseDataTemplate ? { firebase_data_template: data.firebaseDataTemplate } : {}),
706
+ ...(data.huaweiDataTemplate ? { huawei_data_template: data.huaweiDataTemplate } : {}),
757
707
  ...(data.skipDevices ? { skip_devices: true } : {}),
758
708
  });
759
709
  }
@@ -801,7 +751,7 @@ export class StreamChat<
801
751
  // reset client state
802
752
  this.state = new ClientState();
803
753
  // reset token manager
804
- this.tokenManager.reset();
754
+ setTimeout(this.tokenManager.reset); // delay reseting to use token for disconnect calls
805
755
 
806
756
  // close the WS connection
807
757
  return closePromise;
@@ -819,10 +769,7 @@ export class StreamChat<
819
769
  * connectAnonymousUser - Set an anonymous user and open a WebSocket connection
820
770
  */
821
771
  connectAnonymousUser = () => {
822
- if (
823
- (this._isUsingServerAuth() || this.node) &&
824
- !this.options.allowServerSideConnect
825
- ) {
772
+ if ((this._isUsingServerAuth() || this.node) && !this.options.allowServerSideConnect) {
826
773
  console.warn(
827
774
  'Please do not use connectUser server side. connectUser impacts MAU and concurrent connection usage and thus your bill. If you have a valid use-case, add "allowServerSideConnect: true" to the client options to disable this warning.',
828
775
  );
@@ -857,9 +804,10 @@ export class StreamChat<
857
804
  let response: { access_token: string; user: UserResponse<UserType> } | undefined;
858
805
  this.anonymous = true;
859
806
  try {
860
- response = await this.post<
861
- APIResponse & { access_token: string; user: UserResponse<UserType> }
862
- >(this.baseURL + '/guest', { user });
807
+ response = await this.post<APIResponse & { access_token: string; user: UserResponse<UserType> }>(
808
+ this.baseURL + '/guest',
809
+ { user },
810
+ );
863
811
  } catch (e) {
864
812
  this.anonymous = false;
865
813
  throw e;
@@ -867,10 +815,7 @@ export class StreamChat<
867
815
  this.anonymous = false;
868
816
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
869
817
  const { created_at, updated_at, last_active, online, ...guestUser } = response.user;
870
- return await this.connectUser(
871
- guestUser as UserResponse<UserType>,
872
- response.access_token,
873
- );
818
+ return await this.connectUser(guestUser as UserResponse<UserType>, response.access_token);
874
819
  }
875
820
 
876
821
  /**
@@ -912,39 +857,15 @@ export class StreamChat<
912
857
  * @return {{ unsubscribe: () => void }} Description
913
858
  */
914
859
  on(
915
- callback: EventHandler<
916
- AttachmentType,
917
- ChannelType,
918
- CommandType,
919
- EventType,
920
- MessageType,
921
- ReactionType,
922
- UserType
923
- >,
860
+ callback: EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
924
861
  ): { unsubscribe: () => void };
925
862
  on(
926
863
  eventType: string,
927
- callback: EventHandler<
928
- AttachmentType,
929
- ChannelType,
930
- CommandType,
931
- EventType,
932
- MessageType,
933
- ReactionType,
934
- UserType
935
- >,
864
+ callback: EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
936
865
  ): { unsubscribe: () => void };
937
866
  on(
938
867
  callbackOrString:
939
- | EventHandler<
940
- AttachmentType,
941
- ChannelType,
942
- CommandType,
943
- EventType,
944
- MessageType,
945
- ReactionType,
946
- UserType
947
- >
868
+ | EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>
948
869
  | string,
949
870
  callbackOrNothing?: EventHandler<
950
871
  AttachmentType,
@@ -995,39 +916,15 @@ export class StreamChat<
995
916
  *
996
917
  */
997
918
  off(
998
- callback: EventHandler<
999
- AttachmentType,
1000
- ChannelType,
1001
- CommandType,
1002
- EventType,
1003
- MessageType,
1004
- ReactionType,
1005
- UserType
1006
- >,
919
+ callback: EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
1007
920
  ): void;
1008
921
  off(
1009
922
  eventType: string,
1010
- callback: EventHandler<
1011
- AttachmentType,
1012
- ChannelType,
1013
- CommandType,
1014
- EventType,
1015
- MessageType,
1016
- ReactionType,
1017
- UserType
1018
- >,
923
+ callback: EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
1019
924
  ): void;
1020
925
  off(
1021
926
  callbackOrString:
1022
- | EventHandler<
1023
- AttachmentType,
1024
- ChannelType,
1025
- CommandType,
1026
- EventType,
1027
- MessageType,
1028
- ReactionType,
1029
- UserType
1030
- >
927
+ | EventHandler<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>
1031
928
  | string,
1032
929
  callbackOrNothing?: EventHandler<
1033
930
  AttachmentType,
@@ -1082,15 +979,11 @@ export class StreamChat<
1082
979
  }
1083
980
 
1084
981
  _logApiResponse<T>(type: string, url: string, response: AxiosResponse<T>) {
1085
- this.logger(
1086
- 'info',
1087
- `client:${type} - Response - url: ${url} > status ${response.status}`,
1088
- {
1089
- tags: ['api', 'api_response', 'client'],
1090
- url,
1091
- response,
1092
- },
1093
- );
982
+ this.logger('info', `client:${type} - Response - url: ${url} > status ${response.status}`, {
983
+ tags: ['api', 'api_response', 'client'],
984
+ url,
985
+ response,
986
+ });
1094
987
  }
1095
988
 
1096
989
  _logApiError(type: string, url: string, error: unknown) {
@@ -1139,15 +1032,14 @@ export class StreamChat<
1139
1032
  this._logApiResponse<T>(type, url, response);
1140
1033
  this.consecutiveFailures = 0;
1141
1034
  return this.handleResponse(response);
1142
- } catch (e) {
1035
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1036
+ } catch (e: any /**TODO: generalize error types */) {
1143
1037
  e.client_request_id = requestConfig.headers?.['x-client-request-id'];
1144
1038
  this._logApiError(type, url, e);
1145
1039
  this.consecutiveFailures += 1;
1146
1040
  if (e.response) {
1147
- if (
1148
- e.response.data.code === chatCodes.TOKEN_EXPIRED &&
1149
- !this.tokenManager.isStatic()
1150
- ) {
1041
+ /** connection_fallback depends on this token expiration logic */
1042
+ if (e.response.data.code === chatCodes.TOKEN_EXPIRED && !this.tokenManager.isStatic()) {
1151
1043
  if (this.consecutiveFailures > 1) {
1152
1044
  await sleep(retryInterval(this.consecutiveFailures));
1153
1045
  }
@@ -1205,9 +1097,7 @@ export class StreamChat<
1205
1097
  let err: Error & { code?: number; response?: AxiosResponse<T>; status?: number };
1206
1098
  err = new Error(`StreamChat error HTTP code: ${response.status}`);
1207
1099
  if (response.data && response.data.code) {
1208
- err = new Error(
1209
- `StreamChat error code ${response.data.code}: ${response.data.message}`,
1210
- );
1100
+ err = new Error(`StreamChat error code ${response.data.code}: ${response.data.message}`);
1211
1101
  err.code = response.data.code;
1212
1102
  }
1213
1103
  err.response = response;
@@ -1224,16 +1114,10 @@ export class StreamChat<
1224
1114
  }
1225
1115
 
1226
1116
  dispatchEvent = (
1227
- event: Event<
1228
- AttachmentType,
1229
- ChannelType,
1230
- CommandType,
1231
- EventType,
1232
- MessageType,
1233
- ReactionType,
1234
- UserType
1235
- >,
1117
+ event: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
1236
1118
  ) => {
1119
+ if (!event.received_at) event.received_at = new Date();
1120
+
1237
1121
  // client event handlers
1238
1122
  const postListenerCallbacks = this._handleClientEvent(event);
1239
1123
 
@@ -1265,7 +1149,6 @@ export class StreamChat<
1265
1149
  ReactionType,
1266
1150
  UserType
1267
1151
  >;
1268
- event.received_at = new Date();
1269
1152
  this.dispatchEvent(event);
1270
1153
  };
1271
1154
 
@@ -1350,15 +1233,7 @@ export class StreamChat<
1350
1233
  * @param {Event} event
1351
1234
  */
1352
1235
  _handleUserEvent = (
1353
- event: Event<
1354
- AttachmentType,
1355
- ChannelType,
1356
- CommandType,
1357
- EventType,
1358
- MessageType,
1359
- ReactionType,
1360
- UserType
1361
- >,
1236
+ event: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
1362
1237
  ) => {
1363
1238
  if (!event.user) {
1364
1239
  return;
@@ -1400,42 +1275,22 @@ export class StreamChat<
1400
1275
  this._updateUserMessageReferences(event.user);
1401
1276
  }
1402
1277
 
1403
- if (
1404
- event.type === 'user.deleted' &&
1405
- event.user.deleted_at &&
1406
- (event.mark_messages_deleted || event.hard_delete)
1407
- ) {
1278
+ if (event.type === 'user.deleted' && event.user.deleted_at && (event.mark_messages_deleted || event.hard_delete)) {
1408
1279
  this._deleteUserMessageReference(event.user, event.hard_delete);
1409
1280
  }
1410
1281
  };
1411
1282
 
1412
1283
  _handleClientEvent(
1413
- event: Event<
1414
- AttachmentType,
1415
- ChannelType,
1416
- CommandType,
1417
- EventType,
1418
- MessageType,
1419
- ReactionType,
1420
- UserType
1421
- >,
1284
+ event: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
1422
1285
  ) {
1423
1286
  const client = this;
1424
1287
  const postListenerCallbacks = [];
1425
- this.logger(
1426
- 'info',
1427
- `client:_handleClientEvent - Received event of type { ${event.type} }`,
1428
- {
1429
- tags: ['event', 'client'],
1430
- event,
1431
- },
1432
- );
1288
+ this.logger('info', `client:_handleClientEvent - Received event of type { ${event.type} }`, {
1289
+ tags: ['event', 'client'],
1290
+ event,
1291
+ });
1433
1292
 
1434
- if (
1435
- event.type === 'user.presence.changed' ||
1436
- event.type === 'user.updated' ||
1437
- event.type === 'user.deleted'
1438
- ) {
1293
+ if (event.type === 'user.presence.changed' || event.type === 'user.updated' || event.type === 'user.deleted') {
1439
1294
  this._handleUserEvent(event);
1440
1295
  }
1441
1296
 
@@ -1454,12 +1309,8 @@ export class StreamChat<
1454
1309
  const currentMutedChannelIds: string[] = [];
1455
1310
  const nextMutedChannelIds: string[] = [];
1456
1311
 
1457
- this.mutedChannels.forEach(
1458
- (mute) => mute.channel && currentMutedChannelIds.push(mute.channel.cid),
1459
- );
1460
- event.me.channel_mutes.forEach(
1461
- (mute) => mute.channel && nextMutedChannelIds.push(mute.channel.cid),
1462
- );
1312
+ this.mutedChannels.forEach((mute) => mute.channel && currentMutedChannelIds.push(mute.channel.cid));
1313
+ event.me.channel_mutes.forEach((mute) => mute.channel && nextMutedChannelIds.push(mute.channel.cid));
1463
1314
 
1464
1315
  /** Set the unread count of un-muted channels to 0, which is the behaviour of backend */
1465
1316
  currentMutedChannelIds.forEach((cid) => {
@@ -1475,11 +1326,7 @@ export class StreamChat<
1475
1326
  this.mutedUsers = event.me.mutes;
1476
1327
  }
1477
1328
 
1478
- if (
1479
- (event.type === 'channel.deleted' ||
1480
- event.type === 'notification.channel_deleted') &&
1481
- event.cid
1482
- ) {
1329
+ if ((event.type === 'channel.deleted' || event.type === 'notification.channel_deleted') && event.cid) {
1483
1330
  client.state.deleteAllChannelReference(event.cid);
1484
1331
  this.activeChannels[event.cid]?._disconnect();
1485
1332
 
@@ -1499,9 +1346,7 @@ export class StreamChat<
1499
1346
  const mute = this.mutedChannels[i];
1500
1347
  if (mute.channel?.cid === cid) {
1501
1348
  muteStatus = {
1502
- muted: mute.expires
1503
- ? new Date(mute.expires).getTime() > new Date().getTime()
1504
- : true,
1349
+ muted: mute.expires ? new Date(mute.expires).getTime() > new Date().getTime() : true,
1505
1350
  createdAt: mute.created_at ? new Date(mute.created_at) : new Date(),
1506
1351
  expiresAt: mute.expires ? new Date(mute.expires) : null,
1507
1352
  };
@@ -1521,30 +1366,12 @@ export class StreamChat<
1521
1366
  }
1522
1367
 
1523
1368
  _callClientListeners = (
1524
- event: Event<
1525
- AttachmentType,
1526
- ChannelType,
1527
- CommandType,
1528
- EventType,
1529
- MessageType,
1530
- ReactionType,
1531
- UserType
1532
- >,
1369
+ event: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>,
1533
1370
  ) => {
1534
1371
  const client = this;
1535
1372
  // gather and call the listeners
1536
1373
  const listeners: Array<
1537
- (
1538
- event: Event<
1539
- AttachmentType,
1540
- ChannelType,
1541
- CommandType,
1542
- EventType,
1543
- MessageType,
1544
- ReactionType,
1545
- UserType
1546
- >,
1547
- ) => void
1374
+ (event: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>) => void
1548
1375
  > = [];
1549
1376
  if (client.listeners.all) {
1550
1377
  listeners.push(...client.listeners.all);
@@ -1560,21 +1387,15 @@ export class StreamChat<
1560
1387
  };
1561
1388
 
1562
1389
  recoverState = async () => {
1563
- this.logger(
1564
- 'info',
1565
- `client:recoverState() - Start of recoverState with connectionID ${this.wsConnection?.connectionID}`,
1566
- {
1567
- tags: ['connection'],
1568
- },
1569
- );
1390
+ this.logger('info', `client:recoverState() - Start of recoverState with connectionID ${this._getConnectionID()}`, {
1391
+ tags: ['connection'],
1392
+ });
1570
1393
 
1571
1394
  const cids = Object.keys(this.activeChannels);
1572
1395
  if (cids.length && this.recoverStateOnReconnect) {
1573
- this.logger(
1574
- 'info',
1575
- `client:recoverState() - Start the querying of ${cids.length} channels`,
1576
- { tags: ['connection', 'client'] },
1577
- );
1396
+ this.logger('info', `client:recoverState() - Start the querying of ${cids.length} channels`, {
1397
+ tags: ['connection', 'client'],
1398
+ });
1578
1399
 
1579
1400
  await this.queryChannels(
1580
1401
  { cid: { $in: cids } } as ChannelFilters<ChannelType, CommandType, UserType>,
@@ -1603,20 +1424,13 @@ export class StreamChat<
1603
1424
  * @private
1604
1425
  */
1605
1426
  async connect() {
1606
- const client = this;
1607
- this.failures = 0;
1608
-
1609
- if (client.userID == null || this._user == null) {
1610
- throw Error(
1611
- 'Call connectUser or connectAnonymousUser before starting the connection',
1612
- );
1427
+ if (!this.userID || !this._user) {
1428
+ throw Error('Call connectUser or connectAnonymousUser before starting the connection');
1613
1429
  }
1614
-
1615
- if (client.wsBaseURL == null) {
1430
+ if (!this.wsBaseURL) {
1616
1431
  throw Error('Websocket base url not set');
1617
1432
  }
1618
-
1619
- if (client.clientID == null) {
1433
+ if (!this.clientID) {
1620
1434
  throw Error('clientID is not set');
1621
1435
  }
1622
1436
 
@@ -1625,25 +1439,43 @@ export class StreamChat<
1625
1439
  }
1626
1440
 
1627
1441
  // The StableWSConnection handles all the reconnection logic.
1628
- this.wsConnection = new StableWSConnection<ChannelType, CommandType, UserType>({
1629
- wsBaseURL: client.wsBaseURL,
1630
- enableInsights: this.options.enableInsights,
1631
- clientID: client.clientID,
1632
- userID: client.userID,
1633
- tokenManager: client.tokenManager,
1634
- user: this._user,
1635
- authType: this.getAuthType(),
1636
- userAgent: this.getUserAgent(),
1637
- apiKey: this.key,
1638
- recoverCallback: this.recoverState,
1639
- messageCallback: this.handleEvent,
1640
- eventCallback: this.dispatchEvent as (event: ConnectionChangeEvent) => void,
1641
- logger: this.logger,
1642
- device: this.options.device,
1643
- insightMetrics: this.insightMetrics,
1644
- });
1442
+ this.wsConnection = new StableWSConnection<
1443
+ ChannelType,
1444
+ CommandType,
1445
+ UserType,
1446
+ AttachmentType,
1447
+ EventType,
1448
+ MessageType,
1449
+ ReactionType
1450
+ >({ client: this });
1645
1451
 
1646
- return await this.wsConnection.connect();
1452
+ try {
1453
+ // if WSFallback is enabled, ws connect should timeout faster so fallback can try
1454
+ return await this.wsConnection.connect(
1455
+ this.options.enableWSFallback ? this.defaultWSTimeoutWithFallback : this.defaultWSTimeout,
1456
+ );
1457
+ } catch (err) {
1458
+ // run fallback only if it's WS/Network error and not a normal API error
1459
+ // make sure browser is online before even trying the longpoll
1460
+ if (this.options.enableWSFallback && isWSFailure(err) && isOnline()) {
1461
+ this.logger('info', 'client:connect() - WS failed, fallback to longpoll', { tags: ['connection', 'client'] });
1462
+
1463
+ this.wsConnection._destroyCurrentWSConnection();
1464
+ this.wsConnection.disconnect().then(); // close WS so no retry
1465
+ this.wsFallback = new WSConnectionFallback<
1466
+ AttachmentType,
1467
+ ChannelType,
1468
+ CommandType,
1469
+ EventType,
1470
+ MessageType,
1471
+ ReactionType,
1472
+ UserType
1473
+ >({ client: this });
1474
+ return await this.wsFallback.connect();
1475
+ }
1476
+
1477
+ throw err;
1478
+ }
1647
1479
  }
1648
1480
 
1649
1481
  /**
@@ -1675,11 +1507,7 @@ export class StreamChat<
1675
1507
  *
1676
1508
  * @return {Promise<APIResponse & { users: Array<UserResponse<UserType>> }>} User Query Response
1677
1509
  */
1678
- async queryUsers(
1679
- filterConditions: UserFilters<UserType>,
1680
- sort: UserSort<UserType> = [],
1681
- options: UserOptions = {},
1682
- ) {
1510
+ async queryUsers(filterConditions: UserFilters<UserType>, sort: UserSort<UserType> = [], options: UserOptions = {}) {
1683
1511
  const defaultOptions = {
1684
1512
  presence: false,
1685
1513
  };
@@ -1745,10 +1573,7 @@ export class StreamChat<
1745
1573
  *
1746
1574
  * @return {Promise<MessageFlagsResponse<ChannelType, CommandType, UserType>>} Message Flags Response
1747
1575
  */
1748
- async queryMessageFlags(
1749
- filterConditions: MessageFlagsFilters = {},
1750
- options: MessageFlagsPaginationOptions = {},
1751
- ) {
1576
+ async queryMessageFlags(filterConditions: MessageFlagsFilters = {}, options: MessageFlagsPaginationOptions = {}) {
1752
1577
  // Return a list of message flags
1753
1578
  return await this.get<MessageFlagsResponse<ChannelType, CommandType, UserType>>(
1754
1579
  this.baseURL + '/moderation/flags/message',
@@ -1802,14 +1627,7 @@ export class StreamChat<
1802
1627
  };
1803
1628
 
1804
1629
  const data = await this.post<{
1805
- channels: ChannelAPIResponse<
1806
- AttachmentType,
1807
- ChannelType,
1808
- CommandType,
1809
- MessageType,
1810
- ReactionType,
1811
- UserType
1812
- >[];
1630
+ channels: ChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>[];
1813
1631
  }>(this.baseURL + '/channels', payload);
1814
1632
 
1815
1633
  const channels: Channel<
@@ -1855,34 +1673,16 @@ export class StreamChat<
1855
1673
  */
1856
1674
  async search(
1857
1675
  filterConditions: ChannelFilters<ChannelType, CommandType, UserType>,
1858
- query:
1859
- | string
1860
- | MessageFilters<
1861
- AttachmentType,
1862
- ChannelType,
1863
- CommandType,
1864
- MessageType,
1865
- ReactionType,
1866
- UserType
1867
- >,
1676
+ query: string | MessageFilters<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>,
1868
1677
  options: SearchOptions<MessageType> = {},
1869
1678
  ) {
1870
1679
  if (options.offset && (options.sort || options.next)) {
1871
1680
  throw Error(`Cannot specify offset with sort or next parameters`);
1872
1681
  }
1873
- const payload: SearchPayload<
1874
- AttachmentType,
1875
- ChannelType,
1876
- CommandType,
1877
- MessageType,
1878
- ReactionType,
1879
- UserType
1880
- > = {
1682
+ const payload: SearchPayload<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType> = {
1881
1683
  filter_conditions: filterConditions,
1882
1684
  ...options,
1883
- sort: options.sort
1884
- ? normalizeQuerySort<SearchMessageSortBase<MessageType>>(options.sort)
1885
- : undefined,
1685
+ sort: options.sort ? normalizeQuerySort<SearchMessageSortBase<MessageType>>(options.sort) : undefined,
1886
1686
  };
1887
1687
  if (typeof query === 'string') {
1888
1688
  payload.query = query;
@@ -1896,14 +1696,7 @@ export class StreamChat<
1896
1696
  await this.setUserPromise;
1897
1697
 
1898
1698
  return await this.get<
1899
- SearchAPIResponse<
1900
- AttachmentType,
1901
- ChannelType,
1902
- CommandType,
1903
- MessageType,
1904
- ReactionType,
1905
- UserType
1906
- >
1699
+ SearchAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>
1907
1700
  >(this.baseURL + '/search', {
1908
1701
  payload,
1909
1702
  });
@@ -1918,7 +1711,7 @@ export class StreamChat<
1918
1711
  *
1919
1712
  */
1920
1713
  setLocalDevice(device: BaseDeviceFields) {
1921
- if (this.wsConnection) {
1714
+ if (this.wsConnection || this.wsFallback) {
1922
1715
  throw new Error('you can only set device before opening a websocket connection');
1923
1716
  }
1924
1717
 
@@ -1994,14 +1787,7 @@ export class StreamChat<
1994
1787
  }
1995
1788
 
1996
1789
  _addChannelConfig(
1997
- channelState: ChannelAPIResponse<
1998
- AttachmentType,
1999
- ChannelType,
2000
- CommandType,
2001
- MessageType,
2002
- ReactionType,
2003
- UserType
2004
- >,
1790
+ channelState: ChannelAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>,
2005
1791
  ) {
2006
1792
  this.configs[channelState.channel.type] = channelState.channel.config;
2007
1793
  }
@@ -2024,27 +1810,11 @@ export class StreamChat<
2024
1810
  channelType: string,
2025
1811
  channelID?: string | null,
2026
1812
  custom?: ChannelData<ChannelType>,
2027
- ): Channel<
2028
- AttachmentType,
2029
- ChannelType,
2030
- CommandType,
2031
- EventType,
2032
- MessageType,
2033
- ReactionType,
2034
- UserType
2035
- >;
1813
+ ): Channel<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
2036
1814
  channel(
2037
1815
  channelType: string,
2038
1816
  custom?: ChannelData<ChannelType>,
2039
- ): Channel<
2040
- AttachmentType,
2041
- ChannelType,
2042
- CommandType,
2043
- EventType,
2044
- MessageType,
2045
- ReactionType,
2046
- UserType
2047
- >;
1817
+ ): Channel<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
2048
1818
  channel(
2049
1819
  channelType: string,
2050
1820
  channelIDOrCustom?: string | ChannelData<ChannelType> | null,
@@ -2062,15 +1832,12 @@ export class StreamChat<
2062
1832
  // support channel("messaging", undefined, {options})
2063
1833
  // support channel("messaging", "", {options})
2064
1834
  if (channelIDOrCustom == null || channelIDOrCustom === '') {
2065
- return new Channel<
2066
- AttachmentType,
2067
- ChannelType,
2068
- CommandType,
2069
- EventType,
2070
- MessageType,
2071
- ReactionType,
2072
- UserType
2073
- >(this, channelType, undefined, custom);
1835
+ return new Channel<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>(
1836
+ this,
1837
+ channelType,
1838
+ undefined,
1839
+ custom,
1840
+ );
2074
1841
  }
2075
1842
 
2076
1843
  // support channel("messaging", {options})
@@ -2123,9 +1890,7 @@ export class StreamChat<
2123
1890
  }
2124
1891
 
2125
1892
  if (key.indexOf(`${channelType}:!members-`) === 0) {
2126
- const membersStrInExistingChannel = Object.keys(channel.state.members)
2127
- .sort()
2128
- .join(',');
1893
+ const membersStrInExistingChannel = Object.keys(channel.state.members).sort().join(',');
2129
1894
  if (membersStrInExistingChannel === membersStr) {
2130
1895
  return channel;
2131
1896
  }
@@ -2164,11 +1929,7 @@ export class StreamChat<
2164
1929
  *
2165
1930
  * @return {channel} The channel object, initialize it using channel.watch()
2166
1931
  */
2167
- getChannelById = (
2168
- channelType: string,
2169
- channelID: string,
2170
- custom: ChannelData<ChannelType>,
2171
- ) => {
1932
+ getChannelById = (channelType: string, channelID: string, custom: ChannelData<ChannelType>) => {
2172
1933
  if (typeof channelID === 'string' && ~channelID.indexOf(':')) {
2173
1934
  throw Error(`Invalid channel id ${channelID}, can't contain the : character`);
2174
1935
  }
@@ -2316,10 +2077,7 @@ export class StreamChat<
2316
2077
  });
2317
2078
  }
2318
2079
 
2319
- async deactivateUser(
2320
- userID: string,
2321
- options?: { created_by_id?: string; mark_messages_deleted?: boolean },
2322
- ) {
2080
+ async deactivateUser(userID: string, options?: { created_by_id?: string; mark_messages_deleted?: boolean }) {
2323
2081
  return await this.post<APIResponse & { user: UserResponse<UserType> }>(
2324
2082
  this.baseURL + `/users/${userID}/deactivate`,
2325
2083
  {
@@ -2331,14 +2089,7 @@ export class StreamChat<
2331
2089
  async exportUser(userID: string, options?: Record<string, string>) {
2332
2090
  return await this.get<
2333
2091
  APIResponse & {
2334
- messages: MessageResponse<
2335
- AttachmentType,
2336
- ChannelType,
2337
- CommandType,
2338
- MessageType,
2339
- ReactionType,
2340
- UserType
2341
- >[];
2092
+ messages: MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>[];
2342
2093
  reactions: ReactionResponse<ReactionType, UserType>[];
2343
2094
  user: UserResponse<UserType>;
2344
2095
  }
@@ -2357,16 +2108,12 @@ export class StreamChat<
2357
2108
  if (options?.user_id !== undefined) {
2358
2109
  options.banned_by_id = options.user_id;
2359
2110
  delete options.user_id;
2360
- console.warn(
2361
- "banUser: 'user_id' is deprecated, please consider switching to 'banned_by_id'",
2362
- );
2111
+ console.warn("banUser: 'user_id' is deprecated, please consider switching to 'banned_by_id'");
2363
2112
  }
2364
2113
  if (options?.user !== undefined) {
2365
2114
  options.banned_by = options.user;
2366
2115
  delete options.user;
2367
- console.warn(
2368
- "banUser: 'user' is deprecated, please consider switching to 'banned_by'",
2369
- );
2116
+ console.warn("banUser: 'user' is deprecated, please consider switching to 'banned_by'");
2370
2117
  }
2371
2118
  return await this.post<APIResponse>(this.baseURL + '/moderation/ban', {
2372
2119
  target_user_id: targetUserID,
@@ -2420,19 +2167,12 @@ export class StreamChat<
2420
2167
  * @param {MuteUserOptions<UserType>} [options]
2421
2168
  * @returns {Promise<MuteUserResponse<ChannelType, CommandType, UserType>>}
2422
2169
  */
2423
- async muteUser(
2424
- targetID: string,
2425
- userID?: string,
2426
- options: MuteUserOptions<UserType> = {},
2427
- ) {
2428
- return await this.post<MuteUserResponse<ChannelType, CommandType, UserType>>(
2429
- this.baseURL + '/moderation/mute',
2430
- {
2431
- target_id: targetID,
2432
- ...(userID ? { user_id: userID } : {}),
2433
- ...options,
2434
- },
2435
- );
2170
+ async muteUser(targetID: string, userID?: string, options: MuteUserOptions<UserType> = {}) {
2171
+ return await this.post<MuteUserResponse<ChannelType, CommandType, UserType>>(this.baseURL + '/moderation/mute', {
2172
+ target_id: targetID,
2173
+ ...(userID ? { user_id: userID } : {}),
2174
+ ...options,
2175
+ });
2436
2176
  }
2437
2177
 
2438
2178
  /** unmuteUser - unmutes a user
@@ -2471,13 +2211,10 @@ export class StreamChat<
2471
2211
  * @returns {Promise<APIResponse>}
2472
2212
  */
2473
2213
  async flagMessage(targetMessageID: string, options: { user_id?: string } = {}) {
2474
- return await this.post<FlagMessageResponse<UserType>>(
2475
- this.baseURL + '/moderation/flag',
2476
- {
2477
- target_message_id: targetMessageID,
2478
- ...options,
2479
- },
2480
- );
2214
+ return await this.post<FlagMessageResponse<UserType>>(this.baseURL + '/moderation/flag', {
2215
+ target_message_id: targetMessageID,
2216
+ ...options,
2217
+ });
2481
2218
  }
2482
2219
 
2483
2220
  /**
@@ -2487,13 +2224,10 @@ export class StreamChat<
2487
2224
  * @returns {Promise<APIResponse>}
2488
2225
  */
2489
2226
  async flagUser(targetID: string, options: { user_id?: string } = {}) {
2490
- return await this.post<FlagUserResponse<UserType>>(
2491
- this.baseURL + '/moderation/flag',
2492
- {
2493
- target_user_id: targetID,
2494
- ...options,
2495
- },
2496
- );
2227
+ return await this.post<FlagUserResponse<UserType>>(this.baseURL + '/moderation/flag', {
2228
+ target_user_id: targetID,
2229
+ ...options,
2230
+ });
2497
2231
  }
2498
2232
 
2499
2233
  /**
@@ -2503,13 +2237,10 @@ export class StreamChat<
2503
2237
  * @returns {Promise<APIResponse>}
2504
2238
  */
2505
2239
  async unflagMessage(targetMessageID: string, options: { user_id?: string } = {}) {
2506
- return await this.post<FlagMessageResponse<UserType>>(
2507
- this.baseURL + '/moderation/unflag',
2508
- {
2509
- target_message_id: targetMessageID,
2510
- ...options,
2511
- },
2512
- );
2240
+ return await this.post<FlagMessageResponse<UserType>>(this.baseURL + '/moderation/unflag', {
2241
+ target_message_id: targetMessageID,
2242
+ ...options,
2243
+ });
2513
2244
  }
2514
2245
 
2515
2246
  /**
@@ -2519,13 +2250,10 @@ export class StreamChat<
2519
2250
  * @returns {Promise<APIResponse>}
2520
2251
  */
2521
2252
  async unflagUser(targetID: string, options: { user_id?: string } = {}) {
2522
- return await this.post<FlagUserResponse<UserType>>(
2523
- this.baseURL + '/moderation/unflag',
2524
- {
2525
- target_user_id: targetID,
2526
- ...options,
2527
- },
2528
- );
2253
+ return await this.post<FlagUserResponse<UserType>>(this.baseURL + '/moderation/unflag', {
2254
+ target_user_id: targetID,
2255
+ ...options,
2256
+ });
2529
2257
  }
2530
2258
 
2531
2259
  /**
@@ -2553,10 +2281,7 @@ export class StreamChat<
2553
2281
  }
2554
2282
 
2555
2283
  createCommand(data: CreateCommandOptions<CommandType>) {
2556
- return this.post<CreateCommandResponse<CommandType>>(
2557
- this.baseURL + '/commands',
2558
- data,
2559
- );
2284
+ return this.post<CreateCommandResponse<CommandType>>(this.baseURL + '/commands', data);
2560
2285
  }
2561
2286
 
2562
2287
  getCommand(name: string) {
@@ -2564,16 +2289,11 @@ export class StreamChat<
2564
2289
  }
2565
2290
 
2566
2291
  updateCommand(name: string, data: UpdateCommandOptions<CommandType>) {
2567
- return this.put<UpdateCommandResponse<CommandType>>(
2568
- this.baseURL + `/commands/${name}`,
2569
- data,
2570
- );
2292
+ return this.put<UpdateCommandResponse<CommandType>>(this.baseURL + `/commands/${name}`, data);
2571
2293
  }
2572
2294
 
2573
2295
  deleteCommand(name: string) {
2574
- return this.delete<DeleteCommandResponse<CommandType>>(
2575
- this.baseURL + `/commands/${name}`,
2576
- );
2296
+ return this.delete<DeleteCommandResponse<CommandType>>(this.baseURL + `/commands/${name}`);
2577
2297
  }
2578
2298
 
2579
2299
  listCommands() {
@@ -2582,23 +2302,15 @@ export class StreamChat<
2582
2302
 
2583
2303
  createChannelType(data: CreateChannelOptions<CommandType>) {
2584
2304
  const channelData = Object.assign({}, { commands: ['all'] }, data);
2585
- return this.post<CreateChannelResponse<CommandType>>(
2586
- this.baseURL + '/channeltypes',
2587
- channelData,
2588
- );
2305
+ return this.post<CreateChannelResponse<CommandType>>(this.baseURL + '/channeltypes', channelData);
2589
2306
  }
2590
2307
 
2591
2308
  getChannelType(channelType: string) {
2592
- return this.get<GetChannelTypeResponse<CommandType>>(
2593
- this.baseURL + `/channeltypes/${channelType}`,
2594
- );
2309
+ return this.get<GetChannelTypeResponse<CommandType>>(this.baseURL + `/channeltypes/${channelType}`);
2595
2310
  }
2596
2311
 
2597
2312
  updateChannelType(channelType: string, data: UpdateChannelOptions<CommandType>) {
2598
- return this.put<UpdateChannelResponse<CommandType>>(
2599
- this.baseURL + `/channeltypes/${channelType}`,
2600
- data,
2601
- );
2313
+ return this.put<UpdateChannelResponse<CommandType>>(this.baseURL + `/channeltypes/${channelType}`, data);
2602
2314
  }
2603
2315
 
2604
2316
  deleteChannelType(channelType: string) {
@@ -2619,15 +2331,7 @@ export class StreamChat<
2619
2331
  */
2620
2332
  async translateMessage(messageId: string, language: string) {
2621
2333
  return await this.post<
2622
- APIResponse &
2623
- MessageResponse<
2624
- AttachmentType,
2625
- ChannelType,
2626
- CommandType,
2627
- MessageType,
2628
- ReactionType,
2629
- UserType
2630
- >
2334
+ APIResponse & MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>
2631
2335
  >(this.baseURL + `/messages/${messageId}/translate`, {
2632
2336
  language,
2633
2337
  });
@@ -2656,10 +2360,7 @@ export class StreamChat<
2656
2360
  * @param {string | { id: string }} messageOrMessageId message object or message id
2657
2361
  * @param {string} errorText error message to report in case of message id absence
2658
2362
  */
2659
- _validateAndGetMessageId(
2660
- messageOrMessageId: string | { id: string },
2661
- errorText: string,
2662
- ) {
2363
+ _validateAndGetMessageId(messageOrMessageId: string | { id: string }, errorText: string) {
2663
2364
  let messageId: string;
2664
2365
  if (typeof messageOrMessageId === 'string') {
2665
2366
  messageId = messageOrMessageId;
@@ -2704,10 +2405,7 @@ export class StreamChat<
2704
2405
  * @param {string | { id: string }} messageOrMessageId message object or message id
2705
2406
  * @param {string | { id: string }} [userId]
2706
2407
  */
2707
- unpinMessage(
2708
- messageOrMessageId: string | { id: string },
2709
- userId?: string | { id: string },
2710
- ) {
2408
+ unpinMessage(messageOrMessageId: string | { id: string }, userId?: string | { id: string }) {
2711
2409
  const messageId = this._validateAndGetMessageId(
2712
2410
  messageOrMessageId,
2713
2411
  'Please specify the message id when calling unpinMessage',
@@ -2733,14 +2431,7 @@ export class StreamChat<
2733
2431
  * @return {APIResponse & { message: MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType> }} Response that includes the message
2734
2432
  */
2735
2433
  async updateMessage(
2736
- message: UpdatedMessage<
2737
- AttachmentType,
2738
- ChannelType,
2739
- CommandType,
2740
- MessageType,
2741
- ReactionType,
2742
- UserType
2743
- >,
2434
+ message: UpdatedMessage<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>,
2744
2435
  userId?: string | { id: string },
2745
2436
  options?: { skip_enrich_url?: boolean },
2746
2437
  ) {
@@ -2751,24 +2442,13 @@ export class StreamChat<
2751
2442
  const clonedMessage: Message = Object.assign({}, message);
2752
2443
  delete clonedMessage.id;
2753
2444
 
2754
- const reservedMessageFields: Array<
2755
- | 'command'
2756
- | 'created_at'
2757
- | 'html'
2758
- | 'latest_reactions'
2759
- | 'own_reactions'
2760
- | 'reaction_counts'
2761
- | 'reply_count'
2762
- | 'type'
2763
- | 'updated_at'
2764
- | 'user'
2765
- | '__html'
2766
- > = [
2445
+ const reservedMessageFields: Array<ReservedMessageFields> = [
2767
2446
  'command',
2768
2447
  'created_at',
2769
2448
  'html',
2770
2449
  'latest_reactions',
2771
2450
  'own_reactions',
2451
+ 'quoted_message',
2772
2452
  'reaction_counts',
2773
2453
  'reply_count',
2774
2454
  'type',
@@ -2795,24 +2475,12 @@ export class StreamChat<
2795
2475
  * Server always expects mentioned_users to be array of string. We are adding extra check, just in case
2796
2476
  * SDK missed this conversion.
2797
2477
  */
2798
- if (
2799
- Array.isArray(clonedMessage.mentioned_users) &&
2800
- !isString(clonedMessage.mentioned_users[0])
2801
- ) {
2802
- clonedMessage.mentioned_users = clonedMessage.mentioned_users.map(
2803
- (mu) => ((mu as unknown) as UserResponse).id,
2804
- );
2478
+ if (Array.isArray(clonedMessage.mentioned_users) && !isString(clonedMessage.mentioned_users[0])) {
2479
+ clonedMessage.mentioned_users = clonedMessage.mentioned_users.map((mu) => ((mu as unknown) as UserResponse).id);
2805
2480
  }
2806
2481
 
2807
2482
  return await this.post<
2808
- UpdateMessageAPIResponse<
2809
- AttachmentType,
2810
- ChannelType,
2811
- CommandType,
2812
- MessageType,
2813
- ReactionType,
2814
- UserType
2815
- >
2483
+ UpdateMessageAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>
2816
2484
  >(this.baseURL + `/messages/${message.id}`, {
2817
2485
  message: clonedMessage,
2818
2486
  ...options,
@@ -2846,14 +2514,7 @@ export class StreamChat<
2846
2514
  user = { id: userId };
2847
2515
  }
2848
2516
  return await this.put<
2849
- UpdateMessageAPIResponse<
2850
- AttachmentType,
2851
- ChannelType,
2852
- CommandType,
2853
- MessageType,
2854
- ReactionType,
2855
- UserType
2856
- >
2517
+ UpdateMessageAPIResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>
2857
2518
  >(this.baseURL + `/messages/${id}`, {
2858
2519
  ...partialMessageObject,
2859
2520
  ...options,
@@ -2868,14 +2529,7 @@ export class StreamChat<
2868
2529
  }
2869
2530
  return await this.delete<
2870
2531
  APIResponse & {
2871
- message: MessageResponse<
2872
- AttachmentType,
2873
- ChannelType,
2874
- CommandType,
2875
- MessageType,
2876
- ReactionType,
2877
- UserType
2878
- >;
2532
+ message: MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>;
2879
2533
  }
2880
2534
  >(this.baseURL + `/messages/${messageID}`, params);
2881
2535
  }
@@ -2883,24 +2537,14 @@ export class StreamChat<
2883
2537
  async getMessage(messageID: string) {
2884
2538
  return await this.get<
2885
2539
  APIResponse & {
2886
- message: MessageResponse<
2887
- AttachmentType,
2888
- ChannelType,
2889
- CommandType,
2890
- MessageType,
2891
- ReactionType,
2892
- UserType
2893
- >;
2540
+ message: MessageResponse<AttachmentType, ChannelType, CommandType, MessageType, ReactionType, UserType>;
2894
2541
  }
2895
2542
  >(this.baseURL + `/messages/${messageID}`);
2896
2543
  }
2897
2544
 
2898
2545
  getUserAgent() {
2899
2546
  return (
2900
- this.userAgent ||
2901
- `stream-chat-javascript-client-${this.node ? 'node' : 'browser'}-${
2902
- process.env.PKG_VERSION
2903
- }`
2547
+ this.userAgent || `stream-chat-javascript-client-${this.node ? 'node' : 'browser'}-${process.env.PKG_VERSION}`
2904
2548
  );
2905
2549
  }
2906
2550
 
@@ -2919,8 +2563,9 @@ export class StreamChat<
2919
2563
  headers: {},
2920
2564
  config: {},
2921
2565
  },
2922
- ) {
2566
+ ): AxiosRequestConfig {
2923
2567
  const token = this._getToken();
2568
+ const authorization = token ? { Authorization: token } : undefined;
2924
2569
 
2925
2570
  if (!options.headers?.['x-client-request-id']) {
2926
2571
  options.headers = {
@@ -2934,10 +2579,10 @@ export class StreamChat<
2934
2579
  user_id: this.userID,
2935
2580
  ...options.params,
2936
2581
  api_key: this.key,
2937
- connection_id: this.wsConnection?.connectionID,
2582
+ connection_id: this._getConnectionID(),
2938
2583
  },
2939
2584
  headers: {
2940
- Authorization: token,
2585
+ ...authorization,
2941
2586
  'stream-auth-type': this.getAuthType(),
2942
2587
  'X-Stream-Client': this.getUserAgent(),
2943
2588
  ...options.headers,
@@ -2965,6 +2610,22 @@ export class StreamChat<
2965
2610
  }, 500);
2966
2611
  }
2967
2612
 
2613
+ /**
2614
+ * encode ws url payload
2615
+ * @private
2616
+ * @returns json string
2617
+ */
2618
+ _buildWSPayload = (client_request_id?: string) => {
2619
+ return JSON.stringify({
2620
+ user_id: this.userID,
2621
+ user_details: this._user,
2622
+ user_token: this.tokenManager.getToken(),
2623
+ server_determines_connection_id: true,
2624
+ device: this.options.device,
2625
+ client_request_id,
2626
+ });
2627
+ };
2628
+
2968
2629
  verifyWebhook(requestBody: string, xSignature: string) {
2969
2630
  return !!this.secret && CheckSignature(requestBody, this.secret, xSignature);
2970
2631
  }
@@ -3051,15 +2712,7 @@ export class StreamChat<
3051
2712
  sync(channel_cids: string[], last_sync_at: string) {
3052
2713
  return this.post<
3053
2714
  APIResponse & {
3054
- events: Event<
3055
- AttachmentType,
3056
- ChannelType,
3057
- CommandType,
3058
- EventType,
3059
- MessageType,
3060
- ReactionType,
3061
- UserType
3062
- >[];
2715
+ events: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>[];
3063
2716
  }
3064
2717
  >(`${this.baseURL}/sync`, {
3065
2718
  channel_cids,
@@ -3086,15 +2739,11 @@ export class StreamChat<
3086
2739
  }
3087
2740
 
3088
2741
  listBlockLists() {
3089
- return this.get<APIResponse & { blocklists: BlockListResponse[] }>(
3090
- `${this.baseURL}/blocklists`,
3091
- );
2742
+ return this.get<APIResponse & { blocklists: BlockListResponse[] }>(`${this.baseURL}/blocklists`);
3092
2743
  }
3093
2744
 
3094
2745
  getBlockList(name: string) {
3095
- return this.get<APIResponse & { blocklist: BlockListResponse }>(
3096
- `${this.baseURL}/blocklists/${name}`,
3097
- );
2746
+ return this.get<APIResponse & { blocklist: BlockListResponse }>(`${this.baseURL}/blocklists/${name}`);
3098
2747
  }
3099
2748
 
3100
2749
  updateBlockList(name: string, data: { words: string[] }) {
@@ -3105,18 +2754,12 @@ export class StreamChat<
3105
2754
  return this.delete<APIResponse>(`${this.baseURL}/blocklists/${name}`);
3106
2755
  }
3107
2756
 
3108
- exportChannels(
3109
- request: Array<ExportChannelRequest>,
3110
- options: ExportChannelOptions = {},
3111
- ) {
2757
+ exportChannels(request: Array<ExportChannelRequest>, options: ExportChannelOptions = {}) {
3112
2758
  const payload = {
3113
2759
  channels: request,
3114
2760
  ...options,
3115
2761
  };
3116
- return this.post<APIResponse & ExportChannelResponse>(
3117
- `${this.baseURL}/export_channels`,
3118
- payload,
3119
- );
2762
+ return this.post<APIResponse & ExportChannelResponse>(`${this.baseURL}/export_channels`, payload);
3120
2763
  }
3121
2764
 
3122
2765
  exportChannel(request: ExportChannelRequest, options?: ExportChannelOptions) {
@@ -3124,9 +2767,7 @@ export class StreamChat<
3124
2767
  }
3125
2768
 
3126
2769
  getExportChannelStatus(id: string) {
3127
- return this.get<APIResponse & ExportChannelStatusResponse>(
3128
- `${this.baseURL}/export_channels/${id}`,
3129
- );
2770
+ return this.get<APIResponse & ExportChannelStatusResponse>(`${this.baseURL}/export_channels/${id}`);
3130
2771
  }
3131
2772
 
3132
2773
  /**
@@ -3137,10 +2778,7 @@ export class StreamChat<
3137
2778
  * @return {Segment} The Created Segment
3138
2779
  */
3139
2780
  async createSegment(params: SegmentData) {
3140
- const { segment } = await this.post<{ segment: Segment }>(
3141
- this.baseURL + `/segments`,
3142
- { segment: params },
3143
- );
2781
+ const { segment } = await this.post<{ segment: Segment }>(this.baseURL + `/segments`, { segment: params });
3144
2782
  return segment;
3145
2783
  }
3146
2784
 
@@ -3152,9 +2790,7 @@ export class StreamChat<
3152
2790
  * @return {Segment} A Segment
3153
2791
  */
3154
2792
  async getSegment(id: string) {
3155
- const { segment } = await this.get<{ segment: Segment }>(
3156
- this.baseURL + `/segments/${id}`,
3157
- );
2793
+ const { segment } = await this.get<{ segment: Segment }>(this.baseURL + `/segments/${id}`);
3158
2794
  return segment;
3159
2795
  }
3160
2796
 
@@ -3165,10 +2801,7 @@ export class StreamChat<
3165
2801
  * @return {Segment[]} Segments
3166
2802
  */
3167
2803
  async listSegments(options: { limit?: number; offset?: number }) {
3168
- const { segments } = await this.get<{ segments: Segment[] }>(
3169
- this.baseURL + `/segments`,
3170
- options,
3171
- );
2804
+ const { segments } = await this.get<{ segments: Segment[] }>(this.baseURL + `/segments`, options);
3172
2805
  return segments;
3173
2806
  }
3174
2807
 
@@ -3181,10 +2814,7 @@ export class StreamChat<
3181
2814
  * @return {Segment} Updated Segment
3182
2815
  */
3183
2816
  async updateSegment(id: string, params: Partial<SegmentData>) {
3184
- const { segment } = await this.put<{ segment: Segment }>(
3185
- this.baseURL + `/segments/${id}`,
3186
- { segment: params },
3187
- );
2817
+ const { segment } = await this.put<{ segment: Segment }>(this.baseURL + `/segments/${id}`, { segment: params });
3188
2818
  return segment;
3189
2819
  }
3190
2820
 
@@ -3207,10 +2837,7 @@ export class StreamChat<
3207
2837
  * @return {Campaign} The Created Campaign
3208
2838
  */
3209
2839
  async createCampaign(params: CampaignData) {
3210
- const { campaign } = await this.post<{ campaign: Campaign }>(
3211
- this.baseURL + `/campaigns`,
3212
- { campaign: params },
3213
- );
2840
+ const { campaign } = await this.post<{ campaign: Campaign }>(this.baseURL + `/campaigns`, { campaign: params });
3214
2841
  return campaign;
3215
2842
  }
3216
2843
 
@@ -3222,9 +2849,7 @@ export class StreamChat<
3222
2849
  * @return {Campaign} A Campaign
3223
2850
  */
3224
2851
  async getCampaign(id: string) {
3225
- const { campaign } = await this.get<{ campaign: Campaign }>(
3226
- this.baseURL + `/campaigns/${id}`,
3227
- );
2852
+ const { campaign } = await this.get<{ campaign: Campaign }>(this.baseURL + `/campaigns/${id}`);
3228
2853
  return campaign;
3229
2854
  }
3230
2855
 
@@ -3235,10 +2860,7 @@ export class StreamChat<
3235
2860
  * @return {Campaign[]} Campaigns
3236
2861
  */
3237
2862
  async listCampaigns(options: { limit?: number; offset?: number }) {
3238
- const { campaigns } = await this.get<{ campaigns: Campaign[] }>(
3239
- this.baseURL + `/campaigns`,
3240
- options,
3241
- );
2863
+ const { campaigns } = await this.get<{ campaigns: Campaign[] }>(this.baseURL + `/campaigns`, options);
3242
2864
  return campaigns;
3243
2865
  }
3244
2866
 
@@ -3251,10 +2873,9 @@ export class StreamChat<
3251
2873
  * @return {Campaign} Updated Campaign
3252
2874
  */
3253
2875
  async updateCampaign(id: string, params: Partial<CampaignData>) {
3254
- const { campaign } = await this.put<{ campaign: Campaign }>(
3255
- this.baseURL + `/campaigns/${id}`,
3256
- { campaign: params },
3257
- );
2876
+ const { campaign } = await this.put<{ campaign: Campaign }>(this.baseURL + `/campaigns/${id}`, {
2877
+ campaign: params,
2878
+ });
3258
2879
  return campaign;
3259
2880
  }
3260
2881
 
@@ -3279,10 +2900,9 @@ export class StreamChat<
3279
2900
  */
3280
2901
  async scheduleCampaign(id: string, params: { sendAt: number }) {
3281
2902
  const { sendAt } = params;
3282
- const { campaign } = await this.patch<{ campaign: Campaign }>(
3283
- this.baseURL + `/campaigns/${id}/schedule`,
3284
- { send_at: sendAt },
3285
- );
2903
+ const { campaign } = await this.patch<{ campaign: Campaign }>(this.baseURL + `/campaigns/${id}/schedule`, {
2904
+ send_at: sendAt,
2905
+ });
3286
2906
  return campaign;
3287
2907
  }
3288
2908
 
@@ -3294,9 +2914,7 @@ export class StreamChat<
3294
2914
  * @return {Campaign} Stopped Campaign
3295
2915
  */
3296
2916
  async stopCampaign(id: string) {
3297
- const { campaign } = await this.patch<{ campaign: Campaign }>(
3298
- this.baseURL + `/campaigns/${id}/stop`,
3299
- );
2917
+ const { campaign } = await this.patch<{ campaign: Campaign }>(this.baseURL + `/campaigns/${id}/stop`);
3300
2918
  return campaign;
3301
2919
  }
3302
2920
 
@@ -3308,9 +2926,7 @@ export class StreamChat<
3308
2926
  * @return {Campaign} Resumed Campaign
3309
2927
  */
3310
2928
  async resumeCampaign(id: string) {
3311
- const { campaign } = await this.patch<{ campaign: Campaign }>(
3312
- this.baseURL + `/campaigns/${id}/resume`,
3313
- );
2929
+ const { campaign } = await this.patch<{ campaign: Campaign }>(this.baseURL + `/campaigns/${id}/resume`);
3314
2930
  return campaign;
3315
2931
  }
3316
2932
 
@@ -3323,10 +2939,7 @@ export class StreamChat<
3323
2939
  */
3324
2940
  async testCampaign(id: string, params: { users: string[] }) {
3325
2941
  const { users } = params;
3326
- const { campaign } = await this.post<{ campaign: Campaign }>(
3327
- this.baseURL + `/campaigns/${id}/test`,
3328
- { users },
3329
- );
2942
+ const { campaign } = await this.post<{ campaign: Campaign }>(this.baseURL + `/campaigns/${id}/test`, { users });
3330
2943
  return campaign;
3331
2944
  }
3332
2945
 
@@ -3360,10 +2973,10 @@ export class StreamChat<
3360
2973
  * @return {DeleteChannelsResponse} Result of the soft deletion, if server-side, it holds the task ID as well
3361
2974
  */
3362
2975
  async deleteChannels(cids: string[], options: { hard_delete?: boolean } = {}) {
3363
- return await this.post<APIResponse & DeleteChannelsResponse>(
3364
- this.baseURL + `/channels/delete`,
3365
- { cids, ...options },
3366
- );
2976
+ return await this.post<APIResponse & DeleteChannelsResponse>(this.baseURL + `/channels/delete`, {
2977
+ cids,
2978
+ ...options,
2979
+ });
3367
2980
  }
3368
2981
 
3369
2982
  /**
@@ -3378,21 +2991,11 @@ export class StreamChat<
3378
2991
  if (options?.user !== 'soft' && options?.user !== 'hard') {
3379
2992
  throw new Error('Invalid delete user options. user must be one of [soft hard]');
3380
2993
  }
3381
- if (
3382
- options.messages !== undefined &&
3383
- options.messages !== 'soft' &&
3384
- options.messages !== 'hard'
3385
- ) {
2994
+ if (options.messages !== undefined && options.messages !== 'soft' && options.messages !== 'hard') {
3386
2995
  throw new Error('Invalid delete user options. messages must be one of [soft hard]');
3387
2996
  }
3388
- if (
3389
- options.conversations !== undefined &&
3390
- options.conversations !== 'soft' &&
3391
- options.conversations !== 'hard'
3392
- ) {
3393
- throw new Error(
3394
- 'Invalid delete user options. conversations must be one of [soft hard]',
3395
- );
2997
+ if (options.conversations !== undefined && options.conversations !== 'soft' && options.conversations !== 'hard') {
2998
+ throw new Error('Invalid delete user options. conversations must be one of [soft hard]');
3396
2999
  }
3397
3000
  return await this.post<APIResponse & TaskResponse>(this.baseURL + `/users/delete`, {
3398
3001
  user_ids,