@cosmicdrift/kumiko-bundled-features 0.385.0 → 0.387.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/LICENSE +21 -57
- package/dist/agent-tools/tool-catalog.js +1 -1
- package/dist/agent-tools/tool-dispatch.js +0 -2
- package/dist/audit/screens.js +1 -1
- package/dist/auth-email-password/feature.js +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +3 -3
- package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
- package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
- package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.js +1 -1
- package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
- package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
- package/dist/auth-email-password/index.js +5 -5
- package/dist/auth-email-password/lockout-store.js +1 -1
- package/dist/auth-email-password/seeding.d.ts +42 -45
- package/dist/auth-email-password/seeding.js +51 -53
- package/dist/auth-email-password/signup-token-store.js +8 -8
- package/dist/auth-email-password/testing.js +3 -3
- package/dist/auth-email-password/web/auth-client.d.ts +2 -2
- package/dist/auth-email-password/web/auth-client.js +36 -37
- package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
- package/dist/auth-email-password/web/auth-gate.js +15 -16
- package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
- package/dist/auth-email-password/web/client-plugin.js +4 -5
- package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
- package/dist/auth-email-password/web/constants.js +3 -3
- package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
- package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
- package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
- package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
- package/dist/auth-email-password/web/index.js +5 -5
- package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
- package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
- package/dist/auth-email-password/web/login-screen.d.ts +1 -1
- package/dist/auth-email-password/web/login-screen.js +2 -3
- package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
- package/dist/auth-email-password/web/reset-password-screen.js +12 -13
- package/dist/auth-email-password/web/session.d.ts +10 -9
- package/dist/auth-email-password/web/session.js +28 -29
- package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
- package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
- package/dist/auth-email-password/web/signup-screen.js +13 -13
- package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
- package/dist/auth-email-password/web/tenant-switcher.js +21 -22
- package/dist/auth-email-password/web/use-shell-user.js +12 -13
- package/dist/auth-email-password/web/user-menu.d.ts +4 -4
- package/dist/auth-email-password/web/user-menu.js +12 -13
- package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
- package/dist/auth-email-password/web/verify-email-screen.js +9 -10
- package/dist/auth-foundation/boot-checks.js +4 -4
- package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
- package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
- package/dist/auth-foundation/types.js +2 -2
- package/dist/auth-mfa/handlers/disable.write.js +3 -3
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
- package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
- package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
- package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
- package/dist/auth-mfa/handlers/verify.write.js +1 -1
- package/dist/auth-mfa/token-secrets.js +1 -1
- package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
- package/dist/auth-mfa/web/client-plugin.js +1 -1
- package/dist/auth-mfa/web/i18n.js +3 -3
- package/dist/auth-mfa/web/index.js +4 -4
- package/dist/auth-mfa/web/mfa-client.js +17 -16
- package/dist/auth-mfa/web/mfa-verify-screen.js +6 -6
- package/dist/billing-foundation/aggregate-id.d.ts +5 -5
- package/dist/billing-foundation/aggregate-id.js +10 -11
- package/dist/billing-foundation/entities.js +27 -27
- package/dist/billing-foundation/events.js +18 -17
- package/dist/billing-foundation/feature.js +42 -43
- package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
- package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +18 -17
- package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
- package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
- package/dist/billing-foundation/handlers/process-event.write.js +20 -20
- package/dist/billing-foundation/projection.d.ts +15 -11
- package/dist/billing-foundation/projection.js +38 -37
- package/dist/billing-foundation/subscription-tier-sync.js +3 -3
- package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
- package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
- package/dist/billing-foundation/types.d.ts +40 -41
- package/dist/billing-foundation/types.js +20 -20
- package/dist/billing-foundation/web/billing-plans-panel.js +4 -4
- package/dist/cap-counter/aggregate-id.js +8 -9
- package/dist/cap-counter/constants.js +11 -12
- package/dist/cap-counter/enforce-cap.d.ts +59 -62
- package/dist/cap-counter/enforce-cap.js +47 -48
- package/dist/cap-counter/entity.js +29 -29
- package/dist/cap-counter/feature.js +34 -35
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
- package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
- package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
- package/dist/cap-counter/stock-cap-guard.js +1 -1
- package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
- package/dist/channel-email/smtp-transport.d.ts +18 -17
- package/dist/channel-email/smtp-transport.js +24 -21
- package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
- package/dist/compliance-profiles/feature.js +4 -4
- package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
- package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
- package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
- package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
- package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
- package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
- package/dist/compliance-profiles/schema/profile-selection.js +18 -23
- package/dist/compliance-profiles/seeding.js +14 -15
- package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
- package/dist/compliance-profiles-ops/index.js +2 -2
- package/dist/config/handlers/readiness.query.d.ts +4 -4
- package/dist/config/handlers/readiness.query.js +1 -1
- package/dist/config/handlers/reencrypt.job.js +4 -5
- package/dist/config/handlers/set.write.js +4 -4
- package/dist/config/resolver.js +4 -4
- package/dist/config/table.js +10 -10
- package/dist/config/write-helpers.js +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.js +12 -11
- package/dist/custom-fields/aggregate-id.d.ts +12 -13
- package/dist/custom-fields/aggregate-id.js +16 -17
- package/dist/custom-fields/entity.js +49 -47
- package/dist/custom-fields/events.js +7 -8
- package/dist/custom-fields/feature.d.ts +14 -14
- package/dist/custom-fields/feature.js +53 -54
- package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
- package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
- package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
- package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
- package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
- package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
- package/dist/custom-fields/lib/field-access.js +2 -2
- package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
- package/dist/custom-fields/lib/quota.js +1 -1
- package/dist/custom-fields/lib/value-schema.js +10 -12
- package/dist/custom-fields/run-retention.js +3 -3
- package/dist/custom-fields/schemas.js +14 -14
- package/dist/custom-fields/web/client-plugin.js +2 -5
- package/dist/custom-fields/web/constants.js +2 -2
- package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
- package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
- package/dist/custom-fields/wire-for-entity.js +65 -67
- package/dist/custom-fields/wire-user-data-rights.js +3 -3
- package/dist/data-retention/_internal/parse-override.js +2 -3
- package/dist/data-retention/feature.js +11 -18
- package/dist/data-retention/handlers/policy-for.query.js +6 -6
- package/dist/data-retention/keep-for.d.ts +7 -7
- package/dist/data-retention/keep-for.js +15 -15
- package/dist/data-retention/override-schema.d.ts +3 -4
- package/dist/data-retention/override-schema.js +13 -15
- package/dist/data-retention/presets.d.ts +4 -5
- package/dist/data-retention/presets.js +18 -23
- package/dist/data-retention/resolve-tenant-preset.js +17 -16
- package/dist/data-retention/resolver.d.ts +12 -12
- package/dist/data-retention/resolver.js +24 -23
- package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
- package/dist/data-retention/run-retention-cleanup.js +61 -51
- package/dist/data-retention/schema/tenant-retention-override.js +13 -11
- package/dist/delivery/feature.js +2 -2
- package/dist/delivery/handlers/log.query.js +1 -1
- package/dist/delivery/tables.js +9 -8
- package/dist/delivery/unsubscribe.js +4 -4
- package/dist/document-ingest-foundation/entity.js +4 -4
- package/dist/document-ingest-foundation/events.js +2 -2
- package/dist/document-ingest-foundation/feature.js +2 -2
- package/dist/document-ingest-foundation/pages.js +1 -1
- package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
- package/dist/feature-toggles/compose-tier-resolver.js +1 -1
- package/dist/feature-toggles/feature.js +1 -1
- package/dist/feature-toggles/global-feature-state-table.js +1 -1
- package/dist/feature-toggles/handlers/registered.query.js +5 -5
- package/dist/feature-toggles/toggle-sync-signal.js +1 -1
- package/dist/feature-toggles/web/constants.js +1 -1
- package/dist/file-derivatives/feature.d.ts +1 -1
- package/dist/file-derivatives/feature.js +1 -1
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-derivatives/presets.js +1 -1
- package/dist/file-foundation/feature.js +6 -6
- package/dist/file-provider-inmemory/feature.d.ts +1 -1
- package/dist/file-provider-inmemory/feature.js +16 -16
- package/dist/file-provider-s3/feature.js +16 -16
- package/dist/file-provider-s3/lib/s3-provider.js +6 -6
- package/dist/files/feature.js +6 -6
- package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
- package/dist/files-tenant-data/hooks.js +24 -26
- package/dist/files-tenant-data/index.js +1 -1
- package/dist/folders/feature.d.ts +1 -1
- package/dist/folders/web/folder-manager.js +2 -2
- package/dist/folders/web/tree.js +5 -5
- package/dist/form-draft/db/queries/cleanup.js +1 -1
- package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
- package/dist/form-draft/entity.js +3 -3
- package/dist/form-draft/feature.js +4 -5
- package/dist/form-draft/handlers/discard.write.js +1 -1
- package/dist/form-draft/release-file-refs.js +6 -6
- package/dist/form-draft/schemas.js +3 -3
- package/dist/form-draft/web/constants.js +0 -2
- package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
- package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
- package/dist/inbound-mail-foundation/connect-routes.js +4 -3
- package/dist/inbound-mail-foundation/constants.d.ts +22 -24
- package/dist/inbound-mail-foundation/constants.js +30 -33
- package/dist/inbound-mail-foundation/entities.d.ts +12 -12
- package/dist/inbound-mail-foundation/entities.js +47 -48
- package/dist/inbound-mail-foundation/events.d.ts +4 -3
- package/dist/inbound-mail-foundation/events.js +50 -47
- package/dist/inbound-mail-foundation/feature.js +39 -39
- package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
- package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
- package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +11 -11
- package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
- package/dist/inbound-mail-foundation/oauth-state.js +11 -10
- package/dist/inbound-mail-foundation/projection.d.ts +4 -4
- package/dist/inbound-mail-foundation/projection.js +31 -29
- package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
- package/dist/inbound-mail-foundation/provider-factory.js +9 -9
- package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
- package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
- package/dist/inbound-mail-foundation/types.d.ts +45 -46
- package/dist/inbound-mail-foundation/types.js +21 -21
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
- package/dist/inbound-mail-foundation/watch-supervisor.js +57 -56
- package/dist/inbound-provider-imap/credential-document.js +13 -13
- package/dist/inbound-provider-imap/feature.d.ts +2 -2
- package/dist/inbound-provider-imap/feature.js +42 -40
- package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
- package/dist/inbound-provider-imap/imap-client.js +20 -19
- package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
- package/dist/inbound-provider-inmemory/feature.js +19 -19
- package/dist/jobs/db/queries/retention.js +2 -2
- package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
- package/dist/jobs/events.js +1 -1
- package/dist/jobs/feature.js +5 -6
- package/dist/jobs/handlers/detail.query.js +4 -4
- package/dist/jobs/handlers/list.query.js +1 -1
- package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
- package/dist/jobs/handlers/reindex-entity.job.js +1 -1
- package/dist/jobs/handlers/retry.write.js +1 -1
- package/dist/jobs/job-instant.js +1 -1
- package/dist/jobs/job-run-logger.js +10 -11
- package/dist/jobs/job-run-table.js +3 -3
- package/dist/jobs/screens.js +1 -1
- package/dist/jobs/tenant-job-failure-table.js +1 -1
- package/dist/jobs/tenant-job-run-table.js +1 -1
- package/dist/ledger/entity.js +5 -5
- package/dist/ledger/feature.js +3 -3
- package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
- package/dist/ledger/recurring.js +12 -12
- package/dist/ledger/schemas.js +1 -1
- package/dist/ledger/web/constants.js +2 -2
- package/dist/ledger/web/index.js +7 -7
- package/dist/legal-pages/feature.d.ts +4 -4
- package/dist/legal-pages/feature.js +25 -26
- package/dist/legal-pages/markdown.js +3 -3
- package/dist/legal-pages/web/client-plugin.js +23 -24
- package/dist/legal-pages/web/index.js +0 -1
- package/dist/mail-foundation/feature.d.ts +2 -2
- package/dist/mail-foundation/feature.js +24 -25
- package/dist/mail-transport-inmemory/feature.d.ts +4 -4
- package/dist/mail-transport-inmemory/feature.js +27 -28
- package/dist/mail-transport-smtp/feature.js +20 -20
- package/dist/managed-pages/branding.js +6 -6
- package/dist/managed-pages/feature.d.ts +26 -26
- package/dist/managed-pages/feature.js +34 -34
- package/dist/managed-pages/handlers/by-slug.query.js +5 -6
- package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
- package/dist/managed-pages/handlers/set.write.js +5 -7
- package/dist/managed-pages/screens/page-screens.js +11 -11
- package/dist/managed-pages/seeding.js +8 -8
- package/dist/managed-pages/table.js +13 -12
- package/dist/managed-pages/web/client-plugin.d.ts +1 -1
- package/dist/managed-pages/web/client-plugin.js +5 -5
- package/dist/managed-pages/web/i18n.js +1 -1
- package/dist/notes-history/entity.js +4 -4
- package/dist/notes-history/feature.d.ts +2 -2
- package/dist/notes-history-user-data/hooks.js +1 -1
- package/dist/page-render/layout.js +9 -9
- package/dist/page-render/markdown.js +11 -11
- package/dist/page-render/security-headers.js +7 -6
- package/dist/personal-access-tokens/feature.js +5 -5
- package/dist/personal-access-tokens/roles.js +2 -2
- package/dist/readiness/handlers/status.query.js +3 -3
- package/dist/renderer-foundation/api.js +12 -13
- package/dist/renderer-foundation/constants.js +2 -2
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/secrets/feature.js +9 -9
- package/dist/secrets/web/constants.js +2 -2
- package/dist/seo/feature.js +3 -3
- package/dist/sessions/constants.d.ts +1 -1
- package/dist/sessions/constants.js +1 -1
- package/dist/sessions/feature.js +8 -8
- package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
- package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
- package/dist/sessions/handlers/revoke.write.js +1 -1
- package/dist/sessions/schema/user-session.js +4 -4
- package/dist/sessions/screens.js +2 -2
- package/dist/sessions/session-callbacks.js +2 -2
- package/dist/sessions/session-revoked-event.js +2 -2
- package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
- package/dist/shared/decrypt-stored-pii.js +1 -1
- package/dist/shared/encrypt-for-direct-write.js +1 -1
- package/dist/shared/map-with-concurrency.js +5 -4
- package/dist/shared/parent-visibility.js +2 -2
- package/dist/shared/password-hashing.js +1 -1
- package/dist/shared/signup-handover.js +1 -2
- package/dist/shared/single-use-token-store.js +1 -1
- package/dist/subscription-mollie/constants.js +15 -17
- package/dist/subscription-mollie/feature.d.ts +17 -18
- package/dist/subscription-mollie/feature.js +24 -24
- package/dist/subscription-mollie/index.js +3 -3
- package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
- package/dist/subscription-mollie/plugin-methods.js +15 -14
- package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
- package/dist/subscription-mollie/verify-webhook.js +27 -27
- package/dist/subscription-stripe/constants.js +4 -6
- package/dist/subscription-stripe/feature.d.ts +14 -14
- package/dist/subscription-stripe/feature.js +35 -34
- package/dist/subscription-stripe/index.js +6 -6
- package/dist/subscription-stripe/plugin-methods.js +18 -18
- package/dist/subscription-stripe/runtime.d.ts +1 -1
- package/dist/subscription-stripe/runtime.js +1 -1
- package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
- package/dist/subscription-stripe/verify-webhook.js +59 -61
- package/dist/tags/feature.d.ts +3 -3
- package/dist/tags/screens.js +1 -1
- package/dist/template-resolver/api.js +10 -10
- package/dist/template-resolver/handlers/find-by-id.query.js +5 -5
- package/dist/template-resolver/handlers/list.query.js +1 -1
- package/dist/template-resolver/handlers/shared.js +2 -2
- package/dist/template-resolver/handlers/template-write.js +3 -3
- package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
- package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
- package/dist/template-resolver/seeding.js +4 -4
- package/dist/template-resolver/table.js +10 -11
- package/dist/template-resolver/web/client-plugin.js +2 -2
- package/dist/tenant/feature.js +7 -7
- package/dist/tenant/handlers/member-directory.query.js +1 -1
- package/dist/tenant/handlers/members.query.js +1 -1
- package/dist/tenant/handlers/team-list.query.js +1 -1
- package/dist/tenant/handlers/tenant-directory.query.js +1 -1
- package/dist/tenant/invitation-table.js +37 -40
- package/dist/tenant/membership-table.js +3 -3
- package/dist/tenant/schema/tenant.js +7 -9
- package/dist/tenant/screens.js +1 -1
- package/dist/tenant/seeding.d.ts +6 -5
- package/dist/tenant/seeding.js +12 -12
- package/dist/tenant/testing.js +5 -6
- package/dist/tenant/web/constants.js +2 -2
- package/dist/tenant-handover/events.js +5 -7
- package/dist/tenant-handover/feature.js +1 -1
- package/dist/tenant-handover/grant.js +1 -1
- package/dist/tenant-handover/handlers/claim.write.js +1 -1
- package/dist/tenant-handover/move-entity-graph.js +8 -9
- package/dist/tenant-handover/signup-handover-provider.js +1 -2
- package/dist/tenant-handover/transfer-graph.js +4 -5
- package/dist/tenant-lifecycle/boot-checks.js +1 -1
- package/dist/tenant-lifecycle/feature.js +1 -1
- package/dist/tenant-settings/constants.js +1 -1
- package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
- package/dist/tenant-settings/tenant-defaults.js +9 -2
- package/dist/tier-engine/aggregate-id.d.ts +11 -11
- package/dist/tier-engine/aggregate-id.js +15 -16
- package/dist/tier-engine/compose-app.d.ts +14 -14
- package/dist/tier-engine/compose-app.js +1 -1
- package/dist/tier-engine/entity.js +18 -19
- package/dist/tier-engine/feature.d.ts +19 -17
- package/dist/tier-engine/feature.js +68 -69
- package/dist/tier-engine/handlers/active-tier.query.js +6 -7
- package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +5 -5
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +3 -3
- package/dist/tier-engine/tier-resolver.js +2 -2
- package/dist/tier-engine/trial.js +8 -8
- package/dist/tier-engine/web/index.js +4 -4
- package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
- package/dist/user/handlers/create.write.js +4 -4
- package/dist/user/schema/user.d.ts +11 -11
- package/dist/user/schema/user.js +45 -44
- package/dist/user/seeding.d.ts +15 -15
- package/dist/user/seeding.js +28 -28
- package/dist/user/testing.js +3 -3
- package/dist/user-data-rights/audit-download.d.ts +8 -8
- package/dist/user-data-rights/audit-download.js +19 -20
- package/dist/user-data-rights/boot-checks.js +1 -1
- package/dist/user-data-rights/deletion-token.js +1 -1
- package/dist/user-data-rights/feature.d.ts +49 -50
- package/dist/user-data-rights/feature.js +54 -53
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
- package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
- package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
- package/dist/user-data-rights/handlers/export-status.query.js +8 -9
- package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
- package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
- package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
- package/dist/user-data-rights/handlers/request-export.write.js +42 -41
- package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
- package/dist/user-data-rights/index.js +8 -10
- package/dist/user-data-rights/lib/default-mailers.js +3 -3
- package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
- package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
- package/dist/user-data-rights/run-export-jobs.js +127 -233
- package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
- package/dist/user-data-rights/run-forget-cleanup.js +63 -82
- package/dist/user-data-rights/run-user-export.d.ts +34 -33
- package/dist/user-data-rights/run-user-export.js +55 -57
- package/dist/user-data-rights/schema/download-attempt.js +11 -11
- package/dist/user-data-rights/schema/download-token.js +37 -41
- package/dist/user-data-rights/schema/export-job.d.ts +4 -4
- package/dist/user-data-rights/schema/export-job.js +65 -67
- package/dist/user-data-rights/token-helpers.d.ts +8 -9
- package/dist/user-data-rights/token-helpers.js +21 -25
- package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
- package/dist/user-data-rights/web/client-plugin.js +1 -1
- package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
- package/dist/user-data-rights/web/constants.js +1 -1
- package/dist/user-data-rights/web/index.js +5 -5
- package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
- package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
- package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
- package/dist/user-data-rights/zip-path.d.ts +15 -15
- package/dist/user-data-rights/zip-path.js +41 -40
- package/dist/user-data-rights-defaults/feature.js +15 -15
- package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
- package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
- package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
- package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
- package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
- package/dist/user-profile/handlers/change-email.write.js +13 -13
- package/dist/user-profile/index.js +3 -3
- package/dist/user-profile/web/client-plugin.js +1 -1
- package/dist/user-profile/web/constants.js +1 -1
- package/dist/user-profile/web/profile-screen.js +1 -1
- package/dist/workflow-runner/aggregate-id.d.ts +1 -1
- package/dist/workflow-runner/aggregate-id.js +1 -1
- package/dist/workflow-runner/event-subscriber.js +4 -5
- package/dist/workflow-runner/event-trigger.js +4 -17
- package/dist/workflow-runner/feature.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
- package/dist/workflow-runner/pending-projection.js +6 -7
- package/dist/workflow-runner/runner.js +6 -6
- package/dist/workflow-runner/tables.js +6 -6
- package/dist/workflow-runner/workflow-registry.js +1 -1
- package/package.json +10 -10
|
@@ -22,23 +22,23 @@ export type InboundMailContext = {
|
|
|
22
22
|
readonly metricsFor?: (featureName: string) => MetricsHandle;
|
|
23
23
|
};
|
|
24
24
|
/**
|
|
25
|
-
* Plaintext
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* withPiiCiphertextGuard
|
|
25
|
+
* Plaintext view of a connected mailbox exposed to providers.
|
|
26
|
+
* The Foundation decrypts `address` BEFORE the provider call; providers
|
|
27
|
+
* never receive `kumiko-pii:` ciphertext (guard analogous to
|
|
28
|
+
* withPiiCiphertextGuard in the transport path).
|
|
29
29
|
*/
|
|
30
30
|
export type MailAccountRecord = {
|
|
31
|
-
/** = aggregateId
|
|
31
|
+
/** = aggregateId of the mail-account stream and the secret-slot key. */
|
|
32
32
|
readonly id: string;
|
|
33
33
|
readonly tenantId: string;
|
|
34
|
-
/** Provider
|
|
34
|
+
/** Provider key registered with the extension ("imap", "m365-graph",
|
|
35
35
|
* "gmail-rest"). */
|
|
36
36
|
readonly provider: string;
|
|
37
|
-
/** "password" | "xoauth2" | "oauth" — imap
|
|
38
|
-
*
|
|
37
|
+
/** "password" | "xoauth2" | "oauth" — imap uses this to distinguish
|
|
38
|
+
* form-based connect from the XOAUTH2 fallback. */
|
|
39
39
|
readonly authMethod: string;
|
|
40
|
-
/** null = tenant-
|
|
41
|
-
*
|
|
40
|
+
/** null = tenant-shared mailbox; otherwise this user's personal mailbox
|
|
41
|
+
* (visibility scope). */
|
|
42
42
|
readonly ownerUserId: string | null;
|
|
43
43
|
/** Postfach-Adresse, Plaintext (decrypted). */
|
|
44
44
|
readonly address: string;
|
|
@@ -47,36 +47,35 @@ export type MailAccountRecord = {
|
|
|
47
47
|
readonly watchState: string;
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
* Provider-
|
|
52
|
-
*
|
|
50
|
+
* Normalized incoming mail — provider output and ingest input.
|
|
51
|
+
* Provider-specific structures (IMAP flags, Graph objects) remain in the
|
|
52
|
+
* provider; this type contains only the domain-level boundary.
|
|
53
53
|
*/
|
|
54
54
|
export type RawInboundMessage = {
|
|
55
|
-
/** Provider-native
|
|
56
|
-
*
|
|
55
|
+
/** Provider-native message ID (IMAP UID, Graph id, Gmail id) — the
|
|
56
|
+
* idempotency anchor together with providerName. */
|
|
57
57
|
readonly providerMessageId: string;
|
|
58
|
-
/** RFC-5322 Message-ID
|
|
59
|
-
* (Foundation
|
|
58
|
+
/** RFC-5322 Message-ID header without <>; null when the header is missing
|
|
59
|
+
* (the Foundation falls back to a deterministic replacement). */
|
|
60
60
|
readonly messageIdHeader: string | null;
|
|
61
|
-
/** Provider-native
|
|
62
|
-
* conversationId, Gmail threadId); Foundation
|
|
63
|
-
* References
|
|
61
|
+
/** Provider-native thread/conversation ID when available (Graph
|
|
62
|
+
* conversationId, Gmail threadId); the Foundation prefers it over the
|
|
63
|
+
* References chain when building threadKey. */
|
|
64
64
|
readonly providerThreadId: string | null;
|
|
65
|
-
/** References/In-Reply-To
|
|
65
|
+
/** References/In-Reply-To chain (message IDs without <>), oldest first. */
|
|
66
66
|
readonly references: readonly string[];
|
|
67
67
|
readonly from: string;
|
|
68
68
|
readonly to: readonly string[];
|
|
69
69
|
readonly cc: readonly string[];
|
|
70
70
|
readonly subject: string;
|
|
71
|
-
/** Plaintext
|
|
71
|
+
/** Plaintext preview generated by the provider or truncated from the body. */
|
|
72
72
|
readonly snippet: string;
|
|
73
|
-
/** ISO
|
|
73
|
+
/** ISO instant — INTERNALDATE or receivedDateTime, not ingest time. */
|
|
74
74
|
readonly receivedAtIso: string;
|
|
75
|
-
/** Raw MIME; null
|
|
76
|
-
* file-foundation
|
|
77
|
-
* blob-frei. */
|
|
75
|
+
/** Raw MIME; null in snippet-only mode. The Foundation persists it through
|
|
76
|
+
* file-foundation and writes bodyRef, so the event store remains blob-free. */
|
|
78
77
|
readonly rawMime: Uint8Array | null;
|
|
79
|
-
/**
|
|
78
|
+
/** Generic scope hint ("inbox", folder name), with no application semantics. */
|
|
80
79
|
readonly scope: string;
|
|
81
80
|
};
|
|
82
81
|
/** Provider-opaker Cursor: IMAP {uidValidity,lastUid} · Graph {deltaLink}
|
|
@@ -86,8 +85,8 @@ export type SyncCursorPayload = Readonly<Record<string, unknown>>;
|
|
|
86
85
|
export type InboundFetchResult = {
|
|
87
86
|
readonly messages: readonly RawInboundMessage[];
|
|
88
87
|
readonly nextCursor: SyncCursorPayload;
|
|
89
|
-
/** true ⇒ Foundation
|
|
90
|
-
* (
|
|
88
|
+
/** true ⇒ the Foundation calls fetch again in the same poll tick
|
|
89
|
+
* (pagination) until false or the maxMessages budget is exhausted. */
|
|
91
90
|
readonly hasMore: boolean;
|
|
92
91
|
};
|
|
93
92
|
export type OAuthTokenSet = {
|
|
@@ -98,13 +97,13 @@ export type OAuthTokenSet = {
|
|
|
98
97
|
readonly scopesGranted: readonly string[];
|
|
99
98
|
};
|
|
100
99
|
/**
|
|
101
|
-
* OAuth
|
|
102
|
-
*
|
|
103
|
-
* `/inbound-mail/oauth/callback`)
|
|
104
|
-
*
|
|
100
|
+
* OAuth portion of the contract — OAuth providers only; imap in password
|
|
101
|
+
* mode omits `oauth`. The Foundation provides the routes
|
|
102
|
+
* (`/inbound-mail/connect`, `/inbound-mail/oauth/callback`) generically;
|
|
103
|
+
* providers MUST NOT register their own routes.
|
|
105
104
|
*/
|
|
106
105
|
export type InboundOAuthFlow = {
|
|
107
|
-
/** Scopes:
|
|
106
|
+
/** Scopes: request receive AND send in a SINGLE consent; re-consent is costly. */
|
|
108
107
|
readonly scopes: {
|
|
109
108
|
readonly receive: readonly string[];
|
|
110
109
|
readonly send?: readonly string[];
|
|
@@ -128,17 +127,17 @@ export type InboundOAuthFlow = {
|
|
|
128
127
|
}) => Promise<OAuthTokenSet>;
|
|
129
128
|
};
|
|
130
129
|
/**
|
|
131
|
-
* Inbound-
|
|
132
|
-
* (inbound-provider-imap, -m365-graph, -gmail-rest)
|
|
133
|
-
*
|
|
130
|
+
* Inbound-mail provider plugin contract. Each provider feature
|
|
131
|
+
* (inbound-provider-imap, -m365-graph, -gmail-rest) registers an
|
|
132
|
+
* implementation via `r.useExtension(INBOUND_MAIL_PROVIDER_EXTENSION,
|
|
134
133
|
* "<key>", plugin)`.
|
|
135
134
|
*/
|
|
136
135
|
export type InboundMailProviderPlugin = {
|
|
137
|
-
/**
|
|
138
|
-
*
|
|
136
|
+
/** Credential/reachability check during the connect flow — throws a
|
|
137
|
+
* typed error (InboundAuthError, etc.) and returns no value. */
|
|
139
138
|
readonly verify: (ctx: InboundMailContext, account: MailAccountRecord) => Promise<void>;
|
|
140
|
-
/**
|
|
141
|
-
*
|
|
139
|
+
/** Fetch since cursor. `cursor === null` means initial population,
|
|
140
|
+
* limited by backfillWindowDays. */
|
|
142
141
|
readonly fetch: (ctx: InboundMailContext, account: MailAccountRecord, cursor: SyncCursorPayload | null, opts: {
|
|
143
142
|
readonly backfillWindowDays: number;
|
|
144
143
|
readonly maxMessages: number;
|
|
@@ -154,11 +153,11 @@ export type InboundMailProviderPlugin = {
|
|
|
154
153
|
}) => Promise<InboundFetchResult>;
|
|
155
154
|
/** Nur OAuth-Provider. */
|
|
156
155
|
readonly oauth?: InboundOAuthFlow;
|
|
157
|
-
/** Optional
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
156
|
+
/** Optional live push (IMAP IDLE, later Graph/Gmail webhooks).
|
|
157
|
+
* Returns stop(). Polling remains reconciliation: watch optimizes
|
|
158
|
+
* latency but is never the correctness anchor. `onError` reports a dead
|
|
159
|
+
* live connection (drop, auth revocation) to the supervisor, which
|
|
160
|
+
* restarts it with backoff; the provider does NOT reconnect itself. */
|
|
162
161
|
readonly watch?: (ctx: InboundMailContext, account: MailAccountRecord, handlers: {
|
|
163
162
|
readonly onMessages: (msgs: readonly RawInboundMessage[]) => Promise<void>;
|
|
164
163
|
readonly onError: (err: unknown) => void;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
// Provider
|
|
1
|
+
// Provider contract for inbound-mail provider plugins.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Mirrored from mail-foundation's MailTransportContext/-Plugin —
|
|
4
|
+
// the Foundation/provider separation and slim context are identical.
|
|
5
5
|
//
|
|
6
|
-
// **
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
6
|
+
// **Why not a full HandlerContext?** fetch()/watch() run in the worker
|
|
7
|
+
// path (poll cron, IDLE supervisor), where there is no per-request
|
|
8
|
+
// `tx`/`actor`. A plugin that reads more would break the worker path at
|
|
9
|
+
// runtime, and this would only be noticed in production. Plugins that need
|
|
10
|
+
// more must explicitly extend InboundMailContext (a visible breaking change)
|
|
11
|
+
// instead of casting ctx.
|
|
12
12
|
import { INBOUND_MAIL_PROVIDER_EXTENSION } from "./constants.js";
|
|
13
|
-
//
|
|
14
|
-
//
|
|
13
|
+
// `options` in the extension usage is an engine payload (unknown), so
|
|
14
|
+
// validate its structure instead of casting blindly (patterned after
|
|
15
|
+
// isMailTransportPlugin).
|
|
15
16
|
export function isInboundMailProviderPlugin(o) {
|
|
16
17
|
return (typeof o === "object" &&
|
|
17
18
|
o !== null &&
|
|
@@ -21,18 +22,17 @@ export function isInboundMailProviderPlugin(o) {
|
|
|
21
22
|
typeof o.fetch === "function");
|
|
22
23
|
}
|
|
23
24
|
// =============================================================================
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// InboundTransientError → Job-Retry (retries: 3, exponential)
|
|
25
|
+
// Typed errors handled uniformly by the Foundation during polling:
|
|
26
|
+
// InboundAuthError → status=auth_error, no retry
|
|
27
|
+
// InboundRateLimitError → abort poll, wait for the next cron tick
|
|
28
|
+
// InboundCursorInvalidError → reset cursor + full resync in the backfill
|
|
29
|
+
// window; deduplication handles duplicates
|
|
30
|
+
// InboundTransientError → job retry (retries: 3, exponential)
|
|
31
31
|
// =============================================================================
|
|
32
32
|
//
|
|
33
|
-
// `kind
|
|
34
|
-
//
|
|
35
|
-
//
|
|
33
|
+
// Use a `kind` discriminant instead of instanceof: plugins and the Foundation
|
|
34
|
+
// may run in separate bundles (dual-package-hazard), and instanceof is
|
|
35
|
+
// unreliable across realm or package-copy boundaries.
|
|
36
36
|
export class InboundAuthError extends Error {
|
|
37
37
|
kind = "inbound-auth-error";
|
|
38
38
|
constructor(message) {
|
|
@@ -3,13 +3,13 @@ import type { DistributedLock } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
|
3
3
|
import { type OAuthAccessTokenManager } from "./oauth-access-token.ts";
|
|
4
4
|
import { type InboundMailContext, type MailAccountRecord, type RawInboundMessage } from "./types.ts";
|
|
5
5
|
export type InboundMailSupervisorDeps = {
|
|
6
|
-
/** Slim
|
|
7
|
-
*
|
|
6
|
+
/** Slim context for provider calls (`registry` is required; `secrets` is
|
|
7
|
+
* used for provider credential reads). */
|
|
8
8
|
readonly providerCtx: InboundMailContext;
|
|
9
|
-
/** App
|
|
10
|
-
*
|
|
9
|
+
/** App database — direct reads from read_mail_accounts (account snapshot)
|
|
10
|
+
* and direct writes to store_mail_sync_cursors (unmanaged store). */
|
|
11
11
|
readonly db: DbConnection;
|
|
12
|
-
/** Standard
|
|
12
|
+
/** Standard dispatcher with SystemUser — carries ingest-message and
|
|
13
13
|
* update-account. */
|
|
14
14
|
readonly dispatchWrite: (args: {
|
|
15
15
|
readonly handlerQn: string;
|
|
@@ -20,8 +20,8 @@ export type InboundMailSupervisorDeps = {
|
|
|
20
20
|
readonly data?: unknown;
|
|
21
21
|
readonly error?: unknown;
|
|
22
22
|
}>;
|
|
23
|
-
/**
|
|
24
|
-
*
|
|
23
|
+
/** Persists the raw MIME body (file-foundation) and returns its bodyRef
|
|
24
|
+
* for the event. Without this hook, runs in snippet-only mode
|
|
25
25
|
* (bodyRef = ""). */
|
|
26
26
|
readonly storeBody?: (account: MailAccountRecord, msg: RawInboundMessage) => Promise<string>;
|
|
27
27
|
readonly pollIntervalMs?: number;
|
|
@@ -33,7 +33,7 @@ export type InboundMailSupervisorDeps = {
|
|
|
33
33
|
* before `plugin.watch()`, ensureWatcher claims a TTL lease for the
|
|
34
34
|
* account via `lock.acquire` and renews it via `lock.renew` while the
|
|
35
35
|
* watcher lives. Omit for single-process deployments — every active
|
|
36
|
-
* account is watched locally, same as before
|
|
36
|
+
* account is watched locally, same as before. */
|
|
37
37
|
readonly lock?: DistributedLock;
|
|
38
38
|
/** Access-token lifecycle for OAuth accounts. Defaults to a manager built
|
|
39
39
|
* from `db` + `providerCtx.secrets`; pass the instance shared with
|
|
@@ -46,8 +46,8 @@ export type InboundMailSupervisorDeps = {
|
|
|
46
46
|
};
|
|
47
47
|
export type InboundMailSupervisor = {
|
|
48
48
|
readonly start: () => Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
*
|
|
49
|
+
/** Reconciliation run across all active accounts; also usable standalone
|
|
50
|
+
* for tests and manual operational triggers. */
|
|
51
51
|
readonly pollOnce: () => Promise<void>;
|
|
52
52
|
readonly stop: () => Promise<void>;
|
|
53
53
|
};
|
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
// createInboundMailSupervisor —
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
1
|
+
// createInboundMailSupervisor — the long-lived Foundation sync process:
|
|
2
|
+
// starts `plugin.watch()` (IMAP IDLE, seconds-level push) for each active
|
|
3
|
+
// account with reconnect backoff, and also runs the periodic reconciliation
|
|
4
|
+
// poll (default: 5 min) through `plugin.fetch()`.
|
|
5
|
+
// Deduplication in the ingest handler makes watch/poll overlap idempotent:
|
|
6
|
+
// the poll is the correctness anchor, while watch only reduces latency.
|
|
7
7
|
//
|
|
8
|
-
// **
|
|
9
|
-
// cron trigger.
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// app owner still wires the supervisor in bin/server.ts:
|
|
8
|
+
// **Documented deviation:** the poll was originally intended as an `r.job`
|
|
9
|
+
// cron trigger. JobContext previously had no dispatcher (see
|
|
10
|
+
// run-export-jobs.ts), but now has one (`ctx.write`/`ctx.queryAs`,
|
|
11
|
+
// job-runner.ts). Converting the poll to a cron job is a separate migration
|
|
12
|
+
// because it changes the concurrency model, so this dispatcher contract
|
|
13
|
+
// remains in place. The app owner wires the supervisor in bin/server.ts:
|
|
15
14
|
//
|
|
16
15
|
// const supervisor = createInboundMailSupervisor({
|
|
17
16
|
// providerCtx: { registry: deps.registry, secrets, metricsFor: deps.metricsFor },
|
|
@@ -20,29 +19,29 @@
|
|
|
20
19
|
// deps.dispatchSystemWrite({ handlerQn, payload, tenantId: tenantId as TenantId }),
|
|
21
20
|
// // Multi-worker deployments: share one DistributedLock (same Redis,
|
|
22
21
|
// // same key prefix) across every worker process so only one of them
|
|
23
|
-
// // holds the IMAP IDLE connection per account
|
|
22
|
+
// // holds the IMAP IDLE connection per account. Omit `lock`
|
|
24
23
|
// // for a single-process deployment — every active account gets
|
|
25
|
-
// // watched locally,
|
|
24
|
+
// // watched locally, as before.
|
|
26
25
|
// lock: createDistributedLock(redis, `${RedisKeys.lock}inbound-mail:watch:`),
|
|
27
26
|
// });
|
|
28
27
|
// await supervisor.start();
|
|
29
28
|
// // shutdown-hook: await supervisor.stop();
|
|
30
29
|
//
|
|
31
|
-
// **IDLE operational risk
|
|
32
|
-
//
|
|
33
|
-
//
|
|
30
|
+
// **IDLE operational risk:** long-lived sockets run in-process.
|
|
31
|
+
// Backoff restart on onError, clean shutdown of every watcher, and the poll
|
|
32
|
+
// covering every gap mitigate this risk.
|
|
34
33
|
//
|
|
35
|
-
// **Multi-worker coordination
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// the account
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
34
|
+
// **Multi-worker coordination:** the reconciliation poll remains deliberately
|
|
35
|
+
// N-fold: every worker polls every active account, which is idempotent and
|
|
36
|
+
// cheap. Only `plugin.watch()` holds a long-lived connection, and only one
|
|
37
|
+
// worker may hold it per account. With `deps.lock`, `ensureWatcher` claims a
|
|
38
|
+
// TTL lease (`lock.acquire`) before connecting; a worker that fails to claim
|
|
39
|
+
// it leaves the account to its current holder, while the poll still covers
|
|
40
|
+
// it. The holder renews the lease via heartbeat (`lock.renew`, every ttl/3)
|
|
41
|
+
// for as long as the watcher state exists, including reconnect backoff, not
|
|
42
|
+
// only while the connection is open. Losing the claim (for example through
|
|
43
|
+
// a Redis outage or expired TTL) tears down the local watcher instead of
|
|
44
|
+
// racing a new holder.
|
|
46
45
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
47
46
|
import { fetchOne, insertOne, selectMany, updateMany } from "@cosmicdrift/kumiko-framework/db";
|
|
48
47
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
@@ -64,8 +63,8 @@ const DEFAULT_MAX_MESSAGES_PER_POLL = 200;
|
|
|
64
63
|
const DEFAULT_WATCH_BACKOFF_INITIAL_MS = 5_000;
|
|
65
64
|
const DEFAULT_WATCH_BACKOFF_MAX_MS = 5 * 60 * 1000;
|
|
66
65
|
const DEFAULT_WATCH_LEASE_TTL_SECONDS = 90;
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
66
|
+
/** One cursor per account (one mailbox inbox) for now; multi-folder support
|
|
67
|
+
* can add further scopes later without a schema change. */
|
|
69
68
|
const CURSOR_SCOPE = "default";
|
|
70
69
|
export function createInboundMailSupervisor(deps) {
|
|
71
70
|
const pollIntervalMs = deps.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
@@ -103,8 +102,8 @@ export function createInboundMailSupervisor(deps) {
|
|
|
103
102
|
let pollInFlight = null;
|
|
104
103
|
const watchers = new Map();
|
|
105
104
|
// ---------------------------------------------------------------
|
|
106
|
-
// Account
|
|
107
|
-
// (
|
|
105
|
+
// Account snapshot: active accounts of all tenants with the address
|
|
106
|
+
// decrypted (providers never work with ciphertext).
|
|
108
107
|
// ---------------------------------------------------------------
|
|
109
108
|
async function listActiveAccounts() {
|
|
110
109
|
const rows = await selectMany(deps.db, mailAccountsProjectionTable, {
|
|
@@ -154,7 +153,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
154
153
|
return isPlainObject(parsed) ? parsed : null;
|
|
155
154
|
}
|
|
156
155
|
catch {
|
|
157
|
-
return null; //
|
|
156
|
+
return null; // a corrupt cursor counts as none → backfill; dedup catches duplicates
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
159
|
async function saveCursor(account, cursor) {
|
|
@@ -167,7 +166,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
167
166
|
});
|
|
168
167
|
if (existing) {
|
|
169
168
|
await updateMany(deps.db, syncCursorTable, { cursor: serialized, updatedAt: now }, { accountId, scope: CURSOR_SCOPE });
|
|
170
|
-
// skip: update
|
|
169
|
+
// skip: the update path is complete; do not fall through to the insert branch.
|
|
171
170
|
return;
|
|
172
171
|
}
|
|
173
172
|
await insertOne(deps.db, syncCursorTable, {
|
|
@@ -181,12 +180,12 @@ export function createInboundMailSupervisor(deps) {
|
|
|
181
180
|
});
|
|
182
181
|
}
|
|
183
182
|
async function resetCursor(accountId) {
|
|
184
|
-
//
|
|
185
|
-
//
|
|
183
|
+
// No deleteMany import is needed: an empty cursor parses to null, so the
|
|
184
|
+
// next fetch runs as a backfill.
|
|
186
185
|
await updateMany(deps.db, syncCursorTable, { cursor: "", updatedAt: Temporal.Now.instant().toString() }, { accountId, scope: CURSOR_SCOPE });
|
|
187
186
|
}
|
|
188
187
|
// ---------------------------------------------------------------
|
|
189
|
-
// Ingest
|
|
188
|
+
// Ingest: every message goes through the standard write handler.
|
|
190
189
|
// ---------------------------------------------------------------
|
|
191
190
|
async function ingestBatch(account, msgs, cursorSnapshot) {
|
|
192
191
|
for (const msg of msgs) {
|
|
@@ -214,9 +213,9 @@ export function createInboundMailSupervisor(deps) {
|
|
|
214
213
|
},
|
|
215
214
|
});
|
|
216
215
|
if (!result.isSuccess) {
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
216
|
+
// A single-message failure aborts the batch without persisting the cursor.
|
|
217
|
+
// The next tick refetches from the old cursor, and deduplication skips
|
|
218
|
+
// messages that were already processed.
|
|
220
219
|
throw new Error(`ingest-message failed for account ${account.id}: ${JSON.stringify(result.error ?? {})}`);
|
|
221
220
|
}
|
|
222
221
|
}
|
|
@@ -229,8 +228,8 @@ export function createInboundMailSupervisor(deps) {
|
|
|
229
228
|
});
|
|
230
229
|
}
|
|
231
230
|
// ---------------------------------------------------------------
|
|
232
|
-
//
|
|
233
|
-
//
|
|
231
|
+
// Error semantics, shared by poll and watch. Returns true when the
|
|
232
|
+
// account may keep running.
|
|
234
233
|
// ---------------------------------------------------------------
|
|
235
234
|
async function handleSyncError(account, err) {
|
|
236
235
|
if (isInboundAuthError(err)) {
|
|
@@ -241,14 +240,14 @@ export function createInboundMailSupervisor(deps) {
|
|
|
241
240
|
}
|
|
242
241
|
if (isInboundRateLimitError(err)) {
|
|
243
242
|
log(`inbound-mail: account ${account.id} rate-limited (retryAfter ${err.retryAfterMs}ms)`);
|
|
244
|
-
return true; //
|
|
243
|
+
return true; // The next tick retries it
|
|
245
244
|
}
|
|
246
245
|
if (isInboundCursorInvalidError(err)) {
|
|
247
246
|
log(`inbound-mail: account ${account.id} cursor invalid — full resync in backfill window`);
|
|
248
247
|
await resetCursor(account.id);
|
|
249
248
|
return true;
|
|
250
249
|
}
|
|
251
|
-
// Transient
|
|
250
|
+
// Transient or unknown error: log it and retry on the next tick.
|
|
252
251
|
log(`inbound-mail: account ${account.id} sync error: ${describeError(err)}`);
|
|
253
252
|
return true;
|
|
254
253
|
}
|
|
@@ -272,7 +271,8 @@ export function createInboundMailSupervisor(deps) {
|
|
|
272
271
|
let cursor = await loadCursor(account.id);
|
|
273
272
|
let budget = maxMessagesPerPoll;
|
|
274
273
|
try {
|
|
275
|
-
// hasMore
|
|
274
|
+
// hasMore loop: paginate within this tick until the message budget is
|
|
275
|
+
// exhausted.
|
|
276
276
|
for (;;) {
|
|
277
277
|
const accessToken = await resolveAccessToken(account, plugin);
|
|
278
278
|
const result = await plugin.fetch(deps.providerCtx, account, cursor, {
|
|
@@ -308,7 +308,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
308
308
|
if (running)
|
|
309
309
|
await ensureWatcher(account, plugin);
|
|
310
310
|
}
|
|
311
|
-
//
|
|
311
|
+
// Remove watchers for accounts that are no longer active.
|
|
312
312
|
const activeIds = new Set(accounts.map((a) => a.id));
|
|
313
313
|
for (const accountId of watchers.keys()) {
|
|
314
314
|
if (!activeIds.has(accountId))
|
|
@@ -316,7 +316,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
// ---------------------------------------------------------------
|
|
319
|
-
// Watch
|
|
319
|
+
// Watch lifecycle with backoff restart.
|
|
320
320
|
// ---------------------------------------------------------------
|
|
321
321
|
// Heartbeat for a held watch lease. Independent of the connection's own
|
|
322
322
|
// lifecycle — it keeps renewing across reconnect backoff, not just while
|
|
@@ -368,7 +368,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
368
368
|
scheduleRenew(account, state, generation);
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
|
-
// kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional
|
|
371
|
+
// kumiko-lint-ignore complexity-budget yield-point stale-map check after acquire is intentional
|
|
372
372
|
async function acquireWatchLease(account, state) {
|
|
373
373
|
if (!deps.lock || state.lockToken)
|
|
374
374
|
return "acquired";
|
|
@@ -390,11 +390,11 @@ export function createInboundMailSupervisor(deps) {
|
|
|
390
390
|
}
|
|
391
391
|
// kumiko-lint-ignore complexity-budget watch lifecycle (start/stop/restart) stays cohesive
|
|
392
392
|
async function ensureWatcher(account, plugin) {
|
|
393
|
-
// skip:
|
|
393
|
+
// skip: provider without live push; the reconciliation poll covers the account.
|
|
394
394
|
if (!plugin.watch)
|
|
395
395
|
return;
|
|
396
396
|
const existing = watchers.get(account.id);
|
|
397
|
-
// skip:
|
|
397
|
+
// skip: the watcher is already running, starting, or scheduled for restart.
|
|
398
398
|
if (existing?.stop || existing?.restartTimer || existing?.starting)
|
|
399
399
|
return;
|
|
400
400
|
const state = existing ?? {
|
|
@@ -425,7 +425,8 @@ export function createInboundMailSupervisor(deps) {
|
|
|
425
425
|
}
|
|
426
426
|
const generation = state.generation;
|
|
427
427
|
const scheduleRestart = (err) => {
|
|
428
|
-
// skip:
|
|
428
|
+
// skip: the supervisor stopped or the watcher generation changed; a restart
|
|
429
|
+
// would create a zombie watcher.
|
|
429
430
|
if (!running || state.generation !== generation)
|
|
430
431
|
return;
|
|
431
432
|
state.stop = null;
|
|
@@ -464,7 +465,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
464
465
|
}, accessToken !== undefined ? { accessToken } : undefined);
|
|
465
466
|
if (state.generation !== generation || !running) {
|
|
466
467
|
await stop();
|
|
467
|
-
// skip: stop()
|
|
468
|
+
// skip: stop() ran while connecting; the watcher was torn down right away.
|
|
468
469
|
return;
|
|
469
470
|
}
|
|
470
471
|
state.stop = stop;
|
|
@@ -492,7 +493,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
492
493
|
}
|
|
493
494
|
async function stopWatcher(accountId) {
|
|
494
495
|
const state = watchers.get(accountId);
|
|
495
|
-
// skip:
|
|
496
|
+
// skip: no watcher state for this account; it is already torn down.
|
|
496
497
|
if (!state)
|
|
497
498
|
return;
|
|
498
499
|
state.generation += 1;
|
|
@@ -533,7 +534,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
533
534
|
// Lifecycle.
|
|
534
535
|
// ---------------------------------------------------------------
|
|
535
536
|
function scheduleNextPoll() {
|
|
536
|
-
// skip:
|
|
537
|
+
// skip: the supervisor is stopped; do not schedule another poll tick.
|
|
537
538
|
if (!running)
|
|
538
539
|
return;
|
|
539
540
|
pollTimer = setTimeout(() => {
|
|
@@ -548,7 +549,7 @@ export function createInboundMailSupervisor(deps) {
|
|
|
548
549
|
}
|
|
549
550
|
return {
|
|
550
551
|
async start() {
|
|
551
|
-
// skip:
|
|
552
|
+
// skip: already started; start() is idempotent.
|
|
552
553
|
if (running)
|
|
553
554
|
return;
|
|
554
555
|
running = true;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
// Connection
|
|
2
|
-
// (inboundCredentialSecretKey(accountId)
|
|
1
|
+
// Connection document stored in the per-account secret slot
|
|
2
|
+
// (inboundCredentialSecretKey(accountId)).
|
|
3
3
|
//
|
|
4
|
-
// **
|
|
5
|
-
// mail-transport-smtp (
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
4
|
+
// **Why JSON in the secret instead of tenant config keys:** unlike
|
|
5
|
+
// mail-transport-smtp (one SMTP configuration per tenant), inbound mail
|
|
6
|
+
// supports multiple accounts per tenant with different hosts, which
|
|
7
|
+
// per-tenant config keys cannot represent. Host, port, and user belong
|
|
8
|
+
// with the password in one connection document; the slot is encrypted
|
|
9
|
+
// at rest.
|
|
10
10
|
//
|
|
11
|
-
//
|
|
11
|
+
// The account's authMethod determines the interpretation:
|
|
12
12
|
// password → { host, port, secure, user, password }
|
|
13
|
-
// xoauth2 → { host, port, secure, user, accessToken } — V1
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
13
|
+
// xoauth2 → { host, port, secure, user, accessToken } — V1 fallback:
|
|
14
|
+
// OAuth providers (M365/Gmail) handle token acquisition and
|
|
15
|
+
// refresh; the imap provider only consumes a valid access
|
|
16
|
+
// token from the slot.
|
|
17
17
|
import * as z from "zod";
|
|
18
18
|
export const imapCredentialDocumentSchema = z.object({
|
|
19
19
|
host: z.string().min(1).max(255),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type InboundMailProviderPlugin } from "@cosmicdrift/kumiko-bundled-features/inbound-mail-foundation";
|
|
2
2
|
export declare const IMAP_PROVIDER_KEY = "imap";
|
|
3
3
|
export { setImapMailHostLookup } from "./imap-client.ts";
|
|
4
|
-
/**
|
|
5
|
-
*
|
|
4
|
+
/** Exported for the live test (imap-live.integration.test.ts) —
|
|
5
|
+
* applications use the feature, never the plugin directly. */
|
|
6
6
|
export declare const imapInboundMailPlugin: InboundMailProviderPlugin;
|
|
7
7
|
export declare function createInboundProviderImapFeature(): import("@cosmicdrift/kumiko-types/feature").FeatureDefinition & {
|
|
8
8
|
readonly exports: void;
|