@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
|
@@ -5,75 +5,74 @@ import { type GdprMailDefaults } from "./lib/default-mailers.ts";
|
|
|
5
5
|
import { type SendExportFailedEmailFn, type SendExportReadyEmailFn } from "./run-export-jobs.ts";
|
|
6
6
|
import { type SendDeletionExecutedEmailFn } from "./run-forget-cleanup.ts";
|
|
7
7
|
/**
|
|
8
|
-
* Options
|
|
9
|
-
*
|
|
8
|
+
* Options for createUserDataRightsFeature. Notification callbacks follow
|
|
9
|
+
* the password-reset pattern in auth-routes.ts.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* (
|
|
14
|
-
* `mailFoundation.send`
|
|
11
|
+
* The plain token is never stored in the DB, event store, or jobRunsTable.
|
|
12
|
+
* The worker passes it ephemerally as a callback argument to the app-author
|
|
13
|
+
* implementation (typically `delivery.notify(...)` with `r.notification`
|
|
14
|
+
* templates, `mailFoundation.send` directly, or custom Resend/SES).
|
|
15
15
|
*
|
|
16
|
-
* Worker-
|
|
17
|
-
* `jobs
|
|
16
|
+
* Worker-run tracking and retries are provided automatically by the existing
|
|
17
|
+
* `jobs` feature.
|
|
18
18
|
*/
|
|
19
19
|
export type UserDataRightsOptions = {
|
|
20
|
-
/** Email
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* verarbeitet, retry findet niemand.) */
|
|
20
|
+
/** Email notification when the export completes. The app author connects it
|
|
21
|
+
* to an email provider. Best effort: a send failure for Job A does not stop
|
|
22
|
+
* the batch; remaining pending jobs continue, and the failure is exposed via
|
|
23
|
+
* console.warn. Previously, the error reached the r.job wrapper, so with
|
|
24
|
+
* multiple pending jobs A could commit while B/C/D were never processed and
|
|
25
|
+
* no retry could find them. */
|
|
27
26
|
readonly sendExportReadyEmail?: SendExportReadyEmailFn;
|
|
28
27
|
/** Email-Notification beim Export-failed. Best-effort analog
|
|
29
28
|
* sendExportReadyEmail. */
|
|
30
29
|
readonly sendExportFailedEmail?: SendExportFailedEmailFn;
|
|
31
|
-
/** Base
|
|
32
|
-
* "https://app.example.com/user-export/by-token".
|
|
33
|
-
* a URL fragment (`#token=<plain
|
|
34
|
-
* sendExportReadyEmail
|
|
35
|
-
*
|
|
30
|
+
/** Base URL for the magic link, e.g.
|
|
31
|
+
* "https://app.example.com/user-export/by-token". The worker builds
|
|
32
|
+
* a URL fragment (`#token=<plain>`). Required when
|
|
33
|
+
* sendExportReadyEmail is set. Routing uses the reverse-proxy host per
|
|
34
|
+
* tenant rather than a per-tenant config key. */
|
|
36
35
|
readonly appExportDownloadUrl?: string;
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
/** Email notification when deletion is requested ("account deletion in
|
|
37
|
+
* 30 days"). Best effort: a send failure does not abort the status flip
|
|
38
|
+
* (see handlers/request-deletion.write.ts). */
|
|
40
39
|
readonly sendDeletionRequestedEmail?: SendDeletionRequestedEmailFn;
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
40
|
+
/** Email notification after the cleanup runner completes ("account deleted").
|
|
41
|
+
* Best effort. Delivery occurs after the user hook anonymizes the user, so
|
|
42
|
+
* the worker caches userEmail+tenantIds before the transaction and passes
|
|
43
|
+
* them ephemerally to the callback implementation (see
|
|
44
|
+
* run-forget-cleanup.ts). */
|
|
46
45
|
readonly sendDeletionExecutedEmail?: SendDeletionExecutedEmailFn;
|
|
47
|
-
/**
|
|
48
|
-
* HMAC
|
|
49
|
-
*
|
|
50
|
-
* by-token
|
|
46
|
+
/** Anonymous, email-verified apex deletion flow that is safe against lockout.
|
|
47
|
+
* HMAC secret for signing the verification token. Without a secret, the
|
|
48
|
+
* flow remains disabled: request-by-email returns success silently, while
|
|
49
|
+
* confirm-by-token rejects generically. */
|
|
51
50
|
readonly deletionTokenSecret?: string;
|
|
52
|
-
/**
|
|
53
|
-
* "https://app.example.com/delete-account/confirm".
|
|
54
|
-
*
|
|
55
|
-
* deletionTokenSecret
|
|
51
|
+
/** Base URL of the apex confirmation screen, e.g.
|
|
52
|
+
* "https://app.example.com/delete-account/confirm". The handler appends
|
|
53
|
+
* the token as a URL fragment (`#token=<token>`). Required when
|
|
54
|
+
* deletionTokenSecret is set. */
|
|
56
55
|
readonly deletionVerifyUrl?: string;
|
|
57
|
-
/**
|
|
58
|
-
* Best
|
|
59
|
-
* delivery.notify)
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
56
|
+
/** Sends the verification magic link (step 1 of the anonymous flow).
|
|
57
|
+
* Best effort and wired by the app author. It MUST be non-blocking (enqueue,
|
|
58
|
+
* e.g. via delivery.notify): a synchronous send would reintroduce a timing
|
|
59
|
+
* oracle for account enumeration (see SendDeletionVerificationEmailFn-doc).
|
|
60
|
+
* There is deliberately no mail-foundation default because a synchronous
|
|
61
|
+
* default send would restore that enumeration oracle. */
|
|
63
62
|
readonly sendDeletionVerificationEmail?: SendDeletionVerificationEmailFn;
|
|
64
|
-
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* `mailDefaults`
|
|
69
|
-
*
|
|
70
|
-
*
|
|
63
|
+
/** Zero-callback GDPR mail: when mail-foundation and a mail-transport-* are
|
|
64
|
+
* mounted, user-data-rights sends export and deletion notifications itself
|
|
65
|
+
* (export-ready/-failed, deletion-requested/-executed) using the default
|
|
66
|
+
* templates (email-templates.ts), without an app callback.
|
|
67
|
+
* `mailDefaults` brands these default messages (locale and app name). It
|
|
68
|
+
* applies only when the relevant send*Email option is unset; export-ready
|
|
69
|
+
* also requires appExportDownloadUrl. */
|
|
71
70
|
readonly mailDefaults?: GdprMailDefaults;
|
|
72
71
|
/** Hides the Art. 17 deletion section + its request-deletion/cancel-deletion
|
|
73
72
|
* actions on the privacy-center screen — for apps that already offer
|
|
74
73
|
* account deletion elsewhere (e.g. a profile danger-zone), against
|
|
75
74
|
* duplication. Default `true` (deletion section shown).
|
|
76
|
-
* BREAKING
|
|
75
|
+
* BREAKING: replaces the removed client-side
|
|
77
76
|
* `userDataRightsClient({ privacyCenter: { showDeletion } })` option —
|
|
78
77
|
* the screen is now declared once, server-side (`type: "projectionDetail"`),
|
|
79
78
|
* so per-app visibility can only be a boot-time option, not a client-side
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// kumiko-lint-ignore app-feature-structure
|
|
1
|
+
// kumiko-lint-ignore app-feature-structure feature.ts is over 300 lines and not yet split
|
|
2
2
|
import { UNKNOWN_CLIENT_IP } from "@cosmicdrift/kumiko-framework/api";
|
|
3
3
|
import { createSystemConfig, createSystemUser, defineFeature, EXT_USER_DATA, i18nKey, SYSTEM_USER_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
@@ -61,16 +61,16 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
61
61
|
r.envSchema(deletionTokenSecretEnvSchema);
|
|
62
62
|
r.usesApi("compliance.forTenant");
|
|
63
63
|
r.usesApi("retention.policyFor");
|
|
64
|
-
//
|
|
65
|
-
// r.usesApi
|
|
66
|
-
//
|
|
67
|
-
//
|
|
64
|
+
// restrict-account calls sessions.revokeAllForUser across features.
|
|
65
|
+
// r.usesApi enables boot validation: without the sessions feature, the app
|
|
66
|
+
// fails at boot instead of raising an opaque "handler not found" error
|
|
67
|
+
// on the first restrict call.
|
|
68
68
|
r.usesApi("sessions.revokeAllForUser");
|
|
69
|
-
// file-foundation
|
|
70
|
-
//
|
|
71
|
-
//
|
|
69
|
+
// file-foundation is a soft dependency: only the export worker needs it
|
|
70
|
+
// for storage writes. Apps that use only forget (not export) do not need
|
|
71
|
+
// to mount file-foundation.
|
|
72
72
|
r.extendsRegistrar(EXT_USER_DATA, {});
|
|
73
|
-
// GDPR-storage guards V2+V3
|
|
73
|
+
// GDPR-storage guards V2+V3 — moved off the framework-internal
|
|
74
74
|
// boot-validator onto this feature's own r.bootCheck(), since it's the
|
|
75
75
|
// EXT_USER_DATA channel owner: any r.useExtension(EXT_USER_DATA, ...)
|
|
76
76
|
// usage already requires user-data-rights to be mounted.
|
|
@@ -93,20 +93,20 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
93
93
|
options: ["single-user", "multi-user"],
|
|
94
94
|
env: "TENANT_MODEL",
|
|
95
95
|
}));
|
|
96
|
-
//
|
|
96
|
+
// ExportJob-Lifecycle-Entity.
|
|
97
97
|
r.entity({ name: "export-job", ...exportJobEntity });
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
98
|
+
// The worker generates the token when flipping the job to done (see
|
|
99
|
+
// run-export-jobs.ts). The hash is stored in the database, while the plain
|
|
100
|
+
// token is kept in RunExportJobsResult for email notification.
|
|
101
101
|
r.entity({ name: "export-download-token", ...exportDownloadTokenEntity });
|
|
102
|
-
//
|
|
102
|
+
// Audit-Trail invalid Download-Attempts (DPO Brute-Force-Detection).
|
|
103
103
|
r.entity({ name: "download-attempt", ...downloadAttemptEntity });
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
104
|
+
// GDPR Article 18 account freeze (pause processing).
|
|
105
|
+
// The endpoints restrict and lift the restriction. The login block for
|
|
106
|
+
// restricted users lives in auth-email-password/login.write.ts.
|
|
107
107
|
r.writeHandler(restrictAccountWrite);
|
|
108
108
|
r.writeHandler(liftRestrictionWrite);
|
|
109
|
-
//
|
|
109
|
+
// Endpoints for the GDPR Article 17 forget path with a grace period.
|
|
110
110
|
r.writeHandler(createRequestDeletionHandler({
|
|
111
111
|
...(opts.sendDeletionRequestedEmail && {
|
|
112
112
|
sendDeletionRequestedEmail: opts.sendDeletionRequestedEmail,
|
|
@@ -129,26 +129,26 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
129
129
|
r.writeHandler(createConfirmDeletionByTokenHandler(opts.deletionTokenSecret !== undefined
|
|
130
130
|
? { deletionTokenSecret: opts.deletionTokenSecret }
|
|
131
131
|
: {}));
|
|
132
|
-
//
|
|
133
|
-
// sendDeletionExecutedEmail
|
|
134
|
-
//
|
|
135
|
-
//
|
|
132
|
+
// Cleanup runner as a privileged handler. When
|
|
133
|
+
// sendDeletionExecutedEmail is set, the handler passes the callback to
|
|
134
|
+
// runForgetCleanup; the worker caches userEmail+tenantIds before the
|
|
135
|
+
// transaction (see run-forget-cleanup.ts).
|
|
136
136
|
r.writeHandler(createRunForgetCleanupHandler(opts.sendDeletionExecutedEmail
|
|
137
137
|
? { sendDeletionExecutedEmail: opts.sendDeletionExecutedEmail }
|
|
138
138
|
: {}));
|
|
139
139
|
r.exposesApi("userDataRights.runForget");
|
|
140
|
-
//
|
|
140
|
+
// User touchpoints for the asynchronous export pipeline.
|
|
141
141
|
r.writeHandler(requestExportWrite);
|
|
142
142
|
r.queryHandler(exportStatusQuery);
|
|
143
143
|
r.exposesApi("userDataRights.runExport");
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
// r.httpRoute
|
|
144
|
+
// Download endpoints for token and session paths.
|
|
145
|
+
// Both query handlers verify the token or session, obtain a signed URL
|
|
146
|
+
// from the storage provider, and persist audit fields on the token row.
|
|
147
|
+
// The r.httpRoute wrappers below issue the 302 redirect to signedUrl.
|
|
148
148
|
r.queryHandler(downloadByTokenQuery);
|
|
149
149
|
r.queryHandler(downloadByJobQuery);
|
|
150
|
-
//
|
|
151
|
-
//
|
|
150
|
+
// User self-access under Article 15 and operator access to invalid download
|
|
151
|
+
// attempts (DPO).
|
|
152
152
|
r.queryHandler(myAuditLogQuery);
|
|
153
153
|
r.queryHandler(listDownloadAttemptsQuery);
|
|
154
154
|
// Read-only GDPR operator inspector (SystemAdmin) — screens stay inert until an app navs them.
|
|
@@ -306,12 +306,12 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
306
306
|
// Magic-link path (anonymous): the email link carries the token as a
|
|
307
307
|
// URL fragment (`#token=<plain>`) instead of a query param — fragments
|
|
308
308
|
// never leave the browser, so they never hit proxy/access logs
|
|
309
|
-
//
|
|
309
|
+
// This route only serves the interstitial page; the
|
|
310
310
|
// external script reads the fragment client-side and exchanges the
|
|
311
311
|
// token via POST for the signed URL.
|
|
312
312
|
//
|
|
313
|
-
// No `?token=` query fallback: framework is zero-legacy
|
|
314
|
-
// live customer data / no
|
|
313
|
+
// No `?token=` query fallback: framework is zero-legacy — no
|
|
314
|
+
// live customer data / no emails in flight that would need
|
|
315
315
|
// bridging, so there's nothing a fallback would need to cover. A query
|
|
316
316
|
// param reaches the server as part of the request URL and gets written
|
|
317
317
|
// to proxy/access logs regardless of what the handler does with it
|
|
@@ -353,7 +353,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
353
353
|
},
|
|
354
354
|
});
|
|
355
355
|
// Exchange endpoint for the interstitial page: the token arrives here
|
|
356
|
-
// as a POST body instead of a query string
|
|
356
|
+
// as a POST body instead of a query string. A dedicated
|
|
357
357
|
// route rather than letting the browser JS call /api/query directly,
|
|
358
358
|
// for two reasons:
|
|
359
359
|
// 1. IP/UA audit — extractAuditMeta needs the raw request headers and
|
|
@@ -391,7 +391,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
391
391
|
// defaults to 0, ignoring headers entirely) nor a socket, so every
|
|
392
392
|
// fragment-POST download would fall to the shared "unknown" bucket,
|
|
393
393
|
// turning the per-user 30/min backstop into one global bucket an
|
|
394
|
-
// attacker can exhaust for everyone
|
|
394
|
+
// attacker can exhaust for everyone. Passing `clientIp`
|
|
395
395
|
// directly as env — the same server-resolved, trustedProxyHops-
|
|
396
396
|
// aware value the outer request already trusted — reaches
|
|
397
397
|
// extractSocketAddress() without going through header/hop-count
|
|
@@ -417,7 +417,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
417
417
|
unit: "seconds",
|
|
418
418
|
description: "Unix timestamp of the last export storage-cleanup pass that completed. Alert on time() minus this value exceeding the cron interval plus a buffer; the backlog-age gauge alone stays at its last value when the cron stops.",
|
|
419
419
|
});
|
|
420
|
-
//
|
|
420
|
+
// Worker for the asynchronous export pipeline, triggered by a cron job.
|
|
421
421
|
const RUN_EXPORT_JOBS_REASON = "processes pending export jobs across every tenant";
|
|
422
422
|
r.job({
|
|
423
423
|
name: "run-export-jobs",
|
|
@@ -429,9 +429,9 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
429
429
|
throw new Error("run-export-jobs: ctx.registry required (JobContext incomplete)");
|
|
430
430
|
}
|
|
431
431
|
const T = (await import("@cosmicdrift/kumiko-framework/time")).getTemporal();
|
|
432
|
-
// SYSTEM_USER_ID
|
|
433
|
-
//
|
|
434
|
-
//
|
|
432
|
+
// SYSTEM_USER_ID is the framework-wide convention. The job discriminator is
|
|
433
|
+
// recorded in the secret-read audit through
|
|
434
|
+
// handlerName="user-data-rights:run-export-jobs".
|
|
435
435
|
const exportUserId = SYSTEM_USER_ID;
|
|
436
436
|
const exportDb = ctx.db.unsafeRaw(); // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
437
437
|
const exportRegistry = ctx.registry;
|
|
@@ -441,12 +441,13 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
441
441
|
const exportMetrics = ctx.meter
|
|
442
442
|
? createMetricsHandle(ctx.meter, "user-data-rights")
|
|
443
443
|
: createNoopMetricsHandle();
|
|
444
|
-
//
|
|
445
|
-
//
|
|
446
|
-
//
|
|
447
|
-
//
|
|
448
|
-
// The cron runs across tenants
|
|
449
|
-
// recipient's tenant
|
|
444
|
+
// Without explicit send*Email options, a mounted mail transport sends the
|
|
445
|
+
// export notifications itself using the default templates. The resolver
|
|
446
|
+
// builds the per-tenant transport from configResolver, bridging it in
|
|
447
|
+
// the same way as buildStorageProvider.
|
|
448
|
+
// The cron runs across tenants, so each mail needs a notify function
|
|
449
|
+
// bound to the recipient's tenant for template overrides and channel
|
|
450
|
+
// configuration.
|
|
450
451
|
const notifyFactory = ctx._notifyFactory;
|
|
451
452
|
const exportNotifyFor = notifyFactory !== undefined && isGdprMailDeliveryAvailable(exportRegistry)
|
|
452
453
|
? (tenantId) => notifyFactory(createSystemUser(tenantId), tenantId)
|
|
@@ -483,9 +484,9 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
483
484
|
handlerName: "user-data-rights:run-export-jobs",
|
|
484
485
|
}),
|
|
485
486
|
now: T.Now.instant(),
|
|
486
|
-
// App-
|
|
487
|
-
//
|
|
488
|
-
//
|
|
487
|
+
// App-author callbacks take precedence; otherwise the mail-foundation default
|
|
488
|
+
// is used. Both are optional: without a mail transport or callback,
|
|
489
|
+
// clients continue polling via export-status.query.
|
|
489
490
|
...(sendExportReadyEmail && { sendExportReadyEmail }),
|
|
490
491
|
...(sendExportFailedEmail && { sendExportFailedEmail }),
|
|
491
492
|
...(opts.appExportDownloadUrl !== undefined && {
|
|
@@ -498,11 +499,11 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
498
499
|
});
|
|
499
500
|
},
|
|
500
501
|
});
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
// userDataRights.runForget
|
|
504
|
-
//
|
|
505
|
-
//
|
|
502
|
+
// Automatic Article 17 forget cron. After the grace period, it runs
|
|
503
|
+
// runForgetCleanup without supervision; the manual
|
|
504
|
+
// userDataRights.runForget API remains available for operator runs.
|
|
505
|
+
// Without this cron, deletion requests would remain in
|
|
506
|
+
// DeletionRequested indefinitely and Article 17 erasure would never run.
|
|
506
507
|
const RUN_FORGET_CLEANUP_REASON = "executes overdue Art.17 forget requests across every tenant";
|
|
507
508
|
r.job({
|
|
508
509
|
name: "run-forget-cleanup",
|
|
@@ -568,7 +569,7 @@ export function createUserDataRightsFeature(opts = {}) {
|
|
|
568
569
|
escapeHatchAuditLog: ctx.log,
|
|
569
570
|
actor: ctx.systemUser.id,
|
|
570
571
|
});
|
|
571
|
-
// Persist each incomplete report on the job-run log
|
|
572
|
+
// Persist each incomplete report on the job-run log. The write
|
|
572
573
|
// handler returns incomplete[] in its response; the cron path used to
|
|
573
574
|
// discard the array (only a console.warn for rolledBack cases).
|
|
574
575
|
// ctx.log feeds createJobRunLogger → store_job_run_logs.
|
|
@@ -5,17 +5,17 @@ import { isWithinGracePeriod } from "../../shared/index.js";
|
|
|
5
5
|
import { USER_STATUS, userTable } from "../../user/index.js";
|
|
6
6
|
import { updateUserLifecycle } from "../lib/update-user-lifecycle.js";
|
|
7
7
|
const APPEND_LIFECYCLE_EVENT_REASON = "appends the user lifecycle event on the SYSTEM_TENANT_ID user stream";
|
|
8
|
-
// POST /api/user/cancel-deletion
|
|
8
|
+
// POST /api/user/cancel-deletion.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
10
|
+
// During the grace period, the user can withdraw a pending deletion request.
|
|
11
|
+
// This restores:
|
|
12
12
|
// - status = "active"
|
|
13
13
|
// - gracePeriodEnd = null
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
15
|
+
// After the grace period, return 422 because cleanup may already have triggered
|
|
16
|
+
// irreversible hooks.
|
|
17
17
|
//
|
|
18
|
-
//
|
|
18
|
+
// Cancelling as an "active" user also returns 422 because no deletion is pending.
|
|
19
19
|
export const cancelDeletionWrite = defineWriteHandler({
|
|
20
20
|
name: "cancel-deletion",
|
|
21
21
|
schema: z.object({}),
|
|
@@ -48,12 +48,12 @@ export const cancelDeletionWrite = defineWriteHandler({
|
|
|
48
48
|
await updateUserLifecycle(ctx.db.unsafeRaw(), event.user.id, {
|
|
49
49
|
status: USER_STATUS.Active,
|
|
50
50
|
gracePeriodEnd: null,
|
|
51
|
-
//
|
|
51
|
+
// Closes the replay-after-cancel window — a still-TTL-valid email
|
|
52
52
|
// token verified against the nulled requestId can no longer arm a second grace period.
|
|
53
53
|
pendingDeletionRequestId: null,
|
|
54
54
|
});
|
|
55
|
-
// gracePeriodEnd=null
|
|
56
|
-
// ISO
|
|
55
|
+
// gracePeriodEnd=null in the response mirrors request-deletion's
|
|
56
|
+
// ISO timestamp so the frontend can handle both endpoints uniformly.
|
|
57
57
|
const gracePeriodEnd = null;
|
|
58
58
|
return {
|
|
59
59
|
isSuccess: true,
|
|
@@ -4,15 +4,15 @@ import * as z from "zod";
|
|
|
4
4
|
import { USER_STATUS, userTable } from "../../user/index.js";
|
|
5
5
|
import { redeemDeletionToken } from "../deletion-token.js";
|
|
6
6
|
import { resolveGracePeriod, startDeletionGracePeriod } from "./deletion-grace-period.js";
|
|
7
|
-
//
|
|
8
|
-
// expired /
|
|
7
|
+
// Use the same generic 422 for every token-error path (malformed / bad_signature /
|
|
8
|
+
// expired / missing secret) so callers cannot determine whether a token belongs to a user.
|
|
9
9
|
function invalidToken() {
|
|
10
10
|
return new UnprocessableError("invalid_or_expired_token");
|
|
11
11
|
}
|
|
12
|
-
// userId
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// userId comes from the not-yet-verified token (attacker-controlled input). A failed
|
|
13
|
+
// lookup—for example, a type-mismatched id on an int/uuid column—must not surface as a
|
|
14
|
+
// 500; null is treated by the caller as "no pending request" (generic 422). HMAC
|
|
15
|
+
// verification remains the actual gate.
|
|
16
16
|
async function readPendingDeletionRequestId(ctx, userId) {
|
|
17
17
|
try {
|
|
18
18
|
const row = await ctx.db
|
|
@@ -32,7 +32,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
|
|
|
32
32
|
// the actual grace-period transition, so a crash after the spend can no
|
|
33
33
|
// longer lose the write step.
|
|
34
34
|
//
|
|
35
|
-
// Replay protection
|
|
35
|
+
// Replay protection: the row's requestId is part of the verify key.
|
|
36
36
|
// We read it via the (unverified, lookup-only) userId from the token, reject
|
|
37
37
|
// a missing entry, and verify the token against the CURRENT requestId. After
|
|
38
38
|
// a cancel-deletion (status → Active, pendingDeletionRequestId → null), a
|
|
@@ -42,7 +42,7 @@ async function readPendingDeletionRequestId(ctx, userId) {
|
|
|
42
42
|
// the same requestId and both verify the HMAC — commitDeletion spends the
|
|
43
43
|
// anchor AND writes the transition atomically (expect: status===Active &&
|
|
44
44
|
// pendingDeletionRequestId===requestId), so exactly one wins. The loser gets
|
|
45
|
-
// the same generic 422 as an invalid token — no status leak
|
|
45
|
+
// the same generic 422 as an invalid token — no status leak.
|
|
46
46
|
export function createConfirmDeletionByTokenHandler(opts = {}) {
|
|
47
47
|
return defineWriteHandler({
|
|
48
48
|
name: "confirm-deletion-by-token",
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// GET /api/query → user-data-rights:query:download-by-job
|
|
1
|
+
// GET /api/query → user-data-rights:query:download-by-job
|
|
2
2
|
//
|
|
3
|
-
// **UI
|
|
4
|
-
//
|
|
5
|
-
// job.userId
|
|
3
|
+
// **UI download flow** (session-auth): The user polls the status, sees
|
|
4
|
+
// "done", and clicks "Download" with the jobId. The server checks
|
|
5
|
+
// `session.userId == job.userId`; no token is needed because the session
|
|
6
|
+
// provides authentication.
|
|
6
7
|
//
|
|
7
|
-
// **Cross-
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// Plan-Decision (Atom 4b Plan, User-Choice).
|
|
8
|
+
// **Cross-tenant same user:** Jobs are tenant-agnostic (one job per userId
|
|
9
|
+
// across all memberships). If Alice starts an export in Tenant A, later
|
|
10
|
+
// signs in to Tenant B, and clicks Download, the server accepts it:
|
|
11
|
+
// `session.userId == job.userId` is sufficient; no tenant-match check applies.
|
|
12
12
|
//
|
|
13
|
-
// **Cross-
|
|
14
|
-
// (
|
|
15
|
-
//
|
|
13
|
+
// **Cross-user isolation:** If session.userId != job.userId, return 404
|
|
14
|
+
// (not 403) to avoid leaking whether the job exists. Use the same error as
|
|
15
|
+
// for an unknown job ID.
|
|
16
16
|
//
|
|
17
|
-
//
|
|
18
|
-
// 1.
|
|
19
|
-
// 2. job.userId === session.userId (cross-user
|
|
20
|
-
// 3. job.status === "done"
|
|
21
|
-
// 4. job.downloadStorageKey != null
|
|
22
|
-
// 5. tokenRow
|
|
23
|
-
// 6.
|
|
17
|
+
// Verification pipeline:
|
|
18
|
+
// 1. Fetch the job by jobId
|
|
19
|
+
// 2. Check job.userId === session.userId (cross-user isolation)
|
|
20
|
+
// 3. Check job.status === "done"
|
|
21
|
+
// 4. Check job.downloadStorageKey != null
|
|
22
|
+
// 5. Look up tokenRow (audit-row update only)
|
|
23
|
+
// 6. Best-effort audit update: useCount + 1, IP, UA, lastUsedAt
|
|
24
24
|
// 7. Return {url, expiresAt}
|
|
25
25
|
import { requestContext } from "@cosmicdrift/kumiko-framework/api";
|
|
26
26
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
@@ -44,7 +44,7 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
44
44
|
reason: "each signed-in user downloads only their own finished export job; the job " +
|
|
45
45
|
"lookup checks jobRow.userId against the caller's own id and 404s otherwise",
|
|
46
46
|
},
|
|
47
|
-
}, // openToAll
|
|
47
|
+
}, // openToAll requires authentication; anonymous access is not allowed
|
|
48
48
|
description: "Returns a short-lived signed download URL for the calling user's own finished data-export job named by job id, backing the download button in the privacy center once export-status reports the job done.",
|
|
49
49
|
escapeHatch: {
|
|
50
50
|
grants: ["unsafeRaw"],
|
|
@@ -56,9 +56,8 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
56
56
|
const userId = query.user.id;
|
|
57
57
|
const jobId = query.payload.jobId;
|
|
58
58
|
const tenantId = query.user.tenantId;
|
|
59
|
-
// IP
|
|
60
|
-
//
|
|
61
|
-
// Wert (603/2).
|
|
59
|
+
// IP and UA from the request-scoped context populated by
|
|
60
|
+
// requestIdMiddleware; these are server-trusted rather than client-supplied.
|
|
62
61
|
const auditIp = requestContext.get()?.ip ?? null;
|
|
63
62
|
const auditUa = requestContext.get()?.userAgent ?? null;
|
|
64
63
|
const runner = ctx.db.unsafeRaw();
|
|
@@ -149,10 +148,10 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
149
148
|
contentDisposition: `attachment; filename="user-data-export-${jobRow.id}.zip"`,
|
|
150
149
|
});
|
|
151
150
|
const signedUrlExpiresAt = T.Instant.fromEpochMilliseconds(now.epochMilliseconds + SIGNED_URL_TTL_SECONDS * 1000);
|
|
152
|
-
// Step 6:
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
//
|
|
151
|
+
// Step 6: Look up tokenRow for the audit update. The UI flow has no plain
|
|
152
|
+
// token, but its useCount must still be incremented so audit fields remain
|
|
153
|
+
// consistent and UI clicks count as uses. The jobId lookup is safe because
|
|
154
|
+
// the UNIQUE index guarantees at most one row.
|
|
156
155
|
const tokenRow = await fetchOne(runner, exportDownloadTokensTable, {
|
|
157
156
|
jobId,
|
|
158
157
|
});
|
|
@@ -168,9 +167,9 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
168
167
|
userAgent: auditUa,
|
|
169
168
|
});
|
|
170
169
|
}
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
170
|
+
// If tokenRow is missing (which should not happen when the preceding token
|
|
171
|
+
// creation flow completed), skip the audit update and continue the download;
|
|
172
|
+
// an audit failure must not block the user.
|
|
174
173
|
return {
|
|
175
174
|
url: signedUrl,
|
|
176
175
|
expiresAt: signedUrlExpiresAt.toString(),
|