@rapidmx/web-client 0.35.0 → 0.37.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/apps/escrow/matters/[uid].tsx +4 -3
- package/apps/shared/calendar/calendarReminders.ts +35 -0
- package/apps/shared/calendar/sealedEvents.ts +113 -0
- package/apps/shared/calendar/useCalendarReminders.ts +28 -9
- package/apps/shared/components/admin/settings/DomainDnsSetup.tsx +4 -0
- package/apps/shared/components/admin/settings/EncryptionPolicyForm.tsx +89 -3
- package/apps/shared/components/calendar/EventDetails.tsx +13 -2
- package/apps/shared/components/calendar/EventEditor.tsx +124 -17
- package/apps/shared/components/calendar/EventFormParts.tsx +1 -0
- package/apps/shared/components/calendar/EventModal.tsx +19 -2
- package/apps/shared/components/calendar/RequestChangeForm.tsx +4 -1
- package/apps/shared/components/calendar/eventForm.ts +2 -0
- package/apps/shared/components/contacts/ContactCard.tsx +13 -1
- package/apps/shared/components/contacts/ContactDetailPane.tsx +189 -9
- package/apps/shared/components/contacts/ContactForm.tsx +56 -7
- package/apps/shared/components/contacts/KeyChangeReview.tsx +5 -1
- package/apps/shared/components/contacts/contactCardData.ts +8 -4
- package/apps/shared/components/contacts/contactKeys.ts +3 -3
- package/apps/shared/components/layout/AppShell.tsx +34 -5
- package/apps/shared/components/layout/KeyEnrollmentGate.tsx +30 -1
- package/apps/shared/components/layout/SharedMailboxKeyManager.tsx +122 -0
- package/apps/shared/components/layout/UnlockPromptProvider.tsx +34 -0
- package/apps/shared/components/mail/ConversationList.tsx +1 -1
- package/apps/shared/components/mail/InviteCard.tsx +11 -1
- package/apps/shared/components/mail/MessageDetailPane.tsx +59 -26
- package/apps/shared/components/mail/compose/ComposeWindow.tsx +9 -2
- package/apps/shared/components/mail/compose/RecipientInput.tsx +2 -1
- package/apps/shared/components/mail/compose/draftResume.ts +28 -3
- package/apps/shared/components/mail/compose/protectedContactSuggestions.ts +121 -0
- package/apps/shared/components/mail/compose/quotedBody.ts +4 -3
- package/apps/shared/components/mail/invite/inviteStore.ts +17 -1
- package/apps/shared/components/mail/layout/MailShell.tsx +2 -0
- package/apps/shared/components/mail/pinnedSigners.ts +14 -0
- package/apps/shared/components/mail/reading/conversationPrint.ts +3 -3
- package/apps/shared/components/mail/reading/useMessageActions.ts +4 -1
- package/apps/shared/components/settings/KeyExportSetting.tsx +173 -0
- package/apps/shared/components/settings/OpenPgpKeySetting.tsx +240 -0
- package/apps/shared/components/settings/PolicyLockedNotice.tsx +23 -0
- package/apps/shared/components/settings/ZeroAccessSetting.tsx +128 -0
- package/apps/shared/components/settings/ZeroAccessTypes.tsx +175 -0
- package/apps/shared/components/settings/layout/SettingsShell.tsx +9 -2
- package/apps/shared/mail/decryptedMessages.ts +11 -3
- package/apps/shared/mail/outbox/sendJob.ts +349 -50
- package/apps/shared/mail/outbox/sendOutcomes.ts +91 -20
- package/apps/shared/mail/reportNotices.ts +3 -1
- package/apps/shared/mail/zeroAccessSource.ts +27 -0
- package/apps/shared/search/LocalIndexLifecycle.tsx +84 -24
- package/apps/shared/search/localIndexBuilder.ts +57 -20
- package/apps/shared/search/localIndexRpcClient.ts +96 -12
- package/apps/shared/search/localIndexSchema.ts +20 -4
- package/apps/shared/search/localIndexTransport.ts +6 -2
- package/apps/shared/search/localIndexWorker.ts +37 -18
- package/apps/shared/search/searchTier2.ts +5 -0
- package/apps/shared/search/zeroAccessIndexSync.ts +679 -0
- package/apps/shared/search/zeroAccessSyncState.ts +70 -0
- package/apps/shared/zeroAccess/ProtectedMark.tsx +19 -0
- package/apps/shared/zeroAccess/useKeysLocked.ts +25 -0
- package/apps/shared/zeroAccess/useOpenedRecord.ts +71 -0
- package/apps/www/_shell.tsx +2 -0
- package/apps/www/calendar/index.tsx +225 -61
- package/apps/www/contacts/index.tsx +67 -11
- package/apps/www/index.tsx +19 -3
- package/apps/www/settings/encryption/index.tsx +21 -1
- package/apps/www/settings/privacy/index.tsx +89 -39
- package/apps/www/tasks/index.tsx +24 -6
- package/dist/apps/escrow/matters/[uid].js +2 -2
- package/dist/apps/shared/calendar/calendarReminders.d.ts +14 -0
- package/dist/apps/shared/calendar/calendarReminders.js +26 -0
- package/dist/apps/shared/calendar/sealedEvents.d.ts +14 -0
- package/dist/apps/shared/calendar/sealedEvents.js +85 -0
- package/dist/apps/shared/calendar/useCalendarReminders.d.ts +2 -1
- package/dist/apps/shared/calendar/useCalendarReminders.js +27 -9
- package/dist/apps/shared/components/admin/settings/DomainDnsSetup.js +3 -0
- package/dist/apps/shared/components/admin/settings/EncryptionPolicyForm.js +25 -2
- package/dist/apps/shared/components/calendar/EventDetails.js +13 -2
- package/dist/apps/shared/components/calendar/EventEditor.js +115 -18
- package/dist/apps/shared/components/calendar/EventFormParts.js +1 -1
- package/dist/apps/shared/components/calendar/EventModal.d.ts +1 -1
- package/dist/apps/shared/components/calendar/EventModal.js +15 -2
- package/dist/apps/shared/components/calendar/RequestChangeForm.d.ts +3 -0
- package/dist/apps/shared/components/calendar/RequestChangeForm.js +1 -1
- package/dist/apps/shared/components/calendar/eventForm.d.ts +2 -0
- package/dist/apps/shared/components/contacts/ContactCard.js +12 -1
- package/dist/apps/shared/components/contacts/ContactDetailPane.d.ts +2 -1
- package/dist/apps/shared/components/contacts/ContactDetailPane.js +103 -9
- package/dist/apps/shared/components/contacts/ContactForm.d.ts +4 -1
- package/dist/apps/shared/components/contacts/ContactForm.js +37 -7
- package/dist/apps/shared/components/contacts/KeyChangeReview.d.ts +3 -1
- package/dist/apps/shared/components/contacts/KeyChangeReview.js +3 -2
- package/dist/apps/shared/components/contacts/contactCardData.js +8 -4
- package/dist/apps/shared/components/contacts/contactKeys.d.ts +1 -1
- package/dist/apps/shared/components/contacts/contactKeys.js +3 -3
- package/dist/apps/shared/components/layout/AppShell.d.ts +18 -1
- package/dist/apps/shared/components/layout/AppShell.js +17 -6
- package/dist/apps/shared/components/layout/KeyEnrollmentGate.js +27 -2
- package/dist/apps/shared/components/layout/SharedMailboxKeyManager.d.ts +17 -0
- package/dist/apps/shared/components/layout/SharedMailboxKeyManager.js +98 -0
- package/dist/apps/shared/components/layout/UnlockPromptProvider.d.ts +3 -0
- package/dist/apps/shared/components/layout/UnlockPromptProvider.js +32 -0
- package/dist/apps/shared/components/mail/ConversationList.js +1 -1
- package/dist/apps/shared/components/mail/InviteCard.js +11 -1
- package/dist/apps/shared/components/mail/MessageDetailPane.js +46 -21
- package/dist/apps/shared/components/mail/compose/ComposeWindow.js +2 -2
- package/dist/apps/shared/components/mail/compose/RecipientInput.js +3 -2
- package/dist/apps/shared/components/mail/compose/draftResume.d.ts +2 -0
- package/dist/apps/shared/components/mail/compose/draftResume.js +28 -3
- package/dist/apps/shared/components/mail/compose/protectedContactSuggestions.d.ts +14 -0
- package/dist/apps/shared/components/mail/compose/protectedContactSuggestions.js +96 -0
- package/dist/apps/shared/components/mail/compose/quotedBody.d.ts +1 -1
- package/dist/apps/shared/components/mail/compose/quotedBody.js +4 -3
- package/dist/apps/shared/components/mail/invite/inviteStore.js +16 -1
- package/dist/apps/shared/components/mail/layout/MailShell.js +2 -1
- package/dist/apps/shared/components/mail/pinnedSigners.d.ts +6 -0
- package/dist/apps/shared/components/mail/pinnedSigners.js +10 -1
- package/dist/apps/shared/components/mail/reading/conversationPrint.d.ts +2 -2
- package/dist/apps/shared/components/mail/reading/conversationPrint.js +3 -3
- package/dist/apps/shared/components/mail/reading/useMessageActions.js +4 -1
- package/dist/apps/shared/components/settings/KeyExportSetting.d.ts +13 -0
- package/dist/apps/shared/components/settings/KeyExportSetting.js +90 -0
- package/dist/apps/shared/components/settings/OpenPgpKeySetting.d.ts +13 -0
- package/dist/apps/shared/components/settings/OpenPgpKeySetting.js +143 -0
- package/dist/apps/shared/components/settings/PolicyLockedNotice.d.ts +11 -0
- package/dist/apps/shared/components/settings/PolicyLockedNotice.js +9 -0
- package/dist/apps/shared/components/settings/ZeroAccessSetting.d.ts +16 -0
- package/dist/apps/shared/components/settings/ZeroAccessSetting.js +80 -0
- package/dist/apps/shared/components/settings/ZeroAccessTypes.d.ts +29 -0
- package/dist/apps/shared/components/settings/ZeroAccessTypes.js +107 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.d.ts +3 -0
- package/dist/apps/shared/components/settings/layout/SettingsShell.js +3 -2
- package/dist/apps/shared/mail/decryptedMessages.d.ts +1 -1
- package/dist/apps/shared/mail/decryptedMessages.js +11 -3
- package/dist/apps/shared/mail/outbox/sendJob.d.ts +37 -4
- package/dist/apps/shared/mail/outbox/sendJob.js +287 -42
- package/dist/apps/shared/mail/outbox/sendOutcomes.d.ts +12 -0
- package/dist/apps/shared/mail/outbox/sendOutcomes.js +76 -20
- package/dist/apps/shared/mail/reportNotices.js +3 -1
- package/dist/apps/shared/mail/zeroAccessSource.d.ts +8 -0
- package/dist/apps/shared/mail/zeroAccessSource.js +26 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.d.ts +2 -0
- package/dist/apps/shared/search/LocalIndexLifecycle.js +74 -24
- package/dist/apps/shared/search/localIndexBuilder.d.ts +9 -1
- package/dist/apps/shared/search/localIndexBuilder.js +50 -17
- package/dist/apps/shared/search/localIndexRpcClient.d.ts +5 -39
- package/dist/apps/shared/search/localIndexRpcClient.js +129 -11
- package/dist/apps/shared/search/localIndexSchema.d.ts +14 -2
- package/dist/apps/shared/search/localIndexSchema.js +18 -4
- package/dist/apps/shared/search/localIndexTransport.d.ts +6 -2
- package/dist/apps/shared/search/localIndexWorker.d.ts +11 -0
- package/dist/apps/shared/search/localIndexWorker.js +22 -15
- package/dist/apps/shared/search/searchTier2.js +5 -0
- package/dist/apps/shared/search/zeroAccessIndexSync.d.ts +102 -0
- package/dist/apps/shared/search/zeroAccessIndexSync.js +544 -0
- package/dist/apps/shared/search/zeroAccessSyncState.d.ts +20 -0
- package/dist/apps/shared/search/zeroAccessSyncState.js +65 -0
- package/dist/apps/shared/zeroAccess/ProtectedMark.d.ts +7 -0
- package/dist/apps/shared/zeroAccess/ProtectedMark.js +10 -0
- package/dist/apps/shared/zeroAccess/useKeysLocked.d.ts +6 -0
- package/dist/apps/shared/zeroAccess/useKeysLocked.js +20 -0
- package/dist/apps/shared/zeroAccess/useOpenedRecord.d.ts +18 -0
- package/dist/apps/shared/zeroAccess/useOpenedRecord.js +66 -0
- package/dist/apps/www/_shell.d.ts +1 -1
- package/dist/apps/www/_shell.js +2 -2
- package/dist/apps/www/calendar/index.js +88 -20
- package/dist/apps/www/contacts/index.js +64 -11
- package/dist/apps/www/index.js +19 -3
- package/dist/apps/www/settings/encryption/index.js +16 -4
- package/dist/apps/www/settings/privacy/index.js +47 -6
- package/dist/apps/www/tasks/index.js +21 -7
- package/dist/lib/admin/domainsApi.d.ts +1 -1
- package/dist/lib/admin/escrowAccessRequestsApi.d.ts +6 -0
- package/dist/lib/calendar/calendarApi.d.ts +52 -6
- package/dist/lib/calendar/calendarApi.js +110 -17
- package/dist/lib/calendar/calendarMutations.d.ts +20 -7
- package/dist/lib/calendar/calendarMutations.js +73 -14
- package/dist/lib/calendar/inviteApi.d.ts +17 -3
- package/dist/lib/calendar/inviteApi.js +86 -7
- package/dist/lib/calendar/protectedEvents.d.ts +31 -0
- package/dist/lib/calendar/protectedEvents.js +113 -0
- package/dist/lib/calendar/recurrence.d.ts +2 -0
- package/dist/lib/calendar/recurrence.js +16 -0
- package/dist/lib/calendar/sealedInvites.d.ts +9 -0
- package/dist/lib/calendar/sealedInvites.js +65 -0
- package/dist/lib/contacts/contactsApi.d.ts +29 -3
- package/dist/lib/contacts/contactsApi.js +23 -7
- package/dist/lib/contacts/useContactPhotoSrc.d.ts +5 -2
- package/dist/lib/contacts/useContactPhotoSrc.js +91 -11
- package/dist/lib/crypto/composeSecurity.d.ts +11 -3
- package/dist/lib/crypto/composeSecurity.js +13 -5
- package/dist/lib/crypto/keyExport.d.ts +54 -0
- package/dist/lib/crypto/keyExport.js +161 -0
- package/dist/lib/crypto/keyPairMatch.d.ts +9 -0
- package/dist/lib/crypto/keyPairMatch.js +65 -0
- package/dist/lib/crypto/keySession.d.ts +64 -3
- package/dist/lib/crypto/keySession.js +179 -12
- package/dist/lib/crypto/keyvaultApi.d.ts +129 -8
- package/dist/lib/crypto/keyvaultApi.js +45 -3
- package/dist/lib/crypto/meetingKeys.d.ts +25 -0
- package/dist/lib/crypto/meetingKeys.js +87 -0
- package/dist/lib/crypto/messageSecurity.d.ts +56 -3
- package/dist/lib/crypto/messageSecurity.js +201 -13
- package/dist/lib/crypto/mime.d.ts +5 -0
- package/dist/lib/crypto/mime.js +25 -0
- package/dist/lib/crypto/openPgpKey.d.ts +39 -0
- package/dist/lib/crypto/openPgpKey.js +89 -0
- package/dist/lib/crypto/openpgp.d.ts +47 -0
- package/dist/lib/crypto/openpgp.js +88 -0
- package/dist/lib/crypto/pgpMessage.d.ts +51 -0
- package/dist/lib/crypto/pgpMessage.js +271 -0
- package/dist/lib/crypto/sharedMailboxKeys.d.ts +62 -0
- package/dist/lib/crypto/sharedMailboxKeys.js +337 -0
- package/dist/lib/crypto/smimeMessage.d.ts +21 -0
- package/dist/lib/crypto/smimeMessage.js +11 -7
- package/dist/lib/crypto/zeroAccessKey.d.ts +32 -0
- package/dist/lib/crypto/zeroAccessKey.js +71 -0
- package/dist/lib/mail/conversationsApi.d.ts +6 -0
- package/dist/lib/mail/mailApi.d.ts +63 -2
- package/dist/lib/mail/mailApi.js +5 -1
- package/dist/lib/mail/mailboxImportApi.d.ts +10 -0
- package/dist/lib/mail/mailboxImportApi.js +15 -0
- package/dist/lib/search/zeroAccessIndexApi.d.ts +85 -0
- package/dist/lib/search/zeroAccessIndexApi.js +35 -0
- package/dist/lib/tasks/tasksApi.d.ts +11 -1
- package/dist/lib/tasks/tasksApi.js +10 -4
- package/dist/lib/util/clientPlatform.d.ts +14 -0
- package/dist/lib/util/clientPlatform.js +26 -0
- package/dist/lib/videoconf/protectedMeetings.d.ts +74 -0
- package/dist/lib/videoconf/protectedMeetings.js +181 -0
- package/dist/lib/videoconf/videoMeetingsApi.d.ts +25 -4
- package/dist/lib/videoconf/videoMeetingsApi.js +1 -1
- package/dist/lib/zeroAccess/sealedMoves.d.ts +41 -0
- package/dist/lib/zeroAccess/sealedMoves.js +63 -0
- package/dist/lib/zeroAccess/sealedRecords.d.ts +29 -0
- package/dist/lib/zeroAccess/sealedRecords.js +136 -0
- package/lib/admin/domainsApi.ts +1 -1
- package/lib/admin/escrowAccessRequestsApi.ts +6 -0
- package/lib/calendar/calendarApi.ts +143 -17
- package/lib/calendar/calendarMutations.ts +79 -12
- package/lib/calendar/inviteApi.ts +101 -8
- package/lib/calendar/protectedEvents.ts +143 -0
- package/lib/calendar/recurrence.ts +17 -0
- package/lib/calendar/sealedInvites.ts +73 -0
- package/lib/contacts/contactsApi.ts +43 -17
- package/lib/contacts/useContactPhotoSrc.ts +103 -13
- package/lib/crypto/composeSecurity.ts +20 -5
- package/lib/crypto/keyExport.ts +229 -0
- package/lib/crypto/keyPairMatch.ts +69 -0
- package/lib/crypto/keySession.ts +216 -12
- package/lib/crypto/keyvaultApi.ts +174 -10
- package/lib/crypto/meetingKeys.ts +118 -0
- package/lib/crypto/messageSecurity.ts +284 -16
- package/lib/crypto/mime.ts +26 -0
- package/lib/crypto/openPgpKey.ts +118 -0
- package/lib/crypto/openpgp.ts +115 -0
- package/lib/crypto/pgpMessage.ts +336 -0
- package/lib/crypto/sharedMailboxKeys.ts +402 -0
- package/lib/crypto/smimeMessage.ts +11 -7
- package/lib/crypto/zeroAccessKey.ts +102 -0
- package/lib/mail/conversationsApi.ts +3 -0
- package/lib/mail/mailApi.ts +60 -3
- package/lib/mail/mailboxImportApi.ts +24 -0
- package/lib/search/zeroAccessIndexApi.ts +123 -0
- package/lib/tasks/tasksApi.ts +19 -5
- package/lib/util/clientPlatform.ts +50 -0
- package/lib/videoconf/protectedMeetings.ts +221 -0
- package/lib/videoconf/videoMeetingsApi.ts +15 -4
- package/lib/zeroAccess/sealedMoves.ts +79 -0
- package/lib/zeroAccess/sealedRecords.ts +153 -0
- package/package.json +14 -5
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { ApiClient, ApiRequestError } from "../util/api.js";
|
|
12
12
|
import { ListParams } from "../util/apiQuery.js";
|
|
13
|
-
import type { EncryptionPreference, PublicKey } from "../crypto/keyvaultApi.js";
|
|
13
|
+
import type { EncryptionPreference, PublicKey, ZeroAccessKey } from "../crypto/keyvaultApi.js";
|
|
14
14
|
import type { ResolvedPrincipal } from "./mailboxAccessApi.js";
|
|
15
15
|
export type { ListParams };
|
|
16
16
|
export type { EncryptionPreference, PublicKey };
|
|
@@ -95,6 +95,18 @@ export interface Mailbox {
|
|
|
95
95
|
* per the spec's own `PublicKey` doc comment. Absent/empty means no keys enrolled yet
|
|
96
96
|
* (`KeyEnrollmentGate` handles that state). */
|
|
97
97
|
keys?: PublicKey[];
|
|
98
|
+
/** The mailbox's zero-access certificates, managed by the server (never written by a client). Read them from
|
|
99
|
+
* `KeyVault.zeroAccessKeys` to unlock; here only so a settings page can tell whether one is enrolled. */
|
|
100
|
+
zeroAccessKeys?: ZeroAccessKey[];
|
|
101
|
+
/** "Enable zero-access encryption": unset means on. Only the mailbox's owner can change it - restapi drops anybody
|
|
102
|
+
* else's value. Has no effect while the server's `EncryptionPolicy.allowZeroAccess` is off. */
|
|
103
|
+
zeroAccessEnabled?: boolean;
|
|
104
|
+
/** Which data types zero-access encryption covers - see `ZeroAccessTypes`. Owner only. */
|
|
105
|
+
zeroAccessTypes?: ZeroAccessTypes;
|
|
106
|
+
/** The ActiveSync and Outlook clients that sync this mailbox, as the server last saw them. Server-written. */
|
|
107
|
+
syncClients?: SyncClients;
|
|
108
|
+
/** When the owner last exported a zero-access key for a phone or Outlook. Server-written. */
|
|
109
|
+
zeroAccessKeysExportedAt?: number;
|
|
98
110
|
/** How the caller reaches this mailbox: `"owner"` (theirs) or `"delegate"` (shared with them). Set by the plain list and read of
|
|
99
111
|
* mailboxes - not in an administration-scope answer - so a client can label the shared ones; see `isSharedWithMe()`. */
|
|
100
112
|
accessRole?: "owner" | "delegate";
|
|
@@ -191,6 +203,25 @@ export declare function autoProvisionMailbox(selection?: {
|
|
|
191
203
|
* `null` clears them - `JSON.stringify()` drops an `undefined` value entirely, which would instead leave
|
|
192
204
|
* the old value in place.
|
|
193
205
|
*/
|
|
206
|
+
/** The data types zero-access encryption can cover (restapi `specs/search.md` §3 *What It Covers*). */
|
|
207
|
+
export type ZeroAccessDataType = "mail" | "calendar" | "contacts" | "tasks";
|
|
208
|
+
/** `Mailbox.zeroAccessTypes`: per data type, whether zero-access covers it. A type left out is undecided: mail counts as on, the others
|
|
209
|
+
* as off, until the server decides them from the owner's devices. */
|
|
210
|
+
export type ZeroAccessTypes = Partial<Record<ZeroAccessDataType, boolean>>;
|
|
211
|
+
/** `Mailbox.syncClients`: the ActiveSync devices (each with the data types it syncs) and Outlook. */
|
|
212
|
+
export interface SyncClients {
|
|
213
|
+
activesync?: {
|
|
214
|
+
devices: {
|
|
215
|
+
id: string;
|
|
216
|
+
model?: string;
|
|
217
|
+
types: ZeroAccessDataType[];
|
|
218
|
+
lastSeenAt: number;
|
|
219
|
+
}[];
|
|
220
|
+
};
|
|
221
|
+
mapi?: {
|
|
222
|
+
lastSeenAt: number;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
194
225
|
export interface UpdateMailboxInput {
|
|
195
226
|
uid: string;
|
|
196
227
|
version: number;
|
|
@@ -215,6 +246,12 @@ export interface UpdateMailboxInput {
|
|
|
215
246
|
autoSendReceiptsInternal?: boolean;
|
|
216
247
|
autoSendReceiptsFederated?: boolean;
|
|
217
248
|
autoSendReceiptsExternal?: boolean;
|
|
249
|
+
/** Owner-only server-side (anybody else's value is dropped) - see `Mailbox.zeroAccessEnabled`. */
|
|
250
|
+
zeroAccessEnabled?: boolean;
|
|
251
|
+
/** Owner-only server-side; a partial choice is merged into the stored one - see `Mailbox.zeroAccessTypes`. */
|
|
252
|
+
zeroAccessTypes?: ZeroAccessTypes;
|
|
253
|
+
/** The mailbox's own encryption preference, replaced whole - see `Mailbox.encryptPreference`. */
|
|
254
|
+
encryptPreference?: EncryptionPreference;
|
|
218
255
|
/** Trusted-caller-only server-side. `null` (or `""`) unassigns the mailbox's escrow scope; the
|
|
219
256
|
* referenced `EscrowScope` must exist otherwise. See `Mailbox.escrowScopeId`. */
|
|
220
257
|
escrowScopeId?: string | null;
|
|
@@ -424,6 +461,13 @@ export interface Message {
|
|
|
424
461
|
* stored body, not from a flag this API exposes. Always present on a real `Message` (defaults
|
|
425
462
|
* `false` server-side), same optional-for-old-fixtures caveat as `Mailbox.oofEnabled`. */
|
|
426
463
|
encrypted?: boolean;
|
|
464
|
+
/** Set when the server sealed this message at rest on arrival (zero-access, restapi `specs/search.md` §3): its stored body is an
|
|
465
|
+
* S/MIME message only the mailbox's zero-access key opens, `subject` is `[...]` and `bodyPreview` empty - the real ones are in
|
|
466
|
+
* `sealedSummary`. */
|
|
467
|
+
zeroAccess?: {
|
|
468
|
+
keyFingerprint: string;
|
|
469
|
+
sealedSummary: string;
|
|
470
|
+
};
|
|
427
471
|
/** The raw RFC 2369/8058 `List-Unsubscribe` header value, if present — computed server-side by
|
|
428
472
|
* `@rapidmx/restapi`'s `ScanPipeline` from the stored MIME, the strongest single bulk-mail signal it
|
|
429
473
|
* captures. Used client-side to detect list traffic when replying, per
|
|
@@ -560,7 +604,7 @@ export type MessageReportKind = "junk" | "phishing" | "not_junk";
|
|
|
560
604
|
/** Why the server did not teach its spam filter from a report: `encrypted` (it has no plaintext to teach), `disabled` (learning is switched off),
|
|
561
605
|
* `unsupported` (no spam engine, or one that cannot learn), `too_large` (over the size it reads) or `failed` (the engine could not be reached,
|
|
562
606
|
* refused, or the message could not be read). The report itself still happened. */
|
|
563
|
-
export type MessageLearnSkipped = "encrypted" | "disabled" | "unsupported" | "too_large" | "failed";
|
|
607
|
+
export type MessageLearnSkipped = "encrypted" | "zero_access" | "disabled" | "unsupported" | "too_large" | "failed";
|
|
564
608
|
/** What `reportMessage()` answers. */
|
|
565
609
|
export interface MessageReportResult {
|
|
566
610
|
uid: string;
|
|
@@ -581,6 +625,9 @@ export interface ReportMessageOptions {
|
|
|
581
625
|
/** With `not_junk` only (else a 400): also add the message's From address to the mailbox's safe senders. Needs full access to the mailbox (403 otherwise,
|
|
582
626
|
* before anything is done). */
|
|
583
627
|
alwaysTrustSender?: boolean;
|
|
628
|
+
/** For a zero-access message only (else a 400): its original source, base64-encoded, decrypted here - the server holds only the sealed
|
|
629
|
+
* copy, so this is what its spam filter learns from (never stored). Without it the server skips learning (`learnSkipped: "zero_access"`). */
|
|
630
|
+
rawMessage?: string;
|
|
584
631
|
}
|
|
585
632
|
/**
|
|
586
633
|
* Reports a message as junk, phishing or not junk - `POST /mail/messages/:uid/report`. The SERVER moves it (`junk` and `phishing` to the mailbox's Junk Email,
|
|
@@ -708,7 +755,19 @@ export interface AssembleDraftInput {
|
|
|
708
755
|
bcc?: ComposeRecipientInput[];
|
|
709
756
|
subject?: string;
|
|
710
757
|
html: string;
|
|
758
|
+
/** See `AssembleIntent`. */
|
|
759
|
+
intent?: AssembleIntent;
|
|
760
|
+
/** The addresses this copy is delivered to, when not all of `to`/`cc`/`bcc`: one copy of a message sent in two end-to-end
|
|
761
|
+
* formats (an S/MIME and an OpenPGP copy, each listing every recipient) goes only to the recipients of its own format. Each
|
|
762
|
+
* must be one of the recipients. */
|
|
763
|
+
deliverTo?: string[];
|
|
711
764
|
}
|
|
765
|
+
/**
|
|
766
|
+
* Why a draft is being assembled (server `ComposeIntent`). `"save"`, the default, keeps an edit: in a zero-access mailbox the server
|
|
767
|
+
* stores it sealed, which it can't send. `"send"` is the assemble right before `sendMessage()`/`queueMessageSend()`: stored readable
|
|
768
|
+
* until it is relayed, then sealed as the Sent Items copy.
|
|
769
|
+
*/
|
|
770
|
+
export type AssembleIntent = "save" | "send";
|
|
712
771
|
/** What a draft records about the thread it belongs to - see `createDraft()`. */
|
|
713
772
|
export interface DraftThreading {
|
|
714
773
|
/** The `messageId` of the message being replied to (`Message.inReplyTo`). */
|
|
@@ -774,6 +833,8 @@ export interface AssembleDraftRawInput {
|
|
|
774
833
|
* `crypto/smimeMessage.ts`'s `buildSignedOnlyMessage()`/`buildEncryptedMessage()`, combined with
|
|
775
834
|
* the outer envelope headers (`From`/`To`/`Cc`/`Date`/`Message-ID`/`MIME-Version`) by the caller. */
|
|
776
835
|
rawMime: string;
|
|
836
|
+
/** See `AssembleIntent`. */
|
|
837
|
+
intent?: AssembleIntent;
|
|
777
838
|
}
|
|
778
839
|
/**
|
|
779
840
|
* Stores an already-signed/encrypted draft's raw MIME source as its `bodyBlobKey`, completely
|
package/dist/lib/mail/mailApi.js
CHANGED
|
@@ -274,7 +274,11 @@ export function moveMessage(message, folderUid, client) {
|
|
|
274
274
|
export function reportMessage(uid, kind, options = {}, client) {
|
|
275
275
|
return withClient(client, `/mail/messages/${encodeURIComponent(uid)}/report`, {
|
|
276
276
|
method: "POST",
|
|
277
|
-
body: JSON.stringify({
|
|
277
|
+
body: JSON.stringify({
|
|
278
|
+
kind,
|
|
279
|
+
...(options.alwaysTrustSender ? { alwaysTrustSender: true } : {}),
|
|
280
|
+
...(options.rawMessage ? { rawMessage: options.rawMessage } : {}),
|
|
281
|
+
}),
|
|
278
282
|
});
|
|
279
283
|
}
|
|
280
284
|
/**
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { ApiClient } from "../util/api.js";
|
|
9
9
|
import { RequestListParams } from "../util/apiQuery.js";
|
|
10
|
+
import type { Mailbox } from "./mailApi.js";
|
|
10
11
|
export type { RequestListParams };
|
|
11
12
|
export type MailboxImportFormat = "mbox" | "pst";
|
|
12
13
|
export type MailboxImportStatus = "pending" | "processing" | "completed" | "failed";
|
|
@@ -51,3 +52,12 @@ export declare function uploadMailboxImport(file: File, input: UploadMailboxImpo
|
|
|
51
52
|
* first; `params` pages through them (`limit` capped at 500 server-side). */
|
|
52
53
|
export declare function listImportRequests(params?: RequestListParams, client?: ApiClient): Promise<MailboxImportRequest[]>;
|
|
53
54
|
export declare function getImportRequest(uid: string, client?: ApiClient): Promise<MailboxImportRequest>;
|
|
55
|
+
/** Why server-side import is refused for a mailbox whose mail is zero-access (restapi answers such an upload 409). */
|
|
56
|
+
export declare const ZERO_ACCESS_IMPORT_MESSAGE: string;
|
|
57
|
+
/**
|
|
58
|
+
* Whether `mailbox`'s mail is stored zero-access encrypted, so restapi refuses a server-side import into it (`specs/search.md` §3: the
|
|
59
|
+
* uploaded archive would wait readable in the blob store). Decided as restapi's `zeroAccessStatus()` decides it for mail: the
|
|
60
|
+
* administrator's "Allow zero-access encryption" (`allowZeroAccess`; unknown counts as allowed, so an import is never offered only to be
|
|
61
|
+
* refused), the owner's switch (unset is on), mail among the protected types (undecided is on), and a key that isn't revoked.
|
|
62
|
+
*/
|
|
63
|
+
export declare function mailIsZeroAccess(mailbox: Pick<Mailbox, "zeroAccessEnabled" | "zeroAccessTypes" | "zeroAccessKeys">, allowZeroAccess: boolean | undefined): boolean;
|
|
@@ -37,3 +37,18 @@ export function listImportRequests(params = {}, client) {
|
|
|
37
37
|
export function getImportRequest(uid, client) {
|
|
38
38
|
return withClient(client, `/mail/mailbox-import-requests/${encodeURIComponent(uid)}`);
|
|
39
39
|
}
|
|
40
|
+
/** Why server-side import is refused for a mailbox whose mail is zero-access (restapi answers such an upload 409). */
|
|
41
|
+
export const ZERO_ACCESS_IMPORT_MESSAGE = "Importing an archive isn't available while zero-access encryption protects this mailbox's mail: the server would have to keep " +
|
|
42
|
+
"the uploaded archive readable until the import ran, which zero-access encryption doesn't allow.";
|
|
43
|
+
/**
|
|
44
|
+
* Whether `mailbox`'s mail is stored zero-access encrypted, so restapi refuses a server-side import into it (`specs/search.md` §3: the
|
|
45
|
+
* uploaded archive would wait readable in the blob store). Decided as restapi's `zeroAccessStatus()` decides it for mail: the
|
|
46
|
+
* administrator's "Allow zero-access encryption" (`allowZeroAccess`; unknown counts as allowed, so an import is never offered only to be
|
|
47
|
+
* refused), the owner's switch (unset is on), mail among the protected types (undecided is on), and a key that isn't revoked.
|
|
48
|
+
*/
|
|
49
|
+
export function mailIsZeroAccess(mailbox, allowZeroAccess) {
|
|
50
|
+
return (allowZeroAccess !== false &&
|
|
51
|
+
mailbox.zeroAccessEnabled !== false &&
|
|
52
|
+
mailbox.zeroAccessTypes?.mail !== false &&
|
|
53
|
+
(mailbox.zeroAccessKeys ?? []).some((key) => !key.revokedAt));
|
|
54
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed wrappers over `@rapidmx/restapi`'s zero-access search index (`/mail/mailboxes/:id/search-index`, `specs/search.md` §7-8): the
|
|
3
|
+
* sealed index entries the server writes at delivery and the weekly segments clients compact them into. Everything here is
|
|
4
|
+
* ciphertext encrypted to the mailbox's zero-access key - `apps/shared/search/zeroAccessIndexSync.ts` decrypts and imports it.
|
|
5
|
+
*/
|
|
6
|
+
import { type ApiClient } from "../util/api.js";
|
|
7
|
+
export type SegmentOrigin = "zero-access" | "client";
|
|
8
|
+
/** A compacted segment as the manifest lists it. */
|
|
9
|
+
export interface IndexSegment {
|
|
10
|
+
weekId: string;
|
|
11
|
+
generation: number;
|
|
12
|
+
origin: SegmentOrigin;
|
|
13
|
+
bytes: number;
|
|
14
|
+
etag: string;
|
|
15
|
+
schemaVersion: number;
|
|
16
|
+
keyFingerprint: string;
|
|
17
|
+
entryCount: number;
|
|
18
|
+
}
|
|
19
|
+
/** A week's entries not yet compacted. */
|
|
20
|
+
export interface IndexWeekEntries {
|
|
21
|
+
weekId: string;
|
|
22
|
+
pendingEntries: number;
|
|
23
|
+
latestEntryAt: string;
|
|
24
|
+
/** The week has closed and its grace period has passed: a client may compact it. */
|
|
25
|
+
compactable: boolean;
|
|
26
|
+
/** When the week becomes (or became) compactable - where the platform stagger starts. Absent from a server before it. */
|
|
27
|
+
compactableAt?: string;
|
|
28
|
+
/** Size of the week's pending sealed entries (bytes of base64). Absent from a server before it. */
|
|
29
|
+
pendingBytes?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface SegmentManifest {
|
|
32
|
+
mailboxUid: string;
|
|
33
|
+
segments: IndexSegment[];
|
|
34
|
+
weeks: IndexWeekEntries[];
|
|
35
|
+
}
|
|
36
|
+
/** One pending entry: `sealed` is base64 CMS `EnvelopedData` of a gzipped JSON `ZeroAccessIndexDocument`. */
|
|
37
|
+
export interface SealedIndexEntry {
|
|
38
|
+
uid: string;
|
|
39
|
+
weekId: string;
|
|
40
|
+
messageUid: string;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
keyFingerprint: string;
|
|
43
|
+
sealed: string;
|
|
44
|
+
}
|
|
45
|
+
export interface SealedIndexEntryPage {
|
|
46
|
+
entries: SealedIndexEntry[];
|
|
47
|
+
next?: string;
|
|
48
|
+
}
|
|
49
|
+
/** What a sealed entry (and each document of a segment) decrypts to - restapi's `ZeroAccessIndexDocument`. */
|
|
50
|
+
export interface ZeroAccessIndexDocument {
|
|
51
|
+
v: 1;
|
|
52
|
+
messageUid: string;
|
|
53
|
+
subject: string;
|
|
54
|
+
body: string;
|
|
55
|
+
attachmentText: string[];
|
|
56
|
+
attachmentNames: string[];
|
|
57
|
+
deferredAttachments: string[];
|
|
58
|
+
from: string;
|
|
59
|
+
to: string[];
|
|
60
|
+
cc: string[];
|
|
61
|
+
participants: string[];
|
|
62
|
+
dateForSort: number;
|
|
63
|
+
}
|
|
64
|
+
/** `PUT .../segments/:weekId`. */
|
|
65
|
+
export interface SegmentUpload {
|
|
66
|
+
expectedGeneration: number;
|
|
67
|
+
/** Base64 of the sealed file. */
|
|
68
|
+
segment: string;
|
|
69
|
+
keyFingerprint: string;
|
|
70
|
+
schemaVersion: number;
|
|
71
|
+
origin?: SegmentOrigin;
|
|
72
|
+
foldedEntryUids?: string[];
|
|
73
|
+
}
|
|
74
|
+
export declare function getSearchIndexManifest(mailboxUid: string, client?: ApiClient): Promise<SegmentManifest>;
|
|
75
|
+
/** One page of pending entries: of `week` when given, written at or after `since` when given, after uid `after`. */
|
|
76
|
+
export declare function listSearchIndexEntries(mailboxUid: string, options?: {
|
|
77
|
+
week?: string;
|
|
78
|
+
since?: string;
|
|
79
|
+
after?: string;
|
|
80
|
+
limit?: number;
|
|
81
|
+
}, client?: ApiClient): Promise<SealedIndexEntryPage>;
|
|
82
|
+
/** The sealed file of a week's segment generation. */
|
|
83
|
+
export declare function getSearchIndexSegment(mailboxUid: string, segment: Pick<IndexSegment, "weekId" | "generation" | "origin">, client?: ApiClient): Promise<Uint8Array>;
|
|
84
|
+
/** Stores a compaction. Rejects with a `409` when the week's segment changed since `upload.expectedGeneration` was read. */
|
|
85
|
+
export declare function putSearchIndexSegment(mailboxUid: string, weekId: string, upload: SegmentUpload, client?: ApiClient): Promise<IndexSegment>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* Typed wrappers over `@rapidmx/restapi`'s zero-access search index (`/mail/mailboxes/:id/search-index`, `specs/search.md` §7-8): the
|
|
7
|
+
* sealed index entries the server writes at delivery and the weekly segments clients compact them into. Everything here is
|
|
8
|
+
* ciphertext encrypted to the mailbox's zero-access key - `apps/shared/search/zeroAccessIndexSync.ts` decrypts and imports it.
|
|
9
|
+
*/
|
|
10
|
+
import { withClient, withClientBlob } from "../util/api.js";
|
|
11
|
+
const base = (mailboxUid) => `/mail/mailboxes/${encodeURIComponent(mailboxUid)}/search-index`;
|
|
12
|
+
export function getSearchIndexManifest(mailboxUid, client) {
|
|
13
|
+
return withClient(client, `${base(mailboxUid)}/manifest`);
|
|
14
|
+
}
|
|
15
|
+
/** One page of pending entries: of `week` when given, written at or after `since` when given, after uid `after`. */
|
|
16
|
+
export function listSearchIndexEntries(mailboxUid, options = {}, client) {
|
|
17
|
+
const query = new URLSearchParams();
|
|
18
|
+
for (const [key, value] of Object.entries(options)) {
|
|
19
|
+
if (value !== undefined) {
|
|
20
|
+
query.set(key, String(value));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const search = query.toString();
|
|
24
|
+
return withClient(client, `${base(mailboxUid)}/entries${search ? `?${search}` : ""}`);
|
|
25
|
+
}
|
|
26
|
+
/** The sealed file of a week's segment generation. */
|
|
27
|
+
export async function getSearchIndexSegment(mailboxUid, segment, client) {
|
|
28
|
+
const query = `generation=${segment.generation}${segment.origin === "client" ? "&origin=client" : ""}`;
|
|
29
|
+
const blob = await withClientBlob(client, `${base(mailboxUid)}/segments/${encodeURIComponent(segment.weekId)}?${query}`, "Could not download a search index segment.");
|
|
30
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
31
|
+
}
|
|
32
|
+
/** Stores a compaction. Rejects with a `409` when the week's segment changed since `upload.expectedGeneration` was read. */
|
|
33
|
+
export function putSearchIndexSegment(mailboxUid, weekId, upload, client) {
|
|
34
|
+
return withClient(client, `${base(mailboxUid)}/segments/${encodeURIComponent(weekId)}`, { method: "PUT", body: JSON.stringify(upload) });
|
|
35
|
+
}
|
|
@@ -24,6 +24,9 @@ export interface Task {
|
|
|
24
24
|
myDay?: boolean;
|
|
25
25
|
/** The uid of the user this task is assigned to, if any. */
|
|
26
26
|
assignedTo?: string;
|
|
27
|
+
/** The title and notes sealed to the mailbox's zero-access key while it protects tasks (`title` is then a placeholder) - opened with
|
|
28
|
+
* `openSealedRecords()`. Server-written. */
|
|
29
|
+
sealedFields?: Record<string, string> | null;
|
|
27
30
|
}
|
|
28
31
|
/** `client`, given by every function below, is an explicit `ApiClient` from `createApiClient()` (e.g. one
|
|
29
32
|
* account of a multi-account app) to call instead of the default global `apiFetch()` - see `withClient()`'s
|
|
@@ -56,8 +59,15 @@ export interface UpdateTaskInput {
|
|
|
56
59
|
taskListUid?: string;
|
|
57
60
|
myDay?: boolean;
|
|
58
61
|
assignedTo?: string;
|
|
62
|
+
/** A move to another mailbox, with `folderUid` there - refused for a protected task (`SealedMoveError`). */
|
|
63
|
+
mailboxUid?: string;
|
|
64
|
+
folderUid?: string;
|
|
59
65
|
}
|
|
60
|
-
|
|
66
|
+
/** Saves `input`. One moving the task to another mailbox (`mailboxUid`) is refused with `SealedMoveError` when the task is protected -
|
|
67
|
+
* before sending anything when `current` (the task as this device has it) says so, else from the server's refusal. With `current`, a
|
|
68
|
+
* protected task's sealed fields go only when they changed from it, and an emptied one is named in `clearedFields` when `current` was
|
|
69
|
+
* opened here (`protectedRecordWrite()`). */
|
|
70
|
+
export declare function updateTask(input: UpdateTaskInput, client?: ApiClient, current?: Task): Promise<Task>;
|
|
61
71
|
/** Toggles a task's `completed` flag in place — same pattern as `mailApi.ts`'s `setMessageRead`. */
|
|
62
72
|
export declare function setTaskCompleted(task: Task, completed: boolean, client?: ApiClient): Promise<Task>;
|
|
63
73
|
/** Adds/removes a task from the caller's curated "My Day" working set — same thin-wrapper pattern as
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* comment for the shared ACL/authorization model every wrapper file here follows. */
|
|
7
7
|
import { withClient } from "../util/api.js";
|
|
8
8
|
import { SETTINGS_LIST_LIMIT, buildQuery } from "../util/apiQuery.js";
|
|
9
|
+
import { guardSealedMove } from "../zeroAccess/sealedMoves.js";
|
|
10
|
+
import { protectedRecordWrite } from "../zeroAccess/sealedRecords.js";
|
|
9
11
|
/** `client`, given by every function below, is an explicit `ApiClient` from `createApiClient()` (e.g. one
|
|
10
12
|
* account of a multi-account app) to call instead of the default global `apiFetch()` - see `withClient()`'s
|
|
11
13
|
* own doc comment in `util/api.ts`. Omitted (the default), every function here behaves exactly as before. */
|
|
@@ -22,11 +24,15 @@ export function createTask(input, client) {
|
|
|
22
24
|
body: JSON.stringify({ completed: false, priority: "normal", ...input }),
|
|
23
25
|
});
|
|
24
26
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
/** Saves `input`. One moving the task to another mailbox (`mailboxUid`) is refused with `SealedMoveError` when the task is protected -
|
|
28
|
+
* before sending anything when `current` (the task as this device has it) says so, else from the server's refusal. With `current`, a
|
|
29
|
+
* protected task's sealed fields go only when they changed from it, and an emptied one is named in `clearedFields` when `current` was
|
|
30
|
+
* opened here (`protectedRecordWrite()`). */
|
|
31
|
+
export function updateTask(input, client, current) {
|
|
32
|
+
return guardSealedMove(current, input.mailboxUid, () => withClient(client, `/mail/tasks/${encodeURIComponent(input.uid)}`, {
|
|
27
33
|
method: "PUT",
|
|
28
|
-
body: JSON.stringify(input),
|
|
29
|
-
});
|
|
34
|
+
body: JSON.stringify(protectedRecordWrite(current, input)),
|
|
35
|
+
}));
|
|
30
36
|
}
|
|
31
37
|
/** Toggles a task's `completed` flag in place — same pattern as `mailApi.ts`'s `setMessageRead`. */
|
|
32
38
|
export function setTaskCompleted(task, completed, client) {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of client this is, for work that should prefer one over another - the search index's week compaction, which a phone
|
|
3
|
+
* should leave to a computer (restapi `specs/search.md` §8 *Who compacts*).
|
|
4
|
+
*
|
|
5
|
+
* - `"desktop"`: a native app on a computer (`tauri-client`, `electron-client`), which installs `window.rapidmx`.
|
|
6
|
+
* - `"mobile"`: a native app on a phone or tablet (the host sets `window.rapidmx.formFactor: "mobile"`), or a phone or tablet browser.
|
|
7
|
+
* - `"web"`: any other browser.
|
|
8
|
+
*/
|
|
9
|
+
export type ClientPlatform = "desktop" | "web" | "mobile";
|
|
10
|
+
/** This client's platform (see the module comment). */
|
|
11
|
+
export declare function clientPlatform(): ClientPlatform;
|
|
12
|
+
/** Whether the connection is one to spare: cellular, or the user asked to save data (Network Information API, where the browser
|
|
13
|
+
* has it - absent, the connection is taken as unconstrained). */
|
|
14
|
+
export declare function isConstrainedConnection(): boolean;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const MOBILE_USER_AGENT = /Android|iPhone|iPad|iPod|Mobile/i;
|
|
2
|
+
function navigatorHints() {
|
|
3
|
+
return typeof navigator === "undefined" ? undefined : navigator;
|
|
4
|
+
}
|
|
5
|
+
/** Whether this browser runs on a phone or tablet, by its User-Agent Client Hints, else its user agent string. */
|
|
6
|
+
function isMobileBrowser() {
|
|
7
|
+
const hints = navigatorHints();
|
|
8
|
+
if (typeof hints?.userAgentData?.mobile === "boolean") {
|
|
9
|
+
return hints.userAgentData.mobile;
|
|
10
|
+
}
|
|
11
|
+
return MOBILE_USER_AGENT.test(hints?.userAgent ?? "");
|
|
12
|
+
}
|
|
13
|
+
/** This client's platform (see the module comment). */
|
|
14
|
+
export function clientPlatform() {
|
|
15
|
+
const host = typeof window === "undefined" ? undefined : window.rapidmx;
|
|
16
|
+
if (host) {
|
|
17
|
+
return host.formFactor === "mobile" ? "mobile" : "desktop";
|
|
18
|
+
}
|
|
19
|
+
return isMobileBrowser() ? "mobile" : "web";
|
|
20
|
+
}
|
|
21
|
+
/** Whether the connection is one to spare: cellular, or the user asked to save data (Network Information API, where the browser
|
|
22
|
+
* has it - absent, the connection is taken as unconstrained). */
|
|
23
|
+
export function isConstrainedConnection() {
|
|
24
|
+
const connection = navigatorHints()?.connection;
|
|
25
|
+
return connection?.saveData === true || connection?.type === "cellular";
|
|
26
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ApiClient } from "../util/api.js";
|
|
2
|
+
import { type VideoMeetingDetail, type VideoMeetingInvitee } from "./videoMeetingsApi.js";
|
|
3
|
+
/** How long after the meeting ends its key is kept (`mail:videoconf:key_grace_minutes`' default). */
|
|
4
|
+
export declare const MEETING_KEY_GRACE_MS: number;
|
|
5
|
+
/** What `createVideoMeeting()` sends to protect a meeting. */
|
|
6
|
+
export interface MeetingProtectionInput {
|
|
7
|
+
meetingKey: {
|
|
8
|
+
certificate: string;
|
|
9
|
+
};
|
|
10
|
+
keyExpiresAt: string;
|
|
11
|
+
keyWraps: {
|
|
12
|
+
mailboxUid: string;
|
|
13
|
+
recipientFingerprint: string;
|
|
14
|
+
kind: "meeting-key";
|
|
15
|
+
cms: string;
|
|
16
|
+
}[];
|
|
17
|
+
sealedFields: {
|
|
18
|
+
title: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** A protected meeting's fields, as the server returns them to its organizer. */
|
|
22
|
+
export interface ProtectedMeetingFields {
|
|
23
|
+
keyProtected?: boolean | null;
|
|
24
|
+
keyWraps?: {
|
|
25
|
+
mailboxUid: string;
|
|
26
|
+
recipientFingerprint: string;
|
|
27
|
+
kind: string;
|
|
28
|
+
cms: string;
|
|
29
|
+
}[] | null;
|
|
30
|
+
/** When the meeting key is discarded (ISO). */
|
|
31
|
+
keyExpiresAt?: string | null;
|
|
32
|
+
}
|
|
33
|
+
/** Forgets every opened meeting key (sign-out, or the test seam). */
|
|
34
|
+
export declare function clearMeetingKeys(): void;
|
|
35
|
+
/** When a meeting ending at `endDate` - the last occurrence's end, for a series that has one - has its key discarded. */
|
|
36
|
+
export declare function meetingKeyExpiry(endDate: Date, now?: number): Date;
|
|
37
|
+
/** What protects a new meeting of `mailboxUid` titled `title` with `invitees`, its key discarded at `expiresAt` - or `undefined` when
|
|
38
|
+
* the mailbox has no zero-access key unlocked here (the meeting is then not protected). `fragment` is every participant's link key. */
|
|
39
|
+
export declare function protectMeeting(mailboxUid: string, title: string, invitees: VideoMeetingInvitee[], expiresAt: Date): Promise<{
|
|
40
|
+
protection: MeetingProtectionInput;
|
|
41
|
+
invitees: {
|
|
42
|
+
email: string;
|
|
43
|
+
sealedFields?: {
|
|
44
|
+
displayName: string;
|
|
45
|
+
};
|
|
46
|
+
}[];
|
|
47
|
+
fragment: string;
|
|
48
|
+
} | undefined>;
|
|
49
|
+
/** Remembers a just-created meeting's link key - `null` for one that isn't protected - so the save that invites to it needn't fetch the
|
|
50
|
+
* meeting or reopen its wrap. `mailboxUid` is the organizer's mailbox: its lock forgets the key. */
|
|
51
|
+
export declare function rememberMeetingKey(meetingUid: string, fragment: string | null, mailboxUid: string): void;
|
|
52
|
+
/** `meeting`'s link key - remembered, or opened from the organizer's wrap with the mailbox's unlocked zero-access keys - or `undefined`
|
|
53
|
+
* when it isn't protected or can't be opened here. Never rejects. */
|
|
54
|
+
export declare function meetingKeyFragmentOf(meeting: VideoMeetingDetail & ProtectedMeetingFields): Promise<string | undefined>;
|
|
55
|
+
/** `meeting`'s organizer join link, with the meeting key after `#` when it is protected - `null` when there is none, or the key can't
|
|
56
|
+
* be opened here (a protected meeting can't be joined without it). */
|
|
57
|
+
export declare function organizerJoinLink(meeting: VideoMeetingDetail & ProtectedMeetingFields): Promise<string | null>;
|
|
58
|
+
/** For a save of `event` that may invite: every attendee's link key (`invitationContent.meetingLinkSecrets`), when its video meeting is
|
|
59
|
+
* protected and its key opens here - else `undefined`. Never rejects. */
|
|
60
|
+
export declare function meetingLinkSecrets(event: {
|
|
61
|
+
videoMeetingUid?: string | null;
|
|
62
|
+
attendees?: {
|
|
63
|
+
address: string;
|
|
64
|
+
}[];
|
|
65
|
+
organizer?: {
|
|
66
|
+
address: string;
|
|
67
|
+
};
|
|
68
|
+
}, client?: ApiClient): Promise<Record<string, string> | undefined>;
|
|
69
|
+
/**
|
|
70
|
+
* Moves the key expiry of meeting `meetingUid` - when it is protected - to follow its event's new end, `endDate` (the last occurrence's
|
|
71
|
+
* end, for a series): its key is discarded an hour after the end it was made for, so a meeting moved later (or a series extended) would
|
|
72
|
+
* otherwise be unjoinable by then. Best effort, after the event itself is saved: a failure is logged, never thrown.
|
|
73
|
+
*/
|
|
74
|
+
export declare function followMeetingKeyExpiry(meetingUid: string, endDate: Date, client?: ApiClient): Promise<void>;
|