@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
|
@@ -42,37 +42,36 @@
|
|
|
42
42
|
// only while the connection is open. Losing the claim (for example through
|
|
43
43
|
// a Redis outage or expired TTL) tears down the local watcher instead of
|
|
44
44
|
// racing a new holder.
|
|
45
|
-
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
46
|
-
import { fetchOne, insertOne, selectMany, updateMany } from "@cosmicdrift/kumiko-framework/db";
|
|
47
45
|
import { SYSTEM_USER_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
48
46
|
import { systemScopedRunner, tenantScopedSecrets, } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
47
|
+
import { mapWithConcurrency } from "../shared/index.js";
|
|
48
|
+
import { createAccountHealth } from "./account-health.js";
|
|
49
|
+
import { createAccountPoller } from "./account-poll.js";
|
|
50
|
+
import { pollScheduleOf, withPollDeadline } from "./account-poll-schedule.js";
|
|
51
|
+
import { registerMailAccountWakeListener } from "./account-wake.js";
|
|
51
52
|
import { InboundMailAccountStatuses, InboundMailFoundationHandlers } from "./constants.js";
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
53
|
+
import { describeError } from "./describe-error.js";
|
|
54
|
+
import { currentCountBucket } from "./ingest-failures.js";
|
|
55
|
+
import { createOAuthAccessTokenManager, usesFoundationManagedOAuth } from "./oauth-access-token.js";
|
|
56
|
+
import { isInboundAuthError, isInboundCursorInvalidError, isInboundRateLimitError, } from "./provider-errors.js";
|
|
55
57
|
import { resolveInboundProviderForAccount } from "./provider-factory.js";
|
|
56
|
-
import {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
58
|
+
import { createMessageIngester } from "./supervisor-ingest.js";
|
|
59
|
+
import { createSyncAccountNow, runExclusivePerAccount, } from "./sync-account-now.js";
|
|
60
|
+
import { createSyncCursorStore } from "./sync-cursor-store.js";
|
|
61
|
+
import { listSyncableAccounts } from "./syncable-accounts.js";
|
|
62
|
+
import { createWatchCatchUp } from "./watch-catch-up.js";
|
|
63
|
+
import { createWatchLease } from "./watch-lease.js";
|
|
62
64
|
const DEFAULT_POLL_INTERVAL_MS = 5 * 60 * 1000;
|
|
63
65
|
const DEFAULT_BACKFILL_WINDOW_DAYS = 30;
|
|
64
66
|
const DEFAULT_MAX_MESSAGES_PER_POLL = 200;
|
|
65
67
|
const DEFAULT_WATCH_BACKOFF_INITIAL_MS = 5_000;
|
|
66
68
|
const DEFAULT_WATCH_BACKOFF_MAX_MS = 5 * 60 * 1000;
|
|
67
69
|
const DEFAULT_WATCH_LEASE_TTL_SECONDS = 90;
|
|
68
|
-
/** One cursor per account (one mailbox inbox) for now; multi-folder support
|
|
69
|
-
* can add further scopes later without a schema change. */
|
|
70
|
-
const CURSOR_SCOPE = "default";
|
|
71
|
-
const SEEN_LOOKUP_CHUNK_SIZE = 1000;
|
|
72
70
|
export function createInboundMailSupervisor(deps) {
|
|
73
71
|
const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
74
72
|
const backfillWindowDays = deps.backfillWindowDays ?? DEFAULT_BACKFILL_WINDOW_DAYS;
|
|
75
73
|
const maxMessagesPerPoll = deps.maxMessagesPerPoll ?? DEFAULT_MAX_MESSAGES_PER_POLL;
|
|
74
|
+
const { pollConcurrency, accountPollDeadlineMs } = pollScheduleOf(deps);
|
|
76
75
|
const backoffInitialMs = deps.watchBackoffInitialMs ?? DEFAULT_WATCH_BACKOFF_INITIAL_MS;
|
|
77
76
|
const backoffMaxMs = deps.watchBackoffMaxMs ?? DEFAULT_WATCH_BACKOFF_MAX_MS;
|
|
78
77
|
const leaseTtlSeconds = deps.watchLeaseTtlSeconds ?? DEFAULT_WATCH_LEASE_TTL_SECONDS;
|
|
@@ -112,135 +111,16 @@ export function createInboundMailSupervisor(deps) {
|
|
|
112
111
|
let running = false;
|
|
113
112
|
let pollTimer = null;
|
|
114
113
|
let pollInFlight = null;
|
|
114
|
+
let unregisterWake = null;
|
|
115
115
|
const watchers = new Map();
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const piiKms = configuredPiiSubjectKms();
|
|
125
|
-
const records = [];
|
|
126
|
-
for (const row of rows) {
|
|
127
|
-
let decrypted;
|
|
128
|
-
try {
|
|
129
|
-
decrypted = piiKms
|
|
130
|
-
? await decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
|
|
131
|
-
requestId: "inbound-mail-foundation:supervisor:list-accounts",
|
|
132
|
-
})
|
|
133
|
-
: row;
|
|
134
|
-
}
|
|
135
|
-
catch (error) {
|
|
136
|
-
// Class name only: the message may carry ciphertext or key details.
|
|
137
|
-
const errorName = error instanceof Error ? error.name : "unknown error";
|
|
138
|
-
log(`inbound-mail: account ${row.id} skipped — address did not decrypt (${errorName})`);
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
const decryptedAddress = decrypted["address"];
|
|
142
|
-
// Falling back to row.address would hand the ciphertext to a provider.
|
|
143
|
-
// One undecryptable account must not stop polling of every other tenant's accounts.
|
|
144
|
-
if (typeof decryptedAddress !== "string") {
|
|
145
|
-
log(`inbound-mail: account ${row.id} skipped — address did not decrypt to text`);
|
|
146
|
-
continue;
|
|
147
|
-
}
|
|
148
|
-
records.push({
|
|
149
|
-
id: row.id,
|
|
150
|
-
tenantId: row.tenantId,
|
|
151
|
-
provider: row.provider,
|
|
152
|
-
authMethod: row.authMethod,
|
|
153
|
-
ownerUserId: row.ownerUserId ?? null,
|
|
154
|
-
address: decryptedAddress,
|
|
155
|
-
displayName: row.displayName ?? "",
|
|
156
|
-
status: InboundMailAccountStatuses.active,
|
|
157
|
-
watchState: row.watchState ?? "idle",
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
return records;
|
|
161
|
-
}
|
|
162
|
-
// ---------------------------------------------------------------
|
|
163
|
-
// Cursor-Persistenz (unmanaged direct-write store).
|
|
164
|
-
// ---------------------------------------------------------------
|
|
165
|
-
async function loadCursor(accountId) {
|
|
166
|
-
const row = await fetchOne(systemDb, syncCursorTable, {
|
|
167
|
-
accountId,
|
|
168
|
-
scope: CURSOR_SCOPE,
|
|
169
|
-
});
|
|
170
|
-
if (!row)
|
|
171
|
-
return null;
|
|
172
|
-
try {
|
|
173
|
-
const parsed = JSON.parse(row.cursor);
|
|
174
|
-
return isPlainObject(parsed) ? parsed : null;
|
|
175
|
-
}
|
|
176
|
-
catch {
|
|
177
|
-
return null; // a corrupt cursor counts as none → backfill; dedup catches duplicates
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
async function saveCursor(account, cursor) {
|
|
181
|
-
const accountId = account.id;
|
|
182
|
-
const now = Temporal.Now.instant().toString();
|
|
183
|
-
const serialized = JSON.stringify(cursor);
|
|
184
|
-
const existing = await fetchOne(systemDb, syncCursorTable, {
|
|
185
|
-
accountId,
|
|
186
|
-
scope: CURSOR_SCOPE,
|
|
187
|
-
});
|
|
188
|
-
if (existing) {
|
|
189
|
-
await updateMany(systemDb, syncCursorTable, { cursor: serialized, updatedAt: now }, { accountId, scope: CURSOR_SCOPE });
|
|
190
|
-
// skip: the update path is complete; do not fall through to the insert branch.
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
await insertOne(systemDb, syncCursorTable, {
|
|
194
|
-
id: crypto.randomUUID(),
|
|
195
|
-
// Direct-write store: no executor stamps the NOT NULL tenant_id for us.
|
|
196
|
-
tenantId: account.tenantId,
|
|
197
|
-
accountId,
|
|
198
|
-
scope: CURSOR_SCOPE,
|
|
199
|
-
cursor: serialized,
|
|
200
|
-
updatedAt: now,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
async function resetCursor(accountId) {
|
|
204
|
-
// No deleteMany import is needed: an empty cursor parses to null, so the
|
|
205
|
-
// next fetch runs as a backfill.
|
|
206
|
-
await updateMany(systemDb, syncCursorTable, { cursor: "", updatedAt: Temporal.Now.instant().toString() }, { accountId, scope: CURSOR_SCOPE });
|
|
207
|
-
}
|
|
208
|
-
// ---------------------------------------------------------------
|
|
209
|
-
// Ingest: every message goes through the standard write handler.
|
|
210
|
-
// ---------------------------------------------------------------
|
|
211
|
-
async function ingestBatch(account, msgs, cursorSnapshot) {
|
|
212
|
-
for (const msg of msgs) {
|
|
213
|
-
const bodyRef = deps.storeBody && msg.rawMime ? await deps.storeBody(account, msg) : "";
|
|
214
|
-
const result = await deps.dispatchWrite({
|
|
215
|
-
handlerQn: InboundMailFoundationHandlers.ingestMessage,
|
|
216
|
-
tenantId: account.tenantId,
|
|
217
|
-
payload: {
|
|
218
|
-
accountId: account.id,
|
|
219
|
-
ownerUserId: account.ownerUserId,
|
|
220
|
-
providerName: account.provider,
|
|
221
|
-
providerMessageId: msg.providerMessageId,
|
|
222
|
-
messageIdHeader: msg.messageIdHeader,
|
|
223
|
-
providerThreadId: msg.providerThreadId,
|
|
224
|
-
references: msg.references,
|
|
225
|
-
from: msg.from,
|
|
226
|
-
to: msg.to,
|
|
227
|
-
cc: msg.cc,
|
|
228
|
-
subject: msg.subject,
|
|
229
|
-
snippet: msg.snippet,
|
|
230
|
-
receivedAtIso: msg.receivedAtIso,
|
|
231
|
-
bodyRef,
|
|
232
|
-
scope: msg.scope,
|
|
233
|
-
providerCursor: cursorSnapshot,
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
if (!result.isSuccess) {
|
|
237
|
-
// A single-message failure aborts the batch without persisting the cursor.
|
|
238
|
-
// The next tick refetches from the old cursor, and deduplication skips
|
|
239
|
-
// messages that were already processed.
|
|
240
|
-
throw new Error(`ingest-message failed for account ${account.id}: ${JSON.stringify(result.error ?? {})}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
116
|
+
const cursors = createSyncCursorStore(systemDb);
|
|
117
|
+
const ingester = createMessageIngester({
|
|
118
|
+
db: systemDb,
|
|
119
|
+
dispatchWrite: deps.dispatchWrite,
|
|
120
|
+
log,
|
|
121
|
+
...(deps.storeBody && { storeBody: deps.storeBody }),
|
|
122
|
+
});
|
|
123
|
+
const health = createAccountHealth({ db: systemDb, dispatchWrite: deps.dispatchWrite, log });
|
|
244
124
|
async function markAccount(account, fields, reason) {
|
|
245
125
|
await deps.dispatchWrite({
|
|
246
126
|
handlerQn: InboundMailFoundationHandlers.updateAccount,
|
|
@@ -265,162 +145,113 @@ export function createInboundMailSupervisor(deps) {
|
|
|
265
145
|
}
|
|
266
146
|
if (isInboundCursorInvalidError(err)) {
|
|
267
147
|
log(`inbound-mail: account ${account.id} cursor invalid — full resync in backfill window`);
|
|
268
|
-
await
|
|
148
|
+
await cursors.reset(account.id);
|
|
269
149
|
return true;
|
|
270
150
|
}
|
|
271
151
|
// Transient or unknown error: log it and retry on the next tick.
|
|
272
152
|
log(`inbound-mail: account ${account.id} sync error: ${describeError(err)}`);
|
|
273
153
|
return true;
|
|
274
154
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
in: providerMessageIds.slice(start, start + SEEN_LOOKUP_CHUNK_SIZE),
|
|
290
|
-
},
|
|
291
|
-
}, { fields: ["providerMessageId"] });
|
|
292
|
-
for (const row of seen)
|
|
293
|
-
seenIds.add(row.providerMessageId);
|
|
294
|
-
}
|
|
295
|
-
return new Set(providerMessageIds.filter((id) => !seenIds.has(id)));
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
async function pollAccount(account, plugin) {
|
|
299
|
-
let cursor = await loadCursor(account.id);
|
|
300
|
-
let budget = maxMessagesPerPoll;
|
|
155
|
+
const { pollAccount } = createAccountPoller({
|
|
156
|
+
systemDb,
|
|
157
|
+
cursors,
|
|
158
|
+
ingester,
|
|
159
|
+
health,
|
|
160
|
+
providerCtx,
|
|
161
|
+
dispatchWrite: deps.dispatchWrite,
|
|
162
|
+
backfillWindowDays,
|
|
163
|
+
maxMessagesPerPoll,
|
|
164
|
+
resolveAccessToken,
|
|
165
|
+
handleSyncError,
|
|
166
|
+
log,
|
|
167
|
+
});
|
|
168
|
+
function resolvePluginOrLog(account) {
|
|
301
169
|
try {
|
|
302
|
-
|
|
303
|
-
// exhausted.
|
|
304
|
-
for (;;) {
|
|
305
|
-
const accessToken = await resolveAccessToken(account, plugin);
|
|
306
|
-
const result = await plugin.fetch(providerCtx, account, cursor, {
|
|
307
|
-
backfillWindowDays,
|
|
308
|
-
maxMessages: budget,
|
|
309
|
-
filterUnknownMessageIds: unknownMessageIdsFilterFor(account),
|
|
310
|
-
...(accessToken !== undefined && { accessToken }),
|
|
311
|
-
});
|
|
312
|
-
await ingestBatch(account, result.messages, JSON.stringify(result.nextCursor));
|
|
313
|
-
await saveCursor(account, result.nextCursor);
|
|
314
|
-
cursor = result.nextCursor;
|
|
315
|
-
budget -= result.messages.length;
|
|
316
|
-
if (!result.hasMore || budget <= 0)
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
170
|
+
return resolveInboundProviderForAccount(providerCtx, account);
|
|
319
171
|
}
|
|
320
172
|
catch (err) {
|
|
321
|
-
|
|
173
|
+
log(`inbound-mail: ${describeError(err)}`);
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const inFlightSyncs = new Map();
|
|
178
|
+
const pollAccountWithDeadline = withPollDeadline(pollAccount, {
|
|
179
|
+
ms: accountPollDeadlineMs,
|
|
180
|
+
onDeadline: async (account) => {
|
|
181
|
+
log(`inbound-mail: account ${account.id} sync passed its ${accountPollDeadlineMs}ms deadline`);
|
|
182
|
+
await health.onPollFailure(account, "transient");
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
async function pollAccountOfTick(account, plugin) {
|
|
186
|
+
// Joining would hang this tick on the same stuck sync; wake and syncAccountNow still join.
|
|
187
|
+
if (inFlightSyncs.has(account.id)) {
|
|
188
|
+
log(`inbound-mail: account ${account.id} is still syncing, skipped this tick`);
|
|
322
189
|
}
|
|
190
|
+
else {
|
|
191
|
+
await runExclusivePerAccount(inFlightSyncs, account.id, async () => {
|
|
192
|
+
await pollAccountWithDeadline(account, plugin, {
|
|
193
|
+
countBucket: currentCountBucket(deps.now, pollIntervalMs),
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (running)
|
|
198
|
+
await ensureWatcher(account, plugin);
|
|
323
199
|
}
|
|
324
200
|
async function pollOnce() {
|
|
325
|
-
const accounts = await
|
|
326
|
-
|
|
327
|
-
|
|
201
|
+
const accounts = await listSyncableAccounts(systemDb, log, { health });
|
|
202
|
+
// One account failing must not abort the others, and mapWithConcurrency would reject on the first error.
|
|
203
|
+
await mapWithConcurrency(accounts, Math.max(1, pollConcurrency), async (account) => {
|
|
328
204
|
try {
|
|
329
|
-
plugin =
|
|
205
|
+
const plugin = resolvePluginOrLog(account);
|
|
206
|
+
if (plugin)
|
|
207
|
+
await pollAccountOfTick(account, plugin);
|
|
330
208
|
}
|
|
331
209
|
catch (err) {
|
|
332
|
-
log(`inbound-mail: ${describeError(err)}`);
|
|
333
|
-
continue;
|
|
210
|
+
log(`inbound-mail: account ${account.id} tick failed: ${describeError(err)}`);
|
|
334
211
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}
|
|
339
|
-
// Remove watchers for accounts that are no longer active.
|
|
340
|
-
const activeIds = new Set(accounts.map((a) => a.id));
|
|
212
|
+
});
|
|
213
|
+
// Remove watchers for accounts that are no longer syncable.
|
|
214
|
+
const syncableIds = new Set(accounts.map((a) => a.id));
|
|
341
215
|
for (const accountId of watchers.keys()) {
|
|
342
|
-
if (!
|
|
216
|
+
if (!syncableIds.has(accountId))
|
|
343
217
|
await stopWatcher(accountId);
|
|
344
218
|
}
|
|
345
219
|
}
|
|
220
|
+
const watchCatchUp = createWatchCatchUp({
|
|
221
|
+
inFlight: inFlightSyncs,
|
|
222
|
+
pollAccount: pollAccountWithDeadline,
|
|
223
|
+
isRunning: () => running,
|
|
224
|
+
log,
|
|
225
|
+
});
|
|
226
|
+
const syncAccountNow = createSyncAccountNow({
|
|
227
|
+
inFlight: inFlightSyncs,
|
|
228
|
+
loadAccount: async (onlyAccountId) => (await listSyncableAccounts(systemDb, log, { onlyAccountId }))[0],
|
|
229
|
+
resolvePlugin: resolvePluginOrLog,
|
|
230
|
+
pollAccount: pollAccountWithDeadline,
|
|
231
|
+
ensureWatcher,
|
|
232
|
+
isRunning: () => running,
|
|
233
|
+
});
|
|
346
234
|
// ---------------------------------------------------------------
|
|
347
235
|
// Watch lifecycle with backoff restart.
|
|
348
236
|
// ---------------------------------------------------------------
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
async function tearDownAfterRenewFailures(account, state, reason) {
|
|
359
|
-
log(`inbound-mail: watch lease ${reason} for account ${account.id} — tearing down local watcher`);
|
|
360
|
-
state.lockToken = null;
|
|
361
|
-
await stopWatcher(account.id);
|
|
362
|
-
}
|
|
363
|
-
async function renewLeaseOnce(account, state, generation) {
|
|
364
|
-
state.renewTimer = null;
|
|
365
|
-
// skip: supervisor stopped, generation superseded, or lease already lost — stale timer fire, nothing to renew.
|
|
366
|
-
if (!running || state.generation !== generation || !state.lockToken || !deps.lock)
|
|
367
|
-
return;
|
|
368
|
-
let renewed;
|
|
369
|
-
try {
|
|
370
|
-
renewed = await deps.lock.renew(account.id, state.lockToken, leaseTtlSeconds);
|
|
371
|
-
}
|
|
372
|
-
catch (err) {
|
|
373
|
-
log(`inbound-mail: watch lease renew for account ${account.id} failed: ${describeError(err)}`);
|
|
374
|
-
state.renewFailures += 1;
|
|
375
|
-
// After failures spanning a full lease TTL, tear down — otherwise a
|
|
376
|
-
// peer can acquire the expired key while we still hold an IDLE conn.
|
|
377
|
-
if (state.renewFailures * renewIntervalMs >= leaseTtlSeconds * 1000) {
|
|
378
|
-
await tearDownAfterRenewFailures(account, state, "renew failed across TTL");
|
|
379
|
-
// skip: connection torn down, no reschedule
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
rescheduleIfStillHeld(account, state, generation);
|
|
383
|
-
// skip: renew failed but TTL not yet exhausted, retry already scheduled
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
386
|
-
state.renewFailures = 0;
|
|
387
|
-
if (!renewed) {
|
|
388
|
-
await tearDownAfterRenewFailures(account, state, "lost");
|
|
389
|
-
// skip: lease lost and torn down, nothing to reschedule
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
rescheduleIfStillHeld(account, state, generation);
|
|
393
|
-
}
|
|
394
|
-
function rescheduleIfStillHeld(account, state, generation) {
|
|
395
|
-
if (running && state.generation === generation && state.lockToken) {
|
|
396
|
-
scheduleRenew(account, state, generation);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
// kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional
|
|
400
|
-
async function acquireWatchLease(account, state) {
|
|
401
|
-
if (!deps.lock || state.lockToken)
|
|
402
|
-
return "acquired";
|
|
403
|
-
const token = await deps.lock.acquire(account.id, { ttlSeconds: leaseTtlSeconds });
|
|
404
|
-
if (!token)
|
|
405
|
-
return "no-token";
|
|
406
|
-
// The acquire() await is a yield point: a concurrent stopWatcher()
|
|
407
|
-
// (e.g. from a lost-lease renewal on a different generation) may have
|
|
408
|
-
// already retired this exact state and removed it from the map. If so,
|
|
409
|
-
// committing the fresh token onto it would leak the lease — nothing
|
|
410
|
-
// would ever release it, blocking failover for the full TTL.
|
|
411
|
-
if (watchers.get(account.id) !== state) {
|
|
412
|
-
await deps.lock.release(account.id, token);
|
|
413
|
-
return "stale";
|
|
414
|
-
}
|
|
415
|
-
state.lockToken = token;
|
|
416
|
-
scheduleRenew(account, state, state.generation);
|
|
417
|
-
return "acquired";
|
|
418
|
-
}
|
|
237
|
+
const watchLease = createWatchLease({
|
|
238
|
+
lock: deps.lock,
|
|
239
|
+
ttlSeconds: leaseTtlSeconds,
|
|
240
|
+
renewIntervalMs,
|
|
241
|
+
watchers,
|
|
242
|
+
isRunning: () => running,
|
|
243
|
+
stopWatcher,
|
|
244
|
+
log,
|
|
245
|
+
});
|
|
419
246
|
// kumiko-lint-ignore complexity-budget watch lifecycle (start/stop/restart) stays cohesive
|
|
420
247
|
async function ensureWatcher(account, plugin) {
|
|
421
248
|
// skip: provider without live push; the reconciliation poll covers the account.
|
|
422
249
|
if (!plugin.watch)
|
|
423
250
|
return;
|
|
251
|
+
const stale = watchers.get(account.id);
|
|
252
|
+
if (stale?.stop && stale.trustedAuthservId !== (account.trustedAuthservId ?? null)) {
|
|
253
|
+
await stopWatcher(account.id);
|
|
254
|
+
}
|
|
424
255
|
const existing = watchers.get(account.id);
|
|
425
256
|
// skip: the watcher is already running, starting, or scheduled for restart.
|
|
426
257
|
if (existing?.stop || existing?.restartTimer || existing?.starting)
|
|
@@ -434,27 +265,21 @@ export function createInboundMailSupervisor(deps) {
|
|
|
434
265
|
renewTimer: null,
|
|
435
266
|
renewFailures: 0,
|
|
436
267
|
starting: false,
|
|
268
|
+
trustedAuthservId: null,
|
|
437
269
|
};
|
|
438
270
|
watchers.set(account.id, state);
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// connection. Retried on the next tick.
|
|
444
|
-
if (!existing)
|
|
271
|
+
state.starting = true;
|
|
272
|
+
const leaseResult = await watchLease.acquire(account, state);
|
|
273
|
+
if (leaseResult !== "acquired") {
|
|
274
|
+
if (leaseResult === "no-token" && !existing)
|
|
445
275
|
watchers.delete(account.id);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
if (leaseResult === "stale") {
|
|
450
|
-
// skip: state was retired by a concurrent stopWatcher while acquire()
|
|
451
|
-
// awaited — committing here would leak the lease.
|
|
276
|
+
state.starting = false;
|
|
277
|
+
// skip: no-token means the poll still covers the account and the next tick retries; stale means stopWatcher retired this state.
|
|
452
278
|
return;
|
|
453
279
|
}
|
|
454
280
|
const generation = state.generation;
|
|
455
281
|
const scheduleRestart = (err) => {
|
|
456
|
-
// skip:
|
|
457
|
-
// would create a zombie watcher.
|
|
282
|
+
// skip: stopped or superseded generation; a restart would create a zombie watcher.
|
|
458
283
|
if (!running || state.generation !== generation)
|
|
459
284
|
return;
|
|
460
285
|
state.stop = null;
|
|
@@ -467,13 +292,13 @@ export function createInboundMailSupervisor(deps) {
|
|
|
467
292
|
void ensureWatcher(account, plugin);
|
|
468
293
|
}, delay);
|
|
469
294
|
};
|
|
470
|
-
state.
|
|
295
|
+
state.trustedAuthservId = account.trustedAuthservId ?? null;
|
|
471
296
|
try {
|
|
472
297
|
const accessToken = await resolveAccessToken(account, plugin);
|
|
473
298
|
const stop = await plugin.watch(providerCtx, account, {
|
|
474
|
-
onMessages: async (msgs) => {
|
|
299
|
+
onMessages: async (msgs, rejected = []) => {
|
|
475
300
|
try {
|
|
476
|
-
await ingestBatch(account, msgs, "watch");
|
|
301
|
+
await ingester.ingestBatch(account, msgs, rejected, "watch");
|
|
477
302
|
}
|
|
478
303
|
catch (err) {
|
|
479
304
|
// An ingest failure must not kill the watcher: the next poll
|
|
@@ -509,6 +334,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
509
334
|
catch (err) {
|
|
510
335
|
log(`inbound-mail: markAccount(watching) for account ${account.id} failed: ${describeError(err)}`);
|
|
511
336
|
}
|
|
337
|
+
watchCatchUp.schedule(account, plugin);
|
|
512
338
|
}
|
|
513
339
|
catch (err) {
|
|
514
340
|
const keepRunning = await handleSyncError(account, err);
|
|
@@ -581,18 +407,25 @@ export function createInboundMailSupervisor(deps) {
|
|
|
581
407
|
if (running)
|
|
582
408
|
return;
|
|
583
409
|
running = true;
|
|
410
|
+
unregisterWake = registerMailAccountWakeListener((_tenantId, accountId) => {
|
|
411
|
+
syncAccountNow(accountId).catch((err) => log(`inbound-mail: wake sync for account ${accountId} failed: ${describeError(err)}`));
|
|
412
|
+
});
|
|
584
413
|
await pollOnce();
|
|
585
414
|
scheduleNextPoll();
|
|
586
415
|
},
|
|
587
416
|
pollOnce,
|
|
417
|
+
syncAccountNow,
|
|
588
418
|
async stop() {
|
|
589
419
|
running = false;
|
|
420
|
+
unregisterWake?.();
|
|
421
|
+
unregisterWake = null;
|
|
590
422
|
if (pollTimer) {
|
|
591
423
|
clearTimeout(pollTimer);
|
|
592
424
|
pollTimer = null;
|
|
593
425
|
}
|
|
594
426
|
if (pollInFlight)
|
|
595
427
|
await pollInFlight;
|
|
428
|
+
await Promise.allSettled([...inFlightSyncs.values(), watchCatchUp.settled()]);
|
|
596
429
|
for (const accountId of [...watchers.keys()]) {
|
|
597
430
|
await stopWatcher(accountId);
|
|
598
431
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ClientFeatureDefinition } from "@cosmicdrift/kumiko-renderer/web";
|
|
2
|
+
export type InboundMailFoundationClientOptions = {
|
|
3
|
+
/** Mount path of the connect route, when the app moved it (`connectPath` of
|
|
4
|
+
* createInboundMailConnectRoutes). Default "/api/inbound-mail/connect". */
|
|
5
|
+
readonly connectPath?: string;
|
|
6
|
+
/** Leaves the app for the provider's sign-in page. Default: `location.assign`. */
|
|
7
|
+
readonly navigate?: (url: string) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare function oauthReconnectUrl(connectPath: string, row: {
|
|
10
|
+
readonly provider: string;
|
|
11
|
+
readonly address: string;
|
|
12
|
+
readonly ownerUserId?: string;
|
|
13
|
+
}): string;
|
|
14
|
+
export declare function inboundMailFoundationClient(options?: InboundMailFoundationClientOptions): ClientFeatureDefinition;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// Client feature for inbound-mail-foundation. The mail account screens offer "Reactivate" for an OAuth
|
|
2
|
+
// mailbox as a clientAction: signing in again is a browser redirect into the same connect route and
|
|
3
|
+
// callback that created the mailbox, which a writeHandler cannot do.
|
|
4
|
+
import { INBOUND_MAIL_CONNECT_PATH, INBOUND_MAIL_FOUNDATION_FEATURE, INBOUND_MAIL_RECONNECT_OAUTH_ACTION, } from "./constants.js";
|
|
5
|
+
const INVALID_RECONNECT_ROW = "inboundMail.accounts.reconnect.invalidRow";
|
|
6
|
+
function textField(payload, field) {
|
|
7
|
+
const value = payload[field];
|
|
8
|
+
return typeof value === "string" && value !== "" ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
// The callback trusts only the signed state, which the connect route builds from the session and these
|
|
11
|
+
// parameters; scope "user" makes the signed-in caller the owner, so the route re-checks ownership.
|
|
12
|
+
export function oauthReconnectUrl(connectPath, row) {
|
|
13
|
+
const query = new URLSearchParams({
|
|
14
|
+
provider: row.provider,
|
|
15
|
+
scope: row.ownerUserId === undefined ? "shared" : "user",
|
|
16
|
+
mailbox: row.address,
|
|
17
|
+
});
|
|
18
|
+
return `${connectPath}?${query.toString()}`;
|
|
19
|
+
}
|
|
20
|
+
function reconnectOauthHandler(connectPath, navigate) {
|
|
21
|
+
return async (payload) => {
|
|
22
|
+
const provider = textField(payload, "provider");
|
|
23
|
+
const address = textField(payload, "address");
|
|
24
|
+
if (provider === undefined || address === undefined) {
|
|
25
|
+
return { ok: false, message: INVALID_RECONNECT_ROW };
|
|
26
|
+
}
|
|
27
|
+
const ownerUserId = textField(payload, "ownerUserId");
|
|
28
|
+
navigate(oauthReconnectUrl(connectPath, { provider, address, ...(ownerUserId && { ownerUserId }) }));
|
|
29
|
+
return { ok: true };
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export function inboundMailFoundationClient(options = {}) {
|
|
33
|
+
const navigate = options.navigate ?? ((url) => globalThis.location.assign(url));
|
|
34
|
+
return {
|
|
35
|
+
name: INBOUND_MAIL_FOUNDATION_FEATURE,
|
|
36
|
+
clientActions: {
|
|
37
|
+
[INBOUND_MAIL_RECONNECT_OAUTH_ACTION]: reconnectOauthHandler(options.connectPath ?? INBOUND_MAIL_CONNECT_PATH, navigate),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const INBOUND_MAIL_FOUNDATION_FEATURE: "inbound-mail-foundation";
|
|
2
|
+
/** Default mount path of the OAuth connect route; the web client builds the reconnect link from it. */
|
|
3
|
+
export declare const INBOUND_MAIL_CONNECT_PATH = "/api/inbound-mail/connect";
|
|
4
|
+
/** Client action of the OAuth "Reactivate" row action: sends the browser to the OAuth connect route. */
|
|
5
|
+
export declare const INBOUND_MAIL_RECONNECT_OAUTH_ACTION: string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Constants the browser client shares with the server feature; the server constants re-export them.
|
|
2
|
+
export const INBOUND_MAIL_FOUNDATION_FEATURE = "inbound-mail-foundation";
|
|
3
|
+
/** Default mount path of the OAuth connect route; the web client builds the reconnect link from it. */
|
|
4
|
+
export const INBOUND_MAIL_CONNECT_PATH = "/api/inbound-mail/connect";
|
|
5
|
+
/** Client action of the OAuth "Reactivate" row action: sends the browser to the OAuth connect route. */
|
|
6
|
+
export const INBOUND_MAIL_RECONNECT_OAUTH_ACTION = `${INBOUND_MAIL_FOUNDATION_FEATURE}:reconnect-oauth`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { type InboundMailFoundationClientOptions, inboundMailFoundationClient, oauthReconnectUrl, } from "./client-plugin.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { inboundMailFoundationClient, oauthReconnectUrl, } from "./client-plugin.js";
|