@rapidmx/activesync-plugin 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +57 -0
  3. package/dist/lib/BaseDeviceSyncStateRoute.js +90 -0
  4. package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -0
  5. package/dist/lib/BaseEasRoute.js +213 -0
  6. package/dist/lib/BaseEasRoute.js.map +1 -0
  7. package/dist/lib/CompactDateTime.js +43 -0
  8. package/dist/lib/CompactDateTime.js.map +1 -0
  9. package/dist/lib/EasCommandHandler.js +2 -0
  10. package/dist/lib/EasCommandHandler.js.map +1 -0
  11. package/dist/lib/EasSyncKeyUtils.js +119 -0
  12. package/dist/lib/EasSyncKeyUtils.js.map +1 -0
  13. package/dist/lib/adapters/CalendarSyncAdapter.js +255 -0
  14. package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -0
  15. package/dist/lib/adapters/ContactsSyncAdapter.js +164 -0
  16. package/dist/lib/adapters/ContactsSyncAdapter.js.map +1 -0
  17. package/dist/lib/adapters/EasCollectionSyncAdapter.js +2 -0
  18. package/dist/lib/adapters/EasCollectionSyncAdapter.js.map +1 -0
  19. package/dist/lib/adapters/EmailSyncAdapter.js +269 -0
  20. package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -0
  21. package/dist/lib/adapters/TasksSyncAdapter.js +100 -0
  22. package/dist/lib/adapters/TasksSyncAdapter.js.map +1 -0
  23. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +16 -0
  24. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -0
  25. package/dist/lib/adapters/mongo/index.js +6 -0
  26. package/dist/lib/adapters/mongo/index.js.map +1 -0
  27. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +16 -0
  28. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -0
  29. package/dist/lib/adapters/sql/index.js +6 -0
  30. package/dist/lib/adapters/sql/index.js.map +1 -0
  31. package/dist/lib/codec/WbxmlCodePages.js +706 -0
  32. package/dist/lib/codec/WbxmlCodePages.js.map +1 -0
  33. package/dist/lib/codec/WbxmlDecoder.js +154 -0
  34. package/dist/lib/codec/WbxmlDecoder.js.map +1 -0
  35. package/dist/lib/codec/WbxmlElement.js +31 -0
  36. package/dist/lib/codec/WbxmlElement.js.map +1 -0
  37. package/dist/lib/codec/WbxmlEncoder.js +88 -0
  38. package/dist/lib/codec/WbxmlEncoder.js.map +1 -0
  39. package/dist/lib/commands/ComposeMailCommand.js +187 -0
  40. package/dist/lib/commands/ComposeMailCommand.js.map +1 -0
  41. package/dist/lib/commands/FolderSyncCommand.js +155 -0
  42. package/dist/lib/commands/FolderSyncCommand.js.map +1 -0
  43. package/dist/lib/commands/GetItemEstimateCommand.js +149 -0
  44. package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -0
  45. package/dist/lib/commands/ItemOperationsCommand.js +337 -0
  46. package/dist/lib/commands/ItemOperationsCommand.js.map +1 -0
  47. package/dist/lib/commands/MeetingResponseCommand.js +120 -0
  48. package/dist/lib/commands/MeetingResponseCommand.js.map +1 -0
  49. package/dist/lib/commands/MoveItemsCommand.js +103 -0
  50. package/dist/lib/commands/MoveItemsCommand.js.map +1 -0
  51. package/dist/lib/commands/PingCommand.js +145 -0
  52. package/dist/lib/commands/PingCommand.js.map +1 -0
  53. package/dist/lib/commands/ProvisionCommand.js +164 -0
  54. package/dist/lib/commands/ProvisionCommand.js.map +1 -0
  55. package/dist/lib/commands/ResolveRecipientsCommand.js +123 -0
  56. package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -0
  57. package/dist/lib/commands/SearchCommand.js +234 -0
  58. package/dist/lib/commands/SearchCommand.js.map +1 -0
  59. package/dist/lib/commands/SendMailCommand.js +18 -0
  60. package/dist/lib/commands/SendMailCommand.js.map +1 -0
  61. package/dist/lib/commands/SettingsCommand.js +138 -0
  62. package/dist/lib/commands/SettingsCommand.js.map +1 -0
  63. package/dist/lib/commands/SmartForwardCommand.js +22 -0
  64. package/dist/lib/commands/SmartForwardCommand.js.map +1 -0
  65. package/dist/lib/commands/SmartReplyCommand.js +22 -0
  66. package/dist/lib/commands/SmartReplyCommand.js.map +1 -0
  67. package/dist/lib/commands/SyncCommand.js +406 -0
  68. package/dist/lib/commands/SyncCommand.js.map +1 -0
  69. package/dist/lib/commands/mongo/FolderSyncCommandMongo.js +16 -0
  70. package/dist/lib/commands/mongo/FolderSyncCommandMongo.js.map +1 -0
  71. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +21 -0
  72. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -0
  73. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +18 -0
  74. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -0
  75. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +17 -0
  76. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -0
  77. package/dist/lib/commands/mongo/MoveItemsCommandMongo.js +17 -0
  78. package/dist/lib/commands/mongo/MoveItemsCommandMongo.js.map +1 -0
  79. package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js +16 -0
  80. package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js.map +1 -0
  81. package/dist/lib/commands/mongo/SearchCommandMongo.js +19 -0
  82. package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -0
  83. package/dist/lib/commands/mongo/SendMailCommandMongo.js +17 -0
  84. package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -0
  85. package/dist/lib/commands/mongo/SettingsCommandMongo.js +16 -0
  86. package/dist/lib/commands/mongo/SettingsCommandMongo.js.map +1 -0
  87. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +17 -0
  88. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -0
  89. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +17 -0
  90. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -0
  91. package/dist/lib/commands/mongo/SyncCommandMongo.js +26 -0
  92. package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -0
  93. package/dist/lib/commands/mongo/index.js +14 -0
  94. package/dist/lib/commands/mongo/index.js.map +1 -0
  95. package/dist/lib/commands/sql/FolderSyncCommandSQL.js +16 -0
  96. package/dist/lib/commands/sql/FolderSyncCommandSQL.js.map +1 -0
  97. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +21 -0
  98. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -0
  99. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +18 -0
  100. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -0
  101. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +17 -0
  102. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -0
  103. package/dist/lib/commands/sql/MoveItemsCommandSQL.js +17 -0
  104. package/dist/lib/commands/sql/MoveItemsCommandSQL.js.map +1 -0
  105. package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js +16 -0
  106. package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js.map +1 -0
  107. package/dist/lib/commands/sql/SearchCommandSQL.js +19 -0
  108. package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -0
  109. package/dist/lib/commands/sql/SendMailCommandSQL.js +17 -0
  110. package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -0
  111. package/dist/lib/commands/sql/SettingsCommandSQL.js +16 -0
  112. package/dist/lib/commands/sql/SettingsCommandSQL.js.map +1 -0
  113. package/dist/lib/commands/sql/SmartForwardCommandSQL.js +17 -0
  114. package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -0
  115. package/dist/lib/commands/sql/SmartReplyCommandSQL.js +17 -0
  116. package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -0
  117. package/dist/lib/commands/sql/SyncCommandSQL.js +26 -0
  118. package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -0
  119. package/dist/lib/commands/sql/index.js +14 -0
  120. package/dist/lib/commands/sql/index.js.map +1 -0
  121. package/dist/lib/index.js +58 -0
  122. package/dist/lib/index.js.map +1 -0
  123. package/dist/lib/jobs/EasDeviceStateCleanupJob.js +101 -0
  124. package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -0
  125. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +13 -0
  126. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -0
  127. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +13 -0
  128. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -0
  129. package/dist/lib/models/DeviceSyncState.js +2 -0
  130. package/dist/lib/models/DeviceSyncState.js.map +1 -0
  131. package/dist/lib/models/mongo/DeviceSyncStateMongo.js +130 -0
  132. package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -0
  133. package/dist/lib/models/sql/DeviceSyncStateSQL.js +130 -0
  134. package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -0
  135. package/dist/lib/mongo/DeviceSyncStateRouteMongo.js +28 -0
  136. package/dist/lib/mongo/DeviceSyncStateRouteMongo.js.map +1 -0
  137. package/dist/lib/mongo/EasRouteMongo.js +63 -0
  138. package/dist/lib/mongo/EasRouteMongo.js.map +1 -0
  139. package/dist/lib/mongo/index.js +9 -0
  140. package/dist/lib/mongo/index.js.map +1 -0
  141. package/dist/lib/mongo.js +15 -0
  142. package/dist/lib/mongo.js.map +1 -0
  143. package/dist/lib/sql/DeviceSyncStateRouteSQL.js +28 -0
  144. package/dist/lib/sql/DeviceSyncStateRouteSQL.js.map +1 -0
  145. package/dist/lib/sql/EasRouteSQL.js +63 -0
  146. package/dist/lib/sql/EasRouteSQL.js.map +1 -0
  147. package/dist/lib/sql/index.js +9 -0
  148. package/dist/lib/sql/index.js.map +1 -0
  149. package/dist/lib/sql.js +15 -0
  150. package/dist/lib/sql.js.map +1 -0
  151. package/dist/types/BaseDeviceSyncStateRoute.d.ts +34 -0
  152. package/dist/types/BaseEasRoute.d.ts +65 -0
  153. package/dist/types/CompactDateTime.d.ts +22 -0
  154. package/dist/types/EasCommandHandler.d.ts +59 -0
  155. package/dist/types/EasSyncKeyUtils.d.ts +89 -0
  156. package/dist/types/adapters/CalendarSyncAdapter.d.ts +52 -0
  157. package/dist/types/adapters/ContactsSyncAdapter.d.ts +33 -0
  158. package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +55 -0
  159. package/dist/types/adapters/EmailSyncAdapter.d.ts +71 -0
  160. package/dist/types/adapters/TasksSyncAdapter.d.ts +32 -0
  161. package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +7 -0
  162. package/dist/types/adapters/mongo/index.d.ts +1 -0
  163. package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +7 -0
  164. package/dist/types/adapters/sql/index.d.ts +1 -0
  165. package/dist/types/codec/WbxmlCodePages.d.ts +45 -0
  166. package/dist/types/codec/WbxmlDecoder.d.ts +33 -0
  167. package/dist/types/codec/WbxmlElement.d.ts +43 -0
  168. package/dist/types/codec/WbxmlEncoder.d.ts +20 -0
  169. package/dist/types/commands/ComposeMailCommand.d.ts +50 -0
  170. package/dist/types/commands/FolderSyncCommand.d.ts +25 -0
  171. package/dist/types/commands/GetItemEstimateCommand.d.ts +43 -0
  172. package/dist/types/commands/ItemOperationsCommand.d.ts +72 -0
  173. package/dist/types/commands/MeetingResponseCommand.d.ts +32 -0
  174. package/dist/types/commands/MoveItemsCommand.d.ts +29 -0
  175. package/dist/types/commands/PingCommand.d.ts +33 -0
  176. package/dist/types/commands/ProvisionCommand.d.ts +54 -0
  177. package/dist/types/commands/ResolveRecipientsCommand.d.ts +36 -0
  178. package/dist/types/commands/SearchCommand.d.ts +60 -0
  179. package/dist/types/commands/SendMailCommand.d.ts +10 -0
  180. package/dist/types/commands/SettingsCommand.d.ts +37 -0
  181. package/dist/types/commands/SmartForwardCommand.d.ts +17 -0
  182. package/dist/types/commands/SmartReplyCommand.d.ts +17 -0
  183. package/dist/types/commands/SyncCommand.d.ts +100 -0
  184. package/dist/types/commands/mongo/FolderSyncCommandMongo.d.ts +8 -0
  185. package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +7 -0
  186. package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +9 -0
  187. package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +8 -0
  188. package/dist/types/commands/mongo/MoveItemsCommandMongo.d.ts +8 -0
  189. package/dist/types/commands/mongo/ResolveRecipientsCommandMongo.d.ts +7 -0
  190. package/dist/types/commands/mongo/SearchCommandMongo.d.ts +9 -0
  191. package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +8 -0
  192. package/dist/types/commands/mongo/SettingsCommandMongo.d.ts +7 -0
  193. package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +8 -0
  194. package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +8 -0
  195. package/dist/types/commands/mongo/SyncCommandMongo.d.ts +8 -0
  196. package/dist/types/commands/mongo/index.d.ts +9 -0
  197. package/dist/types/commands/sql/FolderSyncCommandSQL.d.ts +8 -0
  198. package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +7 -0
  199. package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +9 -0
  200. package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +8 -0
  201. package/dist/types/commands/sql/MoveItemsCommandSQL.d.ts +8 -0
  202. package/dist/types/commands/sql/ResolveRecipientsCommandSQL.d.ts +7 -0
  203. package/dist/types/commands/sql/SearchCommandSQL.d.ts +9 -0
  204. package/dist/types/commands/sql/SendMailCommandSQL.d.ts +8 -0
  205. package/dist/types/commands/sql/SettingsCommandSQL.d.ts +7 -0
  206. package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +8 -0
  207. package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +8 -0
  208. package/dist/types/commands/sql/SyncCommandSQL.d.ts +8 -0
  209. package/dist/types/commands/sql/index.d.ts +9 -0
  210. package/dist/types/index.d.ts +53 -0
  211. package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +26 -0
  212. package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +5 -0
  213. package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +5 -0
  214. package/dist/types/models/DeviceSyncState.d.ts +30 -0
  215. package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +22 -0
  216. package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +22 -0
  217. package/dist/types/mongo/DeviceSyncStateRouteMongo.d.ts +8 -0
  218. package/dist/types/mongo/EasRouteMongo.d.ts +14 -0
  219. package/dist/types/mongo/index.d.ts +4 -0
  220. package/dist/types/mongo.d.ts +10 -0
  221. package/dist/types/sql/DeviceSyncStateRouteSQL.d.ts +8 -0
  222. package/dist/types/sql/EasRouteSQL.d.ts +14 -0
  223. package/dist/types/sql/index.d.ts +4 -0
  224. package/dist/types/sql.d.ts +10 -0
  225. package/package.json +238 -0
@@ -0,0 +1,32 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCollectionSyncAdapter } from "./EasCollectionSyncAdapter.js";
3
+ import { type Task } from "@rapidmx/restapi";
4
+ /**
5
+ * Maps `Task` to/from the EAS `Sync` `Tasks` collection class (MS-ASTASK).
6
+ *
7
+ * **Pragmatic subset**: only the `Utc*` (UTC Compact DateTime) variant of each date field is emitted, not the
8
+ * paired local-time variant MS-ASTASK also defines (`DueDate`/`StartDate` alongside `UtcDueDate`/
9
+ * `UtcStartDate`) - this library doesn't track a per-task timezone to correctly localize the non-UTC variant,
10
+ * and a real device already treats the `Utc*` field as authoritative. Recurring tasks are not synced (a task's
11
+ * `Recurrence` element mirrors Calendar's, itself already a pragmatic subset there - deferred further here).
12
+ *
13
+ * @author Jean-Philippe Steinmetz
14
+ */
15
+ export declare class TasksSyncAdapter implements EasCollectionSyncAdapter<Task> {
16
+ readonly collectionClass = "Tasks";
17
+ toApplicationData(task: Task): WbxmlElement;
18
+ /**
19
+ * Reverse of `toApplicationData`. `DateCompleted` is never parsed back - it's `task.dateModified` echoed
20
+ * out, not an independent field this library's own `Task` model has room to store separately, so a client
21
+ * setting `Complete` is enough on its own. `ReminderSet="0"` (with no `ReminderTime`) is treated as an
22
+ * explicit "clear the reminder" signal (`reminderDate: null` in the returned partial, which - unlike
23
+ * simply omitting the key - does override an existing reminder when merged onto `existing` for a `Change`)
24
+ * since MS-ASTASK gives no other way to express removing a reminder; `UtcDueDate` has no equivalent
25
+ * explicit-clear signal and so can only be set, never cleared, via `Sync` - a real, narrower gap than
26
+ * `reminderDate`'s, documented here rather than silently accepted. `null`, not `undefined`: TypeORM's
27
+ * `UpdateQueryBuilder` silently drops any `undefined`-valued key from the generated SQL `SET` clause
28
+ * (confirmed by reading its source), so `undefined` here would leave a stale `reminderDate` in place on
29
+ * the SQL backend while correctly clearing it on Mongo - `null` clears it on both.
30
+ */
31
+ fromApplicationData(el: WbxmlElement): Partial<Task>;
32
+ }
@@ -0,0 +1,7 @@
1
+ import { EmailSyncAdapter } from "../EmailSyncAdapter.js";
2
+ /**
3
+ * @author Jean-Philippe Steinmetz
4
+ */
5
+ export declare class EmailSyncAdapterMongo extends EmailSyncAdapter {
6
+ protected labelClass: any;
7
+ }
@@ -0,0 +1 @@
1
+ export * from "./EmailSyncAdapterMongo.js";
@@ -0,0 +1,7 @@
1
+ import { EmailSyncAdapter } from "../EmailSyncAdapter.js";
2
+ /**
3
+ * @author Jean-Philippe Steinmetz
4
+ */
5
+ export declare class EmailSyncAdapterSQL extends EmailSyncAdapter {
6
+ protected labelClass: any;
7
+ }
@@ -0,0 +1 @@
1
+ export * from "./EmailSyncAdapterSQL.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * WBXML code page index -> namespace name, per MS-ASWBXML §2.1.2.1 ("List of Code Pages"). A code page is
3
+ * WBXML's mechanism for reusing the single-byte tag-token space across many XML namespaces: a `SWITCH_PAGE`
4
+ * global token changes which of these tables subsequent tag bytes are looked up against.
5
+ *
6
+ * @author Jean-Philippe Steinmetz
7
+ */
8
+ export declare enum WbxmlCodePage {
9
+ AirSync = 0,
10
+ Contacts = 1,
11
+ Email = 2,
12
+ AirNotification = 3,
13
+ Calendar = 4,
14
+ Move = 5,
15
+ ItemEstimate = 6,
16
+ FolderHierarchy = 7,
17
+ MeetingResponse = 8,
18
+ Tasks = 9,
19
+ ResolveRecipients = 10,
20
+ ValidateCert = 11,
21
+ Contacts2 = 12,
22
+ Ping = 13,
23
+ Provision = 14,
24
+ Search = 15,
25
+ Gal = 16,
26
+ AirSyncBase = 17,
27
+ Settings = 18,
28
+ DocumentLibrary = 19,
29
+ ItemOperations = 20,
30
+ ComposeMail = 21,
31
+ Email2 = 22,
32
+ Notes = 23,
33
+ RightsManagement = 24,
34
+ Find = 25
35
+ }
36
+ /** Resolves a decoded tag byte's low 6 bits back into its tag name for the given code page. Falls back to a
37
+ * synthetic `"Unknown0x.."` name (rather than throwing) for a page/code this table doesn't recognize - a
38
+ * still-decodable, debuggable placeholder for a deferred code page or a future protocol version's new field,
39
+ * rather than failing the entire document over one unrecognized tag. */
40
+ export declare function tagNameForCode(page: number, code: number): string;
41
+ /** Resolves a tag name to its token byte (low 6 bits, before the content-flag bit is applied) for the given
42
+ * code page. Unlike `tagNameForCode()`, this throws on an unknown pair - encoding a tag this table has no
43
+ * token for is always a genuine programming error (an unregistered/deferred field), never legitimate input to
44
+ * tolerate. */
45
+ export declare function codeForTagName(page: number, tag: string): number;
@@ -0,0 +1,33 @@
1
+ import type { WbxmlElement } from "./WbxmlElement.js";
2
+ /**
3
+ * Decodes a WBXML byte stream (an EAS request/response body) back into a `WbxmlElement` tree — the exact
4
+ * inverse of `WbxmlEncoder`. Reads the fixed EAS document header, skips its (always-empty, in real
5
+ * ActiveSync traffic) string table, then parses the single root element.
6
+ *
7
+ * The `publicid` header field is read via the same generic `mb_u_int32` reader used everywhere else, which is
8
+ * only a partial implementation of the full WBXML spec for that field (a raw leading `0x00` byte would
9
+ * signal "public identifier is a string-table reference" under the full spec, a form real ActiveSync traffic
10
+ * never uses) — safe here since every real EAS document sends the literal well-known value `1` ("unknown or
11
+ * missing"), which decodes identically either way.
12
+ *
13
+ * @author Jean-Philippe Steinmetz
14
+ */
15
+ export declare class WbxmlDecoder {
16
+ private buf;
17
+ private pos;
18
+ private currentPage;
19
+ private depth;
20
+ decode(data: Buffer): WbxmlElement;
21
+ private readByte;
22
+ /** Decodes a WBXML `mb_u_int32`: base-128 digits, most significant group first, every byte but the last
23
+ * carrying the 0x80 continuation bit. Mirrors `WbxmlEncoder.writeMbUint()`. */
24
+ private readMbUint;
25
+ private readCString;
26
+ private readTagElement;
27
+ /** Reads a mixed sequence of child tag elements / an inline string / opaque binary content, up to (and
28
+ * consuming) the terminating `END` token — the body of one "has content" element. Also handles a
29
+ * `SWITCH_PAGE` appearing between sibling children, which applies to every subsequent sibling until
30
+ * either the next switch or the end of this content block (switching page is a standing instruction, not
31
+ * scoped to a single following tag). */
32
+ private readContentUntilEnd;
33
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * A minimal, tagged-tree representation of one WBXML element, decoupled from the wire format's binary token
3
+ * encoding — `WbxmlEncoder`/`WbxmlDecoder` are the only code that ever deals in raw bytes; every EAS command
4
+ * handler works against this tree.
5
+ *
6
+ * Deliberately does not model WBXML attributes: MS-ASWBXML's own algorithm description confirms ActiveSync
7
+ * never uses the attribute mechanism (every element's data is carried as child elements or inline text), so
8
+ * there is nothing for an `attrs` field to represent here.
9
+ *
10
+ * A given element is expected to carry exactly one of `children` (nested elements - the common case for a
11
+ * structural container like `Sync`/`Collection`), `text` (a single inline string value - the common case for
12
+ * a leaf field like `SyncKey`/`ServerId`), or `opaque` (raw binary content - used only for `ItemOperations`
13
+ * `Data` and similar binary payloads). Real ActiveSync XML is data-oriented, not document-oriented, so this
14
+ * library never needs to represent mixed content (an element with both nested tags and inline text).
15
+ *
16
+ * @author Jean-Philippe Steinmetz
17
+ */
18
+ export interface WbxmlElement {
19
+ /** The `WbxmlCodePage` this element's tag belongs to. */
20
+ page: number;
21
+ /** The tag name, as registered in `WbxmlCodePages.ts` for `page`. */
22
+ tag: string;
23
+ /** Nested elements, in document order. Empty for a leaf/empty element. */
24
+ children: WbxmlElement[];
25
+ /** Inline string content (WBXML `STR_I`), for a leaf element carrying a single text value. */
26
+ text?: string;
27
+ /** Raw binary content (WBXML `OPAQUE`), for a leaf element carrying binary data. */
28
+ opaque?: Buffer;
29
+ }
30
+ /** Constructs a structural element with nested children (e.g. `<Collection>...</Collection>`). */
31
+ export declare function element(page: number, tag: string, children?: WbxmlElement[]): WbxmlElement;
32
+ /** Constructs a leaf element carrying a single inline string value (e.g. `<SyncKey>1</SyncKey>`). */
33
+ export declare function textElement(page: number, tag: string, text: string): WbxmlElement;
34
+ /** Constructs a leaf element carrying raw binary content (e.g. `<Data>...</Data>` in `ItemOperations`). */
35
+ export declare function opaqueElement(page: number, tag: string, opaque: Buffer): WbxmlElement;
36
+ /** Finds the first direct child of `parent` with the given tag name, or `undefined` if none exists. Does not
37
+ * search recursively — WBXML/EAS field lookups are always relative to a specific known parent element. */
38
+ export declare function findChild(parent: WbxmlElement, tag: string): WbxmlElement | undefined;
39
+ /** Finds every direct child of `parent` with the given tag name (e.g. every `Collection` under
40
+ * `Collections`). */
41
+ export declare function findChildren(parent: WbxmlElement, tag: string): WbxmlElement[];
42
+ /** Shorthand for `findChild(parent, tag)?.text`, the common case of reading a leaf field's string value. */
43
+ export declare function childText(parent: WbxmlElement, tag: string): string | undefined;
@@ -0,0 +1,20 @@
1
+ import type { WbxmlElement } from "./WbxmlElement.js";
2
+ /**
3
+ * Encodes a `WbxmlElement` tree into a WBXML byte stream, per MS-ASWBXML's encoding algorithm. Emits the
4
+ * fixed EAS document header (`version=1.3, publicid=unknown, charset=UTF-8, empty string table`) followed by
5
+ * the token stream for the given root element, switching code pages only when the page actually changes from
6
+ * whatever was last active (starting from `AirSync`, page 0, the implicit default per spec).
7
+ *
8
+ * @author Jean-Philippe Steinmetz
9
+ */
10
+ export declare class WbxmlEncoder {
11
+ private bytes;
12
+ private currentPage;
13
+ encode(root: WbxmlElement): Buffer;
14
+ private writeElement;
15
+ private writeStrI;
16
+ private writeOpaque;
17
+ /** Encodes `value` as a WBXML multi-byte unsigned integer (`mb_u_int32`): base-128 digits, most
18
+ * significant group first, every byte but the last carrying the 0x80 continuation bit. */
19
+ private writeMbUint;
20
+ }
@@ -0,0 +1,50 @@
1
+ import { type Message, RecoverableRepoUtils } from "@rapidmx/restapi";
2
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
3
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
4
+ /**
5
+ * Shared implementation for EAS `SendMail`, `SmartForward`, and `SmartReply` (MS-ASCMD `ComposeMail` namespace)
6
+ * — all three submit a client-composed raw MIME body directly (`<Mime>`, opaque WBXML content) rather than
7
+ * referencing a pre-existing draft `Message`, unlike the webmail REST API's `POST /messages/:id/send` (see
8
+ * `BaseMessageRoute.send()`, which this class's `scanAndRelay()` call shares its scan-then-relay core with via
9
+ * `MailSendUtils.ts`).
10
+ *
11
+ * **Pragmatic subset, deliberately not the full MS-ASCMD semantics**:
12
+ * - `SmartForward`/`SmartReply`'s `<Source>` (the message being forwarded/replied to) is used only to thread
13
+ * the outgoing message (`inReplyTo`/`references`) and to flip the original's `Answered`/`Forwarded` flag - the
14
+ * real spec has the *server* splice the original message's full content into the outgoing MIME so the client
15
+ * never has to download-then-reupload it; this pragmatic subset instead expects the client's own `<Mime>` to
16
+ * already be the complete outgoing message (which is what every mainstream client's own compose UI naturally
17
+ * produces once it has fetched the original for display), matching this library's "pragmatic subset, not full
18
+ * fidelity" precedent elsewhere (e.g. `FolderSyncCommand`'s SyncKey replay-protection gap).
19
+ * - `ReplaceMime`/`AccountId`/`InstanceId` are not read - single-account, non-recurring-meeting compose only.
20
+ * - Attachments present in the composed MIME are relayed correctly (`scanAndRelay()`'s `ScanPipeline` handles
21
+ * the full raw message) but are not additionally persisted as `Attachment` records on the saved Sent Items
22
+ * copy - `Message.hasAttachments` is still set correctly from the parsed MIME, just not each attachment's own
23
+ * row (deferred, matching `ItemOperationsCommand`'s own future `Fetch`-of-Sent-Items scope).
24
+ *
25
+ * `folderClass`/`messageClass` are supplied by the Mongo/SQL concrete subclasses, and `markOriginal()` by the
26
+ * `SmartForwardCommand`/`SmartReplyCommand` subclasses (a no-op here, since plain `SendMailCommand` never has a
27
+ * `<Source>` to act on).
28
+ *
29
+ * @author Jean-Philippe Steinmetz
30
+ */
31
+ export declare abstract class ComposeMailCommand implements EasCommandHandler {
32
+ abstract readonly command: string;
33
+ protected abstract folderClass: any;
34
+ protected abstract messageClass: any;
35
+ private _objectFactory?;
36
+ protected folderRepo?: RecoverableRepoUtils<any>;
37
+ protected messageRepo?: RecoverableRepoUtils<any>;
38
+ private blobStore?;
39
+ private mailTransport?;
40
+ private scanPipeline?;
41
+ private aclUtils?;
42
+ init(): Promise<void>;
43
+ /** Called once the outgoing message has been sent, only when the request carried a `<Source>` (i.e. this
44
+ * is a `SmartForward`/`SmartReply`, never a plain `SendMail`) - flips the referenced original message's own
45
+ * `Answered`/`Forwarded` flag. A no-op here; overridden by the two subclasses that need it. */
46
+ protected markOriginal(_ctx: EasCommandContext, _original: Message & {
47
+ uid: string;
48
+ }): Promise<void>;
49
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
50
+ }
@@ -0,0 +1,25 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ import { Folder } from "@rapidmx/restapi";
4
+ /**
5
+ * Handles EAS `FolderSync`: enumerates `Add`/`Update`/`Delete`s for the caller's mailbox's `Folder` hierarchy
6
+ * since the device's last `FolderSync`, using the shared watermark-based cursor mechanism in
7
+ * `EasSyncKeyUtils.ts` (scoped by `mailboxUid` over the `Folder` collection, rather than `folderUid` over a
8
+ * per-folder item collection the way `SyncCommand` will be).
9
+ *
10
+ * `folderClass` is supplied by the Mongo/SQL concrete subclasses, following the exact one-line-per-backend
11
+ * pattern used throughout this library's other routes/jobs.
12
+ *
13
+ * @author Jean-Philippe Steinmetz
14
+ */
15
+ export declare abstract class FolderSyncCommand<F extends Folder> implements EasCommandHandler {
16
+ readonly command = "FolderSync";
17
+ protected abstract folderClass: any;
18
+ private windowSize;
19
+ private _objectFactory?;
20
+ private folderRepo?;
21
+ init(): Promise<void>;
22
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
23
+ private folderToChangeElement;
24
+ private persistSyncKey;
25
+ }
@@ -0,0 +1,43 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /** Binds one MS-ASCMD `Class` value to the concrete entity class this command counts against - a lighter
4
+ * version of `SyncCommand`'s own `SyncCollectionBinding` (no adapter needed at all, since `GetItemEstimate`
5
+ * never serializes an item, only counts them). Supplied by the Mongo/SQL concrete subclasses. */
6
+ export interface EstimateCollectionBinding {
7
+ entityClass: any;
8
+ }
9
+ /**
10
+ * Handles EAS `GetItemEstimate`: reports, per requested `<Collection>`, an estimated count of the
11
+ * `Add`/`Change`/`Delete`s a subsequent `Sync` of that collection would return - read-only, it never issues or
12
+ * consumes a `SyncKey` itself.
13
+ *
14
+ * The modern (14.0+) request/response reuses `WbxmlCodePage.AirSync`'s own `Collections`/`Collection`/`Class`/
15
+ * `CollectionId`/`SyncKey` via `SWITCH_PAGE` rather than this page's own legacy (`Folders`/`Folder`/`FolderId`)
16
+ * shape - see `WbxmlCodePages.ts`'s own doc comment on `WbxmlCodePage.ItemEstimate` for why, and
17
+ * `ItemOperationsCommand.fetchMessage`'s identical cross-page-reuse precedent.
18
+ *
19
+ * A `SyncKey` of `"0"` (or one this device has never synced this folder with before) reports the folder's
20
+ * total live item count - what a first `Sync` would report as `Add`s. Otherwise reuses `EasSyncKeyUtils.
21
+ * computeChanges()` (the same enumeration `SyncCommand` itself uses), capped at `DEFAULT_MAX_COUNT` - see its
22
+ * own doc comment for why this is a real, documented approximation on a very active folder rather than a
23
+ * precise unbounded count.
24
+ *
25
+ * **ACL-checked like `Sync`**: `estimateCollection()` requires `ACLAction.READ` on the client-supplied
26
+ * `CollectionId` before counting anything - without it, a crafted `CollectionId` belonging to another
27
+ * mailbox's folder would return a real pending-change count for it. A denied folder is reported identically to
28
+ * an unrecognized collection (`Status 2`), never distinguishable from "you don't have this collection at all".
29
+ *
30
+ * @author Jean-Philippe Steinmetz
31
+ */
32
+ export declare abstract class GetItemEstimateCommand implements EasCommandHandler {
33
+ readonly command = "GetItemEstimate";
34
+ protected abstract collectionBindings: Record<string, EstimateCollectionBinding>;
35
+ private maxCount;
36
+ private _objectFactory?;
37
+ private aclUtils?;
38
+ private repos;
39
+ init(): Promise<void>;
40
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
41
+ private estimateCollection;
42
+ private collectionElement;
43
+ }
@@ -0,0 +1,72 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /**
4
+ * Handles EAS `ItemOperations`: `Fetch` (a `Message`'s full body or an `Attachment`'s binary content, by the
5
+ * same `ServerId`/uid this library already exposes elsewhere) and `EmptyFolderContents`.
6
+ *
7
+ * Per the published [MS-ASCMD] `ItemOperations` request schema (confirmed directly, not assumed), the command
8
+ * is a strict choice of exactly three operations - `Fetch` (unbounded), `EmptyFolderContents`, and `Move` -
9
+ * with no fourth "write a new item" capability anywhere in it; `Store` is a required *child* of `Fetch`
10
+ * (`"Mailbox"` or `"DocumentLibrary"`, the same store-selector role it plays in `SearchCommand`), not a
11
+ * separate write/upload command as its name might suggest.
12
+ *
13
+ * **Pragmatic subset, deliberately not the full MS-ASCMD `ItemOperations` semantics**:
14
+ * - `Move` moves an entire *conversation* (by `ConversationId`, opaque binary - see `EmailSyncAdapter`'s
15
+ * `encodeConversationId`/`decodeConversationId`) to a destination folder - unrelated to the standalone
16
+ * `MoveItems` command's per-message `SrcFldId`/`SrcMsgId`/`DstFldId` shape. Every `Message` sharing the
17
+ * decoded `conversationId` across the whole mailbox (not just one folder) that the caller has `UPDATE` on is
18
+ * relocated to `DstFldId`; one lacking permission is silently skipped rather than failing the whole move (a
19
+ * conversation can legitimately span folders the caller doesn't control, e.g. a shared mailbox's Inbox). An
20
+ * optional `MoveAlways` (a hint to keep auto-moving future messages in this conversation) is accepted but not
21
+ * acted on - this library's `MailFilterRule` has no conversation-scoped condition to key an ongoing rule off
22
+ * of, a documented simplification, not silent data loss (the move itself still happens).
23
+ * - `Store: "DocumentLibrary"` is rejected per-`Fetch` - matches `SearchCommand`'s own GAL-only scope decision;
24
+ * this library has no document-library model.
25
+ * - A `Fetch` failure (not found, no permission, malformed) aborts the whole request via an HTTP-level error
26
+ * rather than an embedded per-`Fetch` `Status` code the way `Sync`/`MoveItems`/`ResolveRecipients` report
27
+ * their own per-item failures - a deliberate, documented simplification carried over unchanged from this
28
+ * command's original single-`Fetch` design, not a new gap introduced by adding multi-`Fetch` support.
29
+ * - Only the "inline" delivery method is used (content embedded directly in the WBXML response) - the real
30
+ * spec's "multipart" alternative (WBXML as one part, binary content as a separate part) is not implemented;
31
+ * every attachment this library's own `ScanPipeline` already accepts is assumed to fit comfortably in memory
32
+ * for one response, the same assumption `BaseAttachmentRoute.download()` already makes.
33
+ * - `Options/BodyPreference`'s `Type`/`TruncationSize` are honored for a `Message` body fetch (plain text,
34
+ * HTML, or - `Type 4` - the raw MIME source verbatim); byte-range fetching (`Range`) is not implemented.
35
+ * - `EmptyFolderContents`'s `DeleteSubFolders` option is rejected outright rather than silently ignored -
36
+ * recursive subfolder deletion is out of scope for this pragmatic subset; emptying a single folder's own
37
+ * `Message`s is the common case this implements.
38
+ *
39
+ * `folderClass`/`messageClass`/`attachmentClass` are supplied by the Mongo/SQL concrete subclasses.
40
+ *
41
+ * @author Jean-Philippe Steinmetz
42
+ */
43
+ export declare abstract class ItemOperationsCommand implements EasCommandHandler {
44
+ readonly command = "ItemOperations";
45
+ protected abstract folderClass: any;
46
+ protected abstract messageClass: any;
47
+ protected abstract attachmentClass: any;
48
+ private _objectFactory?;
49
+ private folderRepo?;
50
+ private messageRepo?;
51
+ private attachmentRepo?;
52
+ private blobStore?;
53
+ private aclUtils?;
54
+ private maxFetchesPerRequest;
55
+ private batchSize;
56
+ init(): Promise<void>;
57
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
58
+ private fetchMessage;
59
+ private emptyFolderContents;
60
+ /**
61
+ * Handles `ItemOperations`' `Move`: relocates every `Message` sharing a decoded `ConversationId` to
62
+ * `DstFldId`. Unlike `emptyFolderContents`/`fetchMessage` (which fail the whole request via a thrown
63
+ * `ApiError`), this reports failure through the embedded `Status` the same way `MoveItemsCommand` does for
64
+ * its own per-item results - a malformed/unresolvable `Move` is a normal outcome for this operation, not an
65
+ * exceptional one. Reports failure (`Status 3`) if not even one message was actually moved - including when
66
+ * every message sharing the conversation sits in a folder the caller lacks `UPDATE` on, which would
67
+ * otherwise silently fall through to a false "success" with nothing having moved.
68
+ */
69
+ private moveConversation;
70
+ private moveResponse;
71
+ private fetchAttachment;
72
+ }
@@ -0,0 +1,32 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /**
4
+ * Handles EAS `MeetingResponse`: records the caller's own accept/tentative/decline response to a meeting on
5
+ * the referenced `CalendarEvent`. `RequestId` is the same `Message`/`CalendarEvent.uid` this library already
6
+ * exposes as `ServerId` elsewhere (Sync/FolderSync) - no separate lookup table is needed.
7
+ *
8
+ * **Pragmatic subset**: only the first `<Request>` in the command is processed (the real spec allows several
9
+ * per request, matching `Sync`'s own single-`Collection`-per-request scope in this library). A **decline**
10
+ * soft-deletes the `CalendarEvent` (matching real Exchange behavior) rather than merely flipping the caller's
11
+ * own `Attendee.responseStatus` - each attendee has their own row already (`CalendarEvent.mailboxUid` scopes
12
+ * every event to a single mailbox, per this library's architecture), so deleting *this* row only removes the
13
+ * meeting from the declining attendee's own calendar, leaving the organizer's and every other attendee's own
14
+ * copy untouched. Accept/Tentative still update `Attendee.responseStatus` in place. The response omits
15
+ * `CalendarId` for a decline either way, matching the spec's own convention, so a client relying on that
16
+ * signal isn't misled into thinking a new item was created.
17
+ *
18
+ * `calendarEventClass`/`mailboxClass` are supplied by the Mongo/SQL concrete subclasses.
19
+ *
20
+ * @author Jean-Philippe Steinmetz
21
+ */
22
+ export declare abstract class MeetingResponseCommand implements EasCommandHandler {
23
+ readonly command = "MeetingResponse";
24
+ protected abstract calendarEventClass: any;
25
+ protected abstract mailboxClass: any;
26
+ private _objectFactory?;
27
+ private calendarEventRepo?;
28
+ private mailboxRepo?;
29
+ private aclUtils?;
30
+ init(): Promise<void>;
31
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
32
+ }
@@ -0,0 +1,29 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /**
4
+ * Handles the standalone EAS `MoveItems` command: moves one or more `Message`s between folders in the caller's
5
+ * own mailbox by `ServerId`/`uid`.
6
+ *
7
+ * **Pragmatic subset**: `Message` only - `Contacts`/`Calendar`/`Tasks` moves are rare in practice (unlike
8
+ * `Message`, whose Inbox-to-subfolder filing is a real, common client action) and would each need their own
9
+ * ACL/ownership verification path for comparatively little value; a client attempting one gets `Status 3`
10
+ * rather than being silently ignored. `DstMsgId` in the response is always the same `uid` as `SrcMsgId` - this
11
+ * library never mints a new identifier on move, unlike a real Exchange server, which sometimes does.
12
+ *
13
+ * `messageClass`/`folderClass` are supplied by the Mongo/SQL concrete subclasses.
14
+ *
15
+ * @author Jean-Philippe Steinmetz
16
+ */
17
+ export declare abstract class MoveItemsCommand implements EasCommandHandler {
18
+ readonly command = "MoveItems";
19
+ protected abstract messageClass: any;
20
+ protected abstract folderClass: any;
21
+ private _objectFactory?;
22
+ private messageRepo?;
23
+ private folderRepo?;
24
+ private aclUtils?;
25
+ init(): Promise<void>;
26
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
27
+ private moveOne;
28
+ private responseElement;
29
+ }
@@ -0,0 +1,33 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /**
4
+ * Handles EAS `Ping`: a long-poll HTTP request that blocks for up to `HeartbeatInterval` seconds waiting for a
5
+ * change in any of the client-specified folders, then reports which (if any) actually changed. No websocket
6
+ * needed - subscribes a plain, request-scoped Redis client (the same construction `BasePushRoute.connect()`
7
+ * uses, minus its own per-socket bookkeeping this one-shot request doesn't need) directly to the requested
8
+ * `folderUid` channels. Every `Message`/`CalendarEvent`/etc. mutation already publishes to exactly these
9
+ * channels via `BaseScopedChildRoute.notify()`/`RepoUtils`'s own push - `Ping` needs no new publish call site
10
+ * of its own, only a subscriber.
11
+ *
12
+ * Requested folder uids are filtered down to only those the caller currently has `READ` on before subscribing -
13
+ * matching every other command's ownership check on a client-supplied id, since without it a device could
14
+ * long-poll indefinitely on any folder uid it happens to know (including one it used to have legitimate access
15
+ * to and no longer does) and learn purely from this channel when that other mailbox's data changes.
16
+ *
17
+ * @author Jean-Philippe Steinmetz
18
+ */
19
+ export declare class PingCommand implements EasCommandHandler {
20
+ readonly command = "Ping";
21
+ private redisConfig;
22
+ private minHeartbeatSeconds;
23
+ private maxHeartbeatSeconds;
24
+ private aclUtils?;
25
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
26
+ private statusResponse;
27
+ /** Subscribes to `folderUids` and resolves with whichever of them published a change first, or `[]` if
28
+ * `timeoutSeconds` elapses with no publish. A missing `datastores:events` config (Redis pub/sub not set up
29
+ * for this deployment) fails open to "no changes" rather than blocking forever or throwing - `Ping` is a
30
+ * best-effort low-latency notification path, not the only way a client ever discovers new mail (it will
31
+ * eventually poll a real `Sync`/`FolderSync` regardless). */
32
+ private waitForChange;
33
+ }
@@ -0,0 +1,54 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /**
4
+ * Handles the two-request EAS `Provision` handshake (MS-ASPROV) every client must complete before any other
5
+ * command is honored (see `BaseEasRoute`'s provisioning gate), plus the three-step `RemoteWipe` sub-flow that
6
+ * rides the same command.
7
+ *
8
+ * **Policy issuance/enforcement**: the password/encryption requirements sent back in request 1's policy
9
+ * document are sourced from `@Config("mail:eas:provision:*")` (defaults below are permissive but not
10
+ * `0`/`false` across the board, unlike the old hardcoded document) - a deployment can tighten them without
11
+ * code changes. Enforcement itself is honest but shallow: request 2 must carry back `Status: 1` on its own
12
+ * `Policy` (a device that reports it could *not* apply the policy, or omits `Status` entirely, is rejected
13
+ * without provisioning) - this library does not itself verify the device's actual password/encryption state
14
+ * beyond trusting that self-reported status, matching MS-ASPROV's own protocol design (the wire protocol has
15
+ * no way for the server to inspect device state directly either).
16
+ *
17
+ * - **Request 1** (no `PolicyKey` in the body): mint a new policy key, store it on `DeviceSyncState` (not yet
18
+ * provisioned), and send back the policy document under that key. If a `RemoteWipe` was requested for this
19
+ * device (`DeviceSyncState.remoteWipeRequested`, set by an admin - see the remote-wipe route), skip normal
20
+ * policy issuance entirely and send the `RemoteWipe` directive instead.
21
+ * - **Request 2** (client echoes the `PolicyKey` back, acknowledging the policy): if the key matches what was
22
+ * minted in request 1 *and* the client's own `Status` is `1`, mark the device provisioned and re-confirm the
23
+ * same key; anything else (a stale/replayed key, a device that never actually saw request 1's response, or a
24
+ * device reporting it could not comply) is rejected without provisioning.
25
+ * - **RemoteWipe acknowledgement**: after wiping itself, a device sends a bare `<Provision><RemoteWipe>
26
+ * <Status>1</Status></RemoteWipe></Provision>` (no `Policies`). Detected first, ahead of the normal
27
+ * issue/acknowledge branching. Clears `remoteWipeRequested` and stamps `remoteWipeAcknowledgedAt` for audit,
28
+ * but deliberately leaves `provisioned` untouched (`false`, from when the wipe was requested) - the device
29
+ * must complete a genuine fresh Provision handshake to re-add the account, it does not fall straight back into
30
+ * "provisioned". `remoteWipeAccountOnly` is recorded for admin audit only; the wire directive sent to the
31
+ * device is identical either way (a real "wipe just this account's data" vs. "wipe the whole device"
32
+ * distinction would require an MDM-capable client extension this library doesn't implement).
33
+ *
34
+ * @author Jean-Philippe Steinmetz
35
+ */
36
+ export declare class ProvisionCommand implements EasCommandHandler {
37
+ readonly command = "Provision";
38
+ private passwordEnabled;
39
+ private minPasswordLength;
40
+ private maxFailedAttempts;
41
+ private requireDeviceEncryption;
42
+ private allowSimplePassword;
43
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
44
+ /** Request 1: mint and store a new policy key, send the policy document - or, if a remote wipe is
45
+ * pending for this device, the `RemoteWipe` directive instead. */
46
+ private issuePolicy;
47
+ /** Request 2: the client acknowledges the policy key it was handed in request 1, self-reporting whether
48
+ * it actually applied the policy via its own `Status`. */
49
+ private acknowledgePolicy;
50
+ /** The device has wiped itself and is acknowledging - clear the pending flag but leave `provisioned`
51
+ * alone (still `false`, from when the wipe was requested) so a genuine re-provision is required. */
52
+ private acknowledgeRemoteWipe;
53
+ private persist;
54
+ }
@@ -0,0 +1,36 @@
1
+ import { type WbxmlElement } from "../codec/WbxmlElement.js";
2
+ import type { EasCommandContext, EasCommandHandler } from "../EasCommandHandler.js";
3
+ /**
4
+ * Handles EAS `ResolveRecipients`: resolves each `<To>` value (a display name, partial name, or address) the
5
+ * client is unsure how to reach against the mailbox's own `Contact` (GAL) store - the same substring-match
6
+ * approach `SearchCommand` uses for its own `Store Name="GAL"` lookups, duplicated rather than shared (small
7
+ * enough, and specific enough to each command's own surrounding logic, that extracting a shared utility for two
8
+ * call sites isn't worth a new cross-command dependency). Both use `RepoUtils`' `regex(...)` operator (not
9
+ * `like(...)`, which compiles a glob pattern under `@rapidrest/service-core` ^2.0 - anchored, and with no escape
10
+ * for a literal `*`/`?`): `StringUtils.escapeRegExp(value)` gives a genuine, unanchored literal-substring match
11
+ * with no residual wildcard ambiguity, matching the same fix already applied to the `mapi` plugin's own
12
+ * identical GAL-search gap.
13
+ *
14
+ * **Pragmatic subset**: no free-busy `Availability`, no S/MIME `Certificates`/`Options` handling at all - this
15
+ * command's real-world use is overwhelmingly enterprise S/MIME certificate lookup, which this library doesn't
16
+ * implement elsewhere either (`ComposeMailCommand` sends plain MIME, never signed/encrypted). A `<To>` value
17
+ * that already looks like an email address (`LOOKS_LIKE_EMAIL`) is echoed straight back as its own single
18
+ * exact match rather than searched for, matching a real client's own common case of resolving an address it
19
+ * already typed correctly.
20
+ *
21
+ * `contactClass` is supplied by the Mongo/SQL concrete subclasses.
22
+ *
23
+ * @author Jean-Philippe Steinmetz
24
+ */
25
+ export declare abstract class ResolveRecipientsCommand implements EasCommandHandler {
26
+ readonly command = "ResolveRecipients";
27
+ protected abstract contactClass: any;
28
+ private maxMatches;
29
+ private _objectFactory?;
30
+ private contactRepo?;
31
+ init(): Promise<void>;
32
+ handle(ctx: EasCommandContext): Promise<WbxmlElement | undefined>;
33
+ private resolveOne;
34
+ private recipientElement;
35
+ private responseElement;
36
+ }