@rapidmx/web-client 0.21.0 → 0.22.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 (885) 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 +27 -23
  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 +2 -2
  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 +7 -5
  120. package/apps/shared/components/mail/ConversationThreadPane.tsx +26 -15
  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 +32 -30
  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 +2 -2
  142. package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
  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/messageExport.ts +1 -1
  159. package/apps/shared/components/mail/reading/printMessage.ts +2 -2
  160. package/apps/shared/components/mail/reading/useMessageActions.ts +28 -21
  161. package/apps/shared/components/mail/unreadStyle.tsx +1 -1
  162. package/apps/shared/components/mail/verificationSeals.ts +16 -12
  163. package/apps/shared/components/mail/writableMailboxes.ts +14 -7
  164. package/apps/shared/components/rules/RuleBuilder.tsx +1 -1
  165. package/apps/shared/components/settings/SigningCertificateCard.tsx +4 -4
  166. package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
  167. package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
  168. package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
  169. package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
  170. package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
  171. package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
  172. package/apps/shared/keyboard/ShortcutsDialog.tsx +1 -1
  173. package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
  174. package/apps/shared/mail/folderCounts.ts +8 -4
  175. package/apps/shared/mail/folderOfType.ts +5 -3
  176. package/apps/shared/mail/folderTree.ts +1 -1
  177. package/apps/shared/mail/listSnapshots.ts +2 -2
  178. package/apps/shared/mail/messageReadState.ts +6 -5
  179. package/apps/shared/mail/newMailNotifications.ts +2 -2
  180. package/apps/shared/mail/outbox/composeBridge.ts +2 -2
  181. package/apps/shared/mail/outbox/outboxState.ts +1 -1
  182. package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
  183. package/apps/shared/mail/outbox/sendDecision.ts +2 -2
  184. package/apps/shared/mail/outbox/sendJob.ts +40 -35
  185. package/apps/shared/mail/outbox/sendOutcomes.ts +20 -15
  186. package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
  187. package/apps/shared/mail/permanentDelete.ts +8 -7
  188. package/apps/shared/mail/primaryMailbox.ts +1 -1
  189. package/apps/shared/mail/reportNotices.ts +1 -1
  190. package/apps/shared/mail/senderBlocking.ts +36 -20
  191. package/apps/shared/mail/useMailConnection.ts +12 -10
  192. package/apps/shared/mail/useMailLiveUpdates.ts +3 -3
  193. package/apps/shared/mail/useMailboxUpdateAccess.ts +35 -11
  194. package/apps/shared/mail/useMarkMessageRead.ts +14 -7
  195. package/apps/shared/mail/useNewMailNotifications.ts +1 -1
  196. package/apps/shared/mail/usePermanentDelete.tsx +5 -3
  197. package/apps/shared/navigation/pageTitle.ts +1 -1
  198. package/apps/shared/notifications/NotificationCenter.tsx +1 -1
  199. package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
  200. package/apps/shared/notifications/apiErrors.ts +2 -2
  201. package/apps/shared/notifications/pushStatus.ts +1 -1
  202. package/apps/shared/notifications/systemErrors.ts +1 -1
  203. package/apps/shared/search/LocalIndexLifecycle.tsx +18 -12
  204. package/apps/shared/search/crossMailboxSearch.ts +3 -3
  205. package/apps/shared/search/localIndexBuilder.ts +24 -12
  206. package/apps/shared/search/localIndexKey.ts +1 -1
  207. package/apps/shared/search/localIndexRpcClient.ts +150 -30
  208. package/apps/shared/search/localIndexTransport.ts +84 -0
  209. package/apps/shared/search/localIndexWorker.ts +1 -1
  210. package/apps/shared/search/searchTier2.ts +3 -3
  211. package/apps/shared/signing/enrollmentTracker.ts +3 -3
  212. package/apps/shared/signing/enrollmentView.ts +1 -1
  213. package/apps/shared/signing/signingInfo.ts +1 -1
  214. package/apps/shared/signing/useSigningEnrollmentWatcher.ts +6 -4
  215. package/apps/shared/styles/app.css +14 -23
  216. package/apps/www/_layout.tsx +2 -2
  217. package/apps/www/calendar/index.tsx +20 -18
  218. package/apps/www/contacts/[uid].tsx +8 -6
  219. package/apps/www/contacts/index.tsx +19 -17
  220. package/apps/www/index.tsx +57 -48
  221. package/apps/www/messages/[uid].tsx +11 -9
  222. package/apps/www/settings/auto-reply/index.tsx +6 -4
  223. package/apps/www/settings/blocked-senders/index.tsx +9 -7
  224. package/apps/www/settings/encryption/index.tsx +38 -32
  225. package/apps/www/settings/filters/[uid].tsx +19 -14
  226. package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
  227. package/apps/www/settings/filters/index.tsx +4 -4
  228. package/apps/www/settings/filters/new/index.tsx +10 -8
  229. package/apps/www/settings/labels/index.tsx +12 -10
  230. package/apps/www/settings/privacy/index.tsx +18 -14
  231. package/apps/www/settings/profile/index.tsx +16 -11
  232. package/apps/www/settings/read-receipts/index.tsx +17 -12
  233. package/apps/www/settings/sharing/index.tsx +6 -6
  234. package/apps/www/settings/signatures/[uid].tsx +5 -5
  235. package/apps/www/settings/signatures/index.tsx +4 -4
  236. package/apps/www/settings/signatures/new/index.tsx +4 -4
  237. package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
  238. package/apps/www/tasks/index.tsx +28 -23
  239. package/dist/apps/admin/_layout.d.ts +1 -1
  240. package/dist/apps/admin/_layout.js +1 -1
  241. package/dist/apps/admin/audit-log/index.d.ts +1 -1
  242. package/dist/apps/admin/audit-log/index.js +4 -4
  243. package/dist/apps/admin/branding/index.js +3 -3
  244. package/dist/apps/admin/data-requests/index.js +8 -8
  245. package/dist/apps/admin/distribution-lists/[uid].js +3 -3
  246. package/dist/apps/admin/distribution-lists/index.js +4 -4
  247. package/dist/apps/admin/distribution-lists/new/index.js +5 -5
  248. package/dist/apps/admin/domains/[uid].js +6 -6
  249. package/dist/apps/admin/domains/index.js +4 -4
  250. package/dist/apps/admin/domains/new/index.js +5 -5
  251. package/dist/apps/admin/encryption-policy/index.js +1 -1
  252. package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
  253. package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
  254. package/dist/apps/admin/escrow-scopes/index.js +4 -4
  255. package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
  256. package/dist/apps/admin/index.js +6 -6
  257. package/dist/apps/admin/ingest-queue/index.js +4 -4
  258. package/dist/apps/admin/mailbox-policy/index.js +1 -1
  259. package/dist/apps/admin/mailboxes/[uid].js +5 -5
  260. package/dist/apps/admin/quarantine/index.js +5 -5
  261. package/dist/apps/admin/retention-policy/index.js +3 -3
  262. package/dist/apps/admin/signing-certificates/index.js +5 -5
  263. package/dist/apps/admin/transport-rules/[uid].js +5 -5
  264. package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
  265. package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
  266. package/dist/apps/admin/transport-rules/index.js +4 -4
  267. package/dist/apps/admin/transport-rules/new/index.js +5 -5
  268. package/dist/apps/escrow/_layout.d.ts +1 -1
  269. package/dist/apps/escrow/audit-log/index.d.ts +1 -1
  270. package/dist/apps/escrow/audit-log/index.js +4 -4
  271. package/dist/apps/escrow/index.js +4 -4
  272. package/dist/apps/escrow/matters/[uid].js +10 -10
  273. package/dist/apps/escrow/matters/new/index.js +6 -6
  274. package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
  275. package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
  276. package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
  277. package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
  278. package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
  279. package/dist/apps/shared/appearance/appearanceCache.js +1 -1
  280. package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
  281. package/dist/apps/shared/appearance/theme.d.ts +1 -1
  282. package/dist/apps/shared/auth/adminAccess.js +1 -1
  283. package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
  284. package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
  285. package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
  286. package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
  287. package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
  288. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
  289. package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
  290. package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
  291. package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
  292. package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
  293. package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
  294. package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
  295. package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
  296. package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
  297. package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
  298. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
  299. package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
  300. package/dist/apps/shared/components/admin/elevation.js +1 -1
  301. package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
  302. package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
  303. package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -7
  304. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
  305. package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
  306. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
  307. package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
  308. package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
  309. package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
  310. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
  311. package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
  312. package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
  313. package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
  314. package/dist/apps/shared/components/admin/settings/BrandingForm.js +5 -5
  315. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
  316. package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
  317. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
  318. package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
  319. package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
  320. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
  321. package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
  322. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
  323. package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
  324. package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
  325. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
  326. package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
  327. package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
  328. package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
  329. package/dist/apps/shared/components/admin/signOut.js +1 -1
  330. package/dist/apps/shared/components/admin/usePagedList.js +2 -2
  331. package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
  332. package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
  333. package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
  334. package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
  335. package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
  336. package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
  337. package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
  338. package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
  339. package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
  340. package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
  341. package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
  342. package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
  343. package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
  344. package/dist/apps/shared/components/calendar/EventModal.js +3 -3
  345. package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
  346. package/dist/apps/shared/components/calendar/EventShell.js +1 -1
  347. package/dist/apps/shared/components/calendar/FindATime.js +6 -4
  348. package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
  349. package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
  350. package/dist/apps/shared/components/calendar/MonthView.js +1 -1
  351. package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
  352. package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
  353. package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
  354. package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
  355. package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
  356. package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
  357. package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
  358. package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
  359. package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
  360. package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
  361. package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
  362. package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
  363. package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
  364. package/dist/apps/shared/components/calendar/allDay.js +1 -1
  365. package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
  366. package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
  367. package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
  368. package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
  369. package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
  370. package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
  371. package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
  372. package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
  373. package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
  374. package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
  375. package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
  376. package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
  377. package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
  378. package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
  379. package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
  380. package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
  381. package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
  382. package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
  383. package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
  384. package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
  385. package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
  386. package/dist/apps/shared/components/escrow/layout/EscrowShell.js +6 -6
  387. package/dist/apps/shared/components/forms/StringListField.js +1 -1
  388. package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
  389. package/dist/apps/shared/components/layout/AppShell.js +11 -9
  390. package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
  391. package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
  392. package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
  393. package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
  394. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
  395. package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
  396. package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
  397. package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
  398. package/dist/apps/shared/components/layout/UserMenu.js +6 -4
  399. package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
  400. package/dist/apps/shared/components/mail/ConversationList.js +6 -4
  401. package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
  402. package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
  403. package/dist/apps/shared/components/mail/InviteCard.js +10 -8
  404. package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
  405. package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
  406. package/dist/apps/shared/components/mail/MailAddress.js +1 -1
  407. package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
  408. package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
  409. package/dist/apps/shared/components/mail/MenuButton.js +1 -1
  410. package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
  411. package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
  412. package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
  413. package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
  414. package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
  415. package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
  416. package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
  417. package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
  418. package/dist/apps/shared/components/mail/compose/ComposeWindow.js +34 -30
  419. package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
  420. package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
  421. package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
  422. package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
  423. package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
  424. package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
  425. package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
  426. package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
  427. package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
  428. package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
  429. package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
  430. package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
  431. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
  432. package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
  433. package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
  434. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
  435. package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
  436. package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
  437. package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
  438. package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
  439. package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
  440. package/dist/apps/shared/components/mail/labelMenu.js +9 -7
  441. package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
  442. package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
  443. package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
  444. package/dist/apps/shared/components/mail/listPreferences.js +1 -1
  445. package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
  446. package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
  447. package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
  448. package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
  449. package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
  450. package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
  451. package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
  452. package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
  453. package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
  454. package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
  455. package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
  456. package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
  457. package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
  458. package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
  459. package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
  460. package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
  461. package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
  462. package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
  463. package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
  464. package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
  465. package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
  466. package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
  467. package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
  468. package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
  469. package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
  470. package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
  471. package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
  472. package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
  473. package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
  474. package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
  475. package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
  476. package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
  477. package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
  478. package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
  479. package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
  480. package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
  481. package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
  482. package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
  483. package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
  484. package/dist/apps/shared/mail/folderCounts.js +6 -4
  485. package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
  486. package/dist/apps/shared/mail/folderOfType.js +4 -4
  487. package/dist/apps/shared/mail/folderTree.d.ts +1 -1
  488. package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
  489. package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
  490. package/dist/apps/shared/mail/messageReadState.js +5 -5
  491. package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
  492. package/dist/apps/shared/mail/newMailNotifications.js +1 -1
  493. package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
  494. package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
  495. package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
  496. package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
  497. package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
  498. package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
  499. package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
  500. package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
  501. package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
  502. package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
  503. package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
  504. package/dist/apps/shared/mail/permanentDelete.js +8 -8
  505. package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
  506. package/dist/apps/shared/mail/primaryMailbox.js +1 -1
  507. package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
  508. package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
  509. package/dist/apps/shared/mail/senderBlocking.js +19 -16
  510. package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
  511. package/dist/apps/shared/mail/useMailConnection.js +12 -10
  512. package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
  513. package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
  514. package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
  515. package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
  516. package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
  517. package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
  518. package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
  519. package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
  520. package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
  521. package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
  522. package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
  523. package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
  524. package/dist/apps/shared/notifications/apiErrors.js +2 -2
  525. package/dist/apps/shared/notifications/pushStatus.js +1 -1
  526. package/dist/apps/shared/notifications/systemErrors.js +1 -1
  527. package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
  528. package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
  529. package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
  530. package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
  531. package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
  532. package/dist/apps/shared/search/localIndexBuilder.js +15 -12
  533. package/dist/apps/shared/search/localIndexKey.js +1 -1
  534. package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
  535. package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
  536. package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
  537. package/dist/apps/shared/search/localIndexTransport.js +1 -0
  538. package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
  539. package/dist/apps/shared/search/searchTier2.d.ts +3 -3
  540. package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
  541. package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
  542. package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
  543. package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
  544. package/dist/apps/shared/signing/signingInfo.js +1 -1
  545. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
  546. package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
  547. package/dist/apps/shared/styles/app.css +14 -23
  548. package/dist/apps/www/_layout.d.ts +1 -1
  549. package/dist/apps/www/_layout.js +1 -1
  550. package/dist/apps/www/calendar/index.d.ts +1 -1
  551. package/dist/apps/www/calendar/index.js +20 -18
  552. package/dist/apps/www/contacts/[uid].d.ts +1 -1
  553. package/dist/apps/www/contacts/[uid].js +8 -6
  554. package/dist/apps/www/contacts/index.d.ts +1 -1
  555. package/dist/apps/www/contacts/index.js +19 -17
  556. package/dist/apps/www/index.d.ts +1 -1
  557. package/dist/apps/www/index.js +54 -50
  558. package/dist/apps/www/messages/[uid].d.ts +1 -1
  559. package/dist/apps/www/messages/[uid].js +11 -9
  560. package/dist/apps/www/settings/appearance/index.d.ts +1 -1
  561. package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
  562. package/dist/apps/www/settings/auto-reply/index.js +6 -4
  563. package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
  564. package/dist/apps/www/settings/blocked-senders/index.js +6 -4
  565. package/dist/apps/www/settings/encryption/index.d.ts +1 -1
  566. package/dist/apps/www/settings/encryption/index.js +31 -29
  567. package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
  568. package/dist/apps/www/settings/filters/[uid].js +11 -9
  569. package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
  570. package/dist/apps/www/settings/filters/index.d.ts +1 -1
  571. package/dist/apps/www/settings/filters/index.js +4 -4
  572. package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
  573. package/dist/apps/www/settings/filters/new/index.js +10 -8
  574. package/dist/apps/www/settings/labels/index.d.ts +1 -1
  575. package/dist/apps/www/settings/labels/index.js +12 -10
  576. package/dist/apps/www/settings/privacy/index.d.ts +1 -1
  577. package/dist/apps/www/settings/privacy/index.js +18 -14
  578. package/dist/apps/www/settings/profile/index.d.ts +1 -1
  579. package/dist/apps/www/settings/profile/index.js +7 -5
  580. package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
  581. package/dist/apps/www/settings/read-receipts/index.js +5 -3
  582. package/dist/apps/www/settings/sharing/index.d.ts +1 -1
  583. package/dist/apps/www/settings/sharing/index.js +6 -6
  584. package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
  585. package/dist/apps/www/settings/signatures/[uid].js +5 -5
  586. package/dist/apps/www/settings/signatures/index.d.ts +1 -1
  587. package/dist/apps/www/settings/signatures/index.js +4 -4
  588. package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
  589. package/dist/apps/www/settings/signatures/new/index.js +4 -4
  590. package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
  591. package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
  592. package/dist/apps/www/tasks/index.d.ts +1 -1
  593. package/dist/apps/www/tasks/index.js +17 -15
  594. package/dist/lib/admin/auditLogApi.d.ts +32 -0
  595. package/dist/lib/admin/auditLogApi.js +27 -0
  596. package/dist/lib/admin/distributionListsApi.d.ts +47 -0
  597. package/dist/lib/admin/distributionListsApi.js +34 -0
  598. package/dist/lib/admin/domainsApi.d.ts +75 -0
  599. package/dist/lib/admin/domainsApi.js +44 -0
  600. package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
  601. package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
  602. package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
  603. package/dist/lib/admin/escrowAuditLogApi.js +31 -0
  604. package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
  605. package/dist/lib/admin/escrowScopesApi.js +50 -0
  606. package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
  607. package/dist/lib/admin/leftoverMailboxApi.js +67 -0
  608. package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
  609. package/dist/lib/admin/mailboxPolicyApi.js +21 -0
  610. package/dist/lib/admin/matterExportApi.d.ts +40 -0
  611. package/dist/lib/admin/matterExportApi.js +39 -0
  612. package/dist/lib/admin/matterSearchApi.d.ts +20 -0
  613. package/dist/lib/admin/matterSearchApi.js +27 -0
  614. package/dist/lib/admin/mattersApi.d.ts +56 -0
  615. package/dist/lib/admin/mattersApi.js +40 -0
  616. package/dist/lib/admin/pluginsApi.d.ts +265 -0
  617. package/dist/lib/admin/pluginsApi.js +102 -0
  618. package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
  619. package/dist/lib/admin/retentionPolicyApi.js +29 -0
  620. package/dist/lib/admin/setupApi.d.ts +23 -0
  621. package/dist/lib/admin/setupApi.js +26 -0
  622. package/dist/lib/admin/transportRulesApi.d.ts +66 -0
  623. package/dist/lib/admin/transportRulesApi.js +43 -0
  624. package/dist/lib/appearance/preferencesApi.d.ts +128 -0
  625. package/dist/lib/appearance/preferencesApi.js +257 -0
  626. package/dist/lib/auth/profileApi.d.ts +33 -0
  627. package/dist/lib/auth/profileApi.js +52 -0
  628. package/dist/lib/auth/session.d.ts +70 -0
  629. package/dist/lib/auth/session.js +210 -0
  630. package/dist/lib/branding/brandingApi.d.ts +47 -0
  631. package/dist/lib/branding/brandingApi.js +61 -0
  632. package/dist/lib/branding/useBranding.d.ts +23 -0
  633. package/dist/lib/branding/useBranding.js +65 -0
  634. package/dist/lib/calendar/calendarApi.d.ts +230 -0
  635. package/dist/lib/calendar/calendarApi.js +106 -0
  636. package/dist/lib/calendar/calendarColors.d.ts +18 -0
  637. package/dist/lib/calendar/calendarColors.js +39 -0
  638. package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
  639. package/dist/lib/calendar/calendarDragIds.js +92 -0
  640. package/dist/lib/calendar/calendarMutations.d.ts +68 -0
  641. package/dist/lib/calendar/calendarMutations.js +190 -0
  642. package/dist/lib/calendar/eventDescription.d.ts +43 -0
  643. package/dist/lib/calendar/eventDescription.js +195 -0
  644. package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
  645. package/dist/lib/calendar/freeBusyApi.js +119 -0
  646. package/dist/lib/calendar/inviteApi.d.ts +121 -0
  647. package/dist/lib/calendar/inviteApi.js +54 -0
  648. package/dist/lib/calendar/recurrence.d.ts +60 -0
  649. package/dist/lib/calendar/recurrence.js +225 -0
  650. package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
  651. package/dist/lib/components/avatar/ContactAvatar.js +26 -0
  652. package/dist/lib/components/buttons/Button.d.ts +9 -0
  653. package/dist/lib/components/buttons/Button.js +14 -0
  654. package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
  655. package/dist/lib/components/buttons/CopyButton.js +14 -0
  656. package/dist/lib/components/feedback/Alert.d.ts +4 -0
  657. package/dist/lib/components/feedback/Alert.js +4 -0
  658. package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
  659. package/dist/lib/components/feedback/Skeleton.js +14 -0
  660. package/dist/lib/components/forms/FormField.d.ts +8 -0
  661. package/dist/lib/components/forms/FormField.js +5 -0
  662. package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
  663. package/dist/lib/components/navigation/BottomTabBar.js +32 -0
  664. package/dist/lib/components/overlays/Drawer.d.ts +20 -0
  665. package/dist/lib/components/overlays/Drawer.js +27 -0
  666. package/dist/lib/components/overlays/Modal.d.ts +17 -0
  667. package/dist/lib/components/overlays/Modal.js +25 -0
  668. package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
  669. package/dist/lib/components/overlays/PopoverPortal.js +53 -0
  670. package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
  671. package/dist/lib/components/overlays/overlayStack.js +143 -0
  672. package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
  673. package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
  674. package/dist/lib/contacts/contactsApi.d.ts +174 -0
  675. package/dist/lib/contacts/contactsApi.js +139 -0
  676. package/dist/lib/contacts/vcard.d.ts +23 -0
  677. package/dist/lib/contacts/vcard.js +179 -0
  678. package/dist/lib/crypto/composeSecurity.d.ts +67 -0
  679. package/dist/lib/crypto/composeSecurity.js +93 -0
  680. package/dist/lib/crypto/encoding.d.ts +9 -0
  681. package/dist/lib/crypto/encoding.js +32 -0
  682. package/dist/lib/crypto/escrowKeys.d.ts +25 -0
  683. package/dist/lib/crypto/escrowKeys.js +59 -0
  684. package/dist/lib/crypto/idleTimeout.d.ts +16 -0
  685. package/dist/lib/crypto/idleTimeout.js +47 -0
  686. package/dist/lib/crypto/keySession.d.ts +159 -0
  687. package/dist/lib/crypto/keySession.js +367 -0
  688. package/dist/lib/crypto/keys.d.ts +50 -0
  689. package/dist/lib/crypto/keys.js +89 -0
  690. package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
  691. package/dist/lib/crypto/keyvaultApi.js +350 -0
  692. package/dist/lib/crypto/masterKey.d.ts +59 -0
  693. package/dist/lib/crypto/masterKey.js +104 -0
  694. package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
  695. package/dist/lib/crypto/masterKeyWraps.js +228 -0
  696. package/dist/lib/crypto/messageSecurity.d.ts +248 -0
  697. package/dist/lib/crypto/messageSecurity.js +367 -0
  698. package/dist/lib/crypto/mime.d.ts +143 -0
  699. package/dist/lib/crypto/mime.js +504 -0
  700. package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
  701. package/dist/lib/crypto/passkeyUnlock.js +79 -0
  702. package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
  703. package/dist/lib/crypto/passwordUnlock.js +73 -0
  704. package/dist/lib/crypto/recoveryCode.d.ts +28 -0
  705. package/dist/lib/crypto/recoveryCode.js +86 -0
  706. package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
  707. package/dist/lib/crypto/signingProviderApi.js +60 -0
  708. package/dist/lib/crypto/smime.d.ts +128 -0
  709. package/dist/lib/crypto/smime.js +402 -0
  710. package/dist/lib/crypto/smimeMessage.d.ts +132 -0
  711. package/dist/lib/crypto/smimeMessage.js +341 -0
  712. package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
  713. package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
  714. package/dist/lib/crypto/verificationSeal.d.ts +56 -0
  715. package/dist/lib/crypto/verificationSeal.js +220 -0
  716. package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
  717. package/dist/lib/mail/compose/composeQuoting.js +204 -0
  718. package/dist/lib/mail/compose/emojiData.d.ts +9 -0
  719. package/dist/lib/mail/compose/emojiData.js +18 -0
  720. package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
  721. package/dist/lib/mail/compose/giphyApi.js +20 -0
  722. package/dist/lib/mail/conversationsApi.d.ts +80 -0
  723. package/dist/lib/mail/conversationsApi.js +44 -0
  724. package/dist/lib/mail/dataExportApi.d.ts +50 -0
  725. package/dist/lib/mail/dataExportApi.js +40 -0
  726. package/dist/lib/mail/directoryApi.d.ts +49 -0
  727. package/dist/lib/mail/directoryApi.js +101 -0
  728. package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
  729. package/dist/lib/mail/erasureRequestApi.js +43 -0
  730. package/dist/lib/mail/flaggedMessages.d.ts +22 -0
  731. package/dist/lib/mail/flaggedMessages.js +73 -0
  732. package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
  733. package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
  734. package/dist/lib/mail/labelsApi.d.ts +47 -0
  735. package/dist/lib/mail/labelsApi.js +38 -0
  736. package/dist/lib/mail/mailAddress.d.ts +27 -0
  737. package/dist/lib/mail/mailAddress.js +54 -0
  738. package/dist/lib/mail/mailApi.d.ts +850 -0
  739. package/dist/lib/mail/mailApi.js +602 -0
  740. package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
  741. package/dist/lib/mail/mailDetailHooks.js +53 -0
  742. package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
  743. package/dist/lib/mail/mailFilterRulesApi.js +45 -0
  744. package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
  745. package/dist/lib/mail/mailSignaturesApi.js +42 -0
  746. package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
  747. package/dist/lib/mail/mailboxAccessApi.js +56 -0
  748. package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
  749. package/dist/lib/mail/mailboxImportApi.js +51 -0
  750. package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
  751. package/dist/lib/mail/messageBodySanitizer.js +118 -0
  752. package/dist/lib/mail/pushClient.d.ts +106 -0
  753. package/dist/lib/mail/pushClient.js +317 -0
  754. package/dist/lib/mail/sendEvents.d.ts +33 -0
  755. package/dist/lib/mail/sendEvents.js +44 -0
  756. package/dist/lib/mail/sendFailure.d.ts +16 -0
  757. package/dist/lib/mail/sendFailure.js +86 -0
  758. package/dist/lib/mail/senderListsApi.d.ts +69 -0
  759. package/dist/lib/mail/senderListsApi.js +126 -0
  760. package/dist/lib/search/queryGrammar.d.ts +51 -0
  761. package/dist/lib/search/queryGrammar.js +111 -0
  762. package/dist/lib/search/searchApi.d.ts +100 -0
  763. package/dist/lib/search/searchApi.js +107 -0
  764. package/dist/lib/search/searchScoring.d.ts +34 -0
  765. package/dist/lib/search/searchScoring.js +44 -0
  766. package/dist/lib/search/searchTier3.d.ts +64 -0
  767. package/dist/lib/search/searchTier3.js +335 -0
  768. package/dist/lib/tasks/tasksApi.d.ts +85 -0
  769. package/dist/lib/tasks/tasksApi.js +55 -0
  770. package/dist/lib/util/api.d.ts +160 -0
  771. package/dist/lib/util/api.js +251 -0
  772. package/dist/lib/util/apiClientContext.d.ts +7 -0
  773. package/dist/lib/util/apiClientContext.js +32 -0
  774. package/dist/lib/util/apiQuery.d.ts +32 -0
  775. package/dist/lib/util/apiQuery.js +42 -0
  776. package/dist/lib/util/clipboard.d.ts +7 -0
  777. package/dist/lib/util/clipboard.js +56 -0
  778. package/dist/lib/util/dateInput.d.ts +6 -0
  779. package/dist/lib/util/dateInput.js +14 -0
  780. package/dist/lib/util/timeZone.d.ts +12 -0
  781. package/dist/lib/util/timeZone.js +32 -0
  782. package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
  783. package/dist/lib/util/useCopyToClipboard.js +34 -0
  784. package/dist/lib/util/useIsMobile.d.ts +10 -0
  785. package/dist/lib/util/useIsMobile.js +29 -0
  786. package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
  787. package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
  788. package/lib/admin/auditLogApi.ts +48 -0
  789. package/lib/admin/distributionListsApi.ts +77 -0
  790. package/lib/admin/domainsApi.ts +113 -0
  791. package/lib/admin/escrowAccessRequestsApi.ts +110 -0
  792. package/lib/admin/escrowAuditLogApi.ts +101 -0
  793. package/lib/admin/escrowScopesApi.ts +112 -0
  794. package/lib/admin/leftoverMailboxApi.ts +120 -0
  795. package/lib/admin/mailboxPolicyApi.ts +38 -0
  796. package/lib/admin/matterExportApi.ts +62 -0
  797. package/lib/admin/matterSearchApi.ts +35 -0
  798. package/lib/admin/mattersApi.ts +86 -0
  799. package/lib/admin/pluginsApi.ts +339 -0
  800. package/lib/admin/retentionPolicyApi.ts +46 -0
  801. package/lib/admin/setupApi.ts +40 -0
  802. package/lib/admin/transportRulesApi.ts +106 -0
  803. package/lib/appearance/preferencesApi.ts +335 -0
  804. package/lib/auth/profileApi.ts +77 -0
  805. package/lib/auth/session.ts +238 -0
  806. package/lib/branding/brandingApi.ts +97 -0
  807. package/lib/branding/useBranding.ts +81 -0
  808. package/lib/calendar/calendarApi.ts +309 -0
  809. package/lib/calendar/calendarColors.ts +45 -0
  810. package/lib/calendar/calendarDragIds.ts +112 -0
  811. package/lib/calendar/calendarMutations.ts +236 -0
  812. package/lib/calendar/eventDescription.ts +211 -0
  813. package/lib/calendar/freeBusyApi.ts +197 -0
  814. package/lib/calendar/inviteApi.ts +154 -0
  815. package/lib/calendar/recurrence.ts +285 -0
  816. package/lib/components/avatar/ContactAvatar.tsx +47 -0
  817. package/lib/components/buttons/Button.tsx +40 -0
  818. package/lib/components/buttons/CopyButton.tsx +45 -0
  819. package/lib/components/feedback/Alert.tsx +13 -0
  820. package/lib/components/feedback/Skeleton.tsx +38 -0
  821. package/lib/components/forms/FormField.tsx +23 -0
  822. package/lib/components/navigation/BottomTabBar.tsx +66 -0
  823. package/lib/components/overlays/Drawer.tsx +72 -0
  824. package/lib/components/overlays/Modal.tsx +64 -0
  825. package/lib/components/overlays/PopoverPortal.tsx +74 -0
  826. package/lib/components/overlays/overlayStack.ts +165 -0
  827. package/lib/components/pickers/MiniDatePicker.tsx +90 -0
  828. package/lib/contacts/contactsApi.ts +289 -0
  829. package/lib/contacts/vcard.ts +197 -0
  830. package/lib/crypto/composeSecurity.ts +140 -0
  831. package/lib/crypto/encoding.ts +36 -0
  832. package/lib/crypto/escrowKeys.ts +74 -0
  833. package/lib/crypto/idleTimeout.ts +49 -0
  834. package/lib/crypto/keySession.ts +483 -0
  835. package/lib/crypto/keys.ts +105 -0
  836. package/lib/crypto/keyvaultApi.ts +674 -0
  837. package/lib/crypto/masterKey.ts +134 -0
  838. package/lib/crypto/masterKeyWraps.ts +269 -0
  839. package/lib/crypto/messageSecurity.ts +600 -0
  840. package/lib/crypto/mime.ts +568 -0
  841. package/lib/crypto/passkeyUnlock.ts +103 -0
  842. package/lib/crypto/passwordUnlock.ts +98 -0
  843. package/lib/crypto/recoveryCode.ts +93 -0
  844. package/lib/crypto/signingProviderApi.ts +135 -0
  845. package/lib/crypto/smime.ts +459 -0
  846. package/lib/crypto/smimeMessage.ts +491 -0
  847. package/lib/crypto/useIdleKeyTimeout.ts +176 -0
  848. package/lib/crypto/verificationSeal.ts +288 -0
  849. package/lib/mail/compose/composeQuoting.ts +243 -0
  850. package/lib/mail/compose/emojiData.ts +34 -0
  851. package/lib/mail/compose/giphyApi.ts +31 -0
  852. package/lib/mail/conversationsApi.ts +123 -0
  853. package/lib/mail/dataExportApi.ts +73 -0
  854. package/lib/mail/directoryApi.ts +161 -0
  855. package/lib/mail/erasureRequestApi.ts +71 -0
  856. package/lib/mail/flaggedMessages.ts +80 -0
  857. package/lib/mail/focusedInboxOverridesApi.ts +64 -0
  858. package/lib/mail/labelsApi.ts +71 -0
  859. package/lib/mail/mailAddress.ts +76 -0
  860. package/lib/mail/mailApi.ts +1286 -0
  861. package/lib/mail/mailDetailHooks.ts +57 -0
  862. package/lib/mail/mailFilterRulesApi.ts +116 -0
  863. package/lib/mail/mailSignaturesApi.ts +84 -0
  864. package/lib/mail/mailboxAccessApi.ts +106 -0
  865. package/lib/mail/mailboxImportApi.ts +87 -0
  866. package/lib/mail/messageBodySanitizer.ts +129 -0
  867. package/lib/mail/pushClient.ts +375 -0
  868. package/lib/mail/sendEvents.ts +72 -0
  869. package/lib/mail/sendFailure.ts +98 -0
  870. package/lib/mail/senderListsApi.ts +161 -0
  871. package/lib/search/queryGrammar.ts +179 -0
  872. package/lib/search/searchApi.ts +199 -0
  873. package/lib/search/searchScoring.ts +46 -0
  874. package/lib/search/searchTier3.ts +406 -0
  875. package/lib/tasks/tasksApi.ts +128 -0
  876. package/lib/util/api.ts +318 -0
  877. package/lib/util/apiClientContext.ts +35 -0
  878. package/lib/util/apiQuery.ts +61 -0
  879. package/lib/util/clipboard.ts +55 -0
  880. package/lib/util/dateInput.ts +15 -0
  881. package/lib/util/timeZone.ts +33 -0
  882. package/lib/util/useCopyToClipboard.ts +48 -0
  883. package/lib/util/useIsMobile.ts +34 -0
  884. package/lib/videoconf/videoMeetingsApi.ts +144 -0
  885. package/package.json +10 -4
@@ -0,0 +1,236 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * Higher-level calendar mutations shared by the event modal (edit/delete) and the grid views'
7
+ * drag-and-drop handlers (move/resize) — both need the exact same "this occurrence vs. the entire
8
+ * series" logic for a recurring event, so it lives here once rather than being duplicated.
9
+ *
10
+ * A recurring event has no per-occurrence record server-side — `@rapidmx/restapi` stores only the
11
+ * master `CalendarEvent` plus its `RecurrenceRule` (see `recurrence.ts`'s header comment). Changing
12
+ * (or deleting) a single occurrence is therefore always a two-part operation: add that occurrence's
13
+ * original start to the master's `recurrenceRule.exceptions` (so it stops being generated), and, for
14
+ * an edit (not a delete), separately create a new standalone `CalendarEvent` carrying the edited
15
+ * fields, with `recurrenceId` set to that original occurrence's start for traceability — mirroring
16
+ * how `CalendarEvent.recurrenceId` is documented to work.
17
+ */
18
+
19
+ import {
20
+ CalendarEvent,
21
+ CalendarEventInput,
22
+ UpdateCalendarEventInput,
23
+ createCalendarEvent,
24
+ deleteCalendarEvent,
25
+ getCalendarEvent,
26
+ listCalendarEvents,
27
+ updateCalendarEvent,
28
+ } from "./calendarApi.js";
29
+ import { CalendarOccurrence, fromEventWallClock, toEventWallClock } from "./recurrence.js";
30
+ import { ApiClient } from "../util/api.js";
31
+
32
+ /** `client`, given by every function below, is an explicit `ApiClient` from `createApiClient()` (e.g. one
33
+ * account of a multi-account app) to call instead of the default global `apiFetch()` - see `withClient()`'s
34
+ * own doc comment in `util/api.ts`. Omitted (the default), every function here behaves exactly as before,
35
+ * and threads the same `client` through to every `calendarApi.ts` call it makes internally. */
36
+
37
+ const MS_PER_DAY = 24 * 60 * 60 * 1000;
38
+
39
+ function positiveModulo(value: number, modulus: number): number {
40
+ return ((value % modulus) + modulus) % modulus;
41
+ }
42
+
43
+ function addException(event: CalendarEvent, occurrenceStart: string, client?: ApiClient): Promise<CalendarEvent> {
44
+ const rule = event.recurrenceRule!;
45
+ return updateCalendarEvent(
46
+ {
47
+ uid: event.uid,
48
+ version: event.version,
49
+ recurrenceRule: { ...rule, exceptions: [...rule.exceptions, occurrenceStart] },
50
+ },
51
+ client,
52
+ );
53
+ }
54
+
55
+ /** `saveEventSeries()`'s result: the saved master, plus whether its detached occurrences could be kept in
56
+ * step with a rescheduled series. */
57
+ export type SeriesSaveResult = CalendarEvent & {
58
+ /** `true` when the series' start moved but at least one detached occurrence (a separate event sharing
59
+ * the master's `icalUid`, with a `recurrenceId`) couldn't be re-pointed at its occurrence's new start -
60
+ * that occurrence may then show up twice (the detached copy plus a regenerated one). The master itself
61
+ * was saved. Absent when nothing needed re-pointing or every update succeeded. */
62
+ detachedOccurrenceSyncFailed?: boolean;
63
+ };
64
+
65
+ /**
66
+ * Applies edited `fields` to every occurrence of a recurring event's series (the master record).
67
+ *
68
+ * `fields.startDate` must be the *master's* new start (web-client's `toSeriesFields()` computes it). When
69
+ * it moves the series (or changes its `timezone`/`allDay`), every occurrence moves by the same wall-clock
70
+ * delta, so everything that identifies an occurrence by its original start moves with it: the rule's
71
+ * `exceptions` (else a deleted occurrence reappears and a different one vanishes) and the `recurrenceId` of
72
+ * each detached occurrence in the master's folder (else the server-side reminder/iTIP logic, which matches
73
+ * overrides by `recurrenceId`, no longer pairs them). Detached occurrences keep their own edited times. The
74
+ * master is saved first; re-pointing detached occurrences is best-effort, reported through
75
+ * `detachedOccurrenceSyncFailed` rather than thrown. Series edits that don't move the start make one PUT.
76
+ */
77
+ export async function saveEventSeries(
78
+ occurrence: CalendarOccurrence,
79
+ fields: Partial<CalendarEventInput>,
80
+ client?: ApiClient,
81
+ ): Promise<SeriesSaveResult> {
82
+ const update: UpdateCalendarEventInput = { uid: occurrence.uid, version: occurrence.version, ...fields };
83
+ // A shift can be needed even without an explicit startDate: changing timezone or allDay reinterprets the
84
+ // master's existing (unchanged) instant onto a different wall clock, exactly as the doc comment above
85
+ // promises - so all three fields, not just startDate, gate entry into the shift path below.
86
+ if (fields.startDate === undefined && fields.timezone === undefined && fields.allDay === undefined) {
87
+ return updateCalendarEvent(update, client);
88
+ }
89
+ const master = await getCalendarEvent(occurrence.uid, client);
90
+ const newTimezone = fields.timezone ?? master.timezone;
91
+ const newAllDay = fields.allDay ?? master.allDay;
92
+ const oldStartWall = toEventWallClock(Date.parse(master.startDate), master.timezone, master.allDay);
93
+ const newStartInstant = Date.parse(fields.startDate ?? master.startDate);
94
+ const deltaWallMs = toEventWallClock(newStartInstant, newTimezone, newAllDay) - oldStartWall;
95
+ if (deltaWallMs === 0 && newTimezone === master.timezone && newAllDay === master.allDay) {
96
+ return updateCalendarEvent(update, client);
97
+ }
98
+ // An occurrence is identified by its nominal wall-clock start (the rule's date + the master's time of day),
99
+ // not by the instant's own wall time: one that fell in a DST gap was pushed forward (02:30 -> 03:30 EDT),
100
+ // so shifting that pushed wall time would miss the occurrence the moved rule generates on that date.
101
+ const masterTimeOfDay = positiveModulo(oldStartWall, MS_PER_DAY);
102
+ const nominalWall = (instantMs: number): number => {
103
+ const wall = toEventWallClock(instantMs, master.timezone, master.allDay);
104
+ const dayStart = wall - positiveModulo(wall, MS_PER_DAY);
105
+ const candidates = [dayStart + masterTimeOfDay, dayStart - MS_PER_DAY + masterTimeOfDay];
106
+ // An instant the rule couldn't have generated (e.g. a hand-edited exception) shifts by its own wall time.
107
+ return candidates.find((candidate) => fromEventWallClock(candidate, master.timezone, master.allDay) === instantMs) ?? wall;
108
+ };
109
+ const shift = (instant: string) => new Date(fromEventWallClock(nominalWall(Date.parse(instant)) + deltaWallMs, newTimezone, newAllDay)).toISOString();
110
+
111
+ const rule = fields.recurrenceRule ?? master.recurrenceRule;
112
+ if (rule) {
113
+ update.recurrenceRule = { ...rule, exceptions: rule.exceptions.map(shift) };
114
+ }
115
+ const saved = await updateCalendarEvent(update, client);
116
+
117
+ let detachedOccurrenceSyncFailed = false;
118
+ try {
119
+ const detached = (await listCalendarEvents(master.folderUid, client)).filter(
120
+ (event) => event.icalUid === master.icalUid && event.uid !== master.uid && event.recurrenceId,
121
+ );
122
+ for (const event of detached) {
123
+ try {
124
+ // `recurrenceId` isn't part of `CalendarEventInput`, but restapi's update accepts it (the same
125
+ // way `detachOccurrence()` sends it on create).
126
+ await updateCalendarEvent(
127
+ { uid: event.uid, version: event.version, recurrenceId: shift(event.recurrenceId!) } as UpdateCalendarEventInput,
128
+ client,
129
+ );
130
+ } catch {
131
+ detachedOccurrenceSyncFailed = true;
132
+ }
133
+ }
134
+ } catch {
135
+ detachedOccurrenceSyncFailed = true;
136
+ }
137
+ return detachedOccurrenceSyncFailed ? { ...saved, detachedOccurrenceSyncFailed } : saved;
138
+ }
139
+
140
+ /**
141
+ * Applies edited `fields` to just this one occurrence of a recurring event, detaching it into its own
142
+ * standalone (non-recurring) event and excluding the original occurrence from the series.
143
+ *
144
+ * The detached event is created *first*, and only then is the original occurrence excluded from the
145
+ * master — so a failure part-way never makes the occurrence silently disappear. If adding the
146
+ * exception fails, the just-created detached event is deleted again (best-effort) and the original
147
+ * error is rethrown. The detached event keeps the master's `icalUid` and records the occurrence's
148
+ * original start as its `recurrenceId` (the RFC5545 way of identifying a modified instance), and never
149
+ * carries the series' `recurrenceRule` — even if `fields` includes one (e.g. the event modal's full
150
+ * field set).
151
+ */
152
+ export async function detachOccurrence(
153
+ occurrence: CalendarOccurrence,
154
+ fields: Partial<CalendarEventInput>,
155
+ client?: ApiClient,
156
+ ): Promise<CalendarEvent> {
157
+ const originalStart = occurrence.recurrenceId!;
158
+ // `icalUid`/`recurrenceId` aren't part of `CalendarEventInput`, but `@rapidmx/restapi`'s
159
+ // `CalendarEvent` model accepts both on create (and `createCalendarEvent` already sends `icalUid`).
160
+ const input: CalendarEventInput & { icalUid: string; recurrenceId: string } = {
161
+ mailboxUid: occurrence.mailboxUid,
162
+ folderUid: occurrence.folderUid,
163
+ title: occurrence.title,
164
+ location: occurrence.location,
165
+ startDate: occurrence.startDate,
166
+ endDate: occurrence.endDate,
167
+ allDay: occurrence.allDay,
168
+ timezone: occurrence.timezone,
169
+ organizer: occurrence.organizer,
170
+ attendees: occurrence.attendees,
171
+ status: occurrence.status,
172
+ busyStatus: occurrence.busyStatus,
173
+ reminderMinutesBeforeStart: occurrence.reminderMinutesBeforeStart,
174
+ autoReplyEnabled: occurrence.autoReplyEnabled,
175
+ autoReplyMessage: occurrence.autoReplyMessage,
176
+ // A detached occurrence keeps what the series says about its description, visibility and guests (an absent value is the default).
177
+ description: occurrence.description ?? undefined,
178
+ descriptionHtml: occurrence.descriptionHtml ?? undefined,
179
+ visibility: occurrence.visibility ?? undefined,
180
+ guestsCanModify: occurrence.guestsCanModify ?? undefined,
181
+ guestsCanInviteOthers: occurrence.guestsCanInviteOthers ?? undefined,
182
+ guestsCanSeeGuestList: occurrence.guestsCanSeeGuestList ?? undefined,
183
+ ...fields,
184
+ recurrenceRule: undefined,
185
+ icalUid: occurrence.icalUid,
186
+ recurrenceId: originalStart,
187
+ };
188
+ const created = await createCalendarEvent(input, client);
189
+ try {
190
+ await addException(occurrence, originalStart, client);
191
+ } catch (err) {
192
+ await deleteCalendarEvent(created.uid, created.version, client).catch(() => undefined);
193
+ throw err;
194
+ }
195
+ return created;
196
+ }
197
+
198
+ /** Deletes an entire recurring series (or a genuinely non-recurring event) outright. */
199
+ export function deleteEventSeries(occurrence: CalendarOccurrence, client?: ApiClient): Promise<void> {
200
+ return deleteCalendarEvent(occurrence.uid, occurrence.version, client);
201
+ }
202
+
203
+ /** Removes just this one occurrence from a recurring series, leaving the rest of the series intact. */
204
+ export function deleteEventOccurrence(occurrence: CalendarOccurrence, client?: ApiClient): Promise<CalendarEvent> {
205
+ return addException(occurrence, occurrence.recurrenceId!, client);
206
+ }
207
+
208
+ /**
209
+ * Moves `occurrence` by `deltaMs` (drag-to-move on a grid), preserving its original duration. A
210
+ * recurring occurrence is always detached into its own standalone event — matching Outlook's own
211
+ * drag behavior, which never silently reschedules an entire series from a single dragged instance;
212
+ * moving the whole series is only ever done explicitly, via the event modal's "entire series" option.
213
+ */
214
+ export function moveOccurrence(occurrence: CalendarOccurrence, deltaMs: number, client?: ApiClient): Promise<CalendarEvent> {
215
+ const fields = {
216
+ startDate: new Date(new Date(occurrence.startDate).getTime() + deltaMs).toISOString(),
217
+ endDate: new Date(new Date(occurrence.endDate).getTime() + deltaMs).toISOString(),
218
+ };
219
+ return occurrence.isRecurringOccurrence
220
+ ? detachOccurrence(occurrence, fields, client)
221
+ : updateCalendarEvent({ uid: occurrence.uid, version: occurrence.version, ...fields }, client);
222
+ }
223
+
224
+ /**
225
+ * Resizes `occurrence`'s end time (drag-to-resize on a week/day grid), keeping its start fixed and
226
+ * clamping to a 15-minute minimum duration so a resize can never invert start/end. Same
227
+ * always-detaches-a-recurring-occurrence reasoning as `moveOccurrence` above.
228
+ */
229
+ export function resizeOccurrenceEnd(occurrence: CalendarOccurrence, newEndDate: Date, client?: ApiClient): Promise<CalendarEvent> {
230
+ const start = new Date(occurrence.startDate);
231
+ const minEnd = new Date(start.getTime() + 15 * 60_000);
232
+ const endDate = (newEndDate.getTime() > minEnd.getTime() ? newEndDate : minEnd).toISOString();
233
+ return occurrence.isRecurringOccurrence
234
+ ? detachOccurrence(occurrence, { endDate }, client)
235
+ : updateCalendarEvent({ uid: occurrence.uid, version: occurrence.version, endDate }, client);
236
+ }
@@ -0,0 +1,211 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /**
6
+ * An event's description as a client handles it. `@rapidmx/restapi` stores the rich text as HTML it has sanitized to a handful of tags
7
+ * (`b`/`strong`, `i`/`em`, `u`, `br`, `p`, `ul`/`ol`/`li`, and `a` with an `http`, `https` or `mailto` `href`), and a client must still never trust it: a
8
+ * description can also arrive in an invitation from anyone. So the HTML is never put in the page as it is. `parseEventDescription()` reads it into a small
9
+ * tree of only those elements - built from what the browser's own parser found, by copying text and one attribute across, so nothing else can survive - and
10
+ * everything a client shows or sends is made from that tree: `renderable` by React elements (no `dangerouslySetInnerHTML`), written back out as canonical
11
+ * HTML (`sanitizeEventDescriptionHtml()`) or as plain text (`htmlToPlainText()`, the same text the server derives).
12
+ *
13
+ * Reading needs a DOM (`DOMParser`); without one (server-side rendering) the tree is empty, never the input.
14
+ */
15
+
16
+ /** The longest plain-text description the server takes, in characters (it allows a little more; this is the bound the dialog holds itself to). */
17
+ export const MAX_EVENT_DESCRIPTION_LENGTH = 32_000;
18
+ /** The longest description HTML the server takes, in characters. */
19
+ export const MAX_EVENT_DESCRIPTION_HTML_LENGTH = 64_000;
20
+
21
+ /** The elements a description may hold (`b`/`i` are kept as `strong`/`em`). */
22
+ export type DescriptionTag = "strong" | "em" | "u" | "br" | "p" | "ul" | "ol" | "li" | "a";
23
+
24
+ export type DescriptionNode = { text: string } | { tag: DescriptionTag; href?: string; children: DescriptionNode[] };
25
+
26
+ const TAG_OF: Record<string, DescriptionTag> = {
27
+ b: "strong",
28
+ strong: "strong",
29
+ i: "em",
30
+ em: "em",
31
+ u: "u",
32
+ br: "br",
33
+ p: "p",
34
+ ul: "ul",
35
+ ol: "ol",
36
+ li: "li",
37
+ a: "a",
38
+ };
39
+
40
+ /** Elements that run or embed something: dropped with everything in them (any other element outside the list keeps its text but not its tags). */
41
+ const DROPPED = new Set(["script", "style", "iframe", "frame", "frameset", "object", "embed", "applet", "svg", "math", "template", "noscript", "noembed", "noframes", "textarea", "select", "option", "title", "head", "xmp"]);
42
+
43
+ /**
44
+ * `href` as a link a description may carry - an absolute `http`, `https` or `mailto` URL, in its canonical form - or `undefined` for anything else
45
+ * (`javascript:`, `data:`, a relative link, a URL with a control character in it). Whitespace and control characters are removed first, since a browser
46
+ * ignores them inside a scheme (`java\tscript:`).
47
+ */
48
+ export function safeDescriptionHref(href: string | null | undefined): string | undefined {
49
+ const compact = (href ?? "").replace(/[\p{Cc}\s]+/gu, "");
50
+ let url: URL;
51
+ try {
52
+ url = new URL(compact);
53
+ } catch {
54
+ return undefined;
55
+ }
56
+ return ["http:", "https:", "mailto:"].includes(url.protocol) ? url.href : undefined;
57
+ }
58
+
59
+ function convert(node: Node): DescriptionNode[] {
60
+ if (node.nodeType === 3) {
61
+ return [{ text: node.textContent as string }];
62
+ }
63
+ if (node.nodeType !== 1) {
64
+ return [];
65
+ }
66
+ const element = node as Element;
67
+ const name = element.tagName.toLowerCase();
68
+ if (DROPPED.has(name)) {
69
+ return [];
70
+ }
71
+ const tag = TAG_OF[name];
72
+ if (tag === "br") {
73
+ return [{ tag, children: [] }];
74
+ }
75
+ const children = Array.from(element.childNodes).flatMap(convert);
76
+ if (!tag) {
77
+ return children;
78
+ }
79
+ if (tag === "a") {
80
+ const href = safeDescriptionHref(element.getAttribute("href"));
81
+ return href ? [{ tag, href, children }] : children;
82
+ }
83
+ if (tag === "p" && element.parentElement?.tagName.toLowerCase() === "li") {
84
+ // A list item written `<li><p>text</p></li>` (what a rich-text editor writes) is `<li>text</li>`: a paragraph inside an item would otherwise
85
+ // break the line after its bullet. A second paragraph in the same item stays on its own line.
86
+ return element.previousElementSibling ? [{ tag: "br", children: [] }, ...children] : children;
87
+ }
88
+ return [{ tag, children }];
89
+ }
90
+
91
+ /** The elements and text of `html` a description may hold, and nothing else. `[]` without a DOM. */
92
+ export function parseEventDescription(html: string | null | undefined): DescriptionNode[] {
93
+ if (!html || typeof DOMParser === "undefined") {
94
+ return [];
95
+ }
96
+ const document = new DOMParser().parseFromString(html, "text/html");
97
+ return Array.from(document.body.childNodes).flatMap(convert);
98
+ }
99
+
100
+ function escapeText(text: string): string {
101
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
102
+ }
103
+
104
+ function escapeAttribute(text: string): string {
105
+ return escapeText(text).replace(/"/g, "&quot;");
106
+ }
107
+
108
+ function serialize(nodes: DescriptionNode[]): string {
109
+ return nodes
110
+ .map((node) => {
111
+ if ("text" in node) {
112
+ return escapeText(node.text);
113
+ }
114
+ if (node.tag === "br") {
115
+ return "<br>";
116
+ }
117
+ const open = node.tag === "a" ? `<a href="${escapeAttribute(node.href as string)}" rel="noopener noreferrer">` : `<${node.tag}>`;
118
+ return `${open}${serialize(node.children)}</${node.tag}>`;
119
+ })
120
+ .join("");
121
+ }
122
+
123
+ /** Nothing but white space: text of spaces, a line break, or a paragraph or line break holding only such (what an editor writes for an empty line). */
124
+ function isBlank(node: DescriptionNode): boolean {
125
+ return "text" in node ? node.text.trim() === "" : (node.tag === "p" || node.tag === "br") && node.children.every(isBlank);
126
+ }
127
+
128
+ /**
129
+ * `html` reduced to what a description may hold, written as canonical HTML (a link with `rel="noopener noreferrer"`, text and attribute values escaped,
130
+ * every tag closed) and without the empty paragraphs an editor leaves before and after the text - a description with no text in it is `""`. Idempotent.
131
+ * This is what the dialog sends; the server sanitizes it again. `""` without a DOM.
132
+ */
133
+ export function sanitizeEventDescriptionHtml(html: string | null | undefined): string {
134
+ const nodes = parseEventDescription(html);
135
+ let first = 0;
136
+ let last = nodes.length;
137
+ while (first < last && isBlank(nodes[first])) {
138
+ first++;
139
+ }
140
+ while (last > first && isBlank(nodes[last - 1])) {
141
+ last--;
142
+ }
143
+ return serialize(nodes.slice(first, last));
144
+ }
145
+
146
+ interface TextState {
147
+ text: string;
148
+ lists: { ordered: boolean; count: number }[];
149
+ }
150
+
151
+ function startLine(state: TextState): void {
152
+ if (state.text !== "" && !state.text.endsWith("\n")) {
153
+ state.text += "\n";
154
+ }
155
+ }
156
+
157
+ function writeText(nodes: DescriptionNode[], state: TextState): void {
158
+ for (const node of nodes) {
159
+ if ("text" in node) {
160
+ const collapsed = node.text.replace(/(?![\t\n\r])\p{Cc}/gu, "").replace(/[ \t\r\n]+/g, " ");
161
+ state.text += state.text === "" || state.text.endsWith("\n") ? collapsed.replace(/^ /, "") : collapsed;
162
+ } else if (node.tag === "br") {
163
+ state.text += "\n";
164
+ } else if (node.tag === "p") {
165
+ startLine(state);
166
+ writeText(node.children, state);
167
+ startLine(state);
168
+ } else if (node.tag === "ul" || node.tag === "ol") {
169
+ startLine(state);
170
+ state.lists.push({ ordered: node.tag === "ol", count: 0 });
171
+ writeText(node.children, state);
172
+ state.lists.pop();
173
+ startLine(state);
174
+ } else if (node.tag === "li") {
175
+ startLine(state);
176
+ const list = state.lists[state.lists.length - 1];
177
+ if (list?.ordered) {
178
+ list.count++;
179
+ state.text += `${list.count}. `;
180
+ } else {
181
+ state.text += "- ";
182
+ }
183
+ writeText(node.children, state);
184
+ startLine(state);
185
+ } else if (node.tag === "a") {
186
+ const start = state.text.length;
187
+ writeText(node.children, state);
188
+ const shown = state.text.slice(start).trim();
189
+ const href = node.href as string;
190
+ if (shown !== href && shown !== href.replace(/^mailto:/i, "")) {
191
+ state.text += ` (${href})`;
192
+ }
193
+ } else {
194
+ writeText(node.children, state);
195
+ }
196
+ }
197
+ }
198
+
199
+ /**
200
+ * The plain text of `html`, as the server derives it: a line break for each `<br>`, paragraph and list item (`- ` before a bulleted item, `1. ` before a
201
+ * numbered one), a link's URL in parentheses after its text when the text is not the URL already, every other tag dropped, whitespace collapsed and runs of
202
+ * blank lines shortened. `""` for a description with no text in it, and without a DOM.
203
+ */
204
+ export function htmlToPlainText(html: string | null | undefined): string {
205
+ const state: TextState = { text: "", lists: [] };
206
+ writeText(parseEventDescription(html), state);
207
+ return state.text
208
+ .replace(/[ \t]+\n/g, "\n")
209
+ .replace(/\n{3,}/g, "\n\n")
210
+ .trim();
211
+ }
@@ -0,0 +1,197 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /** The client of `@rapidmx/restapi`'s `POST /mail/calendar-events/free-busy` - when other people are busy, for an event dialog's "Find a time" - and the
6
+ * small pure helpers that turn its answer into what a grid and a list of suggested times need.
7
+ *
8
+ * What the server tells about a person is only ever when they are busy, never why: no title, place, guest or id. And for some people it tells nothing at
9
+ * all, which is not the same as telling they are free: `"unknown"` is a person the server has no calendar for (an external address, or a calendar too big
10
+ * to read) and `"restricted"` is one who chose not to share their free/busy with the caller (`Mailbox.freeBusyVisibility`). Every helper here treats both
11
+ * as *not known to be free* - they are never counted as available, and never make a time "free for everyone". */
12
+
13
+ import { ApiClient, withClient } from "../util/api.js";
14
+
15
+ /** The most addresses one request may ask about. */
16
+ export const FREE_BUSY_MAX_ADDRESSES = 50;
17
+ /** The longest window one request may ask about, in days. */
18
+ export const FREE_BUSY_MAX_DAYS = 31;
19
+
20
+ /** `"available"`: the server read the person's calendar (`busy` is what it found). `"unknown"`: it has no information. `"restricted"`: they hide it from you. */
21
+ export type FreeBusyStatus = "available" | "unknown" | "restricted";
22
+
23
+ /** One stretch of busy time, as the server sends it (UTC ISO 8601, sorted, merged and clipped to the window). */
24
+ export interface FreeBusyWindow {
25
+ start: string;
26
+ end: string;
27
+ /** Only tentatively busy (a tentative event, or an invitation not yet answered firmly). */
28
+ tentative: boolean;
29
+ }
30
+
31
+ export interface FreeBusyResult {
32
+ address: string;
33
+ status: FreeBusyStatus;
34
+ /** Empty unless `status` is `"available"`. */
35
+ busy: FreeBusyWindow[];
36
+ }
37
+
38
+ export interface FreeBusyResponse {
39
+ start: string;
40
+ end: string;
41
+ /** One result per address asked, in the order asked. */
42
+ results: FreeBusyResult[];
43
+ }
44
+
45
+ /** `client`, given by every function below, is an explicit `ApiClient` from `createApiClient()` (e.g. one
46
+ * account of a multi-account app) to call instead of the default global `apiFetch()` - see `withClient()`'s
47
+ * own doc comment in `util/api.ts`. Omitted (the default), every function here behaves exactly as before. */
48
+ /**
49
+ * When the people at `addresses` (1 to `FREE_BUSY_MAX_ADDRESSES`) are busy between `start` and `end` (at most `FREE_BUSY_MAX_DAYS` days). The caller's own
50
+ * mailboxes are always answered as available with their real busy time. Rejects with an `ApiRequestError`: 400 for a bad request, 401 when signed out,
51
+ * 429 when asked too often (60 a minute).
52
+ */
53
+ export function getFreeBusy(addresses: string[], start: Date | string, end: Date | string, client?: ApiClient): Promise<FreeBusyResponse> {
54
+ return withClient(client, "/mail/calendar-events/free-busy", {
55
+ method: "POST",
56
+ body: JSON.stringify({
57
+ addresses,
58
+ start: typeof start === "string" ? start : start.toISOString(),
59
+ end: typeof end === "string" ? end : end.toISOString(),
60
+ }),
61
+ });
62
+ }
63
+
64
+ /** A stretch of busy time as milliseconds since the epoch. */
65
+ export interface BusyBlock {
66
+ startMs: number;
67
+ endMs: number;
68
+ tentative: boolean;
69
+ }
70
+
71
+ /** What is known about one person's time. */
72
+ export interface PersonAvailability {
73
+ address: string;
74
+ status: FreeBusyStatus;
75
+ busy: BusyBlock[];
76
+ }
77
+
78
+ /** What a person's calendar says about a stretch of time. `"unknown"` is any person the server did not (or would not) tell about - never free. */
79
+ export type AvailabilityState = "free" | "tentative" | "busy" | "unknown";
80
+
81
+ /** The response's results with their times as milliseconds (a person who isn't `"available"` has no blocks whatever the server sent). */
82
+ export function availabilityOf(response: FreeBusyResponse): PersonAvailability[] {
83
+ return response.results.map((result) => ({
84
+ address: result.address,
85
+ status: result.status,
86
+ busy:
87
+ result.status === "available"
88
+ ? result.busy.map((window) => ({ startMs: Date.parse(window.start), endMs: Date.parse(window.end), tentative: window.tentative }))
89
+ : [],
90
+ }));
91
+ }
92
+
93
+ /** Whether a block overlaps the stretch from `startMs` to `endMs` (touching ends do not overlap). */
94
+ function overlaps(block: BusyBlock, startMs: number, endMs: number): boolean {
95
+ return block.startMs < endMs && block.endMs > startMs;
96
+ }
97
+
98
+ /** What `person` is between `startMs` and `endMs`: `"busy"` when a firm block overlaps, else `"tentative"` when a tentative one does, else `"free"` - and
99
+ * `"unknown"`, whatever the blocks, for a person who isn't `"available"`. */
100
+ export function availabilityDuring(person: PersonAvailability, startMs: number, endMs: number): AvailabilityState {
101
+ if (person.status !== "available") {
102
+ return "unknown";
103
+ }
104
+ const overlapping = person.busy.filter((block) => overlaps(block, startMs, endMs));
105
+ if (overlapping.some((block) => !block.tentative)) {
106
+ return "busy";
107
+ }
108
+ return overlapping.length > 0 ? "tentative" : "free";
109
+ }
110
+
111
+ /** How a stretch of time stands for a group. */
112
+ export interface AvailabilitySummary {
113
+ /** People known to be busy for some of it (firm or tentative). */
114
+ conflicts: number;
115
+ /** Of those, the ones only tentatively busy. */
116
+ tentative: number;
117
+ /** People nothing is known about (external, too big, or hiding their calendar): not counted as free, not counted as conflicts. */
118
+ unknown: number;
119
+ /** People known to be free for all of it. */
120
+ free: number;
121
+ }
122
+
123
+ export function summarizeAvailability(people: PersonAvailability[], startMs: number, endMs: number): AvailabilitySummary {
124
+ const summary: AvailabilitySummary = { conflicts: 0, tentative: 0, unknown: 0, free: 0 };
125
+ for (const person of people) {
126
+ const state = availabilityDuring(person, startMs, endMs);
127
+ if (state === "unknown") {
128
+ summary.unknown++;
129
+ } else if (state === "free") {
130
+ summary.free++;
131
+ } else {
132
+ summary.conflicts++;
133
+ if (state === "tentative") {
134
+ summary.tentative++;
135
+ }
136
+ }
137
+ }
138
+ return summary;
139
+ }
140
+
141
+ /**
142
+ * `people` without the blocks that are exactly the stretch from `startMs` to `endMs`, for the addresses in `addresses` (lower case): what an event that is
143
+ * being rescheduled takes up on the calendars of the people already invited to it - which would otherwise show as everyone's conflict with itself. Only an
144
+ * exact match goes, so a block that the server merged with a neighbouring or overlapping event stays (a conflict shown, never one hidden).
145
+ */
146
+ export function withoutOwnBlock(people: PersonAvailability[], addresses: Set<string>, startMs: number, endMs: number): PersonAvailability[] {
147
+ return people.map((person) =>
148
+ addresses.has(person.address.toLowerCase())
149
+ ? { ...person, busy: person.busy.filter((block) => block.startMs !== startMs || block.endMs !== endMs) }
150
+ : person,
151
+ );
152
+ }
153
+
154
+ export interface SuggestTimesOptions {
155
+ /** The stretches a meeting may fall in (e.g. each day's 8:00 to 18:00), in any order. A slot lies wholly inside one. */
156
+ windows: { startMs: number; endMs: number }[];
157
+ /** How long the meeting is. */
158
+ durationMs: number;
159
+ /** How far apart slots start, from a window's start (default 30 minutes). */
160
+ stepMs?: number;
161
+ /** How many to find (default 5). */
162
+ limit?: number;
163
+ /** No slot starts before this (e.g. now). */
164
+ notBeforeMs?: number;
165
+ }
166
+
167
+ const DEFAULT_STEP_MS = 30 * 60_000;
168
+ const DEFAULT_SUGGESTIONS = 5;
169
+
170
+ /**
171
+ * The start times (ms) of the first `limit` slots of `durationMs` inside `windows` where everyone known is free - no firm or tentative block overlaps it.
172
+ * People who are `"unknown"` or `"restricted"` cannot be checked and so do not constrain the answer (a caller says so next to the suggestions); with nobody
173
+ * known at all there is nothing to base a suggestion on and the answer is empty.
174
+ */
175
+ export function suggestTimes(people: PersonAvailability[], options: SuggestTimesOptions): number[] {
176
+ const known = people.filter((person) => person.status === "available");
177
+ if (known.length === 0 || options.durationMs <= 0) {
178
+ return [];
179
+ }
180
+ const step = options.stepMs ?? DEFAULT_STEP_MS;
181
+ const limit = options.limit ?? DEFAULT_SUGGESTIONS;
182
+ const found: number[] = [];
183
+ for (const window of [...options.windows].sort((a, b) => a.startMs - b.startMs)) {
184
+ for (let start = window.startMs; start + options.durationMs <= window.endMs; start += step) {
185
+ if (start < (options.notBeforeMs ?? Number.NEGATIVE_INFINITY)) {
186
+ continue;
187
+ }
188
+ if (known.every((person) => availabilityDuring(person, start, start + options.durationMs) === "free")) {
189
+ found.push(start);
190
+ if (found.length >= limit) {
191
+ return found;
192
+ }
193
+ }
194
+ }
195
+ }
196
+ return found;
197
+ }