@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,234 @@
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 { ApiError, ObjectDecorators, StringUtils } from "@rapidrest/core";
15
+ import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
16
+ import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
17
+ import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
18
+ const { Config, Init, Inject } = ObjectDecorators;
19
+ /** Parses a `Range` value (`"m-n"`, a zero-based inclusive index pair) into `{ start, end }`, falling back to
20
+ * `defaultEnd` for a missing/malformed value - never trusting the client to request more than `maxEnd` rows. */
21
+ function parseRange(raw, defaultEnd, maxEnd) {
22
+ const match = raw?.match(/^(\d+)-(\d+)$/);
23
+ if (!match) {
24
+ return { start: 0, end: Math.min(defaultEnd, maxEnd) };
25
+ }
26
+ const start = Number(match[1]);
27
+ const end = Math.min(Number(match[2]), maxEnd);
28
+ return start <= end ? { start, end } : { start: 0, end: Math.min(defaultEnd, maxEnd) };
29
+ }
30
+ /** Clamps a matched-results array against a requested `[start, end]` Range, reporting both the sliced page and
31
+ * the actual Range/Total to echo back - shared by the `GAL` and `Mailbox` branches below, which otherwise
32
+ * differ only in how they produce `matches` and render one entry. Both ends are forced to `0` when there are no
33
+ * matches at all - `start` alone (from a client-requested Range like `"5-10"`) would otherwise survive
34
+ * unclamped, producing a malformed `"5-0"` (start > end) once `end` collapses to `0`. */
35
+ function paginate(matches, start, end) {
36
+ return {
37
+ page: matches.slice(start, end + 1),
38
+ rangeStart: matches.length === 0 ? 0 : start,
39
+ rangeEnd: matches.length === 0 ? 0 : Math.min(end, matches.length - 1),
40
+ };
41
+ }
42
+ /**
43
+ * Handles EAS `Search` for the `GAL` and `Mailbox` stores - `DocumentLibrary` (the real spec's third store type)
44
+ * remains out of scope, matching `ItemOperationsCommand`'s identical scope decision; this library has no
45
+ * document-library model.
46
+ *
47
+ * **`GAL`**: this library's `Contact` records are also the source of truth for GAL lookups against a mailbox's
48
+ * own address book (see the architecture note on `Contact` itself). A simple case-insensitive substring match
49
+ * via `RepoUtils.find()` directly, not the heavier `SearchProvider` full-text index - GAL lookups are
50
+ * small-scale exact/prefix matching against a personal address book, not relevance-ranked full text over large
51
+ * content. Only `displayName`/`givenName`/`surname`/`company` are matched - `Contact.emails`/`phones` are
52
+ * embedded arrays of objects, which a plain per-field regex query can't reach into on either backend (confirmed:
53
+ * MongoDB's `$regex` against an array-of-objects field matches nothing useful, and this library's own
54
+ * query-injection guard rejects dot-notation field paths like `"emails.address"` outright) - a documented gap,
55
+ * not an oversight.
56
+ *
57
+ * **`Mailbox`**: real full-text search over the caller's own `Email` messages, backed by `restapi`'s
58
+ * `SearchProvider` (its own full-text index, kept eventually-consistent with the primary datastore via
59
+ * `SearchIndexJob` - a just-sent/just-received message may briefly not be findable yet). **Pragmatic subset**:
60
+ * only `Class` `Email` is supported (a `Query` naming any other class is rejected, matching the `GAL`-only
61
+ * precedent this file already established for search generally); only the common real-world `Query` shape -
62
+ * `Class`/`CollectionId`/`FreeText`, optionally grouped under one `And` - is parsed, not the full recursive
63
+ * `And`/`Or`/`EqualTo`/`GreaterThan`/`LessThan` boolean-tree grammar MS-ASCMD's schema allows for, so a
64
+ * multi-level nested query silently only sees the first `And` group's own direct children. Each match is
65
+ * re-verified for `READ` on its own current `folderUid` before being included - `SearchProvider`'s index is
66
+ * scoped by `mailboxUid` alone, not per-folder ACL, so this is the one place that check still has to happen
67
+ * per-result rather than once up front. `SearchResultPage` carries no total count (the underlying providers
68
+ * don't compute one cheaply over a relevance-ranked query), so `Total` here means "how many matches this
69
+ * request's own capped fetch actually returned" - a client requesting a `Range` past that cap sees fewer
70
+ * results than may really exist, a documented approximation rather than exact server-side paging.
71
+ *
72
+ * `contactClass`/`messageClass`/`emailAdapterClass` are supplied by the Mongo/SQL concrete subclasses.
73
+ *
74
+ * @author Jean-Philippe Steinmetz
75
+ */
76
+ export class SearchCommand {
77
+ constructor() {
78
+ this.command = "Search";
79
+ this.defaultRangeEnd = 9;
80
+ this.maxRangeEnd = 99;
81
+ }
82
+ async init() {
83
+ this.contactRepo = await this._objectFactory.newInstance(RepoUtils, {
84
+ name: this.contactClass.name,
85
+ args: [this.contactClass],
86
+ });
87
+ this.messageRepo = await this._objectFactory.newInstance(RepoUtils, {
88
+ name: this.messageClass.name,
89
+ args: [this.messageClass],
90
+ });
91
+ this.emailAdapter = await this._objectFactory.newInstance(this.emailAdapterClass);
92
+ }
93
+ async handle(ctx) {
94
+ if (!this.contactRepo || !this.messageRepo || !this.searchProvider || !this.emailAdapter) {
95
+ throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
96
+ }
97
+ const storeEl = ctx.request ? findChild(ctx.request, "Store") : undefined;
98
+ const name = storeEl ? childText(storeEl, "Name") : undefined;
99
+ if (!storeEl || (name !== "GAL" && name !== "Mailbox")) {
100
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "Search requires a Store with Name 'GAL' or 'Mailbox'.");
101
+ }
102
+ const optionsEl = findChild(storeEl, "Options");
103
+ const { start, end } = parseRange(optionsEl ? childText(optionsEl, "Range") : undefined, this.defaultRangeEnd, this.maxRangeEnd);
104
+ const storeResponse = name === "GAL" ? await this.handleGal(ctx, storeEl, start, end) : await this.handleMailbox(ctx, storeEl, start, end);
105
+ return element(WbxmlCodePage.Search, "Search", [
106
+ textElement(WbxmlCodePage.Search, "Status", "1"),
107
+ element(WbxmlCodePage.Search, "Response", [storeResponse]),
108
+ ]);
109
+ }
110
+ async handleGal(ctx, storeEl, start, end) {
111
+ const query = childText(storeEl, "Query");
112
+ if (!query) {
113
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "Search requires a Store with Name 'GAL' and a Query.");
114
+ }
115
+ // `$or` is a Mongo-only feature of this framework's query builder - `buildSearchQuerySQL` (confirmed by
116
+ // reading its source) has no handling for it at all, so passing one on the SQL backend silently builds
117
+ // a broken TypeORM `where` clause (a literal `$or` property, not a real OR) and 500s. Querying each
118
+ // field separately and merging in memory - the same workaround `EasSyncKeyUtils.computeChanges()`
119
+ // already uses for its own two-backend query gap - works identically on both backends instead.
120
+ //
121
+ // Uses `regex()`, not `like()`: `like()` (`@rapidrest/service-core` ^2.0) compiles a **glob** pattern
122
+ // (`*`/`?` as wildcards), anchored on both backends, so a substring match needs wrapping the term in
123
+ // `*...*` - and even then a literal `*`/`?` the user typed still acts as a wildcard, since glob syntax
124
+ // has no escape mechanism for either character. `regex()` takes a real, unanchored regular expression
125
+ // compiled case-insensitively on both backends (`$regex`/driver-native `REGEXP`), so escaping the term
126
+ // with `StringUtils.escapeRegExp` gives a genuine literal-substring match with no residual wildcard
127
+ // ambiguity - no `*...*` wrapping needed, `regex()` already matches anywhere in the field by default.
128
+ const pattern = StringUtils.escapeRegExp(query);
129
+ const findOptions = { ignoreACL: true, limit: this.maxRangeEnd + 1 };
130
+ const perField = await Promise.all(["displayName", "givenName", "surname", "company"].map((field) => this.contactRepo.find({ mailboxUid: ctx.mailboxUid, [field]: `regex(${pattern})`, limit: this.maxRangeEnd + 1 }, findOptions)));
131
+ const byUid = new Map();
132
+ for (const contact of perField.flat()) {
133
+ byUid.set((contact).uid, contact);
134
+ }
135
+ const matches = Array.from(byUid.values()).sort((a, b) => a.displayName.localeCompare(b.displayName));
136
+ const { page, rangeStart, rangeEnd } = paginate(matches, start, end);
137
+ return element(WbxmlCodePage.Search, "Store", [
138
+ textElement(WbxmlCodePage.Search, "Status", "1"),
139
+ ...page.map((contact) => this.contactToResult(contact)),
140
+ textElement(WbxmlCodePage.Search, "Range", `${rangeStart}-${rangeEnd}`),
141
+ textElement(WbxmlCodePage.Search, "Total", String(matches.length)),
142
+ ]);
143
+ }
144
+ async handleMailbox(ctx, storeEl, start, end) {
145
+ const queryEl = findChild(storeEl, "Query");
146
+ if (!queryEl) {
147
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "Search requires a Store with Name 'Mailbox' and a Query.");
148
+ }
149
+ // The common real-world shape groups Class/CollectionId/FreeText under one And - see this class's own
150
+ // doc comment for why a deeper And/Or tree isn't parsed. Falling back to Query's own direct children
151
+ // also accepts a client that omits the And wrapper entirely, which the schema itself permits.
152
+ const container = findChild(queryEl, "And") ?? queryEl;
153
+ const className = childText(container, "Class");
154
+ const folderUid = childText(container, "CollectionId");
155
+ const freeText = childText(container, "FreeText");
156
+ if (className !== "Email" || !freeText) {
157
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "Search on the 'Mailbox' store requires a Query with Class 'Email' and a FreeText term.");
158
+ }
159
+ const resultPage = await this.searchProvider.search({
160
+ mailboxUid: ctx.mailboxUid,
161
+ text: freeText,
162
+ entityTypes: ["message"],
163
+ folderUid,
164
+ limit: this.maxRangeEnd + 1,
165
+ });
166
+ const matches = [];
167
+ for (const result of resultPage.results) {
168
+ const message = await this.messageRepo.findOne(result.entityUid, { ignoreACL: true });
169
+ if (!message) {
170
+ continue;
171
+ }
172
+ if (!(await this.aclUtils.hasPermission(ctx.user, message.folderUid, ACLAction.READ))) {
173
+ continue;
174
+ }
175
+ matches.push(message);
176
+ }
177
+ const { page, rangeStart, rangeEnd } = paginate(matches, start, end);
178
+ const results = await Promise.all(page.map((message) => this.messageToResult(message)));
179
+ return element(WbxmlCodePage.Search, "Store", [
180
+ textElement(WbxmlCodePage.Search, "Status", "1"),
181
+ ...results,
182
+ textElement(WbxmlCodePage.Search, "Range", `${rangeStart}-${rangeEnd}`),
183
+ textElement(WbxmlCodePage.Search, "Total", String(matches.length)),
184
+ ]);
185
+ }
186
+ contactToResult(contact) {
187
+ const properties = [
188
+ textElement(WbxmlCodePage.Gal, "DisplayName", contact.displayName),
189
+ ...(contact.givenName ? [textElement(WbxmlCodePage.Gal, "FirstName", contact.givenName)] : []),
190
+ ...(contact.surname ? [textElement(WbxmlCodePage.Gal, "LastName", contact.surname)] : []),
191
+ ...(contact.company ? [textElement(WbxmlCodePage.Gal, "Company", contact.company)] : []),
192
+ ...(contact.jobTitle ? [textElement(WbxmlCodePage.Gal, "Title", contact.jobTitle)] : []),
193
+ ...(contact.emails[0] ? [textElement(WbxmlCodePage.Gal, "EmailAddress", contact.emails[0].address)] : []),
194
+ ...(contact.phones[0] ? [textElement(WbxmlCodePage.Gal, "Phone", contact.phones[0].phoneNumber)] : []),
195
+ ];
196
+ return element(WbxmlCodePage.Search, "Result", [element(WbxmlCodePage.Search, "Properties", properties)]);
197
+ }
198
+ /** Reuses `EmailSyncAdapter.toApplicationData()`'s own field mapping (Subject/From/To/Cc/Bcc/DateReceived/
199
+ * Importance/Read/Flag/Body/ConversationId) for a Mailbox-store search hit's `Properties` - the same
200
+ * per-field shape `Sync` already renders for this message, rather than a second, parallel mapping that
201
+ * could drift out of sync with it. */
202
+ async messageToResult(message) {
203
+ const applicationData = await this.emailAdapter.toApplicationData(message);
204
+ return element(WbxmlCodePage.Search, "Result", [
205
+ textElement(WbxmlCodePage.AirSync, "Class", "Email"),
206
+ textElement(WbxmlCodePage.AirSync, "CollectionId", message.folderUid),
207
+ textElement(WbxmlCodePage.AirSync, "ServerId", message.uid),
208
+ element(WbxmlCodePage.Search, "Properties", applicationData.children),
209
+ ]);
210
+ }
211
+ }
212
+ __decorate([
213
+ Inject("SearchProvider"),
214
+ __metadata("design:type", Object)
215
+ ], SearchCommand.prototype, "searchProvider", void 0);
216
+ __decorate([
217
+ Inject(ACLUtils),
218
+ __metadata("design:type", ACLUtils)
219
+ ], SearchCommand.prototype, "aclUtils", void 0);
220
+ __decorate([
221
+ Config("mail:eas:search_default_range", 9),
222
+ __metadata("design:type", Number)
223
+ ], SearchCommand.prototype, "defaultRangeEnd", void 0);
224
+ __decorate([
225
+ Config("mail:eas:search_max_range", 99),
226
+ __metadata("design:type", Number)
227
+ ], SearchCommand.prototype, "maxRangeEnd", void 0);
228
+ __decorate([
229
+ Init,
230
+ __metadata("design:type", Function),
231
+ __metadata("design:paramtypes", []),
232
+ __metadata("design:returntype", Promise)
233
+ ], SearchCommand.prototype, "init", null);
234
+ //# sourceMappingURL=SearchCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchCommand.js","sourceRoot":"","sources":["../../../src/commands/SearchCommand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { ComposeMailCommand } from "./ComposeMailCommand.js";
6
+ /**
7
+ * Handles EAS `SendMail`: relays a freshly composed message with no referenced original - see
8
+ * `ComposeMailCommand`'s own doc comment for the full shared implementation.
9
+ *
10
+ * @author Jean-Philippe Steinmetz
11
+ */
12
+ export class SendMailCommand extends ComposeMailCommand {
13
+ constructor() {
14
+ super(...arguments);
15
+ this.command = "SendMail";
16
+ }
17
+ }
18
+ //# sourceMappingURL=SendMailCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SendMailCommand.js","sourceRoot":"","sources":["../../../src/commands/SendMailCommand.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,OAAgB,eAAgB,SAAQ,kBAAkB;IAAhE;;QACoB,YAAO,GAAG,UAAU,CAAC;IACzC,CAAC;CAAA"}
@@ -0,0 +1,138 @@
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 { ApiError, ObjectDecorators } from "@rapidrest/core";
15
+ import { ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
16
+ import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
17
+ import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
18
+ const { Init } = ObjectDecorators;
19
+ /**
20
+ * Handles EAS `Settings`: the first-run/general-purpose device<->server settings exchange. This pragmatic
21
+ * subset supports the sub-elements every real client actually depends on:
22
+ *
23
+ * - `UserInformation`/`Get`: returns the mailbox's `primarySmtpAddress`/`aliasAddresses` as `EmailAddresses`.
24
+ * - `DeviceInformation`/`Set`: acknowledged with `Status 1` but not persisted anywhere - `DeviceSyncState` has
25
+ * no fields for a device's model/IMEI/OS/friendly name, and nothing else in this library currently consumes
26
+ * them. A real client only requires the acknowledgement to proceed past first-run setup, not that the values
27
+ * are retrievable later.
28
+ * - `Oof`/`Get` and `Oof`/`Set`: reads/writes `Mailbox.oofEnabled`/`oofMessage`/`oofStartTime`/`oofEndTime`.
29
+ * `StartTime`/`EndTime` use MS-ASDTYPE's plain `dateTime` type (`Date.prototype.toISOString()`), not Compact
30
+ * DateTime - confirmed against MS-ASSETTINGS directly, unlike `Calendar`/`Tasks`' timestamp fields (see
31
+ * `CompactDateTime.ts`'s own doc comment on that exact distinction). A single combined reply message is stored
32
+ * rather than the spec's three audience-specific `OofMessage` variants (internal/external-known/
33
+ * external-unknown) - matches this codebase's existing "one thing, not three" simplification precedent (e.g.
34
+ * `ContactsSyncAdapter`'s single-slot phone/email handling).
35
+ * - `RightsManagementInformation` is not implemented - deferred, matching this library's "pragmatic subset"
36
+ * precedent elsewhere (e.g. `ComposeMailCommand`'s own documented gaps).
37
+ *
38
+ * `mailboxClass` is supplied by the Mongo/SQL concrete subclasses.
39
+ *
40
+ * @author Jean-Philippe Steinmetz
41
+ */
42
+ export class SettingsCommand {
43
+ constructor() {
44
+ this.command = "Settings";
45
+ }
46
+ async init() {
47
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
48
+ name: this.mailboxClass.name,
49
+ args: [this.mailboxClass],
50
+ });
51
+ }
52
+ async handle(ctx) {
53
+ if (!this.mailboxRepo) {
54
+ throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
55
+ }
56
+ const children = [textElement(WbxmlCodePage.Settings, "Status", "1")];
57
+ if (ctx.request && findChild(ctx.request, "DeviceInformation")) {
58
+ children.push(element(WbxmlCodePage.Settings, "DeviceInformation", [textElement(WbxmlCodePage.Settings, "Status", "1")]));
59
+ }
60
+ if (ctx.request && findChild(ctx.request, "UserInformation")) {
61
+ const mailbox = await this.mailboxRepo.findOne(ctx.mailboxUid, { ignoreACL: true });
62
+ if (!mailbox) {
63
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
64
+ }
65
+ children.push(element(WbxmlCodePage.Settings, "UserInformation", [
66
+ textElement(WbxmlCodePage.Settings, "Status", "1"),
67
+ element(WbxmlCodePage.Settings, "EmailAddresses", [
68
+ textElement(WbxmlCodePage.Settings, "SmtpAddress", mailbox.primarySmtpAddress),
69
+ ...mailbox.aliasAddresses.map((address) => textElement(WbxmlCodePage.Settings, "SmtpAddress", address)),
70
+ ]),
71
+ ]));
72
+ }
73
+ const oofEl = ctx.request ? findChild(ctx.request, "Oof") : undefined;
74
+ if (oofEl) {
75
+ const mailbox = await this.mailboxRepo.findOne(ctx.mailboxUid, { ignoreACL: true });
76
+ if (!mailbox) {
77
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
78
+ }
79
+ const setEl = findChild(oofEl, "Set");
80
+ children.push(setEl ? await this.setOof(ctx, mailbox, setEl) : this.getOof(mailbox));
81
+ }
82
+ return element(WbxmlCodePage.Settings, "Settings", children);
83
+ }
84
+ /** `OofState`: `0`=disabled, `1`=enabled indefinitely, `2`=time-based (only while now is within
85
+ * `StartTime`/`EndTime`). */
86
+ getOof(mailbox) {
87
+ const timed = mailbox.oofEnabled && !!mailbox.oofStartTime && !!mailbox.oofEndTime;
88
+ return element(WbxmlCodePage.Settings, "Oof", [
89
+ textElement(WbxmlCodePage.Settings, "Status", "1"),
90
+ element(WbxmlCodePage.Settings, "Get", [
91
+ textElement(WbxmlCodePage.Settings, "OofState", mailbox.oofEnabled ? (timed ? "2" : "1") : "0"),
92
+ ...(timed
93
+ ? [
94
+ textElement(WbxmlCodePage.Settings, "StartTime", mailbox.oofStartTime.toISOString()),
95
+ textElement(WbxmlCodePage.Settings, "EndTime", mailbox.oofEndTime.toISOString()),
96
+ ]
97
+ : []),
98
+ element(WbxmlCodePage.Settings, "OofMessage", [
99
+ element(WbxmlCodePage.Settings, "AppliesToInternal", []),
100
+ textElement(WbxmlCodePage.Settings, "Enabled", mailbox.oofEnabled ? "1" : "0"),
101
+ textElement(WbxmlCodePage.Settings, "ReplyMessage", mailbox.oofMessage),
102
+ textElement(WbxmlCodePage.Settings, "BodyType", "Text"),
103
+ ]),
104
+ ]),
105
+ ]);
106
+ }
107
+ async setOof(ctx, mailbox, setEl) {
108
+ const oofState = childText(setEl, "OofState") ?? "0";
109
+ const oofMessageEl = findChild(setEl, "OofMessage");
110
+ const replyMessage = oofMessageEl ? (childText(oofMessageEl, "ReplyMessage") ?? "") : "";
111
+ const startTime = childText(setEl, "StartTime");
112
+ const endTime = childText(setEl, "EndTime");
113
+ const timed = oofState === "2" && startTime !== undefined && endTime !== undefined;
114
+ // `null`, not `undefined`, to clear a previously-set window when switching away from time-based Oof:
115
+ // TypeORM's `UpdateQueryBuilder` silently drops any `undefined`-valued key from the generated SQL `SET`
116
+ // clause (confirmed by reading its source), so `undefined` here would leave a stale StartTime/EndTime
117
+ // in place on the SQL backend while correctly clearing it on Mongo - `null` clears it on both.
118
+ await this.mailboxRepo.update({
119
+ uid: mailbox.uid,
120
+ version: mailbox.version,
121
+ oofEnabled: oofState !== "0",
122
+ oofMessage: replyMessage,
123
+ oofStartTime: timed ? new Date(startTime) : null,
124
+ oofEndTime: timed ? new Date(endTime) : null,
125
+ }, mailbox, { ignoreACL: true, user: ctx.user });
126
+ return element(WbxmlCodePage.Settings, "Oof", [
127
+ textElement(WbxmlCodePage.Settings, "Status", "1"),
128
+ element(WbxmlCodePage.Settings, "Set", [textElement(WbxmlCodePage.Settings, "Status", "1")]),
129
+ ]);
130
+ }
131
+ }
132
+ __decorate([
133
+ Init,
134
+ __metadata("design:type", Function),
135
+ __metadata("design:paramtypes", []),
136
+ __metadata("design:returntype", Promise)
137
+ ], SettingsCommand.prototype, "init", null);
138
+ //# sourceMappingURL=SettingsCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SettingsCommand.js","sourceRoot":"","sources":["../../../src/commands/SettingsCommand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { ComposeMailCommand } from "./ComposeMailCommand.js";
6
+ /**
7
+ * Handles EAS `SmartForward`: relays a freshly composed message threaded to (and referencing) the original via
8
+ * `<Source>`, then flips the original's `Forwarded` flag - see `ComposeMailCommand`'s own doc comment for the
9
+ * full shared implementation and this pragmatic subset's scope.
10
+ *
11
+ * @author Jean-Philippe Steinmetz
12
+ */
13
+ export class SmartForwardCommand extends ComposeMailCommand {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.command = "SmartForward";
17
+ }
18
+ async markOriginal(ctx, original) {
19
+ await this.messageRepo.update({ uid: original.uid, version: original.version, flags: { ...original.flags, forwarded: true } }, original, { ignoreACL: true, user: ctx.user });
20
+ }
21
+ }
22
+ //# sourceMappingURL=SmartForwardCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SmartForwardCommand.js","sourceRoot":"","sources":["../../../src/commands/SmartForwardCommand.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAI7D;;;;;;GAMG;AACH,MAAM,OAAgB,mBAAoB,SAAQ,kBAAkB;IAApE;;QACoB,YAAO,GAAG,cAAc,CAAC;IAS7C,CAAC;IAPsB,KAAK,CAAC,YAAY,CAAC,GAAsB,EAAE,QAAoD;QAC9G,MAAM,IAAI,CAAC,WAAY,CAAC,MAAM,CAC1B,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAS,EACtG,QAAQ,EACR,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CACtC,CAAC;IACN,CAAC;CACJ"}
@@ -0,0 +1,22 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { ComposeMailCommand } from "./ComposeMailCommand.js";
6
+ /**
7
+ * Handles EAS `SmartReply`: relays a freshly composed message threaded to (and referencing) the original via
8
+ * `<Source>`, then flips the original's `Answered` flag - see `ComposeMailCommand`'s own doc comment for the
9
+ * full shared implementation and this pragmatic subset's scope.
10
+ *
11
+ * @author Jean-Philippe Steinmetz
12
+ */
13
+ export class SmartReplyCommand extends ComposeMailCommand {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.command = "SmartReply";
17
+ }
18
+ async markOriginal(ctx, original) {
19
+ await this.messageRepo.update({ uid: original.uid, version: original.version, flags: { ...original.flags, answered: true } }, original, { ignoreACL: true, user: ctx.user });
20
+ }
21
+ }
22
+ //# sourceMappingURL=SmartReplyCommand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SmartReplyCommand.js","sourceRoot":"","sources":["../../../src/commands/SmartReplyCommand.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAI7D;;;;;;GAMG;AACH,MAAM,OAAgB,iBAAkB,SAAQ,kBAAkB;IAAlE;;QACoB,YAAO,GAAG,YAAY,CAAC;IAS3C,CAAC;IAPsB,KAAK,CAAC,YAAY,CAAC,GAAsB,EAAE,QAAoD;QAC9G,MAAM,IAAI,CAAC,WAAY,CAAC,MAAM,CAC1B,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAS,EACrG,QAAQ,EACR,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CACtC,CAAC;IACN,CAAC;CACJ"}