@rapidmx/activesync-plugin 1.0.0-beta.2 → 1.0.0-beta.3

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 (201) hide show
  1. package/README.md +4 -4
  2. package/dist/lib/BaseDeviceSyncStateRoute.js +32 -0
  3. package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -1
  4. package/dist/lib/BaseEasRoute.js +91 -15
  5. package/dist/lib/BaseEasRoute.js.map +1 -1
  6. package/dist/lib/EasAuditLog.js +56 -0
  7. package/dist/lib/EasAuditLog.js.map +1 -0
  8. package/dist/lib/EasCollectionLease.js +199 -0
  9. package/dist/lib/EasCollectionLease.js.map +1 -0
  10. package/dist/lib/EasCollectionStore.js +102 -0
  11. package/dist/lib/EasCollectionStore.js.map +1 -0
  12. package/dist/lib/EasCollectionSync.js +294 -0
  13. package/dist/lib/EasCollectionSync.js.map +1 -0
  14. package/dist/lib/EasSyncKeyUtils.js +125 -62
  15. package/dist/lib/EasSyncKeyUtils.js.map +1 -1
  16. package/dist/lib/MessageMoveRules.js +100 -0
  17. package/dist/lib/MessageMoveRules.js.map +1 -0
  18. package/dist/lib/MimeHeaderUtils.js +395 -0
  19. package/dist/lib/MimeHeaderUtils.js.map +1 -0
  20. package/dist/lib/RegexPatternUtils.js +30 -0
  21. package/dist/lib/RegexPatternUtils.js.map +1 -0
  22. package/dist/lib/RestapiCompat.js +41 -0
  23. package/dist/lib/RestapiCompat.js.map +1 -0
  24. package/dist/lib/adapters/CalendarSyncAdapter.js +142 -33
  25. package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -1
  26. package/dist/lib/adapters/EmailSyncAdapter.js +114 -27
  27. package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -1
  28. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +2 -1
  29. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -1
  30. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +2 -1
  31. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -1
  32. package/dist/lib/codec/WbxmlDecoder.js +86 -34
  33. package/dist/lib/codec/WbxmlDecoder.js.map +1 -1
  34. package/dist/lib/codec/WbxmlEncoder.js +103 -22
  35. package/dist/lib/codec/WbxmlEncoder.js.map +1 -1
  36. package/dist/lib/commands/ComposeMailCommand.js +105 -31
  37. package/dist/lib/commands/ComposeMailCommand.js.map +1 -1
  38. package/dist/lib/commands/FolderSyncCommand.js +67 -27
  39. package/dist/lib/commands/FolderSyncCommand.js.map +1 -1
  40. package/dist/lib/commands/GetItemEstimateCommand.js +80 -59
  41. package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -1
  42. package/dist/lib/commands/ItemOperationsCommand.js +219 -56
  43. package/dist/lib/commands/ItemOperationsCommand.js.map +1 -1
  44. package/dist/lib/commands/MeetingResponseCommand.js +212 -53
  45. package/dist/lib/commands/MeetingResponseCommand.js.map +1 -1
  46. package/dist/lib/commands/MoveItemsCommand.js +54 -21
  47. package/dist/lib/commands/MoveItemsCommand.js.map +1 -1
  48. package/dist/lib/commands/PingCommand.js +288 -56
  49. package/dist/lib/commands/PingCommand.js.map +1 -1
  50. package/dist/lib/commands/ProvisionCommand.js +34 -17
  51. package/dist/lib/commands/ProvisionCommand.js.map +1 -1
  52. package/dist/lib/commands/ResolveRecipientsCommand.js +43 -5
  53. package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -1
  54. package/dist/lib/commands/SearchCommand.js +92 -17
  55. package/dist/lib/commands/SearchCommand.js.map +1 -1
  56. package/dist/lib/commands/SettingsCommand.js +2 -1
  57. package/dist/lib/commands/SettingsCommand.js.map +1 -1
  58. package/dist/lib/commands/SmartForwardCommand.js +2 -1
  59. package/dist/lib/commands/SmartForwardCommand.js.map +1 -1
  60. package/dist/lib/commands/SmartReplyCommand.js +2 -1
  61. package/dist/lib/commands/SmartReplyCommand.js.map +1 -1
  62. package/dist/lib/commands/SyncCommand.js +481 -244
  63. package/dist/lib/commands/SyncCommand.js.map +1 -1
  64. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +6 -1
  65. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -1
  66. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +3 -1
  67. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -1
  68. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +2 -1
  69. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -1
  70. package/dist/lib/commands/mongo/PingCommandMongo.js +23 -0
  71. package/dist/lib/commands/mongo/PingCommandMongo.js.map +1 -0
  72. package/dist/lib/commands/mongo/SearchCommandMongo.js +3 -1
  73. package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -1
  74. package/dist/lib/commands/mongo/SendMailCommandMongo.js +2 -1
  75. package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -1
  76. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +2 -1
  77. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -1
  78. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +2 -1
  79. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -1
  80. package/dist/lib/commands/mongo/SyncCommandMongo.js +7 -1
  81. package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -1
  82. package/dist/lib/commands/mongo/index.js +1 -0
  83. package/dist/lib/commands/mongo/index.js.map +1 -1
  84. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +6 -1
  85. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -1
  86. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +3 -1
  87. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -1
  88. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +2 -1
  89. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -1
  90. package/dist/lib/commands/sql/PingCommandSQL.js +23 -0
  91. package/dist/lib/commands/sql/PingCommandSQL.js.map +1 -0
  92. package/dist/lib/commands/sql/SearchCommandSQL.js +3 -1
  93. package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -1
  94. package/dist/lib/commands/sql/SendMailCommandSQL.js +2 -1
  95. package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -1
  96. package/dist/lib/commands/sql/SmartForwardCommandSQL.js +2 -1
  97. package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -1
  98. package/dist/lib/commands/sql/SmartReplyCommandSQL.js +2 -1
  99. package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -1
  100. package/dist/lib/commands/sql/SyncCommandSQL.js +7 -1
  101. package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -1
  102. package/dist/lib/commands/sql/index.js +1 -0
  103. package/dist/lib/commands/sql/index.js.map +1 -1
  104. package/dist/lib/index.js +5 -0
  105. package/dist/lib/index.js.map +1 -1
  106. package/dist/lib/jobs/EasDeviceStateCleanupJob.js +44 -2
  107. package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -1
  108. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +4 -0
  109. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -1
  110. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +9 -0
  111. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -1
  112. package/dist/lib/models/EasCollectionChunk.js +2 -0
  113. package/dist/lib/models/EasCollectionChunk.js.map +1 -0
  114. package/dist/lib/models/EasCollectionState.js +2 -0
  115. package/dist/lib/models/EasCollectionState.js.map +1 -0
  116. package/dist/lib/models/mongo/DeviceSyncStateMongo.js +7 -0
  117. package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -1
  118. package/dist/lib/models/mongo/EasCollectionChunkMongo.js +83 -0
  119. package/dist/lib/models/mongo/EasCollectionChunkMongo.js.map +1 -0
  120. package/dist/lib/models/mongo/EasCollectionStateMongo.js +162 -0
  121. package/dist/lib/models/mongo/EasCollectionStateMongo.js.map +1 -0
  122. package/dist/lib/models/sql/DeviceSyncStateSQL.js +7 -0
  123. package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -1
  124. package/dist/lib/models/sql/EasCollectionChunkSQL.js +83 -0
  125. package/dist/lib/models/sql/EasCollectionChunkSQL.js.map +1 -0
  126. package/dist/lib/models/sql/EasCollectionStateSQL.js +162 -0
  127. package/dist/lib/models/sql/EasCollectionStateSQL.js.map +1 -0
  128. package/dist/lib/mongo/EasRouteMongo.js +2 -2
  129. package/dist/lib/mongo/EasRouteMongo.js.map +1 -1
  130. package/dist/lib/mongo.js +2 -0
  131. package/dist/lib/mongo.js.map +1 -1
  132. package/dist/lib/sql/EasRouteSQL.js +2 -2
  133. package/dist/lib/sql/EasRouteSQL.js.map +1 -1
  134. package/dist/lib/sql.js +2 -0
  135. package/dist/lib/sql.js.map +1 -1
  136. package/dist/types/BaseDeviceSyncStateRoute.d.ts +10 -0
  137. package/dist/types/BaseEasRoute.d.ts +15 -4
  138. package/dist/types/EasAuditLog.d.ts +47 -0
  139. package/dist/types/EasCollectionLease.d.ts +60 -0
  140. package/dist/types/EasCollectionStore.d.ts +51 -0
  141. package/dist/types/EasCollectionSync.d.ts +96 -0
  142. package/dist/types/EasCommandHandler.d.ts +7 -4
  143. package/dist/types/EasSyncKeyUtils.d.ts +73 -42
  144. package/dist/types/MessageMoveRules.d.ts +45 -0
  145. package/dist/types/MimeHeaderUtils.d.ts +73 -0
  146. package/dist/types/RegexPatternUtils.d.ts +14 -0
  147. package/dist/types/RestapiCompat.d.ts +24 -0
  148. package/dist/types/adapters/CalendarSyncAdapter.d.ts +47 -18
  149. package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +18 -2
  150. package/dist/types/adapters/EmailSyncAdapter.d.ts +24 -7
  151. package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +1 -0
  152. package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +1 -0
  153. package/dist/types/codec/WbxmlDecoder.d.ts +39 -0
  154. package/dist/types/codec/WbxmlEncoder.d.ts +36 -1
  155. package/dist/types/commands/ComposeMailCommand.d.ts +33 -15
  156. package/dist/types/commands/FolderSyncCommand.d.ts +4 -1
  157. package/dist/types/commands/GetItemEstimateCommand.d.ts +20 -15
  158. package/dist/types/commands/ItemOperationsCommand.d.ts +29 -4
  159. package/dist/types/commands/MeetingResponseCommand.d.ts +52 -13
  160. package/dist/types/commands/MoveItemsCommand.d.ts +13 -3
  161. package/dist/types/commands/PingCommand.d.ts +72 -14
  162. package/dist/types/commands/ProvisionCommand.d.ts +16 -11
  163. package/dist/types/commands/ResolveRecipientsCommand.d.ts +4 -0
  164. package/dist/types/commands/SearchCommand.d.ts +13 -1
  165. package/dist/types/commands/SyncCommand.d.ts +102 -62
  166. package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +3 -0
  167. package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +2 -0
  168. package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +1 -0
  169. package/dist/types/commands/mongo/PingCommandMongo.d.ts +8 -0
  170. package/dist/types/commands/mongo/SearchCommandMongo.d.ts +2 -0
  171. package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +1 -0
  172. package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +1 -0
  173. package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +1 -0
  174. package/dist/types/commands/mongo/SyncCommandMongo.d.ts +4 -0
  175. package/dist/types/commands/mongo/index.d.ts +1 -0
  176. package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +3 -0
  177. package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +2 -0
  178. package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +1 -0
  179. package/dist/types/commands/sql/PingCommandSQL.d.ts +8 -0
  180. package/dist/types/commands/sql/SearchCommandSQL.d.ts +2 -0
  181. package/dist/types/commands/sql/SendMailCommandSQL.d.ts +1 -0
  182. package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +1 -0
  183. package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +1 -0
  184. package/dist/types/commands/sql/SyncCommandSQL.d.ts +4 -0
  185. package/dist/types/commands/sql/index.d.ts +1 -0
  186. package/dist/types/index.d.ts +5 -0
  187. package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +15 -0
  188. package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +2 -0
  189. package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +4 -0
  190. package/dist/types/models/DeviceSyncState.d.ts +13 -2
  191. package/dist/types/models/EasCollectionChunk.d.ts +20 -0
  192. package/dist/types/models/EasCollectionState.d.ts +79 -0
  193. package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +1 -0
  194. package/dist/types/models/mongo/EasCollectionChunkMongo.d.ts +16 -0
  195. package/dist/types/models/mongo/EasCollectionStateMongo.d.ts +27 -0
  196. package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +1 -0
  197. package/dist/types/models/sql/EasCollectionChunkSQL.d.ts +16 -0
  198. package/dist/types/models/sql/EasCollectionStateSQL.d.ts +27 -0
  199. package/dist/types/mongo.d.ts +2 -0
  200. package/dist/types/sql.d.ts +2 -0
  201. package/package.json +267 -238
@@ -12,8 +12,12 @@ import { DeviceSyncState } from "../models/DeviceSyncState.js";
12
12
  */
13
13
  export declare abstract class EasDeviceStateCleanupJob<D extends DeviceSyncState> extends BackgroundService {
14
14
  protected abstract deviceSyncStateClass: any;
15
+ protected abstract collectionStateClass: any;
16
+ protected abstract collectionChunkClass: any;
15
17
  private _objectFactory?;
16
18
  private deviceSyncStateRepo?;
19
+ private collectionStateRepo?;
20
+ private collectionChunkRepo?;
17
21
  private scheduleExpr;
18
22
  private batchSize;
19
23
  private deviceTtlDays;
@@ -22,5 +26,16 @@ export declare abstract class EasDeviceStateCleanupJob<D extends DeviceSyncState
22
26
  init(): Promise<void>;
23
27
  start(): Promise<void>;
24
28
  stop(): Promise<void> | void;
29
+ /**
30
+ * The query value matching rows whose boolean flag isn't set (`false`, `null` or unset) - used for
31
+ * `remoteWipeRequested` and `blocked`. MongoDB's
32
+ * `$ne: true` already matches a missing/`null` field; `EasDeviceStateCleanupJobSQL` overrides this, since SQL's
33
+ * `!=` never matches `NULL`.
34
+ */
35
+ protected noPendingWipeQueryValue(): any;
25
36
  run(): Promise<void>;
37
+ /** Purges the device's per-collection `Sync` state rows (`EasCollectionState`) before the device row itself,
38
+ * so a forgotten device leaves nothing behind - deleted first, so a failure here leaves the device row in place
39
+ * for the next run to retry. */
40
+ private deleteCollectionStates;
26
41
  }
@@ -2,4 +2,6 @@ import { EasDeviceStateCleanupJob } from "../EasDeviceStateCleanupJob.js";
2
2
  import { DeviceSyncStateMongo } from "../../models/mongo/DeviceSyncStateMongo.js";
3
3
  export declare class EasDeviceStateCleanupJobMongo extends EasDeviceStateCleanupJob<DeviceSyncStateMongo> {
4
4
  protected deviceSyncStateClass: any;
5
+ protected collectionStateClass: any;
6
+ protected collectionChunkClass: any;
5
7
  }
@@ -2,4 +2,8 @@ import { EasDeviceStateCleanupJob } from "../EasDeviceStateCleanupJob.js";
2
2
  import { DeviceSyncStateSQL } from "../../models/sql/DeviceSyncStateSQL.js";
3
3
  export declare class EasDeviceStateCleanupJobSQL extends EasDeviceStateCleanupJob<DeviceSyncStateSQL> {
4
4
  protected deviceSyncStateClass: any;
5
+ protected collectionStateClass: any;
6
+ protected collectionChunkClass: any;
7
+ /** SQL's `column != true` is never true for a `NULL` column, so "no pending wipe" needs an explicit `IS NULL`. */
8
+ protected noPendingWipeQueryValue(): any;
5
9
  }
@@ -10,11 +10,14 @@ export interface DeviceSyncState extends BaseEntity {
10
10
  deviceType: string;
11
11
  /** The EAS provisioning policy key most recently acknowledged by the device. */
12
12
  policyKey?: string;
13
- /** The per-folder EAS `SyncKey` cursor, keyed by `Folder.uid`. */
13
+ /** EAS `SyncKey` cursors kept on the device row - today only the folder hierarchy's (`FolderSync`, under the
14
+ * `"$foldersync"` key). `Sync` collections keep theirs in `EasCollectionState`; entries keyed by a `Folder.uid`
15
+ * are left over from before that and are no longer read. */
14
16
  folderSyncKeys: Record<string, string>;
15
17
  /** The EAS `Class` (`"Email"`, `"Contacts"`, ...) most recently synced for a folder, keyed by `Folder.uid` -
16
18
  * lets a `Sync` request omit `Class` after its first request for a collection, per [MS-ASCMD], without the
17
- * server losing track of which entity type that collection holds. */
19
+ * server losing track of which entity type that collection holds. No longer read or written: superseded by
20
+ * `EasCollectionState.collectionClass`; kept so existing rows still load. */
18
21
  folderCollectionClasses: Record<string, string>;
19
22
  lastSyncAt?: Date;
20
23
  provisioned: boolean;
@@ -27,4 +30,12 @@ export interface DeviceSyncState extends BaseEntity {
27
30
  remoteWipeAccountOnly?: boolean;
28
31
  /** When the device most recently acknowledged a remote wipe request. */
29
32
  remoteWipeAcknowledgedAt?: Date;
33
+ /** `true` once the device has acknowledged a remote wipe: every further request carrying this row's `DeviceId`
34
+ * (including a fresh `Provision`) is refused until an administrator clears the flag
35
+ * (`BaseDeviceSyncStateRoute.unblock`), so a client can't acknowledge the wipe and then simply provision again under
36
+ * the same id. **The block is per `DeviceId`, not per physical device or credential**: `DeviceId` is chosen by the
37
+ * client, so a client that ignores the wipe and sends a different `DeviceId` with the same user's token pairs as a
38
+ * new device. Cutting a device off entirely means revoking the account's tokens/password, which this plugin
39
+ * doesn't do. */
40
+ blocked?: boolean;
30
41
  }
@@ -0,0 +1,20 @@
1
+ import type { BaseEntity } from "@rapidrest/service-core";
2
+ /**
3
+ * One fixed-size slice of the `ServerId`s a device holds for a `Sync` collection (see
4
+ * `EasCollectionState.chunked`). A large collection's held set is split across these rows instead of one array on
5
+ * the state row, so no single document approaches MongoDB's 16 MB limit and a round rewrites only the chunks its
6
+ * own additions/removals touched rather than the whole set.
7
+ *
8
+ * @author Jean-Philippe Steinmetz
9
+ */
10
+ export interface EasCollectionChunk extends BaseEntity {
11
+ /** The caller's own `Mailbox` (the device's account), as on `EasCollectionState`. */
12
+ mailboxUid: string;
13
+ deviceId: string;
14
+ /** The `Folder.uid` of the collection. */
15
+ folderUid: string;
16
+ /** Position of the chunk within the collection - unique per (mailbox, device, folder). */
17
+ chunkIndex: number;
18
+ /** The `ServerId`s this chunk holds (at most `HELD_CHUNK_SIZE`). */
19
+ ids: string[];
20
+ }
@@ -0,0 +1,79 @@
1
+ import type { BaseEntity } from "@rapidrest/service-core";
2
+ /**
3
+ * The sync state of one round of a `Sync` collection, as it stood *before* that round was applied - kept so a
4
+ * client that never received a response (a dropped connection) can retry with the `SyncKey` it still holds, per
5
+ * [MS-ASCMD]'s "the server MUST accept the previous SyncKey" rule, and so re-sent `Add`s are not created twice.
6
+ * Only the round's delta against `serverIds` is kept (`addedIds`/`removedIds`), never a second full copy.
7
+ */
8
+ export interface EasCollectionRound {
9
+ /** The `SyncKey` the client sent for the round - accepting it again replays the round from this state. */
10
+ syncKey: string;
11
+ cursorDate: string;
12
+ cursorUid: string;
13
+ moveCursorDate: string;
14
+ moveCursorUid: string;
15
+ /** Item uids the round added to `serverIds` (removed again when the round is replayed). */
16
+ addedIds: string[];
17
+ /** Item uids the round removed from `serverIds` (restored when the round is replayed). */
18
+ removedIds: string[];
19
+ /** `echoes` as they stood before the round. */
20
+ echoes: Record<string, string>;
21
+ /** `recent` as it stood before the round (absent on rows written before it existed). */
22
+ recent?: Record<string, string>;
23
+ /** `reconcileCursor` as it stood before the round. */
24
+ reconcileCursor?: string;
25
+ /** Each client `ClientId` with the `ServerId` created for it during the round, so a replayed `Add` is answered with
26
+ * the same item instead of creating a duplicate. A list rather than a map: `ClientId` is client-chosen text, never
27
+ * safe as a document/object key. */
28
+ clientIds: {
29
+ clientId: string;
30
+ serverId: string;
31
+ }[];
32
+ }
33
+ /**
34
+ * Per-device state of one EAS `Sync` collection (a folder), separate from `DeviceSyncState` so that concurrent
35
+ * `Sync` requests for different folders of one device never contend for the same row, and so a large item set
36
+ * never bloats the device row every other command reads.
37
+ *
38
+ * @author Jean-Philippe Steinmetz
39
+ */
40
+ export interface EasCollectionState extends BaseEntity {
41
+ /** The caller's own `Mailbox` (the device's account) - not necessarily the folder's owner for a shared folder. */
42
+ mailboxUid: string;
43
+ deviceId: string;
44
+ /** The `Folder.uid` this collection syncs (the EAS `CollectionId`). */
45
+ folderUid: string;
46
+ /** The EAS `Class` (`"Email"`, `"Contacts"`, ...) of the collection, so later requests may omit `Class`. */
47
+ collectionClass: string;
48
+ /** The `SyncKey` most recently issued for this collection. */
49
+ syncKey: string;
50
+ /** Position in the folder's own `(dateModified, uid)` change stream. */
51
+ cursorDate: Date;
52
+ cursorUid: string;
53
+ /** Position in the mailbox-wide stream of items outside this folder, which is how items moved out of the
54
+ * folder (or deleted after being moved) are found and reported as `Delete`s. */
55
+ moveCursorDate: Date;
56
+ moveCursorUid: string;
57
+ /** The `ServerId`s the device currently holds for this collection, while the set is small. Decides `Add` vs
58
+ * `Change` for a changed item and whether a removed item needs a `Delete` at all. Empty once `chunked`. */
59
+ serverIds: string[];
60
+ /** `true` once the held set has outgrown `serverIds` and lives in `EasCollectionChunk` rows instead. Stays set
61
+ * until the collection is restarted with `SyncKey 0`. */
62
+ chunked?: boolean;
63
+ /** Item uid -> `dateModified` (ISO) of the rows the folder stream processed within the overlap window before
64
+ * the cursor. The folder stream re-reads that window every round (a row committed out of order by another
65
+ * replica can carry a timestamp the cursor already passed), and a row still matching its entry here was
66
+ * already reported. */
67
+ recent?: Record<string, string>;
68
+ /** The last held `ServerId` checked against the store by the periodic reconcile (`""` = start over), which
69
+ * finds items that vanished without a trace in either change stream (hard purges). */
70
+ reconcileCursor?: string;
71
+ /** Item uid -> the `dateModified` (ISO) the device's own write left on it. A changed row whose `dateModified`
72
+ * still equals this value is the device's own `Add`/`Change` and is not echoed back. Entries are pruned once
73
+ * `cursorDate` passes them. */
74
+ echoes: Record<string, string>;
75
+ /** The `FilterType` the collection was synced with (`undefined` = no filter). */
76
+ filterType?: string;
77
+ /** See `EasCollectionRound`. */
78
+ previous?: EasCollectionRound;
79
+ }
@@ -18,5 +18,6 @@ export declare class DeviceSyncStateMongo extends BaseMongoEntity implements Dev
18
18
  remoteWipeRequested?: boolean;
19
19
  remoteWipeAccountOnly?: boolean;
20
20
  remoteWipeAcknowledgedAt?: Date;
21
+ blocked?: boolean;
21
22
  constructor(other?: Partial<DeviceSyncStateMongo>);
22
23
  }
@@ -0,0 +1,16 @@
1
+ import { BaseMongoEntity } from "@rapidrest/service-core";
2
+ import type { EasCollectionChunk } from "../EasCollectionChunk.js";
3
+ /**
4
+ * Implementation of the `EasCollectionChunk` interface for storage in a MongoDB database. If SQL is desired,
5
+ * please use `models.sql.EasCollectionChunkSQL` instead.
6
+ *
7
+ * @author Jean-Philippe Steinmetz
8
+ */
9
+ export declare class EasCollectionChunkMongo extends BaseMongoEntity implements EasCollectionChunk {
10
+ mailboxUid: string;
11
+ deviceId: string;
12
+ folderUid: string;
13
+ chunkIndex: number;
14
+ ids: string[];
15
+ constructor(other?: Partial<EasCollectionChunkMongo>);
16
+ }
@@ -0,0 +1,27 @@
1
+ import { BaseMongoEntity } from "@rapidrest/service-core";
2
+ import type { EasCollectionRound, EasCollectionState } from "../EasCollectionState.js";
3
+ /**
4
+ * Implementation of the `EasCollectionState` interface for storage in a MongoDB database. If SQL is desired,
5
+ * please use `models.sql.EasCollectionStateSQL` instead.
6
+ *
7
+ * @author Jean-Philippe Steinmetz
8
+ */
9
+ export declare class EasCollectionStateMongo extends BaseMongoEntity implements EasCollectionState {
10
+ mailboxUid: string;
11
+ deviceId: string;
12
+ folderUid: string;
13
+ collectionClass: string;
14
+ syncKey: string;
15
+ cursorDate: Date;
16
+ cursorUid: string;
17
+ moveCursorDate: Date;
18
+ moveCursorUid: string;
19
+ serverIds: string[];
20
+ echoes: Record<string, string>;
21
+ chunked?: boolean;
22
+ recent?: Record<string, string>;
23
+ reconcileCursor?: string;
24
+ filterType?: string;
25
+ previous?: EasCollectionRound;
26
+ constructor(other?: Partial<EasCollectionStateMongo>);
27
+ }
@@ -18,5 +18,6 @@ export declare class DeviceSyncStateSQL extends BaseEntity implements DeviceSync
18
18
  remoteWipeRequested?: boolean;
19
19
  remoteWipeAccountOnly?: boolean;
20
20
  remoteWipeAcknowledgedAt?: Date;
21
+ blocked?: boolean;
21
22
  constructor(other?: Partial<DeviceSyncStateSQL>);
22
23
  }
@@ -0,0 +1,16 @@
1
+ import { BaseEntity } from "@rapidrest/service-core";
2
+ import type { EasCollectionChunk } from "../EasCollectionChunk.js";
3
+ /**
4
+ * Implementation of the `EasCollectionChunk` interface for storage in a SQL database. If MongoDB is desired,
5
+ * please use `models.mongo.EasCollectionChunkMongo` instead.
6
+ *
7
+ * @author Jean-Philippe Steinmetz
8
+ */
9
+ export declare class EasCollectionChunkSQL extends BaseEntity implements EasCollectionChunk {
10
+ mailboxUid: string;
11
+ deviceId: string;
12
+ folderUid: string;
13
+ chunkIndex: number;
14
+ ids: string[];
15
+ constructor(other?: Partial<EasCollectionChunkSQL>);
16
+ }
@@ -0,0 +1,27 @@
1
+ import { BaseEntity } from "@rapidrest/service-core";
2
+ import type { EasCollectionRound, EasCollectionState } from "../EasCollectionState.js";
3
+ /**
4
+ * Implementation of the `EasCollectionState` interface for storage in a SQL database. If MongoDB is desired,
5
+ * please use `models.mongo.EasCollectionStateMongo` instead.
6
+ *
7
+ * @author Jean-Philippe Steinmetz
8
+ */
9
+ export declare class EasCollectionStateSQL extends BaseEntity implements EasCollectionState {
10
+ mailboxUid: string;
11
+ deviceId: string;
12
+ folderUid: string;
13
+ collectionClass: string;
14
+ syncKey: string;
15
+ cursorDate: Date;
16
+ cursorUid: string;
17
+ moveCursorDate: Date;
18
+ moveCursorUid: string;
19
+ serverIds: string[];
20
+ echoes: Record<string, string>;
21
+ chunked?: boolean;
22
+ recent?: Record<string, string>;
23
+ reconcileCursor?: string;
24
+ filterType?: string;
25
+ previous?: EasCollectionRound;
26
+ constructor(other?: Partial<EasCollectionStateSQL>);
27
+ }
@@ -7,4 +7,6 @@
7
7
  export { EasRouteMongo } from "./mongo/EasRouteMongo.js";
8
8
  export { DeviceSyncStateRouteMongo } from "./mongo/DeviceSyncStateRouteMongo.js";
9
9
  export { DeviceSyncStateMongo } from "./models/mongo/DeviceSyncStateMongo.js";
10
+ export { EasCollectionStateMongo } from "./models/mongo/EasCollectionStateMongo.js";
11
+ export { EasCollectionChunkMongo } from "./models/mongo/EasCollectionChunkMongo.js";
10
12
  export { EasDeviceStateCleanupJobMongo } from "./jobs/mongo/EasDeviceStateCleanupJobMongo.js";
@@ -7,4 +7,6 @@
7
7
  export { EasRouteSQL } from "./sql/EasRouteSQL.js";
8
8
  export { DeviceSyncStateRouteSQL } from "./sql/DeviceSyncStateRouteSQL.js";
9
9
  export { DeviceSyncStateSQL } from "./models/sql/DeviceSyncStateSQL.js";
10
+ export { EasCollectionStateSQL } from "./models/sql/EasCollectionStateSQL.js";
11
+ export { EasCollectionChunkSQL } from "./models/sql/EasCollectionChunkSQL.js";
10
12
  export { EasDeviceStateCleanupJobSQL } from "./jobs/sql/EasDeviceStateCleanupJobSQL.js";