stream-chat 9.50.2 → 10.0.0-rc.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.
- package/dist/cjs/index.browser.js +18869 -13986
- package/dist/cjs/index.browser.js.map +4 -4
- package/dist/cjs/index.node.js +18888 -13992
- package/dist/cjs/index.node.js.map +4 -4
- package/dist/esm/index.mjs +18866 -13983
- package/dist/esm/index.mjs.map +4 -4
- package/dist/types/ChannelPaginatorsOrchestrator.d.ts +130 -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 +436 -480
- package/dist/types/channel_batch_updater.d.ts +0 -93
- package/dist/types/channel_manager.d.ts +8 -10
- package/dist/types/channel_state.d.ts +53 -201
- package/dist/types/client.d.ts +440 -1910
- 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/errors.d.ts +2 -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 +5 -5
- package/dist/types/insights.d.ts +7 -6
- 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 +110 -87
- 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 +82 -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 +709 -0
- package/dist/types/pagination/paginators/ChannelPaginator.d.ts +58 -0
- package/dist/types/pagination/paginators/MessageIntervalPaginator.d.ts +260 -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/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 +51 -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 +196 -3384
- 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.d.ts +104 -137
- package/package.json +7 -3
- package/src/ChannelPaginatorsOrchestrator.ts +545 -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 +1262 -1277
- package/src/channel_batch_updater.ts +1 -211
- package/src/channel_manager.ts +92 -88
- package/src/channel_state.ts +144 -1013
- package/src/client.ts +888 -3944
- 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/errors.ts +2 -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 +5 -10
- 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 +198 -132
- package/src/offline-support/offline_sync_manager.ts +17 -7
- 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 +112 -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 +2393 -0
- package/src/pagination/paginators/ChannelPaginator.ts +427 -0
- package/src/pagination/paginators/MessageIntervalPaginator.ts +1073 -0
- package/src/pagination/paginators/MessagePaginator.ts +496 -0
- package/src/pagination/paginators/PinnedMessagePaginator.ts +105 -0
- package/src/pagination/paginators/ReminderPaginator.ts +105 -0
- package/src/pagination/paginators/UserGroupPaginator.ts +125 -0
- package/src/pagination/paginators/index.ts +7 -0
- package/src/pagination/sortCompiler.ts +178 -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 +439 -236
- package/src/thread_manager.ts +36 -15
- package/src/token_manager.ts +21 -10
- package/src/types.ts +375 -4524
- 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.ts +133 -524
- 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/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
|
@@ -3,7 +3,10 @@ import type { StreamChat } from '../client';
|
|
|
3
3
|
import type { AbstractOfflineDB } from './offline_support_api';
|
|
4
4
|
import type { AxiosError } from 'axios';
|
|
5
5
|
import { isAxiosError } from 'axios';
|
|
6
|
-
import
|
|
6
|
+
import { chatLoggerSystem } from '../logger';
|
|
7
|
+
import type { APIError } from '../types';
|
|
8
|
+
|
|
9
|
+
const logger = chatLoggerSystem.getLogger('offline-db');
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Manages synchronization between the local offline database and the Stream backend.
|
|
@@ -40,8 +43,8 @@ export class OfflineDBSyncManager {
|
|
|
40
43
|
*/
|
|
41
44
|
public init = async () => {
|
|
42
45
|
try {
|
|
43
|
-
// If the
|
|
44
|
-
// the sync
|
|
46
|
+
// If the WebSocket connection is already active, then call
|
|
47
|
+
// the sync API straight away and also execute pending API calls.
|
|
45
48
|
// Otherwise wait for the `connection.changed` event.
|
|
46
49
|
if (this.client.user?.id && this.client.wsConnection?.isHealthy) {
|
|
47
50
|
await this.syncAndExecutePendingTasks();
|
|
@@ -69,7 +72,9 @@ export class OfflineDBSyncManager {
|
|
|
69
72
|
},
|
|
70
73
|
);
|
|
71
74
|
} catch (error) {
|
|
72
|
-
|
|
75
|
+
logger
|
|
76
|
+
.withExtraTags('init')
|
|
77
|
+
.error('Failed to initialize the offline DB sync manager.', { error });
|
|
73
78
|
}
|
|
74
79
|
};
|
|
75
80
|
|
|
@@ -159,7 +164,10 @@ export class OfflineDBSyncManager {
|
|
|
159
164
|
// In that case reset the entire DB and start fresh.
|
|
160
165
|
await this.offlineDb.resetDB();
|
|
161
166
|
} else {
|
|
162
|
-
const result = await this.client.sync(
|
|
167
|
+
const result = await this.client.sync({
|
|
168
|
+
channel_cids: cids,
|
|
169
|
+
last_sync_at: lastSyncedAtDate,
|
|
170
|
+
});
|
|
163
171
|
const queryPromises = result.events.map((event) =>
|
|
164
172
|
this.offlineDb.handleEvent({ event, execute: false }),
|
|
165
173
|
);
|
|
@@ -176,14 +184,16 @@ export class OfflineDBSyncManager {
|
|
|
176
184
|
lastSyncedAt: new Date().toString(),
|
|
177
185
|
});
|
|
178
186
|
} catch (e) {
|
|
179
|
-
|
|
187
|
+
logger
|
|
188
|
+
.withExtraTags('syncAndExecutePendingTasks')
|
|
189
|
+
.error('An error occurred while syncing the database.', { error: e });
|
|
180
190
|
|
|
181
191
|
if (isAxiosError(e) && e.code === 'ECONNABORTED') {
|
|
182
192
|
// If the sync was aborted due to timeout, we can simply return
|
|
183
193
|
return;
|
|
184
194
|
}
|
|
185
195
|
|
|
186
|
-
const error = e as AxiosError<
|
|
196
|
+
const error = e as AxiosError<APIError>;
|
|
187
197
|
|
|
188
198
|
if (error.response?.data?.code === 23) {
|
|
189
199
|
return;
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
AppSettingsAPIResponse,
|
|
3
|
-
ChannelAPIResponse,
|
|
4
2
|
ChannelFilters,
|
|
5
3
|
ChannelMemberResponse,
|
|
6
4
|
ChannelOptions,
|
|
7
5
|
ChannelResponse,
|
|
8
6
|
ChannelSort,
|
|
7
|
+
ChannelStateResponseFields,
|
|
9
8
|
DraftResponse,
|
|
9
|
+
GetApplicationResponse,
|
|
10
10
|
LocalMessage,
|
|
11
11
|
MessageResponse,
|
|
12
|
-
|
|
12
|
+
PollResponse_old,
|
|
13
|
+
QueryChannelsRequest,
|
|
13
14
|
ReactionFilters,
|
|
14
15
|
ReactionResponse,
|
|
15
16
|
ReactionSort,
|
|
16
|
-
|
|
17
|
+
ReadStateResponse,
|
|
17
18
|
} from '../types';
|
|
18
19
|
import type { Channel } from '../channel';
|
|
19
20
|
import type { StreamChat } from '../client';
|
|
@@ -26,7 +27,7 @@ export type PrepareBatchDBQueries =
|
|
|
26
27
|
* Options to insert a reaction into a message.
|
|
27
28
|
*/
|
|
28
29
|
export type DBInsertReactionType = {
|
|
29
|
-
/**
|
|
30
|
+
/** MessageRequest to which the reaction is applied. */
|
|
30
31
|
message: MessageResponse | LocalMessage;
|
|
31
32
|
/** The reaction to insert. */
|
|
32
33
|
reaction: ReactionResponse;
|
|
@@ -55,7 +56,7 @@ export type DBUpsertCidsForQueryType = {
|
|
|
55
56
|
*/
|
|
56
57
|
export type DBUpsertChannelsType = {
|
|
57
58
|
/** Array of channel API responses. */
|
|
58
|
-
channels:
|
|
59
|
+
channels: ChannelStateResponseFields[];
|
|
59
60
|
/** Whether to immediately execute the operation. */
|
|
60
61
|
execute?: boolean;
|
|
61
62
|
/** If true, marks that the latest messages are already set. */
|
|
@@ -67,7 +68,7 @@ export type DBUpsertChannelsType = {
|
|
|
67
68
|
*/
|
|
68
69
|
export type DBUpsertAppSettingsType = {
|
|
69
70
|
/** App settings data. */
|
|
70
|
-
appSettings:
|
|
71
|
+
appSettings: GetApplicationResponse;
|
|
71
72
|
/** ID of the user the settings belong to. */
|
|
72
73
|
userId: string;
|
|
73
74
|
/** Whether to immediately execute the operation. */
|
|
@@ -91,7 +92,7 @@ export type DBUpsertUserSyncStatusType = {
|
|
|
91
92
|
*/
|
|
92
93
|
export type DBUpsertPollType = {
|
|
93
94
|
/** Poll data to be stored. */
|
|
94
|
-
poll:
|
|
95
|
+
poll: PollResponse_old;
|
|
95
96
|
/** Whether to immediately execute the operation. */
|
|
96
97
|
execute?: boolean;
|
|
97
98
|
};
|
|
@@ -113,7 +114,7 @@ export type DBUpsertReadsType = {
|
|
|
113
114
|
/** Channel ID. */
|
|
114
115
|
cid: string;
|
|
115
116
|
/** Array of read statuses. */
|
|
116
|
-
reads:
|
|
117
|
+
reads: ReadStateResponse[];
|
|
117
118
|
/** Whether to immediately execute the operation. */
|
|
118
119
|
execute?: boolean;
|
|
119
120
|
};
|
|
@@ -144,7 +145,7 @@ export type DBUpsertMembersType = {
|
|
|
144
145
|
* Options to update a reaction.
|
|
145
146
|
*/
|
|
146
147
|
export type DBUpdateReactionType = {
|
|
147
|
-
/**
|
|
148
|
+
/** MessageRequest associated with the reaction. */
|
|
148
149
|
message: MessageResponse | LocalMessage;
|
|
149
150
|
/** The updated reaction. */
|
|
150
151
|
reaction: ReactionResponse;
|
|
@@ -156,7 +157,7 @@ export type DBUpdateReactionType = {
|
|
|
156
157
|
* Options to update a message.
|
|
157
158
|
*/
|
|
158
159
|
export type DBUpdateMessageType = {
|
|
159
|
-
/**
|
|
160
|
+
/** MessageRequest to update. */
|
|
160
161
|
message: MessageResponse | LocalMessage;
|
|
161
162
|
/** Whether to immediately execute the operation. */
|
|
162
163
|
execute?: boolean;
|
|
@@ -179,15 +180,11 @@ export type DBGetChannelsForQueryType = {
|
|
|
179
180
|
/** ID of the user. */
|
|
180
181
|
userId: string;
|
|
181
182
|
/** Optional filters for channels. */
|
|
182
|
-
|
|
183
|
-
/** Optional full query options for channels. */
|
|
184
|
-
options?: ChannelOptions;
|
|
185
|
-
/** Optional sorting for the channels. */
|
|
186
|
-
sort?: ChannelSort;
|
|
183
|
+
options?: QueryChannelsRequest;
|
|
187
184
|
};
|
|
188
185
|
|
|
189
186
|
/**
|
|
190
|
-
*
|
|
187
|
+
* Payload for retrieving the last sync timestamp for a user.
|
|
191
188
|
*/
|
|
192
189
|
export type DBGetLastSyncedAtType = {
|
|
193
190
|
/** ID of the user. */
|
|
@@ -203,7 +200,7 @@ export type DBGetPendingTasksType = {
|
|
|
203
200
|
};
|
|
204
201
|
|
|
205
202
|
/**
|
|
206
|
-
*
|
|
203
|
+
* Payload for retrieving application settings for a user.
|
|
207
204
|
*/
|
|
208
205
|
export type DBGetAppSettingsType = {
|
|
209
206
|
/** ID of the user. */
|
|
@@ -217,7 +214,7 @@ export type DBGetReactionsType = {
|
|
|
217
214
|
/** ID of the message. */
|
|
218
215
|
messageId: string;
|
|
219
216
|
/** Optional filter to apply to reactions. */
|
|
220
|
-
filters?:
|
|
217
|
+
filters?: ReactionFilters;
|
|
221
218
|
/** Optional sorting for reactions. */
|
|
222
219
|
sort?: ReactionSort;
|
|
223
220
|
/** Optional maximum number of reactions to return. */
|
|
@@ -225,7 +222,7 @@ export type DBGetReactionsType = {
|
|
|
225
222
|
};
|
|
226
223
|
|
|
227
224
|
/**
|
|
228
|
-
*
|
|
225
|
+
* Payload for deleting a pending task by ID.
|
|
229
226
|
*/
|
|
230
227
|
export type DBDeletePendingTaskType = {
|
|
231
228
|
/** ID of the pending task. */
|
|
@@ -233,7 +230,7 @@ export type DBDeletePendingTaskType = {
|
|
|
233
230
|
};
|
|
234
231
|
|
|
235
232
|
/**
|
|
236
|
-
*
|
|
233
|
+
* Payload for updating a pending task by ID.
|
|
237
234
|
*/
|
|
238
235
|
export type DBUpdatePendingTaskType = {
|
|
239
236
|
/** ID of the pending task. */
|
|
@@ -305,13 +302,13 @@ export type DBDeleteMessagesForChannelType = {
|
|
|
305
302
|
/** Channel ID. */
|
|
306
303
|
cid: string;
|
|
307
304
|
/** Timestamp before which messages are deleted. */
|
|
308
|
-
truncated_at?:
|
|
305
|
+
truncated_at?: Date;
|
|
309
306
|
/** Whether to immediately execute the operation. */
|
|
310
307
|
execute?: boolean;
|
|
311
308
|
};
|
|
312
309
|
|
|
313
310
|
/**
|
|
314
|
-
*
|
|
311
|
+
* Payload for checking whether a channel exists by ID.
|
|
315
312
|
*/
|
|
316
313
|
export type DBChannelExistsType = {
|
|
317
314
|
/** Channel ID. */
|
|
@@ -373,15 +370,15 @@ export interface OfflineDBApi {
|
|
|
373
370
|
getDraft: (options: DBGetDraftType) => Promise<DraftResponse | null>;
|
|
374
371
|
getChannels: (
|
|
375
372
|
options: DBGetChannelsType,
|
|
376
|
-
) => Promise<Omit<
|
|
373
|
+
) => Promise<Omit<ChannelStateResponseFields, 'duration'>[] | null>;
|
|
377
374
|
getChannelsForQuery: (
|
|
378
375
|
options: DBGetChannelsForQueryType,
|
|
379
|
-
) => Promise<Omit<
|
|
376
|
+
) => Promise<Omit<ChannelStateResponseFields, 'duration'>[] | null>;
|
|
380
377
|
getAllChannelCids: () => Promise<string[]>;
|
|
381
378
|
getLastSyncedAt: (options: DBGetLastSyncedAtType) => Promise<string | undefined>;
|
|
382
379
|
getAppSettings: (
|
|
383
380
|
options: DBGetAppSettingsType,
|
|
384
|
-
) => Promise<
|
|
381
|
+
) => Promise<GetApplicationResponse | null>;
|
|
385
382
|
getReactions: (options: DBGetReactionsType) => Promise<ReactionResponse[] | null>;
|
|
386
383
|
executeSqlBatch: (queries: ExecuteBatchDBQueriesType) => Promise<unknown>;
|
|
387
384
|
addPendingTask: (task: PendingTask) => Promise<() => Promise<void>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Attachment
|
|
1
|
+
import type { Attachment } from '../types';
|
|
2
2
|
|
|
3
3
|
export const isLocalUrl = (value: string | undefined) =>
|
|
4
4
|
!!value && !value.startsWith('http');
|
|
@@ -11,9 +11,10 @@ export const isAttachmentReplayable = (attachment: Attachment) => {
|
|
|
11
11
|
return !isLocalUrl(attachment.asset_url) && !isLocalUrl(attachment.image_url);
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export const isMessageUpdateReplayable = (
|
|
15
|
-
|
|
16
|
-
) =>
|
|
14
|
+
export const isMessageUpdateReplayable = (minimalMessage: {
|
|
15
|
+
attachments?: Attachment[];
|
|
16
|
+
}) =>
|
|
17
|
+
!minimalMessage.attachments?.some((attachment) => !isAttachmentReplayable(attachment));
|
|
17
18
|
|
|
18
19
|
export const getPendingTaskChannelData = (cid?: string) => {
|
|
19
20
|
if (!cid) {
|
|
@@ -31,7 +31,10 @@ export type FilterBuilderGenerators<
|
|
|
31
31
|
};
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
-
export type FilterBuilderOptions<
|
|
34
|
+
export type FilterBuilderOptions<
|
|
35
|
+
TFilters,
|
|
36
|
+
TContext extends Record<string, unknown> = Record<string, unknown>,
|
|
37
|
+
> = {
|
|
35
38
|
initialFilterConfig?: FilterBuilderGenerators<TFilters, TContext>;
|
|
36
39
|
initialContext?: TContext;
|
|
37
40
|
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export type ItemIndexOptions<T> = {
|
|
2
|
+
getId: (item: T) => string;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The ItemIndex is a canonical, ID-addressable storage layer for domain items.
|
|
7
|
+
*
|
|
8
|
+
* It provides a single source of truth for all items managed by one or more
|
|
9
|
+
* paginators, views, or interval caches. Instead of duplicating objects inside
|
|
10
|
+
* multiple paginated ranges, every item is stored exactly once in the ItemIndex
|
|
11
|
+
* and is referenced by ID from interval windows, caches, or UI layers.
|
|
12
|
+
*
|
|
13
|
+
* ## Purpose
|
|
14
|
+
*
|
|
15
|
+
* Pagination flows (especially those supporting random-access page jumps
|
|
16
|
+
* or “load-around-anchor” requests) require representing discontinuous windows
|
|
17
|
+
* of items. Attempting to store full item objects in every interval causes
|
|
18
|
+
* duplication, inconsistent updates, increased memory usage, and difficult
|
|
19
|
+
* merging logic.
|
|
20
|
+
*
|
|
21
|
+
* The ItemIndex solves this by:
|
|
22
|
+
*
|
|
23
|
+
* - Storing each item exactly once.
|
|
24
|
+
* - Making all intervals store only `itemIds: string[]` in sorted order.
|
|
25
|
+
* - Making paginators read visible items through `itemIndex.get(id)`.
|
|
26
|
+
* - Ensuring that any mutation of an item is immediately visible everywhere.
|
|
27
|
+
*
|
|
28
|
+
* ## Benefits
|
|
29
|
+
*
|
|
30
|
+
* - **Consistency:** Updates propagate automatically because intervals reference
|
|
31
|
+
* items by ID. No need to synchronize multiple arrays of objects.
|
|
32
|
+
* - **Efficiency:** Items are only stored once; intervals are lightweight lists
|
|
33
|
+
* of IDs.
|
|
34
|
+
* - **Scalability:** Supports multiple disjoint intervals (e.g. random jumps),
|
|
35
|
+
* merging of ranges, and multiple independent paginators sharing the same
|
|
36
|
+
* item set.
|
|
37
|
+
* - **Clean separation of concerns:** The paginator manages window boundaries;
|
|
38
|
+
* the ItemIndex manages object identity and update semantics.
|
|
39
|
+
*
|
|
40
|
+
* ## Typical Usage
|
|
41
|
+
*
|
|
42
|
+
* 1. A paginator fetches a page of items from the server.
|
|
43
|
+
* 2. It calls `itemIndex.setMany(fetchedItems)` to update the canonical store.
|
|
44
|
+
* 3. It constructs or updates an interval using the IDs only:
|
|
45
|
+
* `{ itemIds: fetchedItems.map(item => itemIndex.getId(item)) }`
|
|
46
|
+
* 4. The UI renders the active interval’s items using:
|
|
47
|
+
* `interval.itemIds.map(id => itemIndex.get(id))`
|
|
48
|
+
*
|
|
49
|
+
* ## Update Semantics
|
|
50
|
+
*
|
|
51
|
+
* Updates should always be performed through `setOne()` or `setMany()`.
|
|
52
|
+
* This ensures that:
|
|
53
|
+
*
|
|
54
|
+
* - The item object is replaced (immutable semantics).
|
|
55
|
+
* - All consumers reading via ID immediately observe the new value.
|
|
56
|
+
*
|
|
57
|
+
* The ItemIndex does not automatically re-sort intervals; interval or paginator
|
|
58
|
+
* logic may reorder their `itemIds` arrays when necessary.
|
|
59
|
+
*
|
|
60
|
+
* ## Notes
|
|
61
|
+
*
|
|
62
|
+
* - The ItemIndex does not apply filtering or sorting. Those are the paginator’s
|
|
63
|
+
* responsibilities.
|
|
64
|
+
* - The ItemIndex intentionally exposes only minimal CRUD operations to keep it
|
|
65
|
+
* predictable and side-effect-free.
|
|
66
|
+
* - Consumers should treat items as immutable snapshots. If mutation is needed,
|
|
67
|
+
* always create a new item instance and pass it to `setOne()`.
|
|
68
|
+
*
|
|
69
|
+
* @template T The domain item type managed by the index.
|
|
70
|
+
*/
|
|
71
|
+
export class ItemIndex<T> {
|
|
72
|
+
private byId = new Map<string, T>();
|
|
73
|
+
private readonly getId: (item: T) => string;
|
|
74
|
+
|
|
75
|
+
constructor(options: ItemIndexOptions<T>) {
|
|
76
|
+
this.getId = options.getId;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setMany(items: T[]) {
|
|
80
|
+
for (const item of items) {
|
|
81
|
+
this.byId.set(this.getId(item), item);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
setOne(item: T) {
|
|
86
|
+
this.byId.set(this.getId(item), item);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get(id: string): T | undefined {
|
|
90
|
+
return this.byId.get(id);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
has(id: string): boolean {
|
|
94
|
+
return this.byId.has(id);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
remove(id: string) {
|
|
98
|
+
this.byId.delete(id);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
clear() {
|
|
102
|
+
this.byId.clear();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
entries() {
|
|
106
|
+
return [...this.byId.entries()];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
values() {
|
|
110
|
+
return [...this.byId.values()];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { binarySearch } from '../sortCompiler';
|
|
2
|
+
import type { BasePaginator, CursorDeriveContext, PaginationFlags } from '../paginators';
|
|
3
|
+
import { ComparisonResult } from '../types.normalization';
|
|
4
|
+
|
|
5
|
+
export const deriveCreatedAtAroundPaginationFlags = <
|
|
6
|
+
T extends { id: string; created_at: Date },
|
|
7
|
+
Q extends { created_at_around?: Date | string },
|
|
8
|
+
P extends BasePaginator<T, Q>,
|
|
9
|
+
>({
|
|
10
|
+
hasMoreHead,
|
|
11
|
+
hasMoreTail,
|
|
12
|
+
interval,
|
|
13
|
+
page,
|
|
14
|
+
paginator,
|
|
15
|
+
queryShape,
|
|
16
|
+
requestedPageSize,
|
|
17
|
+
}: CursorDeriveContext<T, Q> & { paginator: P }): PaginationFlags => {
|
|
18
|
+
let flags: PaginationFlags = { hasMoreHead, hasMoreTail };
|
|
19
|
+
if (!queryShape?.created_at_around) return flags;
|
|
20
|
+
const createdAtAroundDate = new Date(queryShape.created_at_around);
|
|
21
|
+
const [firstPageItem, lastPageItem] = [page[0], page.slice(-1)[0]];
|
|
22
|
+
|
|
23
|
+
// expect ASC order (from oldest to newest)
|
|
24
|
+
const isAboveHeadBound =
|
|
25
|
+
paginator.sortComparator({ created_at: createdAtAroundDate } as T, lastPageItem) ===
|
|
26
|
+
ComparisonResult.A_PRECEDES_B;
|
|
27
|
+
const isBelowTailBound =
|
|
28
|
+
paginator.sortComparator(firstPageItem, { created_at: createdAtAroundDate } as T) ===
|
|
29
|
+
ComparisonResult.A_PRECEDES_B;
|
|
30
|
+
|
|
31
|
+
const requestedPageSizeNotMet =
|
|
32
|
+
requestedPageSize > interval.itemIds.length && requestedPageSize > page.length;
|
|
33
|
+
const noMoreMessages =
|
|
34
|
+
(requestedPageSize > interval.itemIds.length ||
|
|
35
|
+
interval.itemIds.length >= page.length) &&
|
|
36
|
+
requestedPageSize > page.length;
|
|
37
|
+
|
|
38
|
+
if (isAboveHeadBound) {
|
|
39
|
+
flags.hasMoreHead = false;
|
|
40
|
+
if (requestedPageSizeNotMet) {
|
|
41
|
+
flags.hasMoreTail = false;
|
|
42
|
+
}
|
|
43
|
+
} else if (isBelowTailBound) {
|
|
44
|
+
flags.hasMoreTail = false;
|
|
45
|
+
if (requestedPageSizeNotMet) {
|
|
46
|
+
flags.hasMoreHead = false;
|
|
47
|
+
}
|
|
48
|
+
} else if (noMoreMessages) {
|
|
49
|
+
flags = { hasMoreHead: false, hasMoreTail: false };
|
|
50
|
+
} else {
|
|
51
|
+
const [firstPageMsgIsFirstInSet, lastPageMsgIsLastInSet] = [
|
|
52
|
+
firstPageItem?.id && firstPageItem.id === interval.itemIds[0],
|
|
53
|
+
lastPageItem?.id && lastPageItem.id === interval.itemIds.slice(-1)[0],
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
const midPointByCount = Math.floor(page.length / 2);
|
|
57
|
+
const { insertionIndex } = binarySearch({
|
|
58
|
+
needle: { created_at: createdAtAroundDate } as T,
|
|
59
|
+
length: page.length,
|
|
60
|
+
getItemAt: (index) => page[index],
|
|
61
|
+
compare: (a, b) => a.created_at?.getTime() - b.created_at.getTime(),
|
|
62
|
+
itemIdentityEquals: (a, b) => a.created_at?.getTime() === b.created_at?.getTime(),
|
|
63
|
+
plateauScan: false,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
if (insertionIndex !== -1) {
|
|
67
|
+
if (firstPageMsgIsFirstInSet) flags.hasMoreTail = midPointByCount <= insertionIndex;
|
|
68
|
+
if (lastPageMsgIsLastInSet) flags.hasMoreHead = midPointByCount >= insertionIndex;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return flags;
|
|
73
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CursorDeriveContext, PaginationFlags } from '../paginators';
|
|
2
|
+
|
|
3
|
+
export const deriveIdAroundPaginationFlags = <
|
|
4
|
+
T extends { id: string },
|
|
5
|
+
Q extends { id_around?: string },
|
|
6
|
+
>({
|
|
7
|
+
hasMoreHead,
|
|
8
|
+
hasMoreTail,
|
|
9
|
+
interval,
|
|
10
|
+
page,
|
|
11
|
+
queryShape,
|
|
12
|
+
requestedPageSize,
|
|
13
|
+
}: CursorDeriveContext<T, Q>): PaginationFlags => {
|
|
14
|
+
let flags: PaginationFlags = { hasMoreHead, hasMoreTail };
|
|
15
|
+
if (!queryShape?.id_around) return flags;
|
|
16
|
+
const { id_around } = queryShape;
|
|
17
|
+
|
|
18
|
+
const [firstPageMsg, lastPageMsg] = [page[0], page.slice(-1)[0]];
|
|
19
|
+
const [firstPageMsgIsFirstInSet, lastPageMsgIsLastInSet] = [
|
|
20
|
+
firstPageMsg?.id === interval.itemIds[0],
|
|
21
|
+
lastPageMsg?.id === interval.itemIds.slice(-1)[0],
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const midPoint = Math.floor(page.length / 2);
|
|
25
|
+
const noMoreMessages =
|
|
26
|
+
(requestedPageSize > interval.itemIds.length ||
|
|
27
|
+
interval.itemIds.length >= page.length) &&
|
|
28
|
+
requestedPageSize > page.length;
|
|
29
|
+
|
|
30
|
+
if (noMoreMessages) {
|
|
31
|
+
flags = { hasMoreHead: false, hasMoreTail: false };
|
|
32
|
+
} else if (!page[midPoint]) {
|
|
33
|
+
return flags;
|
|
34
|
+
} else if (page[midPoint].id === id_around) {
|
|
35
|
+
flags = { hasMoreHead: true, hasMoreTail: true };
|
|
36
|
+
} else {
|
|
37
|
+
const halves = [page.slice(0, midPoint), page.slice(midPoint)];
|
|
38
|
+
if (firstPageMsgIsFirstInSet) {
|
|
39
|
+
const targetMsg = halves[0].find((message) => message.id === id_around);
|
|
40
|
+
if (targetMsg) {
|
|
41
|
+
flags.hasMoreTail = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (lastPageMsgIsLastInSet) {
|
|
45
|
+
const targetMsg = halves[1].find((message) => message.id === id_around);
|
|
46
|
+
if (targetMsg) {
|
|
47
|
+
flags.hasMoreHead = false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return flags;
|
|
53
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './createdAtAroundPaginationFlags';
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { CursorDeriveContext, PaginationFlags } from '../paginators';
|
|
2
|
+
import type { MessagePaginationOptions, PaginationOptions } from '../../types';
|
|
3
|
+
|
|
4
|
+
const TAILWARD_QUERY_PROPERTIES: Array<keyof PaginationOptions> = [
|
|
5
|
+
'created_at_before_or_equal',
|
|
6
|
+
'created_at_before',
|
|
7
|
+
'id_lt',
|
|
8
|
+
'id_lte',
|
|
9
|
+
'offset',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
const HEADWARD_QUERY_PROPERTIES: Array<keyof PaginationOptions> = [
|
|
13
|
+
'created_at_after_or_equal',
|
|
14
|
+
'created_at_after',
|
|
15
|
+
'id_gt',
|
|
16
|
+
'id_gte',
|
|
17
|
+
];
|
|
18
|
+
export const deriveLinearPaginationFlags = <
|
|
19
|
+
T extends { id: string; created_at: Date },
|
|
20
|
+
Q extends PaginationOptions,
|
|
21
|
+
>({
|
|
22
|
+
direction,
|
|
23
|
+
hasMoreHead,
|
|
24
|
+
hasMoreTail,
|
|
25
|
+
interval,
|
|
26
|
+
page,
|
|
27
|
+
queryShape,
|
|
28
|
+
requestedPageSize,
|
|
29
|
+
}: CursorDeriveContext<T, Q>): PaginationFlags => {
|
|
30
|
+
const flags: PaginationFlags = { hasMoreHead, hasMoreTail };
|
|
31
|
+
const [firstPageMsg, lastPageMsg] = [page[0], page.slice(-1)[0]];
|
|
32
|
+
const [firstPageMsgIsFirstInSet, lastPageMsgIsLastInSet] = [
|
|
33
|
+
firstPageMsg?.id && firstPageMsg.id === interval.itemIds[0],
|
|
34
|
+
lastPageMsg?.id && lastPageMsg.id === interval.itemIds.slice(-1)[0],
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const containsCursorPaginationProperties =
|
|
38
|
+
!!queryShape &&
|
|
39
|
+
HEADWARD_QUERY_PROPERTIES.concat(TAILWARD_QUERY_PROPERTIES).some(
|
|
40
|
+
(p) => typeof queryShape[p] !== 'undefined',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const queriedMessagesTowardsHead =
|
|
44
|
+
direction === 'headward' ||
|
|
45
|
+
(!!queryShape &&
|
|
46
|
+
HEADWARD_QUERY_PROPERTIES.some((p) => typeof queryShape[p] !== 'undefined'));
|
|
47
|
+
|
|
48
|
+
const queriedMessagesTowardsTail =
|
|
49
|
+
direction === 'tailward' ||
|
|
50
|
+
typeof queryShape === 'undefined' ||
|
|
51
|
+
TAILWARD_QUERY_PROPERTIES.some((p) => typeof queryShape[p] !== 'undefined');
|
|
52
|
+
|
|
53
|
+
const containsNonLinearPaginationProperties =
|
|
54
|
+
!!(queryShape as MessagePaginationOptions)?.id_around ||
|
|
55
|
+
!!(queryShape as MessagePaginationOptions)?.created_at_around;
|
|
56
|
+
|
|
57
|
+
const containsUnrecognizedOptionsOnly =
|
|
58
|
+
!queriedMessagesTowardsHead &&
|
|
59
|
+
!queriedMessagesTowardsTail &&
|
|
60
|
+
!containsNonLinearPaginationProperties;
|
|
61
|
+
|
|
62
|
+
const isFirstPage = !containsCursorPaginationProperties;
|
|
63
|
+
|
|
64
|
+
const hasMore = page.length >= requestedPageSize;
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
typeof queriedMessagesTowardsTail !== 'undefined' ||
|
|
68
|
+
containsUnrecognizedOptionsOnly
|
|
69
|
+
) {
|
|
70
|
+
// Without the isFirstPage branch, a partial first page (e.g. an
|
|
71
|
+
// offline prehydrate returning fewer than `requestedPageSize`) latches `hasMoreTail`
|
|
72
|
+
// to `false` and a subsequent full network first page can never restore it.
|
|
73
|
+
hasMoreTail = isFirstPage ? hasMore : !hasMoreTail ? false : hasMore;
|
|
74
|
+
}
|
|
75
|
+
if (typeof queriedMessagesTowardsHead !== 'undefined') {
|
|
76
|
+
hasMoreHead = !hasMoreHead || isFirstPage ? false : hasMore;
|
|
77
|
+
}
|
|
78
|
+
const pageIsEmpty = page.length === 0;
|
|
79
|
+
|
|
80
|
+
if ((firstPageMsgIsFirstInSet || pageIsEmpty) && typeof hasMoreTail !== 'undefined')
|
|
81
|
+
flags.hasMoreTail = hasMoreTail;
|
|
82
|
+
if ((lastPageMsgIsLastInSet || pageIsEmpty) && typeof hasMoreHead !== 'undefined')
|
|
83
|
+
flags.hasMoreHead = hasMoreHead;
|
|
84
|
+
|
|
85
|
+
return flags;
|
|
86
|
+
};
|