stream-chat 4.4.3-dev.3 → 5.0.1

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 +1258 -722
  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 +1258 -721
  7. package/dist/browser.js.map +1 -1
  8. package/dist/index.es.js +1258 -722
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.js +1258 -721
  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 +25 -42
  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 +41 -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 +16 -9
  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 +95 -89
  35. package/dist/types/types.d.ts.map +1 -1
  36. package/dist/types/utils.d.ts +13 -3
  37. package/dist/types/utils.d.ts.map +1 -1
  38. package/package.json +4 -4
  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 +298 -712
  43. package/src/client_state.ts +2 -2
  44. package/src/connection.ts +146 -395
  45. package/src/connection_fallback.ts +209 -0
  46. package/src/errors.ts +58 -0
  47. package/src/insights.ts +37 -31
  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 +269 -512
  52. package/src/utils.ts +58 -24
  53. package/CHANGELOG.md +0 -844
@@ -0,0 +1,209 @@
1
+ import axios, { AxiosRequestConfig, CancelTokenSource } from 'axios';
2
+ import { StreamChat } from './client';
3
+ import { addConnectionEventListeners, removeConnectionEventListeners, retryInterval, sleep } from './utils';
4
+ import { isAPIError, isConnectionIDError, isErrorRetryable } from './errors';
5
+ import { ConnectionOpen, Event, UnknownType, UR, LiteralStringForUnion, LogLevel } from './types';
6
+
7
+ export enum ConnectionState {
8
+ Closed = 'CLOSED',
9
+ Connected = 'CONNECTED',
10
+ Connecting = 'CONNECTING',
11
+ Disconnected = 'DISCONNECTED',
12
+ Init = 'INIT',
13
+ }
14
+
15
+ export class WSConnectionFallback<
16
+ AttachmentType extends UR = UR,
17
+ ChannelType extends UR = UR,
18
+ CommandType extends string = LiteralStringForUnion,
19
+ EventType extends UR = UR,
20
+ MessageType extends UR = UR,
21
+ ReactionType extends UR = UR,
22
+ UserType extends UR = UR
23
+ > {
24
+ client: StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
25
+ state: ConnectionState;
26
+ consecutiveFailures: number;
27
+ connectionID?: string;
28
+ cancelToken?: CancelTokenSource;
29
+
30
+ constructor({
31
+ client,
32
+ }: {
33
+ client: StreamChat<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>;
34
+ }) {
35
+ this.client = client;
36
+ this.state = ConnectionState.Init;
37
+ this.consecutiveFailures = 0;
38
+
39
+ addConnectionEventListeners(this._onlineStatusChanged);
40
+ }
41
+
42
+ _log(msg: string, extra: UR = {}, level: LogLevel = 'info') {
43
+ this.client.logger(level, 'WSConnectionFallback:' + msg, { tags: ['connection_fallback', 'connection'], ...extra });
44
+ }
45
+
46
+ _setState(state: ConnectionState) {
47
+ this._log(`_setState() - ${state}`);
48
+
49
+ // transition from connecting => connected
50
+ if (this.state === ConnectionState.Connecting && state === ConnectionState.Connected) {
51
+ //@ts-expect-error
52
+ this.client.dispatchEvent({ type: 'connection.changed', online: true });
53
+ }
54
+
55
+ if (state === ConnectionState.Closed || state === ConnectionState.Disconnected) {
56
+ //@ts-expect-error
57
+ this.client.dispatchEvent({ type: 'connection.changed', online: false });
58
+ }
59
+
60
+ this.state = state;
61
+ }
62
+
63
+ /** @private */
64
+ _onlineStatusChanged = (event: { type: string }) => {
65
+ this._log(`_onlineStatusChanged() - ${event.type}`);
66
+
67
+ if (event.type === 'offline') {
68
+ this._setState(ConnectionState.Closed);
69
+ this.cancelToken?.cancel('disconnect() is called');
70
+ this.cancelToken = undefined;
71
+ return;
72
+ }
73
+
74
+ if (event.type === 'online' && this.state === ConnectionState.Closed) {
75
+ this.connect(true);
76
+ }
77
+ };
78
+
79
+ /** @private */
80
+ _req = async <T = UR>(params: UnknownType, config: AxiosRequestConfig, retry: boolean): Promise<T> => {
81
+ if (!this.cancelToken && !params.close) {
82
+ this.cancelToken = axios.CancelToken.source();
83
+ }
84
+
85
+ try {
86
+ const res = await this.client.doAxiosRequest<T>(
87
+ 'get',
88
+ (this.client.baseURL as string).replace(':3030', ':8900') + '/longpoll', // replace port if present for testing with local API
89
+ undefined,
90
+ {
91
+ config: { ...config, cancelToken: this.cancelToken?.token },
92
+ params,
93
+ },
94
+ );
95
+
96
+ this.consecutiveFailures = 0; // always reset in case of no error
97
+ return res;
98
+ } catch (err) {
99
+ this.consecutiveFailures += 1;
100
+
101
+ if (retry && isErrorRetryable(err)) {
102
+ this._log(`_req() - Retryable error, retrying request`);
103
+ await sleep(retryInterval(this.consecutiveFailures));
104
+ return this._req<T>(params, config, retry);
105
+ }
106
+
107
+ throw err;
108
+ }
109
+ };
110
+
111
+ /** @private */
112
+ _poll = async () => {
113
+ while (this.state === ConnectionState.Connected) {
114
+ try {
115
+ const data = await this._req<{
116
+ events: Event<AttachmentType, ChannelType, CommandType, EventType, MessageType, ReactionType, UserType>[];
117
+ }>({}, { timeout: 30000 }, true); // 30s => API responds in 20s if there is no event
118
+
119
+ if (data.events?.length) {
120
+ for (let i = 0; i < data.events.length; i++) {
121
+ this.client.dispatchEvent(data.events[i]);
122
+ }
123
+ }
124
+ } catch (err) {
125
+ if (axios.isCancel(err)) {
126
+ this._log(`_poll() - axios canceled request`);
127
+ return;
128
+ }
129
+
130
+ /** client.doAxiosRequest will take care of TOKEN_EXPIRED error */
131
+
132
+ if (isConnectionIDError(err)) {
133
+ this._log(`_poll() - ConnectionID error, connecting without ID...`);
134
+ this._setState(ConnectionState.Disconnected);
135
+ this.connect(true);
136
+ return;
137
+ }
138
+
139
+ if (isAPIError(err) && !isErrorRetryable(err)) {
140
+ this._setState(ConnectionState.Closed);
141
+ return;
142
+ }
143
+
144
+ await sleep(retryInterval(this.consecutiveFailures));
145
+ }
146
+ }
147
+ };
148
+
149
+ /**
150
+ * connect try to open a longpoll request
151
+ * @param reconnect should be false for first call and true for subsequent calls to keep the connection alive and call recoverState
152
+ */
153
+ connect = async (reconnect = false) => {
154
+ if (this.state === ConnectionState.Connecting) {
155
+ this._log('connect() - connecting already in progress', { reconnect }, 'warn');
156
+ return;
157
+ }
158
+ if (this.state === ConnectionState.Connected) {
159
+ this._log('connect() - already connected and polling', { reconnect }, 'warn');
160
+ return;
161
+ }
162
+
163
+ this._setState(ConnectionState.Connecting);
164
+ this.connectionID = undefined; // connect should be sent with empty connection_id so API creates one
165
+ try {
166
+ const { event } = await this._req<{ event: ConnectionOpen<ChannelType, CommandType, UserType> }>(
167
+ { json: this.client._buildWSPayload() },
168
+ { timeout: 8000 }, // 8s
169
+ reconnect,
170
+ );
171
+
172
+ this._setState(ConnectionState.Connected);
173
+ this.connectionID = event.connection_id;
174
+ this._poll();
175
+ if (reconnect) {
176
+ this.client.recoverState();
177
+ }
178
+ return event;
179
+ } catch (err) {
180
+ this._setState(ConnectionState.Closed);
181
+ throw err;
182
+ }
183
+ };
184
+
185
+ /**
186
+ * isHealthy checks if there is a connectionID and connection is in Connected state
187
+ */
188
+ isHealthy = () => {
189
+ return this.connectionID && this.state === ConnectionState.Connected;
190
+ };
191
+
192
+ disconnect = async (timeout = 2000) => {
193
+ removeConnectionEventListeners(this._onlineStatusChanged);
194
+
195
+ this._setState(ConnectionState.Disconnected);
196
+ this.cancelToken?.cancel('disconnect() is called');
197
+ this.cancelToken = undefined;
198
+
199
+ const connection_id = this.connectionID;
200
+ this.connectionID = undefined;
201
+
202
+ try {
203
+ await this._req({ close: true, connection_id }, { timeout }, false);
204
+ this._log(`disconnect() - Closed connectionID`);
205
+ } catch (err) {
206
+ this._log(`disconnect() - Failed`, { err }, 'error');
207
+ }
208
+ };
209
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,58 @@
1
+ export const APIErrorCodes: Record<string, { name: string; retryable: boolean }> = {
2
+ '-1': { name: 'InternalSystemError', retryable: true },
3
+ '2': { name: 'AccessKeyError', retryable: false },
4
+ '3': { name: 'AuthenticationFailedError', retryable: true },
5
+ '4': { name: 'InputError', retryable: false },
6
+ '6': { name: 'DuplicateUsernameError', retryable: false },
7
+ '9': { name: 'RateLimitError', retryable: true },
8
+ '16': { name: 'DoesNotExistError', retryable: false },
9
+ '17': { name: 'NotAllowedError', retryable: false },
10
+ '18': { name: 'EventNotSupportedError', retryable: false },
11
+ '19': { name: 'ChannelFeatureNotSupportedError', retryable: false },
12
+ '20': { name: 'MessageTooLongError', retryable: false },
13
+ '21': { name: 'MultipleNestingLevelError', retryable: false },
14
+ '22': { name: 'PayloadTooBigError', retryable: false },
15
+ '23': { name: 'RequestTimeoutError', retryable: true },
16
+ '24': { name: 'MaxHeaderSizeExceededError', retryable: false },
17
+ '40': { name: 'AuthErrorTokenExpired', retryable: false },
18
+ '41': { name: 'AuthErrorTokenNotValidYet', retryable: false },
19
+ '42': { name: 'AuthErrorTokenUsedBeforeIssuedAt', retryable: false },
20
+ '43': { name: 'AuthErrorTokenSignatureInvalid', retryable: false },
21
+ '44': { name: 'CustomCommandEndpointMissingError', retryable: false },
22
+ '45': { name: 'CustomCommandEndpointCallError', retryable: true },
23
+ '46': { name: 'ConnectionIDNotFoundError', retryable: false },
24
+ '60': { name: 'CoolDownError', retryable: true },
25
+ '69': { name: 'ErrWrongRegion', retryable: false },
26
+ '70': { name: 'ErrQueryChannelPermissions', retryable: false },
27
+ '71': { name: 'ErrTooManyConnections', retryable: true },
28
+ '99': { name: 'AppSuspendedError', retryable: false },
29
+ };
30
+
31
+ type APIError = Error & { code: number; isWSFailure?: boolean };
32
+
33
+ export function isAPIError(error: Error): error is APIError {
34
+ return (error as APIError).code !== undefined;
35
+ }
36
+
37
+ export function isErrorRetryable(error: APIError) {
38
+ if (!error.code) return false;
39
+ const err = APIErrorCodes[`${error.code}`];
40
+ if (!err) return false;
41
+ return err.retryable;
42
+ }
43
+
44
+ export function isConnectionIDError(error: APIError) {
45
+ return error.code === 46; // ConnectionIDNotFoundError
46
+ }
47
+
48
+ export function isWSFailure(err: APIError): boolean {
49
+ if (typeof err.isWSFailure === 'boolean') {
50
+ return err.isWSFailure;
51
+ }
52
+
53
+ try {
54
+ return JSON.parse(err.message).isWSFailure;
55
+ } catch (_) {
56
+ return false;
57
+ }
58
+ }
package/src/insights.ts CHANGED
@@ -1,6 +1,6 @@
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
5
  export type InsightTypes = 'ws_fatal' | 'ws_success_after_failure' | 'http_hi_failed';
6
6
  export class InsightMetrics {
@@ -17,51 +17,57 @@ 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>,
26
- event: Record<string, unknown>,
27
- ) {
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 (insightType: InsightTypes, insights: Record<string, unknown>) => {
28
+ const maxAttempts = 3;
29
+ for (let i = 0; i < maxAttempts; i++) {
30
+ try {
31
+ await axios.post(`https://chat-insights.getstream.io/insights/${insightType}`, insights);
32
+ } catch (e) {
33
+ await sleep((i + 1) * 3000);
34
+ continue;
35
+ }
36
+ break;
37
+ }
38
+ };
39
+
40
+ export function buildWsFatalInsight(connection: StableWSConnection, event: Record<string, unknown>) {
28
41
  return {
29
42
  ...event,
30
43
  ...buildWsBaseInsight(connection),
31
44
  };
32
45
  }
33
46
 
34
- function buildWsBaseInsight<
35
- ChannelType extends UnknownType = UnknownType,
36
- CommandType extends string = LiteralStringForUnion,
37
- UserType extends UnknownType = UnknownType
38
- >(connection: StableWSConnection<ChannelType, CommandType, UserType>) {
47
+ function buildWsBaseInsight(connection: StableWSConnection) {
48
+ const { client } = connection;
39
49
  return {
40
50
  ready_state: connection.ws?.readyState,
41
- url: connection._buildUrl(connection.requestID),
42
- api_key: connection.apiKey,
43
- start_ts: connection.insightMetrics.connectionStartTimestamp,
51
+ url: connection._buildUrl(),
52
+ api_key: client.key,
53
+ start_ts: client.insightMetrics.connectionStartTimestamp,
44
54
  end_ts: new Date().getTime(),
45
- auth_type: connection.authType,
46
- token: connection.tokenManager.token,
47
- user_id: connection.userID,
48
- user_details: connection.user,
49
- device: connection.device,
55
+ auth_type: client.getAuthType(),
56
+ token: client.tokenManager.token,
57
+ user_id: client.userID,
58
+ user_details: client._user,
59
+ device: client.options.device,
50
60
  client_id: connection.connectionID,
51
61
  ws_details: connection.ws,
52
- ws_consecutive_failures: connection.insightMetrics.wsConsecutiveFailures,
53
- ws_total_failures: connection.insightMetrics.wsTotalFailures,
62
+ ws_consecutive_failures: client.insightMetrics.wsConsecutiveFailures,
63
+ ws_total_failures: client.insightMetrics.wsTotalFailures,
54
64
  request_id: connection.requestID,
55
65
  online: typeof navigator !== 'undefined' ? navigator?.onLine : null,
56
66
  user_agent: typeof navigator !== 'undefined' ? navigator?.userAgent : null,
57
- instance_client_id: connection.insightMetrics.instanceClientId,
67
+ instance_client_id: client.insightMetrics.instanceClientId,
58
68
  };
59
69
  }
60
70
 
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>) {
71
+ export function buildWsSuccessAfterFailureInsight(connection: StableWSConnection) {
66
72
  return buildWsBaseInsight(connection);
67
73
  }
@@ -37,33 +37,12 @@ export class Permission {
37
37
  }
38
38
 
39
39
  // deprecated
40
- export const AllowAll = new Permission(
41
- 'Allow all',
42
- MaxPriority,
43
- AnyResource,
44
- AnyRole,
45
- false,
46
- Allow,
47
- );
40
+ export const AllowAll = new Permission('Allow all', MaxPriority, AnyResource, AnyRole, false, Allow);
48
41
 
49
42
  // deprecated
50
- export const DenyAll = new Permission(
51
- 'Deny all',
52
- MinPriority,
53
- AnyResource,
54
- AnyRole,
55
- false,
56
- Deny,
57
- );
43
+ export const DenyAll = new Permission('Deny all', MinPriority, AnyResource, AnyRole, false, Deny);
58
44
 
59
- export type Role =
60
- | 'admin'
61
- | 'user'
62
- | 'guest'
63
- | 'anonymous'
64
- | 'channel_member'
65
- | 'channel_moderator'
66
- | string;
45
+ export type Role = 'admin' | 'user' | 'guest' | 'anonymous' | 'channel_member' | 'channel_moderator' | string;
67
46
 
68
47
  export const BuiltinRoles = {
69
48
  Admin: 'admin',
package/src/signing.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import jwt, { Secret, SignOptions } from 'jsonwebtoken';
2
2
  import crypto from 'crypto';
3
3
  import { encodeBase64, decodeBase64 } from './base64';
4
- import { UnknownType } from './types';
4
+ import { UR } from './types';
5
5
 
6
6
  /**
7
7
  * Creates the JWT token that can be used for a UserSession
@@ -10,21 +10,16 @@ import { UnknownType } from './types';
10
10
  * @private
11
11
  * @param {Secret} apiSecret - API Secret key
12
12
  * @param {string} userId - The user_id key in the JWT payload
13
- * @param {UnknownType} [extraData] - Extra that should be part of the JWT token
13
+ * @param {UR} [extraData] - Extra that should be part of the JWT token
14
14
  * @param {SignOptions} [jwtOptions] - Options that can be past to jwt.sign
15
15
  * @return {string} JWT Token
16
16
  */
17
- export function JWTUserToken(
18
- apiSecret: Secret,
19
- userId: string,
20
- extraData: UnknownType = {},
21
- jwtOptions: SignOptions = {},
22
- ) {
17
+ export function JWTUserToken(apiSecret: Secret, userId: string, extraData: UR = {}, jwtOptions: SignOptions = {}) {
23
18
  if (typeof userId !== 'string') {
24
19
  throw new TypeError('userId should be a string');
25
20
  }
26
21
 
27
- const payload: { user_id: string } & UnknownType = {
22
+ const payload: { user_id: string } & UR = {
28
23
  user_id: userId,
29
24
  ...extraData,
30
25
  };
@@ -36,10 +31,7 @@ export function JWTUserToken(
36
31
  );
37
32
  }
38
33
 
39
- const opts: SignOptions = Object.assign(
40
- { algorithm: 'HS256', noTimestamp: true },
41
- jwtOptions,
42
- );
34
+ const opts: SignOptions = Object.assign({ algorithm: 'HS256', noTimestamp: true }, jwtOptions);
43
35
 
44
36
  if (payload.iat) {
45
37
  opts.noTimestamp = false;
@@ -52,10 +44,7 @@ export function JWTServerToken(apiSecret: Secret, jwtOptions: SignOptions = {})
52
44
  server: true,
53
45
  };
54
46
 
55
- const opts: SignOptions = Object.assign(
56
- { algorithm: 'HS256', noTimestamp: true },
57
- jwtOptions,
58
- );
47
+ const opts: SignOptions = Object.assign({ algorithm: 'HS256', noTimestamp: true }, jwtOptions);
59
48
  return jwt.sign(payload, apiSecret, opts);
60
49
  }
61
50
 
@@ -1,14 +1,14 @@
1
1
  import { Secret } from 'jsonwebtoken';
2
2
  import { UserFromToken, JWTServerToken, JWTUserToken } from './signing';
3
3
  import { isFunction } from './utils';
4
- import { TokenOrProvider, UnknownType, UserResponse } from './types';
4
+ import { TokenOrProvider, UR, UserResponse } from './types';
5
5
 
6
6
  /**
7
7
  * TokenManager
8
8
  *
9
9
  * Handles all the operations around user token.
10
10
  */
11
- export class TokenManager<UserType extends UnknownType = UnknownType> {
11
+ export class TokenManager<UserType extends UR = UR> {
12
12
  loadTokenPromise: Promise<string> | null;
13
13
  type: 'static' | 'provider';
14
14
  secret?: Secret;
@@ -40,10 +40,7 @@ export class TokenManager<UserType extends UnknownType = UnknownType> {
40
40
  * @param {TokenOrProvider} tokenOrProvider
41
41
  * @param {UserResponse<UserType>} user
42
42
  */
43
- setTokenOrProvider = async (
44
- tokenOrProvider: TokenOrProvider,
45
- user: UserResponse<UserType>,
46
- ) => {
43
+ setTokenOrProvider = async (tokenOrProvider: TokenOrProvider, user: UserResponse<UserType>) => {
47
44
  this.validateToken(tokenOrProvider, user);
48
45
  this.user = user;
49
46
 
@@ -85,11 +82,7 @@ export class TokenManager<UserType extends UnknownType = UnknownType> {
85
82
  throw new Error('User token can not be empty');
86
83
  }
87
84
 
88
- if (
89
- tokenOrProvider &&
90
- typeof tokenOrProvider !== 'string' &&
91
- !isFunction(tokenOrProvider)
92
- ) {
85
+ if (tokenOrProvider && typeof tokenOrProvider !== 'string' && !isFunction(tokenOrProvider)) {
93
86
  throw new Error('user token should either be a string or a function');
94
87
  }
95
88
 
@@ -98,13 +91,8 @@ export class TokenManager<UserType extends UnknownType = UnknownType> {
98
91
  if (user.anon && tokenOrProvider === '') return;
99
92
 
100
93
  const tokenUserId = UserFromToken(tokenOrProvider);
101
- if (
102
- tokenOrProvider != null &&
103
- (tokenUserId == null || tokenUserId === '' || tokenUserId !== user.id)
104
- ) {
105
- throw new Error(
106
- 'userToken does not have a user_id or is not matching with user.id',
107
- );
94
+ if (tokenOrProvider != null && (tokenUserId == null || tokenUserId === '' || tokenUserId !== user.id)) {
95
+ throw new Error('userToken does not have a user_id or is not matching with user.id');
108
96
  }
109
97
  }
110
98
  };