@rapidmx/web-client 0.21.0 → 0.23.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 (897) hide show
  1. package/README.md +48 -14
  2. package/apps/admin/_layout.tsx +2 -2
  3. package/apps/admin/audit-log/index.tsx +4 -4
  4. package/apps/admin/branding/index.tsx +3 -3
  5. package/apps/admin/data-requests/index.tsx +8 -8
  6. package/apps/admin/distribution-lists/[uid].tsx +3 -3
  7. package/apps/admin/distribution-lists/index.tsx +4 -4
  8. package/apps/admin/distribution-lists/new/index.tsx +5 -5
  9. package/apps/admin/domains/[uid].tsx +6 -6
  10. package/apps/admin/domains/index.tsx +4 -4
  11. package/apps/admin/domains/new/index.tsx +5 -5
  12. package/apps/admin/encryption-policy/index.tsx +1 -1
  13. package/apps/admin/escrow-scopes/[uid].tsx +7 -7
  14. package/apps/admin/escrow-scopes/index.tsx +4 -4
  15. package/apps/admin/escrow-scopes/new/index.tsx +5 -5
  16. package/apps/admin/index.tsx +6 -6
  17. package/apps/admin/ingest-queue/index.tsx +4 -4
  18. package/apps/admin/mailbox-policy/index.tsx +1 -1
  19. package/apps/admin/mailboxes/[uid].tsx +5 -5
  20. package/apps/admin/quarantine/index.tsx +5 -5
  21. package/apps/admin/retention-policy/index.tsx +3 -3
  22. package/apps/admin/signing-certificates/index.tsx +5 -5
  23. package/apps/admin/transport-rules/[uid].tsx +5 -5
  24. package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
  25. package/apps/admin/transport-rules/index.tsx +4 -4
  26. package/apps/admin/transport-rules/new/index.tsx +5 -5
  27. package/apps/escrow/_layout.tsx +1 -1
  28. package/apps/escrow/audit-log/index.tsx +4 -4
  29. package/apps/escrow/index.tsx +4 -4
  30. package/apps/escrow/matters/[uid].tsx +10 -10
  31. package/apps/escrow/matters/new/index.tsx +6 -6
  32. package/apps/shared/appearance/AppearanceForm.tsx +3 -3
  33. package/apps/shared/appearance/AppearanceHead.tsx +1 -1
  34. package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
  35. package/apps/shared/appearance/appearanceCache.ts +1 -1
  36. package/apps/shared/appearance/appearanceContext.ts +1 -1
  37. package/apps/shared/appearance/theme.ts +1 -1
  38. package/apps/shared/auth/adminAccess.ts +1 -1
  39. package/apps/shared/calendar/bookingPlugin.ts +1 -1
  40. package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
  41. package/apps/shared/calendar/calendarReminders.ts +1 -1
  42. package/apps/shared/calendar/useCalendarReminders.ts +1 -1
  43. package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
  44. package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
  45. package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
  46. package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
  47. package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
  48. package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
  49. package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
  50. package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
  51. package/apps/shared/components/admin/diagnostics/format.ts +1 -1
  52. package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
  53. package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
  54. package/apps/shared/components/admin/elevation.ts +1 -1
  55. package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
  56. package/apps/shared/components/admin/layout/AdminShell.tsx +8 -8
  57. package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +6 -6
  58. package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +10 -8
  59. package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +4 -4
  60. package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
  61. package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
  62. package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
  63. package/apps/shared/components/admin/settings/BrandingForm.tsx +5 -5
  64. package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
  65. package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +4 -4
  66. package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
  67. package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
  68. package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +4 -4
  69. package/apps/shared/components/admin/settings/PluginsManager.tsx +5 -5
  70. package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +5 -5
  71. package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +7 -7
  72. package/apps/shared/components/admin/setup/SetupWizard.tsx +28 -24
  73. package/apps/shared/components/admin/signOut.ts +1 -1
  74. package/apps/shared/components/admin/usePagedList.tsx +2 -2
  75. package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
  76. package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
  77. package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
  78. package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
  79. package/apps/shared/components/calendar/EventDetails.tsx +16 -14
  80. package/apps/shared/components/calendar/EventEditor.tsx +54 -39
  81. package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
  82. package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
  83. package/apps/shared/components/calendar/EventModal.tsx +5 -5
  84. package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
  85. package/apps/shared/components/calendar/EventShell.tsx +1 -1
  86. package/apps/shared/components/calendar/FindATime.tsx +6 -4
  87. package/apps/shared/components/calendar/GuestInput.tsx +2 -2
  88. package/apps/shared/components/calendar/MonthView.tsx +2 -2
  89. package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
  90. package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
  91. package/apps/shared/components/calendar/RecurrenceEditor.tsx +2 -2
  92. package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
  93. package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
  94. package/apps/shared/components/calendar/SplitDayView.tsx +1 -1
  95. package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
  96. package/apps/shared/components/calendar/TimeGridView.tsx +5 -3
  97. package/apps/shared/components/calendar/allDay.ts +2 -2
  98. package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
  99. package/apps/shared/components/calendar/eventForm.ts +3 -3
  100. package/apps/shared/components/calendar/eventFormat.ts +3 -3
  101. package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
  102. package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
  103. package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
  104. package/apps/shared/components/contacts/ContactForm.tsx +27 -17
  105. package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
  106. package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
  107. package/apps/shared/components/contacts/contactKeys.ts +3 -3
  108. package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
  109. package/apps/shared/components/escrow/layout/EscrowShell.tsx +6 -6
  110. package/apps/shared/components/forms/StringListField.tsx +1 -1
  111. package/apps/shared/components/layout/AppShell.tsx +12 -10
  112. package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
  113. package/apps/shared/components/layout/KeyEnrollmentGate.tsx +9 -9
  114. package/apps/shared/components/layout/MailboxProvisioning.tsx +8 -6
  115. package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
  116. package/apps/shared/components/layout/ThemeSwitch.tsx +1 -1
  117. package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
  118. package/apps/shared/components/layout/UserMenu.tsx +6 -4
  119. package/apps/shared/components/mail/ConversationList.tsx +25 -10
  120. package/apps/shared/components/mail/ConversationThreadPane.tsx +33 -17
  121. package/apps/shared/components/mail/InviteCard.tsx +11 -9
  122. package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
  123. package/apps/shared/components/mail/MailAddress.tsx +1 -1
  124. package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
  125. package/apps/shared/components/mail/MailSelectionBar.tsx +2 -2
  126. package/apps/shared/components/mail/MenuButton.tsx +1 -1
  127. package/apps/shared/components/mail/MessageDetailPane.tsx +69 -48
  128. package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
  129. package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
  130. package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
  131. package/apps/shared/components/mail/compose/ComposeContext.tsx +14 -3
  132. package/apps/shared/components/mail/compose/ComposeWindow.tsx +34 -30
  133. package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
  134. package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
  135. package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
  136. package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
  137. package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
  138. package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
  139. package/apps/shared/components/mail/compose/quotedBody.ts +5 -5
  140. package/apps/shared/components/mail/compose/recipients.ts +1 -1
  141. package/apps/shared/components/mail/invite/InviteRowChip.tsx +10 -14
  142. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +11 -9
  143. package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
  144. package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
  145. package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
  146. package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
  147. package/apps/shared/components/mail/labelMenu.tsx +9 -7
  148. package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
  149. package/apps/shared/components/mail/listPreferences.ts +2 -2
  150. package/apps/shared/components/mail/pinnedSigners.ts +45 -19
  151. package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
  152. package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
  153. package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
  154. package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +3 -3
  155. package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
  156. package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
  157. package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
  158. package/apps/shared/components/mail/reading/conversationPrint.ts +76 -0
  159. package/apps/shared/components/mail/reading/messageExport.ts +1 -1
  160. package/apps/shared/components/mail/reading/printMessage.ts +46 -9
  161. package/apps/shared/components/mail/reading/useMessageActions.ts +28 -21
  162. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  163. package/apps/shared/components/mail/verificationSeals.ts +16 -12
  164. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  165. package/apps/shared/components/rules/RuleBuilder.tsx +1 -1
  166. package/apps/shared/components/settings/SigningCertificateCard.tsx +4 -4
  167. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  168. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  169. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  170. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  171. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  172. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  173. package/apps/shared/keyboard/ShortcutsDialog.tsx +1 -1
  174. package/apps/shared/keyboard/keymap.ts +1 -0
  175. package/apps/shared/mail/decryptedMessages.ts +116 -0
  176. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  177. package/apps/shared/mail/folderCounts.ts +8 -4
  178. package/apps/shared/mail/folderOfType.ts +5 -3
  179. package/apps/shared/mail/folderTree.ts +1 -1
  180. package/apps/shared/mail/listSnapshots.ts +2 -2
  181. package/apps/shared/mail/messageReadState.ts +6 -5
  182. package/apps/shared/mail/newMailNotifications.ts +2 -2
  183. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  184. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  185. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  186. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  187. package/apps/shared/mail/outbox/sendJob.ts +40 -35
  188. package/apps/shared/mail/outbox/sendOutcomes.ts +20 -15
  189. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  190. package/apps/shared/mail/permanentDelete.ts +8 -7
  191. package/apps/shared/mail/primaryMailbox.ts +1 -1
  192. package/apps/shared/mail/reportNotices.ts +1 -1
  193. package/apps/shared/mail/senderBlocking.ts +36 -20
  194. package/apps/shared/mail/useMailConnection.ts +12 -10
  195. package/apps/shared/mail/useMailLiveUpdates.ts +3 -3
  196. package/apps/shared/mail/useMailboxUpdateAccess.ts +35 -11
  197. package/apps/shared/mail/useMarkMessageRead.ts +14 -7
  198. package/apps/shared/mail/useNewMailNotifications.ts +1 -1
  199. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  200. package/apps/shared/navigation/pageTitle.ts +1 -1
  201. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  202. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  203. package/apps/shared/notifications/apiErrors.ts +2 -2
  204. package/apps/shared/notifications/pushStatus.ts +1 -1
  205. package/apps/shared/notifications/systemErrors.ts +1 -1
  206. package/apps/shared/search/LocalIndexLifecycle.tsx +18 -12
  207. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  208. package/apps/shared/search/localIndexBuilder.ts +24 -12
  209. package/apps/shared/search/localIndexKey.ts +1 -1
  210. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  211. package/apps/shared/search/localIndexTransport.ts +84 -0
  212. package/apps/shared/search/localIndexWorker.ts +1 -1
  213. package/apps/shared/search/searchTier2.ts +3 -3
  214. package/apps/shared/signing/enrollmentTracker.ts +3 -3
  215. package/apps/shared/signing/enrollmentView.ts +1 -1
  216. package/apps/shared/signing/signingInfo.ts +1 -1
  217. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +6 -4
  218. package/apps/shared/styles/app.css +14 -23
  219. package/apps/www/_layout.tsx +2 -2
  220. package/apps/www/calendar/index.tsx +20 -18
  221. package/apps/www/contacts/[uid].tsx +8 -6
  222. package/apps/www/contacts/index.tsx +19 -17
  223. package/apps/www/index.tsx +68 -48
  224. package/apps/www/messages/[uid].tsx +11 -9
  225. package/apps/www/settings/auto-reply/index.tsx +6 -4
  226. package/apps/www/settings/blocked-senders/index.tsx +9 -7
  227. package/apps/www/settings/encryption/index.tsx +38 -32
  228. package/apps/www/settings/filters/[uid].tsx +19 -14
  229. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  230. package/apps/www/settings/filters/index.tsx +4 -4
  231. package/apps/www/settings/filters/new/index.tsx +10 -8
  232. package/apps/www/settings/labels/index.tsx +12 -10
  233. package/apps/www/settings/privacy/index.tsx +18 -14
  234. package/apps/www/settings/profile/index.tsx +16 -11
  235. package/apps/www/settings/read-receipts/index.tsx +17 -12
  236. package/apps/www/settings/sharing/index.tsx +6 -6
  237. package/apps/www/settings/signatures/[uid].tsx +5 -5
  238. package/apps/www/settings/signatures/index.tsx +4 -4
  239. package/apps/www/settings/signatures/new/index.tsx +4 -4
  240. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  241. package/apps/www/tasks/index.tsx +28 -23
  242. package/dist/apps/admin/_layout.d.ts +1 -1
  243. package/dist/apps/admin/_layout.js +1 -1
  244. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  245. package/dist/apps/admin/audit-log/index.js +4 -4
  246. package/dist/apps/admin/branding/index.js +3 -3
  247. package/dist/apps/admin/data-requests/index.js +8 -8
  248. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  249. package/dist/apps/admin/distribution-lists/index.js +4 -4
  250. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  251. package/dist/apps/admin/domains/[uid].js +6 -6
  252. package/dist/apps/admin/domains/index.js +4 -4
  253. package/dist/apps/admin/domains/new/index.js +5 -5
  254. package/dist/apps/admin/encryption-policy/index.js +1 -1
  255. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  256. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  257. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  258. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  259. package/dist/apps/admin/index.js +6 -6
  260. package/dist/apps/admin/ingest-queue/index.js +4 -4
  261. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  262. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  263. package/dist/apps/admin/quarantine/index.js +5 -5
  264. package/dist/apps/admin/retention-policy/index.js +3 -3
  265. package/dist/apps/admin/signing-certificates/index.js +5 -5
  266. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  267. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  268. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  269. package/dist/apps/admin/transport-rules/index.js +4 -4
  270. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  271. package/dist/apps/escrow/_layout.d.ts +1 -1
  272. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  273. package/dist/apps/escrow/audit-log/index.js +4 -4
  274. package/dist/apps/escrow/index.js +4 -4
  275. package/dist/apps/escrow/matters/[uid].js +10 -10
  276. package/dist/apps/escrow/matters/new/index.js +6 -6
  277. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  278. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  279. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  280. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  281. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  282. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  283. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  284. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  285. package/dist/apps/shared/auth/adminAccess.js +1 -1
  286. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  287. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  288. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  289. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  290. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  291. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  292. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  293. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  294. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  295. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  296. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  297. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  298. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  299. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  300. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  301. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  302. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  303. package/dist/apps/shared/components/admin/elevation.js +1 -1
  304. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  305. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  306. package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -7
  307. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  308. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  309. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  310. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  311. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  312. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  313. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  314. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  315. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  316. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  317. package/dist/apps/shared/components/admin/settings/BrandingForm.js +5 -5
  318. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  319. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  320. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  321. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  322. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  323. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  324. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  325. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  327. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  328. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  329. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  330. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  331. package/dist/apps/shared/components/admin/setup/SetupWizard.d.ts +4 -4
  332. package/dist/apps/shared/components/admin/setup/SetupWizard.js +27 -23
  333. package/dist/apps/shared/components/admin/signOut.js +1 -1
  334. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  335. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  336. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  337. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  338. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  339. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  340. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  341. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  342. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  343. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  344. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  345. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  346. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  347. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  348. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  349. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  350. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  351. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  352. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  353. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  354. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  355. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  356. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  357. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  358. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  359. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  360. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  361. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  362. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  363. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  364. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  365. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  366. package/dist/apps/shared/components/calendar/TimeGridView.js +2 -2
  367. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  368. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  369. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  370. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  371. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  372. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  374. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  375. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  376. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  377. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  378. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  379. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  380. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  381. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  382. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  383. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  384. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  385. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  386. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  387. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  388. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  389. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  390. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +6 -6
  391. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  392. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  393. package/dist/apps/shared/components/layout/AppShell.js +11 -9
  394. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  395. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  396. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  397. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  398. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  399. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  400. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  401. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  402. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  403. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  404. package/dist/apps/shared/components/mail/ConversationList.js +23 -8
  405. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  406. package/dist/apps/shared/components/mail/ConversationThreadPane.js +19 -13
  407. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  408. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  409. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  410. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  411. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  412. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  413. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  414. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  415. package/dist/apps/shared/components/mail/MessageDetailPane.js +61 -46
  416. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  417. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  418. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  419. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  420. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  421. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  422. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +34 -30
  423. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  424. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  425. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  426. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  427. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  428. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  429. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  430. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  431. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  432. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  433. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +3 -3
  435. package/dist/apps/shared/components/mail/invite/InviteRowChip.js +7 -9
  436. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +2 -2
  437. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +11 -9
  438. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  439. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  440. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  441. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  442. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  443. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  444. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  445. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  446. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  447. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  448. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  449. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  450. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  451. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  452. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  453. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  454. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  455. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  456. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  457. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  458. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  459. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  460. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  461. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  462. package/dist/apps/shared/components/mail/reading/conversationPrint.d.ts +15 -0
  463. package/dist/apps/shared/components/mail/reading/conversationPrint.js +69 -0
  464. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  465. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +9 -3
  466. package/dist/apps/shared/components/mail/reading/printMessage.js +33 -6
  467. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  468. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  469. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  470. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  471. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  472. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  473. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  474. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  475. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  476. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  477. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  478. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  479. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  480. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  481. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  482. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  483. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  484. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  485. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  486. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  487. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  488. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  489. package/dist/apps/shared/keyboard/keymap.d.ts +6 -0
  490. package/dist/apps/shared/keyboard/keymap.js +1 -0
  491. package/dist/apps/shared/mail/decryptedMessages.d.ts +27 -0
  492. package/dist/apps/shared/mail/decryptedMessages.js +97 -0
  493. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  494. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  495. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  496. package/dist/apps/shared/mail/folderCounts.js +6 -4
  497. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  498. package/dist/apps/shared/mail/folderOfType.js +4 -4
  499. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  500. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  501. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  502. package/dist/apps/shared/mail/messageReadState.js +5 -5
  503. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  504. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  505. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  506. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  507. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  508. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  509. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  510. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  511. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  512. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  513. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  514. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  515. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  516. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  517. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  518. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  519. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  520. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  521. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  522. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  523. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  524. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  525. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  526. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  527. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  528. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  529. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  530. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  531. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  532. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  533. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  534. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  535. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  536. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  537. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  538. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  539. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  540. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  541. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  542. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  543. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  544. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  545. package/dist/apps/shared/search/localIndexKey.js +1 -1
  546. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  547. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  548. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  549. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  550. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  551. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  552. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  553. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  554. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  555. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  556. package/dist/apps/shared/signing/signingInfo.js +1 -1
  557. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  558. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  559. package/dist/apps/shared/styles/app.css +14 -23
  560. package/dist/apps/www/_layout.d.ts +1 -1
  561. package/dist/apps/www/_layout.js +1 -1
  562. package/dist/apps/www/calendar/index.d.ts +1 -1
  563. package/dist/apps/www/calendar/index.js +20 -18
  564. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  565. package/dist/apps/www/contacts/[uid].js +8 -6
  566. package/dist/apps/www/contacts/index.d.ts +1 -1
  567. package/dist/apps/www/contacts/index.js +19 -17
  568. package/dist/apps/www/index.d.ts +1 -1
  569. package/dist/apps/www/index.js +64 -50
  570. package/dist/apps/www/messages/[uid].d.ts +1 -1
  571. package/dist/apps/www/messages/[uid].js +11 -9
  572. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  573. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  574. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  575. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  576. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  577. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  578. package/dist/apps/www/settings/encryption/index.js +31 -29
  579. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  580. package/dist/apps/www/settings/filters/[uid].js +11 -9
  581. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  582. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  583. package/dist/apps/www/settings/filters/index.js +4 -4
  584. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  585. package/dist/apps/www/settings/filters/new/index.js +10 -8
  586. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  587. package/dist/apps/www/settings/labels/index.js +12 -10
  588. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  589. package/dist/apps/www/settings/privacy/index.js +18 -14
  590. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  591. package/dist/apps/www/settings/profile/index.js +7 -5
  592. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  593. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  594. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  595. package/dist/apps/www/settings/sharing/index.js +6 -6
  596. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  597. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  598. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  599. package/dist/apps/www/settings/signatures/index.js +4 -4
  600. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  601. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  602. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  603. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  604. package/dist/apps/www/tasks/index.d.ts +1 -1
  605. package/dist/apps/www/tasks/index.js +17 -15
  606. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  607. package/dist/lib/admin/auditLogApi.js +27 -0
  608. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  609. package/dist/lib/admin/distributionListsApi.js +34 -0
  610. package/dist/lib/admin/domainsApi.d.ts +75 -0
  611. package/dist/lib/admin/domainsApi.js +44 -0
  612. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  613. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  614. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  615. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  616. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  617. package/dist/lib/admin/escrowScopesApi.js +50 -0
  618. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  619. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  620. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  621. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  622. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  623. package/dist/lib/admin/matterExportApi.js +39 -0
  624. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  625. package/dist/lib/admin/matterSearchApi.js +27 -0
  626. package/dist/lib/admin/mattersApi.d.ts +56 -0
  627. package/dist/lib/admin/mattersApi.js +40 -0
  628. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  629. package/dist/lib/admin/pluginsApi.js +102 -0
  630. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  631. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  632. package/dist/lib/admin/setupApi.d.ts +23 -0
  633. package/dist/lib/admin/setupApi.js +26 -0
  634. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  635. package/dist/lib/admin/transportRulesApi.js +43 -0
  636. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  637. package/dist/lib/appearance/preferencesApi.js +257 -0
  638. package/dist/lib/auth/profileApi.d.ts +33 -0
  639. package/dist/lib/auth/profileApi.js +52 -0
  640. package/dist/lib/auth/session.d.ts +70 -0
  641. package/dist/lib/auth/session.js +210 -0
  642. package/dist/lib/branding/brandingApi.d.ts +47 -0
  643. package/dist/lib/branding/brandingApi.js +61 -0
  644. package/dist/lib/branding/useBranding.d.ts +23 -0
  645. package/dist/lib/branding/useBranding.js +65 -0
  646. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  647. package/dist/lib/calendar/calendarApi.js +106 -0
  648. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  649. package/dist/lib/calendar/calendarColors.js +39 -0
  650. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  651. package/dist/lib/calendar/calendarDragIds.js +92 -0
  652. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  653. package/dist/lib/calendar/calendarMutations.js +190 -0
  654. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  655. package/dist/lib/calendar/eventDescription.js +195 -0
  656. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  657. package/dist/lib/calendar/freeBusyApi.js +119 -0
  658. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  659. package/dist/lib/calendar/inviteApi.js +54 -0
  660. package/dist/lib/calendar/recurrence.d.ts +60 -0
  661. package/dist/lib/calendar/recurrence.js +225 -0
  662. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  663. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  664. package/dist/lib/components/buttons/Button.d.ts +9 -0
  665. package/dist/lib/components/buttons/Button.js +14 -0
  666. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  667. package/dist/lib/components/buttons/CopyButton.js +14 -0
  668. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  669. package/dist/lib/components/feedback/Alert.js +4 -0
  670. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  671. package/dist/lib/components/feedback/Skeleton.js +14 -0
  672. package/dist/lib/components/forms/FormField.d.ts +8 -0
  673. package/dist/lib/components/forms/FormField.js +5 -0
  674. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  675. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  676. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  677. package/dist/lib/components/overlays/Drawer.js +27 -0
  678. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  679. package/dist/lib/components/overlays/Modal.js +25 -0
  680. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  681. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  682. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  683. package/dist/lib/components/overlays/overlayStack.js +143 -0
  684. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  685. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  686. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  687. package/dist/lib/contacts/contactsApi.js +139 -0
  688. package/dist/lib/contacts/vcard.d.ts +23 -0
  689. package/dist/lib/contacts/vcard.js +179 -0
  690. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  691. package/dist/lib/crypto/composeSecurity.js +93 -0
  692. package/dist/lib/crypto/encoding.d.ts +9 -0
  693. package/dist/lib/crypto/encoding.js +32 -0
  694. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  695. package/dist/lib/crypto/escrowKeys.js +59 -0
  696. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  697. package/dist/lib/crypto/idleTimeout.js +47 -0
  698. package/dist/lib/crypto/keySession.d.ts +159 -0
  699. package/dist/lib/crypto/keySession.js +367 -0
  700. package/dist/lib/crypto/keys.d.ts +50 -0
  701. package/dist/lib/crypto/keys.js +89 -0
  702. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  703. package/dist/lib/crypto/keyvaultApi.js +350 -0
  704. package/dist/lib/crypto/masterKey.d.ts +59 -0
  705. package/dist/lib/crypto/masterKey.js +104 -0
  706. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  707. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  708. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  709. package/dist/lib/crypto/messageSecurity.js +367 -0
  710. package/dist/lib/crypto/mime.d.ts +143 -0
  711. package/dist/lib/crypto/mime.js +504 -0
  712. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  713. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  714. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  715. package/dist/lib/crypto/passwordUnlock.js +73 -0
  716. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  717. package/dist/lib/crypto/recoveryCode.js +86 -0
  718. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  719. package/dist/lib/crypto/signingProviderApi.js +60 -0
  720. package/dist/lib/crypto/smime.d.ts +128 -0
  721. package/dist/lib/crypto/smime.js +402 -0
  722. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  723. package/dist/lib/crypto/smimeMessage.js +341 -0
  724. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  725. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  726. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  727. package/dist/lib/crypto/verificationSeal.js +220 -0
  728. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  729. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  730. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  731. package/dist/lib/mail/compose/emojiData.js +18 -0
  732. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  733. package/dist/lib/mail/compose/giphyApi.js +20 -0
  734. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  735. package/dist/lib/mail/conversationsApi.js +44 -0
  736. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  737. package/dist/lib/mail/dataExportApi.js +40 -0
  738. package/dist/lib/mail/directoryApi.d.ts +49 -0
  739. package/dist/lib/mail/directoryApi.js +101 -0
  740. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  741. package/dist/lib/mail/erasureRequestApi.js +43 -0
  742. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  743. package/dist/lib/mail/flaggedMessages.js +73 -0
  744. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  745. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  746. package/dist/lib/mail/labelsApi.d.ts +47 -0
  747. package/dist/lib/mail/labelsApi.js +38 -0
  748. package/dist/lib/mail/mailAddress.d.ts +27 -0
  749. package/dist/lib/mail/mailAddress.js +54 -0
  750. package/dist/lib/mail/mailApi.d.ts +850 -0
  751. package/dist/lib/mail/mailApi.js +602 -0
  752. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  753. package/dist/lib/mail/mailDetailHooks.js +53 -0
  754. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  755. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  756. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  757. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  758. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  759. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  760. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  761. package/dist/lib/mail/mailboxImportApi.js +51 -0
  762. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  763. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  764. package/dist/lib/mail/pushClient.d.ts +106 -0
  765. package/dist/lib/mail/pushClient.js +317 -0
  766. package/dist/lib/mail/sendEvents.d.ts +33 -0
  767. package/dist/lib/mail/sendEvents.js +44 -0
  768. package/dist/lib/mail/sendFailure.d.ts +16 -0
  769. package/dist/lib/mail/sendFailure.js +86 -0
  770. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  771. package/dist/lib/mail/senderListsApi.js +126 -0
  772. package/dist/lib/search/queryGrammar.d.ts +51 -0
  773. package/dist/lib/search/queryGrammar.js +111 -0
  774. package/dist/lib/search/searchApi.d.ts +100 -0
  775. package/dist/lib/search/searchApi.js +107 -0
  776. package/dist/lib/search/searchScoring.d.ts +34 -0
  777. package/dist/lib/search/searchScoring.js +44 -0
  778. package/dist/lib/search/searchTier3.d.ts +64 -0
  779. package/dist/lib/search/searchTier3.js +335 -0
  780. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  781. package/dist/lib/tasks/tasksApi.js +55 -0
  782. package/dist/lib/util/api.d.ts +160 -0
  783. package/dist/lib/util/api.js +251 -0
  784. package/dist/lib/util/apiClientContext.d.ts +7 -0
  785. package/dist/lib/util/apiClientContext.js +32 -0
  786. package/dist/lib/util/apiQuery.d.ts +32 -0
  787. package/dist/lib/util/apiQuery.js +42 -0
  788. package/dist/lib/util/clipboard.d.ts +7 -0
  789. package/dist/lib/util/clipboard.js +56 -0
  790. package/dist/lib/util/dateInput.d.ts +6 -0
  791. package/dist/lib/util/dateInput.js +14 -0
  792. package/dist/lib/util/timeZone.d.ts +12 -0
  793. package/dist/lib/util/timeZone.js +32 -0
  794. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  795. package/dist/lib/util/useCopyToClipboard.js +34 -0
  796. package/dist/lib/util/useIsMobile.d.ts +10 -0
  797. package/dist/lib/util/useIsMobile.js +29 -0
  798. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  799. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  800. package/lib/admin/auditLogApi.ts +48 -0
  801. package/lib/admin/distributionListsApi.ts +77 -0
  802. package/lib/admin/domainsApi.ts +113 -0
  803. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  804. package/lib/admin/escrowAuditLogApi.ts +101 -0
  805. package/lib/admin/escrowScopesApi.ts +112 -0
  806. package/lib/admin/leftoverMailboxApi.ts +120 -0
  807. package/lib/admin/mailboxPolicyApi.ts +38 -0
  808. package/lib/admin/matterExportApi.ts +62 -0
  809. package/lib/admin/matterSearchApi.ts +35 -0
  810. package/lib/admin/mattersApi.ts +86 -0
  811. package/lib/admin/pluginsApi.ts +339 -0
  812. package/lib/admin/retentionPolicyApi.ts +46 -0
  813. package/lib/admin/setupApi.ts +40 -0
  814. package/lib/admin/transportRulesApi.ts +106 -0
  815. package/lib/appearance/preferencesApi.ts +335 -0
  816. package/lib/auth/profileApi.ts +77 -0
  817. package/lib/auth/session.ts +238 -0
  818. package/lib/branding/brandingApi.ts +97 -0
  819. package/lib/branding/useBranding.ts +81 -0
  820. package/lib/calendar/calendarApi.ts +309 -0
  821. package/lib/calendar/calendarColors.ts +45 -0
  822. package/lib/calendar/calendarDragIds.ts +112 -0
  823. package/lib/calendar/calendarMutations.ts +236 -0
  824. package/lib/calendar/eventDescription.ts +211 -0
  825. package/lib/calendar/freeBusyApi.ts +197 -0
  826. package/lib/calendar/inviteApi.ts +154 -0
  827. package/lib/calendar/recurrence.ts +285 -0
  828. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  829. package/lib/components/buttons/Button.tsx +40 -0
  830. package/lib/components/buttons/CopyButton.tsx +45 -0
  831. package/lib/components/feedback/Alert.tsx +13 -0
  832. package/lib/components/feedback/Skeleton.tsx +38 -0
  833. package/lib/components/forms/FormField.tsx +23 -0
  834. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  835. package/lib/components/overlays/Drawer.tsx +72 -0
  836. package/lib/components/overlays/Modal.tsx +64 -0
  837. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  838. package/lib/components/overlays/overlayStack.ts +165 -0
  839. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  840. package/lib/contacts/contactsApi.ts +289 -0
  841. package/lib/contacts/vcard.ts +197 -0
  842. package/lib/crypto/composeSecurity.ts +140 -0
  843. package/lib/crypto/encoding.ts +36 -0
  844. package/lib/crypto/escrowKeys.ts +74 -0
  845. package/lib/crypto/idleTimeout.ts +49 -0
  846. package/lib/crypto/keySession.ts +483 -0
  847. package/lib/crypto/keys.ts +105 -0
  848. package/lib/crypto/keyvaultApi.ts +674 -0
  849. package/lib/crypto/masterKey.ts +134 -0
  850. package/lib/crypto/masterKeyWraps.ts +269 -0
  851. package/lib/crypto/messageSecurity.ts +600 -0
  852. package/lib/crypto/mime.ts +568 -0
  853. package/lib/crypto/passkeyUnlock.ts +103 -0
  854. package/lib/crypto/passwordUnlock.ts +98 -0
  855. package/lib/crypto/recoveryCode.ts +93 -0
  856. package/lib/crypto/signingProviderApi.ts +135 -0
  857. package/lib/crypto/smime.ts +459 -0
  858. package/lib/crypto/smimeMessage.ts +491 -0
  859. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  860. package/lib/crypto/verificationSeal.ts +288 -0
  861. package/lib/mail/compose/composeQuoting.ts +243 -0
  862. package/lib/mail/compose/emojiData.ts +34 -0
  863. package/lib/mail/compose/giphyApi.ts +31 -0
  864. package/lib/mail/conversationsApi.ts +123 -0
  865. package/lib/mail/dataExportApi.ts +73 -0
  866. package/lib/mail/directoryApi.ts +161 -0
  867. package/lib/mail/erasureRequestApi.ts +71 -0
  868. package/lib/mail/flaggedMessages.ts +80 -0
  869. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  870. package/lib/mail/labelsApi.ts +71 -0
  871. package/lib/mail/mailAddress.ts +76 -0
  872. package/lib/mail/mailApi.ts +1286 -0
  873. package/lib/mail/mailDetailHooks.ts +57 -0
  874. package/lib/mail/mailFilterRulesApi.ts +116 -0
  875. package/lib/mail/mailSignaturesApi.ts +84 -0
  876. package/lib/mail/mailboxAccessApi.ts +106 -0
  877. package/lib/mail/mailboxImportApi.ts +87 -0
  878. package/lib/mail/messageBodySanitizer.ts +129 -0
  879. package/lib/mail/pushClient.ts +375 -0
  880. package/lib/mail/sendEvents.ts +72 -0
  881. package/lib/mail/sendFailure.ts +98 -0
  882. package/lib/mail/senderListsApi.ts +161 -0
  883. package/lib/search/queryGrammar.ts +179 -0
  884. package/lib/search/searchApi.ts +199 -0
  885. package/lib/search/searchScoring.ts +46 -0
  886. package/lib/search/searchTier3.ts +406 -0
  887. package/lib/tasks/tasksApi.ts +128 -0
  888. package/lib/util/api.ts +318 -0
  889. package/lib/util/apiClientContext.ts +35 -0
  890. package/lib/util/apiQuery.ts +61 -0
  891. package/lib/util/clipboard.ts +55 -0
  892. package/lib/util/dateInput.ts +15 -0
  893. package/lib/util/timeZone.ts +33 -0
  894. package/lib/util/useCopyToClipboard.ts +48 -0
  895. package/lib/util/useIsMobile.ts +34 -0
  896. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  897. package/package.json +10 -4
@@ -0,0 +1,1286 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * Typed wrappers over `@rapidmx/restapi`'s REST surface. Shared by `apps/admin` and (from Phase 3 on)
7
+ * `apps/www` — there is no separate admin-only endpoint set to isolate: every route here is gated entirely
8
+ * by the ACL system, so the exact same call returns the caller's own data and what is shared with them - an
9
+ * administrator's included: no role reads another user's mail. The one exception is the administration scope
10
+ * (`scope: "admin"` -> `?scope=admin`), which ONLY the admin console passes: mailboxes then come back as
11
+ * administrative metadata (no keys, settings or content) and every call is audited; an administrator who needs
12
+ * to see somebody's mail impersonates them (`impersonateUser()`). See `BaseMailboxRoute`'s doc comment in
13
+ * `@rapidmx/restapi` and this repo's `.claude/NOTES.md`.
14
+ */
15
+
16
+ import { ApiClient, ApiRequestError, apiFetch, apiUrl, authApiFetch, withClient, withCsrfHeader } from "../util/api.js";
17
+ import { ListParams, buildQuery } from "../util/apiQuery.js";
18
+ import { deviceTimeZone } from "../util/timeZone.js";
19
+ import type { EncryptionPreference, PublicKey } from "../crypto/keyvaultApi.js";
20
+ import { bytesToBinaryString } from "../crypto/mime.js";
21
+ import type { ResolvedPrincipal } from "./mailboxAccessApi.js";
22
+
23
+ export type { ListParams };
24
+ export type { EncryptionPreference, PublicKey };
25
+ export type { ResolvedPrincipal };
26
+
27
+ /** Whether `mailbox` is one shared with the caller rather than their own: a delegate grant on somebody's mailbox, or a mailbox with no
28
+ * owner at all (a shared/org mailbox they were granted). */
29
+ export function isSharedWithMe(mailbox: Pick<Mailbox, "accessRole" | "ownerUserUid">): boolean {
30
+ return mailbox.accessRole ? mailbox.accessRole === "delegate" : !mailbox.ownerUserUid;
31
+ }
32
+
33
+ /** `?scope=admin`: the administration scope, for the admin console only (trusted + elevated callers; audited). A mailbox
34
+ * then carries administrative metadata only - see `Mailbox.shared`. */
35
+ export interface AdminScopeParams {
36
+ scope?: "admin";
37
+ }
38
+
39
+ /** The `scope` query parameter of `params`, when set. */
40
+ function scopeQuery(params: AdminScopeParams): Record<string, string> {
41
+ return params.scope ? { scope: params.scope } : {};
42
+ }
43
+
44
+ /** Who may see a mailbox's free/busy times (`POST /calendar-events/free-busy`): `"domain"` - anyone signed in who owns a mailbox in the same domain (the default);
45
+ * `"shared"` - only people who already hold access to the mailbox or its calendars; `"nobody"` - only the owner and delegates with full access;
46
+ * `"everyone"` - anyone signed in to this server. */
47
+ export type FreeBusyVisibility = "domain" | "shared" | "nobody" | "everyone";
48
+
49
+ /** The free/busy visibility `mailbox` has: `"domain"` when it says none (a mailbox stored before the setting existed). */
50
+ export function freeBusyVisibilityOf(mailbox: Pick<Mailbox, "freeBusyVisibility">): FreeBusyVisibility {
51
+ return mailbox.freeBusyVisibility ?? "domain";
52
+ }
53
+
54
+ export interface Mailbox {
55
+ uid: string;
56
+ version: number;
57
+ dateCreated: string;
58
+ dateModified: string;
59
+ /** Absent for a true ownerless shared mailbox (e.g. `support@example.com`) — access is ACL-only. */
60
+ ownerUserUid?: string;
61
+ primarySmtpAddress: string;
62
+ aliasAddresses: string[];
63
+ displayName: string;
64
+ timezone: string;
65
+ quotaBytes: number;
66
+ usedBytes: number;
67
+ /** `true` for a bookable resource (Exchange's "room"/"equipment" mailbox concept) rather than a
68
+ * person — trusted-role-only to create, same gate as any other ownerless mailbox. */
69
+ isResource?: boolean;
70
+ /** Only meaningful when `isResource` is `true`. */
71
+ resourceType?: "room" | "equipment";
72
+ /** Informational only — not used by any accept/decline logic. */
73
+ resourceCapacity?: number;
74
+ /** Mirrors Exchange's `AutomateProcessing AutoAccept` — off by default. Has no effect unless
75
+ * `isResource` is also `true`. */
76
+ autoAcceptBookings?: boolean;
77
+ /** When set, every request is auto-accepted regardless of existing bookings. */
78
+ allowConflicts?: boolean;
79
+ /** A request starting further out than this many days is auto-declined. `undefined` means no limit. */
80
+ bookingWindowDays?: number;
81
+ /** A request longer than this many minutes is auto-declined. `undefined` means no limit. */
82
+ maxDurationMinutes?: number;
83
+ /** `true` if this mailbox's out-of-office auto-reply is currently enabled. Always present on a real
84
+ * `Mailbox` (defaults to `false` server-side) — optional here only because some older test fixtures
85
+ * predate this field, matching this file's existing convention for every other boolean flag above. */
86
+ oofEnabled?: boolean;
87
+ /** The out-of-office auto-reply message body — a single combined message rather than per-audience
88
+ * variants, matching `@rapidmx/restapi`'s own deliberate simplification. Always present (defaults to
89
+ * `""`), same caveat as `oofEnabled` above. */
90
+ oofMessage?: string;
91
+ /** When set together with `oofEndTime`, the auto-reply is only active within this window rather than
92
+ * indefinitely while `oofEnabled` is `true`. */
93
+ oofStartTime?: string;
94
+ oofEndTime?: string;
95
+ /** Whether `send()` attaches a real RFC 3798 receipt request to every outgoing message by default, for
96
+ * internal vs. external recipients respectively — a per-draft `Message.requestReceipt` (see
97
+ * `setMessageRequestReceipt()`) always overrides both at once when explicitly set. Always present on a
98
+ * real `Mailbox` (defaults `true`/`false` server-side respectively), same optional-for-old-fixtures
99
+ * caveat as `oofEnabled`. */
100
+ alwaysRequestReceiptInternal?: boolean;
101
+ /** Same as `alwaysRequestReceiptInternal`/`External`, for a recipient in a *different* organisation
102
+ * that has opted into federation with this one - a third tier between same-org and fully external. */
103
+ alwaysRequestReceiptFederated?: boolean;
104
+ alwaysRequestReceiptExternal?: boolean;
105
+ /** Whether this mailbox, as the *recipient* of a receipt request, sends one back immediately versus
106
+ * holding it for the owner's explicit approval (`approveReceipt()`/`declineReceipt()`) — classifies the
107
+ * requester, not the recipient. Always present (defaults `true`/`false` respectively), same caveat. */
108
+ autoSendReceiptsInternal?: boolean;
109
+ /** The federated-tier counterpart of `autoSendReceiptsInternal`/`External`. */
110
+ autoSendReceiptsFederated?: boolean;
111
+ autoSendReceiptsExternal?: boolean;
112
+ /** This mailbox's own encryption preference, per `specs/end-to-end_encryption.md` — a client
113
+ * defaults to encrypting only when *both* sender and recipient report `"mutual"`. Absent is
114
+ * equivalent to `{ preferEncrypt: "nopreference" }` (no keys enrolled yet). */
115
+ encryptPreference?: EncryptionPreference;
116
+ /** This mailbox's published public keys (signing and/or encryption) — safe to expose publicly,
117
+ * per the spec's own `PublicKey` doc comment. Absent/empty means no keys enrolled yet
118
+ * (`KeyEnrollmentGate` handles that state). */
119
+ keys?: PublicKey[];
120
+ /** How the caller reaches this mailbox: `"owner"` (theirs) or `"delegate"` (shared with them). Set by the plain list and read of
121
+ * mailboxes - not in an administration-scope answer - so a client can label the shared ones; see `isSharedWithMe()`. */
122
+ accessRole?: "owner" | "delegate";
123
+ /** Only in an administration-scope answer (`scope: "admin"`): the mailbox has no single owner. Such an answer
124
+ * carries administrative metadata only, so the fields it leaves out (`oofMessage`, `keys`, ...) are `undefined`. */
125
+ shared?: boolean;
126
+ /** The `EscrowScope` this mailbox is currently assigned to, if any — an admin-only assignment
127
+ * (`specs/end-to-end_encryption.md`'s Escrow Scoping). Assigning this alone does **not** create any
128
+ * `MasterKeyWrap` — the mailbox owner must separately wrap MK against the scope's public key (see
129
+ * `crypto/keyvaultApi.ts`'s `getEscrowInfo()` and `crypto/masterKeyWraps.ts`'s `buildEscrowWrap()`).
130
+ * Absent means this mailbox has no escrow scope assigned. */
131
+ escrowScopeId?: string;
132
+ /** Who may see when this mailbox is busy - see `FreeBusyVisibility`. Absent on a mailbox from before the setting existed, which reads as `"domain"`
133
+ * (`freeBusyVisibilityOf()`). Only the owner (or a delegate with full access) may change it. */
134
+ freeBusyVisibility?: FreeBusyVisibility;
135
+ /** This mailbox's Blocked Senders list (Outlook's): lowercase addresses (`user@example.com`) and domains (`@example.com`, that exact domain
136
+ * and not its subdomains), at most 1,000 of them, never also on `safeSenders`. Mail from one goes to Junk Email. Absent on a mailbox from
137
+ * before the list existed, or from a server without it - read it with `blockedSendersOf()`. Change it with `addBlockedSender()` and
138
+ * `removeBlockedSender()` (full access to the mailbox), not by writing the whole array. */
139
+ blockedSenders?: string[];
140
+ /** This mailbox's Safe Senders list, the same shape as `blockedSenders`. Authenticated mail from one is not sent to Junk Email for the spam
141
+ * filter's verdict. Read it with `safeSendersOf()`; change it with `addSafeSender()` and `removeSafeSender()`. */
142
+ safeSenders?: string[];
143
+ }
144
+
145
+ /** The blocked senders `mailbox` has: none when it says none (a mailbox stored before the list existed, or `null` from an SQL row). */
146
+ export function blockedSendersOf(mailbox: Pick<Mailbox, "blockedSenders">): string[] {
147
+ return mailbox.blockedSenders ?? [];
148
+ }
149
+
150
+ /** The safe senders `mailbox` has: none when it says none. */
151
+ export function safeSendersOf(mailbox: Pick<Mailbox, "safeSenders">): string[] {
152
+ return mailbox.safeSenders ?? [];
153
+ }
154
+
155
+ /** Lists the mailboxes the caller owns or has been granted - the same for everyone, an administrator included. With
156
+ * `scope: "admin"` (admin console only) it lists every mailbox as administrative metadata instead. */
157
+ export function listMailboxes(params: ListParams & AdminScopeParams = {}, client?: ApiClient): Promise<Mailbox[]> {
158
+ return withClient(client, `/mail/mailboxes?${buildQuery(params, scopeQuery(params))}`);
159
+ }
160
+
161
+ /** One mailbox the caller owns or has been granted; with `scope: "admin"` (admin console only), its administrative metadata. */
162
+ export function getMailbox(uid: string, options: AdminScopeParams = {}, client?: ApiClient): Promise<Mailbox> {
163
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(uid)}${options.scope ? `?scope=${options.scope}` : ""}`);
164
+ }
165
+
166
+ /** Lists bookable resource mailboxes (rooms/equipment) visible to the caller — same ACL scoping as
167
+ * `listMailboxes()`, just pre-filtered server-side to `isResource: true` so `ResourcePicker` doesn't
168
+ * need to fetch and filter the caller's entire mailbox list client-side. */
169
+ export function listResourceMailboxes(params: ListParams = {}, client?: ApiClient): Promise<Mailbox[]> {
170
+ return withClient(client, `/mail/mailboxes?${buildQuery(params, { isResource: "true" })}`);
171
+ }
172
+
173
+ export interface CreateMailboxInput {
174
+ /** Omit entirely to create a true ownerless shared mailbox — trusted-role-only (see BaseMailboxRoute). */
175
+ ownerUserUid?: string;
176
+ primarySmtpAddress: string;
177
+ aliasAddresses?: string[];
178
+ displayName: string;
179
+ timezone: string;
180
+ quotaBytes: number;
181
+ isResource?: boolean;
182
+ resourceType?: "room" | "equipment";
183
+ resourceCapacity?: number;
184
+ autoAcceptBookings?: boolean;
185
+ allowConflicts?: boolean;
186
+ bookingWindowDays?: number;
187
+ maxDurationMinutes?: number;
188
+ }
189
+
190
+ export function createMailbox(input: CreateMailboxInput, client?: ApiClient): Promise<Mailbox> {
191
+ return withClient(client, "/mail/mailboxes", {
192
+ method: "POST",
193
+ body: JSON.stringify({ aliasAddresses: [], usedBytes: 0, ...input }),
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Who a typed principal - a mailbox address, an auth-server username or e-mail alias, or a user uid - is, for an
199
+ * administrator naming a mailbox's owner (a new mailbox here, or a future reassignment) to confirm before saving.
200
+ * Rejects with a 404 `ApiRequestError` ("No user found for ...") for nobody. Trusted-role-only
201
+ * (`BaseMailboxRoute.resolveOwner()` in `@rapidmx/restapi`) - the same exact-match resolution
202
+ * `resolveMailboxPrincipal()` (`mailboxAccessApi.ts`) uses for sharing.
203
+ */
204
+ export function resolveMailboxOwner(principal: string, client?: ApiClient): Promise<ResolvedPrincipal> {
205
+ return withClient(client, `/mail/mailboxes/resolve-owner?principal=${encodeURIComponent(principal)}`);
206
+ }
207
+
208
+ /** This server's configured domain list (`mail:domains`) — empty when unconfigured, meaning no
209
+ * restriction applies and a `primarySmtpAddress` may be on any domain. */
210
+ export function listMailboxDomains(client?: ApiClient): Promise<string[]> {
211
+ return withClient(client, "/mail/mailboxes/domains");
212
+ }
213
+
214
+ /** One (name alias, domain) combination the caller could register as their mailbox address. */
215
+ export interface MailboxAutoProvisionAliasOption {
216
+ alias: string;
217
+ domain: string;
218
+ primarySmtpAddress: string;
219
+ }
220
+
221
+ export type MailboxAutoProvisionResult =
222
+ | { status: "created"; mailbox: Mailbox }
223
+ | { status: "existing"; mailbox: Mailbox }
224
+ | { status: "needs_selection"; options: MailboxAutoProvisionAliasOption[] };
225
+
226
+ /**
227
+ * Self-service mailbox creation for a user with none yet — see `BaseMailboxRoute.autoProvision()`'s own
228
+ * doc comment in `@rapidmx/restapi` for the full contract. Call with no `selection` first; if the
229
+ * result is `needs_selection`, call again with the option the user picked from that list.
230
+ */
231
+ export function autoProvisionMailbox(
232
+ selection?: { alias: string; domain: string },
233
+ client?: ApiClient,
234
+ ): Promise<MailboxAutoProvisionResult> {
235
+ return withClient(client, "/mail/mailboxes/auto-provision", {
236
+ method: "POST",
237
+ // The device's time zone goes with every call, for the mailbox this may create.
238
+ body: JSON.stringify({ ...selection, timezone: deviceTimeZone() }),
239
+ });
240
+ }
241
+
242
+ /**
243
+ * A partial mailbox update. The fields typed `| null` are the ones `@rapidmx/restapi`'s `Mailbox` model
244
+ * copies whenever the key is *present* in the body (not only when it's non-`undefined`), so sending
245
+ * `null` clears them - `JSON.stringify()` drops an `undefined` value entirely, which would instead leave
246
+ * the old value in place.
247
+ */
248
+ export interface UpdateMailboxInput {
249
+ uid: string;
250
+ version: number;
251
+ displayName?: string;
252
+ timezone?: string;
253
+ quotaBytes?: number;
254
+ aliasAddresses?: string[];
255
+ isResource?: boolean;
256
+ resourceType?: "room" | "equipment" | null;
257
+ resourceCapacity?: number | null;
258
+ autoAcceptBookings?: boolean;
259
+ allowConflicts?: boolean;
260
+ bookingWindowDays?: number | null;
261
+ maxDurationMinutes?: number | null;
262
+ oofEnabled?: boolean;
263
+ oofMessage?: string;
264
+ oofStartTime?: string | null;
265
+ oofEndTime?: string | null;
266
+ alwaysRequestReceiptInternal?: boolean;
267
+ alwaysRequestReceiptFederated?: boolean;
268
+ alwaysRequestReceiptExternal?: boolean;
269
+ autoSendReceiptsInternal?: boolean;
270
+ autoSendReceiptsFederated?: boolean;
271
+ autoSendReceiptsExternal?: boolean;
272
+ /** Trusted-caller-only server-side. `null` (or `""`) unassigns the mailbox's escrow scope; the
273
+ * referenced `EscrowScope` must exist otherwise. See `Mailbox.escrowScopeId`. */
274
+ escrowScopeId?: string | null;
275
+ /** The owner's own record only (403 for anyone without full access to the mailbox; 400 for a value that is not one of the four). */
276
+ freeBusyVisibility?: FreeBusyVisibility;
277
+ }
278
+
279
+ export function updateMailbox(input: UpdateMailboxInput, client?: ApiClient): Promise<Mailbox> {
280
+ return withClient(client, `/mail/mailboxes/${encodeURIComponent(input.uid)}`, {
281
+ method: "PUT",
282
+ body: JSON.stringify(input),
283
+ });
284
+ }
285
+
286
+ /**
287
+ * Deletes a mailbox. Only the mailbox itself goes: its folders and everything in them stay - and the address can't be used for a
288
+ * new mailbox - until that data is erased. `erase: true` (administrators only; anybody else is refused 403 before anything is
289
+ * deleted) also files that erasure in the same step, which the server then runs in the background; without it the data is left for
290
+ * `admin/leftoverMailboxApi.ts` to list and erase later.
291
+ */
292
+ export function deleteMailbox(uid: string, version: number, options: { erase?: boolean } = {}, client?: ApiClient): Promise<void> {
293
+ return withClient(
294
+ client,
295
+ `/mail/mailboxes/${encodeURIComponent(uid)}?version=${version}${options.erase ? "&erase=true" : ""}`,
296
+ { method: "DELETE" },
297
+ );
298
+ }
299
+
300
+ export type QuarantineReason = "infected" | "spam_policy" | "transport_rule" | "other";
301
+
302
+ export interface QuarantineEntry {
303
+ uid: string;
304
+ version: number;
305
+ dateCreated: string;
306
+ dateModified: string;
307
+ mailboxUid: string;
308
+ originalMessageUid?: string;
309
+ reason: QuarantineReason;
310
+ scanResultUid: string;
311
+ rawBlobKey: string;
312
+ releasedAt?: string;
313
+ releasedByUserUid?: string;
314
+ }
315
+
316
+ /** Lists quarantined mail for a mailbox the caller owns or has been granted; with `scope: "admin"` (admin console only), for any mailbox. */
317
+ export function listQuarantine(
318
+ mailboxUid: string,
319
+ params: ListParams & AdminScopeParams = {},
320
+ client?: ApiClient,
321
+ ): Promise<QuarantineEntry[]> {
322
+ return withClient(client, `/mail/quarantine?${buildQuery(params, { mailboxUid, ...scopeQuery(params) })}`);
323
+ }
324
+
325
+ /**
326
+ * Marks a quarantined entry released. This only updates the record's metadata — it does not re-inject the
327
+ * message into normal delivery (see `@rapidmx/restapi`'s NOTES.md for why that's an explicit non-goal here).
328
+ */
329
+ export function releaseQuarantineEntry(
330
+ uid: string,
331
+ version: number,
332
+ releasedByUserUid: string,
333
+ client?: ApiClient,
334
+ ): Promise<QuarantineEntry> {
335
+ return withClient(client, `/mail/quarantine/${encodeURIComponent(uid)}`, {
336
+ method: "PUT",
337
+ body: JSON.stringify({ uid, version, releasedAt: new Date().toISOString(), releasedByUserUid }),
338
+ });
339
+ }
340
+
341
+ export type IngestStatus = "pending" | "scanning" | "delivered" | "failed";
342
+
343
+ export interface IngestQueueEntry {
344
+ uid: string;
345
+ version: number;
346
+ dateCreated: string;
347
+ dateModified: string;
348
+ mailboxUid: string;
349
+ envelopeFrom: string;
350
+ envelopeTo: string[];
351
+ rawBlobKey: string;
352
+ status: IngestStatus;
353
+ errorMessage?: string;
354
+ /** How many times scanning has failed for this entry; unset until the first failure. */
355
+ attempts?: number;
356
+ /** ISO 8601 - when a `failed` entry becomes eligible for its next retry; unset once retries are exhausted. */
357
+ nextAttemptAt?: string;
358
+ /** ISO 8601 - while `scanning`, when the claiming worker's lease expires (after which the entry can be
359
+ * claimed again). */
360
+ scanLeaseExpiresAt?: string;
361
+ }
362
+
363
+ /** Lists ingest-queue entries for a mailbox the caller owns or has been granted - useful for diagnosing stuck delivery; with
364
+ * `scope: "admin"` (admin console only), for any mailbox. */
365
+ export function listIngestQueue(
366
+ mailboxUid: string,
367
+ params: ListParams & AdminScopeParams = {},
368
+ client?: ApiClient,
369
+ ): Promise<IngestQueueEntry[]> {
370
+ return withClient(client, `/mail/ingest-queue?${buildQuery(params, { mailboxUid, ...scopeQuery(params) })}`);
371
+ }
372
+
373
+ export interface AclRecord {
374
+ userOrRoleId: string;
375
+ actions: string[];
376
+ }
377
+
378
+ export interface AccessControlList {
379
+ uid: string;
380
+ version: number;
381
+ parentUid?: string;
382
+ records: AclRecord[];
383
+ }
384
+
385
+ /** Fetches a mailbox's own ACL — its `records` are its owner's/delegates' grants (see BaseACLRoute). The server only answers a
386
+ * caller who holds full access to that mailbox as themselves (never through a trusted role): to list, grant or revoke a mailbox's
387
+ * members use `listMailboxAccess()`/`setMailboxAccess()`/`removeMailboxAccess()` (`mailboxAccessApi.ts`), which is also the
388
+ * audited way an administrator shares an ownerless mailbox. */
389
+ export function getMailboxAcl(mailboxUid: string, client?: ApiClient): Promise<AccessControlList> {
390
+ return withClient(client, `/acls/${encodeURIComponent(mailboxUid)}`);
391
+ }
392
+
393
+ /**
394
+ * Grants (or replaces, if `userOrRoleId` already has a record) a delegate's access to a mailbox — the
395
+ * mechanism behind Exchange-style shared mailboxes. Read-modify-write against the ACL's own optimistic
396
+ * `version`, so concurrent grants can conflict; the caller should retry on a 409/version-mismatch.
397
+ */
398
+ export async function grantMailboxAccess(
399
+ mailboxUid: string,
400
+ userOrRoleId: string,
401
+ actions: string[],
402
+ client?: ApiClient,
403
+ ): Promise<AccessControlList> {
404
+ const acl = await getMailboxAcl(mailboxUid, client);
405
+ const records = acl.records.filter((r) => r.userOrRoleId !== userOrRoleId);
406
+ records.push({ userOrRoleId, actions });
407
+ return withClient(client, `/acls/${encodeURIComponent(mailboxUid)}`, {
408
+ method: "PUT",
409
+ body: JSON.stringify({ uid: mailboxUid, version: acl.version, records }),
410
+ });
411
+ }
412
+
413
+ /** Revokes a delegate's access to a mailbox previously granted via `grantMailboxAccess`. */
414
+ export async function revokeMailboxAccess(mailboxUid: string, userOrRoleId: string, client?: ApiClient): Promise<AccessControlList> {
415
+ const acl = await getMailboxAcl(mailboxUid, client);
416
+ const records = acl.records.filter((r) => r.userOrRoleId !== userOrRoleId);
417
+ return withClient(client, `/acls/${encodeURIComponent(mailboxUid)}`, {
418
+ method: "PUT",
419
+ body: JSON.stringify({ uid: mailboxUid, version: acl.version, records }),
420
+ });
421
+ }
422
+
423
+ export type FolderType =
424
+ | "inbox"
425
+ | "sent_items"
426
+ | "drafts"
427
+ | "deleted_items"
428
+ | "outbox"
429
+ | "junk"
430
+ | "archive"
431
+ | "calendar"
432
+ | "contacts"
433
+ | "tasks"
434
+ | "notes"
435
+ | "user";
436
+
437
+ export interface Folder {
438
+ uid: string;
439
+ version: number;
440
+ dateCreated: string;
441
+ dateModified: string;
442
+ mailboxUid: string;
443
+ name: string;
444
+ type: FolderType;
445
+ parentFolderUid?: string;
446
+ unreadCount: number;
447
+ totalCount: number;
448
+ /** Display color for a `calendar`-type folder (a hex string or palette key) — unused by every other
449
+ * folder type. Absent on a mailbox's original auto-provisioned calendar; see `calendarColors.ts`'s
450
+ * `colorForFolder` for the fallback every caller should use instead of reading this field directly. */
451
+ color?: string;
452
+ }
453
+
454
+ /** Lists a mailbox's folders — visible to its owner, any delegate the mailbox is shared with, or (trusted) anyone. */
455
+ export function listFolders(mailboxUid: string, client?: ApiClient): Promise<Folder[]> {
456
+ return withClient(client, `/mail/folders?${buildQuery({ limit: 200 }, { mailboxUid })}`);
457
+ }
458
+
459
+ export interface CreateFolderInput {
460
+ mailboxUid: string;
461
+ name: string;
462
+ type: FolderType;
463
+ parentFolderUid?: string;
464
+ color?: string;
465
+ }
466
+
467
+ /** Creates a new folder — e.g. an additional `calendar`-type folder for multi-calendar support. Nothing
468
+ * about folder creation is type-restricted server-side (see the Phase 4 plan's own note on
469
+ * `BaseFolderRoute.create()`), so this is just a thin wrapper, not a new backend capability. */
470
+ export function createFolder(input: CreateFolderInput, client?: ApiClient): Promise<Folder> {
471
+ return withClient(client, "/mail/folders", {
472
+ method: "POST",
473
+ body: JSON.stringify({ unreadCount: 0, totalCount: 0, ...input }),
474
+ });
475
+ }
476
+
477
+ export interface UpdateFolderInput {
478
+ uid: string;
479
+ version: number;
480
+ name?: string;
481
+ color?: string;
482
+ }
483
+
484
+ export function updateFolder(input: UpdateFolderInput, client?: ApiClient): Promise<Folder> {
485
+ return withClient(client, `/mail/folders/${encodeURIComponent(input.uid)}`, {
486
+ method: "PUT",
487
+ body: JSON.stringify(input),
488
+ });
489
+ }
490
+
491
+ export type RecipientType = "to" | "cc" | "bcc";
492
+
493
+ export interface Recipient {
494
+ address: string;
495
+ displayName?: string;
496
+ type: RecipientType;
497
+ }
498
+
499
+ export interface MessageFlags {
500
+ read: boolean;
501
+ flagged: boolean;
502
+ answered: boolean;
503
+ forwarded: boolean;
504
+ }
505
+
506
+ export type MessageImportance = "low" | "normal" | "high";
507
+
508
+ /** Mirrors `@rapidmx/restapi`'s `MessageClassification` enum values exactly (`classify()`'s `classifyAs`
509
+ * body field only accepts these two literal strings). */
510
+ export type MessageClassification = "focused" | "other";
511
+
512
+ export interface Message {
513
+ uid: string;
514
+ version: number;
515
+ dateCreated: string;
516
+ dateModified: string;
517
+ folderUid: string;
518
+ mailboxUid: string;
519
+ messageId: string;
520
+ subject: string;
521
+ from: Recipient;
522
+ recipients: Recipient[];
523
+ sentDate: string;
524
+ receivedDate: string;
525
+ bodyPreview: string;
526
+ flags: MessageFlags;
527
+ /** Server-managed mirror of `flags.read`, which exists only so the *server* can filter and sort on it
528
+ * (`flags` is one JSON column, so nothing can index into it). Read `flags.read`, not this: it is never
529
+ * accepted in a request body, and a message stored before these fields existed carries none of them. */
530
+ read?: boolean;
531
+ /** Server-managed mirror of `flags.flagged` — see `read`. */
532
+ flagged?: boolean;
533
+ /** Server-managed mirror of `from.address`, lowercased and trimmed — see `read`. */
534
+ fromAddress?: string;
535
+ importance: MessageImportance;
536
+ /** Server-managed sortable rank of `importance` (low 0, normal 1, high 2) — see `read`. */
537
+ importanceRank?: number;
538
+ hasAttachments: boolean;
539
+ /** Set once `recallMessage()` has been called on this message — a recall is asynchronous and
540
+ * best-effort (see that function's own doc comment), so this is the only signal a caller gets;
541
+ * there is no separate "recalled successfully"/"failed" outcome synced onto the record itself. */
542
+ recallRequestedAt?: string;
543
+ /** Set by `sendMessage()` with a future `scheduledSendTime` option, which defers relay until then instead of
544
+ * sending immediately — mirrors Outlook's "Do not deliver before". The message sits in the mailbox's
545
+ * `outbox` folder (lazily created on the first scheduled send — never eagerly provisioned the way
546
+ * Drafts/Sent Items are) until `@rapidmx/restapi`'s own `ScheduledSendJob` relays it and clears
547
+ * this field. */
548
+ scheduledSendTime?: string;
549
+ /** Consecutive failed `ScheduledSendJob` attempts for the current scheduled send - each failure pushes
550
+ * `scheduledSendTime` forward; cleared on success and when the job gives up. */
551
+ scheduledSendAttempts?: number;
552
+ /** Why `ScheduledSendJob` gave up on (or refused) this scheduled send. Set together with clearing
553
+ * `scheduledSendTime`, so the message stays in Outbox unsent - show it so the user can fix and resend.
554
+ * Cleared on a successful send. */
555
+ scheduledSendError?: string;
556
+ /** Set once the transport accepted this message; cleared when it is filed into Sent Items. */
557
+ scheduledSendRelayedAt?: string;
558
+ /** The in-flight marker of a send (the claim's lease expiry). While it lies in the future the message can't be
559
+ * moved out of Outbox. */
560
+ scheduledSendLeaseExpiresAt?: string;
561
+ /** Absent means Focused — see `classifyMessage()` and `@rapidmx/restapi`'s own
562
+ * `FocusedInboxUtils.classifyMessage()` doc comment for the full precedence rule this reflects. */
563
+ inferenceClassification?: MessageClassification;
564
+ /** Set on a draft before `sendMessage()` to request a receipt, overriding the mailbox's own
565
+ * `alwaysRequestReceiptInternal`/`External` defaults — see `setMessageRequestReceipt()`. Meaningless
566
+ * once the message has actually been sent. */
567
+ requestReceipt?: boolean;
568
+ /** `true` when a delivery/read receipt was requested but this (recipient) mailbox's
569
+ * `autoSendReceiptsInternal`/`External` setting held it for the owner's explicit approval instead of
570
+ * sending it immediately — see `approveReceipt()`/`declineReceipt()`. Always present on a real
571
+ * delivered `Message` (defaults `false`), same optional-for-old-fixtures caveat as `Mailbox.oofEnabled`. */
572
+ deliveryReceiptPending?: boolean;
573
+ readReceiptPending?: boolean;
574
+ /** The per-recipient delivery/read roster on a *sent* message — the client-visible tracking indicator.
575
+ * `undefined` (not an empty array) when no receipt was ever requested for this message. */
576
+ receiptStatus?: MessageReceiptEntry[];
577
+ /** `true` when this message's body is S/MIME (CMS) encrypted — computed server-side by
578
+ * `@rapidmx/restapi`'s `ScanPipeline` from the actual stored MIME structure, not something a
579
+ * client ever sets directly. Says nothing about whether it's *signed* — see
580
+ * `specs/end-to-end_encryption.md`'s Message Security Indicators (encryption and signing are
581
+ * separate guarantees); signature verification happens client-side by decrypting/parsing the
582
+ * stored body, not from a flag this API exposes. Always present on a real `Message` (defaults
583
+ * `false` server-side), same optional-for-old-fixtures caveat as `Mailbox.oofEnabled`. */
584
+ encrypted?: boolean;
585
+ /** The raw RFC 2369/8058 `List-Unsubscribe` header value, if present — computed server-side by
586
+ * `@rapidmx/restapi`'s `ScanPipeline` from the stored MIME, the strongest single bulk-mail signal it
587
+ * captures. Used client-side to detect list traffic when replying, per
588
+ * `specs/end-to-end_encryption.md`'s "Mailing lists" note under Digital Signatures: a list that
589
+ * appends a footer after signing invalidates the signature, so composing a reply to one SHOULD
590
+ * default signing off. `undefined` for ordinary mail — never computed or guessed client-side. */
591
+ listUnsubscribeHeader?: string;
592
+ /** `Label.uid`s applied to this message (Gmail-style labels, `labelsApi.ts`) - either set directly
593
+ * (`setMessageLabels()`) or auto-applied server-side by a `MailFilterActionType.APPLY_LABEL` rule.
594
+ * `undefined`/empty means no labels. */
595
+ labelUids?: string[];
596
+ /** An opaque client-written verification seal (at most 2048 characters of `[A-Za-z0-9+/=_.:-]`) recording that this
597
+ * message's signature verified when first opened - see `crypto/verificationSeal.ts` and
598
+ * `setMessageVerificationSeal()`. The server stores it as given, never interprets it, and never copies it to another
599
+ * message. Absent until a client writes one. */
600
+ verificationSeal?: string;
601
+ /** The vault `masterKeyGeneration` `verificationSeal` was written under, as sent to `setMessageVerificationSeal()`. */
602
+ verificationSealGeneration?: number;
603
+ /** What the reader reported this message as (`reportMessage()`); server-managed, never written by a client. Absent (or `null`) when never reported. */
604
+ reportedAs?: MessageReportKind | null;
605
+ /** When `reportedAs` was last set. */
606
+ dateReported?: string | null;
607
+ /** The `messageId` of the message this one replies to (RFC 5322 `In-Reply-To`, angle brackets stripped) -
608
+ * parsed from the MIME on a delivered message, and what a compose client passes to `createDraft()` when it
609
+ * opens a reply. Absent on a message that replies to nothing. */
610
+ inReplyTo?: string;
611
+ /** This message's thread, oldest first (RFC 5322 `References`, angle brackets stripped): the thread root's
612
+ * `messageId`, then each reply's down to the one this message replies to. Empty, or absent on a message
613
+ * stored before the field existed. See `buildReplyThreading()` in `compose/composeQuoting.ts`. */
614
+ references?: string[];
615
+ /** Server-assigned: the thread this message is grouped under in a conversation list (`conversationsApi.ts`).
616
+ * Derived at delivery/send time from `references`/`inReplyTo`, never accepted in a request body. */
617
+ conversationId?: string;
618
+ /** Server-managed: the iTIP `METHOD` of the calendar file this message carries - `"REQUEST"`, `"REPLY"`, `"CANCEL"`,
619
+ * `"PUBLISH"` or `"COUNTER"`, or `""` when the file names none - set on delivery for any message with a readable calendar file.
620
+ * Absent on a message with none. Lets a list mark meeting requests without opening every message. */
621
+ meetingMethod?: string;
622
+ /** Server-managed: what the reader answered a meeting request with (`respondToMessageInvite()`), when they have. */
623
+ meetingResponse?: "accepted" | "tentative" | "declined";
624
+ }
625
+
626
+ export interface MessageReceiptEntry {
627
+ recipientAddress: string;
628
+ deliveredAt?: string;
629
+ readAt?: string;
630
+ }
631
+
632
+ /**
633
+ * The sort keys `listMessages()` accepts, mirroring `@rapidmx/restapi`'s own `MESSAGE_LIST_SORTS` exactly —
634
+ * anything else is refused with a 400. This is the whole set this data model can support: Outlook's sort menu
635
+ * also offers Category, Flag status due date, Size and Type, and a RapidMX `Message` has no field behind any of
636
+ * those (labels are multi-valued, so there is no single category to order by; there is no follow-up due date,
637
+ * no stored message size and no message class). "Flag status" is `flagged`.
638
+ */
639
+ export type MessageListSort = "date" | "sentDate" | "from" | "subject" | "importance" | "flagged";
640
+
641
+ /**
642
+ * Outlook's "Newest on top" (`desc`) / "Oldest on top" (`asc`). Left off, the server picks the direction that
643
+ * reads naturally for the key — newest/highest/flagged first for `date`/`sentDate`/`importance`/`flagged`, A-Z
644
+ * for `from`/`subject`.
645
+ */
646
+ export type MessageSortOrder = "asc" | "desc";
647
+
648
+ /**
649
+ * The named filters `listMessages()` accepts, mirroring `@rapidmx/restapi`'s own `MESSAGE_LIST_FILTERS`
650
+ * exactly. `focused`/`other` are the Focused Inbox split (only meaningful in the Inbox; `focused` includes a
651
+ * message carrying no classification at all, which is what absent means).
652
+ *
653
+ * Outlook's filter menu additionally offers "To me", "Mentions me" and "Has calendar invites"; none of the
654
+ * three is a server-side filter here, because nothing on a `Message` records them.
655
+ */
656
+ export type MessageListFilter = "all" | "unread" | "read" | "flagged" | "hasAttachments" | "focused" | "other";
657
+
658
+ /**
659
+ * The most labels one list request may filter by, matching `@rapidmx/restapi`'s own
660
+ * `MAX_MESSAGE_LABEL_FILTER_UIDS` — a longer set is refused with a 400, so a label menu offering multiple
661
+ * selection should stop the user here rather than send it.
662
+ */
663
+ export const MAX_MESSAGE_LABEL_FILTER = 20;
664
+
665
+ /** Paging plus the mail list's own sort/filter vocabulary. */
666
+ export interface MessageListParams extends ListParams {
667
+ sortBy?: MessageListSort;
668
+ sortOrder?: MessageSortOrder;
669
+ filter?: MessageListFilter;
670
+ /**
671
+ * `Label.uid`s to filter by (list the mailbox's labels with `listLabels()` in `labelsApi.ts`). A message is
672
+ * listed if it carries ANY of them, which is then ANDed with `filter` — so `{ filter: "unread", labelUids:
673
+ * [red, blue] }` is "unread, and labelled red or blue". Order is irrelevant and duplicates are ignored. An
674
+ * empty array is no filter at all, and a uid naming no label (or one belonging to another mailbox) matches
675
+ * nothing. At most `MAX_MESSAGE_LABEL_FILTER` of them, each a real uid — anything else is a 400.
676
+ */
677
+ labelUids?: string[];
678
+ }
679
+
680
+ /** Only the sort/filter params actually set, so a default list sends the same URL it always did. */
681
+ export function messageListQuery(params: MessageListParams): Record<string, string> {
682
+ const query: Record<string, string> = {};
683
+ if (params.sortBy) {
684
+ query.sortBy = params.sortBy;
685
+ }
686
+ if (params.sortOrder) {
687
+ query.sortOrder = params.sortOrder;
688
+ }
689
+ if (params.filter) {
690
+ query.filter = params.filter;
691
+ }
692
+ // One comma-separated value, the only form the server parses. An empty selection sends no parameter at all
693
+ // rather than an empty one, so a filter menu with nothing ticked makes the request it always made.
694
+ if (params.labelUids?.length) {
695
+ query.labelUids = params.labelUids.join(",");
696
+ }
697
+ return query;
698
+ }
699
+
700
+ /**
701
+ * Lists messages in a folder — newest received first unless `sortBy`/`sortOrder` say otherwise, and unfiltered
702
+ * unless `filter`/`labelUids` say otherwise. All of them are applied by the *database*, over the whole folder
703
+ * rather than over the page this call happens to return, so `page`/`limit` stay correct under a filter and a
704
+ * sort. Ties are broken by `uid`, so a message never appears on two pages or on neither.
705
+ */
706
+ export function listMessages(folderUid: string, params: MessageListParams = {}, client?: ApiClient): Promise<Message[]> {
707
+ return withClient(client, `/mail/messages?${buildQuery(params, { folderUid, ...messageListQuery(params) })}`);
708
+ }
709
+
710
+ export function getMessage(uid: string, client?: ApiClient): Promise<Message> {
711
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}`);
712
+ }
713
+
714
+ /**
715
+ * Attempts to recall a message this mailbox previously sent — only valid for a message currently in
716
+ * Sent Items (enforced server-side). Asynchronous and best-effort: this composes and relays a control
717
+ * message to every original recipient, but the actual delete-if-still-unread mutation happens later on
718
+ * each recipient's own mail system — there is no synchronous "recalled" outcome to report back, and the
719
+ * only visible effect here is `recallRequestedAt` getting set on the response.
720
+ */
721
+ export function recallMessage(uid: string, client?: ApiClient): Promise<Message> {
722
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/recall`, { method: "POST" });
723
+ }
724
+
725
+ /**
726
+ * Moves a message into the mailbox's Archive folder (`FolderType.ARCHIVE`), lazily created on first use
727
+ * server-side — same pattern as Sent Items/Outbox, no client-side folder-creation step needed. Rejects
728
+ * (400, surfaced as an `ApiRequestError`) for a message currently in Drafts or Outbox. Idempotent —
729
+ * archiving an already-archived message is a no-op that still returns the message.
730
+ */
731
+ export function archiveMessage(uid: string, client?: ApiClient): Promise<Message> {
732
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/archive`, { method: "POST" });
733
+ }
734
+
735
+ /**
736
+ * Moves a message between the Focused and Other halves of the Inbox — Outlook's "Move to Other" gesture.
737
+ * With `applyToSender: true` ("Always move to Other"), also upserts a standing `FocusedInboxOverride` for
738
+ * every future message from the same sender (see `focusedInboxOverridesApi.ts`) in the same round trip.
739
+ */
740
+ export function classifyMessage(
741
+ uid: string,
742
+ classifyAs: MessageClassification,
743
+ applyToSender = false,
744
+ client?: ApiClient,
745
+ ): Promise<Message> {
746
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/classify`, {
747
+ method: "POST",
748
+ body: JSON.stringify({ classifyAs, applyToSender }),
749
+ });
750
+ }
751
+
752
+ /**
753
+ * Marks a message read/unread in place. `folderUid` must be included even though it isn't changing — every
754
+ * `Message` update is scoped by its owning folder (see `BaseScopedChildRoute`), and this framework's `PUT`
755
+ * routes replace the whole record rather than patch individual fields.
756
+ */
757
+ export function setMessageRead(message: Message, read: boolean, client?: ApiClient): Promise<Message> {
758
+ return withClient(client, `/mail/messages/${encodeURIComponent(message.uid)}`, {
759
+ method: "PUT",
760
+ body: JSON.stringify({ uid: message.uid, version: message.version, flags: { ...message.flags, read } }),
761
+ });
762
+ }
763
+
764
+ /** Flags/unflags a message in place — Outlook's flag-status toggle, and what `listMessages({ filter:
765
+ * "flagged" })` and `listFlaggedMessages()` select on. */
766
+ export function setMessageFlagged(message: Message, flagged: boolean, client?: ApiClient): Promise<Message> {
767
+ return withClient(client, `/mail/messages/${encodeURIComponent(message.uid)}`, {
768
+ method: "PUT",
769
+ body: JSON.stringify({ uid: message.uid, version: message.version, flags: { ...message.flags, flagged } }),
770
+ });
771
+ }
772
+
773
+ /** Moves a message into another folder of the same mailbox — how "Move to", "Archive" (to the Archive folder),
774
+ * "Report junk" (to Junk) and "Delete" (to Deleted Items) are all expressed. */
775
+ export function moveMessage(message: Message, folderUid: string, client?: ApiClient): Promise<Message> {
776
+ return withClient(client, `/mail/messages/${encodeURIComponent(message.uid)}`, {
777
+ method: "PUT",
778
+ body: JSON.stringify({ uid: message.uid, version: message.version, folderUid }),
779
+ });
780
+ }
781
+
782
+ /** What a message can be reported as - see `reportMessage()`. */
783
+ export type MessageReportKind = "junk" | "phishing" | "not_junk";
784
+
785
+ /** Why the server did not teach its spam filter from a report: `encrypted` (it has no plaintext to teach), `disabled` (learning is switched off),
786
+ * `unsupported` (no spam engine, or one that cannot learn), `too_large` (over the size it reads) or `failed` (the engine could not be reached,
787
+ * refused, or the message could not be read). The report itself still happened. */
788
+ export type MessageLearnSkipped = "encrypted" | "disabled" | "unsupported" | "too_large" | "failed";
789
+
790
+ /** What `reportMessage()` answers. */
791
+ export interface MessageReportResult {
792
+ uid: string;
793
+ kind: MessageReportKind;
794
+ /** The message changed folder; `false` when it was already where the report sends it. */
795
+ moved: boolean;
796
+ /** The folder the message is in now: the mailbox's Junk Email (`junk`, `phishing`) or Inbox (`not_junk`). */
797
+ folderUid: string;
798
+ /** The spam filter was taught from the message. */
799
+ learned: boolean;
800
+ /** Why it was not, when `learned` is `false` (absent when it was). */
801
+ learnSkipped?: MessageLearnSkipped;
802
+ /** The address added to the mailbox's safe senders, for `alwaysTrustSender`; absent when none was asked or the message names no usable one. */
803
+ safeSender?: string;
804
+ }
805
+
806
+ /** What `reportMessage()` may also ask for. */
807
+ export interface ReportMessageOptions {
808
+ /** With `not_junk` only (else a 400): also add the message's From address to the mailbox's safe senders. Needs full access to the mailbox (403 otherwise,
809
+ * before anything is done). */
810
+ alwaysTrustSender?: boolean;
811
+ }
812
+
813
+ /**
814
+ * Reports a message as junk, phishing or not junk - `POST /mail/messages/:uid/report`. The SERVER moves it (`junk` and `phishing` to the mailbox's Junk Email,
815
+ * `not_junk` to its Inbox; a message already there is not moved), records what was reported, teaches its spam filter (never for an encrypted message) and
816
+ * writes an audit entry; a caller needs no folder uid and no `version`. Rejects with a 400 for a message in Drafts or Outbox, a 403 without the right to change
817
+ * the message (or, for `alwaysTrustSender`, without full access to the mailbox), a 404 for an unknown message - and also for a server that predates the route,
818
+ * where a caller falls back to `moveMessage()`.
819
+ */
820
+ export function reportMessage(
821
+ uid: string,
822
+ kind: MessageReportKind,
823
+ options: ReportMessageOptions = {},
824
+ client?: ApiClient,
825
+ ): Promise<MessageReportResult> {
826
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/report`, {
827
+ method: "POST",
828
+ body: JSON.stringify({ kind, ...(options.alwaysTrustSender ? { alwaysTrustSender: true } : {}) }),
829
+ });
830
+ }
831
+
832
+ /**
833
+ * The most updates `bulkUpdateMessages()` puts in one request, matching `@rapidmx/restapi`'s own
834
+ * `MAX_BULK_UPDATE` — a longer list is refused with a 400, so the helper below chunks rather than sending it.
835
+ */
836
+ export const MAX_BULK_MESSAGE_UPDATE = 100;
837
+
838
+ /** One element of a bulk update: `uid` and the caller's last-known `version` (the server rejects a stale one
839
+ * with a 409), plus whichever fields are changing. */
840
+ export interface MessageUpdate {
841
+ uid: string;
842
+ version: number;
843
+ [field: string]: unknown;
844
+ }
845
+
846
+ /**
847
+ * Applies `updates` through `PUT /mail/messages`, the bulk form of the single-message update — one request per
848
+ * `MAX_BULK_MESSAGE_UPDATE` updates instead of one per message, with every permission, folder and legal-hold
849
+ * check the single-message path makes.
850
+ *
851
+ * Deliberately NOT atomic, on either side of the wire: the server applies each element in order and the first
852
+ * failure (a stale `version`, a refused move) aborts the rest of *that* request, leaving the elements before it
853
+ * applied; this helper then stops and rejects rather than sending the remaining chunks. So a caller should
854
+ * refetch the list after a rejection instead of assuming nothing happened, and should send fresh `version`s -
855
+ * a bulk action over a stale selection is the common way to hit a 409. For a per-element outcome, call the
856
+ * single-message functions instead.
857
+ *
858
+ * Resolves with every message actually updated, in request order.
859
+ */
860
+ export async function bulkUpdateMessages(updates: MessageUpdate[], client?: ApiClient): Promise<Message[]> {
861
+ const updated: Message[] = [];
862
+ for (let i = 0; i < updates.length; i += MAX_BULK_MESSAGE_UPDATE) {
863
+ const chunk = updates.slice(i, i + MAX_BULK_MESSAGE_UPDATE);
864
+ updated.push(...(await withClient<Message[]>(client, "/mail/messages", { method: "PUT", body: JSON.stringify(chunk) })));
865
+ }
866
+ return updated;
867
+ }
868
+
869
+ /** Marks a whole selection read/unread in one pass — see `bulkUpdateMessages()` for the failure semantics. */
870
+ export function setMessagesRead(messages: Message[], read: boolean, client?: ApiClient): Promise<Message[]> {
871
+ return bulkUpdateMessages(
872
+ messages.map((message) => ({ uid: message.uid, version: message.version, flags: { ...message.flags, read } })),
873
+ client,
874
+ );
875
+ }
876
+
877
+ /** Flags/unflags a whole selection in one pass — see `bulkUpdateMessages()`. */
878
+ export function setMessagesFlagged(messages: Message[], flagged: boolean, client?: ApiClient): Promise<Message[]> {
879
+ return bulkUpdateMessages(
880
+ messages.map((message) => ({ uid: message.uid, version: message.version, flags: { ...message.flags, flagged } })),
881
+ client,
882
+ );
883
+ }
884
+
885
+ /** Moves a whole selection into `folderUid` — bulk Move, Archive, Report junk and Delete (to Deleted Items)
886
+ * are all this call with a different target folder. See `bulkUpdateMessages()`. */
887
+ export function moveMessages(messages: Message[], folderUid: string, client?: ApiClient): Promise<Message[]> {
888
+ return bulkUpdateMessages(
889
+ messages.map((message) => ({ uid: message.uid, version: message.version, folderUid })),
890
+ client,
891
+ );
892
+ }
893
+
894
+ /** Sets the full `labelUids` list on a whole selection — see `setMessageLabels()` and `bulkUpdateMessages()`. */
895
+ export function setMessagesLabels(messages: Message[], labelUids: string[], client?: ApiClient): Promise<Message[]> {
896
+ return bulkUpdateMessages(
897
+ messages.map((message) => ({ uid: message.uid, version: message.version, labelUids })),
898
+ client,
899
+ );
900
+ }
901
+
902
+ /** Sets a message's full `labelUids` list (not an add/remove delta - the caller computes the complete
903
+ * new set, same convention as `Note`/`Task` label-like fields elsewhere in restapi). Deleting a label
904
+ * elsewhere already strips it server-side from every message (`labelsApi.ts#deleteLabel()`'s own doc
905
+ * comment) - this is only for a user explicitly applying/removing labels on one message. */
906
+ export function setMessageLabels(message: Message, labelUids: string[], client?: ApiClient): Promise<Message> {
907
+ return withClient(client, `/mail/messages/${encodeURIComponent(message.uid)}`, {
908
+ method: "PUT",
909
+ body: JSON.stringify({ uid: message.uid, version: message.version, labelUids }),
910
+ });
911
+ }
912
+
913
+ /**
914
+ * Cancels a scheduled send by moving the message out of Outbox into `draftsFolderUid` (the mailbox's Drafts
915
+ * folder, which the caller resolves). The server clears `scheduledSendTime` itself on any move out of Outbox;
916
+ * the `null` sent here is accepted and ignored.
917
+ */
918
+ export function cancelScheduledSend(message: Message, draftsFolderUid: string, client?: ApiClient): Promise<Message> {
919
+ return withClient(client, `/mail/messages/${encodeURIComponent(message.uid)}`, {
920
+ method: "PUT",
921
+ body: JSON.stringify({ uid: message.uid, version: message.version, scheduledSendTime: null, folderUid: draftsFolderUid }),
922
+ });
923
+ }
924
+
925
+ /**
926
+ * Sets a draft's `requestReceipt` ahead of calling `sendMessage()`, overriding the mailbox's own
927
+ * `alwaysRequestReceiptInternal`/`External` defaults for this one message — same ordinary-`PUT`-before-
928
+ * `send()` convention.
929
+ */
930
+ export function setMessageRequestReceipt(message: Message, requestReceipt: boolean, client?: ApiClient): Promise<Message> {
931
+ return withClient(client, `/mail/messages/${encodeURIComponent(message.uid)}`, {
932
+ method: "PUT",
933
+ body: JSON.stringify({ uid: message.uid, version: message.version, requestReceipt }),
934
+ });
935
+ }
936
+
937
+ /**
938
+ * Thrown by `setMessageVerificationSeal()` when restapi answers `409`: the message already carries a different seal the
939
+ * server won't replace (it replaces one only when the stored seal's generation is older than the vault's current
940
+ * `masterKeyGeneration` and the request's generation equals the current one). Still an `ApiRequestError` (`status` 409).
941
+ * A caller writing seals best effort should ignore it.
942
+ */
943
+ export class VerificationSealConflictError extends ApiRequestError {
944
+ constructor(message: string, code?: string) {
945
+ super(message, 409, code);
946
+ this.name = "VerificationSealConflictError";
947
+ }
948
+ }
949
+
950
+ /**
951
+ * Stores a verification seal on a message (`PUT /mail/messages/:id/verification-seal` with
952
+ * `{ seal, masterKeyGeneration }`), normally `MessageSecurityResult.sealToWrite`'s two fields from
953
+ * `crypto/messageSecurity.ts`'s `evaluateMessageSecurityWithSeal()`. Resolves with the updated message; storing the
954
+ * identical seal again succeeds. Rejects with `VerificationSealConflictError` on `409` (a different seal is stored and
955
+ * isn't replaceable - see that class), and a plain `ApiRequestError` for `400` (an invalid seal or generation) and
956
+ * `403`/`404`.
957
+ */
958
+ export async function setMessageVerificationSeal(
959
+ messageUid: string,
960
+ seal: string,
961
+ masterKeyGeneration: number,
962
+ client?: ApiClient,
963
+ ): Promise<Message> {
964
+ try {
965
+ return await withClient<Message>(client, `/mail/messages/${encodeURIComponent(messageUid)}/verification-seal`, {
966
+ method: "PUT",
967
+ body: JSON.stringify({ seal, masterKeyGeneration }),
968
+ });
969
+ } catch (err) {
970
+ if (err instanceof ApiRequestError && err.status === 409) {
971
+ throw new VerificationSealConflictError(err.message, err.code);
972
+ }
973
+ throw err;
974
+ }
975
+ }
976
+
977
+ export type ReceiptType = "delivery" | "read";
978
+
979
+ /** Sends a delivery/read receipt this mailbox held pending the owner's explicit approval (see
980
+ * `Message.deliveryReceiptPending`/`readReceiptPending`). */
981
+ export function approveReceipt(uid: string, type: ReceiptType, client?: ApiClient): Promise<Message> {
982
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/receipt/approve`, {
983
+ method: "POST",
984
+ body: JSON.stringify({ type }),
985
+ });
986
+ }
987
+
988
+ /** Permanently declines a pending receipt — no later re-prompt for that same event. */
989
+ export function declineReceipt(uid: string, type: ReceiptType, client?: ApiClient): Promise<Message> {
990
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/receipt/decline`, {
991
+ method: "POST",
992
+ body: JSON.stringify({ type }),
993
+ });
994
+ }
995
+
996
+ export interface Attachment {
997
+ uid: string;
998
+ version: number;
999
+ dateCreated: string;
1000
+ dateModified: string;
1001
+ messageUid: string;
1002
+ folderUid: string;
1003
+ mailboxUid: string;
1004
+ filename: string;
1005
+ mimeType: string;
1006
+ sizeBytes: number;
1007
+ isInline: boolean;
1008
+ }
1009
+
1010
+ /** Lists the attachments belonging to a single message. */
1011
+ export function listAttachments(folderUid: string, messageUid: string, client?: ApiClient): Promise<Attachment[]> {
1012
+ return withClient(client, `/mail/attachments?${buildQuery({ limit: 200 }, { folderUid, messageUid })}`);
1013
+ }
1014
+
1015
+ /** The URL to download/display an attachment's binary content — not fetched via `apiFetch`, used directly as
1016
+ * a link/image `href`/`src`. Honors `configureApiBaseUrl()` (see `apiUrl()`). */
1017
+ export function attachmentContentUrl(uid: string): string {
1018
+ return apiUrl(`/mail/attachments/${encodeURIComponent(uid)}/content`);
1019
+ }
1020
+
1021
+ /**
1022
+ * Uploads a file's raw bytes as a new attachment on a not-yet-sent draft. Bypasses `apiFetch` — that helper
1023
+ * always forces `Content-Type: application/json`, which would corrupt binary content; this sends the file's
1024
+ * own bytes/type directly instead, matching `BaseAttachmentRoute.upload`'s expectation of a raw request body.
1025
+ */
1026
+ export async function uploadAttachment(messageUid: string, file: File): Promise<Attachment> {
1027
+ const params = new URLSearchParams({ messageUid, filename: file.name, mimeType: file.type || "application/octet-stream" });
1028
+ const res = await fetch(apiUrl(`/mail/attachments/upload?${params.toString()}`), {
1029
+ method: "POST",
1030
+ credentials: "include",
1031
+ headers: withCsrfHeader({ "Content-Type": file.type || "application/octet-stream" }),
1032
+ body: file,
1033
+ });
1034
+ const contentType = res.headers.get("content-type") ?? "";
1035
+ const responseBody = contentType.includes("application/json") ? await res.json().catch(() => undefined) : undefined;
1036
+ if (!res.ok) {
1037
+ const message = (responseBody && (responseBody.message || responseBody.error)) || res.statusText || "Upload failed.";
1038
+ throw new ApiRequestError(message, res.status, responseBody?.code);
1039
+ }
1040
+ return responseBody as Attachment;
1041
+ }
1042
+
1043
+ export interface ComposeRecipientInput {
1044
+ address: string;
1045
+ displayName?: string;
1046
+ }
1047
+
1048
+ export interface AssembleDraftInput {
1049
+ to: ComposeRecipientInput[];
1050
+ cc?: ComposeRecipientInput[];
1051
+ bcc?: ComposeRecipientInput[];
1052
+ subject?: string;
1053
+ html: string;
1054
+ }
1055
+
1056
+ /** What a draft records about the thread it belongs to - see `createDraft()`. */
1057
+ export interface DraftThreading {
1058
+ /** The `messageId` of the message being replied to (`Message.inReplyTo`). */
1059
+ inReplyTo?: string;
1060
+ /** The thread's `References` chain, oldest first, ending with `inReplyTo` (`Message.references`). */
1061
+ references?: string[];
1062
+ }
1063
+
1064
+ /**
1065
+ * Creates a blank draft `Message` in the given folder (normally the mailbox's Drafts folder) to compose into.
1066
+ *
1067
+ * A reply MUST pass `threading` (build it with `buildReplyThreading()` from the message being replied to).
1068
+ * `@rapidmx/restapi` writes those values into the `In-Reply-To`/`References` headers of the MIME it relays and
1069
+ * groups the message into the replied-to message's conversation; a reply created without them is relayed with no
1070
+ * threading headers at all, and every recipient - and the sender's own Sent Items copy - files it as a brand-new
1071
+ * conversation. Nothing else recovers them: this server composes the MIME from the recipients, subject and HTML
1072
+ * passed to `assembleDraft()`, which say nothing about what is being replied to.
1073
+ */
1074
+ export function createDraft(
1075
+ mailboxUid: string,
1076
+ folderUid: string,
1077
+ threading?: DraftThreading,
1078
+ client?: ApiClient,
1079
+ ): Promise<Message> {
1080
+ return withClient(client, "/mail/messages", {
1081
+ method: "POST",
1082
+ body: JSON.stringify({
1083
+ mailboxUid,
1084
+ folderUid,
1085
+ messageId: `${crypto.randomUUID()}@webmail`,
1086
+ ...(threading?.inReplyTo ? { inReplyTo: threading.inReplyTo } : {}),
1087
+ ...(threading?.references?.length ? { references: threading.references } : {}),
1088
+ }),
1089
+ });
1090
+ }
1091
+
1092
+ /** Deletes a message - e.g. an unsent draft that's no longer needed because compose switched to a different
1093
+ * sending mailbox. */
1094
+ export function deleteMessage(uid: string, version: number, client?: ApiClient): Promise<void> {
1095
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}?version=${version}`, { method: "DELETE" });
1096
+ }
1097
+
1098
+ /**
1099
+ * Permanently deletes one message - `DELETE /mail/messages/:uid?purge=true`, the hard delete: the row and its search
1100
+ * document are gone and it cannot be restored, unlike `deleteMessage()` (a soft delete the server can bring back) or a
1101
+ * move into Deleted Items. Whoever may delete in the message's folder may do this - the mailbox's owner, or a delegate
1102
+ * granted `delete` - there is nothing administrative about it.
1103
+ *
1104
+ * Takes no `version`: one that no longer matches (another device flipped a flag since the list was fetched) would make
1105
+ * the server answer "not found", which is no reason to keep a message the reader has asked to erase. Refused with a
1106
+ * `409` for a message under an active legal hold (the response's message says so) or with a send in flight, and with a
1107
+ * `403` without the `delete` grant. Nothing about the message changes when it is refused, so a caller deleting several
1108
+ * reports each refusal and carries on with the rest.
1109
+ */
1110
+ export function purgeMessage(uid: string, client?: ApiClient): Promise<void> {
1111
+ return withClient(client, `/mail/messages/${encodeURIComponent(uid)}?purge=true`, { method: "DELETE" });
1112
+ }
1113
+
1114
+ /**
1115
+ * Permanently deletes every message in a folder in one request - `DELETE /mail/messages?folderUid=` (the collection
1116
+ * `truncate`, always a hard delete). All or nothing: the server checks every message for a legal hold first and refuses
1117
+ * the whole request with a `409` if any is held, and with a `403` unless the caller holds the `truncate` grant on the
1118
+ * mailbox (its owner or a manager - not a delegate who may merely delete). Resolves without saying how many messages
1119
+ * went, so a caller that needs to says so from what it knew. Messages already soft-deleted are not part of it.
1120
+ *
1121
+ * After a refusal, fall back to `purgeMessage()` per message: that removes what may be removed and says which may not.
1122
+ */
1123
+ export function emptyFolder(folderUid: string, client?: ApiClient): Promise<void> {
1124
+ return withClient(client, `/mail/messages?${new URLSearchParams({ folderUid }).toString()}`, { method: "DELETE" });
1125
+ }
1126
+
1127
+ /**
1128
+ * Assembles a draft's structured compose input (recipients/subject/HTML body, plus whatever attachments have
1129
+ * already been `uploadAttachment()`-ed onto it) into RFC 5322 MIME and stores it as the draft's `bodyBlobKey`
1130
+ * — see `BaseMailComposeRoute` (this app's own compose-assembly glue, since `@rapidmx/restapi`'s `send()`
1131
+ * itself does no MIME composition). Does not send the message.
1132
+ */
1133
+ export function assembleDraft(messageUid: string, input: AssembleDraftInput, client?: ApiClient): Promise<Message> {
1134
+ return withClient(client, `/mail/compose/${encodeURIComponent(messageUid)}/assemble`, {
1135
+ method: "POST",
1136
+ body: JSON.stringify(input),
1137
+ });
1138
+ }
1139
+
1140
+ export interface AssembleDraftRawInput {
1141
+ to: ComposeRecipientInput[];
1142
+ cc?: ComposeRecipientInput[];
1143
+ bcc?: ComposeRecipientInput[];
1144
+ /** The message's own top-level `Subject` — for an encrypted message this MUST be the outer,
1145
+ * RFC 9788 `hcp_baseline`-obscured value (`"[...]"`, see `crypto/smimeMessage.ts`'s
1146
+ * `applyBaselineOuterHeaders()`), matching what's already in `rawMime`'s own outer header. */
1147
+ subject: string;
1148
+ /** The complete RFC 5322 message source, already finalized client-side — see
1149
+ * `crypto/smimeMessage.ts`'s `buildSignedOnlyMessage()`/`buildEncryptedMessage()`, combined with
1150
+ * the outer envelope headers (`From`/`To`/`Cc`/`Date`/`Message-ID`/`MIME-Version`) by the caller. */
1151
+ rawMime: string;
1152
+ }
1153
+
1154
+ /**
1155
+ * Stores an already-signed/encrypted draft's raw MIME source as its `bodyBlobKey`, completely
1156
+ * unmodified — the E2E counterpart to `assembleDraft()`, for when the message body was built
1157
+ * client-side via `crypto/smimeMessage.ts` rather than from plain HTML. Does not send the message. A
1158
+ * draft assembled this way cannot carry file attachments yet — see `BaseMailComposeRoute.assembleRaw()`'s
1159
+ * own doc comment in `server`.
1160
+ */
1161
+ export function assembleDraftRaw(messageUid: string, input: AssembleDraftRawInput, client?: ApiClient): Promise<Message> {
1162
+ return withClient(client, `/mail/compose/${encodeURIComponent(messageUid)}/assemble-raw`, {
1163
+ method: "POST",
1164
+ body: JSON.stringify(input),
1165
+ });
1166
+ }
1167
+
1168
+ /** Options for `sendMessage()`. */
1169
+ export interface SendMessageOptions {
1170
+ /** A future time (ISO 8601) to queue the message in Outbox until, instead of sending now. */
1171
+ scheduledSendTime?: string;
1172
+ }
1173
+
1174
+ /**
1175
+ * Scans, relays, and moves an already-assembled draft into Sent Items - or, with a future
1176
+ * `scheduledSendTime`, queues it in Outbox for `@rapidmx/restapi`'s `ScheduledSendJob`. A message already in
1177
+ * Outbox or Sent Items is refused (409); move it back to Drafts first.
1178
+ */
1179
+ export function sendMessage(messageUid: string, options?: SendMessageOptions, client?: ApiClient): Promise<Message> {
1180
+ return withClient(client, `/mail/messages/${encodeURIComponent(messageUid)}/send`, {
1181
+ method: "POST",
1182
+ ...(options?.scheduledSendTime ? { body: JSON.stringify({ scheduledSendTime: options.scheduledSendTime }) } : {}),
1183
+ });
1184
+ }
1185
+
1186
+ /** What `queueMessageSend()` resolves with. */
1187
+ export interface QueuedSend {
1188
+ /** `true`: the server accepted the message and will relay it in the background (`202 { status: "queued", message }`) - the
1189
+ * outcome arrives as a `send-succeeded`/`send-failed`/`send-retrying` push event (see `sendEvents.ts`). `false`: a server that
1190
+ * doesn't queue relayed it before answering, so the message has already been sent (`message` is its Sent Items copy). */
1191
+ queued: boolean;
1192
+ /** The message as the server holds it now - in Outbox, with its send in flight, when `queued`. */
1193
+ message: Message;
1194
+ }
1195
+
1196
+ /**
1197
+ * Sends an already-assembled draft *in the background*: `POST /mail/messages/:id/send` with `{ background: true }`. The server
1198
+ * checks the message, moves it into Outbox with a lease and answers `202 { status: "queued", message }` at once - relaying happens
1199
+ * afterwards, and its outcome is published as push events on the mailbox's, Outbox's and Sent Items' channels rather than answered
1200
+ * here. Rejects with the server's own error (validation, permissions: the message was not queued). A server that has no queue
1201
+ * relays first and answers with the message, which resolves as `{ queued: false }` - as does a message class with no send job, which
1202
+ * answers a background send `501`: it is then sent the ordinary, synchronous way.
1203
+ */
1204
+ export async function queueMessageSend(messageUid: string, client?: ApiClient): Promise<QueuedSend> {
1205
+ let result: Message | { status?: string; message?: Message };
1206
+ try {
1207
+ result = await withClient<Message | { status?: string; message?: Message }>(
1208
+ client,
1209
+ `/mail/messages/${encodeURIComponent(messageUid)}/send`,
1210
+ { method: "POST", body: JSON.stringify({ background: true }) },
1211
+ );
1212
+ } catch (err) {
1213
+ if (err instanceof ApiRequestError && err.status === 501) {
1214
+ return { queued: false, message: await sendMessage(messageUid, undefined, client) };
1215
+ }
1216
+ throw err;
1217
+ }
1218
+ const queued = result as { status?: string; message?: Message };
1219
+ if (queued.status === "queued" && queued.message) {
1220
+ return { queued: true, message: queued.message };
1221
+ }
1222
+ return { queued: false, message: result as Message };
1223
+ }
1224
+
1225
+ /**
1226
+ * Fetches a message's raw RFC 5322 MIME source — for client-side E2E decrypt/signature-verification
1227
+ * only (`crypto/smimeMessage.ts`), never for display. Bypasses `apiFetch()` (which only ever decodes a
1228
+ * JSON response body): this server-local route (`BaseMessageRawContentRoute.ts` in `server`, mounted
1229
+ * alongside `@rapidmx/restapi`'s own `MessageRoute`) returns `message/rfc822`, not JSON — the one thing
1230
+ * that library's own `GET /:id/content` deliberately never serves (see that route's own doc comment).
1231
+ *
1232
+ * Returns a *binary string* - the response bytes decoded as Latin-1, exactly one character per byte -
1233
+ * rather than `res.text()`'s UTF-8 decoding, which would replace every invalid byte of a non-UTF-8 8bit
1234
+ * part with U+FFFD before its own `charset` is known and break any signature over those bytes. Pass it
1235
+ * straight to `evaluateMessageSecurity()` (see `crypto/mime.ts`'s doc comment on binary strings); it is
1236
+ * not display text.
1237
+ */
1238
+ export async function getMessageRawContent(messageUid: string): Promise<string> {
1239
+ const res = await fetch(apiUrl(`/mail/messages/${encodeURIComponent(messageUid)}/raw`), { credentials: "include" });
1240
+ if (!res.ok) {
1241
+ const contentType = res.headers.get("content-type") ?? "";
1242
+ const body = contentType.includes("application/json") ? await res.json().catch(() => undefined) : undefined;
1243
+ const message = (body && (body.message || body.error)) || res.statusText || "Could not load this message's raw content.";
1244
+ throw new ApiRequestError(message, res.status, body?.code);
1245
+ }
1246
+ // Not `new TextDecoder("latin1")`: WHATWG maps that label to windows-1252, which remaps 0x80-0x9F and so
1247
+ // isn't byte-preserving. `bytesToBinaryString()` is an exact byte -> code unit conversion.
1248
+ return bytesToBinaryString(new Uint8Array(await res.arrayBuffer()));
1249
+ }
1250
+
1251
+ export interface ImpersonationResult {
1252
+ token: string;
1253
+ user: { uid: string; roles: string[]; scopes: string[] };
1254
+ }
1255
+
1256
+ /**
1257
+ * Trusted-role-only: starts an admin "log in as user" session. In production this calls auth-server
1258
+ * directly (not this app's own API) since only auth-server can mint a token carrying `userUid`'s real
1259
+ * roles/scopes the way its own sign-in flow would — see `@rapidrest/auth`'s `BaseImpersonationRoute` and
1260
+ * this repo's `.claude/NOTES.md`. The browser's `jwt` cookie is swapped for the freshly-minted token, and
1261
+ * the caller's own session is stashed (`jwt_impersonator`) so `stopImpersonating()` can restore it later.
1262
+ * The caller is responsible for navigating to `/` afterward — this call only swaps the cookie, it doesn't
1263
+ * redirect.
1264
+ *
1265
+ * @param impersonationBaseUrl The origin to call — the real auth-server in production, or `""` under
1266
+ * `yarn dev` (see `AdminConsoleRoute`/`wwwRoute`'s `fetchProps`) to instead call this app's own local
1267
+ * dev-only endpoint (`DevImpersonationRoute`), since a real auth-server isn't running locally.
1268
+ */
1269
+ export function impersonateUser(impersonationBaseUrl: string, userUid: string): Promise<ImpersonationResult> {
1270
+ const init: RequestInit = { method: "POST", body: JSON.stringify({ userUid }) };
1271
+ return impersonationBaseUrl
1272
+ ? authApiFetch(impersonationBaseUrl, "/admin/impersonate", init)
1273
+ : apiFetch("/admin/impersonate", init);
1274
+ }
1275
+
1276
+ /**
1277
+ * Ends an active impersonation session, restoring the admin's own — a no-op (`restored: false`) if none is
1278
+ * active. POST, not GET: this mutates state (swaps the active `jwt` cookie back), and a state-changing GET
1279
+ * is exploitable via a bare navigation — no form or script required at all — bypassing CSRF defenses
1280
+ * entirely, since they only ever apply to non-safe methods. See `@rapidrest/auth`'s `BaseImpersonationRoute`.
1281
+ */
1282
+ export function stopImpersonating(impersonationBaseUrl: string): Promise<{ restored: boolean }> {
1283
+ return impersonationBaseUrl
1284
+ ? authApiFetch(impersonationBaseUrl, "/admin/impersonate/stop", { method: "POST" })
1285
+ : apiFetch("/admin/impersonate/stop", { method: "POST" });
1286
+ }