@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.
- package/README.md +48 -14
- package/apps/admin/_layout.tsx +2 -2
- package/apps/admin/audit-log/index.tsx +4 -4
- package/apps/admin/branding/index.tsx +3 -3
- package/apps/admin/data-requests/index.tsx +8 -8
- package/apps/admin/distribution-lists/[uid].tsx +3 -3
- package/apps/admin/distribution-lists/index.tsx +4 -4
- package/apps/admin/distribution-lists/new/index.tsx +5 -5
- package/apps/admin/domains/[uid].tsx +6 -6
- package/apps/admin/domains/index.tsx +4 -4
- package/apps/admin/domains/new/index.tsx +5 -5
- package/apps/admin/encryption-policy/index.tsx +1 -1
- package/apps/admin/escrow-scopes/[uid].tsx +7 -7
- package/apps/admin/escrow-scopes/index.tsx +4 -4
- package/apps/admin/escrow-scopes/new/index.tsx +5 -5
- package/apps/admin/index.tsx +6 -6
- package/apps/admin/ingest-queue/index.tsx +4 -4
- package/apps/admin/mailbox-policy/index.tsx +1 -1
- package/apps/admin/mailboxes/[uid].tsx +5 -5
- package/apps/admin/quarantine/index.tsx +5 -5
- package/apps/admin/retention-policy/index.tsx +3 -3
- package/apps/admin/signing-certificates/index.tsx +5 -5
- package/apps/admin/transport-rules/[uid].tsx +5 -5
- package/apps/admin/transport-rules/_transportRuleConfig.tsx +3 -3
- package/apps/admin/transport-rules/index.tsx +4 -4
- package/apps/admin/transport-rules/new/index.tsx +5 -5
- package/apps/escrow/_layout.tsx +1 -1
- package/apps/escrow/audit-log/index.tsx +4 -4
- package/apps/escrow/index.tsx +4 -4
- package/apps/escrow/matters/[uid].tsx +10 -10
- package/apps/escrow/matters/new/index.tsx +6 -6
- package/apps/shared/appearance/AppearanceForm.tsx +3 -3
- package/apps/shared/appearance/AppearanceHead.tsx +1 -1
- package/apps/shared/appearance/AppearanceProvider.tsx +3 -3
- package/apps/shared/appearance/appearanceCache.ts +1 -1
- package/apps/shared/appearance/appearanceContext.ts +1 -1
- package/apps/shared/appearance/theme.ts +1 -1
- package/apps/shared/auth/adminAccess.ts +1 -1
- package/apps/shared/calendar/bookingPlugin.ts +1 -1
- package/apps/shared/calendar/calendarLiveUpdates.ts +1 -1
- package/apps/shared/calendar/calendarReminders.ts +1 -1
- package/apps/shared/calendar/useCalendarReminders.ts +1 -1
- package/apps/shared/components/admin/diagnostics/DiagnosticsManager.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/InstalledPlugins.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/LogsPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/PackagesTable.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/RuntimePanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/SystemPanel.tsx +2 -2
- package/apps/shared/components/admin/diagnostics/VersionsPanel.tsx +1 -1
- package/apps/shared/components/admin/diagnostics/diagnosticsApi.ts +1 -1
- package/apps/shared/components/admin/diagnostics/format.ts +1 -1
- package/apps/shared/components/admin/diagnostics/logClient.ts +1 -1
- package/apps/shared/components/admin/distributionLists/MemberListCard.tsx +4 -4
- package/apps/shared/components/admin/elevation.ts +1 -1
- package/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.tsx +3 -3
- package/apps/shared/components/admin/layout/AdminShell.tsx +8 -8
- package/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.tsx +6 -6
- package/apps/shared/components/admin/mailboxes/EscrowScopeCard.tsx +10 -8
- package/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.tsx +4 -4
- package/apps/shared/components/admin/mailboxes/MailboxTable.tsx +1 -1
- package/apps/shared/components/admin/mailboxes/ResourceSettingsCard.tsx +19 -14
- package/apps/shared/components/admin/mailboxes/ShareAccessCard.tsx +5 -5
- package/apps/shared/components/admin/settings/BrandingForm.tsx +5 -5
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +5 -5
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +4 -4
- package/apps/shared/components/admin/settings/LoadedSettingsForm.tsx +2 -2
- package/apps/shared/components/admin/settings/MailboxCreateForm.tsx +32 -27
- package/apps/shared/components/admin/settings/MailboxPolicyForm.tsx +4 -4
- package/apps/shared/components/admin/settings/PluginsManager.tsx +5 -5
- package/apps/shared/components/admin/settings/RetentionPolicyForm.tsx +5 -5
- package/apps/shared/components/admin/setup/EscrowSetupStep.tsx +7 -7
- package/apps/shared/components/admin/setup/SetupWizard.tsx +27 -23
- package/apps/shared/components/admin/signOut.ts +1 -1
- package/apps/shared/components/admin/usePagedList.tsx +2 -2
- package/apps/shared/components/calendar/AppointmentQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/CalendarListSidebar.tsx +3 -3
- package/apps/shared/components/calendar/DescriptionEditor.tsx +1 -1
- package/apps/shared/components/calendar/EventDescriptionView.tsx +1 -1
- package/apps/shared/components/calendar/EventDetails.tsx +16 -14
- package/apps/shared/components/calendar/EventEditor.tsx +54 -39
- package/apps/shared/components/calendar/EventExpandedForm.tsx +3 -3
- package/apps/shared/components/calendar/EventFormParts.tsx +2 -2
- package/apps/shared/components/calendar/EventModal.tsx +5 -5
- package/apps/shared/components/calendar/EventQuickForm.tsx +3 -3
- package/apps/shared/components/calendar/EventShell.tsx +1 -1
- package/apps/shared/components/calendar/FindATime.tsx +6 -4
- package/apps/shared/components/calendar/GuestInput.tsx +2 -2
- package/apps/shared/components/calendar/MonthView.tsx +2 -2
- package/apps/shared/components/calendar/QuickCreateFaces.tsx +1 -1
- package/apps/shared/components/calendar/QuickFaceFrame.tsx +1 -1
- package/apps/shared/components/calendar/RecurrenceEditor.tsx +2 -2
- package/apps/shared/components/calendar/RequestChangeForm.tsx +8 -6
- package/apps/shared/components/calendar/ResourcePicker.tsx +7 -5
- package/apps/shared/components/calendar/SplitDayView.tsx +1 -1
- package/apps/shared/components/calendar/TaskQuickForm.tsx +23 -18
- package/apps/shared/components/calendar/TimeGridView.tsx +2 -2
- package/apps/shared/components/calendar/allDay.ts +2 -2
- package/apps/shared/components/calendar/eventDialogFields.ts +3 -3
- package/apps/shared/components/calendar/eventForm.ts +3 -3
- package/apps/shared/components/calendar/eventFormat.ts +3 -3
- package/apps/shared/components/calendar/findTimeGrid.ts +2 -2
- package/apps/shared/components/calendar/layout/CalendarShell.tsx +11 -9
- package/apps/shared/components/contacts/ContactDetailPane.tsx +4 -4
- package/apps/shared/components/contacts/ContactForm.tsx +27 -17
- package/apps/shared/components/contacts/ContactsSidebar.tsx +7 -5
- package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -5
- package/apps/shared/components/contacts/contactKeys.ts +3 -3
- package/apps/shared/components/contacts/layout/ContactsShell.tsx +9 -7
- package/apps/shared/components/escrow/layout/EscrowShell.tsx +6 -6
- package/apps/shared/components/forms/StringListField.tsx +1 -1
- package/apps/shared/components/layout/AppShell.tsx +12 -10
- package/apps/shared/components/layout/BrandingChrome.tsx +1 -1
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +9 -9
- package/apps/shared/components/layout/MailboxProvisioning.tsx +8 -6
- package/apps/shared/components/layout/RecoveryCodeUnlock.tsx +9 -9
- package/apps/shared/components/layout/ThemeSwitch.tsx +1 -1
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +6 -6
- package/apps/shared/components/layout/UserMenu.tsx +6 -4
- package/apps/shared/components/mail/ConversationList.tsx +7 -5
- package/apps/shared/components/mail/ConversationThreadPane.tsx +26 -15
- package/apps/shared/components/mail/InviteCard.tsx +11 -9
- package/apps/shared/components/mail/LazyReadingPane.tsx +2 -2
- package/apps/shared/components/mail/MailAddress.tsx +1 -1
- package/apps/shared/components/mail/MailListToolbar.tsx +2 -2
- package/apps/shared/components/mail/MailSelectionBar.tsx +2 -2
- package/apps/shared/components/mail/MenuButton.tsx +1 -1
- package/apps/shared/components/mail/MessageDetailPane.tsx +32 -30
- package/apps/shared/components/mail/MoveToFolderDialog.tsx +8 -6
- package/apps/shared/components/mail/OutboxRowStatus.tsx +1 -1
- package/apps/shared/components/mail/PermanentDeleteDialog.tsx +2 -2
- package/apps/shared/components/mail/compose/ComposeContext.tsx +14 -3
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +34 -30
- package/apps/shared/components/mail/compose/ComposeWindowPlaceholder.tsx +4 -4
- package/apps/shared/components/mail/compose/EmojiPicker.tsx +2 -2
- package/apps/shared/components/mail/compose/GifPicker.tsx +3 -3
- package/apps/shared/components/mail/compose/RecipientInput.tsx +1 -1
- package/apps/shared/components/mail/compose/ScheduleSendPicker.tsx +1 -1
- package/apps/shared/components/mail/compose/encryptionRequirement.ts +2 -2
- package/apps/shared/components/mail/compose/quotedBody.ts +5 -5
- package/apps/shared/components/mail/compose/recipients.ts +1 -1
- package/apps/shared/components/mail/invite/InviteRowChip.tsx +2 -2
- package/apps/shared/components/mail/invite/InviteRsvpPopover.tsx +9 -7
- package/apps/shared/components/mail/invite/InviteTimeline.tsx +1 -1
- package/apps/shared/components/mail/invite/ProposeTimeForm.tsx +2 -2
- package/apps/shared/components/mail/invite/inviteFormat.ts +2 -2
- package/apps/shared/components/mail/invite/inviteStore.ts +14 -6
- package/apps/shared/components/mail/labelMenu.tsx +9 -7
- package/apps/shared/components/mail/layout/MailShell.tsx +4 -4
- package/apps/shared/components/mail/listPreferences.ts +2 -2
- package/apps/shared/components/mail/pinnedSigners.ts +45 -19
- package/apps/shared/components/mail/reading/EncryptedBody.tsx +2 -2
- package/apps/shared/components/mail/reading/MessageBody.tsx +6 -6
- package/apps/shared/components/mail/reading/MessageCard.tsx +3 -3
- package/apps/shared/components/mail/reading/MessageSourceDialog.tsx +3 -3
- package/apps/shared/components/mail/reading/PendingMessageCard.tsx +1 -1
- package/apps/shared/components/mail/reading/bodyContent.ts +3 -3
- package/apps/shared/components/mail/reading/bodyHtml.ts +1 -1
- package/apps/shared/components/mail/reading/messageExport.ts +1 -1
- package/apps/shared/components/mail/reading/printMessage.ts +2 -2
- package/apps/shared/components/mail/reading/useMessageActions.ts +28 -21
- package/apps/shared/components/mail/unreadStyle.tsx +1 -1
- package/apps/shared/components/mail/verificationSeals.ts +16 -12
- package/apps/shared/components/mail/writableMailboxes.ts +14 -7
- package/apps/shared/components/rules/RuleBuilder.tsx +1 -1
- package/apps/shared/components/settings/SigningCertificateCard.tsx +4 -4
- package/apps/shared/components/settings/layout/SettingsShell.tsx +8 -6
- package/apps/shared/components/sharing/PrincipalListField.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalPicker.tsx +1 -1
- package/apps/shared/components/sharing/PrincipalResolver.tsx +4 -4
- package/apps/shared/components/tasks/TasksSidebar.tsx +7 -5
- package/apps/shared/components/tasks/layout/TasksShell.tsx +9 -7
- package/apps/shared/keyboard/ShortcutsDialog.tsx +1 -1
- package/apps/shared/mail/findWellKnownFolderUid.ts +6 -4
- package/apps/shared/mail/folderCounts.ts +8 -4
- package/apps/shared/mail/folderOfType.ts +5 -3
- package/apps/shared/mail/folderTree.ts +1 -1
- package/apps/shared/mail/listSnapshots.ts +2 -2
- package/apps/shared/mail/messageReadState.ts +6 -5
- package/apps/shared/mail/newMailNotifications.ts +2 -2
- package/apps/shared/mail/outbox/composeBridge.ts +2 -2
- package/apps/shared/mail/outbox/outboxState.ts +1 -1
- package/apps/shared/mail/outbox/outgoingReplies.ts +2 -2
- package/apps/shared/mail/outbox/sendDecision.ts +2 -2
- package/apps/shared/mail/outbox/sendJob.ts +40 -35
- package/apps/shared/mail/outbox/sendOutcomes.ts +20 -15
- package/apps/shared/mail/outbox/useOutboxStatus.ts +5 -3
- package/apps/shared/mail/permanentDelete.ts +8 -7
- package/apps/shared/mail/primaryMailbox.ts +1 -1
- package/apps/shared/mail/reportNotices.ts +1 -1
- package/apps/shared/mail/senderBlocking.ts +36 -20
- package/apps/shared/mail/useMailConnection.ts +12 -10
- package/apps/shared/mail/useMailLiveUpdates.ts +3 -3
- package/apps/shared/mail/useMailboxUpdateAccess.ts +35 -11
- package/apps/shared/mail/useMarkMessageRead.ts +14 -7
- package/apps/shared/mail/useNewMailNotifications.ts +1 -1
- package/apps/shared/mail/usePermanentDelete.tsx +5 -3
- package/apps/shared/navigation/pageTitle.ts +1 -1
- package/apps/shared/notifications/NotificationCenter.tsx +1 -1
- package/apps/shared/notifications/NotificationHistoryDialog.tsx +2 -2
- package/apps/shared/notifications/apiErrors.ts +2 -2
- package/apps/shared/notifications/pushStatus.ts +1 -1
- package/apps/shared/notifications/systemErrors.ts +1 -1
- package/apps/shared/search/LocalIndexLifecycle.tsx +18 -12
- package/apps/shared/search/crossMailboxSearch.ts +3 -3
- package/apps/shared/search/localIndexBuilder.ts +24 -12
- package/apps/shared/search/localIndexKey.ts +1 -1
- package/apps/shared/search/localIndexRpcClient.ts +150 -30
- package/apps/shared/search/localIndexTransport.ts +84 -0
- package/apps/shared/search/localIndexWorker.ts +1 -1
- package/apps/shared/search/searchTier2.ts +3 -3
- package/apps/shared/signing/enrollmentTracker.ts +3 -3
- package/apps/shared/signing/enrollmentView.ts +1 -1
- package/apps/shared/signing/signingInfo.ts +1 -1
- package/apps/shared/signing/useSigningEnrollmentWatcher.ts +6 -4
- package/apps/shared/styles/app.css +14 -23
- package/apps/www/_layout.tsx +2 -2
- package/apps/www/calendar/index.tsx +20 -18
- package/apps/www/contacts/[uid].tsx +8 -6
- package/apps/www/contacts/index.tsx +19 -17
- package/apps/www/index.tsx +57 -48
- package/apps/www/messages/[uid].tsx +11 -9
- package/apps/www/settings/auto-reply/index.tsx +6 -4
- package/apps/www/settings/blocked-senders/index.tsx +9 -7
- package/apps/www/settings/encryption/index.tsx +38 -32
- package/apps/www/settings/filters/[uid].tsx +19 -14
- package/apps/www/settings/filters/_mailFilterRuleConfig.tsx +2 -2
- package/apps/www/settings/filters/index.tsx +4 -4
- package/apps/www/settings/filters/new/index.tsx +10 -8
- package/apps/www/settings/labels/index.tsx +12 -10
- package/apps/www/settings/privacy/index.tsx +18 -14
- package/apps/www/settings/profile/index.tsx +16 -11
- package/apps/www/settings/read-receipts/index.tsx +17 -12
- package/apps/www/settings/sharing/index.tsx +6 -6
- package/apps/www/settings/signatures/[uid].tsx +5 -5
- package/apps/www/settings/signatures/index.tsx +4 -4
- package/apps/www/settings/signatures/new/index.tsx +4 -4
- package/apps/www/settings/signatures/signatureDefaults.ts +1 -1
- package/apps/www/tasks/index.tsx +28 -23
- package/dist/apps/admin/_layout.d.ts +1 -1
- package/dist/apps/admin/_layout.js +1 -1
- package/dist/apps/admin/audit-log/index.d.ts +1 -1
- package/dist/apps/admin/audit-log/index.js +4 -4
- package/dist/apps/admin/branding/index.js +3 -3
- package/dist/apps/admin/data-requests/index.js +8 -8
- package/dist/apps/admin/distribution-lists/[uid].js +3 -3
- package/dist/apps/admin/distribution-lists/index.js +4 -4
- package/dist/apps/admin/distribution-lists/new/index.js +5 -5
- package/dist/apps/admin/domains/[uid].js +6 -6
- package/dist/apps/admin/domains/index.js +4 -4
- package/dist/apps/admin/domains/new/index.js +5 -5
- package/dist/apps/admin/encryption-policy/index.js +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].d.ts +1 -1
- package/dist/apps/admin/escrow-scopes/[uid].js +7 -7
- package/dist/apps/admin/escrow-scopes/index.js +4 -4
- package/dist/apps/admin/escrow-scopes/new/index.js +5 -5
- package/dist/apps/admin/index.js +6 -6
- package/dist/apps/admin/ingest-queue/index.js +4 -4
- package/dist/apps/admin/mailbox-policy/index.js +1 -1
- package/dist/apps/admin/mailboxes/[uid].js +5 -5
- package/dist/apps/admin/quarantine/index.js +5 -5
- package/dist/apps/admin/retention-policy/index.js +3 -3
- package/dist/apps/admin/signing-certificates/index.js +5 -5
- package/dist/apps/admin/transport-rules/[uid].js +5 -5
- package/dist/apps/admin/transport-rules/_transportRuleConfig.d.ts +1 -1
- package/dist/apps/admin/transport-rules/_transportRuleConfig.js +2 -2
- package/dist/apps/admin/transport-rules/index.js +4 -4
- package/dist/apps/admin/transport-rules/new/index.js +5 -5
- package/dist/apps/escrow/_layout.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.d.ts +1 -1
- package/dist/apps/escrow/audit-log/index.js +4 -4
- package/dist/apps/escrow/index.js +4 -4
- package/dist/apps/escrow/matters/[uid].js +10 -10
- package/dist/apps/escrow/matters/new/index.js +6 -6
- package/dist/apps/shared/appearance/AppearanceForm.js +3 -3
- package/dist/apps/shared/appearance/AppearanceHead.js +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.d.ts +1 -1
- package/dist/apps/shared/appearance/AppearanceProvider.js +3 -3
- package/dist/apps/shared/appearance/appearanceCache.d.ts +1 -1
- package/dist/apps/shared/appearance/appearanceCache.js +1 -1
- package/dist/apps/shared/appearance/appearanceContext.d.ts +1 -1
- package/dist/apps/shared/appearance/theme.d.ts +1 -1
- package/dist/apps/shared/auth/adminAccess.js +1 -1
- package/dist/apps/shared/calendar/bookingPlugin.js +1 -1
- package/dist/apps/shared/calendar/calendarLiveUpdates.d.ts +1 -1
- package/dist/apps/shared/calendar/calendarReminders.d.ts +1 -1
- package/dist/apps/shared/calendar/useCalendarReminders.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/DiagnosticsManager.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.d.ts +1 -1
- package/dist/apps/shared/components/admin/diagnostics/InstalledPlugins.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/LogsPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/PackagesTable.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/RuntimePanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/SystemPanel.js +2 -2
- package/dist/apps/shared/components/admin/diagnostics/VersionsPanel.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/diagnosticsApi.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/format.js +1 -1
- package/dist/apps/shared/components/admin/diagnostics/logClient.js +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/distributionLists/MemberListCard.js +4 -4
- package/dist/apps/shared/components/admin/elevation.js +1 -1
- package/dist/apps/shared/components/admin/escrowScopes/EscrowScopeKeyAndHoldersFields.js +3 -3
- package/dist/apps/shared/components/admin/layout/AdminShell.d.ts +1 -1
- package/dist/apps/shared/components/admin/layout/AdminShell.js +7 -7
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EraseLeftoverDataDialog.js +6 -6
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/EscrowScopeCard.js +10 -8
- package/dist/apps/shared/components/admin/mailboxes/LeftoverMailboxesSection.js +4 -4
- package/dist/apps/shared/components/admin/mailboxes/MailboxTable.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.d.ts +1 -1
- package/dist/apps/shared/components/admin/mailboxes/ResourceSettingsCard.js +7 -5
- package/dist/apps/shared/components/admin/mailboxes/ShareAccessCard.js +5 -5
- package/dist/apps/shared/components/admin/settings/BrandingForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/BrandingForm.js +5 -5
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +5 -5
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/LoadedSettingsForm.js +2 -2
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxCreateForm.js +14 -12
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/MailboxPolicyForm.js +4 -4
- package/dist/apps/shared/components/admin/settings/PluginsManager.js +5 -5
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.d.ts +1 -1
- package/dist/apps/shared/components/admin/settings/RetentionPolicyForm.js +5 -5
- package/dist/apps/shared/components/admin/setup/EscrowSetupStep.js +7 -7
- package/dist/apps/shared/components/admin/setup/SetupWizard.js +26 -22
- package/dist/apps/shared/components/admin/signOut.js +1 -1
- package/dist/apps/shared/components/admin/usePagedList.js +2 -2
- package/dist/apps/shared/components/calendar/AppointmentQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/CalendarListSidebar.d.ts +1 -1
- package/dist/apps/shared/components/calendar/CalendarListSidebar.js +2 -2
- package/dist/apps/shared/components/calendar/DescriptionEditor.js +1 -1
- package/dist/apps/shared/components/calendar/EventDescriptionView.js +1 -1
- package/dist/apps/shared/components/calendar/EventDetails.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventDetails.js +16 -14
- package/dist/apps/shared/components/calendar/EventEditor.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventEditor.js +22 -20
- package/dist/apps/shared/components/calendar/EventExpandedForm.js +2 -2
- package/dist/apps/shared/components/calendar/EventFormParts.d.ts +1 -1
- package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
- package/dist/apps/shared/components/calendar/EventModal.d.ts +2 -2
- package/dist/apps/shared/components/calendar/EventModal.js +3 -3
- package/dist/apps/shared/components/calendar/EventQuickForm.js +3 -3
- package/dist/apps/shared/components/calendar/EventShell.js +1 -1
- package/dist/apps/shared/components/calendar/FindATime.js +6 -4
- package/dist/apps/shared/components/calendar/GuestInput.d.ts +2 -2
- package/dist/apps/shared/components/calendar/MonthView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/MonthView.js +1 -1
- package/dist/apps/shared/components/calendar/QuickCreateFaces.js +1 -1
- package/dist/apps/shared/components/calendar/QuickFaceFrame.js +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.d.ts +1 -1
- package/dist/apps/shared/components/calendar/RecurrenceEditor.js +1 -1
- package/dist/apps/shared/components/calendar/RequestChangeForm.js +8 -6
- package/dist/apps/shared/components/calendar/ResourcePicker.d.ts +1 -1
- package/dist/apps/shared/components/calendar/ResourcePicker.js +7 -5
- package/dist/apps/shared/components/calendar/SplitDayView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TaskQuickForm.js +9 -7
- package/dist/apps/shared/components/calendar/TimeGridView.d.ts +1 -1
- package/dist/apps/shared/components/calendar/TimeGridView.js +1 -1
- package/dist/apps/shared/components/calendar/allDay.d.ts +2 -2
- package/dist/apps/shared/components/calendar/allDay.js +1 -1
- package/dist/apps/shared/components/calendar/eventDialogFields.d.ts +2 -2
- package/dist/apps/shared/components/calendar/eventDialogFields.js +2 -2
- package/dist/apps/shared/components/calendar/eventForm.d.ts +3 -3
- package/dist/apps/shared/components/calendar/eventFormat.d.ts +1 -1
- package/dist/apps/shared/components/calendar/eventFormat.js +2 -2
- package/dist/apps/shared/components/calendar/findTimeGrid.d.ts +1 -1
- package/dist/apps/shared/components/calendar/findTimeGrid.js +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.d.ts +1 -1
- package/dist/apps/shared/components/calendar/layout/CalendarShell.js +11 -9
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +2 -2
- package/dist/apps/shared/components/contacts/ContactForm.js +12 -8
- package/dist/apps/shared/components/contacts/ContactsSidebar.d.ts +1 -1
- package/dist/apps/shared/components/contacts/ContactsSidebar.js +7 -5
- package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +1 -1
- package/dist/apps/shared/components/contacts/KeyChangeReview.js +5 -5
- package/dist/apps/shared/components/contacts/contactKeys.d.ts +2 -2
- package/dist/apps/shared/components/contacts/contactKeys.js +2 -2
- package/dist/apps/shared/components/contacts/layout/ContactsShell.d.ts +1 -1
- package/dist/apps/shared/components/contacts/layout/ContactsShell.js +9 -7
- package/dist/apps/shared/components/escrow/layout/EscrowShell.js +6 -6
- package/dist/apps/shared/components/forms/StringListField.js +1 -1
- package/dist/apps/shared/components/layout/AppShell.d.ts +2 -2
- package/dist/apps/shared/components/layout/AppShell.js +11 -9
- package/dist/apps/shared/components/layout/BrandingChrome.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.d.ts +1 -1
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +9 -9
- package/dist/apps/shared/components/layout/MailboxProvisioning.js +8 -6
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.d.ts +1 -1
- package/dist/apps/shared/components/layout/RecoveryCodeUnlock.js +9 -9
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +2 -2
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +5 -5
- package/dist/apps/shared/components/layout/UserMenu.js +6 -4
- package/dist/apps/shared/components/mail/ConversationList.d.ts +2 -2
- package/dist/apps/shared/components/mail/ConversationList.js +6 -4
- package/dist/apps/shared/components/mail/ConversationThreadPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/ConversationThreadPane.js +13 -11
- package/dist/apps/shared/components/mail/InviteCard.js +10 -8
- package/dist/apps/shared/components/mail/LazyReadingPane.js +2 -2
- package/dist/apps/shared/components/mail/MailAddress.d.ts +1 -1
- package/dist/apps/shared/components/mail/MailAddress.js +1 -1
- package/dist/apps/shared/components/mail/MailListToolbar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MailSelectionBar.d.ts +2 -2
- package/dist/apps/shared/components/mail/MenuButton.js +1 -1
- package/dist/apps/shared/components/mail/MessageDetailPane.d.ts +3 -3
- package/dist/apps/shared/components/mail/MessageDetailPane.js +30 -28
- package/dist/apps/shared/components/mail/MoveToFolderDialog.d.ts +1 -1
- package/dist/apps/shared/components/mail/MoveToFolderDialog.js +8 -6
- package/dist/apps/shared/components/mail/OutboxRowStatus.d.ts +1 -1
- package/dist/apps/shared/components/mail/PermanentDeleteDialog.js +2 -2
- package/dist/apps/shared/components/mail/compose/ComposeContext.d.ts +10 -1
- package/dist/apps/shared/components/mail/compose/ComposeContext.js +4 -2
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +34 -30
- package/dist/apps/shared/components/mail/compose/ComposeWindowPlaceholder.js +4 -4
- package/dist/apps/shared/components/mail/compose/EmojiPicker.js +2 -2
- package/dist/apps/shared/components/mail/compose/GifPicker.js +3 -3
- package/dist/apps/shared/components/mail/compose/RecipientInput.d.ts +1 -1
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +1 -1
- package/dist/apps/shared/components/mail/compose/ScheduleSendPicker.js +1 -1
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.d.ts +2 -2
- package/dist/apps/shared/components/mail/compose/encryptionRequirement.js +1 -1
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +3 -3
- package/dist/apps/shared/components/mail/compose/quotedBody.js +3 -3
- package/dist/apps/shared/components/mail/compose/recipients.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRowChip.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/InviteRsvpPopover.js +9 -7
- package/dist/apps/shared/components/mail/invite/InviteTimeline.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.d.ts +1 -1
- package/dist/apps/shared/components/mail/invite/ProposeTimeForm.js +1 -1
- package/dist/apps/shared/components/mail/invite/inviteFormat.d.ts +2 -2
- package/dist/apps/shared/components/mail/invite/inviteStore.d.ts +9 -3
- package/dist/apps/shared/components/mail/invite/inviteStore.js +13 -6
- package/dist/apps/shared/components/mail/labelMenu.d.ts +1 -1
- package/dist/apps/shared/components/mail/labelMenu.js +9 -7
- package/dist/apps/shared/components/mail/layout/MailShell.d.ts +1 -1
- package/dist/apps/shared/components/mail/layout/MailShell.js +3 -3
- package/dist/apps/shared/components/mail/listPreferences.d.ts +2 -2
- package/dist/apps/shared/components/mail/listPreferences.js +1 -1
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +12 -5
- package/dist/apps/shared/components/mail/pinnedSigners.js +37 -18
- package/dist/apps/shared/components/mail/reading/EncryptedBody.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/MessageBody.js +4 -4
- package/dist/apps/shared/components/mail/reading/MessageCard.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/MessageCard.js +2 -2
- package/dist/apps/shared/components/mail/reading/MessageSourceDialog.js +3 -3
- package/dist/apps/shared/components/mail/reading/PendingMessageCard.js +1 -1
- package/dist/apps/shared/components/mail/reading/bodyContent.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/bodyContent.js +2 -2
- package/dist/apps/shared/components/mail/reading/bodyHtml.js +1 -1
- package/dist/apps/shared/components/mail/reading/messageExport.js +1 -1
- package/dist/apps/shared/components/mail/reading/printMessage.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/useMessageActions.d.ts +1 -1
- package/dist/apps/shared/components/mail/reading/useMessageActions.js +18 -16
- package/dist/apps/shared/components/mail/unreadStyle.d.ts +1 -1
- package/dist/apps/shared/components/mail/verificationSeals.d.ts +27 -6
- package/dist/apps/shared/components/mail/verificationSeals.js +15 -11
- package/dist/apps/shared/components/mail/writableMailboxes.d.ts +8 -3
- package/dist/apps/shared/components/mail/writableMailboxes.js +12 -6
- package/dist/apps/shared/components/rules/RuleBuilder.js +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.d.ts +1 -1
- package/dist/apps/shared/components/settings/SigningCertificateCard.js +3 -3
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +1 -1
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +8 -6
- package/dist/apps/shared/components/sharing/PrincipalListField.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalPicker.js +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.d.ts +1 -1
- package/dist/apps/shared/components/sharing/PrincipalResolver.js +3 -3
- package/dist/apps/shared/components/tasks/TasksSidebar.d.ts +1 -1
- package/dist/apps/shared/components/tasks/TasksSidebar.js +7 -5
- package/dist/apps/shared/components/tasks/layout/TasksShell.d.ts +1 -1
- package/dist/apps/shared/components/tasks/layout/TasksShell.js +9 -7
- package/dist/apps/shared/keyboard/ShortcutsDialog.js +1 -1
- package/dist/apps/shared/mail/findWellKnownFolderUid.d.ts +5 -3
- package/dist/apps/shared/mail/findWellKnownFolderUid.js +5 -4
- package/dist/apps/shared/mail/folderCounts.d.ts +1 -1
- package/dist/apps/shared/mail/folderCounts.js +6 -4
- package/dist/apps/shared/mail/folderOfType.d.ts +3 -2
- package/dist/apps/shared/mail/folderOfType.js +4 -4
- package/dist/apps/shared/mail/folderTree.d.ts +1 -1
- package/dist/apps/shared/mail/listSnapshots.d.ts +2 -2
- package/dist/apps/shared/mail/messageReadState.d.ts +4 -3
- package/dist/apps/shared/mail/messageReadState.js +5 -5
- package/dist/apps/shared/mail/newMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/newMailNotifications.js +1 -1
- package/dist/apps/shared/mail/outbox/composeBridge.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/outboxState.d.ts +1 -1
- package/dist/apps/shared/mail/outbox/outgoingReplies.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.d.ts +2 -2
- package/dist/apps/shared/mail/outbox/sendDecision.js +1 -1
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +10 -6
- package/dist/apps/shared/mail/outbox/sendJob.js +39 -35
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +6 -3
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +19 -15
- package/dist/apps/shared/mail/outbox/useOutboxStatus.js +5 -3
- package/dist/apps/shared/mail/permanentDelete.d.ts +4 -3
- package/dist/apps/shared/mail/permanentDelete.js +8 -8
- package/dist/apps/shared/mail/primaryMailbox.d.ts +1 -1
- package/dist/apps/shared/mail/primaryMailbox.js +1 -1
- package/dist/apps/shared/mail/reportNotices.d.ts +1 -1
- package/dist/apps/shared/mail/senderBlocking.d.ts +5 -4
- package/dist/apps/shared/mail/senderBlocking.js +19 -16
- package/dist/apps/shared/mail/useMailConnection.d.ts +1 -1
- package/dist/apps/shared/mail/useMailConnection.js +12 -10
- package/dist/apps/shared/mail/useMailLiveUpdates.d.ts +2 -2
- package/dist/apps/shared/mail/useMailLiveUpdates.js +2 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.d.ts +3 -2
- package/dist/apps/shared/mail/useMailboxUpdateAccess.js +31 -10
- package/dist/apps/shared/mail/useMarkMessageRead.d.ts +1 -1
- package/dist/apps/shared/mail/useMarkMessageRead.js +5 -3
- package/dist/apps/shared/mail/useNewMailNotifications.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.d.ts +1 -1
- package/dist/apps/shared/mail/usePermanentDelete.js +4 -2
- package/dist/apps/shared/navigation/pageTitle.d.ts +1 -1
- package/dist/apps/shared/notifications/NotificationCenter.js +1 -1
- package/dist/apps/shared/notifications/NotificationHistoryDialog.js +2 -2
- package/dist/apps/shared/notifications/apiErrors.js +2 -2
- package/dist/apps/shared/notifications/pushStatus.js +1 -1
- package/dist/apps/shared/notifications/systemErrors.js +1 -1
- package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -2
- package/dist/apps/shared/search/LocalIndexLifecycle.js +15 -10
- package/dist/apps/shared/search/crossMailboxSearch.d.ts +1 -1
- package/dist/apps/shared/search/crossMailboxSearch.js +2 -2
- package/dist/apps/shared/search/localIndexBuilder.d.ts +4 -3
- package/dist/apps/shared/search/localIndexBuilder.js +15 -12
- package/dist/apps/shared/search/localIndexKey.js +1 -1
- package/dist/apps/shared/search/localIndexRpcClient.d.ts +43 -31
- package/dist/apps/shared/search/localIndexRpcClient.js +113 -20
- package/dist/apps/shared/search/localIndexTransport.d.ts +77 -0
- package/dist/apps/shared/search/localIndexTransport.js +1 -0
- package/dist/apps/shared/search/localIndexWorker.d.ts +1 -1
- package/dist/apps/shared/search/searchTier2.d.ts +3 -3
- package/dist/apps/shared/signing/enrollmentTracker.d.ts +1 -1
- package/dist/apps/shared/signing/enrollmentTracker.js +3 -3
- package/dist/apps/shared/signing/enrollmentView.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.d.ts +1 -1
- package/dist/apps/shared/signing/signingInfo.js +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.d.ts +1 -1
- package/dist/apps/shared/signing/useSigningEnrollmentWatcher.js +5 -3
- package/dist/apps/shared/styles/app.css +14 -23
- package/dist/apps/www/_layout.d.ts +1 -1
- package/dist/apps/www/_layout.js +1 -1
- package/dist/apps/www/calendar/index.d.ts +1 -1
- package/dist/apps/www/calendar/index.js +20 -18
- package/dist/apps/www/contacts/[uid].d.ts +1 -1
- package/dist/apps/www/contacts/[uid].js +8 -6
- package/dist/apps/www/contacts/index.d.ts +1 -1
- package/dist/apps/www/contacts/index.js +19 -17
- package/dist/apps/www/index.d.ts +1 -1
- package/dist/apps/www/index.js +54 -50
- package/dist/apps/www/messages/[uid].d.ts +1 -1
- package/dist/apps/www/messages/[uid].js +11 -9
- package/dist/apps/www/settings/appearance/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.d.ts +1 -1
- package/dist/apps/www/settings/auto-reply/index.js +6 -4
- package/dist/apps/www/settings/blocked-senders/index.d.ts +1 -1
- package/dist/apps/www/settings/blocked-senders/index.js +6 -4
- package/dist/apps/www/settings/encryption/index.d.ts +1 -1
- package/dist/apps/www/settings/encryption/index.js +31 -29
- package/dist/apps/www/settings/filters/[uid].d.ts +1 -1
- package/dist/apps/www/settings/filters/[uid].js +11 -9
- package/dist/apps/www/settings/filters/_mailFilterRuleConfig.d.ts +2 -2
- package/dist/apps/www/settings/filters/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/index.js +4 -4
- package/dist/apps/www/settings/filters/new/index.d.ts +1 -1
- package/dist/apps/www/settings/filters/new/index.js +10 -8
- package/dist/apps/www/settings/labels/index.d.ts +1 -1
- package/dist/apps/www/settings/labels/index.js +12 -10
- package/dist/apps/www/settings/privacy/index.d.ts +1 -1
- package/dist/apps/www/settings/privacy/index.js +18 -14
- package/dist/apps/www/settings/profile/index.d.ts +1 -1
- package/dist/apps/www/settings/profile/index.js +7 -5
- package/dist/apps/www/settings/read-receipts/index.d.ts +1 -1
- package/dist/apps/www/settings/read-receipts/index.js +5 -3
- package/dist/apps/www/settings/sharing/index.d.ts +1 -1
- package/dist/apps/www/settings/sharing/index.js +6 -6
- package/dist/apps/www/settings/signatures/[uid].d.ts +1 -1
- package/dist/apps/www/settings/signatures/[uid].js +5 -5
- package/dist/apps/www/settings/signatures/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/index.js +4 -4
- package/dist/apps/www/settings/signatures/new/index.d.ts +1 -1
- package/dist/apps/www/settings/signatures/new/index.js +4 -4
- package/dist/apps/www/settings/signatures/signatureDefaults.d.ts +1 -1
- package/dist/apps/www/settings/signatures/signatureDefaults.js +1 -1
- package/dist/apps/www/tasks/index.d.ts +1 -1
- package/dist/apps/www/tasks/index.js +17 -15
- package/dist/lib/admin/auditLogApi.d.ts +32 -0
- package/dist/lib/admin/auditLogApi.js +27 -0
- package/dist/lib/admin/distributionListsApi.d.ts +47 -0
- package/dist/lib/admin/distributionListsApi.js +34 -0
- package/dist/lib/admin/domainsApi.d.ts +75 -0
- package/dist/lib/admin/domainsApi.js +44 -0
- package/dist/lib/admin/escrowAccessRequestsApi.d.ts +79 -0
- package/dist/lib/admin/escrowAccessRequestsApi.js +46 -0
- package/dist/lib/admin/escrowAuditLogApi.d.ts +66 -0
- package/dist/lib/admin/escrowAuditLogApi.js +31 -0
- package/dist/lib/admin/escrowScopesApi.d.ts +78 -0
- package/dist/lib/admin/escrowScopesApi.js +50 -0
- package/dist/lib/admin/leftoverMailboxApi.d.ts +73 -0
- package/dist/lib/admin/leftoverMailboxApi.js +67 -0
- package/dist/lib/admin/mailboxPolicyApi.d.ts +27 -0
- package/dist/lib/admin/mailboxPolicyApi.js +21 -0
- package/dist/lib/admin/matterExportApi.d.ts +40 -0
- package/dist/lib/admin/matterExportApi.js +39 -0
- package/dist/lib/admin/matterSearchApi.d.ts +20 -0
- package/dist/lib/admin/matterSearchApi.js +27 -0
- package/dist/lib/admin/mattersApi.d.ts +56 -0
- package/dist/lib/admin/mattersApi.js +40 -0
- package/dist/lib/admin/pluginsApi.d.ts +265 -0
- package/dist/lib/admin/pluginsApi.js +102 -0
- package/dist/lib/admin/retentionPolicyApi.d.ts +32 -0
- package/dist/lib/admin/retentionPolicyApi.js +29 -0
- package/dist/lib/admin/setupApi.d.ts +23 -0
- package/dist/lib/admin/setupApi.js +26 -0
- package/dist/lib/admin/transportRulesApi.d.ts +66 -0
- package/dist/lib/admin/transportRulesApi.js +43 -0
- package/dist/lib/appearance/preferencesApi.d.ts +128 -0
- package/dist/lib/appearance/preferencesApi.js +257 -0
- package/dist/lib/auth/profileApi.d.ts +33 -0
- package/dist/lib/auth/profileApi.js +52 -0
- package/dist/lib/auth/session.d.ts +70 -0
- package/dist/lib/auth/session.js +210 -0
- package/dist/lib/branding/brandingApi.d.ts +47 -0
- package/dist/lib/branding/brandingApi.js +61 -0
- package/dist/lib/branding/useBranding.d.ts +23 -0
- package/dist/lib/branding/useBranding.js +65 -0
- package/dist/lib/calendar/calendarApi.d.ts +230 -0
- package/dist/lib/calendar/calendarApi.js +106 -0
- package/dist/lib/calendar/calendarColors.d.ts +18 -0
- package/dist/lib/calendar/calendarColors.js +39 -0
- package/dist/lib/calendar/calendarDragIds.d.ts +42 -0
- package/dist/lib/calendar/calendarDragIds.js +92 -0
- package/dist/lib/calendar/calendarMutations.d.ts +68 -0
- package/dist/lib/calendar/calendarMutations.js +190 -0
- package/dist/lib/calendar/eventDescription.d.ts +43 -0
- package/dist/lib/calendar/eventDescription.js +195 -0
- package/dist/lib/calendar/freeBusyApi.d.ts +100 -0
- package/dist/lib/calendar/freeBusyApi.js +119 -0
- package/dist/lib/calendar/inviteApi.d.ts +121 -0
- package/dist/lib/calendar/inviteApi.js +54 -0
- package/dist/lib/calendar/recurrence.d.ts +60 -0
- package/dist/lib/calendar/recurrence.js +225 -0
- package/dist/lib/components/avatar/ContactAvatar.d.ts +8 -0
- package/dist/lib/components/avatar/ContactAvatar.js +26 -0
- package/dist/lib/components/buttons/Button.d.ts +9 -0
- package/dist/lib/components/buttons/Button.js +14 -0
- package/dist/lib/components/buttons/CopyButton.d.ts +18 -0
- package/dist/lib/components/buttons/CopyButton.js +14 -0
- package/dist/lib/components/feedback/Alert.d.ts +4 -0
- package/dist/lib/components/feedback/Alert.js +4 -0
- package/dist/lib/components/feedback/Skeleton.d.ts +21 -0
- package/dist/lib/components/feedback/Skeleton.js +14 -0
- package/dist/lib/components/forms/FormField.d.ts +8 -0
- package/dist/lib/components/forms/FormField.js +5 -0
- package/dist/lib/components/navigation/BottomTabBar.d.ts +27 -0
- package/dist/lib/components/navigation/BottomTabBar.js +32 -0
- package/dist/lib/components/overlays/Drawer.d.ts +20 -0
- package/dist/lib/components/overlays/Drawer.js +27 -0
- package/dist/lib/components/overlays/Modal.d.ts +17 -0
- package/dist/lib/components/overlays/Modal.js +25 -0
- package/dist/lib/components/overlays/PopoverPortal.d.ts +23 -0
- package/dist/lib/components/overlays/PopoverPortal.js +53 -0
- package/dist/lib/components/overlays/overlayStack.d.ts +31 -0
- package/dist/lib/components/overlays/overlayStack.js +143 -0
- package/dist/lib/components/pickers/MiniDatePicker.d.ts +13 -0
- package/dist/lib/components/pickers/MiniDatePicker.js +42 -0
- package/dist/lib/contacts/contactsApi.d.ts +174 -0
- package/dist/lib/contacts/contactsApi.js +139 -0
- package/dist/lib/contacts/vcard.d.ts +23 -0
- package/dist/lib/contacts/vcard.js +179 -0
- package/dist/lib/crypto/composeSecurity.d.ts +67 -0
- package/dist/lib/crypto/composeSecurity.js +93 -0
- package/dist/lib/crypto/encoding.d.ts +9 -0
- package/dist/lib/crypto/encoding.js +32 -0
- package/dist/lib/crypto/escrowKeys.d.ts +25 -0
- package/dist/lib/crypto/escrowKeys.js +59 -0
- package/dist/lib/crypto/idleTimeout.d.ts +16 -0
- package/dist/lib/crypto/idleTimeout.js +47 -0
- package/dist/lib/crypto/keySession.d.ts +159 -0
- package/dist/lib/crypto/keySession.js +367 -0
- package/dist/lib/crypto/keys.d.ts +50 -0
- package/dist/lib/crypto/keys.js +89 -0
- package/dist/lib/crypto/keyvaultApi.d.ts +394 -0
- package/dist/lib/crypto/keyvaultApi.js +350 -0
- package/dist/lib/crypto/masterKey.d.ts +59 -0
- package/dist/lib/crypto/masterKey.js +104 -0
- package/dist/lib/crypto/masterKeyWraps.d.ts +108 -0
- package/dist/lib/crypto/masterKeyWraps.js +228 -0
- package/dist/lib/crypto/messageSecurity.d.ts +248 -0
- package/dist/lib/crypto/messageSecurity.js +367 -0
- package/dist/lib/crypto/mime.d.ts +143 -0
- package/dist/lib/crypto/mime.js +504 -0
- package/dist/lib/crypto/passkeyUnlock.d.ts +20 -0
- package/dist/lib/crypto/passkeyUnlock.js +79 -0
- package/dist/lib/crypto/passwordUnlock.d.ts +39 -0
- package/dist/lib/crypto/passwordUnlock.js +73 -0
- package/dist/lib/crypto/recoveryCode.d.ts +28 -0
- package/dist/lib/crypto/recoveryCode.js +86 -0
- package/dist/lib/crypto/signingProviderApi.d.ts +95 -0
- package/dist/lib/crypto/signingProviderApi.js +60 -0
- package/dist/lib/crypto/smime.d.ts +128 -0
- package/dist/lib/crypto/smime.js +402 -0
- package/dist/lib/crypto/smimeMessage.d.ts +132 -0
- package/dist/lib/crypto/smimeMessage.js +341 -0
- package/dist/lib/crypto/useIdleKeyTimeout.d.ts +10 -0
- package/dist/lib/crypto/useIdleKeyTimeout.js +163 -0
- package/dist/lib/crypto/verificationSeal.d.ts +56 -0
- package/dist/lib/crypto/verificationSeal.js +220 -0
- package/dist/lib/mail/compose/composeQuoting.d.ts +88 -0
- package/dist/lib/mail/compose/composeQuoting.js +204 -0
- package/dist/lib/mail/compose/emojiData.d.ts +9 -0
- package/dist/lib/mail/compose/emojiData.js +18 -0
- package/dist/lib/mail/compose/giphyApi.d.ts +17 -0
- package/dist/lib/mail/compose/giphyApi.js +20 -0
- package/dist/lib/mail/conversationsApi.d.ts +80 -0
- package/dist/lib/mail/conversationsApi.js +44 -0
- package/dist/lib/mail/dataExportApi.d.ts +50 -0
- package/dist/lib/mail/dataExportApi.js +40 -0
- package/dist/lib/mail/directoryApi.d.ts +49 -0
- package/dist/lib/mail/directoryApi.js +101 -0
- package/dist/lib/mail/erasureRequestApi.d.ts +45 -0
- package/dist/lib/mail/erasureRequestApi.js +43 -0
- package/dist/lib/mail/flaggedMessages.d.ts +22 -0
- package/dist/lib/mail/flaggedMessages.js +73 -0
- package/dist/lib/mail/focusedInboxOverridesApi.d.ts +37 -0
- package/dist/lib/mail/focusedInboxOverridesApi.js +34 -0
- package/dist/lib/mail/labelsApi.d.ts +47 -0
- package/dist/lib/mail/labelsApi.js +38 -0
- package/dist/lib/mail/mailAddress.d.ts +27 -0
- package/dist/lib/mail/mailAddress.js +54 -0
- package/dist/lib/mail/mailApi.d.ts +850 -0
- package/dist/lib/mail/mailApi.js +602 -0
- package/dist/lib/mail/mailDetailHooks.d.ts +10 -0
- package/dist/lib/mail/mailDetailHooks.js +53 -0
- package/dist/lib/mail/mailFilterRulesApi.d.ts +76 -0
- package/dist/lib/mail/mailFilterRulesApi.js +45 -0
- package/dist/lib/mail/mailSignaturesApi.d.ts +49 -0
- package/dist/lib/mail/mailSignaturesApi.js +42 -0
- package/dist/lib/mail/mailboxAccessApi.d.ts +74 -0
- package/dist/lib/mail/mailboxAccessApi.js +56 -0
- package/dist/lib/mail/mailboxImportApi.d.ts +53 -0
- package/dist/lib/mail/mailboxImportApi.js +51 -0
- package/dist/lib/mail/messageBodySanitizer.d.ts +16 -0
- package/dist/lib/mail/messageBodySanitizer.js +118 -0
- package/dist/lib/mail/pushClient.d.ts +106 -0
- package/dist/lib/mail/pushClient.js +317 -0
- package/dist/lib/mail/sendEvents.d.ts +33 -0
- package/dist/lib/mail/sendEvents.js +44 -0
- package/dist/lib/mail/sendFailure.d.ts +16 -0
- package/dist/lib/mail/sendFailure.js +86 -0
- package/dist/lib/mail/senderListsApi.d.ts +69 -0
- package/dist/lib/mail/senderListsApi.js +126 -0
- package/dist/lib/search/queryGrammar.d.ts +51 -0
- package/dist/lib/search/queryGrammar.js +111 -0
- package/dist/lib/search/searchApi.d.ts +100 -0
- package/dist/lib/search/searchApi.js +107 -0
- package/dist/lib/search/searchScoring.d.ts +34 -0
- package/dist/lib/search/searchScoring.js +44 -0
- package/dist/lib/search/searchTier3.d.ts +64 -0
- package/dist/lib/search/searchTier3.js +335 -0
- package/dist/lib/tasks/tasksApi.d.ts +85 -0
- package/dist/lib/tasks/tasksApi.js +55 -0
- package/dist/lib/util/api.d.ts +160 -0
- package/dist/lib/util/api.js +251 -0
- package/dist/lib/util/apiClientContext.d.ts +7 -0
- package/dist/lib/util/apiClientContext.js +32 -0
- package/dist/lib/util/apiQuery.d.ts +32 -0
- package/dist/lib/util/apiQuery.js +42 -0
- package/dist/lib/util/clipboard.d.ts +7 -0
- package/dist/lib/util/clipboard.js +56 -0
- package/dist/lib/util/dateInput.d.ts +6 -0
- package/dist/lib/util/dateInput.js +14 -0
- package/dist/lib/util/timeZone.d.ts +12 -0
- package/dist/lib/util/timeZone.js +32 -0
- package/dist/lib/util/useCopyToClipboard.d.ts +12 -0
- package/dist/lib/util/useCopyToClipboard.js +34 -0
- package/dist/lib/util/useIsMobile.d.ts +10 -0
- package/dist/lib/util/useIsMobile.js +29 -0
- package/dist/lib/videoconf/videoMeetingsApi.d.ts +116 -0
- package/dist/lib/videoconf/videoMeetingsApi.js +49 -0
- package/lib/admin/auditLogApi.ts +48 -0
- package/lib/admin/distributionListsApi.ts +77 -0
- package/lib/admin/domainsApi.ts +113 -0
- package/lib/admin/escrowAccessRequestsApi.ts +110 -0
- package/lib/admin/escrowAuditLogApi.ts +101 -0
- package/lib/admin/escrowScopesApi.ts +112 -0
- package/lib/admin/leftoverMailboxApi.ts +120 -0
- package/lib/admin/mailboxPolicyApi.ts +38 -0
- package/lib/admin/matterExportApi.ts +62 -0
- package/lib/admin/matterSearchApi.ts +35 -0
- package/lib/admin/mattersApi.ts +86 -0
- package/lib/admin/pluginsApi.ts +339 -0
- package/lib/admin/retentionPolicyApi.ts +46 -0
- package/lib/admin/setupApi.ts +40 -0
- package/lib/admin/transportRulesApi.ts +106 -0
- package/lib/appearance/preferencesApi.ts +335 -0
- package/lib/auth/profileApi.ts +77 -0
- package/lib/auth/session.ts +238 -0
- package/lib/branding/brandingApi.ts +97 -0
- package/lib/branding/useBranding.ts +81 -0
- package/lib/calendar/calendarApi.ts +309 -0
- package/lib/calendar/calendarColors.ts +45 -0
- package/lib/calendar/calendarDragIds.ts +112 -0
- package/lib/calendar/calendarMutations.ts +236 -0
- package/lib/calendar/eventDescription.ts +211 -0
- package/lib/calendar/freeBusyApi.ts +197 -0
- package/lib/calendar/inviteApi.ts +154 -0
- package/lib/calendar/recurrence.ts +285 -0
- package/lib/components/avatar/ContactAvatar.tsx +47 -0
- package/lib/components/buttons/Button.tsx +40 -0
- package/lib/components/buttons/CopyButton.tsx +45 -0
- package/lib/components/feedback/Alert.tsx +13 -0
- package/lib/components/feedback/Skeleton.tsx +38 -0
- package/lib/components/forms/FormField.tsx +23 -0
- package/lib/components/navigation/BottomTabBar.tsx +66 -0
- package/lib/components/overlays/Drawer.tsx +72 -0
- package/lib/components/overlays/Modal.tsx +64 -0
- package/lib/components/overlays/PopoverPortal.tsx +74 -0
- package/lib/components/overlays/overlayStack.ts +165 -0
- package/lib/components/pickers/MiniDatePicker.tsx +90 -0
- package/lib/contacts/contactsApi.ts +289 -0
- package/lib/contacts/vcard.ts +197 -0
- package/lib/crypto/composeSecurity.ts +140 -0
- package/lib/crypto/encoding.ts +36 -0
- package/lib/crypto/escrowKeys.ts +74 -0
- package/lib/crypto/idleTimeout.ts +49 -0
- package/lib/crypto/keySession.ts +483 -0
- package/lib/crypto/keys.ts +105 -0
- package/lib/crypto/keyvaultApi.ts +674 -0
- package/lib/crypto/masterKey.ts +134 -0
- package/lib/crypto/masterKeyWraps.ts +269 -0
- package/lib/crypto/messageSecurity.ts +600 -0
- package/lib/crypto/mime.ts +568 -0
- package/lib/crypto/passkeyUnlock.ts +103 -0
- package/lib/crypto/passwordUnlock.ts +98 -0
- package/lib/crypto/recoveryCode.ts +93 -0
- package/lib/crypto/signingProviderApi.ts +135 -0
- package/lib/crypto/smime.ts +459 -0
- package/lib/crypto/smimeMessage.ts +491 -0
- package/lib/crypto/useIdleKeyTimeout.ts +176 -0
- package/lib/crypto/verificationSeal.ts +288 -0
- package/lib/mail/compose/composeQuoting.ts +243 -0
- package/lib/mail/compose/emojiData.ts +34 -0
- package/lib/mail/compose/giphyApi.ts +31 -0
- package/lib/mail/conversationsApi.ts +123 -0
- package/lib/mail/dataExportApi.ts +73 -0
- package/lib/mail/directoryApi.ts +161 -0
- package/lib/mail/erasureRequestApi.ts +71 -0
- package/lib/mail/flaggedMessages.ts +80 -0
- package/lib/mail/focusedInboxOverridesApi.ts +64 -0
- package/lib/mail/labelsApi.ts +71 -0
- package/lib/mail/mailAddress.ts +76 -0
- package/lib/mail/mailApi.ts +1286 -0
- package/lib/mail/mailDetailHooks.ts +57 -0
- package/lib/mail/mailFilterRulesApi.ts +116 -0
- package/lib/mail/mailSignaturesApi.ts +84 -0
- package/lib/mail/mailboxAccessApi.ts +106 -0
- package/lib/mail/mailboxImportApi.ts +87 -0
- package/lib/mail/messageBodySanitizer.ts +129 -0
- package/lib/mail/pushClient.ts +375 -0
- package/lib/mail/sendEvents.ts +72 -0
- package/lib/mail/sendFailure.ts +98 -0
- package/lib/mail/senderListsApi.ts +161 -0
- package/lib/search/queryGrammar.ts +179 -0
- package/lib/search/searchApi.ts +199 -0
- package/lib/search/searchScoring.ts +46 -0
- package/lib/search/searchTier3.ts +406 -0
- package/lib/tasks/tasksApi.ts +128 -0
- package/lib/util/api.ts +318 -0
- package/lib/util/apiClientContext.ts +35 -0
- package/lib/util/apiQuery.ts +61 -0
- package/lib/util/clipboard.ts +55 -0
- package/lib/util/dateInput.ts +15 -0
- package/lib/util/timeZone.ts +33 -0
- package/lib/util/useCopyToClipboard.ts +48 -0
- package/lib/util/useIsMobile.ts +34 -0
- package/lib/videoconf/videoMeetingsApi.ts +144 -0
- package/package.json +10 -4
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Verification seals: a client-side record that a message's signature verified when it was first opened, so later key
|
|
7
|
+
* events (a signer key replaced beyond the retained previous keys, a deleted contact, expiry, a later compromise
|
|
8
|
+
* revocation) don't make it look untrusted. See `messageSecurity.ts`'s `evaluateMessageSecurityWithSeal()` for how a
|
|
9
|
+
* seal is used.
|
|
10
|
+
*
|
|
11
|
+
* A seal is an HMAC-SHA256 over the verification result, keyed by a per-mailbox key HKDF-derived from the unlocked
|
|
12
|
+
* master key - so the server, which stores it as the opaque `Message.verificationSeal` (`mailApi.ts`'s
|
|
13
|
+
* `setMessageVerificationSeal()`), can't forge or alter one. The seal key is derived on each call (the same way
|
|
14
|
+
* web-client's local search index key is: `hkdfDerive()` with a fixed salt and a mailbox-bound label), imported as a
|
|
15
|
+
* non-extractable `CryptoKey`, its raw bytes zeroed at once, and never cached or persisted - there is nothing to destroy
|
|
16
|
+
* beyond the master key `keySession.ts`'s `destroyUnlockedKeys()` already zeroes. A destroyed `UnlockedKeys` object
|
|
17
|
+
* throws `KeysLockedError` (checked on entry and again after the derivation awaits).
|
|
18
|
+
*
|
|
19
|
+
* **Format (v1).** `v1.<base64url(JSON payload)>.<base64url(32-byte HMAC-SHA256 tag)>`, where the payload is
|
|
20
|
+
* `{ "v": 1, "mb": mailboxUid, "id": messageUid, "g": masterKeyGeneration, "h": rawSha256, "fp": signerFingerprint,
|
|
21
|
+
* "st": state, "t": verifiedAt }` (`h`/`fp` lowercased hex). The tag is not computed over the JSON text: it covers a canonical encoding of the fields
|
|
22
|
+
* (see `canonicalBytes()`), so the JSON's own formatting carries no meaning. Every character is in restapi's allowed
|
|
23
|
+
* `[A-Za-z0-9+/=_.:-]`, and a seal is at most `MAX_VERIFICATION_SEAL_LENGTH` (2048) characters.
|
|
24
|
+
*
|
|
25
|
+
* **Master key rotation.** A seal is bound to the vault's `masterKeyGeneration` (`keyvaultApi.ts`'s
|
|
26
|
+
* `KeyVault.masterKeyGeneration`): the generation is MAC-covered, and `openVerificationSeal()` refuses a seal for a
|
|
27
|
+
* generation other than the expected one. Seals are deliberately not carried across a `rekey()`: a seal from an older
|
|
28
|
+
* generation never opens under the new master key (its MAC fails too), so a rotation after a suspected compromise can't
|
|
29
|
+
* bless seals that someone holding the old master key could have written. Such a message shows its live result until its
|
|
30
|
+
* next successful live verification, when `evaluateMessageSecurityWithSeal()` re-seals it lazily under the current
|
|
31
|
+
* generation (the server replaces a stored seal only for a newer, current generation).
|
|
32
|
+
*/
|
|
33
|
+
import { fromBase64Url, toBase64Url } from "./encoding.js";
|
|
34
|
+
import { KeysLockedError, hkdfDerive } from "./masterKey.js";
|
|
35
|
+
import { binaryStringToBytes } from "./mime.js";
|
|
36
|
+
|
|
37
|
+
/** The longest seal restapi stores (`Message.verificationSeal`). */
|
|
38
|
+
export const MAX_VERIFICATION_SEAL_LENGTH = 2048;
|
|
39
|
+
|
|
40
|
+
/** The characters restapi allows in a seal. */
|
|
41
|
+
export const VERIFICATION_SEAL_CHARSET = /^[A-Za-z0-9+/=_.:-]*$/;
|
|
42
|
+
|
|
43
|
+
/** The HKDF label the seal key is derived under (suffixed with `:<mailboxUid>`). */
|
|
44
|
+
export const VERIFICATION_SEAL_HKDF_LABEL = "rapidmx:verification-seal:v1";
|
|
45
|
+
|
|
46
|
+
/** Fixed, non-secret HKDF salt: the master key is already uniformly random (see web-client's `localIndexKey.ts`). */
|
|
47
|
+
const FIXED_SALT = new TextEncoder().encode("rapidmx-verification-seal-v1");
|
|
48
|
+
|
|
49
|
+
const SEAL_VERSION = "v1";
|
|
50
|
+
const TAG_LENGTH_BYTES = 32;
|
|
51
|
+
const SHA256_HEX = /^[0-9a-f]{64}$/;
|
|
52
|
+
|
|
53
|
+
/** The verified states a seal can record. */
|
|
54
|
+
export type SealedVerificationState = "signed_verified" | "encrypted_verified";
|
|
55
|
+
|
|
56
|
+
const SEALED_STATES: readonly string[] = ["signed_verified", "encrypted_verified"];
|
|
57
|
+
|
|
58
|
+
/** What `buildVerificationSeal()` seals. */
|
|
59
|
+
export interface VerificationSealInput {
|
|
60
|
+
messageUid: string;
|
|
61
|
+
/** SHA-256 (hex) of the message's raw MIME - `rawMimeSha256()`. */
|
|
62
|
+
rawSha256: string;
|
|
63
|
+
/** The live signer certificate fingerprint (`MessageSecurityResult.signerFingerprint`). */
|
|
64
|
+
signerFingerprint: string;
|
|
65
|
+
state: SealedVerificationState;
|
|
66
|
+
/** UTC timestamp (epoch ms) at which the signature verified. */
|
|
67
|
+
verifiedAt: number;
|
|
68
|
+
/** The vault's `masterKeyGeneration` the seal is written under (`0` when the vault reports none). */
|
|
69
|
+
masterKeyGeneration: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** What `openVerificationSeal()` recovers from a valid seal. */
|
|
73
|
+
export interface OpenedVerificationSeal {
|
|
74
|
+
/** Lowercased hex. */
|
|
75
|
+
signerFingerprint: string;
|
|
76
|
+
state: SealedVerificationState;
|
|
77
|
+
verifiedAt: number;
|
|
78
|
+
masterKeyGeneration: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The part of `keySession.ts`'s `UnlockedKeys` a seal needs. */
|
|
82
|
+
export interface SealKeyMaterial {
|
|
83
|
+
masterKey: Uint8Array;
|
|
84
|
+
destroyed?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** SHA-256 (lowercase hex) of a message's raw MIME, as `evaluateMessageSecurity()` receives it: a binary string
|
|
88
|
+
* (`mailApi.ts`'s `getMessageRawContent()`, one code unit per byte) or the bytes themselves. A string that can't be a
|
|
89
|
+
* binary string (a code unit above 0xFF) is hashed as its UTF-8 encoding, like `mime.ts`'s `binaryStringToBytes()`. */
|
|
90
|
+
export async function rawMimeSha256(raw: string | Uint8Array): Promise<string> {
|
|
91
|
+
const bytes = typeof raw === "string" ? binaryStringToBytes(raw) : raw;
|
|
92
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes as BufferSource));
|
|
93
|
+
return [...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function assertNotDestroyed(unlocked: SealKeyMaterial): void {
|
|
97
|
+
if (unlocked.destroyed) {
|
|
98
|
+
throw new KeysLockedError();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Derives the mailbox's seal key and imports it as a non-extractable HMAC key, zeroing the raw bytes. Throws
|
|
103
|
+
* `KeysLockedError` for destroyed or zeroed key material. */
|
|
104
|
+
async function importSealKey(mailboxUid: string, unlocked: SealKeyMaterial): Promise<CryptoKey> {
|
|
105
|
+
assertNotDestroyed(unlocked);
|
|
106
|
+
const raw = await hkdfDerive(unlocked.masterKey, FIXED_SALT, `${VERIFICATION_SEAL_HKDF_LABEL}:${mailboxUid}`);
|
|
107
|
+
try {
|
|
108
|
+
const key = await crypto.subtle.importKey("raw", raw as BufferSource, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
109
|
+
// Locked while deriving: the derivation may have copied the master key before it was zeroed.
|
|
110
|
+
assertNotDestroyed(unlocked);
|
|
111
|
+
return key;
|
|
112
|
+
} finally {
|
|
113
|
+
raw.fill(0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Length-prefixed (4-byte big-endian) UTF-8 fields, so no two field lists encode alike. */
|
|
118
|
+
function canonicalBytes(fields: string[]): Uint8Array {
|
|
119
|
+
const encoded = fields.map((field) => new TextEncoder().encode(field));
|
|
120
|
+
const out = new Uint8Array(encoded.reduce((total, bytes) => total + 4 + bytes.length, 0));
|
|
121
|
+
const view = new DataView(out.buffer);
|
|
122
|
+
let offset = 0;
|
|
123
|
+
for (const bytes of encoded) {
|
|
124
|
+
view.setUint32(offset, bytes.length);
|
|
125
|
+
out.set(bytes, offset + 4);
|
|
126
|
+
offset += 4 + bytes.length;
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface SealFields {
|
|
132
|
+
mailboxUid: string;
|
|
133
|
+
messageUid: string;
|
|
134
|
+
rawSha256: string;
|
|
135
|
+
signerFingerprint: string;
|
|
136
|
+
state: string;
|
|
137
|
+
verifiedAt: number;
|
|
138
|
+
masterKeyGeneration: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function computeTag(key: CryptoKey, fields: SealFields): Promise<Uint8Array> {
|
|
142
|
+
const data = canonicalBytes([
|
|
143
|
+
SEAL_VERSION,
|
|
144
|
+
fields.mailboxUid,
|
|
145
|
+
fields.messageUid,
|
|
146
|
+
String(fields.masterKeyGeneration),
|
|
147
|
+
fields.rawSha256,
|
|
148
|
+
fields.signerFingerprint,
|
|
149
|
+
fields.state,
|
|
150
|
+
String(fields.verifiedAt),
|
|
151
|
+
]);
|
|
152
|
+
return new Uint8Array(await crypto.subtle.sign("HMAC", key, data as BufferSource));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Compares every byte whatever the first difference (callers only pass 32-byte tags; a length difference still fails). */
|
|
156
|
+
function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
157
|
+
let diff = a.length ^ b.length;
|
|
158
|
+
for (let i = 0; i < a.length; i++) {
|
|
159
|
+
diff |= a[i] ^ b[i];
|
|
160
|
+
}
|
|
161
|
+
return diff === 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** A non-negative safe integer - a `verifiedAt` or a `masterKeyGeneration`. */
|
|
165
|
+
function isValidTimestamp(value: unknown): value is number {
|
|
166
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Seals a verification result for `mailboxUid`'s message `input.messageUid` - see this module's doc comment for the
|
|
171
|
+
* format. Throws `KeysLockedError` for destroyed keys, and a plain `Error` for input that can't be sealed: empty uids or
|
|
172
|
+
* fingerprint, a `rawSha256` that isn't 64 hex digits, an unknown `state`, a `verifiedAt` or `masterKeyGeneration` that isn't a non-negative
|
|
173
|
+
* integer, or a seal that would exceed `MAX_VERIFICATION_SEAL_LENGTH` (very long uids).
|
|
174
|
+
*/
|
|
175
|
+
export async function buildVerificationSeal(mailboxUid: string, unlocked: SealKeyMaterial, input: VerificationSealInput): Promise<string> {
|
|
176
|
+
const fields: SealFields = {
|
|
177
|
+
mailboxUid,
|
|
178
|
+
messageUid: input.messageUid,
|
|
179
|
+
rawSha256: typeof input.rawSha256 === "string" ? input.rawSha256.toLowerCase() : "",
|
|
180
|
+
signerFingerprint: typeof input.signerFingerprint === "string" ? input.signerFingerprint.toLowerCase() : "",
|
|
181
|
+
state: input.state,
|
|
182
|
+
verifiedAt: input.verifiedAt,
|
|
183
|
+
masterKeyGeneration: input.masterKeyGeneration,
|
|
184
|
+
};
|
|
185
|
+
if (!mailboxUid || !fields.messageUid || !fields.signerFingerprint) {
|
|
186
|
+
throw new Error("A verification seal needs a mailbox uid, message uid and signer fingerprint.");
|
|
187
|
+
}
|
|
188
|
+
if (!SHA256_HEX.test(fields.rawSha256)) {
|
|
189
|
+
throw new Error("A verification seal's rawSha256 must be a hex SHA-256 digest.");
|
|
190
|
+
}
|
|
191
|
+
if (!SEALED_STATES.includes(fields.state) || !isValidTimestamp(fields.verifiedAt) || !isValidTimestamp(fields.masterKeyGeneration)) {
|
|
192
|
+
throw new Error("A verification seal needs a verified state and non-negative integer verifiedAt and masterKeyGeneration.");
|
|
193
|
+
}
|
|
194
|
+
const key = await importSealKey(mailboxUid, unlocked);
|
|
195
|
+
const payload = { v: 1, mb: fields.mailboxUid, id: fields.messageUid, g: fields.masterKeyGeneration, h: fields.rawSha256, fp: fields.signerFingerprint, st: fields.state, t: fields.verifiedAt };
|
|
196
|
+
const payloadText = toBase64Url(new TextEncoder().encode(JSON.stringify(payload)));
|
|
197
|
+
const seal = `${SEAL_VERSION}.${payloadText}.${toBase64Url(await computeTag(key, fields))}`;
|
|
198
|
+
if (seal.length > MAX_VERIFICATION_SEAL_LENGTH) {
|
|
199
|
+
throw new Error(`A verification seal can't exceed ${MAX_VERIFICATION_SEAL_LENGTH} characters.`);
|
|
200
|
+
}
|
|
201
|
+
return seal;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
205
|
+
|
|
206
|
+
/** Splits and decodes a v1 seal, or `undefined` for anything malformed. Never throws. */
|
|
207
|
+
function parseSeal(seal: unknown): { fields: SealFields; tag: Uint8Array } | undefined {
|
|
208
|
+
if (typeof seal !== "string" || seal.length > MAX_VERIFICATION_SEAL_LENGTH || !VERIFICATION_SEAL_CHARSET.test(seal)) {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
const parts = seal.split(".");
|
|
212
|
+
if (parts.length !== 3 || parts[0] !== SEAL_VERSION || !BASE64URL.test(parts[1]) || !BASE64URL.test(parts[2])) {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const tag = fromBase64Url(parts[2]);
|
|
217
|
+
const payload: unknown = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(fromBase64Url(parts[1])));
|
|
218
|
+
if (tag.length !== TAG_LENGTH_BYTES || typeof payload !== "object" || payload === null) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
const { v, mb, id, g, h, fp, st, t } = payload as Record<string, unknown>;
|
|
222
|
+
if (
|
|
223
|
+
v !== 1 ||
|
|
224
|
+
typeof mb !== "string" ||
|
|
225
|
+
typeof id !== "string" ||
|
|
226
|
+
!isValidTimestamp(g) ||
|
|
227
|
+
typeof h !== "string" ||
|
|
228
|
+
!SHA256_HEX.test(h) ||
|
|
229
|
+
typeof fp !== "string" ||
|
|
230
|
+
!fp ||
|
|
231
|
+
typeof st !== "string" ||
|
|
232
|
+
!SEALED_STATES.includes(st) ||
|
|
233
|
+
!isValidTimestamp(t)
|
|
234
|
+
) {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
return { fields: { mailboxUid: mb, messageUid: id, rawSha256: h, signerFingerprint: fp, state: st, verifiedAt: t, masterKeyGeneration: g }, tag };
|
|
238
|
+
} catch {
|
|
239
|
+
// Invalid base64 or UTF-8, or JSON that won't parse.
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Opens a seal written by `buildVerificationSeal()`. Resolves `{ signerFingerprint, state, verifiedAt, masterKeyGeneration }`
|
|
246
|
+
* only when the version is known, the tag verifies (constant-time comparison) under this mailbox's seal key, the sealed mailbox and
|
|
247
|
+
* message uids equal `mailboxUid`/`messageUid`, and the sealed hash equals `rawSha256` (the current raw MIME's
|
|
248
|
+
* `rawMimeSha256()`), and - when `expectedMasterKeyGeneration` is given - the sealed generation equals it (the vault's
|
|
249
|
+
* current `masterKeyGeneration`; a seal from another generation wouldn't open under that master key anyway, but this
|
|
250
|
+
* checks it explicitly). Anything else - a malformed, truncated, tampered, foreign or oversized seal, or a seal sealed
|
|
251
|
+
* under a different master key - resolves `undefined`; this never throws for the seal's content. Throws
|
|
252
|
+
* `KeysLockedError` for destroyed keys, whatever the seal.
|
|
253
|
+
*/
|
|
254
|
+
export async function openVerificationSeal(
|
|
255
|
+
mailboxUid: string,
|
|
256
|
+
unlocked: SealKeyMaterial,
|
|
257
|
+
messageUid: string,
|
|
258
|
+
seal: string | undefined,
|
|
259
|
+
rawSha256: string,
|
|
260
|
+
expectedMasterKeyGeneration?: number,
|
|
261
|
+
): Promise<OpenedVerificationSeal | undefined> {
|
|
262
|
+
assertNotDestroyed(unlocked);
|
|
263
|
+
const parsed = parseSeal(seal);
|
|
264
|
+
if (!parsed) {
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
const key = await importSealKey(mailboxUid, unlocked);
|
|
268
|
+
// The tag is computed over the values the caller expects, so a seal for another mailbox or message fails the MAC as
|
|
269
|
+
// well as the explicit comparison below.
|
|
270
|
+
const expected = await computeTag(key, { ...parsed.fields, mailboxUid, messageUid });
|
|
271
|
+
const { fields } = parsed;
|
|
272
|
+
const tagMatches = constantTimeEqual(expected, parsed.tag);
|
|
273
|
+
if (
|
|
274
|
+
!tagMatches ||
|
|
275
|
+
fields.mailboxUid !== mailboxUid ||
|
|
276
|
+
fields.messageUid !== messageUid ||
|
|
277
|
+
fields.rawSha256 !== String(rawSha256).toLowerCase() ||
|
|
278
|
+
(expectedMasterKeyGeneration !== undefined && fields.masterKeyGeneration !== expectedMasterKeyGeneration)
|
|
279
|
+
) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
return {
|
|
283
|
+
signerFingerprint: fields.signerFingerprint,
|
|
284
|
+
state: fields.state as SealedVerificationState,
|
|
285
|
+
verifiedAt: fields.verifiedAt,
|
|
286
|
+
masterKeyGeneration: fields.masterKeyGeneration,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Builds what a Reply/Reply All/Forward compose starts with - its recipients, its quoted original message, and the
|
|
7
|
+
* body layout around the quote - entirely client-side, same "restapi never composes message bodies" convention as
|
|
8
|
+
* `mailApi.ts`'s `assembleDraft()`.
|
|
9
|
+
*
|
|
10
|
+
* The quote carries the original's full body, as the reader saw it: the caller passes a `QuotedBody` (the message's
|
|
11
|
+
* sanitized HTML body, its decrypted/verified content, or its plain-text body). HTML is sanitized again here with
|
|
12
|
+
* the same remote-resource-stripping policy used to display a client-rendered body (`messageBodySanitizer.ts`), plus
|
|
13
|
+
* a stricter tag list for the editor. `message.bodyPreview` - a server-derived excerpt, truncated - is only the last
|
|
14
|
+
* resort, when no body could be loaded.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Message, Recipient } from "../mailApi.js";
|
|
18
|
+
import { sanitizeQuotedHtml } from "../messageBodySanitizer.js";
|
|
19
|
+
|
|
20
|
+
function escapeHtml(text: string): string {
|
|
21
|
+
return text
|
|
22
|
+
.replace(/&/g, "&")
|
|
23
|
+
.replace(/</g, "<")
|
|
24
|
+
.replace(/>/g, ">")
|
|
25
|
+
.replace(/"/g, """)
|
|
26
|
+
.replace(/'/g, "'");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function textToHtml(text: string): string {
|
|
30
|
+
return escapeHtml(text)
|
|
31
|
+
.split(/\r?\n/)
|
|
32
|
+
.map((line) => `<p>${line || " "}</p>`)
|
|
33
|
+
.join("");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A recipient's display name, without the address the server sometimes leaves in it: an ingested message's `from`
|
|
38
|
+
* carries the whole `"Bob Allen" <bob@example.com>` From header as its display name, which would otherwise be shown
|
|
39
|
+
* (and addressed) as `"Bob Allen" <bob@example.com> <bob@example.com>`. A name that is, or ends in, the recipient's
|
|
40
|
+
* own address is reduced to the part before it; a name that is any other address is dropped entirely.
|
|
41
|
+
*/
|
|
42
|
+
export function recipientDisplayName(recipient: Recipient): string | undefined {
|
|
43
|
+
const name = recipient.displayName?.trim();
|
|
44
|
+
if (!name) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const angle = /^(.*)<([^<>]*)>$/.exec(name);
|
|
48
|
+
const inner = angle ? angle[2].trim() : name;
|
|
49
|
+
if (!inner.includes("@")) {
|
|
50
|
+
return name;
|
|
51
|
+
}
|
|
52
|
+
if (inner.toLowerCase() !== recipient.address.trim().toLowerCase()) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
const before = (angle ? angle[1] : "").trim().replace(/^"(.*)"$/s, "$1").trim();
|
|
56
|
+
return before || undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** The same recipient with `recipientDisplayName()`'s cleaned-up name (absent when nothing is left of it). */
|
|
60
|
+
function cleaned(recipient: Recipient): Recipient {
|
|
61
|
+
const name = recipientDisplayName(recipient);
|
|
62
|
+
return name === recipient.displayName ? recipient : { ...recipient, ...(name ? { displayName: name } : { displayName: undefined }) };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** `Name <address>`, or the bare address when there's no usable display name. */
|
|
66
|
+
function nameAndAddress(recipient: Recipient): string {
|
|
67
|
+
const name = recipientDisplayName(recipient);
|
|
68
|
+
return name ? `${name} <${recipient.address}>` : recipient.address;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const QUOTE_STYLE = "border-left:2px solid #ccc;margin:0;padding-left:1em;color:#666;";
|
|
72
|
+
|
|
73
|
+
/** The body of the message being quoted, as loaded by the caller. `html` is preferred over `text`; either may be
|
|
74
|
+
* absent (e.g. an encrypted message this device can't open, or a failed fetch). */
|
|
75
|
+
export interface QuotedBody {
|
|
76
|
+
/** An HTML body. Sanitized again by the quote builders, so it may come from any source. */
|
|
77
|
+
html?: string;
|
|
78
|
+
/** A plain-text body. HTML-escaped by the quote builders. */
|
|
79
|
+
text?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Whether sanitized HTML still shows anything: text other than whitespace, or an image. */
|
|
83
|
+
function hasVisibleContent(html: string): boolean {
|
|
84
|
+
return /<img\b/i.test(html) || html.replace(/<[^>]*>/g, "").replace(/ |\s/g, "") !== "";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The quoted content: the sanitized HTML body, else the text body, else the body preview. */
|
|
88
|
+
function quotedContent(message: Message, body: QuotedBody | undefined): string {
|
|
89
|
+
if (body?.html) {
|
|
90
|
+
const sanitized = sanitizeQuotedHtml(body.html);
|
|
91
|
+
if (hasVisibleContent(sanitized)) {
|
|
92
|
+
return sanitized;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (body?.text?.trim()) {
|
|
96
|
+
return textToHtml(body.text);
|
|
97
|
+
}
|
|
98
|
+
return textToHtml(message.bodyPreview ?? "");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Prepends "Re: " unless `subject` already starts with it (any casing) — the same convention every
|
|
102
|
+
* mail client uses to avoid a "Re: Re: Re: ..." chain. */
|
|
103
|
+
export function replySubject(subject: string): string {
|
|
104
|
+
return /^re:/i.test(subject.trim()) ? subject : `Re: ${subject}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Same convention as `replySubject()`, for forwards. */
|
|
108
|
+
export function forwardSubject(subject: string): string {
|
|
109
|
+
return /^fwd:/i.test(subject.trim()) ? subject : `Fwd: ${subject}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** The quoted original a Reply/Reply All compose starts with: an "On <date>, Name <address> wrote:" line and the
|
|
113
|
+
* original body in a blockquote. `buildComposeBodyHtml()` places it in the body. */
|
|
114
|
+
export function buildReplyQuote(message: Message, body?: QuotedBody): string {
|
|
115
|
+
const when = new Date(message.receivedDate).toLocaleString();
|
|
116
|
+
return (
|
|
117
|
+
`<p>On ${escapeHtml(when)}, ${escapeHtml(nameAndAddress(message.from))} wrote:</p>` +
|
|
118
|
+
`<blockquote style="${QUOTE_STYLE}">${quotedContent(message, body)}</blockquote>`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** The quoted body a Forward compose starts with — includes the original headers block, matching every
|
|
123
|
+
* major mail client's own forward convention, since a forward (unlike a reply) has no visible reply
|
|
124
|
+
* chain of its own to imply who originally sent it. */
|
|
125
|
+
export function buildForwardQuote(message: Message, body?: QuotedBody): string {
|
|
126
|
+
const when = new Date(message.receivedDate).toLocaleString();
|
|
127
|
+
const to = message.recipients
|
|
128
|
+
.filter((r) => r.type === "to")
|
|
129
|
+
.map(nameAndAddress)
|
|
130
|
+
.join(", ");
|
|
131
|
+
return (
|
|
132
|
+
`<p>---------- Forwarded message ----------<br>` +
|
|
133
|
+
`From: ${escapeHtml(nameAndAddress(message.from))}<br>` +
|
|
134
|
+
`Date: ${escapeHtml(when)}<br>` +
|
|
135
|
+
`Subject: ${escapeHtml(message.subject || "(no subject)")}<br>` +
|
|
136
|
+
`To: ${escapeHtml(to)}</p>` +
|
|
137
|
+
`<blockquote style="${QUOTE_STYLE}">${quotedContent(message, body)}</blockquote>`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The HTML a compose body starts with, laid out the way Outlook and Gmail do: an empty paragraph first (where the
|
|
143
|
+
* caret goes, so typing lands above everything else), then the signature, then a blank line, then the quoted
|
|
144
|
+
* original. `""` when there is neither a signature nor a quote, which leaves the editor's own single empty
|
|
145
|
+
* paragraph.
|
|
146
|
+
*
|
|
147
|
+
* A quote is always preceded by an empty paragraph of its own, on top of the one that separates it from a
|
|
148
|
+
* signature: what the reply opens with is the caret's line and then the quote right underneath it, and there has to
|
|
149
|
+
* be somewhere to press Enter into above the "On ... wrote:" line without first making room for it by hand.
|
|
150
|
+
*/
|
|
151
|
+
export function buildComposeBodyHtml(signatureHtml?: string, quotedHtml?: string): string {
|
|
152
|
+
if (!signatureHtml && !quotedHtml) {
|
|
153
|
+
return "";
|
|
154
|
+
}
|
|
155
|
+
const separator = signatureHtml && quotedHtml ? "<p></p>" : "";
|
|
156
|
+
const aboveQuote = quotedHtml ? "<p></p>" : "";
|
|
157
|
+
return `<p></p>${signatureHtml ?? ""}${separator}${aboveQuote}${quotedHtml ?? ""}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The longest `References` chain `buildReplyThreading()` reports. A chain grows by one entry per reply forever
|
|
162
|
+
* and the server trims what it actually writes into the header anyway; sending an unbounded one is pointless.
|
|
163
|
+
* The root is always kept - it is what a conversation is keyed on - and the oldest entries after it are dropped.
|
|
164
|
+
*/
|
|
165
|
+
export const MAX_REPLY_REFERENCES = 20;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* What a reply or forward must record about the thread it continues, from the message it is a reply to: the
|
|
169
|
+
* RFC 5322 `In-Reply-To` (that message's own `messageId`) and the `References` chain (its own chain, with its
|
|
170
|
+
* `messageId` appended). Pass the result to `createDraft()`.
|
|
171
|
+
*
|
|
172
|
+
* Without it a reply is relayed with no threading headers at all - this server composes a reply's MIME from the
|
|
173
|
+
* structured compose input, which says nothing about what is being replied to - so every recipient's mail server,
|
|
174
|
+
* and the sender's own Sent Items copy, files it as a brand-new conversation instead of adding it to the thread.
|
|
175
|
+
*/
|
|
176
|
+
export function buildReplyThreading(message: Pick<Message, "messageId" | "references">): { inReplyTo: string; references: string[] } {
|
|
177
|
+
const parent: string = message.messageId.trim();
|
|
178
|
+
const chain: string[] = [];
|
|
179
|
+
for (const reference of message.references ?? []) {
|
|
180
|
+
const id: string = typeof reference === "string" ? reference.trim() : "";
|
|
181
|
+
if (id && id !== parent && !chain.includes(id)) {
|
|
182
|
+
chain.push(id);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
chain.push(parent);
|
|
186
|
+
// Trimmed from after the root, so the thread's first message (what the conversation is keyed on) and the
|
|
187
|
+
// most recent ancestors both survive.
|
|
188
|
+
while (chain.length > MAX_REPLY_REFERENCES) {
|
|
189
|
+
chain.splice(1, 1);
|
|
190
|
+
}
|
|
191
|
+
return { inReplyTo: parent, references: chain };
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** The To and Cc a reply starts with. */
|
|
195
|
+
export interface ReplyRecipients {
|
|
196
|
+
to: Recipient[];
|
|
197
|
+
cc: Recipient[];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Who a Reply or Reply All goes to. The replying mailbox itself (`ownAddresses`: its primary address and aliases,
|
|
202
|
+
* compared case-insensitively) is left out, and no address is repeated.
|
|
203
|
+
*
|
|
204
|
+
* A reply to someone else's message goes to its sender. Reply All adds the original To recipients to To and the
|
|
205
|
+
* original Cc recipients to Cc.
|
|
206
|
+
*
|
|
207
|
+
* A reply to a message the mailbox sent itself (e.g. from Sent Items) goes to the original To recipients instead of
|
|
208
|
+
* back to the mailbox, and Reply All keeps the original Cc as Cc. With no other To recipient, the original Cc
|
|
209
|
+
* recipients become To. Only a message the mailbox sent to nobody but itself is replied to itself.
|
|
210
|
+
*
|
|
211
|
+
* Bcc recipients are never carried over. Display names are kept.
|
|
212
|
+
*/
|
|
213
|
+
export function buildReplyRecipients(message: Message, ownAddresses: string[], replyAll: boolean): ReplyRecipients {
|
|
214
|
+
const own = new Set(ownAddresses.map((address) => address.trim().toLowerCase()));
|
|
215
|
+
const seen = new Set<string>();
|
|
216
|
+
const take = (recipients: Recipient[]): Recipient[] =>
|
|
217
|
+
recipients
|
|
218
|
+
.filter((recipient) => {
|
|
219
|
+
const key = recipient.address.trim().toLowerCase();
|
|
220
|
+
if (!key || own.has(key) || seen.has(key)) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
seen.add(key);
|
|
224
|
+
return true;
|
|
225
|
+
})
|
|
226
|
+
.map(cleaned);
|
|
227
|
+
const originalTo = message.recipients.filter((r) => r.type === "to");
|
|
228
|
+
const originalCc = message.recipients.filter((r) => r.type === "cc");
|
|
229
|
+
|
|
230
|
+
if (!own.has(message.from.address.trim().toLowerCase())) {
|
|
231
|
+
const to = take([message.from, ...(replyAll ? originalTo : [])]);
|
|
232
|
+
return { to, cc: replyAll ? take(originalCc) : [] };
|
|
233
|
+
}
|
|
234
|
+
const to = take(originalTo);
|
|
235
|
+
const cc = take(originalCc);
|
|
236
|
+
if (to.length > 0) {
|
|
237
|
+
return { to, cc: replyAll ? cc : [] };
|
|
238
|
+
}
|
|
239
|
+
if (cc.length > 0) {
|
|
240
|
+
return { to: cc, cc: [] };
|
|
241
|
+
}
|
|
242
|
+
return { to: [cleaned(message.from)], cc: [] };
|
|
243
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Emoji data for the Compose window's "Insert emoji" picker — the full standard unicode emoji set
|
|
7
|
+
* (categories + native characters), sourced from `@emoji-mart/data` (used as a pure data file only;
|
|
8
|
+
* this app has its own grid UI rather than pulling in `@emoji-mart`'s full React picker component).
|
|
9
|
+
*/
|
|
10
|
+
import emojiData from "@emoji-mart/data" with { type: "json" };
|
|
11
|
+
|
|
12
|
+
/** The subset of `@emoji-mart/data`'s own shape this file actually reads — declared locally rather
|
|
13
|
+
* than importing its `EmojiMartData` type, which would need a second, non-`{type:"json"}` import of
|
|
14
|
+
* the same specifier (its `.d.ts` and its JSON data resolve from the same package name, so importing
|
|
15
|
+
* both is a `no-duplicate-imports` lint error, not just style noise). */
|
|
16
|
+
interface RawEmojiMartData {
|
|
17
|
+
categories: { id: string; emojis: string[] }[];
|
|
18
|
+
emojis: Record<string, { id: string; name: string; skins: { native: string }[] } | undefined>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const data = emojiData as RawEmojiMartData;
|
|
22
|
+
|
|
23
|
+
export interface EmojiCategory {
|
|
24
|
+
id: string;
|
|
25
|
+
emojis: { id: string; native: string; name: string }[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const EMOJI_CATEGORIES: EmojiCategory[] = data.categories.map((category) => ({
|
|
29
|
+
id: category.id,
|
|
30
|
+
emojis: category.emojis
|
|
31
|
+
.map((emojiId) => data.emojis[emojiId])
|
|
32
|
+
.filter((emoji): emoji is NonNullable<typeof emoji> => emoji != null && emoji.skins.length > 0)
|
|
33
|
+
.map((emoji) => ({ id: emoji.id, native: emoji.skins[0].native, name: emoji.name })),
|
|
34
|
+
}));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/** Thin wrapper over this server's own Giphy search proxy (`src/routes/BaseGiphySearchRoute.ts`) —
|
|
6
|
+
* never calls Giphy directly from the browser, since that would require shipping the API key in the
|
|
7
|
+
* client bundle. See `GifPicker.tsx` for the UI this backs. */
|
|
8
|
+
|
|
9
|
+
import { ApiClient, withClient } from "../../util/api.js";
|
|
10
|
+
|
|
11
|
+
export interface GiphyGif {
|
|
12
|
+
id: string;
|
|
13
|
+
/** A small rendition, for the search grid's thumbnails. */
|
|
14
|
+
previewUrl: string;
|
|
15
|
+
/** The full-resolution GIF — inserted into the message body when chosen. */
|
|
16
|
+
url: string;
|
|
17
|
+
title: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** An empty/blank `query` returns Giphy's trending feed (see the backend route's own doc comment). `client`,
|
|
21
|
+
* given here, is an explicit `ApiClient` from `createApiClient()` (e.g. one account of a multi-account app) to
|
|
22
|
+
* call instead of the default global `apiFetch()` - see `withClient()`'s own doc comment in `util/api.ts`.
|
|
23
|
+
* Omitted (the default), this behaves exactly as before. */
|
|
24
|
+
export function searchGifs(query: string, client?: ApiClient): Promise<GiphyGif[]> {
|
|
25
|
+
const params = new URLSearchParams();
|
|
26
|
+
const trimmed = query.trim();
|
|
27
|
+
if (trimmed) {
|
|
28
|
+
params.set("q", trimmed);
|
|
29
|
+
}
|
|
30
|
+
return withClient(client, `/mail/giphy/search?${params.toString()}`);
|
|
31
|
+
}
|