stream-chat 4.4.3-dev.0 → 4.4.3

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/src/client.ts CHANGED
@@ -118,7 +118,7 @@ import {
118
118
  DeleteChannelsResponse,
119
119
  TaskResponse,
120
120
  } from './types';
121
- import { InsightTypes, InsightMetrics } from './insights';
121
+ import { InsightMetrics, postInsights } from './insights';
122
122
 
123
123
  function isString(x: unknown): x is string {
124
124
  return typeof x === 'string' || x instanceof String;
@@ -269,7 +269,7 @@ export class StreamChat<
269
269
 
270
270
  this.axiosInstance = axios.create(this.options);
271
271
 
272
- this.setBaseURL(this.options.baseURL || 'https://chat-us-east-1.stream-io-api.com');
272
+ this.setBaseURL(this.options.baseURL || 'https://chat.stream-io-api.com');
273
273
 
274
274
  if (typeof process !== 'undefined' && process.env.STREAM_LOCAL_TEST_RUN) {
275
275
  this.setBaseURL('http://localhost:3030');
@@ -1140,6 +1140,7 @@ export class StreamChat<
1140
1140
  this.consecutiveFailures = 0;
1141
1141
  return this.handleResponse(response);
1142
1142
  } catch (e) {
1143
+ e.client_request_id = requestConfig.headers?.['x-client-request-id'];
1143
1144
  this._logApiError(type, url, e);
1144
1145
  this.consecutiveFailures += 1;
1145
1146
  if (e.response) {
@@ -1619,10 +1620,14 @@ export class StreamChat<
1619
1620
  throw Error('clientID is not set');
1620
1621
  }
1621
1622
 
1622
- // The StableWSConnection handles all the reconnection logic.
1623
+ if (!this.wsConnection && (this.options.warmUp || this.options.enableInsights)) {
1624
+ this._sayHi();
1625
+ }
1623
1626
 
1627
+ // The StableWSConnection handles all the reconnection logic.
1624
1628
  this.wsConnection = new StableWSConnection<ChannelType, CommandType, UserType>({
1625
1629
  wsBaseURL: client.wsBaseURL,
1630
+ enableInsights: this.options.enableInsights,
1626
1631
  clientID: client.clientID,
1627
1632
  userID: client.userID,
1628
1633
  tokenManager: client.tokenManager,
@@ -1635,24 +1640,29 @@ export class StreamChat<
1635
1640
  eventCallback: this.dispatchEvent as (event: ConnectionChangeEvent) => void,
1636
1641
  logger: this.logger,
1637
1642
  device: this.options.device,
1638
- postInsights: this.options.enableInsights ? this.postInsights : undefined,
1639
1643
  insightMetrics: this.insightMetrics,
1640
1644
  });
1641
1645
 
1642
- let warmUpPromise;
1643
- if (this.options.warmUp) {
1644
- warmUpPromise = this.doAxiosRequest('options', this.baseURL + '/connect');
1645
- }
1646
- const handshake = await this.wsConnection.connect();
1647
- try {
1648
- await warmUpPromise;
1649
- } catch (e) {
1650
- this.logger('error', 'Warmup request failed', {
1651
- error: e,
1652
- });
1653
- }
1646
+ return await this.wsConnection.connect();
1647
+ }
1654
1648
 
1655
- return handshake;
1649
+ /**
1650
+ * Check the connectivity with server for warmup purpose.
1651
+ *
1652
+ * @private
1653
+ */
1654
+ _sayHi() {
1655
+ const client_request_id = randomId();
1656
+ const opts = { headers: { 'x-client-request-id': client_request_id } };
1657
+ this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => {
1658
+ if (this.options.enableInsights) {
1659
+ postInsights('http_hi_failed', {
1660
+ api_key: this.key,
1661
+ err: e,
1662
+ client_request_id,
1663
+ });
1664
+ }
1665
+ });
1656
1666
  }
1657
1667
 
1658
1668
  /**
@@ -2912,6 +2922,13 @@ export class StreamChat<
2912
2922
  ) {
2913
2923
  const token = this._getToken();
2914
2924
 
2925
+ if (!options.headers?.['x-client-request-id']) {
2926
+ options.headers = {
2927
+ ...options.headers,
2928
+ 'x-client-request-id': randomId(),
2929
+ };
2930
+ }
2931
+
2915
2932
  return {
2916
2933
  params: {
2917
2934
  user_id: this.userID,
@@ -3349,27 +3366,6 @@ export class StreamChat<
3349
3366
  );
3350
3367
  }
3351
3368
 
3352
- postInsights = async (insightType: InsightTypes, insights: Record<string, unknown>) => {
3353
- const maxAttempts = 3;
3354
- for (let i = 0; i < maxAttempts; i++) {
3355
- try {
3356
- await this.axiosInstance.post(
3357
- `https://insights.stream-io-api.com/insights/${insightType}`,
3358
- insights,
3359
- );
3360
- } catch (e) {
3361
- this.logger('warn', `failed to send insights event ${insightType}`, {
3362
- tags: ['insights', 'connection'],
3363
- error: e,
3364
- insights,
3365
- });
3366
- await sleep((i + 1) * 3000);
3367
- continue;
3368
- }
3369
- break;
3370
- }
3371
- };
3372
-
3373
3369
  /**
3374
3370
  * deleteUsers - Batch Delete Users
3375
3371
  *
package/src/connection.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  buildWsFatalInsight,
6
6
  buildWsSuccessAfterFailureInsight,
7
7
  InsightMetrics,
8
- InsightTypes,
8
+ postInsights,
9
9
  } from './insights';
10
10
  import {
11
11
  BaseDeviceFields,
@@ -48,7 +48,7 @@ type Constructor<
48
48
  userID: string;
49
49
  wsBaseURL: string;
50
50
  device?: BaseDeviceFields;
51
- postInsights?: (eventType: InsightTypes, event: Record<string, unknown>) => void;
51
+ enableInsights?: boolean;
52
52
  };
53
53
 
54
54
  /**
@@ -110,7 +110,7 @@ export class StableWSConnection<
110
110
  totalFailures: number;
111
111
  ws?: WebSocket;
112
112
  wsID: number;
113
- postInsights?: Constructor<ChannelType, CommandType, UserType>['postInsights'];
113
+ enableInsights?: boolean;
114
114
  insightMetrics: InsightMetrics;
115
115
  constructor({
116
116
  apiKey,
@@ -126,7 +126,7 @@ export class StableWSConnection<
126
126
  userID,
127
127
  wsBaseURL,
128
128
  device,
129
- postInsights,
129
+ enableInsights,
130
130
  insightMetrics,
131
131
  }: Constructor<ChannelType, CommandType, UserType>) {
132
132
  this.wsBaseURL = wsBaseURL;
@@ -161,7 +161,7 @@ export class StableWSConnection<
161
161
  this.pingInterval = 25 * 1000;
162
162
  this.connectionCheckTimeout = this.pingInterval + 10 * 1000;
163
163
  this._listenForConnectionChanges();
164
- this.postInsights = postInsights;
164
+ this.enableInsights = enableInsights;
165
165
  this.insightMetrics = insightMetrics;
166
166
  }
167
167
 
@@ -270,7 +270,7 @@ export class StableWSConnection<
270
270
  user_token: this.tokenManager.getToken(),
271
271
  server_determines_connection_id: true,
272
272
  device: this.device,
273
- request_id: reqID,
273
+ client_request_id: reqID,
274
274
  };
275
275
  const qs = encodeURIComponent(JSON.stringify(params));
276
276
  const token = this.tokenManager.getToken();
@@ -388,10 +388,10 @@ export class StableWSConnection<
388
388
 
389
389
  if (response) {
390
390
  this.connectionID = response.connection_id;
391
- if (this.insightMetrics.wsConsecutiveFailures > 0 && this.postInsights) {
392
- this.postInsights(
391
+ if (this.insightMetrics.wsConsecutiveFailures > 0 && this.enableInsights) {
392
+ postInsights(
393
393
  'ws_success_after_failure',
394
- buildWsSuccessAfterFailureInsight(this),
394
+ buildWsSuccessAfterFailureInsight((this as unknown) as StableWSConnection),
395
395
  );
396
396
  this.insightMetrics.wsConsecutiveFailures = 0;
397
397
  }
@@ -400,13 +400,15 @@ export class StableWSConnection<
400
400
  } catch (err) {
401
401
  this.isConnecting = false;
402
402
 
403
- if (this.postInsights) {
403
+ if (this.enableInsights) {
404
404
  this.insightMetrics.wsConsecutiveFailures++;
405
405
  this.insightMetrics.wsTotalFailures++;
406
406
 
407
- // @ts-ignore
408
- const insights = buildWsFatalInsight(this, convertErrorToJson(err));
409
- this.postInsights?.('ws_fatal', insights);
407
+ const insights = buildWsFatalInsight(
408
+ (this as unknown) as StableWSConnection,
409
+ convertErrorToJson(err as Error),
410
+ );
411
+ postInsights?.('ws_fatal', insights);
410
412
  }
411
413
  throw err;
412
414
  }
package/src/insights.ts CHANGED
@@ -1,8 +1,8 @@
1
+ import axios from 'axios';
1
2
  import { StableWSConnection } from './connection';
2
- import { LiteralStringForUnion, UnknownType } from './types';
3
- import { randomId } from './utils';
3
+ import { randomId, sleep } from './utils';
4
4
 
5
- export type InsightTypes = 'ws_fatal' | 'ws_success_after_failure';
5
+ export type InsightTypes = 'ws_fatal' | 'ws_success_after_failure' | 'http_hi_failed';
6
6
  export class InsightMetrics {
7
7
  connectionStartTimestamp: number | null;
8
8
  wsConsecutiveFailures: number;
@@ -17,12 +17,34 @@ export class InsightMetrics {
17
17
  }
18
18
  }
19
19
 
20
- export function buildWsFatalInsight<
21
- ChannelType extends UnknownType = UnknownType,
22
- CommandType extends string = LiteralStringForUnion,
23
- UserType extends UnknownType = UnknownType
24
- >(
25
- connection: StableWSConnection<ChannelType, CommandType, UserType>,
20
+ /**
21
+ * postInsights is not supposed to be used by end users directly within chat application, and thus is kept isolated
22
+ * from all the client/connection code/logic.
23
+ *
24
+ * @param insightType
25
+ * @param insights
26
+ */
27
+ export const postInsights = async (
28
+ insightType: InsightTypes,
29
+ insights: Record<string, unknown>,
30
+ ) => {
31
+ const maxAttempts = 3;
32
+ for (let i = 0; i < maxAttempts; i++) {
33
+ try {
34
+ await axios.post(
35
+ `https://chat-insights.getstream.io/insights/${insightType}`,
36
+ insights,
37
+ );
38
+ } catch (e) {
39
+ await sleep((i + 1) * 3000);
40
+ continue;
41
+ }
42
+ break;
43
+ }
44
+ };
45
+
46
+ export function buildWsFatalInsight(
47
+ connection: StableWSConnection,
26
48
  event: Record<string, unknown>,
27
49
  ) {
28
50
  return {
@@ -31,11 +53,7 @@ export function buildWsFatalInsight<
31
53
  };
32
54
  }
33
55
 
34
- function buildWsBaseInsight<
35
- ChannelType extends UnknownType = UnknownType,
36
- CommandType extends string = LiteralStringForUnion,
37
- UserType extends UnknownType = UnknownType
38
- >(connection: StableWSConnection<ChannelType, CommandType, UserType>) {
56
+ function buildWsBaseInsight(connection: StableWSConnection) {
39
57
  return {
40
58
  ready_state: connection.ws?.readyState,
41
59
  url: connection._buildUrl(connection.requestID),
@@ -58,10 +76,6 @@ function buildWsBaseInsight<
58
76
  };
59
77
  }
60
78
 
61
- export function buildWsSuccessAfterFailureInsight<
62
- ChannelType extends UnknownType = UnknownType,
63
- CommandType extends string = LiteralStringForUnion,
64
- UserType extends UnknownType = UnknownType
65
- >(connection: StableWSConnection<ChannelType, CommandType, UserType>) {
79
+ export function buildWsSuccessAfterFailureInsight(connection: StableWSConnection) {
66
80
  return buildWsBaseInsight(connection);
67
81
  }
package/src/types.ts CHANGED
@@ -2163,11 +2163,13 @@ export type DeleteType = 'soft' | 'hard';
2163
2163
  implies that all related objects (messages, flags, etc) will be hard-deleted as well.
2164
2164
  `conversations` soft|hard will delete any 1to1 channels that the user was a member of.
2165
2165
  `messages` soft-hard will delete any messages that the user has sent.
2166
+ `new_channel_owner_id` any channels owned by the hard-deleted user will be transferred to this user ID
2166
2167
  */
2167
2168
  export type DeleteUserOptions = {
2168
2169
  user: DeleteType;
2169
2170
  conversations?: DeleteType;
2170
2171
  messages?: DeleteType;
2172
+ new_channel_owner_id?: string;
2171
2173
  };
2172
2174
 
2173
2175
  export type SegmentData = {
package/src/utils.ts CHANGED
@@ -199,15 +199,20 @@ function getRandomBytes(length: number): Uint8Array {
199
199
  return bytes;
200
200
  }
201
201
 
202
- export function convertErrorToJson(err: unknown) {
202
+ export function convertErrorToJson(err: Error) {
203
203
  const jsonObj = {} as Record<string, unknown>;
204
204
 
205
205
  if (!err) return jsonObj;
206
206
 
207
- Object.getOwnPropertyNames(err).forEach((key) => {
208
- // @ts-ignore
209
- jsonObj[key] = err[key];
210
- });
207
+ try {
208
+ Object.getOwnPropertyNames(err).forEach((key) => {
209
+ jsonObj[key] = Object.getOwnPropertyDescriptor(err, key);
210
+ });
211
+ } catch (_) {
212
+ return {
213
+ error: 'failed to serialize the error',
214
+ };
215
+ }
211
216
 
212
217
  return jsonObj;
213
218
  }