@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
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { IMAP_CONNECT_SCREEN_ID } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
1
|
+
import { IMAP_CONNECT_SCREEN_ID, IMAP_RECONNECT_SCREEN_ID, MY_IMAP_CONNECT_SCREEN_ID, MY_IMAP_RECONNECT_SCREEN_ID, } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
2
2
|
import { ACTION_FORM_ENTITY } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
3
3
|
import { fieldLabelKey } from "@cosmicdrift/kumiko-renderer/headless";
|
|
4
|
-
import { FEATURE_NAME, IMAP_CONNECT_ERRORS, SMTP_CONNECT_ERRORS } from "./constants.js";
|
|
4
|
+
import { FEATURE_NAME, IMAP_CONNECT_ERRORS, IMAP_RECONNECT_ERRORS, SMTP_CONNECT_ERRORS, } from "./constants.js";
|
|
5
5
|
const formFieldKey = (field) => fieldLabelKey(FEATURE_NAME, ACTION_FORM_ENTITY, field);
|
|
6
6
|
export const INBOUND_PROVIDER_IMAP_I18N = {
|
|
7
7
|
[`screen:${IMAP_CONNECT_SCREEN_ID}.title`]: { en: "Connect mailbox" },
|
|
8
|
+
[`screen:${IMAP_RECONNECT_SCREEN_ID}.title`]: { en: "Reconnect mailbox" },
|
|
9
|
+
[`screen:${MY_IMAP_CONNECT_SCREEN_ID}.title`]: { en: "Connect mailbox" },
|
|
10
|
+
[`screen:${MY_IMAP_RECONNECT_SCREEN_ID}.title`]: { en: "Reconnect mailbox" },
|
|
11
|
+
[formFieldKey("accountId")]: { en: "Mailbox" },
|
|
8
12
|
[formFieldKey("displayName")]: { en: "Name" },
|
|
9
13
|
[formFieldKey("address")]: { en: "Email address" },
|
|
10
14
|
[formFieldKey("host")]: { en: "IMAP server" },
|
|
@@ -18,7 +22,19 @@ export const INBOUND_PROVIDER_IMAP_I18N = {
|
|
|
18
22
|
"inboundMail.imap.connect.outgoingHint": {
|
|
19
23
|
en: "Replies go out from this address over your own mail server, with the same user name and password. Leave the field empty if the server is called like the IMAP server, with smtp. instead of imap. Usually 587, even when your provider lists 465/SSL.",
|
|
20
24
|
},
|
|
25
|
+
[formFieldKey("trustedAuthservId")]: { en: "Mail server ID" },
|
|
26
|
+
"inboundMail.imap.connect.senderCheck": { en: "Sender check" },
|
|
27
|
+
"inboundMail.imap.connect.senderCheckHint": {
|
|
28
|
+
en: "Identifier of the mail server from the Authentication-Results header, for example mx.example.com. Only with it the system checks whether a sender is genuine. Leave empty if unknown.",
|
|
29
|
+
},
|
|
21
30
|
"inboundMail.imap.connect.submit": { en: "Check and connect" },
|
|
31
|
+
"inboundMail.imap.reconnect.hint": {
|
|
32
|
+
en: "The sign-in is checked before saving. Then the system fetches the mail again.",
|
|
33
|
+
},
|
|
34
|
+
"inboundMail.imap.reconnect.submit": { en: "Check and reconnect" },
|
|
35
|
+
[IMAP_RECONNECT_ERRORS.notPasswordImap.i18nKey]: {
|
|
36
|
+
en: "Only password IMAP mailboxes can be reconnected here.",
|
|
37
|
+
},
|
|
22
38
|
[IMAP_CONNECT_ERRORS.hostUnreachable.i18nKey]: {
|
|
23
39
|
en: "The IMAP server cannot be reached or is not allowed. Check the server name.",
|
|
24
40
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type MailHostGuardOptions } from "@cosmicdrift/kumiko-bundled-features/foundation-shared";
|
|
2
|
-
import { type
|
|
1
|
+
import { type MailHostGuardOptions, resolveMailConnectTarget } from "@cosmicdrift/kumiko-bundled-features/foundation-shared";
|
|
2
|
+
import { type SyncCursorPayload } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
3
3
|
import { ImapFlow } from "imapflow";
|
|
4
4
|
import type { ImapCredentialDocument } from "./credential-document.ts";
|
|
5
5
|
export declare const IMAP_MAILBOX = "INBOX";
|
|
@@ -7,6 +7,31 @@ export declare const IMAP_HOST_UNREACHABLE_MESSAGE = "IMAP host is not reachable
|
|
|
7
7
|
export declare function setImapMailHostLookup(fn: MailHostGuardOptions["lookupFn"]): void;
|
|
8
8
|
export declare function imapMailHostGuard(): MailHostGuardOptions;
|
|
9
9
|
export declare function createImapClient(doc: ImapCredentialDocument, hostGuard?: MailHostGuardOptions): Promise<ImapFlow>;
|
|
10
|
+
export declare function imapClientOptions(doc: ImapCredentialDocument, target: Awaited<ReturnType<typeof resolveMailConnectTarget>>): {
|
|
11
|
+
host: string;
|
|
12
|
+
port: number;
|
|
13
|
+
secure: boolean;
|
|
14
|
+
servername?: string | undefined;
|
|
15
|
+
tls?: {
|
|
16
|
+
servername: undefined;
|
|
17
|
+
} | undefined;
|
|
18
|
+
auth: {
|
|
19
|
+
user: string;
|
|
20
|
+
pass: string;
|
|
21
|
+
accessToken?: undefined;
|
|
22
|
+
} | {
|
|
23
|
+
pass?: undefined;
|
|
24
|
+
user: string;
|
|
25
|
+
accessToken: string;
|
|
26
|
+
};
|
|
27
|
+
connectionTimeout: number;
|
|
28
|
+
greetingTimeout: number;
|
|
29
|
+
socketTimeout: number;
|
|
30
|
+
logger: false;
|
|
31
|
+
emitLogs: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** Drops the connection when the supervisor abandons the sync, so a hung command does not keep the socket open. */
|
|
34
|
+
export declare function closeOnAbort(client: ImapFlow, signal: AbortSignal | undefined): () => void;
|
|
10
35
|
/** Server-Fehler → typisierte Foundation-Fehlerklassen (Plan §2). */
|
|
11
36
|
export declare function connectOnce(doc: ImapCredentialDocument, examineInbox: boolean): Promise<void>;
|
|
12
37
|
export declare function mapImapError(err: unknown, host: string): Error;
|
|
@@ -19,24 +44,3 @@ export declare function parseImapCursor(cursor: SyncCursorPayload | null): ImapC
|
|
|
19
44
|
* foundation resets the cursor and performs a full resync within the
|
|
20
45
|
* backfill window (deduplication catches duplicates). */
|
|
21
46
|
export declare function assertUidValidity(cursor: ImapCursor | null, serverUidValidity: string): void;
|
|
22
|
-
/** Normalizes the References chain: mailparser returns string | string[]
|
|
23
|
-
* | undefined; when References is missing, In-Reply-To provides the thread
|
|
24
|
-
* anchor. */
|
|
25
|
-
export declare function normalizeReferences(parsed: {
|
|
26
|
-
references?: string | string[];
|
|
27
|
-
inReplyTo?: string;
|
|
28
|
-
}): string[];
|
|
29
|
-
/** imapflow provides internalDate as either a Date or string depending on
|
|
30
|
-
* the code path. The string form is deliberately discarded (no date parsing
|
|
31
|
-
* in feature code, no-date-api); toRawInboundMessage then falls back to the
|
|
32
|
-
* mailparser Date header, which is always a real Date. */
|
|
33
|
-
export declare function coerceDate(d: Date | string | undefined): Date | undefined;
|
|
34
|
-
/** providerMessageId: a UID is unique only within a UIDVALIDITY; together,
|
|
35
|
-
* they form the stable idempotency anchor. */
|
|
36
|
-
export declare function buildProviderMessageId(uidValidity: string, uid: number): string;
|
|
37
|
-
export declare function toRawInboundMessage(args: {
|
|
38
|
-
readonly source: Uint8Array;
|
|
39
|
-
readonly uid: number;
|
|
40
|
-
readonly uidValidity: string;
|
|
41
|
-
readonly internalDate: Date | undefined;
|
|
42
|
-
}): Promise<RawInboundMessage>;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
// imapflow wiring and pure helpers for IMAP providers.
|
|
2
2
|
// Reference implementation: /Users/marc/code/doc-o-mat (HEINZ), but uses an
|
|
3
3
|
// incremental UIDVALIDITY:lastUid cursor instead of a '1:*' full scan.
|
|
4
|
+
import { isIP } from "node:net";
|
|
4
5
|
import { BlockedHostError, readAllowedPrivateMailHostsFromEnv, resolveMailConnectTarget, } from "@cosmicdrift/kumiko-bundled-features/foundation-shared";
|
|
5
6
|
import { InboundAuthError, InboundCursorInvalidError, InboundTransientError, } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
6
7
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
7
|
-
import { legacyDateToInstant } from "@cosmicdrift/kumiko-framework/time";
|
|
8
|
-
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
9
8
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
10
9
|
import { ImapFlow } from "imapflow";
|
|
11
|
-
import { simpleParser } from "mailparser";
|
|
12
10
|
export const IMAP_MAILBOX = "INBOX";
|
|
13
|
-
const SNIPPET_MAX = 300;
|
|
14
11
|
const log = createFallbackLogger("inbound-provider-imap");
|
|
12
|
+
// Explicit instead of imapflow's defaults (90 s connect, 16 s greeting, 5 min idle socket): a
|
|
13
|
+
// mailbox that stops answering must fail within a poll, not minutes later. The socket timeout
|
|
14
|
+
// stays at the library's value because IDLE connections rely on it between server keep-alives.
|
|
15
|
+
const IMAP_CONNECTION_TIMEOUT_MS = 20_000;
|
|
16
|
+
const IMAP_GREETING_TIMEOUT_MS = 16_000;
|
|
17
|
+
const IMAP_SOCKET_TIMEOUT_MS = 5 * 60 * 1000;
|
|
15
18
|
// Tenant-visible for both branches below — must not reveal whether the host
|
|
16
19
|
// was blocked (private/reserved range) or merely failed to resolve, and
|
|
17
20
|
// must never include the host or the underlying error text. Server-side
|
|
@@ -57,17 +60,38 @@ export async function createImapClient(doc, hostGuard = {}) {
|
|
|
57
60
|
log.warn("host resolution failed", { host: doc.host, reason });
|
|
58
61
|
throw new InboundTransientError(IMAP_HOST_UNREACHABLE_MESSAGE);
|
|
59
62
|
}
|
|
60
|
-
return new ImapFlow(
|
|
63
|
+
return new ImapFlow(imapClientOptions(doc, target));
|
|
64
|
+
}
|
|
65
|
+
// For an IP-literal host imapflow itself sets `servername: false`, which Bun's tls.connect rejects
|
|
66
|
+
// ("servername argument must be an string"). Overriding it through `tls` with undefined makes the
|
|
67
|
+
// socket connect without SNI; the certificate is still verified against the IP. Not setting the
|
|
68
|
+
// option at all (as for SMTP, where nodemailer omits a falsy servername) does not help here.
|
|
69
|
+
export function imapClientOptions(doc, target) {
|
|
70
|
+
return {
|
|
61
71
|
host: target.host,
|
|
62
72
|
port: doc.port,
|
|
63
73
|
secure: doc.secure,
|
|
64
74
|
...(target.servername && { servername: target.servername }),
|
|
75
|
+
...(isIP(target.host) !== 0 && { tls: { servername: undefined } }),
|
|
65
76
|
auth: doc.password
|
|
66
77
|
? { user: doc.user, pass: doc.password }
|
|
67
78
|
: { user: doc.user, accessToken: doc.accessToken },
|
|
79
|
+
connectionTimeout: IMAP_CONNECTION_TIMEOUT_MS,
|
|
80
|
+
greetingTimeout: IMAP_GREETING_TIMEOUT_MS,
|
|
81
|
+
socketTimeout: IMAP_SOCKET_TIMEOUT_MS,
|
|
68
82
|
logger: false,
|
|
69
83
|
emitLogs: false,
|
|
70
|
-
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Drops the connection when the supervisor abandons the sync, so a hung command does not keep the socket open. */
|
|
87
|
+
export function closeOnAbort(client, signal) {
|
|
88
|
+
if (!signal)
|
|
89
|
+
return () => { };
|
|
90
|
+
const close = () => client.close();
|
|
91
|
+
if (signal.aborted)
|
|
92
|
+
close();
|
|
93
|
+
signal.addEventListener("abort", close, { once: true });
|
|
94
|
+
return () => signal.removeEventListener("abort", close);
|
|
71
95
|
}
|
|
72
96
|
/** Server-Fehler → typisierte Foundation-Fehlerklassen (Plan §2). */
|
|
73
97
|
// examineInbox opens INBOX with EXAMINE instead of SELECT, so the system check never touches \Recent or flags.
|
|
@@ -118,65 +142,3 @@ export function assertUidValidity(cursor, serverUidValidity) {
|
|
|
118
142
|
throw new InboundCursorInvalidError(`UIDVALIDITY changed (${cursor.uidValidity} → ${serverUidValidity}) — full resync required`);
|
|
119
143
|
}
|
|
120
144
|
}
|
|
121
|
-
// =============================================================================
|
|
122
|
-
// Message-Normalisierung → RawInboundMessage
|
|
123
|
-
// =============================================================================
|
|
124
|
-
function stripAngles(id) {
|
|
125
|
-
return id.replace(/^<|>$/g, "").trim();
|
|
126
|
-
}
|
|
127
|
-
/** Normalizes the References chain: mailparser returns string | string[]
|
|
128
|
-
* | undefined; when References is missing, In-Reply-To provides the thread
|
|
129
|
-
* anchor. */
|
|
130
|
-
export function normalizeReferences(parsed) {
|
|
131
|
-
const raw = parsed.references;
|
|
132
|
-
const list = raw === undefined ? [] : Array.isArray(raw) ? raw : raw.split(/\s+/);
|
|
133
|
-
const refs = list.map(stripAngles).filter((r) => r.length > 0);
|
|
134
|
-
if (refs.length === 0 && parsed.inReplyTo) {
|
|
135
|
-
const inReplyTo = stripAngles(parsed.inReplyTo);
|
|
136
|
-
if (inReplyTo)
|
|
137
|
-
return [inReplyTo];
|
|
138
|
-
}
|
|
139
|
-
return refs;
|
|
140
|
-
}
|
|
141
|
-
function addressList(a) {
|
|
142
|
-
if (!a)
|
|
143
|
-
return [];
|
|
144
|
-
const arr = Array.isArray(a) ? a : [a];
|
|
145
|
-
return arr.map((x) => x.text).filter((t) => Boolean(t));
|
|
146
|
-
}
|
|
147
|
-
/** imapflow provides internalDate as either a Date or string depending on
|
|
148
|
-
* the code path. The string form is deliberately discarded (no date parsing
|
|
149
|
-
* in feature code, no-date-api); toRawInboundMessage then falls back to the
|
|
150
|
-
* mailparser Date header, which is always a real Date. */
|
|
151
|
-
export function coerceDate(d) {
|
|
152
|
-
return d instanceof Date ? d : undefined;
|
|
153
|
-
}
|
|
154
|
-
/** providerMessageId: a UID is unique only within a UIDVALIDITY; together,
|
|
155
|
-
* they form the stable idempotency anchor. */
|
|
156
|
-
export function buildProviderMessageId(uidValidity, uid) {
|
|
157
|
-
return `${uidValidity}:${uid}`;
|
|
158
|
-
}
|
|
159
|
-
export async function toRawInboundMessage(args) {
|
|
160
|
-
const parsed = await simpleParser(Buffer.from(args.source));
|
|
161
|
-
const messageIdHeader = parsed.messageId ? stripAngles(parsed.messageId) : null;
|
|
162
|
-
const receivedAt = args.internalDate ?? parsed.date;
|
|
163
|
-
const text = parsed.text?.trim() ?? "";
|
|
164
|
-
return {
|
|
165
|
-
providerMessageId: buildProviderMessageId(args.uidValidity, args.uid),
|
|
166
|
-
messageIdHeader: messageIdHeader && messageIdHeader.length > 0 ? messageIdHeader : null,
|
|
167
|
-
providerThreadId: null,
|
|
168
|
-
references: normalizeReferences(parsed),
|
|
169
|
-
from: parsed.from?.text ?? "",
|
|
170
|
-
to: addressList(parsed.to),
|
|
171
|
-
cc: addressList(parsed.cc),
|
|
172
|
-
subject: parsed.subject ?? "",
|
|
173
|
-
snippet: text.slice(0, SNIPPET_MAX),
|
|
174
|
-
// Lib-boundary bridge (Date from imapflow/mailparser) → Temporal;
|
|
175
|
-
// when no date exists, Epoch is the honest marker.
|
|
176
|
-
receivedAtIso: (receivedAt
|
|
177
|
-
? legacyDateToInstant(receivedAt)
|
|
178
|
-
: Temporal.Instant.fromEpochMilliseconds(0)).toString(),
|
|
179
|
-
rawMime: args.source,
|
|
180
|
-
scope: "inbox",
|
|
181
|
-
};
|
|
182
|
-
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { type ImapCredentialDocument, imapCredentialDocumentSchema, type ParseCredentialResult, parseImapCredentialDocument, } from "./credential-document.ts";
|
|
2
2
|
export { createInboundProviderImapFeature, IMAP_PROVIDER_KEY, setImapMailHostLookup, } from "./feature.ts";
|
|
3
|
-
export { assertUidValidity,
|
|
3
|
+
export { assertUidValidity, type ImapCursor, mapImapError, parseImapCursor, } from "./imap-client.ts";
|
|
4
|
+
export { buildProviderMessageId, normalizeReferences } from "./parse-message.ts";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// Public API of the inbound-provider-imap bundled-feature.
|
|
2
2
|
export { imapCredentialDocumentSchema, parseImapCredentialDocument, } from "./credential-document.js";
|
|
3
3
|
export { createInboundProviderImapFeature, IMAP_PROVIDER_KEY, setImapMailHostLookup, } from "./feature.js";
|
|
4
|
-
export { assertUidValidity,
|
|
4
|
+
export { assertUidValidity, mapImapError, parseImapCursor, } from "./imap-client.js";
|
|
5
|
+
export { buildProviderMessageId, normalizeReferences } from "./parse-message.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RawInboundAttachment, SkippedInboundAttachment } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
2
|
+
import { type ParsedMail } from "mailparser";
|
|
3
|
+
/** A forwarded mail inside a forwarded mail inside a forwarded mail is still read; deeper is skipped. */
|
|
4
|
+
export declare const MIME_FORWARD_MAX_DEPTH = 3;
|
|
5
|
+
export type MailContent = {
|
|
6
|
+
readonly text: string;
|
|
7
|
+
readonly html: string | null;
|
|
8
|
+
readonly attachments: readonly RawInboundAttachment[];
|
|
9
|
+
readonly skipped: readonly SkippedInboundAttachment[];
|
|
10
|
+
};
|
|
11
|
+
export declare function extractMailContent(parsed: ParsedMail, depth?: number): Promise<MailContent>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Turns a parsed MIME tree into the content the foundation stores: full text, HTML, real
|
|
2
|
+
// attachments, and the attachments it deliberately leaves out. Pure; the parsing itself stays in
|
|
3
|
+
// imap-client.ts.
|
|
4
|
+
import { simpleParser } from "mailparser";
|
|
5
|
+
/** A forwarded mail inside a forwarded mail inside a forwarded mail is still read; deeper is skipped. */
|
|
6
|
+
export const MIME_FORWARD_MAX_DEPTH = 3;
|
|
7
|
+
const FORWARDED_MESSAGE_MIME = "message/rfc822";
|
|
8
|
+
const FALLBACK_ATTACHMENT_NAME = "attachment";
|
|
9
|
+
const FALLBACK_FORWARDED_NAME = "forwarded.eml";
|
|
10
|
+
// Signature logos and tracking pixels travel as inline images; a PDF with a Content-Id (Apple Mail
|
|
11
|
+
// inlines PDFs that way) is a real document and must survive.
|
|
12
|
+
function isInlineImage(attachment) {
|
|
13
|
+
if (!attachment.contentType.toLowerCase().startsWith("image/"))
|
|
14
|
+
return false;
|
|
15
|
+
const referencedByBody = attachment.related === true;
|
|
16
|
+
const inlineWithContentId = attachment.contentDisposition === "inline" && Boolean(attachment.contentId);
|
|
17
|
+
return referencedByBody || inlineWithContentId;
|
|
18
|
+
}
|
|
19
|
+
function forwardedHeader(inner) {
|
|
20
|
+
return `\n\n---------- Forwarded message ----------\nFrom: ${inner.from?.text ?? ""}\nSubject: ${inner.subject ?? ""}\n\n`;
|
|
21
|
+
}
|
|
22
|
+
async function readForwardedMail(source, depth) {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = await simpleParser(source);
|
|
25
|
+
return { header: forwardedHeader(parsed), content: await extractMailContent(parsed, depth) };
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function extractMailContent(parsed, depth = 0) {
|
|
32
|
+
let text = parsed.text?.trim() ?? "";
|
|
33
|
+
const attachments = [];
|
|
34
|
+
const skipped = [];
|
|
35
|
+
for (const attachment of parsed.attachments) {
|
|
36
|
+
const mimeType = attachment.contentType;
|
|
37
|
+
if (isInlineImage(attachment))
|
|
38
|
+
continue;
|
|
39
|
+
if (mimeType.toLowerCase() === FORWARDED_MESSAGE_MIME) {
|
|
40
|
+
if (depth + 1 > MIME_FORWARD_MAX_DEPTH) {
|
|
41
|
+
skipped.push({ mimeType, size: attachment.size, reason: "nesting_too_deep" });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const inner = await readForwardedMail(attachment.content, depth + 1);
|
|
45
|
+
if (inner === null) {
|
|
46
|
+
// A forwarded mail the parser chokes on must not sink the outer mail: keep it as a plain .eml.
|
|
47
|
+
attachments.push({
|
|
48
|
+
fileName: attachment.filename ?? FALLBACK_FORWARDED_NAME,
|
|
49
|
+
mimeType,
|
|
50
|
+
data: attachment.content,
|
|
51
|
+
});
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
text += inner.header + inner.content.text;
|
|
55
|
+
attachments.push(...inner.content.attachments);
|
|
56
|
+
skipped.push(...inner.content.skipped);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
attachments.push({
|
|
60
|
+
fileName: attachment.filename ?? FALLBACK_ATTACHMENT_NAME,
|
|
61
|
+
mimeType,
|
|
62
|
+
data: attachment.content,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return { text, html: parsed.html || null, attachments, skipped };
|
|
66
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RawInboundMessage, RejectedInboundMessage } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
2
|
+
import type { MessageEnvelopeObject } from "imapflow";
|
|
3
|
+
/** Normalizes the References chain: mailparser returns string | string[]
|
|
4
|
+
* | undefined; when References is missing, In-Reply-To provides the thread
|
|
5
|
+
* anchor. */
|
|
6
|
+
export declare function normalizeReferences(parsed: {
|
|
7
|
+
references?: string | string[];
|
|
8
|
+
inReplyTo?: string;
|
|
9
|
+
}): string[];
|
|
10
|
+
/** imapflow provides internalDate as either a Date or string depending on
|
|
11
|
+
* the code path. The string form is deliberately discarded (no date parsing
|
|
12
|
+
* in feature code, no-date-api); toRawInboundMessage then falls back to the
|
|
13
|
+
* mailparser Date header, which is always a real Date. */
|
|
14
|
+
export declare function coerceDate(d: Date | string | undefined): Date | undefined;
|
|
15
|
+
/** providerMessageId: a UID is unique only within a UIDVALIDITY; together,
|
|
16
|
+
* they form the stable idempotency anchor. */
|
|
17
|
+
export declare function buildProviderMessageId(uidValidity: string, uid: number): string;
|
|
18
|
+
export declare function toRawInboundMessage(args: {
|
|
19
|
+
readonly source: Uint8Array;
|
|
20
|
+
readonly uid: number;
|
|
21
|
+
readonly uidValidity: string;
|
|
22
|
+
readonly internalDate: Date | undefined;
|
|
23
|
+
/** IMAP envelope; lets a mail that cannot be parsed still show sender and subject. */
|
|
24
|
+
readonly envelope?: MessageEnvelopeObject | undefined;
|
|
25
|
+
/** authserv-id of the mailbox's own server; without it senderAuthentication is null. */
|
|
26
|
+
readonly trustedAuthservId?: string | null;
|
|
27
|
+
}): Promise<RawInboundMessage>;
|
|
28
|
+
export type ParsedOrRejected = {
|
|
29
|
+
readonly message: RawInboundMessage;
|
|
30
|
+
} | {
|
|
31
|
+
readonly rejected: RejectedInboundMessage;
|
|
32
|
+
};
|
|
33
|
+
/** A mail the MIME parser cannot handle must not stop the mailbox: it is reported as rejected so the
|
|
34
|
+
* foundation quarantines it and the cursor moves on. */
|
|
35
|
+
export declare function parseOrReject(args: Parameters<typeof toRawInboundMessage>[0]): Promise<ParsedOrRejected>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
2
|
+
import { legacyDateToInstant } from "@cosmicdrift/kumiko-framework/time";
|
|
3
|
+
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
|
+
import { simpleParser } from "mailparser";
|
|
5
|
+
import { extractMailContent } from "./mime-content.js";
|
|
6
|
+
import { senderAuthenticationOf } from "./sender-auth-headers.js";
|
|
7
|
+
const SNIPPET_MAX = 300;
|
|
8
|
+
const log = createFallbackLogger("inbound-provider-imap");
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Message-Normalisierung → RawInboundMessage
|
|
11
|
+
// =============================================================================
|
|
12
|
+
function stripAngles(id) {
|
|
13
|
+
return id.replace(/^<|>$/g, "").trim();
|
|
14
|
+
}
|
|
15
|
+
/** Normalizes the References chain: mailparser returns string | string[]
|
|
16
|
+
* | undefined; when References is missing, In-Reply-To provides the thread
|
|
17
|
+
* anchor. */
|
|
18
|
+
export function normalizeReferences(parsed) {
|
|
19
|
+
const raw = parsed.references;
|
|
20
|
+
const list = raw === undefined ? [] : Array.isArray(raw) ? raw : raw.split(/\s+/);
|
|
21
|
+
const refs = list.map(stripAngles).filter((r) => r.length > 0);
|
|
22
|
+
if (refs.length === 0 && parsed.inReplyTo) {
|
|
23
|
+
const inReplyTo = stripAngles(parsed.inReplyTo);
|
|
24
|
+
if (inReplyTo)
|
|
25
|
+
return [inReplyTo];
|
|
26
|
+
}
|
|
27
|
+
return refs;
|
|
28
|
+
}
|
|
29
|
+
function addressList(a) {
|
|
30
|
+
if (!a)
|
|
31
|
+
return [];
|
|
32
|
+
const arr = Array.isArray(a) ? a : [a];
|
|
33
|
+
return arr.map((x) => x.text).filter((t) => Boolean(t));
|
|
34
|
+
}
|
|
35
|
+
/** imapflow provides internalDate as either a Date or string depending on
|
|
36
|
+
* the code path. The string form is deliberately discarded (no date parsing
|
|
37
|
+
* in feature code, no-date-api); toRawInboundMessage then falls back to the
|
|
38
|
+
* mailparser Date header, which is always a real Date. */
|
|
39
|
+
export function coerceDate(d) {
|
|
40
|
+
return d instanceof Date ? d : undefined;
|
|
41
|
+
}
|
|
42
|
+
/** providerMessageId: a UID is unique only within a UIDVALIDITY; together,
|
|
43
|
+
* they form the stable idempotency anchor. */
|
|
44
|
+
export function buildProviderMessageId(uidValidity, uid) {
|
|
45
|
+
return `${uidValidity}:${uid}`;
|
|
46
|
+
}
|
|
47
|
+
export async function toRawInboundMessage(args) {
|
|
48
|
+
const parsed = await simpleParser(Buffer.from(args.source));
|
|
49
|
+
const messageIdHeader = parsed.messageId ? stripAngles(parsed.messageId) : null;
|
|
50
|
+
const receivedAt = args.internalDate ?? parsed.date;
|
|
51
|
+
const content = await extractMailContent(parsed);
|
|
52
|
+
const text = parsed.text?.trim() ?? "";
|
|
53
|
+
return {
|
|
54
|
+
providerMessageId: buildProviderMessageId(args.uidValidity, args.uid),
|
|
55
|
+
messageIdHeader: messageIdHeader && messageIdHeader.length > 0 ? messageIdHeader : null,
|
|
56
|
+
providerThreadId: null,
|
|
57
|
+
references: normalizeReferences(parsed),
|
|
58
|
+
from: parsed.from?.text ?? "",
|
|
59
|
+
to: addressList(parsed.to),
|
|
60
|
+
cc: addressList(parsed.cc),
|
|
61
|
+
subject: parsed.subject ?? "",
|
|
62
|
+
snippet: text.slice(0, SNIPPET_MAX),
|
|
63
|
+
// Lib-boundary bridge (Date from imapflow/mailparser) → Temporal;
|
|
64
|
+
// when no date exists, Epoch is the honest marker.
|
|
65
|
+
receivedAtIso: (receivedAt
|
|
66
|
+
? legacyDateToInstant(receivedAt)
|
|
67
|
+
: Temporal.Instant.fromEpochMilliseconds(0)).toString(),
|
|
68
|
+
text: content.text,
|
|
69
|
+
...(content.html !== null && { html: content.html }),
|
|
70
|
+
attachments: content.attachments,
|
|
71
|
+
skippedAttachments: content.skipped,
|
|
72
|
+
senderAuthentication: senderAuthenticationOf(parsed, args.trustedAuthservId ?? null),
|
|
73
|
+
rawMime: args.source,
|
|
74
|
+
scope: "inbox",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function envelopeAddresses(list) {
|
|
78
|
+
return (list ?? []).map((a) => a.address ?? "").filter((a) => a !== "");
|
|
79
|
+
}
|
|
80
|
+
function headersOfEnvelope(envelope, internalDate) {
|
|
81
|
+
const sender = envelope.from?.[0];
|
|
82
|
+
const receivedAt = internalDate ?? coerceDate(envelope.date);
|
|
83
|
+
return {
|
|
84
|
+
from: sender?.address ?? "",
|
|
85
|
+
to: envelopeAddresses(envelope.to),
|
|
86
|
+
cc: envelopeAddresses(envelope.cc),
|
|
87
|
+
subject: envelope.subject ?? "",
|
|
88
|
+
messageIdHeader: envelope.messageId ? stripAngles(envelope.messageId) : null,
|
|
89
|
+
receivedAtIso: receivedAt ? legacyDateToInstant(receivedAt).toString() : "",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** A mail the MIME parser cannot handle must not stop the mailbox: it is reported as rejected so the
|
|
93
|
+
* foundation quarantines it and the cursor moves on. */
|
|
94
|
+
export async function parseOrReject(args) {
|
|
95
|
+
try {
|
|
96
|
+
return { message: await toRawInboundMessage(args) };
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
// Class name only: parser messages can quote header content.
|
|
100
|
+
log.warn("mail could not be parsed", {
|
|
101
|
+
uid: args.uid,
|
|
102
|
+
error: err instanceof Error ? err.name : typeof err,
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
rejected: {
|
|
106
|
+
providerMessageId: buildProviderMessageId(args.uidValidity, args.uid),
|
|
107
|
+
reason: "unparseable",
|
|
108
|
+
rawMime: args.source,
|
|
109
|
+
...(args.envelope && { headers: headersOfEnvelope(args.envelope, args.internalDate) }),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { type WriteFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
3
|
+
type ReconnectAccountRow = {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly provider: string;
|
|
6
|
+
readonly authMethod: string;
|
|
7
|
+
readonly displayName: string | null;
|
|
8
|
+
readonly ownerUserId: string | null;
|
|
9
|
+
readonly status: string | null;
|
|
10
|
+
readonly trustedAuthservId: string | null;
|
|
11
|
+
};
|
|
12
|
+
export type ReconnectAccountLookup = {
|
|
13
|
+
readonly isSuccess: true;
|
|
14
|
+
readonly row: ReconnectAccountRow;
|
|
15
|
+
} | WriteFailure;
|
|
16
|
+
export declare function findReconnectableAccount(db: Pick<TenantDb, "fetchOne">, accountId: string): Promise<ReconnectAccountLookup>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Loads the mailbox a reconnect targets. The db is tenant-scoped, so another tenant's account id
|
|
2
|
+
// finds nothing. A disconnected mailbox is reconnectable on purpose: it keeps its id, threads and
|
|
3
|
+
// sync cursor, so reconnecting never re-imports history.
|
|
4
|
+
import { InboundMailAuthMethods, mailAccountsProjectionTable, } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
5
|
+
import { failNotFound, UnprocessableError, writeFailure, } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
|
+
import { IMAP_PROVIDER_KEY, IMAP_RECONNECT_ERRORS } from "./constants.js";
|
|
7
|
+
export async function findReconnectableAccount(db, accountId) {
|
|
8
|
+
const row = await db.fetchOne(mailAccountsProjectionTable, {
|
|
9
|
+
id: accountId,
|
|
10
|
+
});
|
|
11
|
+
if (!row)
|
|
12
|
+
return failNotFound("mail-account", accountId);
|
|
13
|
+
if (row.provider !== IMAP_PROVIDER_KEY || row.authMethod !== InboundMailAuthMethods.password) {
|
|
14
|
+
const { reason, i18nKey } = IMAP_RECONNECT_ERRORS.notPasswordImap;
|
|
15
|
+
return writeFailure(new UnprocessableError(reason, { i18nKey, details: { accountId } }));
|
|
16
|
+
}
|
|
17
|
+
return { isSuccess: true, row };
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type InboundMailRoles } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
export declare const createReconnectMailboxHandler: ({ memberRoles, adminRoles }: InboundMailRoles) => import("@cosmicdrift/kumiko-types/define-handler").WriteHandlerDefinition<"reconnect-mailbox", z.ZodObject<{
|
|
4
|
+
host: z.ZodString;
|
|
5
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
6
|
+
user: z.ZodString;
|
|
7
|
+
password: z.ZodString;
|
|
8
|
+
smtpHost: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodLiteral<"">, z.ZodString]>>>;
|
|
9
|
+
smtpPort: z.ZodNumber;
|
|
10
|
+
trustedAuthservId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
11
|
+
accountId: z.ZodUUID;
|
|
12
|
+
}, z.core.$strip>, {
|
|
13
|
+
accountId: string;
|
|
14
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, null, undefined>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// reconnect-mailbox: new credentials for an existing password IMAP mailbox, typically after the
|
|
2
|
+
// account went to auth_error. The login is checked before the transaction opens; only then the
|
|
3
|
+
// credential secret of the same account is overwritten and the account goes back to active (also from disconnected), both
|
|
4
|
+
// on the request transaction, so a failure leaves status and secret as they were.
|
|
5
|
+
import { InboundMailFoundationHandlers, inboundCredentialSecretKey, isMailAdmin, mayReactivateMailbox, } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
6
|
+
import { requireSecretsContext } from "@cosmicdrift/kumiko-bundled-features/secrets";
|
|
7
|
+
import { defineWriteHandler, } from "@cosmicdrift/kumiko-framework/engine";
|
|
8
|
+
import { failNotFound } from "@cosmicdrift/kumiko-framework/errors";
|
|
9
|
+
import * as z from "zod";
|
|
10
|
+
import { CREDENTIAL_PREVIEW, checkMailboxLogin, connectMailboxSchema, credentialDocumentOf, } from "./connect-mailbox.js";
|
|
11
|
+
import { findReconnectableAccount } from "./reconnect-account.js";
|
|
12
|
+
const reconnectMailboxSchema = connectMailboxSchema
|
|
13
|
+
.pick({
|
|
14
|
+
host: true,
|
|
15
|
+
port: true,
|
|
16
|
+
user: true,
|
|
17
|
+
password: true,
|
|
18
|
+
smtpHost: true,
|
|
19
|
+
smtpPort: true,
|
|
20
|
+
trustedAuthservId: true,
|
|
21
|
+
})
|
|
22
|
+
.extend({ accountId: z.uuid() });
|
|
23
|
+
const SECRET_TRANSACTION_REASON = "the credential secret is written on the request transaction so a failed reconnect rolls the account status back too";
|
|
24
|
+
export const createReconnectMailboxHandler = ({ memberRoles, adminRoles }) => defineWriteHandler({
|
|
25
|
+
name: "reconnect-mailbox",
|
|
26
|
+
description: "Replaces the credentials of an existing password IMAP mailbox: checks the IMAP login and the SMTP login of the outgoing server first, then overwrites the credential secret of the same account and sets it back to active, keeping its threads and sync cursor.",
|
|
27
|
+
// The payload carries the plaintext password: as a tool call it would reach the model provider.
|
|
28
|
+
agent: { expose: false },
|
|
29
|
+
schema: reconnectMailboxSchema,
|
|
30
|
+
// Members: the owner re-arms a personal mailbox; the lookup gate keeps shared ones to admins.
|
|
31
|
+
access: { roles: [...memberRoles] },
|
|
32
|
+
// Same cap as connect: each call opens a connection to a host the caller picks.
|
|
33
|
+
rateLimit: { per: "tenant+handler", limit: 10, windowSeconds: 600 },
|
|
34
|
+
escapeHatch: { grants: ["unsafeRaw"], reason: SECRET_TRANSACTION_REASON },
|
|
35
|
+
beforeTransaction: async (event, ctx) => {
|
|
36
|
+
const lookup = await findReconnectableAccount(ctx.db, event.payload.accountId);
|
|
37
|
+
if (!lookup.isSuccess)
|
|
38
|
+
return lookup;
|
|
39
|
+
// Before the login check, so a denied caller cannot make the server probe a host; same answer as a missing account.
|
|
40
|
+
if (!mayReactivateMailbox(lookup.row.ownerUserId, ctx.user, adminRoles)) {
|
|
41
|
+
return failNotFound("mail-account", event.payload.accountId);
|
|
42
|
+
}
|
|
43
|
+
return checkMailboxLogin(credentialDocumentOf(event.payload), ctx.obs.log);
|
|
44
|
+
},
|
|
45
|
+
handler: async (event, ctx) => {
|
|
46
|
+
const { accountId } = event.payload;
|
|
47
|
+
// Secret first: reactivate from disconnected requires the credential to exist.
|
|
48
|
+
const secrets = requireSecretsContext({ secrets: ctx.services.secrets, user: ctx.user }, "reconnect-mailbox").withRunner(ctx.db.unsafeRaw());
|
|
49
|
+
await secrets.set(event.user.tenantId, inboundCredentialSecretKey(accountId), JSON.stringify(credentialDocumentOf(event.payload)), {
|
|
50
|
+
redact: () => CREDENTIAL_PREVIEW,
|
|
51
|
+
hint: `IMAP credentials for inbound mail account ${accountId}`,
|
|
52
|
+
updatedBy: event.user.id,
|
|
53
|
+
});
|
|
54
|
+
const reactivated = await ctx.dispatch.write(InboundMailFoundationHandlers.reactivateAccount, {
|
|
55
|
+
accountId,
|
|
56
|
+
});
|
|
57
|
+
if (!reactivated.isSuccess)
|
|
58
|
+
return reactivated;
|
|
59
|
+
// The sender-check policy is tenant-wide: only admins set it, the owner's form does not carry it.
|
|
60
|
+
if (isMailAdmin(event.user, adminRoles)) {
|
|
61
|
+
// The admin form shows the stored id, so an empty field means the admin cleared it.
|
|
62
|
+
const trusted = await ctx.dispatch.write(InboundMailFoundationHandlers.setTrustedAuthservId, {
|
|
63
|
+
accountId,
|
|
64
|
+
trustedAuthservId: event.payload.trustedAuthservId ?? null,
|
|
65
|
+
});
|
|
66
|
+
if (!trusted.isSuccess)
|
|
67
|
+
return trusted;
|
|
68
|
+
}
|
|
69
|
+
return { isSuccess: true, data: { accountId } };
|
|
70
|
+
},
|
|
71
|
+
});
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type InboundMailRoles } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
2
|
+
import type { QueryHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
export declare const createReconnectPrefillQuery: ({ memberRoles, adminRoles, }: InboundMailRoles) => QueryHandlerDef;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Initial values of the reconnect form: the server settings of the stored credential, never the
|
|
2
|
+
// password. A missing or unreadable credential leaves the fields empty for the admin to fill in.
|
|
3
|
+
import { inboundCredentialSecretKey, mayReactivateMailbox, } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
4
|
+
import { requireSecretsContext } from "@cosmicdrift/kumiko-bundled-features/secrets";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
import { FEATURE_NAME } from "./constants.js";
|
|
7
|
+
import { parseImapCredentialDocument } from "./credential-document.js";
|
|
8
|
+
import { findReconnectableAccount } from "./reconnect-account.js";
|
|
9
|
+
const reconnectPrefillSchema = z.object({ accountId: z.uuid() }).strict();
|
|
10
|
+
export const createReconnectPrefillQuery = ({ memberRoles, adminRoles, }) => ({
|
|
11
|
+
name: "reconnect-prefill",
|
|
12
|
+
schema: reconnectPrefillSchema,
|
|
13
|
+
access: { roles: [...memberRoles] },
|
|
14
|
+
// Only the reconnect form needs these settings; the agent has no write to use them with.
|
|
15
|
+
agent: { expose: false },
|
|
16
|
+
description: "Returns the server settings (IMAP host and port, user name, SMTP host and port, trusted authserv-id) stored for a password IMAP mailbox without its password, or null when the mailbox is not found or not a password IMAP mailbox; use it to prefill the reconnect form.",
|
|
17
|
+
handler: async (query, ctx) => {
|
|
18
|
+
const { accountId } = reconnectPrefillSchema.parse(query.payload);
|
|
19
|
+
const lookup = await findReconnectableAccount(ctx.db, accountId);
|
|
20
|
+
// Same null as for a missing account: a caller who may not reconnect it learns nothing about it.
|
|
21
|
+
if (!lookup.isSuccess || !mayReactivateMailbox(lookup.row.ownerUserId, ctx.user, adminRoles)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const secrets = requireSecretsContext({ secrets: ctx.services.secrets, user: ctx.user }, FEATURE_NAME);
|
|
25
|
+
const stored = await secrets.get(ctx.user.tenantId, inboundCredentialSecretKey(accountId));
|
|
26
|
+
const parsed = stored ? parseImapCredentialDocument(stored.reveal()) : undefined;
|
|
27
|
+
const displayName = lookup.row.displayName ?? "";
|
|
28
|
+
const trustedAuthservId = lookup.row.trustedAuthservId ?? "";
|
|
29
|
+
if (!parsed?.ok)
|
|
30
|
+
return { accountId, displayName, trustedAuthservId };
|
|
31
|
+
const { host, port, user, smtpHost, smtpPort } = parsed.doc;
|
|
32
|
+
return { accountId, displayName, host, port, user, smtpHost, smtpPort, trustedAuthservId };
|
|
33
|
+
},
|
|
34
|
+
});
|