stream-chat 9.50.3 → 10.0.0-rc.2
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/dist/cjs/index.browser.js +19798 -14472
- package/dist/cjs/index.browser.js.map +4 -4
- package/dist/cjs/index.node.js +19817 -14482
- package/dist/cjs/index.node.js.map +4 -4
- package/dist/esm/index.mjs +19795 -14469
- package/dist/esm/index.mjs.map +4 -4
- package/dist/types/ChannelManager.d.ts +221 -0
- package/dist/types/EventHandlerPipeline.d.ts +104 -0
- package/dist/types/api-client.d.ts +26 -0
- package/dist/types/campaign.d.ts +0 -44
- package/dist/types/channel.d.ts +454 -479
- package/dist/types/channel_batch_updater.d.ts +0 -93
- package/dist/types/channel_state.d.ts +53 -201
- package/dist/types/client.d.ts +451 -1920
- package/dist/types/client_state.d.ts +4 -4
- package/dist/types/configuration/InstanceConfigurationService.d.ts +26 -0
- package/dist/types/configuration/index.d.ts +1 -0
- package/dist/types/configuration/types.d.ts +53 -0
- package/dist/types/connection.d.ts +49 -41
- package/dist/types/connection_fallback.d.ts +6 -4
- package/dist/types/constants.d.ts +0 -4
- package/dist/types/custom_types.d.ts +2 -0
- package/dist/types/entityStore/EntityStore.d.ts +111 -0
- package/dist/types/entityStore/StoreBackedItemIndex.d.ts +60 -0
- package/dist/types/entityStore/applyReactionLocally.d.ts +26 -0
- package/dist/types/entityStore/index.d.ts +2 -0
- package/dist/types/errors.d.ts +10 -2
- package/dist/types/gen/chat/ChannelApi.d.ts +45 -0
- package/dist/types/gen/chat/ChatApi.d.ts +334 -0
- package/dist/types/gen/model-decoders/decoders.d.ts +3 -0
- package/dist/types/gen/model-decoders/event-decoder-mapping.d.ts +6 -0
- package/dist/types/gen/models/index.d.ts +8836 -0
- package/dist/types/gen/moderation/ModerationApi.d.ts +50 -0
- package/dist/types/gen-imports.d.ts +3 -0
- package/dist/types/index.d.ts +8 -7
- package/dist/types/insights.d.ts +9 -8
- package/dist/types/logger.d.ts +9 -0
- package/dist/types/messageComposer/LocationComposer.d.ts +7 -4
- package/dist/types/messageComposer/attachmentIdentity.d.ts +2 -2
- package/dist/types/messageComposer/attachmentManager.d.ts +8 -4
- package/dist/types/messageComposer/configuration/types.d.ts +11 -11
- package/dist/types/messageComposer/linkPreviewsManager.d.ts +25 -13
- package/dist/types/messageComposer/messageComposer.d.ts +10 -10
- package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +3 -3
- package/dist/types/messageComposer/middleware/pollComposer/types.d.ts +4 -7
- package/dist/types/messageComposer/middleware/textComposer/commandUtils.d.ts +3 -3
- package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +7 -7
- package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +218 -4
- package/dist/types/messageComposer/middleware/textComposer/types.d.ts +9 -4
- package/dist/types/messageComposer/pollComposer.d.ts +5 -4
- package/dist/types/messageComposer/textComposer.d.ts +8 -3
- package/dist/types/messageComposer/types.d.ts +6 -28
- package/dist/types/messageDelivery/MessageDeliveryReporter.d.ts +40 -25
- package/dist/types/messageDelivery/MessageReceiptsTracker.d.ts +53 -12
- package/dist/types/messageOperations/MessageOperationStatePolicy.d.ts +19 -0
- package/dist/types/messageOperations/MessageOperations.d.ts +17 -0
- package/dist/types/messageOperations/index.d.ts +3 -0
- package/dist/types/messageOperations/types.d.ts +49 -0
- package/dist/types/moderation.d.ts +21 -203
- package/dist/types/notifications/types.d.ts +8 -7
- package/dist/types/offline-support/offline_support_api.d.ts +111 -87
- package/dist/types/offline-support/offline_sync_manager.d.ts +0 -7
- package/dist/types/offline-support/types.d.ts +19 -23
- package/dist/types/offline-support/util.d.ts +4 -2
- package/dist/types/pagination/FilterBuilder.d.ts +1 -1
- package/dist/types/pagination/ItemIndex.d.ts +25 -0
- package/dist/types/pagination/cursorDerivation/createdAtAroundPaginationFlags.d.ts +9 -0
- package/dist/types/pagination/cursorDerivation/idAroundPaginationFlags.d.ts +6 -0
- package/dist/types/pagination/cursorDerivation/index.d.ts +1 -0
- package/dist/types/pagination/cursorDerivation/linearPaginationFlags.d.ts +6 -0
- package/dist/types/pagination/filterCompiler.d.ts +7 -0
- package/dist/types/pagination/index.d.ts +1 -3
- package/dist/types/pagination/paginators/BasePaginator.d.ts +848 -0
- package/dist/types/pagination/paginators/ChannelPaginator.d.ts +212 -0
- package/dist/types/pagination/paginators/MessageIntervalPaginator.d.ts +282 -0
- package/dist/types/pagination/paginators/MessagePaginator.d.ts +208 -0
- package/dist/types/pagination/paginators/PinnedMessagePaginator.d.ts +39 -0
- package/dist/types/pagination/paginators/ReminderPaginator.d.ts +19 -0
- package/dist/types/pagination/paginators/UserGroupPaginator.d.ts +24 -0
- package/dist/types/pagination/paginators/index.d.ts +7 -0
- package/dist/types/pagination/paginators/stateThrottling.d.ts +2 -0
- package/dist/types/pagination/sortCompiler.d.ts +49 -0
- package/dist/types/pagination/types.normalization.d.ts +11 -0
- package/dist/types/pagination/utility.normalization.d.ts +18 -0
- package/dist/types/pagination/utility.queryChannel.d.ts +26 -0
- package/dist/types/pagination/utility.search.d.ts +13 -0
- package/dist/types/permissions.d.ts +1 -1
- package/dist/types/poll.d.ts +29 -32
- package/dist/types/poll_manager.d.ts +2 -2
- package/dist/types/reminders/Reminder.d.ts +2 -5
- package/dist/types/reminders/ReminderManager.d.ts +2 -9
- package/dist/types/search/MessageSearchSource.d.ts +4 -4
- package/dist/types/search/UserSearchSource.d.ts +1 -1
- package/dist/types/search/types.d.ts +3 -3
- package/dist/types/segment.d.ts +0 -34
- package/dist/types/signing.d.ts +105 -65
- package/dist/types/store.d.ts +1 -0
- package/dist/types/thread.d.ts +70 -34
- package/dist/types/thread_manager.d.ts +3 -3
- package/dist/types/token_manager.d.ts +15 -10
- package/dist/types/types.d.ts +197 -3404
- package/dist/types/utils/FixedSizeQueueCache.d.ts +12 -6
- package/dist/types/utils/WithSubscriptions.d.ts +2 -1
- package/dist/types/utils/concurrency.d.ts +4 -4
- package/dist/types/utils/mergeWith/mergeWith.d.ts +3 -3
- package/dist/types/utils/mergeWith/mergeWithCore.d.ts +8 -3
- package/dist/types/utils/retryable.d.ts +34 -0
- package/dist/types/utils/throttling/throttle.d.ts +36 -0
- package/dist/types/utils.d.ts +100 -194
- package/package.json +7 -3
- package/src/ChannelManager.ts +724 -0
- package/src/CooldownTimer.ts +2 -2
- package/src/EventHandlerPipeline.ts +228 -0
- package/src/LiveLocationManager.ts +12 -11
- package/src/api-client.ts +275 -0
- package/src/campaign.ts +1 -77
- package/src/channel.ts +1344 -1282
- package/src/channel_batch_updater.ts +1 -211
- package/src/channel_state.ts +144 -1013
- package/src/client.ts +936 -3995
- package/src/client_state.ts +4 -4
- package/src/configuration/InstanceConfigurationService.ts +73 -0
- package/src/configuration/index.ts +1 -0
- package/src/configuration/types.ts +81 -0
- package/src/connection.ts +145 -94
- package/src/connection_fallback.ts +29 -27
- package/src/constants.ts +1 -5
- package/src/custom_types.ts +4 -1
- package/src/entityStore/EntityStore.ts +208 -0
- package/src/entityStore/StoreBackedItemIndex.ts +115 -0
- package/src/entityStore/applyReactionLocally.ts +113 -0
- package/src/entityStore/index.ts +2 -0
- package/src/errors.ts +14 -2
- package/src/gen/chat/ChannelApi.ts +275 -0
- package/src/gen/chat/ChatApi.ts +2554 -0
- package/src/gen/model-decoders/decoders.ts +2680 -0
- package/src/gen/model-decoders/event-decoder-mapping.ts +198 -0
- package/src/gen/models/index.ts +12673 -0
- package/src/gen/moderation/ModerationApi.ts +607 -0
- package/src/gen-imports.ts +3 -0
- package/src/index.ts +11 -12
- package/src/insights.ts +6 -5
- package/src/logger.ts +28 -0
- package/src/messageComposer/LocationComposer.ts +9 -5
- package/src/messageComposer/MessageComposerEffectHandlers.ts +1 -0
- package/src/messageComposer/attachmentIdentity.ts +16 -9
- package/src/messageComposer/attachmentManager.ts +13 -8
- package/src/messageComposer/configuration/types.ts +11 -11
- package/src/messageComposer/linkPreviewsManager.ts +9 -10
- package/src/messageComposer/messageComposer.ts +57 -53
- package/src/messageComposer/middleware/messageComposer/attachments.ts +1 -2
- package/src/messageComposer/middleware/messageComposer/cleanData.ts +1 -1
- package/src/messageComposer/middleware/messageComposer/compositionValidation.ts +2 -2
- package/src/messageComposer/middleware/messageComposer/messageComposerState.ts +2 -2
- package/src/messageComposer/middleware/messageComposer/sharedLocation.ts +4 -3
- package/src/messageComposer/middleware/messageComposer/textComposer.ts +2 -2
- package/src/messageComposer/middleware/messageComposer/types.ts +3 -4
- package/src/messageComposer/middleware/messageComposer/userDataInjection.ts +9 -5
- package/src/messageComposer/middleware/pollComposer/state.ts +0 -2
- package/src/messageComposer/middleware/pollComposer/types.ts +4 -7
- package/src/messageComposer/middleware/textComposer/TextComposerMiddlewareExecutor.ts +10 -1
- package/src/messageComposer/middleware/textComposer/commandEffects.ts +4 -4
- package/src/messageComposer/middleware/textComposer/commandUtils.ts +3 -6
- package/src/messageComposer/middleware/textComposer/commands.ts +3 -3
- package/src/messageComposer/middleware/textComposer/mentionUtils.ts +1 -1
- package/src/messageComposer/middleware/textComposer/mentions.ts +31 -15
- package/src/messageComposer/middleware/textComposer/types.ts +9 -4
- package/src/messageComposer/pollComposer.ts +6 -8
- package/src/messageComposer/textComposer.ts +27 -2
- package/src/messageComposer/types.ts +6 -28
- package/src/messageDelivery/MessageDeliveryReporter.ts +101 -45
- package/src/messageDelivery/MessageReceiptsTracker.ts +294 -21
- package/src/messageOperations/MessageOperationStatePolicy.ts +77 -0
- package/src/messageOperations/MessageOperations.ts +212 -0
- package/src/messageOperations/index.ts +10 -0
- package/src/messageOperations/types.ts +64 -0
- package/src/moderation.ts +38 -431
- package/src/notifications/types.ts +8 -7
- package/src/offline-support/offline_support_api.ts +200 -133
- package/src/offline-support/offline_sync_manager.ts +23 -44
- package/src/offline-support/types.ts +23 -26
- package/src/offline-support/util.ts +5 -4
- package/src/pagination/FilterBuilder.ts +4 -1
- package/src/pagination/ItemIndex.ts +25 -0
- package/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts +73 -0
- package/src/pagination/cursorDerivation/idAroundPaginationFlags.ts +53 -0
- package/src/pagination/cursorDerivation/index.ts +1 -0
- package/src/pagination/cursorDerivation/linearPaginationFlags.ts +86 -0
- package/src/pagination/filterCompiler.ts +196 -0
- package/src/pagination/index.ts +1 -3
- package/src/pagination/paginators/BasePaginator.ts +2859 -0
- package/src/pagination/paginators/ChannelPaginator.ts +729 -0
- package/src/pagination/paginators/MessageIntervalPaginator.ts +1116 -0
- package/src/pagination/paginators/MessagePaginator.ts +508 -0
- package/src/pagination/paginators/PinnedMessagePaginator.ts +108 -0
- package/src/pagination/paginators/ReminderPaginator.ts +107 -0
- package/src/pagination/paginators/UserGroupPaginator.ts +127 -0
- package/src/pagination/paginators/index.ts +7 -0
- package/src/pagination/paginators/stateThrottling.ts +31 -0
- package/src/pagination/sortCompiler.ts +193 -0
- package/src/pagination/types.normalization.ts +14 -0
- package/src/pagination/utility.normalization.ts +106 -0
- package/src/pagination/utility.queryChannel.ts +84 -0
- package/src/pagination/utility.search.ts +80 -0
- package/src/permissions.ts +7 -8
- package/src/poll.ts +101 -110
- package/src/poll_manager.ts +14 -8
- package/src/reminders/Reminder.ts +2 -5
- package/src/reminders/ReminderManager.ts +19 -21
- package/src/search/BaseSearchSource.ts +0 -3
- package/src/search/ChannelMemberSearchSource.ts +7 -1
- package/src/search/ChannelSearchSource.ts +10 -3
- package/src/search/MessageSearchSource.ts +29 -30
- package/src/search/UserSearchSource.ts +12 -8
- package/src/search/types.ts +3 -4
- package/src/segment.ts +1 -95
- package/src/signing.ts +107 -67
- package/src/store.ts +2 -1
- package/src/thread.ts +562 -238
- package/src/thread_manager.ts +37 -16
- package/src/token_manager.ts +21 -10
- package/src/types.ts +364 -4533
- package/src/uploadManager.ts +8 -0
- package/src/utils/FixedSizeQueueCache.ts +12 -6
- package/src/utils/WithSubscriptions.ts +2 -1
- package/src/utils/concurrency.ts +4 -4
- package/src/utils/mergeWith/mergeWith.ts +3 -3
- package/src/utils/mergeWith/mergeWithCore.ts +140 -164
- package/src/utils/mergeWith/mergeWithDiff.ts +3 -3
- package/src/utils/retryable.ts +117 -0
- package/src/utils/throttling/throttle.ts +130 -0
- package/src/utils.ts +208 -754
- package/dist/types/channel_manager.d.ts +0 -122
- package/dist/types/events.d.ts +0 -72
- package/dist/types/pagination/BasePaginator.d.ts +0 -69
- package/dist/types/pagination/ReminderPaginator.d.ts +0 -16
- package/dist/types/pagination/UserGroupPaginator.d.ts +0 -21
- package/src/channel_manager.ts +0 -830
- package/src/events.ts +0 -75
- package/src/pagination/BasePaginator.ts +0 -184
- package/src/pagination/ReminderPaginator.ts +0 -56
- package/src/pagination/UserGroupPaginator.ts +0 -93
package/src/connection.ts
CHANGED
|
@@ -13,9 +13,14 @@ import {
|
|
|
13
13
|
buildWsSuccessAfterFailureInsight,
|
|
14
14
|
postInsights,
|
|
15
15
|
} from './insights';
|
|
16
|
-
import
|
|
16
|
+
import { chatLoggerSystem } from './logger';
|
|
17
|
+
import type { ConnectAPIResponse, ConnectionOpen, EventPayload } from './types';
|
|
17
18
|
import type { StreamChat } from './client';
|
|
18
19
|
import type { APIError } from './errors';
|
|
20
|
+
import { decodeWSEvent } from './gen/model-decoders/event-decoder-mapping';
|
|
21
|
+
import type { WSEvent } from './gen/models';
|
|
22
|
+
|
|
23
|
+
const logger = chatLoggerSystem.getLogger('connection');
|
|
19
24
|
|
|
20
25
|
// Type guards to check WebSocket error type
|
|
21
26
|
const isCloseEvent = (
|
|
@@ -27,15 +32,16 @@ const isErrorEvent = (
|
|
|
27
32
|
): res is WebSocket.ErrorEvent => (res as WebSocket.ErrorEvent).error !== undefined;
|
|
28
33
|
|
|
29
34
|
/**
|
|
30
|
-
*
|
|
35
|
+
* A WS connection that reconnects upon failure.
|
|
36
|
+
*
|
|
31
37
|
* - the browser will sometimes report that you're online or offline
|
|
32
38
|
* - the WS connection can break and fail (there is a 30s health check)
|
|
33
39
|
* - sometimes your WS connection will seem to work while the user is in fact offline
|
|
34
|
-
* - to speed up online/offline detection you can use the window.addEventListener('offline')
|
|
40
|
+
* - to speed up online/offline detection you can use the `window.addEventListener('offline')`
|
|
35
41
|
*
|
|
36
42
|
* There are 4 ways in which a connection can become unhealthy:
|
|
37
|
-
* -
|
|
38
|
-
* -
|
|
43
|
+
* - WebSocket.onerror is called
|
|
44
|
+
* - WebSocket.onclose is called
|
|
39
45
|
* - the health check fails and no event is received for ~40 seconds
|
|
40
46
|
* - the browser indicates the connection is now offline
|
|
41
47
|
*
|
|
@@ -99,18 +105,15 @@ export class StableWSConnection {
|
|
|
99
105
|
addConnectionEventListeners(this.onlineStatusChanged);
|
|
100
106
|
}
|
|
101
107
|
|
|
102
|
-
_log(msg: string, extra: UR = {}, level: LogLevel = 'info') {
|
|
103
|
-
this.client.logger(level, 'connection:' + msg, { tags: ['connection'], ...extra });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
108
|
setClient(client: StreamChat) {
|
|
107
109
|
this.client = client;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* @
|
|
113
|
+
* Connects to the WS URL. The default 15s timeout allows between 2 and 3 tries.
|
|
114
|
+
*
|
|
115
|
+
* @param timeout - Connect timeout in milliseconds (optional, defaults to `15000`).
|
|
116
|
+
* @returns A promise that resolves once the first health check message is received.
|
|
114
117
|
*/
|
|
115
118
|
async connect(timeout = 15000) {
|
|
116
119
|
if (this.isConnecting) {
|
|
@@ -125,8 +128,9 @@ export class StableWSConnection {
|
|
|
125
128
|
const healthCheck = await this._connect();
|
|
126
129
|
this.consecutiveFailures = 0;
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
|
|
131
|
+
logger
|
|
132
|
+
.withExtraTags('connect')
|
|
133
|
+
.info(`Established a WebSocket connection. Health check: ${healthCheck}.`);
|
|
130
134
|
} catch (error: any) {
|
|
131
135
|
this.isHealthy = false;
|
|
132
136
|
this.consecutiveFailures += 1;
|
|
@@ -134,9 +138,11 @@ export class StableWSConnection {
|
|
|
134
138
|
const e = error as APIError;
|
|
135
139
|
|
|
136
140
|
if (e.code === chatCodes.TOKEN_EXPIRED && !this.client.tokenManager.isStatic()) {
|
|
137
|
-
|
|
138
|
-
'connect
|
|
139
|
-
|
|
141
|
+
logger
|
|
142
|
+
.withExtraTags('connect')
|
|
143
|
+
.warn(
|
|
144
|
+
'WebSocket connection failed due to an expired token. Reloading the token and reconnecting.',
|
|
145
|
+
);
|
|
140
146
|
this._reconnect({ refreshToken: true });
|
|
141
147
|
} else if (!e.isWSFailure) {
|
|
142
148
|
// API rejected the connection and we should not retry
|
|
@@ -157,7 +163,8 @@ export class StableWSConnection {
|
|
|
157
163
|
/**
|
|
158
164
|
* _waitForHealthy polls the promise connection to see if its resolved until it times out
|
|
159
165
|
* the default 15s timeout allows between 2~3 tries
|
|
160
|
-
*
|
|
166
|
+
*
|
|
167
|
+
* @param timeout - duration (ms)
|
|
161
168
|
*/
|
|
162
169
|
_waitForHealthy(timeout = 15000) {
|
|
163
170
|
return Promise.race([
|
|
@@ -166,7 +173,6 @@ export class StableWSConnection {
|
|
|
166
173
|
for (let i = 0; i <= timeout; i += interval) {
|
|
167
174
|
try {
|
|
168
175
|
return await this.connectionOpen;
|
|
169
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
170
176
|
} catch (error: any) {
|
|
171
177
|
if (i === timeout) {
|
|
172
178
|
throw new Error(
|
|
@@ -198,7 +204,8 @@ export class StableWSConnection {
|
|
|
198
204
|
}
|
|
199
205
|
|
|
200
206
|
/**
|
|
201
|
-
* Builds and returns the
|
|
207
|
+
* Builds and returns the URL for the WebSocket connection.
|
|
208
|
+
*
|
|
202
209
|
* @private
|
|
203
210
|
* @returns url string
|
|
204
211
|
*/
|
|
@@ -220,11 +227,14 @@ export class StableWSConnection {
|
|
|
220
227
|
};
|
|
221
228
|
|
|
222
229
|
/**
|
|
223
|
-
*
|
|
230
|
+
* Disconnects the connection without attempting to recover.
|
|
224
231
|
*
|
|
232
|
+
* @param timeout - Optional timeout in milliseconds to wait for the close frame from the server.
|
|
225
233
|
*/
|
|
226
234
|
disconnect(timeout?: number) {
|
|
227
|
-
|
|
235
|
+
logger
|
|
236
|
+
.withExtraTags('disconnect')
|
|
237
|
+
.info(`Closing the WebSocket connection for wsID ${this.wsID}.`);
|
|
228
238
|
|
|
229
239
|
this.wsID += 1;
|
|
230
240
|
this.isConnecting = false;
|
|
@@ -255,29 +265,33 @@ export class StableWSConnection {
|
|
|
255
265
|
if (ws && ws.close && ws.readyState === ws.OPEN) {
|
|
256
266
|
isClosedPromise = new Promise((resolve) => {
|
|
257
267
|
const onclose = (event: WebSocket.CloseEvent) => {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
268
|
+
logger
|
|
269
|
+
.withExtraTags('disconnect')
|
|
270
|
+
.debug(
|
|
271
|
+
`Resolving the close promise ${event ? 'with' : 'without'} a close frame.`,
|
|
272
|
+
{ event },
|
|
273
|
+
);
|
|
262
274
|
resolve();
|
|
263
275
|
};
|
|
264
276
|
|
|
265
277
|
ws.onclose = onclose;
|
|
266
|
-
// In case we don't receive close frame
|
|
278
|
+
// In case we don't receive a close frame from the WebSocket server in time,
|
|
267
279
|
// lets not wait for more than 1 seconds.
|
|
268
280
|
setTimeout(onclose, timeout != null ? timeout : 1000);
|
|
269
281
|
});
|
|
270
282
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
283
|
+
logger
|
|
284
|
+
.withExtraTags('disconnect')
|
|
285
|
+
.debug('Manually closing the connection via client.disconnect().');
|
|
274
286
|
|
|
275
287
|
ws.close(
|
|
276
288
|
chatCodes.WS_CLOSED_SUCCESS,
|
|
277
289
|
'Manually closed connection by calling client.disconnect()',
|
|
278
290
|
);
|
|
279
291
|
} else {
|
|
280
|
-
|
|
292
|
+
logger
|
|
293
|
+
.withExtraTags('disconnect')
|
|
294
|
+
.debug('The WebSocket connection does not exist or is already closed.');
|
|
281
295
|
isClosedPromise = Promise.resolve();
|
|
282
296
|
}
|
|
283
297
|
|
|
@@ -287,9 +301,9 @@ export class StableWSConnection {
|
|
|
287
301
|
}
|
|
288
302
|
|
|
289
303
|
/**
|
|
290
|
-
*
|
|
304
|
+
* Connects to the WS endpoint.
|
|
291
305
|
*
|
|
292
|
-
* @
|
|
306
|
+
* @returns A promise that resolves once the first health check message is received.
|
|
293
307
|
*/
|
|
294
308
|
async _connect() {
|
|
295
309
|
if (
|
|
@@ -302,7 +316,7 @@ export class StableWSConnection {
|
|
|
302
316
|
this.client.insightMetrics.connectionStartTimestamp = new Date().getTime();
|
|
303
317
|
let isTokenReady = false;
|
|
304
318
|
try {
|
|
305
|
-
|
|
319
|
+
logger.withExtraTags('_connect').debug('Waiting for the auth token.');
|
|
306
320
|
await this.client.tokenManager.tokenReady();
|
|
307
321
|
isTokenReady = true;
|
|
308
322
|
} catch (e) {
|
|
@@ -311,13 +325,15 @@ export class StableWSConnection {
|
|
|
311
325
|
|
|
312
326
|
try {
|
|
313
327
|
if (!isTokenReady) {
|
|
314
|
-
|
|
328
|
+
logger
|
|
329
|
+
.withExtraTags('_connect')
|
|
330
|
+
.warn('The token provider failed previously. Retrying.');
|
|
315
331
|
await this.client.tokenManager.loadToken();
|
|
316
332
|
}
|
|
317
333
|
|
|
318
334
|
this._setupConnectionPromise();
|
|
319
335
|
const wsURL = this._buildUrl();
|
|
320
|
-
|
|
336
|
+
logger.withExtraTags('_connect').info(`Connecting to ${wsURL}.`, {
|
|
321
337
|
wsURL,
|
|
322
338
|
requestID: this.requestID,
|
|
323
339
|
});
|
|
@@ -343,10 +359,11 @@ export class StableWSConnection {
|
|
|
343
359
|
}
|
|
344
360
|
return response;
|
|
345
361
|
}
|
|
346
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
347
362
|
} catch (error: any) {
|
|
348
363
|
this.isConnecting = false;
|
|
349
|
-
|
|
364
|
+
logger
|
|
365
|
+
.withExtraTags('_connect')
|
|
366
|
+
.warn('An error occurred while connecting.', { error });
|
|
350
367
|
if (this.client.options.enableInsights) {
|
|
351
368
|
this.client.insightMetrics.wsConsecutiveFailures++;
|
|
352
369
|
this.client.insightMetrics.wsTotalFailures++;
|
|
@@ -362,21 +379,22 @@ export class StableWSConnection {
|
|
|
362
379
|
}
|
|
363
380
|
|
|
364
381
|
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* @param {{ interval?: number; refreshToken?: boolean }} options Following options are available
|
|
382
|
+
* Retries the connection to the WS endpoint.
|
|
368
383
|
*
|
|
369
|
-
*
|
|
370
|
-
* -
|
|
384
|
+
* @param options - Reconnect options.
|
|
385
|
+
* @param options.interval - Number of milliseconds to wait before reconnecting.
|
|
386
|
+
* @param options.refreshToken - Reload/refresh the user token before attempting to reconnect.
|
|
371
387
|
*/
|
|
372
388
|
async _reconnect(
|
|
373
389
|
options: { interval?: number; refreshToken?: boolean } = {},
|
|
374
390
|
): Promise<void> {
|
|
375
|
-
|
|
391
|
+
logger.withExtraTags('_reconnect').info('Initiating a reconnect.');
|
|
376
392
|
|
|
377
393
|
// only allow 1 connection at the time
|
|
378
394
|
if (this.isConnecting || this.isHealthy) {
|
|
379
|
-
|
|
395
|
+
logger
|
|
396
|
+
.withExtraTags('_reconnect')
|
|
397
|
+
.debug('Aborting reconnect: already connecting or healthy (check 1).');
|
|
380
398
|
return;
|
|
381
399
|
}
|
|
382
400
|
|
|
@@ -392,16 +410,22 @@ export class StableWSConnection {
|
|
|
392
410
|
// Check once again if by some other call to _reconnect is active or connection is
|
|
393
411
|
// already restored, then no need to proceed.
|
|
394
412
|
if (this.isConnecting || this.isHealthy) {
|
|
395
|
-
|
|
413
|
+
logger
|
|
414
|
+
.withExtraTags('_reconnect')
|
|
415
|
+
.debug('Aborting reconnect: already connecting or healthy (check 2).');
|
|
396
416
|
return;
|
|
397
417
|
}
|
|
398
418
|
|
|
399
419
|
if (this.isDisconnected && this.client.options.enableWSFallback) {
|
|
400
|
-
|
|
420
|
+
logger
|
|
421
|
+
.withExtraTags('_reconnect')
|
|
422
|
+
.debug('Aborting reconnect: disconnect() was called.');
|
|
401
423
|
return;
|
|
402
424
|
}
|
|
403
425
|
|
|
404
|
-
|
|
426
|
+
logger
|
|
427
|
+
.withExtraTags('_reconnect')
|
|
428
|
+
.info('Destroying the current WebSocket connection.');
|
|
405
429
|
|
|
406
430
|
// cleanup the old connection
|
|
407
431
|
this._destroyCurrentWSConnection();
|
|
@@ -412,12 +436,11 @@ export class StableWSConnection {
|
|
|
412
436
|
|
|
413
437
|
try {
|
|
414
438
|
await this._connect();
|
|
415
|
-
|
|
439
|
+
logger.withExtraTags('_reconnect').debug('Waiting for the recover callback.');
|
|
416
440
|
await this.client.recoverState();
|
|
417
|
-
|
|
441
|
+
logger.withExtraTags('_reconnect').debug('Finished the recover callback.');
|
|
418
442
|
|
|
419
443
|
this.consecutiveFailures = 0;
|
|
420
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
421
444
|
} catch (error: any) {
|
|
422
445
|
this.isHealthy = false;
|
|
423
446
|
this.consecutiveFailures += 1;
|
|
@@ -425,60 +448,71 @@ export class StableWSConnection {
|
|
|
425
448
|
error.code === chatCodes.TOKEN_EXPIRED &&
|
|
426
449
|
!this.client.tokenManager.isStatic()
|
|
427
450
|
) {
|
|
428
|
-
|
|
429
|
-
'_reconnect
|
|
430
|
-
|
|
451
|
+
logger
|
|
452
|
+
.withExtraTags('_reconnect')
|
|
453
|
+
.warn(
|
|
454
|
+
'WebSocket connection failed due to an expired token. Reloading the token and reconnecting.',
|
|
455
|
+
);
|
|
431
456
|
|
|
432
457
|
return this._reconnect({ refreshToken: true });
|
|
433
458
|
}
|
|
434
459
|
|
|
435
460
|
// reconnect on WS failures, don't reconnect if there is a code bug
|
|
436
461
|
if (error.isWSFailure) {
|
|
437
|
-
|
|
462
|
+
logger
|
|
463
|
+
.withExtraTags('_reconnect')
|
|
464
|
+
.warn('WebSocket connection failed. Retrying the reconnect.');
|
|
438
465
|
|
|
439
466
|
this._reconnect();
|
|
440
467
|
}
|
|
441
468
|
}
|
|
442
|
-
|
|
469
|
+
logger.withExtraTags('_reconnect').debug('Reconnect attempt finished.');
|
|
443
470
|
}
|
|
444
471
|
|
|
445
472
|
/**
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
* @param {Event} event Event with type online or offline
|
|
473
|
+
* Called when the browser connects or disconnects from the internet.
|
|
449
474
|
*
|
|
475
|
+
* @param event - The DOM event whose `type` is `'online'` or `'offline'`.
|
|
450
476
|
*/
|
|
451
477
|
onlineStatusChanged = (event: Event) => {
|
|
452
478
|
if (event.type === 'offline') {
|
|
453
479
|
// mark the connection as down
|
|
454
|
-
|
|
480
|
+
logger
|
|
481
|
+
.withExtraTags('onlineStatusChanged')
|
|
482
|
+
.info('Network status changed to offline.');
|
|
455
483
|
this._setHealth(false);
|
|
456
484
|
} else if (event.type === 'online') {
|
|
457
485
|
// retry right now...
|
|
458
486
|
// We check this.isHealthy, not sure if it's always
|
|
459
487
|
// smart to create a new WS connection if the old one is still up and running.
|
|
460
488
|
// it's possible we didn't miss any messages, so this process is just expensive and not needed.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
489
|
+
logger
|
|
490
|
+
.withExtraTags('onlineStatusChanged')
|
|
491
|
+
.info(`Network status changed to online. isHealthy: ${this.isHealthy}.`);
|
|
464
492
|
if (!this.isHealthy) {
|
|
465
493
|
this._reconnect({ interval: 10 });
|
|
466
494
|
}
|
|
467
495
|
}
|
|
468
496
|
};
|
|
469
497
|
|
|
470
|
-
onopen = (
|
|
471
|
-
if (this.wsID !==
|
|
498
|
+
onopen = (wsId: number) => {
|
|
499
|
+
if (this.wsID !== wsId) return;
|
|
472
500
|
|
|
473
|
-
|
|
501
|
+
logger.withExtraTags('onopen').debug('WebSocket onopen callback fired.', {
|
|
502
|
+
wsID: wsId,
|
|
503
|
+
});
|
|
474
504
|
};
|
|
475
505
|
|
|
476
|
-
onmessage = (
|
|
477
|
-
if (this.wsID !==
|
|
506
|
+
onmessage = (wsId: number, event: WebSocket.MessageEvent) => {
|
|
507
|
+
if (this.wsID !== wsId) return;
|
|
478
508
|
|
|
479
|
-
|
|
509
|
+
logger.withExtraTags('onmessage').trace('WebSocket onmessage callback fired.', {
|
|
510
|
+
event,
|
|
511
|
+
wsID: wsId,
|
|
512
|
+
});
|
|
480
513
|
if (typeof event.data !== 'string') return;
|
|
481
514
|
const data = JSON.parse(event.data);
|
|
515
|
+
const decodedData = decodeWSEvent(data) as WSEvent;
|
|
482
516
|
|
|
483
517
|
// we wait till the first message before we consider the connection open..
|
|
484
518
|
// the reason for this is that auth errors and similar errors trigger a ws.onopen and immediately
|
|
@@ -490,7 +524,7 @@ export class StableWSConnection {
|
|
|
490
524
|
return;
|
|
491
525
|
}
|
|
492
526
|
|
|
493
|
-
this.resolvePromise?.(
|
|
527
|
+
this.resolvePromise?.(decodedData as EventPayload<'health.check'>);
|
|
494
528
|
this._setHealth(true);
|
|
495
529
|
}
|
|
496
530
|
|
|
@@ -501,14 +535,19 @@ export class StableWSConnection {
|
|
|
501
535
|
this.scheduleNextPing();
|
|
502
536
|
}
|
|
503
537
|
|
|
504
|
-
this.client.dispatchEvent(
|
|
538
|
+
this.client.dispatchEvent(decodedData);
|
|
505
539
|
this.scheduleConnectionCheck();
|
|
506
540
|
};
|
|
507
541
|
|
|
508
|
-
onclose = (
|
|
509
|
-
if (this.wsID !==
|
|
542
|
+
onclose = (wsId: number, event: WebSocket.CloseEvent) => {
|
|
543
|
+
if (this.wsID !== wsId) return;
|
|
510
544
|
|
|
511
|
-
|
|
545
|
+
logger
|
|
546
|
+
.withExtraTags('onclose')
|
|
547
|
+
.debug(`WebSocket onclose callback fired with code ${event.code}.`, {
|
|
548
|
+
event,
|
|
549
|
+
wsID: wsId,
|
|
550
|
+
});
|
|
512
551
|
|
|
513
552
|
if (event.code === chatCodes.WS_CLOSED_SUCCESS) {
|
|
514
553
|
// this is a permanent error raised by stream..
|
|
@@ -523,7 +562,9 @@ export class StableWSConnection {
|
|
|
523
562
|
error.target = event.target;
|
|
524
563
|
|
|
525
564
|
this.rejectPromise?.(error);
|
|
526
|
-
|
|
565
|
+
logger
|
|
566
|
+
.withExtraTags('onclose')
|
|
567
|
+
.warn(`The WebSocket connection was rejected: ${event.reason}.`, { event });
|
|
527
568
|
} else {
|
|
528
569
|
this.consecutiveFailures += 1;
|
|
529
570
|
this.totalFailures += 1;
|
|
@@ -532,15 +573,19 @@ export class StableWSConnection {
|
|
|
532
573
|
|
|
533
574
|
this.rejectPromise?.(this._errorFromWSEvent(event));
|
|
534
575
|
|
|
535
|
-
|
|
576
|
+
logger
|
|
577
|
+
.withExtraTags('onclose')
|
|
578
|
+
.warn('The WebSocket connection was closed. Attempting to reconnect.', {
|
|
579
|
+
event,
|
|
580
|
+
});
|
|
536
581
|
|
|
537
582
|
// reconnect if its an abnormal failure
|
|
538
583
|
this._reconnect();
|
|
539
584
|
}
|
|
540
585
|
};
|
|
541
586
|
|
|
542
|
-
onerror = (
|
|
543
|
-
if (this.wsID !==
|
|
587
|
+
onerror = (wsId: number, event: WebSocket.ErrorEvent) => {
|
|
588
|
+
if (this.wsID !== wsId) return;
|
|
544
589
|
|
|
545
590
|
this.consecutiveFailures += 1;
|
|
546
591
|
this.totalFailures += 1;
|
|
@@ -548,17 +593,17 @@ export class StableWSConnection {
|
|
|
548
593
|
this.isConnecting = false;
|
|
549
594
|
|
|
550
595
|
this.rejectPromise?.(this._errorFromWSEvent(event));
|
|
551
|
-
|
|
596
|
+
logger
|
|
597
|
+
.withExtraTags('onerror')
|
|
598
|
+
.warn('The WebSocket connection raised an error.', { event });
|
|
552
599
|
|
|
553
600
|
this._reconnect();
|
|
554
601
|
};
|
|
555
602
|
|
|
556
603
|
/**
|
|
557
|
-
*
|
|
558
|
-
* Broadcasts an event in case the connection status changed.
|
|
559
|
-
*
|
|
560
|
-
* @param {boolean} healthy boolean indicating if the connection is healthy or not
|
|
604
|
+
* Sets the connection to healthy or unhealthy. Broadcasts an event if the connection status changed.
|
|
561
605
|
*
|
|
606
|
+
* @param healthy - Whether the connection is healthy.
|
|
562
607
|
*/
|
|
563
608
|
_setHealth = (healthy: boolean) => {
|
|
564
609
|
if (healthy === this.isHealthy) return;
|
|
@@ -578,8 +623,11 @@ export class StableWSConnection {
|
|
|
578
623
|
};
|
|
579
624
|
|
|
580
625
|
/**
|
|
581
|
-
*
|
|
626
|
+
* Creates an error object for the WS event.
|
|
582
627
|
*
|
|
628
|
+
* @param event - The raw WebSocket close / data / error event.
|
|
629
|
+
* @param isWSFailure - Whether the underlying cause is a WebSocket failure (optional, defaults to `true`).
|
|
630
|
+
* @returns A normalized error describing the WS failure.
|
|
583
631
|
*/
|
|
584
632
|
_errorFromWSEvent = (
|
|
585
633
|
event: WebSocket.CloseEvent | WebSocket.Data | WebSocket.ErrorEvent,
|
|
@@ -601,7 +649,9 @@ export class StableWSConnection {
|
|
|
601
649
|
}
|
|
602
650
|
|
|
603
651
|
// Keeping this `warn` level log, to avoid cluttering of error logs from ws failures.
|
|
604
|
-
|
|
652
|
+
logger
|
|
653
|
+
.withExtraTags('_errorFromWSEvent')
|
|
654
|
+
.warn(`The WebSocket failed with code ${code}.`, { event });
|
|
605
655
|
|
|
606
656
|
const error = new Error(
|
|
607
657
|
`WS failed with code ${code} and reason - ${message}`,
|
|
@@ -621,8 +671,7 @@ export class StableWSConnection {
|
|
|
621
671
|
};
|
|
622
672
|
|
|
623
673
|
/**
|
|
624
|
-
*
|
|
625
|
-
*
|
|
674
|
+
* Removes the current WS connection.
|
|
626
675
|
*/
|
|
627
676
|
_destroyCurrentWSConnection() {
|
|
628
677
|
// increment the ID, meaning we will ignore all messages from the old
|
|
@@ -638,7 +687,7 @@ export class StableWSConnection {
|
|
|
638
687
|
}
|
|
639
688
|
|
|
640
689
|
/**
|
|
641
|
-
*
|
|
690
|
+
* Sets up the `this.connectionOpen` promise.
|
|
642
691
|
*/
|
|
643
692
|
_setupConnectionPromise = () => {
|
|
644
693
|
this.isResolved = false;
|
|
@@ -650,7 +699,7 @@ export class StableWSConnection {
|
|
|
650
699
|
};
|
|
651
700
|
|
|
652
701
|
/**
|
|
653
|
-
* Schedules
|
|
702
|
+
* Schedules the next health check ping for the WebSocket connection.
|
|
654
703
|
*/
|
|
655
704
|
scheduleNextPing = () => {
|
|
656
705
|
if (this.healthCheckTimeoutRef) {
|
|
@@ -660,7 +709,7 @@ export class StableWSConnection {
|
|
|
660
709
|
// 30 seconds is the recommended interval (messenger uses this)
|
|
661
710
|
this.healthCheckTimeoutRef = setTimeout(() => {
|
|
662
711
|
// send the healthcheck.., server replies with a health check event
|
|
663
|
-
const data = [{ type: 'health.check', client_id: this.client.
|
|
712
|
+
const data = [{ type: 'health.check', client_id: this.client.clientId }];
|
|
664
713
|
// try to send on the connection
|
|
665
714
|
try {
|
|
666
715
|
this.ws?.send(JSON.stringify(data));
|
|
@@ -671,9 +720,9 @@ export class StableWSConnection {
|
|
|
671
720
|
};
|
|
672
721
|
|
|
673
722
|
/**
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* to be reconnected.
|
|
723
|
+
* Schedules a check for the time difference between the last received event and now. If the
|
|
724
|
+
* difference is more than 35 seconds, it means our health check logic has failed and the
|
|
725
|
+
* WebSocket needs to be reconnected.
|
|
677
726
|
*/
|
|
678
727
|
scheduleConnectionCheck = () => {
|
|
679
728
|
if (this.connectionCheckTimeoutRef) {
|
|
@@ -686,7 +735,9 @@ export class StableWSConnection {
|
|
|
686
735
|
this.lastEvent &&
|
|
687
736
|
now.getTime() - this.lastEvent.getTime() > this.connectionCheckTimeout
|
|
688
737
|
) {
|
|
689
|
-
|
|
738
|
+
logger
|
|
739
|
+
.withExtraTags('scheduleConnectionCheck')
|
|
740
|
+
.warn('No events received within the health-check window. Reconnecting.');
|
|
690
741
|
this._setHealth(false);
|
|
691
742
|
this._reconnect();
|
|
692
743
|
}
|