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.
- package/README.md +4 -13
- package/dist/browser.es.js +1571 -1071
- package/dist/browser.es.js.map +1 -1
- package/dist/browser.full-bundle.min.js +1 -1
- package/dist/browser.full-bundle.min.js.map +1 -1
- package/dist/browser.js +1571 -1071
- package/dist/browser.js.map +1 -1
- package/dist/index.es.js +1571 -1071
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1571 -1071
- package/dist/index.js.map +1 -1
- package/dist/types/base64.d.ts.map +1 -1
- package/dist/types/channel.d.ts +19 -15
- package/dist/types/channel.d.ts.map +1 -1
- package/dist/types/channel_state.d.ts +2 -2
- package/dist/types/channel_state.d.ts.map +1 -1
- package/dist/types/client.d.ts +18 -39
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/client_state.d.ts +2 -2
- package/dist/types/client_state.d.ts.map +1 -1
- package/dist/types/connection.d.ts +14 -49
- package/dist/types/connection.d.ts.map +1 -1
- package/dist/types/connection_fallback.d.ts +42 -0
- package/dist/types/connection_fallback.d.ts.map +1 -0
- package/dist/types/errors.d.ts +14 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/insights.d.ts +6 -6
- package/dist/types/insights.d.ts.map +1 -1
- package/dist/types/permissions.d.ts.map +1 -1
- package/dist/types/signing.d.ts +3 -3
- package/dist/types/signing.d.ts.map +1 -1
- package/dist/types/token_manager.d.ts +2 -2
- package/dist/types/token_manager.d.ts.map +1 -1
- package/dist/types/types.d.ts +94 -80
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts +12 -2
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/base64.ts +1 -4
- package/src/channel.ts +133 -461
- package/src/channel_state.ts +31 -158
- package/src/client.ts +277 -674
- package/src/client_state.ts +2 -2
- package/src/connection.ts +143 -394
- package/src/connection_fallback.ts +205 -0
- package/src/errors.ts +58 -0
- package/src/insights.ts +15 -23
- package/src/permissions.ts +3 -24
- package/src/signing.ts +6 -17
- package/src/token_manager.ts +6 -18
- package/src/types.ts +268 -504
- package/src/utils.ts +39 -19
- package/CHANGELOG.md +0 -844
|
@@ -0,0 +1,205 @@
|
|
|
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
|
+
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
|
+
throw new Error('connecting already in progress');
|
|
156
|
+
}
|
|
157
|
+
if (this.state === ConnectionState.Connected) {
|
|
158
|
+
throw new Error('already connected and polling');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this._setState(ConnectionState.Connecting);
|
|
162
|
+
this.connectionID = undefined; // connect should be sent with empty connection_id so API creates one
|
|
163
|
+
try {
|
|
164
|
+
const { event } = await this._req<{ event: ConnectionOpen<ChannelType, CommandType, UserType> }>(
|
|
165
|
+
{ json: this.client._buildWSPayload() },
|
|
166
|
+
{ timeout: 8000 }, // 8s
|
|
167
|
+
reconnect,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
this._setState(ConnectionState.Connected);
|
|
171
|
+
this.connectionID = event.connection_id;
|
|
172
|
+
this._poll();
|
|
173
|
+
if (reconnect) {
|
|
174
|
+
this.client.recoverState();
|
|
175
|
+
}
|
|
176
|
+
return event;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
this._setState(ConnectionState.Closed);
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* isHealthy checks if there is a connectionID and connection is in Connected state
|
|
185
|
+
*/
|
|
186
|
+
isHealthy = () => {
|
|
187
|
+
return this.connectionID && this.state === ConnectionState.Connected;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
disconnect = async (timeout = 2000) => {
|
|
191
|
+
removeConnectionEventListeners(this._onlineStatusChanged);
|
|
192
|
+
|
|
193
|
+
this._setState(ConnectionState.Disconnected);
|
|
194
|
+
this.cancelToken?.cancel('disconnect() is called');
|
|
195
|
+
this.cancelToken = undefined;
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await this._req({ close: true }, { timeout }, false);
|
|
199
|
+
this.connectionID = undefined;
|
|
200
|
+
this._log(`disconnect() - Closed connectionID`);
|
|
201
|
+
} catch (err) {
|
|
202
|
+
this._log(`disconnect() - Failed`, { err });
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
}
|
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
|
@@ -24,17 +24,11 @@ export class InsightMetrics {
|
|
|
24
24
|
* @param insightType
|
|
25
25
|
* @param insights
|
|
26
26
|
*/
|
|
27
|
-
export const postInsights = async (
|
|
28
|
-
insightType: InsightTypes,
|
|
29
|
-
insights: Record<string, unknown>,
|
|
30
|
-
) => {
|
|
27
|
+
export const postInsights = async (insightType: InsightTypes, insights: Record<string, unknown>) => {
|
|
31
28
|
const maxAttempts = 3;
|
|
32
29
|
for (let i = 0; i < maxAttempts; i++) {
|
|
33
30
|
try {
|
|
34
|
-
await axios.post(
|
|
35
|
-
`https://chat-insights.getstream.io/insights/${insightType}`,
|
|
36
|
-
insights,
|
|
37
|
-
);
|
|
31
|
+
await axios.post(`https://chat-insights.getstream.io/insights/${insightType}`, insights);
|
|
38
32
|
} catch (e) {
|
|
39
33
|
await sleep((i + 1) * 3000);
|
|
40
34
|
continue;
|
|
@@ -43,10 +37,7 @@ export const postInsights = async (
|
|
|
43
37
|
}
|
|
44
38
|
};
|
|
45
39
|
|
|
46
|
-
export function buildWsFatalInsight(
|
|
47
|
-
connection: StableWSConnection,
|
|
48
|
-
event: Record<string, unknown>,
|
|
49
|
-
) {
|
|
40
|
+
export function buildWsFatalInsight(connection: StableWSConnection, event: Record<string, unknown>) {
|
|
50
41
|
return {
|
|
51
42
|
...event,
|
|
52
43
|
...buildWsBaseInsight(connection),
|
|
@@ -54,25 +45,26 @@ export function buildWsFatalInsight(
|
|
|
54
45
|
}
|
|
55
46
|
|
|
56
47
|
function buildWsBaseInsight(connection: StableWSConnection) {
|
|
48
|
+
const { client } = connection;
|
|
57
49
|
return {
|
|
58
50
|
ready_state: connection.ws?.readyState,
|
|
59
|
-
url: connection._buildUrl(
|
|
60
|
-
api_key:
|
|
61
|
-
start_ts:
|
|
51
|
+
url: connection._buildUrl(),
|
|
52
|
+
api_key: client.key,
|
|
53
|
+
start_ts: client.insightMetrics.connectionStartTimestamp,
|
|
62
54
|
end_ts: new Date().getTime(),
|
|
63
|
-
auth_type:
|
|
64
|
-
token:
|
|
65
|
-
user_id:
|
|
66
|
-
user_details:
|
|
67
|
-
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,
|
|
68
60
|
client_id: connection.connectionID,
|
|
69
61
|
ws_details: connection.ws,
|
|
70
|
-
ws_consecutive_failures:
|
|
71
|
-
ws_total_failures:
|
|
62
|
+
ws_consecutive_failures: client.insightMetrics.wsConsecutiveFailures,
|
|
63
|
+
ws_total_failures: client.insightMetrics.wsTotalFailures,
|
|
72
64
|
request_id: connection.requestID,
|
|
73
65
|
online: typeof navigator !== 'undefined' ? navigator?.onLine : null,
|
|
74
66
|
user_agent: typeof navigator !== 'undefined' ? navigator?.userAgent : null,
|
|
75
|
-
instance_client_id:
|
|
67
|
+
instance_client_id: client.insightMetrics.instanceClientId,
|
|
76
68
|
};
|
|
77
69
|
}
|
|
78
70
|
|
package/src/permissions.ts
CHANGED
|
@@ -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 {
|
|
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 {
|
|
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 } &
|
|
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
|
|
package/src/token_manager.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
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
|
};
|