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.
Files changed (229) hide show
  1. package/dist/cjs/index.browser.js +18869 -13986
  2. package/dist/cjs/index.browser.js.map +4 -4
  3. package/dist/cjs/index.node.js +18888 -13992
  4. package/dist/cjs/index.node.js.map +4 -4
  5. package/dist/esm/index.mjs +18866 -13983
  6. package/dist/esm/index.mjs.map +4 -4
  7. package/dist/types/ChannelPaginatorsOrchestrator.d.ts +130 -0
  8. package/dist/types/EventHandlerPipeline.d.ts +104 -0
  9. package/dist/types/api-client.d.ts +26 -0
  10. package/dist/types/campaign.d.ts +0 -44
  11. package/dist/types/channel.d.ts +436 -480
  12. package/dist/types/channel_batch_updater.d.ts +0 -93
  13. package/dist/types/channel_manager.d.ts +8 -10
  14. package/dist/types/channel_state.d.ts +53 -201
  15. package/dist/types/client.d.ts +440 -1910
  16. package/dist/types/client_state.d.ts +4 -4
  17. package/dist/types/configuration/InstanceConfigurationService.d.ts +26 -0
  18. package/dist/types/configuration/index.d.ts +1 -0
  19. package/dist/types/configuration/types.d.ts +53 -0
  20. package/dist/types/connection.d.ts +49 -41
  21. package/dist/types/connection_fallback.d.ts +6 -4
  22. package/dist/types/constants.d.ts +0 -4
  23. package/dist/types/custom_types.d.ts +2 -0
  24. package/dist/types/errors.d.ts +2 -2
  25. package/dist/types/gen/chat/ChannelApi.d.ts +45 -0
  26. package/dist/types/gen/chat/ChatApi.d.ts +334 -0
  27. package/dist/types/gen/model-decoders/decoders.d.ts +3 -0
  28. package/dist/types/gen/model-decoders/event-decoder-mapping.d.ts +6 -0
  29. package/dist/types/gen/models/index.d.ts +8836 -0
  30. package/dist/types/gen/moderation/ModerationApi.d.ts +50 -0
  31. package/dist/types/gen-imports.d.ts +3 -0
  32. package/dist/types/index.d.ts +5 -5
  33. package/dist/types/insights.d.ts +7 -6
  34. package/dist/types/logger.d.ts +9 -0
  35. package/dist/types/messageComposer/LocationComposer.d.ts +7 -4
  36. package/dist/types/messageComposer/attachmentIdentity.d.ts +2 -2
  37. package/dist/types/messageComposer/attachmentManager.d.ts +8 -4
  38. package/dist/types/messageComposer/configuration/types.d.ts +11 -11
  39. package/dist/types/messageComposer/linkPreviewsManager.d.ts +25 -13
  40. package/dist/types/messageComposer/messageComposer.d.ts +10 -10
  41. package/dist/types/messageComposer/middleware/messageComposer/types.d.ts +3 -3
  42. package/dist/types/messageComposer/middleware/pollComposer/types.d.ts +4 -7
  43. package/dist/types/messageComposer/middleware/textComposer/commandUtils.d.ts +3 -3
  44. package/dist/types/messageComposer/middleware/textComposer/commands.d.ts +7 -7
  45. package/dist/types/messageComposer/middleware/textComposer/mentions.d.ts +218 -4
  46. package/dist/types/messageComposer/middleware/textComposer/types.d.ts +9 -4
  47. package/dist/types/messageComposer/pollComposer.d.ts +5 -4
  48. package/dist/types/messageComposer/textComposer.d.ts +8 -3
  49. package/dist/types/messageComposer/types.d.ts +6 -28
  50. package/dist/types/messageDelivery/MessageDeliveryReporter.d.ts +40 -25
  51. package/dist/types/messageDelivery/MessageReceiptsTracker.d.ts +53 -12
  52. package/dist/types/messageOperations/MessageOperationStatePolicy.d.ts +19 -0
  53. package/dist/types/messageOperations/MessageOperations.d.ts +17 -0
  54. package/dist/types/messageOperations/index.d.ts +3 -0
  55. package/dist/types/messageOperations/types.d.ts +49 -0
  56. package/dist/types/moderation.d.ts +21 -203
  57. package/dist/types/notifications/types.d.ts +8 -7
  58. package/dist/types/offline-support/offline_support_api.d.ts +110 -87
  59. package/dist/types/offline-support/types.d.ts +19 -23
  60. package/dist/types/offline-support/util.d.ts +4 -2
  61. package/dist/types/pagination/FilterBuilder.d.ts +1 -1
  62. package/dist/types/pagination/ItemIndex.d.ts +82 -0
  63. package/dist/types/pagination/cursorDerivation/createdAtAroundPaginationFlags.d.ts +9 -0
  64. package/dist/types/pagination/cursorDerivation/idAroundPaginationFlags.d.ts +6 -0
  65. package/dist/types/pagination/cursorDerivation/index.d.ts +1 -0
  66. package/dist/types/pagination/cursorDerivation/linearPaginationFlags.d.ts +6 -0
  67. package/dist/types/pagination/filterCompiler.d.ts +7 -0
  68. package/dist/types/pagination/index.d.ts +1 -3
  69. package/dist/types/pagination/paginators/BasePaginator.d.ts +709 -0
  70. package/dist/types/pagination/paginators/ChannelPaginator.d.ts +58 -0
  71. package/dist/types/pagination/paginators/MessageIntervalPaginator.d.ts +260 -0
  72. package/dist/types/pagination/paginators/MessagePaginator.d.ts +208 -0
  73. package/dist/types/pagination/paginators/PinnedMessagePaginator.d.ts +39 -0
  74. package/dist/types/pagination/paginators/ReminderPaginator.d.ts +19 -0
  75. package/dist/types/pagination/paginators/UserGroupPaginator.d.ts +24 -0
  76. package/dist/types/pagination/paginators/index.d.ts +7 -0
  77. package/dist/types/pagination/sortCompiler.d.ts +49 -0
  78. package/dist/types/pagination/types.normalization.d.ts +11 -0
  79. package/dist/types/pagination/utility.normalization.d.ts +18 -0
  80. package/dist/types/pagination/utility.queryChannel.d.ts +26 -0
  81. package/dist/types/pagination/utility.search.d.ts +13 -0
  82. package/dist/types/permissions.d.ts +1 -1
  83. package/dist/types/poll.d.ts +29 -32
  84. package/dist/types/poll_manager.d.ts +2 -2
  85. package/dist/types/reminders/Reminder.d.ts +2 -5
  86. package/dist/types/reminders/ReminderManager.d.ts +2 -9
  87. package/dist/types/search/MessageSearchSource.d.ts +4 -4
  88. package/dist/types/search/UserSearchSource.d.ts +1 -1
  89. package/dist/types/search/types.d.ts +3 -3
  90. package/dist/types/segment.d.ts +0 -34
  91. package/dist/types/signing.d.ts +105 -65
  92. package/dist/types/store.d.ts +1 -0
  93. package/dist/types/thread.d.ts +51 -34
  94. package/dist/types/thread_manager.d.ts +3 -3
  95. package/dist/types/token_manager.d.ts +15 -10
  96. package/dist/types/types.d.ts +196 -3384
  97. package/dist/types/utils/FixedSizeQueueCache.d.ts +12 -6
  98. package/dist/types/utils/WithSubscriptions.d.ts +2 -1
  99. package/dist/types/utils/concurrency.d.ts +4 -4
  100. package/dist/types/utils/mergeWith/mergeWith.d.ts +3 -3
  101. package/dist/types/utils/mergeWith/mergeWithCore.d.ts +8 -3
  102. package/dist/types/utils/retryable.d.ts +34 -0
  103. package/dist/types/utils.d.ts +104 -137
  104. package/package.json +7 -3
  105. package/src/ChannelPaginatorsOrchestrator.ts +545 -0
  106. package/src/CooldownTimer.ts +2 -2
  107. package/src/EventHandlerPipeline.ts +228 -0
  108. package/src/LiveLocationManager.ts +12 -11
  109. package/src/api-client.ts +275 -0
  110. package/src/campaign.ts +1 -77
  111. package/src/channel.ts +1262 -1277
  112. package/src/channel_batch_updater.ts +1 -211
  113. package/src/channel_manager.ts +92 -88
  114. package/src/channel_state.ts +144 -1013
  115. package/src/client.ts +888 -3944
  116. package/src/client_state.ts +4 -4
  117. package/src/configuration/InstanceConfigurationService.ts +73 -0
  118. package/src/configuration/index.ts +1 -0
  119. package/src/configuration/types.ts +81 -0
  120. package/src/connection.ts +145 -94
  121. package/src/connection_fallback.ts +29 -27
  122. package/src/constants.ts +1 -5
  123. package/src/custom_types.ts +4 -1
  124. package/src/errors.ts +2 -2
  125. package/src/gen/chat/ChannelApi.ts +275 -0
  126. package/src/gen/chat/ChatApi.ts +2554 -0
  127. package/src/gen/model-decoders/decoders.ts +2680 -0
  128. package/src/gen/model-decoders/event-decoder-mapping.ts +198 -0
  129. package/src/gen/models/index.ts +12673 -0
  130. package/src/gen/moderation/ModerationApi.ts +607 -0
  131. package/src/gen-imports.ts +3 -0
  132. package/src/index.ts +5 -10
  133. package/src/insights.ts +6 -5
  134. package/src/logger.ts +28 -0
  135. package/src/messageComposer/LocationComposer.ts +9 -5
  136. package/src/messageComposer/MessageComposerEffectHandlers.ts +1 -0
  137. package/src/messageComposer/attachmentIdentity.ts +16 -9
  138. package/src/messageComposer/attachmentManager.ts +13 -8
  139. package/src/messageComposer/configuration/types.ts +11 -11
  140. package/src/messageComposer/linkPreviewsManager.ts +9 -10
  141. package/src/messageComposer/messageComposer.ts +57 -53
  142. package/src/messageComposer/middleware/messageComposer/attachments.ts +1 -2
  143. package/src/messageComposer/middleware/messageComposer/cleanData.ts +1 -1
  144. package/src/messageComposer/middleware/messageComposer/compositionValidation.ts +2 -2
  145. package/src/messageComposer/middleware/messageComposer/messageComposerState.ts +2 -2
  146. package/src/messageComposer/middleware/messageComposer/sharedLocation.ts +4 -3
  147. package/src/messageComposer/middleware/messageComposer/textComposer.ts +2 -2
  148. package/src/messageComposer/middleware/messageComposer/types.ts +3 -4
  149. package/src/messageComposer/middleware/messageComposer/userDataInjection.ts +9 -5
  150. package/src/messageComposer/middleware/pollComposer/state.ts +0 -2
  151. package/src/messageComposer/middleware/pollComposer/types.ts +4 -7
  152. package/src/messageComposer/middleware/textComposer/TextComposerMiddlewareExecutor.ts +10 -1
  153. package/src/messageComposer/middleware/textComposer/commandEffects.ts +4 -4
  154. package/src/messageComposer/middleware/textComposer/commandUtils.ts +3 -6
  155. package/src/messageComposer/middleware/textComposer/commands.ts +3 -3
  156. package/src/messageComposer/middleware/textComposer/mentionUtils.ts +1 -1
  157. package/src/messageComposer/middleware/textComposer/mentions.ts +31 -15
  158. package/src/messageComposer/middleware/textComposer/types.ts +9 -4
  159. package/src/messageComposer/pollComposer.ts +6 -8
  160. package/src/messageComposer/textComposer.ts +27 -2
  161. package/src/messageComposer/types.ts +6 -28
  162. package/src/messageDelivery/MessageDeliveryReporter.ts +101 -45
  163. package/src/messageDelivery/MessageReceiptsTracker.ts +294 -21
  164. package/src/messageOperations/MessageOperationStatePolicy.ts +77 -0
  165. package/src/messageOperations/MessageOperations.ts +212 -0
  166. package/src/messageOperations/index.ts +10 -0
  167. package/src/messageOperations/types.ts +64 -0
  168. package/src/moderation.ts +38 -431
  169. package/src/notifications/types.ts +8 -7
  170. package/src/offline-support/offline_support_api.ts +198 -132
  171. package/src/offline-support/offline_sync_manager.ts +17 -7
  172. package/src/offline-support/types.ts +23 -26
  173. package/src/offline-support/util.ts +5 -4
  174. package/src/pagination/FilterBuilder.ts +4 -1
  175. package/src/pagination/ItemIndex.ts +112 -0
  176. package/src/pagination/cursorDerivation/createdAtAroundPaginationFlags.ts +73 -0
  177. package/src/pagination/cursorDerivation/idAroundPaginationFlags.ts +53 -0
  178. package/src/pagination/cursorDerivation/index.ts +1 -0
  179. package/src/pagination/cursorDerivation/linearPaginationFlags.ts +86 -0
  180. package/src/pagination/filterCompiler.ts +196 -0
  181. package/src/pagination/index.ts +1 -3
  182. package/src/pagination/paginators/BasePaginator.ts +2393 -0
  183. package/src/pagination/paginators/ChannelPaginator.ts +427 -0
  184. package/src/pagination/paginators/MessageIntervalPaginator.ts +1073 -0
  185. package/src/pagination/paginators/MessagePaginator.ts +496 -0
  186. package/src/pagination/paginators/PinnedMessagePaginator.ts +105 -0
  187. package/src/pagination/paginators/ReminderPaginator.ts +105 -0
  188. package/src/pagination/paginators/UserGroupPaginator.ts +125 -0
  189. package/src/pagination/paginators/index.ts +7 -0
  190. package/src/pagination/sortCompiler.ts +178 -0
  191. package/src/pagination/types.normalization.ts +14 -0
  192. package/src/pagination/utility.normalization.ts +106 -0
  193. package/src/pagination/utility.queryChannel.ts +84 -0
  194. package/src/pagination/utility.search.ts +80 -0
  195. package/src/permissions.ts +7 -8
  196. package/src/poll.ts +101 -110
  197. package/src/poll_manager.ts +14 -8
  198. package/src/reminders/Reminder.ts +2 -5
  199. package/src/reminders/ReminderManager.ts +19 -21
  200. package/src/search/BaseSearchSource.ts +0 -3
  201. package/src/search/ChannelMemberSearchSource.ts +7 -1
  202. package/src/search/ChannelSearchSource.ts +10 -3
  203. package/src/search/MessageSearchSource.ts +29 -30
  204. package/src/search/UserSearchSource.ts +12 -8
  205. package/src/search/types.ts +3 -4
  206. package/src/segment.ts +1 -95
  207. package/src/signing.ts +107 -67
  208. package/src/store.ts +2 -1
  209. package/src/thread.ts +439 -236
  210. package/src/thread_manager.ts +36 -15
  211. package/src/token_manager.ts +21 -10
  212. package/src/types.ts +375 -4524
  213. package/src/uploadManager.ts +8 -0
  214. package/src/utils/FixedSizeQueueCache.ts +12 -6
  215. package/src/utils/WithSubscriptions.ts +2 -1
  216. package/src/utils/concurrency.ts +4 -4
  217. package/src/utils/mergeWith/mergeWith.ts +3 -3
  218. package/src/utils/mergeWith/mergeWithCore.ts +140 -164
  219. package/src/utils/mergeWith/mergeWithDiff.ts +3 -3
  220. package/src/utils/retryable.ts +117 -0
  221. package/src/utils.ts +133 -524
  222. package/dist/types/events.d.ts +0 -72
  223. package/dist/types/pagination/BasePaginator.d.ts +0 -69
  224. package/dist/types/pagination/ReminderPaginator.d.ts +0 -16
  225. package/dist/types/pagination/UserGroupPaginator.d.ts +0 -21
  226. package/src/events.ts +0 -75
  227. package/src/pagination/BasePaginator.ts +0 -184
  228. package/src/pagination/ReminderPaginator.ts +0 -56
  229. 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 type { APIErrorResponse } from '../types';
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 websocket connection is already active, then call
44
- // the sync api straight away and also execute pending api calls.
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
- console.log('Error in DBSyncManager.init: ', error);
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(cids, lastSyncedAtDate.toISOString());
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
- console.log('An error has occurred while syncing the DB.', e);
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<APIErrorResponse>;
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
- PollResponse,
12
+ PollResponse_old,
13
+ QueryChannelsRequest,
13
14
  ReactionFilters,
14
15
  ReactionResponse,
15
16
  ReactionSort,
16
- ReadResponse,
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
- /** Message to which the reaction is applied. */
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: ChannelAPIResponse[];
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: AppSettingsAPIResponse;
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: PollResponse;
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: ReadResponse[];
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
- /** Message associated with the reaction. */
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
- /** Message to update. */
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
- filters?: ChannelFilters;
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
- * Get the last sync timestamp for a user.
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
- * Get application settings for a user.
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?: Pick<ReactionFilters, 'type'>;
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
- * Delete a pending task by ID.
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
- * Update a pending task by ID.
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?: string;
305
+ truncated_at?: Date;
309
306
  /** Whether to immediately execute the operation. */
310
307
  execute?: boolean;
311
308
  };
312
309
 
313
310
  /**
314
- * Check if a channel exists by ID.
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<ChannelAPIResponse, 'duration'>[] | null>;
373
+ ) => Promise<Omit<ChannelStateResponseFields, 'duration'>[] | null>;
377
374
  getChannelsForQuery: (
378
375
  options: DBGetChannelsForQueryType,
379
- ) => Promise<Omit<ChannelAPIResponse, 'duration'>[] | null>;
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<AppSettingsAPIResponse | null>;
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, LocalMessage, MessageResponse } from '../types';
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
- message: LocalMessage | Partial<MessageResponse>,
16
- ) => !message.attachments?.some((attachment) => !isAttachmentReplayable(attachment));
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<TFilters, TContext extends Record<string, unknown>> = {
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
+ };