@cosmicdrift/kumiko-bundled-features 0.392.2 → 0.394.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/dist/account-security/feature.d.ts +2 -0
- package/dist/account-security/feature.js +3 -1
- package/dist/audit/event-classification.js +1 -0
- package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +1 -1
- package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
- package/dist/auth-email-password/web/auth-client.d.ts +3 -7
- package/dist/auth-email-password/web/auth-client.js +3 -24
- package/dist/auth-email-password/web/auth-gate.d.ts +2 -4
- package/dist/auth-email-password/web/auth-redirect.d.ts +2 -2
- package/dist/auth-email-password/web/auth-redirect.js +3 -12
- package/dist/auth-email-password/web/index.d.ts +2 -1
- package/dist/auth-email-password/web/index.js +1 -0
- package/dist/auth-email-password/web/login-failure.d.ts +9 -0
- package/dist/auth-email-password/web/login-failure.js +32 -0
- package/dist/auth-email-password/web/login-url.d.ts +5 -0
- package/dist/auth-email-password/web/login-url.js +13 -0
- package/dist/auth-email-password/web/static-login.d.ts +42 -0
- package/dist/auth-email-password/web/static-login.js +122 -0
- package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
- package/dist/auth-mfa/web/mfa-client.js +4 -43
- package/dist/billing-foundation/checkout-core.d.ts +2 -1
- package/dist/billing-foundation/checkout-core.js +2 -11
- package/dist/billing-foundation/events.d.ts +1 -0
- package/dist/billing-foundation/events.js +2 -0
- package/dist/billing-foundation/feature.js +1 -1
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +9 -2
- package/dist/billing-foundation/handlers/process-payment-event.write.d.ts +1 -0
- package/dist/billing-foundation/handlers/process-payment-event.write.js +2 -0
- package/dist/billing-foundation/index.d.ts +1 -1
- package/dist/billing-foundation/one-off-checkout-gate.d.ts +9 -0
- package/dist/billing-foundation/one-off-checkout-gate.js +30 -0
- package/dist/billing-foundation/one-off-customer.d.ts +6 -0
- package/dist/billing-foundation/one-off-customer.js +12 -0
- package/dist/billing-foundation/plan-catalog.js +1 -0
- package/dist/billing-foundation/types.d.ts +25 -0
- package/dist/billing-foundation/web/billing-plans-panel.js +1 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +7 -3
- package/dist/billing-foundation/web/plan-switch-recurring.d.ts +16 -0
- package/dist/billing-foundation/web/plan-switch-recurring.js +10 -0
- package/dist/billing-foundation/webhook-handler.js +1 -0
- package/dist/cap-overview/handlers/caps-usage.query.js +3 -3
- package/dist/cap-overview/types.d.ts +2 -0
- package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
- package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
- package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
- package/dist/compliance-profiles/web/compliance-profile-catalog.js +8 -1
- package/dist/config/handlers/reset.write.d.ts +1 -1
- package/dist/config/handlers/set.write.d.ts +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
- package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
- package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +2 -0
- package/dist/file-foundation/read-file-ref-content.js +9 -3
- package/dist/form-draft/handlers/discard.write.d.ts +1 -1
- package/dist/form-draft/handlers/save.write.d.ts +1 -1
- package/dist/inbound-mail-foundation/account-health.d.ts +17 -0
- package/dist/inbound-mail-foundation/account-health.js +101 -0
- package/dist/inbound-mail-foundation/account-poll-schedule.d.ts +19 -0
- package/dist/inbound-mail-foundation/account-poll-schedule.js +44 -0
- package/dist/inbound-mail-foundation/account-poll.d.ts +24 -0
- package/dist/inbound-mail-foundation/account-poll.js +157 -0
- package/dist/inbound-mail-foundation/account-record.d.ts +1 -0
- package/dist/inbound-mail-foundation/account-record.js +1 -0
- package/dist/inbound-mail-foundation/account-wake.d.ts +3 -0
- package/dist/inbound-mail-foundation/account-wake.js +17 -0
- package/dist/inbound-mail-foundation/connect-callback-verify.d.ts +10 -0
- package/dist/inbound-mail-foundation/connect-callback-verify.js +31 -0
- package/dist/inbound-mail-foundation/connect-route-responses.d.ts +13 -0
- package/dist/inbound-mail-foundation/connect-route-responses.js +16 -0
- package/dist/inbound-mail-foundation/connect-routes.js +52 -33
- package/dist/inbound-mail-foundation/constants.d.ts +50 -2
- package/dist/inbound-mail-foundation/constants.js +53 -3
- package/dist/inbound-mail-foundation/content-refetch.d.ts +14 -0
- package/dist/inbound-mail-foundation/content-refetch.js +93 -0
- package/dist/inbound-mail-foundation/describe-error.d.ts +1 -0
- package/dist/inbound-mail-foundation/describe-error.js +5 -0
- package/dist/inbound-mail-foundation/entities.d.ts +26 -0
- package/dist/inbound-mail-foundation/entities.js +43 -3
- package/dist/inbound-mail-foundation/events.d.ts +97 -0
- package/dist/inbound-mail-foundation/events.js +57 -2
- package/dist/inbound-mail-foundation/feature.d.ts +5 -2
- package/dist/inbound-mail-foundation/feature.js +122 -106
- package/dist/inbound-mail-foundation/file-storage-failure.d.ts +8 -0
- package/dist/inbound-mail-foundation/file-storage-failure.js +19 -0
- package/dist/inbound-mail-foundation/handlers/account-address.d.ts +16 -0
- package/dist/inbound-mail-foundation/handlers/account-address.js +51 -0
- package/dist/inbound-mail-foundation/handlers/acknowledge-quarantine.write.d.ts +8 -0
- package/dist/inbound-mail-foundation/handlers/acknowledge-quarantine.write.js +39 -0
- package/dist/inbound-mail-foundation/handlers/attach-message-content.write.d.ts +60 -0
- package/dist/inbound-mail-foundation/handlers/attach-message-content.write.js +128 -0
- package/dist/inbound-mail-foundation/handlers/connect-account.write.d.ts +3 -2
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +97 -56
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.d.ts +2 -1
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +55 -42
- package/dist/inbound-mail-foundation/handlers/ingest-message-prepare.d.ts +26 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message-prepare.js +131 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message-schema.d.ts +64 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message-schema.js +39 -0
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.d.ts +74 -21
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +57 -134
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.d.ts +2 -1
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +60 -26
- package/dist/inbound-mail-foundation/handlers/list-messages.query.d.ts +2 -1
- package/dist/inbound-mail-foundation/handlers/list-messages.query.js +43 -29
- package/dist/inbound-mail-foundation/handlers/list-quarantine.query.d.ts +3 -0
- package/dist/inbound-mail-foundation/handlers/list-quarantine.query.js +75 -0
- package/dist/inbound-mail-foundation/handlers/mark-credential-stored.write.d.ts +7 -0
- package/dist/inbound-mail-foundation/handlers/mark-credential-stored.write.js +38 -0
- package/dist/inbound-mail-foundation/handlers/message-content.d.ts +70 -0
- package/dist/inbound-mail-foundation/handlers/message-content.js +137 -0
- package/dist/inbound-mail-foundation/handlers/message-content.query.d.ts +19 -0
- package/dist/inbound-mail-foundation/handlers/message-content.query.js +59 -0
- package/dist/inbound-mail-foundation/handlers/message-detail.query.d.ts +3 -0
- package/dist/inbound-mail-foundation/handlers/message-detail.query.js +23 -0
- package/dist/inbound-mail-foundation/handlers/reactivate-account.write.d.ts +8 -0
- package/dist/inbound-mail-foundation/handlers/reactivate-account.write.js +84 -0
- package/dist/inbound-mail-foundation/handlers/retry-quarantined-message.write.d.ts +8 -0
- package/dist/inbound-mail-foundation/handlers/retry-quarantined-message.write.js +34 -0
- package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +10 -2
- package/dist/inbound-mail-foundation/handlers/scope-visibility.js +27 -8
- package/dist/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.d.ts +13 -0
- package/dist/inbound-mail-foundation/handlers/set-trusted-authserv-id.write.js +54 -0
- package/dist/inbound-mail-foundation/handlers/thread-rollup.d.ts +24 -0
- package/dist/inbound-mail-foundation/handlers/thread-rollup.js +98 -0
- package/dist/inbound-mail-foundation/handlers/trusted-authserv-id.d.ts +3 -0
- package/dist/inbound-mail-foundation/handlers/trusted-authserv-id.js +3 -0
- package/dist/inbound-mail-foundation/handlers/update-account.write.d.ts +13 -3
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +62 -46
- package/dist/inbound-mail-foundation/i18n.js +72 -2
- package/dist/inbound-mail-foundation/inbound-oauth-flow.d.ts +43 -0
- package/dist/inbound-mail-foundation/inbound-oauth-flow.js +1 -0
- package/dist/inbound-mail-foundation/index.d.ts +19 -6
- package/dist/inbound-mail-foundation/index.js +17 -6
- package/dist/inbound-mail-foundation/ingest-failures.d.ts +77 -0
- package/dist/inbound-mail-foundation/ingest-failures.js +79 -0
- package/dist/inbound-mail-foundation/ingest-payload.d.ts +7 -0
- package/dist/inbound-mail-foundation/ingest-payload.js +34 -0
- package/dist/inbound-mail-foundation/mail-roles.d.ts +21 -0
- package/dist/inbound-mail-foundation/mail-roles.js +83 -0
- package/dist/inbound-mail-foundation/normalize-inbound-message.d.ts +17 -0
- package/dist/inbound-mail-foundation/normalize-inbound-message.js +90 -0
- package/dist/inbound-mail-foundation/oauth-access-token.d.ts +3 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +13 -2
- package/dist/inbound-mail-foundation/outbound-attachment-access.d.ts +5 -0
- package/dist/inbound-mail-foundation/outbound-attachment-access.js +16 -0
- package/dist/inbound-mail-foundation/outbound-attachments.d.ts +18 -0
- package/dist/inbound-mail-foundation/outbound-attachments.js +77 -0
- package/dist/inbound-mail-foundation/outbound-mail.d.ts +15 -1
- package/dist/inbound-mail-foundation/outbound-mail.js +4 -0
- package/dist/inbound-mail-foundation/probe.js +1 -1
- package/dist/inbound-mail-foundation/projection.d.ts +28 -0
- package/dist/inbound-mail-foundation/projection.js +25 -3
- package/dist/inbound-mail-foundation/provider-errors.d.ts +28 -0
- package/dist/inbound-mail-foundation/provider-errors.js +66 -0
- package/dist/inbound-mail-foundation/quarantine-entities.d.ts +61 -0
- package/dist/inbound-mail-foundation/quarantine-entities.js +47 -0
- package/dist/inbound-mail-foundation/quarantine-header-import.d.ts +9 -0
- package/dist/inbound-mail-foundation/quarantine-header-import.js +53 -0
- package/dist/inbound-mail-foundation/quarantine-retry.d.ts +13 -0
- package/dist/inbound-mail-foundation/quarantine-retry.js +97 -0
- package/dist/inbound-mail-foundation/quarantine-screens.d.ts +4 -0
- package/dist/inbound-mail-foundation/quarantine-screens.js +57 -0
- package/dist/inbound-mail-foundation/quarantine.d.ts +13 -0
- package/dist/inbound-mail-foundation/quarantine.js +46 -0
- package/dist/inbound-mail-foundation/retention-job.d.ts +2 -0
- package/dist/inbound-mail-foundation/retention-job.js +62 -0
- package/dist/inbound-mail-foundation/retention-sweep.d.ts +4 -0
- package/dist/inbound-mail-foundation/retention-sweep.js +35 -8
- package/dist/inbound-mail-foundation/screens.d.ts +14 -2
- package/dist/inbound-mail-foundation/screens.js +217 -79
- package/dist/inbound-mail-foundation/send-from-account.d.ts +11 -1
- package/dist/inbound-mail-foundation/send-from-account.js +19 -2
- package/dist/inbound-mail-foundation/sender-auth.d.ts +63 -0
- package/dist/inbound-mail-foundation/sender-auth.js +198 -0
- package/dist/inbound-mail-foundation/supervisor-ingest.d.ts +43 -0
- package/dist/inbound-mail-foundation/supervisor-ingest.js +195 -0
- package/dist/inbound-mail-foundation/sync-account-now.d.ts +22 -0
- package/dist/inbound-mail-foundation/sync-account-now.js +29 -0
- package/dist/inbound-mail-foundation/sync-cursor-store.d.ts +9 -0
- package/dist/inbound-mail-foundation/sync-cursor-store.js +54 -0
- package/dist/inbound-mail-foundation/syncable-accounts.d.ts +16 -0
- package/dist/inbound-mail-foundation/syncable-accounts.js +79 -0
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +5 -1
- package/dist/inbound-mail-foundation/types.d.ts +61 -62
- package/dist/inbound-mail-foundation/types.js +0 -54
- package/dist/inbound-mail-foundation/wake-consumer.d.ts +2 -0
- package/dist/inbound-mail-foundation/wake-consumer.js +30 -0
- package/dist/inbound-mail-foundation/watch-catch-up.d.ts +19 -0
- package/dist/inbound-mail-foundation/watch-catch-up.js +33 -0
- package/dist/inbound-mail-foundation/watch-lease.d.ts +15 -0
- package/dist/inbound-mail-foundation/watch-lease.js +81 -0
- package/dist/inbound-mail-foundation/watch-supervisor-types.d.ts +83 -0
- package/dist/inbound-mail-foundation/watch-supervisor-types.js +1 -0
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +1 -53
- package/dist/inbound-mail-foundation/watch-supervisor.js +123 -290
- package/dist/inbound-mail-foundation/web/client-plugin.d.ts +14 -0
- package/dist/inbound-mail-foundation/web/client-plugin.js +40 -0
- package/dist/inbound-mail-foundation/web/constants.d.ts +5 -0
- package/dist/inbound-mail-foundation/web/constants.js +6 -0
- package/dist/inbound-mail-foundation/web/index.d.ts +1 -0
- package/dist/inbound-mail-foundation/web/index.js +1 -0
- package/dist/inbound-provider-imap/connect-mailbox.d.ts +28 -5
- package/dist/inbound-provider-imap/connect-mailbox.js +91 -41
- package/dist/inbound-provider-imap/connect-my-mailbox.d.ts +13 -0
- package/dist/inbound-provider-imap/connect-my-mailbox.js +25 -0
- package/dist/inbound-provider-imap/constants.d.ts +12 -1
- package/dist/inbound-provider-imap/constants.js +12 -1
- package/dist/inbound-provider-imap/credential-document.js +2 -2
- package/dist/inbound-provider-imap/download-batch.d.ts +6 -0
- package/dist/inbound-provider-imap/download-batch.js +24 -0
- package/dist/inbound-provider-imap/feature.d.ts +2 -2
- package/dist/inbound-provider-imap/feature.js +49 -32
- package/dist/inbound-provider-imap/fetch-content.d.ts +4 -0
- package/dist/inbound-provider-imap/fetch-content.js +68 -0
- package/dist/inbound-provider-imap/i18n.js +18 -2
- package/dist/inbound-provider-imap/imap-client.d.ts +27 -23
- package/dist/inbound-provider-imap/imap-client.js +30 -68
- package/dist/inbound-provider-imap/index.d.ts +2 -1
- package/dist/inbound-provider-imap/index.js +2 -1
- package/dist/inbound-provider-imap/mime-content.d.ts +11 -0
- package/dist/inbound-provider-imap/mime-content.js +66 -0
- package/dist/inbound-provider-imap/parse-message.d.ts +35 -0
- package/dist/inbound-provider-imap/parse-message.js +113 -0
- package/dist/inbound-provider-imap/reconnect-account.d.ts +17 -0
- package/dist/inbound-provider-imap/reconnect-account.js +18 -0
- package/dist/inbound-provider-imap/reconnect-mailbox.d.ts +14 -0
- package/dist/inbound-provider-imap/reconnect-mailbox.js +71 -0
- package/dist/inbound-provider-imap/reconnect-prefill.query.d.ts +3 -0
- package/dist/inbound-provider-imap/reconnect-prefill.query.js +34 -0
- package/dist/inbound-provider-imap/screens.d.ts +6 -1
- package/dist/inbound-provider-imap/screens.js +174 -41
- package/dist/inbound-provider-imap/send-mail.d.ts +2 -2
- package/dist/inbound-provider-imap/send-mail.js +8 -1
- package/dist/inbound-provider-imap/sender-auth-headers.d.ts +3 -0
- package/dist/inbound-provider-imap/sender-auth-headers.js +18 -0
- package/dist/inbound-provider-inmemory/feature.d.ts +15 -0
- package/dist/inbound-provider-inmemory/feature.js +57 -2
- package/dist/inbound-provider-inmemory/index.d.ts +1 -1
- package/dist/inbound-provider-inmemory/index.js +1 -1
- package/dist/jobs/handlers/consumer.write.d.ts +4 -4
- package/dist/jobs/handlers/retry.write.d.ts +1 -1
- package/dist/jobs/handlers/trigger.write.d.ts +1 -1
- package/dist/managed-pages/handlers/set.write.d.ts +1 -1
- package/dist/managed-pages/web/i18n.js +1 -0
- package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
- package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
- package/dist/readiness/handlers/run-probes.write.d.ts +1 -1
- package/dist/secrets/handlers/delete.write.d.ts +1 -1
- package/dist/secrets/handlers/set.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke.write.d.ts +1 -1
- package/dist/subscription-paddle/checkout-config.query.d.ts +3 -0
- package/dist/subscription-paddle/checkout-config.query.js +14 -0
- package/dist/subscription-paddle/checkout-methods.d.ts +6 -0
- package/dist/subscription-paddle/checkout-methods.js +85 -0
- package/dist/subscription-paddle/checkout-page.d.ts +19 -0
- package/dist/subscription-paddle/checkout-page.js +126 -0
- package/dist/subscription-paddle/client.d.ts +24 -0
- package/dist/subscription-paddle/client.js +60 -0
- package/dist/subscription-paddle/constants.d.ts +16 -0
- package/dist/subscription-paddle/constants.js +25 -0
- package/dist/subscription-paddle/feature.d.ts +49 -0
- package/dist/subscription-paddle/feature.js +192 -0
- package/dist/subscription-paddle/index.d.ts +2 -0
- package/dist/subscription-paddle/index.js +4 -0
- package/dist/subscription-paddle/plan-switch-methods.d.ts +6 -0
- package/dist/subscription-paddle/plan-switch-methods.js +114 -0
- package/dist/subscription-paddle/price-methods.d.ts +15 -0
- package/dist/subscription-paddle/price-methods.js +105 -0
- package/dist/subscription-paddle/runtime.d.ts +42 -0
- package/dist/subscription-paddle/runtime.js +109 -0
- package/dist/subscription-paddle/subscription-state.d.ts +34 -0
- package/dist/subscription-paddle/subscription-state.js +48 -0
- package/dist/subscription-paddle/tenant-binding.d.ts +8 -0
- package/dist/subscription-paddle/tenant-binding.js +26 -0
- package/dist/subscription-paddle/verify-webhook.d.ts +4 -0
- package/dist/subscription-paddle/verify-webhook.js +55 -0
- package/dist/subscription-paddle/webhook-events.d.ts +9 -0
- package/dist/subscription-paddle/webhook-events.js +124 -0
- package/dist/template-resolver/handlers/collection-item.query.js +2 -2
- package/dist/template-resolver/handlers/collection-list.query.js +4 -2
- package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-shared.d.ts +11 -0
- package/dist/template-resolver/handlers/collection-shared.js +16 -0
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +1 -1
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +2 -2
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +1 -1
- package/dist/template-resolver/handlers/patch-draft.write.d.ts +1 -1
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +1 -1
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +1 -1
- package/dist/template-resolver/handlers/send-test.write.d.ts +1 -1
- package/dist/template-resolver/handlers/set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +4 -4
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +1 -1
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +1 -1
- package/dist/template-resolver/web/client-plugin.d.ts +4 -0
- package/dist/template-resolver/web/client-plugin.js +134 -52
- package/dist/template-resolver/web/i18n.js +1 -1
- package/dist/tenant/handlers/add-member.write.d.ts +1 -1
- package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
- package/dist/tenant/handlers/create.write.d.ts +1 -1
- package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
- package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update.write.d.ts +1 -1
- package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +2 -1
- package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
- package/dist/waitlist/handlers/export-purge.write.d.ts +2 -2
- package/dist/waitlist/handlers/invite.write.d.ts +1 -1
- package/dist/waitlist/handlers/reject.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.d.ts +1 -1
- package/package.json +33 -17
- package/src/account-security/changes.json +6 -0
- package/src/auth-email-password/changes.json +6 -0
- package/src/billing-foundation/changes.json +30 -0
- package/src/cap-overview/changes.json +6 -0
- package/src/files/changes.json +18 -0
- package/src/inbound-mail-foundation/changes.json +155 -0
- package/src/inbound-provider-imap/changes.json +42 -0
- package/src/jobs/changes.json +18 -0
- package/src/subscription-paddle/changes.json +15 -0
- package/src/template-resolver/changes.json +12 -0
- package/dist/subscription-dodo/client.d.ts +0 -17
- package/dist/subscription-dodo/client.js +0 -31
- package/dist/subscription-dodo/constants.d.ts +0 -12
- package/dist/subscription-dodo/constants.js +0 -19
- package/dist/subscription-dodo/feature.d.ts +0 -37
- package/dist/subscription-dodo/feature.js +0 -144
- package/dist/subscription-dodo/index.d.ts +0 -2
- package/dist/subscription-dodo/index.js +0 -4
- package/dist/subscription-dodo/plugin-methods.d.ts +0 -23
- package/dist/subscription-dodo/plugin-methods.js +0 -260
- package/dist/subscription-dodo/runtime.d.ts +0 -27
- package/dist/subscription-dodo/runtime.js +0 -89
- package/dist/subscription-dodo/verify-webhook.d.ts +0 -37
- package/dist/subscription-dodo/verify-webhook.js +0 -189
- package/src/subscription-dodo/changes.json +0 -14
|
@@ -3,40 +3,74 @@
|
|
|
3
3
|
// billing-foundation list-subscriptions pattern.
|
|
4
4
|
//
|
|
5
5
|
// **Scope visibility:** authorized users can see shared mailboxes
|
|
6
|
-
// (ownerUserId=null); personal mailboxes are
|
|
7
|
-
//
|
|
6
|
+
// (ownerUserId=null); personal mailboxes are listed (metadata only) for their owner and the
|
|
7
|
+
// admin roles, but only the owner sees their quarantine. Filtering runs in memory
|
|
8
8
|
// after the tenant-scoped select because a tenant has only a single- to
|
|
9
9
|
// double-digit number of connected mailboxes.
|
|
10
10
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
11
11
|
import { MAX_LIST_LIMIT } from "@cosmicdrift/kumiko-framework/engine";
|
|
12
12
|
import * as z from "zod";
|
|
13
|
+
import { IMAP_PROVIDER_KEY, InboundMailAccountStatuses, InboundMailAuthMethods, MailAccountReactivationPaths, } from "../constants.js";
|
|
13
14
|
import { MAIL_ACCOUNT_PII_FIELDS } from "../entities.js";
|
|
14
15
|
import { mailAccountsProjectionTable } from "../projection.js";
|
|
15
|
-
import {
|
|
16
|
+
import { quarantineTable } from "../quarantine-entities.js";
|
|
17
|
+
import { isAccountListableBy, mayManageQuarantine, mayReactivateMailbox, } from "./scope-visibility.js";
|
|
18
|
+
const REACTIVATABLE_STATUSES = new Set([
|
|
19
|
+
InboundMailAccountStatuses.disconnected,
|
|
20
|
+
InboundMailAccountStatuses.authError,
|
|
21
|
+
]);
|
|
22
|
+
// Null where no way back exists for this caller, so no row action leads into a refusal.
|
|
23
|
+
function reactivationPathOf(row, user, adminRoles) {
|
|
24
|
+
if (!REACTIVATABLE_STATUSES.has(row["status"]))
|
|
25
|
+
return null;
|
|
26
|
+
if (!mayReactivateMailbox(row["ownerUserId"], user, adminRoles))
|
|
27
|
+
return null;
|
|
28
|
+
if (row["authMethod"] === InboundMailAuthMethods.oauth)
|
|
29
|
+
return MailAccountReactivationPaths.oauthFlow;
|
|
30
|
+
const isPasswordImap = row["provider"] === IMAP_PROVIDER_KEY && row["authMethod"] === InboundMailAuthMethods.password;
|
|
31
|
+
return isPasswordImap ? MailAccountReactivationPaths.passwordForm : null;
|
|
32
|
+
}
|
|
16
33
|
// The projectionList screen always sends `limit`; unknown keys stay rejected.
|
|
17
34
|
const listAccountsSchema = z
|
|
18
35
|
.object({ limit: z.number().int().min(1).max(MAX_LIST_LIMIT).default(MAX_LIST_LIMIT) })
|
|
19
36
|
.strict();
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
export function createListAccountsQuery({ memberRoles, adminRoles, }) {
|
|
38
|
+
return {
|
|
39
|
+
name: "account:list",
|
|
40
|
+
schema: listAccountsSchema,
|
|
41
|
+
access: { roles: [...memberRoles] },
|
|
42
|
+
description: "Lists the connected mailboxes of the caller's tenant with the mailbox address decrypted, hiding personal mailboxes the caller neither owns nor administers and the quarantine of personal mailboxes the caller does not own; use it for the mail connect and settings views.",
|
|
43
|
+
handler: async (query, ctx) => {
|
|
44
|
+
const { limit } = listAccountsSchema.parse(query.payload);
|
|
45
|
+
const allRows = await ctx.db.selectMany(mailAccountsProjectionTable, {
|
|
46
|
+
tenantId: ctx.user.tenantId,
|
|
47
|
+
});
|
|
48
|
+
const rows = allRows
|
|
49
|
+
.filter((row) => isAccountListableBy(row, ctx.user, adminRoles))
|
|
50
|
+
.sort((a, b) => String(a["id"]).localeCompare(String(b["id"])))
|
|
51
|
+
.slice(0, limit);
|
|
52
|
+
// One grouped count for all mailboxes instead of a query per row.
|
|
53
|
+
const openQuarantine = await ctx.db.aggregate(quarantineTable, { measure: { fn: "count" }, groupBy: [{ field: "accountId" }] }, { acknowledgedAt: null });
|
|
54
|
+
const quarantinedCountOf = new Map(openQuarantine.map((group) => [String(group.keys[0]), group.value ?? 0]));
|
|
55
|
+
// The screen's row condition cannot compare a count or combine fields, so it gets booleans.
|
|
56
|
+
const withQuarantine = (row) => {
|
|
57
|
+
const owner = row["ownerUserId"];
|
|
58
|
+
const mayManage = mayManageQuarantine(typeof owner === "string" && owner !== "" ? owner : null, ctx.user, adminRoles);
|
|
59
|
+
const quarantinedCount = mayManage ? (quarantinedCountOf.get(String(row["id"])) ?? 0) : 0;
|
|
60
|
+
return {
|
|
61
|
+
...row,
|
|
62
|
+
quarantinedCount,
|
|
63
|
+
hasOpenQuarantine: quarantinedCount > 0,
|
|
64
|
+
reactivateVia: reactivationPathOf(row, ctx.user, adminRoles),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
const piiKms = configuredPiiSubjectKms();
|
|
68
|
+
if (!piiKms)
|
|
69
|
+
return { rows: rows.map(withQuarantine) };
|
|
70
|
+
const decryptedRows = await Promise.all(rows.map((row) => decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
|
|
71
|
+
requestId: `inbound-mail-foundation:list-accounts:${ctx.user.tenantId}`,
|
|
72
|
+
})));
|
|
73
|
+
return { rows: decryptedRows.map(withQuarantine) };
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import type { QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
-
|
|
2
|
+
import type { InboundMailRoles } from "../mail-roles.ts";
|
|
3
|
+
export declare function createListMessagesQuery({ memberRoles }: InboundMailRoles): QueryHandlerDef;
|
|
@@ -9,9 +9,20 @@
|
|
|
9
9
|
// compatibly without breaking the QN.
|
|
10
10
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
11
11
|
import * as z from "zod";
|
|
12
|
-
import { INBOUND_MESSAGE_PII_FIELDS } from "../entities.js";
|
|
12
|
+
import { INBOUND_MESSAGE_PII_FIELDS, inboundMessageEntity } from "../entities.js";
|
|
13
13
|
import { inboundMessagesProjectionTable } from "../projection.js";
|
|
14
14
|
import { isVisibleToCaller } from "./scope-visibility.js";
|
|
15
|
+
// The full text is delivered only by message:content; selecting it here would also decrypt it.
|
|
16
|
+
const LIST_MESSAGE_COLUMNS = [
|
|
17
|
+
"id",
|
|
18
|
+
"tenantId",
|
|
19
|
+
"version",
|
|
20
|
+
"insertedAt",
|
|
21
|
+
"modifiedAt",
|
|
22
|
+
"insertedById",
|
|
23
|
+
"modifiedById",
|
|
24
|
+
...Object.keys(inboundMessageEntity.fields).filter((name) => name !== "bodyText"),
|
|
25
|
+
];
|
|
15
26
|
const listMessagesSchema = z
|
|
16
27
|
.object({
|
|
17
28
|
accountId: z.uuid().optional(),
|
|
@@ -19,31 +30,34 @@ const listMessagesSchema = z
|
|
|
19
30
|
scope: z.string().min(1).max(200).optional(),
|
|
20
31
|
})
|
|
21
32
|
.strict();
|
|
22
|
-
export
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
};
|
|
33
|
+
export function createListMessagesQuery({ memberRoles }) {
|
|
34
|
+
return {
|
|
35
|
+
name: "message:list",
|
|
36
|
+
schema: listMessagesSchema,
|
|
37
|
+
access: { roles: [...memberRoles] },
|
|
38
|
+
description: "Lists received mail of the caller's tenant with sender, recipients, subject and snippet decrypted, optionally narrowed to one account, thread or folder scope and hiding messages of personal mailboxes the caller does not own; use it to read an inbox or a conversation.",
|
|
39
|
+
handler: async (query, ctx) => {
|
|
40
|
+
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
41
|
+
const payload = query.payload;
|
|
42
|
+
const filter = { tenantId: ctx.user.tenantId };
|
|
43
|
+
if (payload.accountId)
|
|
44
|
+
filter["accountId"] = payload.accountId;
|
|
45
|
+
if (payload.threadKey)
|
|
46
|
+
filter["threadKey"] = payload.threadKey;
|
|
47
|
+
if (payload.scope)
|
|
48
|
+
filter["scope"] = payload.scope;
|
|
49
|
+
const allRows = await ctx.db.selectMany(inboundMessagesProjectionTable, filter, {
|
|
50
|
+
fields: LIST_MESSAGE_COLUMNS,
|
|
51
|
+
});
|
|
52
|
+
// Scope visibility: messages in personal mailboxes are visible only to the owner.
|
|
53
|
+
const rows = allRows.filter((row) => isVisibleToCaller(row, ctx.user));
|
|
54
|
+
const piiKms = configuredPiiSubjectKms();
|
|
55
|
+
if (!piiKms)
|
|
56
|
+
return { rows };
|
|
57
|
+
const decryptedRows = await Promise.all(rows.map((row) => decryptPiiFieldValues(row, INBOUND_MESSAGE_PII_FIELDS, piiKms, {
|
|
58
|
+
requestId: `inbound-mail-foundation:list-messages:${ctx.user.tenantId}`,
|
|
59
|
+
})));
|
|
60
|
+
return { rows: decryptedRows };
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// quarantine:list — open quarantine entries with the sender and subject of the header-only import
|
|
2
|
+
// (looked up by the deterministic message aggregate id; null when the provider gave no headers).
|
|
3
|
+
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
4
|
+
import { MAX_LIST_LIMIT } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { inboundMessageAggregateId } from "../aggregate-id.js";
|
|
7
|
+
import { INBOUND_MESSAGE_PII_FIELDS } from "../entities.js";
|
|
8
|
+
import { inboundMessagesProjectionTable, mailAccountsProjectionTable } from "../projection.js";
|
|
9
|
+
import { quarantineTable } from "../quarantine-entities.js";
|
|
10
|
+
import { mayManageQuarantine } from "./scope-visibility.js";
|
|
11
|
+
// The projectionList screen always sends `limit`; unknown keys stay rejected.
|
|
12
|
+
const listQuarantineSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
accountId: z.uuid().optional(),
|
|
15
|
+
limit: z.number().int().min(1).max(MAX_LIST_LIMIT).default(MAX_LIST_LIMIT),
|
|
16
|
+
})
|
|
17
|
+
.strict();
|
|
18
|
+
function textOrNull(value) {
|
|
19
|
+
return typeof value === "string" ? value : null;
|
|
20
|
+
}
|
|
21
|
+
export function createListQuarantineQuery({ memberRoles, adminRoles, }) {
|
|
22
|
+
return {
|
|
23
|
+
name: "quarantine:list",
|
|
24
|
+
schema: listQuarantineSchema,
|
|
25
|
+
access: { roles: [...memberRoles] },
|
|
26
|
+
agent: { expose: false },
|
|
27
|
+
description: "Lists the open quarantine entries (mails the import could not take) of the mailboxes the caller may manage, newest first, with reason, retry state and the sender and subject of the header-only import when there is one; use it to review and retry quarantined mails.",
|
|
28
|
+
handler: async (query, ctx) => {
|
|
29
|
+
const { accountId, limit } = listQuarantineSchema.parse(query.payload);
|
|
30
|
+
const accounts = await ctx.db.selectMany(mailAccountsProjectionTable, {
|
|
31
|
+
tenantId: ctx.user.tenantId,
|
|
32
|
+
});
|
|
33
|
+
const managed = accounts.filter((row) => (accountId === undefined || row["id"] === accountId) &&
|
|
34
|
+
mayManageQuarantine(textOrNull(row["ownerUserId"]) || null, ctx.user, adminRoles));
|
|
35
|
+
if (managed.length === 0)
|
|
36
|
+
return { rows: [] };
|
|
37
|
+
const accountName = new Map(managed.map((row) => [String(row["id"]), row["displayName"]]));
|
|
38
|
+
const entries = await ctx.db.selectMany(quarantineTable, { accountId: { in: [...accountName.keys()] }, acknowledgedAt: null }, { limit, orderBy: [{ col: "quarantinedAt", direction: "desc" }, { col: "id" }] });
|
|
39
|
+
if (entries.length === 0)
|
|
40
|
+
return { rows: [] };
|
|
41
|
+
const headers = await ctx.db.selectMany(inboundMessagesProjectionTable, {
|
|
42
|
+
id: {
|
|
43
|
+
in: entries.map((entry) => inboundMessageAggregateId(String(entry["accountId"]), String(entry["providerMessageId"]))),
|
|
44
|
+
},
|
|
45
|
+
}, { fields: ["id", "from", "subject"] });
|
|
46
|
+
const piiKms = configuredPiiSubjectKms();
|
|
47
|
+
const readableHeaders = new Map();
|
|
48
|
+
for (const header of headers) {
|
|
49
|
+
const readable = piiKms
|
|
50
|
+
? await decryptPiiFieldValues(header, INBOUND_MESSAGE_PII_FIELDS, piiKms, {
|
|
51
|
+
requestId: `inbound-mail-foundation:list-quarantine:${ctx.user.tenantId}`,
|
|
52
|
+
})
|
|
53
|
+
: header;
|
|
54
|
+
readableHeaders.set(String(header["id"]), readable);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
rows: entries.map((entry) => {
|
|
58
|
+
const accountIdOfEntry = String(entry["accountId"]);
|
|
59
|
+
const header = readableHeaders.get(inboundMessageAggregateId(accountIdOfEntry, String(entry["providerMessageId"])));
|
|
60
|
+
return {
|
|
61
|
+
id: entry["id"],
|
|
62
|
+
accountId: accountIdOfEntry,
|
|
63
|
+
accountName: accountName.get(accountIdOfEntry) ?? null,
|
|
64
|
+
providerMessageId: entry["providerMessageId"],
|
|
65
|
+
reason: entry["reasonCode"],
|
|
66
|
+
quarantinedAt: entry["quarantinedAt"],
|
|
67
|
+
retryRequestedAt: entry["retryRequestedAt"] ?? null,
|
|
68
|
+
sender: textOrNull(header?.["from"]),
|
|
69
|
+
subject: textOrNull(header?.["subject"]),
|
|
70
|
+
};
|
|
71
|
+
}),
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
export declare const CREDENTIAL_STORED_AUDIT_REASON = "credential_stored";
|
|
4
|
+
export declare const markCredentialStoredSchema: z.ZodObject<{
|
|
5
|
+
accountId: z.ZodUUID;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
export declare const markCredentialStoredHandler: WriteHandlerDef;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// mark-credential-stored — the OAuth callback stores the refresh token after the account commit,
|
|
2
|
+
// so the wake from connect-account can fire before the credential exists. This re-emits the
|
|
3
|
+
// unchanged active state with a fresh event, which the wake consumer turns into a sync.
|
|
4
|
+
import { failNotFound } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { InboundMailAccountStatuses } from "../constants.js";
|
|
7
|
+
import { MAIL_ACCOUNT_AGGREGATE_TYPE, MAIL_ACCOUNT_UPDATED_EVENT_QN, } from "../events.js";
|
|
8
|
+
import { loadCurrentMailAccountPayload } from "./account-state.js";
|
|
9
|
+
export const CREDENTIAL_STORED_AUDIT_REASON = "credential_stored";
|
|
10
|
+
export const markCredentialStoredSchema = z.object({ accountId: z.uuid() });
|
|
11
|
+
export const markCredentialStoredHandler = {
|
|
12
|
+
name: "mark-credential-stored",
|
|
13
|
+
schema: markCredentialStoredSchema,
|
|
14
|
+
access: { roles: ["SystemAdmin"] },
|
|
15
|
+
agent: { expose: false },
|
|
16
|
+
description: "Signals that the credential of an active mailbox was stored after the account was created, so the first sync can start right away.",
|
|
17
|
+
handler: async (event, ctx) => {
|
|
18
|
+
const payload = markCredentialStoredSchema.parse(event.payload);
|
|
19
|
+
const current = await loadCurrentMailAccountPayload(ctx, payload.accountId);
|
|
20
|
+
if (!current)
|
|
21
|
+
return failNotFound("mail-account", payload.accountId);
|
|
22
|
+
if (current.status !== InboundMailAccountStatuses.active) {
|
|
23
|
+
return { isSuccess: true, data: { accountId: payload.accountId, marked: false } };
|
|
24
|
+
}
|
|
25
|
+
const headers = {
|
|
26
|
+
providerName: current.provider,
|
|
27
|
+
reason: CREDENTIAL_STORED_AUDIT_REASON,
|
|
28
|
+
};
|
|
29
|
+
await ctx.events.unsafeAppend({
|
|
30
|
+
aggregateId: payload.accountId,
|
|
31
|
+
aggregateType: MAIL_ACCOUNT_AGGREGATE_TYPE,
|
|
32
|
+
type: MAIL_ACCOUNT_UPDATED_EVENT_QN,
|
|
33
|
+
payload: current,
|
|
34
|
+
headers,
|
|
35
|
+
});
|
|
36
|
+
return { isSuccess: true, data: { accountId: payload.accountId, marked: true } };
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { HandlerContext, Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { type InboundContentState } from "../events.ts";
|
|
4
|
+
import type { RawInboundAttachment, SkippedInboundAttachment } from "../types.ts";
|
|
5
|
+
export declare const inboundContentPayloadShape: {
|
|
6
|
+
bodyText: z.ZodDefault<z.ZodString>;
|
|
7
|
+
bodyTextTruncated: z.ZodDefault<z.ZodBoolean>;
|
|
8
|
+
rawMime: z.ZodOptional<z.ZodInstanceOf<Uint8Array<ArrayBuffer>>>;
|
|
9
|
+
bodyHtml: z.ZodOptional<z.ZodString>;
|
|
10
|
+
attachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
11
|
+
fileName: z.ZodString;
|
|
12
|
+
mimeType: z.ZodString;
|
|
13
|
+
data: z.ZodInstanceOf<Uint8Array<ArrayBuffer>>;
|
|
14
|
+
}, z.core.$strip>>>;
|
|
15
|
+
skippedAttachments: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
16
|
+
mimeType: z.ZodString;
|
|
17
|
+
size: z.ZodNumber;
|
|
18
|
+
reason: z.ZodEnum<{
|
|
19
|
+
mail_too_large: "mail_too_large";
|
|
20
|
+
nesting_too_deep: "nesting_too_deep";
|
|
21
|
+
no_file_storage: "no_file_storage";
|
|
22
|
+
rejected: "rejected";
|
|
23
|
+
too_large: "too_large";
|
|
24
|
+
}>;
|
|
25
|
+
}, z.core.$strip>>>;
|
|
26
|
+
};
|
|
27
|
+
export type InboundContentInput = {
|
|
28
|
+
readonly accountId: string;
|
|
29
|
+
readonly messageAggId: string;
|
|
30
|
+
/** From the app's storeBody hook; wins over the default storage. */
|
|
31
|
+
readonly bodyRef: string;
|
|
32
|
+
readonly rawMime?: Uint8Array | undefined;
|
|
33
|
+
readonly bodyHtml?: string | undefined;
|
|
34
|
+
readonly attachments?: readonly RawInboundAttachment[] | undefined;
|
|
35
|
+
readonly skippedAttachments?: readonly SkippedInboundAttachment[] | undefined;
|
|
36
|
+
};
|
|
37
|
+
export type StoredInboundContent = {
|
|
38
|
+
readonly bodyRef: string;
|
|
39
|
+
readonly attachmentFileIds: string[];
|
|
40
|
+
readonly skippedAttachments: SkippedInboundAttachment[];
|
|
41
|
+
/** "unavailable" when something had to be stored but the dispatcher has no file storage. */
|
|
42
|
+
readonly contentStorage: "stored" | "unavailable";
|
|
43
|
+
};
|
|
44
|
+
type ContentContext = Pick<HandlerContext, "db" | "services" | "registry" | "user">;
|
|
45
|
+
export declare function inboundMessageBodyKey(tenantId: string, accountId: string, messageAggId: string, extension: "eml" | "html"): string;
|
|
46
|
+
export type OriginalToStore = {
|
|
47
|
+
readonly data: Uint8Array;
|
|
48
|
+
readonly extension: "eml" | "html";
|
|
49
|
+
readonly mimeType: string;
|
|
50
|
+
};
|
|
51
|
+
export declare function originalOf(input: InboundContentInput): OriginalToStore | undefined;
|
|
52
|
+
export declare function inboundAttachmentMaxSize(registry: Registry): number;
|
|
53
|
+
/** Binds an attachment fileRef to the mail row, so the file guard uses the mail's visibility. */
|
|
54
|
+
export declare function inboundAttachmentTarget(messageAggId: string): {
|
|
55
|
+
readonly entityType: "inbound-message";
|
|
56
|
+
readonly entityId: string;
|
|
57
|
+
readonly fieldName: "attachmentFileIds";
|
|
58
|
+
};
|
|
59
|
+
export declare function needsFileStorage(input: InboundContentInput): boolean;
|
|
60
|
+
/** What a dispatcher without file storage makes of the content: nothing is stored, attachments are skipped. */
|
|
61
|
+
export declare function contentWithoutFileStorage(input: InboundContentInput): StoredInboundContent;
|
|
62
|
+
export declare function storeInboundMessageContent(ctx: ContentContext, input: InboundContentInput): Promise<StoredInboundContent>;
|
|
63
|
+
export declare const MESSAGE_LOCK_NAMESPACE = 1768844643;
|
|
64
|
+
export declare function encryptMessageBodyText(tenantId: string, messageAggId: string, bodyText: string): Promise<string>;
|
|
65
|
+
export declare function contentStateOf(args: {
|
|
66
|
+
readonly bodyText: string;
|
|
67
|
+
readonly hasOriginal: boolean;
|
|
68
|
+
readonly contentStorage: StoredInboundContent["contentStorage"];
|
|
69
|
+
}): InboundContentState;
|
|
70
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Content persistence shared by ingest-message and attach-message-content: the original
|
|
2
|
+
// message and the attachments go to file storage, the event only carries references.
|
|
3
|
+
import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
4
|
+
import { configuredMaxUploadBytes, createFileRefFromBytes, } from "@cosmicdrift/kumiko-framework/files";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { INBOUND_MESSAGE_PII_FIELDS, inboundMessageEntity } from "../entities.js";
|
|
7
|
+
import { INBOUND_MESSAGE_AGGREGATE_TYPE, skippedAttachmentSchema, } from "../events.js";
|
|
8
|
+
import { INBOUND_ATTACHMENT_MAX_BYTES, INBOUND_BODY_HTML_MAX_CHARS, INBOUND_BODY_TEXT_MAX_CHARS, INBOUND_RAW_MIME_MAX_BYTES, INBOUND_SKIPPED_ATTACHMENTS_MAX_COUNT, } from "../normalize-inbound-message.js";
|
|
9
|
+
// Bytes only travel in-process (system dispatch); no JSON request can carry a Uint8Array.
|
|
10
|
+
export const inboundContentPayloadShape = {
|
|
11
|
+
bodyText: z.string().max(INBOUND_BODY_TEXT_MAX_CHARS).default(""),
|
|
12
|
+
bodyTextTruncated: z.boolean().default(false),
|
|
13
|
+
rawMime: z
|
|
14
|
+
.instanceof(Uint8Array)
|
|
15
|
+
.refine((bytes) => bytes.byteLength <= INBOUND_RAW_MIME_MAX_BYTES)
|
|
16
|
+
.optional(),
|
|
17
|
+
bodyHtml: z.string().max(INBOUND_BODY_HTML_MAX_CHARS).optional(),
|
|
18
|
+
attachments: z
|
|
19
|
+
.array(z.object({
|
|
20
|
+
fileName: z.string().max(255),
|
|
21
|
+
mimeType: z.string().max(255),
|
|
22
|
+
data: z.instanceof(Uint8Array),
|
|
23
|
+
}))
|
|
24
|
+
.max(50)
|
|
25
|
+
.optional(),
|
|
26
|
+
skippedAttachments: z.array(skippedAttachmentSchema).max(200).optional(),
|
|
27
|
+
};
|
|
28
|
+
// Deterministic, so a re-ingest after a rollback overwrites the same object instead of orphaning one.
|
|
29
|
+
// Same keyspace the retention sweep deletes from (createFileProviderForTenant).
|
|
30
|
+
export function inboundMessageBodyKey(tenantId, accountId, messageAggId, extension) {
|
|
31
|
+
return `${tenantId}/inbound-mail/${accountId}/${messageAggId}.${extension}`;
|
|
32
|
+
}
|
|
33
|
+
export function originalOf(input) {
|
|
34
|
+
if (input.rawMime !== undefined) {
|
|
35
|
+
return { data: input.rawMime, extension: "eml", mimeType: "message/rfc822" };
|
|
36
|
+
}
|
|
37
|
+
if (input.bodyHtml === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
return {
|
|
40
|
+
data: new TextEncoder().encode(input.bodyHtml),
|
|
41
|
+
extension: "html",
|
|
42
|
+
mimeType: "text/html",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// The mail size limit is the cap; an app that configured a smaller upload limit keeps it, but the
|
|
46
|
+
// 10 MB default of createFileRefFromBytes must not cut a 25 MB mail attachment that was accepted above.
|
|
47
|
+
export function inboundAttachmentMaxSize(registry) {
|
|
48
|
+
return Math.min(INBOUND_ATTACHMENT_MAX_BYTES, configuredMaxUploadBytes(registry) ?? INBOUND_ATTACHMENT_MAX_BYTES);
|
|
49
|
+
}
|
|
50
|
+
/** Binds an attachment fileRef to the mail row, so the file guard uses the mail's visibility. */
|
|
51
|
+
export function inboundAttachmentTarget(messageAggId) {
|
|
52
|
+
return {
|
|
53
|
+
entityType: INBOUND_MESSAGE_AGGREGATE_TYPE,
|
|
54
|
+
entityId: messageAggId,
|
|
55
|
+
fieldName: "attachmentFileIds",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function needsFileStorage(input) {
|
|
59
|
+
const hasOriginal = input.rawMime !== undefined || input.bodyHtml !== undefined;
|
|
60
|
+
return (input.attachments ?? []).length > 0 || (input.bodyRef === "" && hasOriginal);
|
|
61
|
+
}
|
|
62
|
+
/** What a dispatcher without file storage makes of the content: nothing is stored, attachments are skipped. */
|
|
63
|
+
export function contentWithoutFileStorage(input) {
|
|
64
|
+
const skipped = [...(input.skippedAttachments ?? [])];
|
|
65
|
+
for (const attachment of input.attachments ?? []) {
|
|
66
|
+
skipped.push({
|
|
67
|
+
mimeType: attachment.mimeType,
|
|
68
|
+
size: attachment.data.byteLength,
|
|
69
|
+
reason: "no_file_storage",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
bodyRef: input.bodyRef,
|
|
74
|
+
attachmentFileIds: [],
|
|
75
|
+
skippedAttachments: skipped.slice(0, INBOUND_SKIPPED_ATTACHMENTS_MAX_COUNT),
|
|
76
|
+
contentStorage: needsFileStorage(input) ? "unavailable" : "stored",
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function storeOriginal(files, tenantId, input) {
|
|
80
|
+
const original = originalOf(input);
|
|
81
|
+
if (!original)
|
|
82
|
+
return "";
|
|
83
|
+
const key = inboundMessageBodyKey(tenantId, input.accountId, input.messageAggId, original.extension);
|
|
84
|
+
await files.ref(key).write(original.data, original.mimeType);
|
|
85
|
+
return key;
|
|
86
|
+
}
|
|
87
|
+
// Nested dispatches (an app handler calling ingest-message) have no pre-transaction phase, so the
|
|
88
|
+
// bytes are written here, inside the caller's transaction.
|
|
89
|
+
export async function storeInboundMessageContent(ctx, input) {
|
|
90
|
+
const { files } = ctx.services;
|
|
91
|
+
if (!files)
|
|
92
|
+
return contentWithoutFileStorage(input);
|
|
93
|
+
const skipped = [...(input.skippedAttachments ?? [])];
|
|
94
|
+
const bodyRef = input.bodyRef !== "" ? input.bodyRef : await storeOriginal(files, ctx.user.tenantId, input);
|
|
95
|
+
const attachmentFileIds = [];
|
|
96
|
+
const maxSize = inboundAttachmentMaxSize(ctx.registry);
|
|
97
|
+
for (const attachment of input.attachments ?? []) {
|
|
98
|
+
const created = await createFileRefFromBytes(ctx, {
|
|
99
|
+
fileName: attachment.fileName,
|
|
100
|
+
mimeType: attachment.mimeType,
|
|
101
|
+
data: attachment.data,
|
|
102
|
+
attachTo: inboundAttachmentTarget(input.messageAggId),
|
|
103
|
+
maxSize,
|
|
104
|
+
});
|
|
105
|
+
if (created.isSuccess) {
|
|
106
|
+
attachmentFileIds.push(created.data.id);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
skipped.push({
|
|
110
|
+
mimeType: attachment.mimeType,
|
|
111
|
+
size: attachment.data.byteLength,
|
|
112
|
+
reason: "rejected",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
bodyRef,
|
|
118
|
+
attachmentFileIds,
|
|
119
|
+
skippedAttachments: skipped.slice(0, INBOUND_SKIPPED_ATTACHMENTS_MAX_COUNT),
|
|
120
|
+
contentStorage: "stored",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// 'inmc': serializes ingest and content attachment per message; disjoint from the thread-rollup namespace.
|
|
124
|
+
export const MESSAGE_LOCK_NAMESPACE = 0x696e6d63;
|
|
125
|
+
// Encrypts a single message PII field outside the batch encryption of ingest-message.
|
|
126
|
+
export async function encryptMessageBodyText(tenantId, messageAggId, bodyText) {
|
|
127
|
+
const piiKms = configuredPiiSubjectKms();
|
|
128
|
+
if (!piiKms)
|
|
129
|
+
return bodyText;
|
|
130
|
+
const encrypted = await encryptPiiFieldValues({ tenantId, bodyText }, inboundMessageEntity, INBOUND_MESSAGE_PII_FIELDS, piiKms, { requestId: `inbound-mail-foundation:message-content:${messageAggId}`, tenantId }, { tenantId, entityName: INBOUND_MESSAGE_AGGREGATE_TYPE });
|
|
131
|
+
return encrypted.bodyText;
|
|
132
|
+
}
|
|
133
|
+
export function contentStateOf(args) {
|
|
134
|
+
if (args.contentStorage === "unavailable")
|
|
135
|
+
return "unavailable";
|
|
136
|
+
return args.bodyText.length > 0 || args.hasOriginal ? "complete" : "snippet";
|
|
137
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HandlerContext, QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { type InboundContentState, type InboundContentUnavailableReason } from "../events.ts";
|
|
3
|
+
import type { InboundMailRoles } from "../mail-roles.ts";
|
|
4
|
+
import { type SenderAuthentication } from "../sender-auth.ts";
|
|
5
|
+
import type { SkippedInboundAttachment } from "../types.ts";
|
|
6
|
+
export type InboundMessageContent = {
|
|
7
|
+
readonly messageId: string;
|
|
8
|
+
readonly bodyText: string;
|
|
9
|
+
readonly bodyTextTruncated: boolean;
|
|
10
|
+
readonly bodyRef: string;
|
|
11
|
+
readonly attachmentFileIds: readonly string[];
|
|
12
|
+
readonly skippedAttachments: readonly SkippedInboundAttachment[];
|
|
13
|
+
readonly contentState: InboundContentState;
|
|
14
|
+
readonly contentUnavailableReason: InboundContentUnavailableReason | null;
|
|
15
|
+
/** null = no trustworthy statement; see isSenderAuthenticated. */
|
|
16
|
+
readonly senderAuthentication: SenderAuthentication | null;
|
|
17
|
+
};
|
|
18
|
+
export declare function loadInboundMessageContent(ctx: Pick<HandlerContext, "db" | "user">, messageId: string): Promise<InboundMessageContent | null>;
|
|
19
|
+
export declare function createMessageContentQuery({ memberRoles }: InboundMailRoles): QueryHandlerDef;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// message:content — the decrypted full text, attachment ids and content state of one message.
|
|
2
|
+
// message:list deliberately omits all of it so inbox lists stay small.
|
|
3
|
+
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
4
|
+
import { parseJsonOrThrow } from "@cosmicdrift/kumiko-framework/utils";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { INBOUND_MESSAGE_PII_FIELDS } from "../entities.js";
|
|
7
|
+
import { INBOUND_CONTENT_STATES, INBOUND_CONTENT_UNAVAILABLE_REASONS, skippedAttachmentSchema, } from "../events.js";
|
|
8
|
+
import { inboundMessagesProjectionTable } from "../projection.js";
|
|
9
|
+
import { senderAuthenticationFieldSchema } from "../sender-auth.js";
|
|
10
|
+
import { isVisibleToCaller } from "./scope-visibility.js";
|
|
11
|
+
const messageContentSchema = z.object({ messageId: z.uuid() }).strict();
|
|
12
|
+
const fileIdsSchema = z.array(z.uuid());
|
|
13
|
+
const skippedListSchema = z.array(skippedAttachmentSchema);
|
|
14
|
+
// Columns hold JSON text (nullable JSON stays text); rows from before the columns read as empty.
|
|
15
|
+
function parseJsonColumn(raw, schema, fallback) {
|
|
16
|
+
if (typeof raw !== "string" || raw === "")
|
|
17
|
+
return fallback;
|
|
18
|
+
return schema.parse(parseJsonOrThrow(raw, "inbound message content column"));
|
|
19
|
+
}
|
|
20
|
+
function contentStateOfRow(raw) {
|
|
21
|
+
return INBOUND_CONTENT_STATES.find((state) => state === raw) ?? "snippet";
|
|
22
|
+
}
|
|
23
|
+
// Same visibility and decryption as the query, for server-side consumers (apps).
|
|
24
|
+
export async function loadInboundMessageContent(ctx, messageId) {
|
|
25
|
+
const row = await ctx.db.fetchOne(inboundMessagesProjectionTable, { id: messageId });
|
|
26
|
+
if (!row || !isVisibleToCaller(row, ctx.user))
|
|
27
|
+
return null;
|
|
28
|
+
const piiKms = configuredPiiSubjectKms();
|
|
29
|
+
const readable = piiKms
|
|
30
|
+
? await decryptPiiFieldValues(row, INBOUND_MESSAGE_PII_FIELDS, piiKms, {
|
|
31
|
+
requestId: `inbound-mail-foundation:message-content:${ctx.user.tenantId}`,
|
|
32
|
+
})
|
|
33
|
+
: row;
|
|
34
|
+
const bodyText = readable["bodyText"];
|
|
35
|
+
return {
|
|
36
|
+
messageId,
|
|
37
|
+
bodyText: typeof bodyText === "string" ? bodyText : "",
|
|
38
|
+
bodyTextTruncated: row["bodyTextTruncated"] === true,
|
|
39
|
+
bodyRef: typeof row["bodyRef"] === "string" ? row["bodyRef"] : "",
|
|
40
|
+
attachmentFileIds: fileIdsSchema.parse(row["attachmentFileIds"] ?? []),
|
|
41
|
+
skippedAttachments: parseJsonColumn(row["skippedAttachments"], skippedListSchema, []),
|
|
42
|
+
contentState: contentStateOfRow(row["contentState"]),
|
|
43
|
+
contentUnavailableReason: INBOUND_CONTENT_UNAVAILABLE_REASONS.find((reason) => reason === row["contentUnavailableReason"]) ?? null,
|
|
44
|
+
senderAuthentication: parseJsonColumn(row["senderAuthentication"], senderAuthenticationFieldSchema, null),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function createMessageContentQuery({ memberRoles }) {
|
|
48
|
+
return {
|
|
49
|
+
name: "message:content",
|
|
50
|
+
schema: messageContentSchema,
|
|
51
|
+
access: { roles: [...memberRoles] },
|
|
52
|
+
agent: { expose: false },
|
|
53
|
+
description: "Returns the full decrypted text, the attachment file ids, the skipped attachments and the content state (complete, snippet or unavailable) of one received mail, or null when it does not exist or belongs to a personal mailbox the caller may not see; use it to read a mail beyond its snippet.",
|
|
54
|
+
handler: async (query, ctx) => {
|
|
55
|
+
const { messageId } = messageContentSchema.parse(query.payload);
|
|
56
|
+
return loadInboundMessageContent(ctx, messageId);
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// inbound-message:detail — the record the file read guard asks for when someone opens a mail
|
|
2
|
+
// attachment (createRecordReadCheck runs "<entity>:detail" as the caller). It answers with
|
|
3
|
+
// visibility only: no PII column is selected, so opening a file never reaches the KMS.
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { INBOUND_MESSAGE_AGGREGATE_TYPE } from "../events.js";
|
|
6
|
+
import { inboundMessagesProjectionTable } from "../projection.js";
|
|
7
|
+
import { isVisibleToCaller } from "./scope-visibility.js";
|
|
8
|
+
const messageDetailSchema = z.object({ id: z.uuid() }).strict();
|
|
9
|
+
const DETAIL_COLUMNS = ["id", "tenantId", "accountId", "ownerUserId", "scope", "receivedAt"];
|
|
10
|
+
export function createMessageDetailQuery({ memberRoles }) {
|
|
11
|
+
return {
|
|
12
|
+
name: `${INBOUND_MESSAGE_AGGREGATE_TYPE}:detail`,
|
|
13
|
+
schema: messageDetailSchema,
|
|
14
|
+
access: { roles: [...memberRoles] },
|
|
15
|
+
agent: { expose: false },
|
|
16
|
+
description: "Returns the non-personal envelope fields of one received mail, or null when it does not exist or belongs to a personal mailbox the caller does not own; the file read guard uses it to decide who may open the mail's attachments.",
|
|
17
|
+
handler: async (query, ctx) => {
|
|
18
|
+
const { id } = messageDetailSchema.parse(query.payload);
|
|
19
|
+
const [row] = await ctx.db.selectMany(inboundMessagesProjectionTable, { id, tenantId: ctx.user.tenantId }, { fields: DETAIL_COLUMNS });
|
|
20
|
+
return row && isVisibleToCaller(row, ctx.user) ? row : null;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|