@rapidmx/activesync 1.0.0-beta.0

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 (195) 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 +230 -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/codec/WbxmlCodePages.js +706 -0
  24. package/dist/lib/codec/WbxmlCodePages.js.map +1 -0
  25. package/dist/lib/codec/WbxmlDecoder.js +154 -0
  26. package/dist/lib/codec/WbxmlDecoder.js.map +1 -0
  27. package/dist/lib/codec/WbxmlElement.js +31 -0
  28. package/dist/lib/codec/WbxmlElement.js.map +1 -0
  29. package/dist/lib/codec/WbxmlEncoder.js +88 -0
  30. package/dist/lib/codec/WbxmlEncoder.js.map +1 -0
  31. package/dist/lib/commands/ComposeMailCommand.js +187 -0
  32. package/dist/lib/commands/ComposeMailCommand.js.map +1 -0
  33. package/dist/lib/commands/FolderSyncCommand.js +150 -0
  34. package/dist/lib/commands/FolderSyncCommand.js.map +1 -0
  35. package/dist/lib/commands/GetItemEstimateCommand.js +149 -0
  36. package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -0
  37. package/dist/lib/commands/ItemOperationsCommand.js +337 -0
  38. package/dist/lib/commands/ItemOperationsCommand.js.map +1 -0
  39. package/dist/lib/commands/MeetingResponseCommand.js +120 -0
  40. package/dist/lib/commands/MeetingResponseCommand.js.map +1 -0
  41. package/dist/lib/commands/MoveItemsCommand.js +103 -0
  42. package/dist/lib/commands/MoveItemsCommand.js.map +1 -0
  43. package/dist/lib/commands/PingCommand.js +145 -0
  44. package/dist/lib/commands/PingCommand.js.map +1 -0
  45. package/dist/lib/commands/ProvisionCommand.js +164 -0
  46. package/dist/lib/commands/ProvisionCommand.js.map +1 -0
  47. package/dist/lib/commands/ResolveRecipientsCommand.js +124 -0
  48. package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -0
  49. package/dist/lib/commands/SearchCommand.js +137 -0
  50. package/dist/lib/commands/SearchCommand.js.map +1 -0
  51. package/dist/lib/commands/SendMailCommand.js +18 -0
  52. package/dist/lib/commands/SendMailCommand.js.map +1 -0
  53. package/dist/lib/commands/SettingsCommand.js +138 -0
  54. package/dist/lib/commands/SettingsCommand.js.map +1 -0
  55. package/dist/lib/commands/SmartForwardCommand.js +22 -0
  56. package/dist/lib/commands/SmartForwardCommand.js.map +1 -0
  57. package/dist/lib/commands/SmartReplyCommand.js +22 -0
  58. package/dist/lib/commands/SmartReplyCommand.js.map +1 -0
  59. package/dist/lib/commands/SyncCommand.js +406 -0
  60. package/dist/lib/commands/SyncCommand.js.map +1 -0
  61. package/dist/lib/commands/mongo/FolderSyncCommandMongo.js +16 -0
  62. package/dist/lib/commands/mongo/FolderSyncCommandMongo.js.map +1 -0
  63. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +21 -0
  64. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -0
  65. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +18 -0
  66. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -0
  67. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +17 -0
  68. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -0
  69. package/dist/lib/commands/mongo/MoveItemsCommandMongo.js +17 -0
  70. package/dist/lib/commands/mongo/MoveItemsCommandMongo.js.map +1 -0
  71. package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js +20 -0
  72. package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js.map +1 -0
  73. package/dist/lib/commands/mongo/SearchCommandMongo.js +20 -0
  74. package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -0
  75. package/dist/lib/commands/mongo/SendMailCommandMongo.js +17 -0
  76. package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -0
  77. package/dist/lib/commands/mongo/SettingsCommandMongo.js +16 -0
  78. package/dist/lib/commands/mongo/SettingsCommandMongo.js.map +1 -0
  79. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +17 -0
  80. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -0
  81. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +17 -0
  82. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -0
  83. package/dist/lib/commands/mongo/SyncCommandMongo.js +26 -0
  84. package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -0
  85. package/dist/lib/commands/mongo/index.js +14 -0
  86. package/dist/lib/commands/mongo/index.js.map +1 -0
  87. package/dist/lib/commands/sql/FolderSyncCommandSQL.js +16 -0
  88. package/dist/lib/commands/sql/FolderSyncCommandSQL.js.map +1 -0
  89. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +21 -0
  90. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -0
  91. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +18 -0
  92. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -0
  93. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +17 -0
  94. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -0
  95. package/dist/lib/commands/sql/MoveItemsCommandSQL.js +17 -0
  96. package/dist/lib/commands/sql/MoveItemsCommandSQL.js.map +1 -0
  97. package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js +20 -0
  98. package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js.map +1 -0
  99. package/dist/lib/commands/sql/SearchCommandSQL.js +20 -0
  100. package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -0
  101. package/dist/lib/commands/sql/SendMailCommandSQL.js +17 -0
  102. package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -0
  103. package/dist/lib/commands/sql/SettingsCommandSQL.js +16 -0
  104. package/dist/lib/commands/sql/SettingsCommandSQL.js.map +1 -0
  105. package/dist/lib/commands/sql/SmartForwardCommandSQL.js +17 -0
  106. package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -0
  107. package/dist/lib/commands/sql/SmartReplyCommandSQL.js +17 -0
  108. package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -0
  109. package/dist/lib/commands/sql/SyncCommandSQL.js +26 -0
  110. package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -0
  111. package/dist/lib/commands/sql/index.js +14 -0
  112. package/dist/lib/commands/sql/index.js.map +1 -0
  113. package/dist/lib/index.js +56 -0
  114. package/dist/lib/index.js.map +1 -0
  115. package/dist/lib/mongo/DeviceSyncStateRouteMongo.js +16 -0
  116. package/dist/lib/mongo/DeviceSyncStateRouteMongo.js.map +1 -0
  117. package/dist/lib/mongo/EasRouteMongo.js +51 -0
  118. package/dist/lib/mongo/EasRouteMongo.js.map +1 -0
  119. package/dist/lib/mongo/index.js +8 -0
  120. package/dist/lib/mongo/index.js.map +1 -0
  121. package/dist/lib/mongo.js +6 -0
  122. package/dist/lib/mongo.js.map +1 -0
  123. package/dist/lib/sql/DeviceSyncStateRouteSQL.js +16 -0
  124. package/dist/lib/sql/DeviceSyncStateRouteSQL.js.map +1 -0
  125. package/dist/lib/sql/EasRouteSQL.js +50 -0
  126. package/dist/lib/sql/EasRouteSQL.js.map +1 -0
  127. package/dist/lib/sql/index.js +8 -0
  128. package/dist/lib/sql/index.js.map +1 -0
  129. package/dist/lib/sql.js +6 -0
  130. package/dist/lib/sql.js.map +1 -0
  131. package/dist/types/BaseDeviceSyncStateRoute.d.ts +34 -0
  132. package/dist/types/BaseEasRoute.d.ts +64 -0
  133. package/dist/types/CompactDateTime.d.ts +22 -0
  134. package/dist/types/EasCommandHandler.d.ts +59 -0
  135. package/dist/types/EasSyncKeyUtils.d.ts +89 -0
  136. package/dist/types/adapters/CalendarSyncAdapter.d.ts +52 -0
  137. package/dist/types/adapters/ContactsSyncAdapter.d.ts +33 -0
  138. package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +52 -0
  139. package/dist/types/adapters/EmailSyncAdapter.d.ts +53 -0
  140. package/dist/types/adapters/TasksSyncAdapter.d.ts +32 -0
  141. package/dist/types/codec/WbxmlCodePages.d.ts +45 -0
  142. package/dist/types/codec/WbxmlDecoder.d.ts +33 -0
  143. package/dist/types/codec/WbxmlElement.d.ts +43 -0
  144. package/dist/types/codec/WbxmlEncoder.d.ts +20 -0
  145. package/dist/types/commands/ComposeMailCommand.d.ts +50 -0
  146. package/dist/types/commands/FolderSyncCommand.d.ts +25 -0
  147. package/dist/types/commands/GetItemEstimateCommand.d.ts +43 -0
  148. package/dist/types/commands/ItemOperationsCommand.d.ts +72 -0
  149. package/dist/types/commands/MeetingResponseCommand.d.ts +32 -0
  150. package/dist/types/commands/MoveItemsCommand.d.ts +29 -0
  151. package/dist/types/commands/PingCommand.d.ts +33 -0
  152. package/dist/types/commands/ProvisionCommand.d.ts +54 -0
  153. package/dist/types/commands/ResolveRecipientsCommand.d.ts +34 -0
  154. package/dist/types/commands/SearchCommand.d.ts +39 -0
  155. package/dist/types/commands/SendMailCommand.d.ts +10 -0
  156. package/dist/types/commands/SettingsCommand.d.ts +37 -0
  157. package/dist/types/commands/SmartForwardCommand.d.ts +17 -0
  158. package/dist/types/commands/SmartReplyCommand.d.ts +17 -0
  159. package/dist/types/commands/SyncCommand.d.ts +100 -0
  160. package/dist/types/commands/mongo/FolderSyncCommandMongo.d.ts +8 -0
  161. package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +7 -0
  162. package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +9 -0
  163. package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +8 -0
  164. package/dist/types/commands/mongo/MoveItemsCommandMongo.d.ts +8 -0
  165. package/dist/types/commands/mongo/ResolveRecipientsCommandMongo.d.ts +8 -0
  166. package/dist/types/commands/mongo/SearchCommandMongo.d.ts +8 -0
  167. package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +8 -0
  168. package/dist/types/commands/mongo/SettingsCommandMongo.d.ts +7 -0
  169. package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +8 -0
  170. package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +8 -0
  171. package/dist/types/commands/mongo/SyncCommandMongo.d.ts +8 -0
  172. package/dist/types/commands/mongo/index.d.ts +9 -0
  173. package/dist/types/commands/sql/FolderSyncCommandSQL.d.ts +8 -0
  174. package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +7 -0
  175. package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +9 -0
  176. package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +8 -0
  177. package/dist/types/commands/sql/MoveItemsCommandSQL.d.ts +8 -0
  178. package/dist/types/commands/sql/ResolveRecipientsCommandSQL.d.ts +8 -0
  179. package/dist/types/commands/sql/SearchCommandSQL.d.ts +8 -0
  180. package/dist/types/commands/sql/SendMailCommandSQL.d.ts +8 -0
  181. package/dist/types/commands/sql/SettingsCommandSQL.d.ts +7 -0
  182. package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +8 -0
  183. package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +8 -0
  184. package/dist/types/commands/sql/SyncCommandSQL.d.ts +8 -0
  185. package/dist/types/commands/sql/index.d.ts +9 -0
  186. package/dist/types/index.d.ts +51 -0
  187. package/dist/types/mongo/DeviceSyncStateRouteMongo.d.ts +8 -0
  188. package/dist/types/mongo/EasRouteMongo.d.ts +14 -0
  189. package/dist/types/mongo/index.d.ts +3 -0
  190. package/dist/types/mongo.d.ts +1 -0
  191. package/dist/types/sql/DeviceSyncStateRouteSQL.d.ts +8 -0
  192. package/dist/types/sql/EasRouteSQL.d.ts +13 -0
  193. package/dist/types/sql/index.d.ts +3 -0
  194. package/dist/types/sql.d.ts +1 -0
  195. package/package.json +94 -0
@@ -0,0 +1,230 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ // SPDX-License-Identifier: MPL-2.0
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ import * as crypto from "crypto";
15
+ import { ObjectDecorators } from "@rapidrest/core";
16
+ import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
17
+ import { childText, element, findChild, opaqueElement, textElement } from "../codec/WbxmlElement.js";
18
+ import { MessageImportance, RecipientType } from "@rapidmx/restapi";
19
+ const { Inject } = ObjectDecorators;
20
+ /** MS-ASEMAIL `Importance`: 0=Low, 1=Normal, 2=High. */
21
+ const IMPORTANCE_CODES = {
22
+ [MessageImportance.LOW]: "0",
23
+ [MessageImportance.NORMAL]: "1",
24
+ [MessageImportance.HIGH]: "2",
25
+ };
26
+ const IMPORTANCE_BY_CODE = {
27
+ "0": MessageImportance.LOW,
28
+ "1": MessageImportance.NORMAL,
29
+ "2": MessageImportance.HIGH,
30
+ };
31
+ /** MS-ASAIRSYNCBASE `Body.Type`: 1 = plain text, 2 = HTML, 3 = RTF, 4 = MIME. */
32
+ const BODY_TYPE_PLAIN_TEXT = "1";
33
+ /**
34
+ * Maps `Message` to/from the EAS `Sync` `Email` collection class (MS-ASEMAIL). Only a plain-text preview of the
35
+ * body is included here (`Message.bodyPreview`, always already loaded on the entity, `Truncated: 1`) rather
36
+ * than the full sanitized HTML body from the `BlobStore` - a real device fetches the full body on demand via
37
+ * `ItemOperations`' `Fetch` (see the architecture plan's command table), the same two-step "list, then fetch
38
+ * body" flow every real EAS client already implements for exactly this reason (bodies can be large; a sync
39
+ * window's Add/Change list shouldn't have to pull every one of them from blob storage up front).
40
+ *
41
+ * Also handles client-originated `Add`/`Change` for Drafts (`SyncCommand`'s own doc comment covers why this is
42
+ * the only `Email` write EAS itself allows) - a plain-text-only pragmatic subset: no HTML body, no attachments
43
+ * (mirrors `ComposeMailCommand`'s own already-documented attachment gap). `To`/`Cc`/`Bcc` (the latter MS-ASEMAIL2's
44
+ * own `Bcc` tag) are ghosted independently per recipient type, not as one combined group - a `Change` touching
45
+ * only one of them leaves the others untouched, carried over from `existing.recipients`.
46
+ *
47
+ * Emits MS-ASEMAIL2's `Email2:ConversationId` (read-only - no `fromApplicationData` handling, since EAS itself
48
+ * never lets a client set it) whenever `Message.conversationId` is populated, so a device's threaded-view UI can
49
+ * group messages the same way `BaseMessageRoute.conversations()` does server-side. See `encodeConversationId`'s
50
+ * own doc comment for the wire encoding, and `ItemOperationsCommand`'s `Move` handling for the one place this
51
+ * gets decoded back.
52
+ *
53
+ * @author Jean-Philippe Steinmetz
54
+ */
55
+ export class EmailSyncAdapter {
56
+ constructor() {
57
+ this.collectionClass = "Email";
58
+ }
59
+ toApplicationData(message) {
60
+ const to = message.recipients.filter((r) => r.type === RecipientType.TO).map((r) => r.address);
61
+ const cc = message.recipients.filter((r) => r.type === RecipientType.CC).map((r) => r.address);
62
+ const bcc = message.recipients.filter((r) => r.type === RecipientType.BCC).map((r) => r.address);
63
+ return element(WbxmlCodePage.AirSync, "ApplicationData", [
64
+ textElement(WbxmlCodePage.Email, "Subject", message.subject),
65
+ textElement(WbxmlCodePage.Email, "From", formatAddress(message.from.address, message.from.displayName)),
66
+ ...(to.length > 0 ? [textElement(WbxmlCodePage.Email, "To", to.join("; "))] : []),
67
+ ...(cc.length > 0 ? [textElement(WbxmlCodePage.Email, "Cc", cc.join("; "))] : []),
68
+ ...(bcc.length > 0 ? [textElement(WbxmlCodePage.Email2, "Bcc", bcc.join("; "))] : []),
69
+ textElement(WbxmlCodePage.Email, "DateReceived", message.receivedDate.toISOString()),
70
+ textElement(WbxmlCodePage.Email, "Importance", IMPORTANCE_CODES[message.importance]),
71
+ textElement(WbxmlCodePage.Email, "Read", message.flags.read ? "1" : "0"),
72
+ textElement(WbxmlCodePage.Email, "Flag", message.flags.flagged ? "1" : "0"),
73
+ element(WbxmlCodePage.AirSyncBase, "Body", [
74
+ textElement(WbxmlCodePage.AirSyncBase, "Type", BODY_TYPE_PLAIN_TEXT),
75
+ textElement(WbxmlCodePage.AirSyncBase, "EstimatedDataSize", String(Buffer.byteLength(message.bodyPreview, "utf8"))),
76
+ textElement(WbxmlCodePage.AirSyncBase, "Truncated", "1"),
77
+ textElement(WbxmlCodePage.AirSyncBase, "Data", message.bodyPreview),
78
+ ]),
79
+ ...(message.conversationId ? [opaqueElement(WbxmlCodePage.Email2, "ConversationId", encodeConversationId(message.conversationId))] : []),
80
+ ]);
81
+ }
82
+ /**
83
+ * `Message.bodyBlobKey` is documented (see the `Message` interface itself) as holding raw MIME "unmodified
84
+ * from ingestion/send" - `ItemOperationsCommand.fetchMessage` parses it with `simpleParser` unconditionally
85
+ * for every message, Draft or not. A Draft's plain-text body is therefore wrapped in a minimal valid
86
+ * RFC 5322 message here (via `buildPlainTextMime`) rather than stored as bare text, so that contract holds
87
+ * for every consumer, not just this write path - a Draft created/edited via `Sync` must `Fetch` correctly
88
+ * the same way any other message does.
89
+ */
90
+ async fromApplicationData(el, existing) {
91
+ const partial = {};
92
+ const subject = childText(el, "Subject");
93
+ if (subject !== undefined)
94
+ partial.subject = subject;
95
+ const to = childText(el, "To");
96
+ const cc = childText(el, "Cc");
97
+ const bcc = childText(el, "Bcc");
98
+ if (to !== undefined || cc !== undefined || bcc !== undefined) {
99
+ // Ghosted per-recipient-type, not as one combined group: a `Change` touching only `To` must leave
100
+ // any existing `Cc`/`Bcc` recipients alone, so untouched types are carried over from `existing`
101
+ // rather than the whole `recipients` array being rebuilt from just what's present in `el`.
102
+ const untouched = (existing?.recipients ?? []).filter((r) => (r.type !== RecipientType.TO || to === undefined) &&
103
+ (r.type !== RecipientType.CC || cc === undefined) &&
104
+ (r.type !== RecipientType.BCC || bcc === undefined));
105
+ partial.recipients = [
106
+ ...untouched,
107
+ ...(to !== undefined ? parseAddressList(to, RecipientType.TO) : []),
108
+ ...(cc !== undefined ? parseAddressList(cc, RecipientType.CC) : []),
109
+ ...(bcc !== undefined ? parseAddressList(bcc, RecipientType.BCC) : []),
110
+ ];
111
+ }
112
+ const importance = childText(el, "Importance");
113
+ if (importance !== undefined) {
114
+ partial.importance = IMPORTANCE_BY_CODE[importance] ?? MessageImportance.NORMAL;
115
+ }
116
+ const read = childText(el, "Read");
117
+ const flag = childText(el, "Flag");
118
+ if (read !== undefined || flag !== undefined) {
119
+ const baseFlags = existing?.flags ?? { read: false, flagged: false, answered: false, forwarded: false };
120
+ partial.flags = {
121
+ ...baseFlags,
122
+ ...(read !== undefined ? { read: read === "1" } : {}),
123
+ ...(flag !== undefined ? { flagged: flag === "1" } : {}),
124
+ };
125
+ }
126
+ const bodyEl = findChild(el, "Body");
127
+ if (bodyEl) {
128
+ const text = childText(bodyEl, "Data") ?? "";
129
+ // Reuse the existing blob key on a Change (overwriting its content) rather than minting a new one
130
+ // - a fresh key is only needed the first time a body is set (a bare `existing.bodyBlobKey` of ""
131
+ // means an earlier Add never included a Body element at all).
132
+ const bodyBlobKey = existing?.bodyBlobKey || `bodies/${crypto.randomUUID()}`;
133
+ const mime = buildPlainTextMime({
134
+ subject: partial.subject ?? existing?.subject ?? "",
135
+ from: existing?.from,
136
+ recipients: partial.recipients ?? existing?.recipients ?? [],
137
+ date: existing?.sentDate ?? new Date(),
138
+ text,
139
+ });
140
+ await this.blobStore.put(bodyBlobKey, Buffer.from(mime, "utf-8"), { contentType: "message/rfc822" });
141
+ partial.bodyBlobKey = bodyBlobKey;
142
+ partial.bodyPreview = text.slice(0, 200);
143
+ }
144
+ return partial;
145
+ }
146
+ /** Defaults for a brand-new Draft created via a client-originated `Add` - `from` is the caller's own
147
+ * mailbox address, per `ComposeMailCommand`'s identical `{ address, type: RecipientType.TO }` shape
148
+ * convention for a `from` field (the `Recipient` struct's `type` is only meaningful for real recipients;
149
+ * it's reused here as a harmless placeholder). */
150
+ newEntityDefaults(mailbox) {
151
+ return {
152
+ messageId: `<${crypto.randomUUID()}@eas>`,
153
+ subject: "",
154
+ from: { address: mailbox.primarySmtpAddress, displayName: mailbox.displayName, type: RecipientType.TO },
155
+ recipients: [],
156
+ sentDate: new Date(),
157
+ receivedDate: new Date(),
158
+ bodyBlobKey: "",
159
+ bodyPreview: "",
160
+ flags: { read: true, flagged: false, answered: false, forwarded: false },
161
+ importance: MessageImportance.NORMAL,
162
+ references: [],
163
+ hasAttachments: false,
164
+ };
165
+ }
166
+ }
167
+ __decorate([
168
+ Inject("BlobStore"),
169
+ __metadata("design:type", Object)
170
+ ], EmailSyncAdapter.prototype, "blobStore", void 0);
171
+ /** Encodes `Message.conversationId` (an internal string uid) into the opaque binary blob MS-ASEMAIL2's
172
+ * `Email2:ConversationId` carries on the wire. The spec never mandates any particular binary format for this
173
+ * value - a real Exchange server mints a GUID, but a client only ever compares/echoes it byte-for-byte, never
174
+ * interprets it - so encoding the uid's own UTF-8 bytes directly (rather than hashing into a 16-byte GUID
175
+ * shape) is a valid, simpler choice that `decodeConversationId` can invert exactly, which `ItemOperationsCommand`
176
+ * relies on to resolve an `ItemOperations` `Move`'s `ConversationId` back into this same uid. */
177
+ export function encodeConversationId(conversationId) {
178
+ return Buffer.from(conversationId, "utf8");
179
+ }
180
+ /** Reverse of `encodeConversationId`. */
181
+ export function decodeConversationId(opaque) {
182
+ return opaque.toString("utf8");
183
+ }
184
+ function formatAddress(address, displayName) {
185
+ return displayName ? `${displayName} <${address}>` : address;
186
+ }
187
+ /** Parses a `;`/`,`-separated address list (`"Name <a@x.com>; b@y.com"`, or the bare-address-only form this
188
+ * adapter's own `toApplicationData` emits) into `Recipient`s of `type`. */
189
+ function parseAddressList(value, type) {
190
+ return value
191
+ .split(/[;,]/)
192
+ .map((part) => part.trim())
193
+ .filter((part) => part.length > 0)
194
+ .map((part) => {
195
+ const match = /^(.*)<(.+)>$/.exec(part);
196
+ return match
197
+ ? { address: match[2].trim(), displayName: match[1].trim() || undefined, type }
198
+ : { address: part, type };
199
+ });
200
+ }
201
+ /** Strips CR/LF from a value about to be interpolated into a single RFC 5322 header line. `Subject`/`To`/`Cc`
202
+ * arrive from client-controlled WBXML inline strings (`WbxmlDecoder.readCString()`, which terminates only on a
203
+ * NUL byte - literal `\r`/`\n` bytes pass through untouched), so without this a crafted value like
204
+ * `"Hi\r\nBcc: attacker@evil.com"` would inject an arbitrary extra header line (or, via a blank line, a forged
205
+ * second message) into the constructed MIME below. Folds onto a single line rather than rejecting outright -
206
+ * a real device is never expected to send this, but a header value silently losing its embedded newlines is
207
+ * safer than the request failing outright over what a client will never notice either way. */
208
+ function sanitizeHeaderValue(value) {
209
+ return value.replace(/[\r\n]+/g, " ");
210
+ }
211
+ /** Builds a minimal, valid RFC 5322 plain-text message - just enough structure for `simpleParser` (used by
212
+ * `ItemOperationsCommand.fetchMessage`) to read it back correctly. No multipart/HTML/attachments - matches this
213
+ * adapter's own documented pragmatic-subset scope. */
214
+ function buildPlainTextMime(parts) {
215
+ const to = parts.recipients.filter((r) => r.type === RecipientType.TO).map((r) => formatAddress(r.address, r.displayName));
216
+ const cc = parts.recipients.filter((r) => r.type === RecipientType.CC).map((r) => formatAddress(r.address, r.displayName));
217
+ const bcc = parts.recipients.filter((r) => r.type === RecipientType.BCC).map((r) => formatAddress(r.address, r.displayName));
218
+ const headers = [
219
+ ...(parts.from ? [`From: ${sanitizeHeaderValue(formatAddress(parts.from.address, parts.from.displayName))}`] : []),
220
+ ...(to.length > 0 ? [`To: ${sanitizeHeaderValue(to.join(", "))}`] : []),
221
+ ...(cc.length > 0 ? [`Cc: ${sanitizeHeaderValue(cc.join(", "))}`] : []),
222
+ ...(bcc.length > 0 ? [`Bcc: ${sanitizeHeaderValue(bcc.join(", "))}`] : []),
223
+ `Subject: ${sanitizeHeaderValue(parts.subject)}`,
224
+ `Date: ${parts.date.toUTCString()}`,
225
+ "MIME-Version: 1.0",
226
+ "Content-Type: text/plain; charset=utf-8",
227
+ ];
228
+ return `${headers.join("\r\n")}\r\n\r\n${parts.text}`;
229
+ }
230
+ //# sourceMappingURL=EmailSyncAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailSyncAdapter.js","sourceRoot":"","sources":["../../../src/adapters/EmailSyncAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
6
+ import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
7
+ import { fromCompactDateTime, toCompactDateTime } from "../CompactDateTime.js";
8
+ import { TaskPriority } from "@rapidmx/restapi";
9
+ /** MS-ASTASK `Importance` reuses the same 0/1/2 scale as MS-ASEMAIL's `Importance` (see `EmailSyncAdapter`'s
10
+ * own identical table) - Low/Normal/High. */
11
+ const IMPORTANCE_CODES = {
12
+ [TaskPriority.LOW]: "0",
13
+ [TaskPriority.NORMAL]: "1",
14
+ [TaskPriority.HIGH]: "2",
15
+ };
16
+ const IMPORTANCE_FROM_CODE = Object.fromEntries(Object.entries(IMPORTANCE_CODES).map(([k, v]) => [v, k]));
17
+ /**
18
+ * Maps `Task` to/from the EAS `Sync` `Tasks` collection class (MS-ASTASK).
19
+ *
20
+ * **Pragmatic subset**: only the `Utc*` (UTC Compact DateTime) variant of each date field is emitted, not the
21
+ * paired local-time variant MS-ASTASK also defines (`DueDate`/`StartDate` alongside `UtcDueDate`/
22
+ * `UtcStartDate`) - this library doesn't track a per-task timezone to correctly localize the non-UTC variant,
23
+ * and a real device already treats the `Utc*` field as authoritative. Recurring tasks are not synced (a task's
24
+ * `Recurrence` element mirrors Calendar's, itself already a pragmatic subset there - deferred further here).
25
+ *
26
+ * @author Jean-Philippe Steinmetz
27
+ */
28
+ export class TasksSyncAdapter {
29
+ constructor() {
30
+ this.collectionClass = "Tasks";
31
+ }
32
+ toApplicationData(task) {
33
+ return element(WbxmlCodePage.AirSync, "ApplicationData", [
34
+ textElement(WbxmlCodePage.Tasks, "Subject", task.title),
35
+ textElement(WbxmlCodePage.Tasks, "Complete", task.completed ? "1" : "0"),
36
+ ...(task.completed ? [textElement(WbxmlCodePage.Tasks, "DateCompleted", toCompactDateTime(task.dateModified))] : []),
37
+ ...(task.dueDate ? [textElement(WbxmlCodePage.Tasks, "UtcDueDate", toCompactDateTime(task.dueDate))] : []),
38
+ textElement(WbxmlCodePage.Tasks, "Importance", IMPORTANCE_CODES[task.priority]),
39
+ textElement(WbxmlCodePage.Tasks, "Sensitivity", "0"),
40
+ textElement(WbxmlCodePage.Tasks, "ReminderSet", task.reminderDate ? "1" : "0"),
41
+ ...(task.reminderDate ? [textElement(WbxmlCodePage.Tasks, "ReminderTime", toCompactDateTime(task.reminderDate))] : []),
42
+ ...(task.body
43
+ ? [
44
+ element(WbxmlCodePage.AirSyncBase, "Body", [
45
+ textElement(WbxmlCodePage.AirSyncBase, "Type", "1"),
46
+ textElement(WbxmlCodePage.AirSyncBase, "EstimatedDataSize", String(Buffer.byteLength(task.body, "utf8"))),
47
+ textElement(WbxmlCodePage.AirSyncBase, "Data", task.body),
48
+ ]),
49
+ ]
50
+ : []),
51
+ ]);
52
+ }
53
+ /**
54
+ * Reverse of `toApplicationData`. `DateCompleted` is never parsed back - it's `task.dateModified` echoed
55
+ * out, not an independent field this library's own `Task` model has room to store separately, so a client
56
+ * setting `Complete` is enough on its own. `ReminderSet="0"` (with no `ReminderTime`) is treated as an
57
+ * explicit "clear the reminder" signal (`reminderDate: null` in the returned partial, which - unlike
58
+ * simply omitting the key - does override an existing reminder when merged onto `existing` for a `Change`)
59
+ * since MS-ASTASK gives no other way to express removing a reminder; `UtcDueDate` has no equivalent
60
+ * explicit-clear signal and so can only be set, never cleared, via `Sync` - a real, narrower gap than
61
+ * `reminderDate`'s, documented here rather than silently accepted. `null`, not `undefined`: TypeORM's
62
+ * `UpdateQueryBuilder` silently drops any `undefined`-valued key from the generated SQL `SET` clause
63
+ * (confirmed by reading its source), so `undefined` here would leave a stale `reminderDate` in place on
64
+ * the SQL backend while correctly clearing it on Mongo - `null` clears it on both.
65
+ */
66
+ fromApplicationData(el) {
67
+ const partial = {};
68
+ const subject = childText(el, "Subject");
69
+ if (subject !== undefined)
70
+ partial.title = subject;
71
+ const complete = childText(el, "Complete");
72
+ if (complete !== undefined)
73
+ partial.completed = complete === "1";
74
+ const utcDueDate = childText(el, "UtcDueDate");
75
+ if (utcDueDate !== undefined)
76
+ partial.dueDate = fromCompactDateTime(utcDueDate);
77
+ const importance = childText(el, "Importance");
78
+ if (importance !== undefined) {
79
+ const mapped = IMPORTANCE_FROM_CODE[importance];
80
+ if (!mapped) {
81
+ throw new Error(`Unrecognized Importance value: '${importance}'`);
82
+ }
83
+ partial.priority = mapped;
84
+ }
85
+ const reminderSet = childText(el, "ReminderSet");
86
+ const reminderTime = childText(el, "ReminderTime");
87
+ if (reminderTime !== undefined) {
88
+ partial.reminderDate = fromCompactDateTime(reminderTime);
89
+ }
90
+ else if (reminderSet === "0") {
91
+ partial.reminderDate = null;
92
+ }
93
+ const bodyEl = findChild(el, "Body");
94
+ const body = bodyEl ? childText(bodyEl, "Data") : undefined;
95
+ if (body !== undefined)
96
+ partial.body = body;
97
+ return partial;
98
+ }
99
+ }
100
+ //# sourceMappingURL=TasksSyncAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TasksSyncAdapter.js","sourceRoot":"","sources":["../../../src/adapters/TasksSyncAdapter.ts"],"names":[],"mappings":"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"}