@cosmicdrift/kumiko-samples 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/README.md +1 -1
- package/package.json +2 -2
- package/packages/bundled-features/package.json +2 -2
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +0 -2
- package/packages/bundled-features/src/audit/screens.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
- package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
- package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
- package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
- package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
- package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
- package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
- package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
- package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
- package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
- package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
- package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
- package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/web/i18n.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +6 -6
- package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
- package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
- package/packages/bundled-features/src/billing-foundation/events.ts +22 -21
- package/packages/bundled-features/src/billing-foundation/feature.ts +42 -43
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +18 -17
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +20 -20
- package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
- package/packages/bundled-features/src/billing-foundation/types.ts +64 -65
- package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +4 -4
- package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
- package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +64 -67
- package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
- package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
- package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
- package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +1 -1
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
- package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
- package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
- package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
- package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
- package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
- package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
- package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
- package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
- package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
- package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/resolver.ts +4 -4
- package/packages/bundled-features/src/config/table.ts +10 -10
- package/packages/bundled-features/src/config/write-helpers.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +13 -12
- package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
- package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
- package/packages/bundled-features/src/custom-fields/events.ts +7 -8
- package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
- package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
- package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
- package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
- package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
- package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
- package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
- package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
- package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
- package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
- package/packages/bundled-features/src/data-retention/feature.ts +11 -18
- package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
- package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
- package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
- package/packages/bundled-features/src/data-retention/presets.ts +18 -23
- package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
- package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
- package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
- package/packages/bundled-features/src/delivery/feature.ts +3 -3
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
- package/packages/bundled-features/src/delivery/tables.ts +9 -8
- package/packages/bundled-features/src/delivery/unsubscribe.ts +4 -4
- package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
- package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
- package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
- package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
- package/packages/bundled-features/src/files/feature.ts +6 -6
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/folders/feature.ts +1 -1
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
- package/packages/bundled-features/src/folders/web/tree.ts +5 -5
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +1 -1
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
- package/packages/bundled-features/src/form-draft/entity.ts +3 -3
- package/packages/bundled-features/src/form-draft/feature.ts +4 -5
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
- package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
- package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
- package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
- package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
- package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +11 -11
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
- package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +66 -67
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +69 -68
- package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +6 -7
- package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
- package/packages/bundled-features/src/jobs/job-run-logger.ts +14 -15
- package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
- package/packages/bundled-features/src/jobs/screens.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
- package/packages/bundled-features/src/ledger/entity.ts +5 -5
- package/packages/bundled-features/src/ledger/feature.ts +3 -3
- package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
- package/packages/bundled-features/src/ledger/recurring.ts +12 -12
- package/packages/bundled-features/src/ledger/schemas.ts +1 -1
- package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
- package/packages/bundled-features/src/ledger/web/index.ts +7 -7
- package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
- package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
- package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
- package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
- package/packages/bundled-features/src/mail-foundation/feature.ts +26 -27
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
- package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
- package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
- package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
- package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
- package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
- package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
- package/packages/bundled-features/src/managed-pages/table.ts +16 -15
- package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
- package/packages/bundled-features/src/notes-history/entity.ts +4 -4
- package/packages/bundled-features/src/notes-history/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
- package/packages/bundled-features/src/page-render/layout.ts +9 -9
- package/packages/bundled-features/src/page-render/markdown.ts +11 -11
- package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +5 -5
- package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
- package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
- package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
- package/packages/bundled-features/src/seo/feature.ts +3 -3
- package/packages/bundled-features/src/sessions/constants.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +9 -9
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
- package/packages/bundled-features/src/sessions/screens.ts +2 -2
- package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
- package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
- package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +1 -1
- package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
- package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
- package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
- package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
- package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
- package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
- package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
- package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
- package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
- package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
- package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
- package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
- package/packages/bundled-features/src/subscription-stripe/feature.ts +44 -43
- package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +2 -2
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
- package/packages/bundled-features/src/tags/feature.ts +3 -3
- package/packages/bundled-features/src/tags/screens.ts +1 -1
- package/packages/bundled-features/src/template-resolver/api.ts +10 -10
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +5 -5
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
- package/packages/bundled-features/src/template-resolver/seeding.ts +4 -4
- package/packages/bundled-features/src/template-resolver/table.ts +13 -14
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
- package/packages/bundled-features/src/tenant/feature.ts +7 -7
- package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +1 -1
- package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
- package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
- package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
- package/packages/bundled-features/src/tenant/screens.ts +1 -1
- package/packages/bundled-features/src/tenant/seeding.ts +19 -18
- package/packages/bundled-features/src/tenant/testing.ts +5 -6
- package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
- package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
- package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
- package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
- package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +2 -2
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
- package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
- package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
- package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
- package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +8 -8
- package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
- package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
- package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
- package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
- package/packages/bundled-features/src/user/schema/user.ts +45 -44
- package/packages/bundled-features/src/user/seeding.ts +40 -40
- package/packages/bundled-features/src/user/testing.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
- package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
- package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +166 -276
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +85 -107
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
- package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
- package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
- package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
- package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
- package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
- package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
- package/packages/bundled-features/src/user-profile/index.ts +3 -3
- package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
- package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
|
@@ -1,39 +1,32 @@
|
|
|
1
1
|
import { createFallbackLogger, type Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
2
|
-
// Forget-
|
|
2
|
+
// Forget-cleanup runner — pure-function pipeline.
|
|
3
3
|
//
|
|
4
|
-
// After the grace period
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// history from kumiko_events).
|
|
4
|
+
// After the grace period, this runner processes users in DeletionRequested
|
|
5
|
+
// state and triggers EXT_USER_DATA delete hooks in every tenant where the user
|
|
6
|
+
// is or was a member, using membership history from kumiko_events.
|
|
8
7
|
//
|
|
9
|
-
// **Cross-tenant iteration:** a
|
|
10
|
-
//
|
|
11
|
-
// the request — see
|
|
12
|
-
// docs/plans/architecture/user-data-rights.md "Cross-Tenant-Semantik".
|
|
8
|
+
// **Cross-tenant iteration:** a request in tenant A must remove the user's data
|
|
9
|
+
// in all of their tenants, including tenants they left before the request.
|
|
13
10
|
//
|
|
14
|
-
// **Strategy
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
11
|
+
// **Strategy dispatch:** each entity's data-retention policy determines whether
|
|
12
|
+
// to run "delete" or "anonymize". A blockDelete policy always produces
|
|
13
|
+
// "anonymize", leaving the data object in place while removing its association
|
|
14
|
+
// with the person.
|
|
18
15
|
//
|
|
19
|
-
// **Per-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// Cleanup-Run beim ersten Hook-Throw zurueckrollen.
|
|
16
|
+
// **Per-user atomicity:** each user runs in its own sub-transaction
|
|
17
|
+
// (db.transaction → SAVEPOINT when an outer transaction is active, BEGIN
|
|
18
|
+
// otherwise). A failing hook rolls back only that user's transaction, while
|
|
19
|
+
// other users and previously completed status changes remain commit-able.
|
|
20
|
+
// Without this isolation, the outer dispatcher transaction would roll back the
|
|
21
|
+
// entire cleanup run on the first hook failure.
|
|
26
22
|
//
|
|
27
|
-
// **
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// Lauf den User nicht mehr findet.
|
|
23
|
+
// **Idempotency:** hooks are designed to be idempotent, so rerunning after
|
|
24
|
+
// crash recovery is safe. Flipping the status to "Deleted" at the end keeps
|
|
25
|
+
// the user out of subsequent runs.
|
|
31
26
|
//
|
|
32
|
-
// **Error
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// gefailten Hooks bleibt im DeletionRequested-Status (next Lauf
|
|
36
|
-
// retried automatisch).
|
|
27
|
+
// **Error handling:** a throwing hook must not stop the run. Errors are
|
|
28
|
+
// collected and returned for operator visibility, while a user with failed
|
|
29
|
+
// hooks remains in DeletionRequested for automatic retry.
|
|
37
30
|
|
|
38
31
|
import {
|
|
39
32
|
configuredPiiSubjectKms,
|
|
@@ -90,18 +83,15 @@ import { updateUserLifecycle } from "./lib/update-user-lifecycle.ts";
|
|
|
90
83
|
type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
91
84
|
|
|
92
85
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* persistiert den failed-Run in jobRunsTable (siehe
|
|
96
|
-
* jobs/__tests__/jobs-feature.integration.ts Scenario 2 — der throw-
|
|
97
|
-
* Pfad eines r.job-handlers wird dort gepinnt).
|
|
86
|
+
* Callback for the forget-cleanup path. A callback failure propagates to the
|
|
87
|
+
* r.job wrapper so the jobs feature can persist the failed run.
|
|
98
88
|
*
|
|
99
|
-
* **executedAt:**
|
|
100
|
-
*
|
|
89
|
+
* **executedAt:** The time of the delete flip, passed as an ISO string so the
|
|
90
|
+
* app author can insert it into an email template.
|
|
101
91
|
*
|
|
102
|
-
* **tenantIds:**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
92
|
+
* **tenantIds:** All memberships the user had before deletion. An email
|
|
93
|
+
* template can use this to describe which tenant accounts were deleted. The
|
|
94
|
+
* list is empty for an orphan user with no memberships.
|
|
105
95
|
*/
|
|
106
96
|
export type SendDeletionExecutedEmailFn = (args: {
|
|
107
97
|
readonly userId: string;
|
|
@@ -117,14 +107,13 @@ export interface RunForgetCleanupArgs {
|
|
|
117
107
|
readonly db: DbRunner;
|
|
118
108
|
readonly registry: Registry;
|
|
119
109
|
/**
|
|
120
|
-
*
|
|
121
|
-
* Pattern aus data-retention/keep-for.ts (advisor-pinned).
|
|
110
|
+
* Injects the current time so tests can control it without mocking the date.
|
|
122
111
|
*/
|
|
123
112
|
readonly now: Instant;
|
|
124
113
|
|
|
125
|
-
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
114
|
+
/** Sends an optional email notification after the delete flip. Without a
|
|
115
|
+
* callback, the worker remains silent because the user already received the
|
|
116
|
+
* request-deletion email and grace-period reminder. */
|
|
128
117
|
readonly sendDeletionExecutedEmail?: SendDeletionExecutedEmailFn;
|
|
129
118
|
|
|
130
119
|
/**
|
|
@@ -147,8 +136,8 @@ export interface RunForgetCleanupArgs {
|
|
|
147
136
|
readonly kms?: KmsAdapter;
|
|
148
137
|
|
|
149
138
|
/**
|
|
150
|
-
* Search adapter for purging derived Meili docs of searchable subject-PII
|
|
151
|
-
*
|
|
139
|
+
* Search adapter for purging derived Meili docs of searchable subject-PII.
|
|
140
|
+
* Same seam as forget-subject. Omitted → no search purge (apps
|
|
152
141
|
* without Meili).
|
|
153
142
|
*/
|
|
154
143
|
readonly searchAdapter?: SearchAdapter;
|
|
@@ -162,7 +151,7 @@ export interface RunForgetCleanupArgs {
|
|
|
162
151
|
*/
|
|
163
152
|
readonly tenantModel?: TenantUserModel;
|
|
164
153
|
|
|
165
|
-
//
|
|
154
|
+
// Sourced from the owning job's ctx (_escapeHatchAuditSink,
|
|
166
155
|
// systemUser.id); attributes+audits any EXT_USER_DATA usage's declared
|
|
167
156
|
// escapeHatch when its delete hook calls ctx.db.unsafeRaw().
|
|
168
157
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
@@ -196,11 +185,11 @@ export interface ForgetCleanupIncomplete {
|
|
|
196
185
|
}
|
|
197
186
|
|
|
198
187
|
export interface RunForgetCleanupResult {
|
|
199
|
-
/**
|
|
188
|
+
/** Users flipped from DeletionRequested to Deleted during this run. */
|
|
200
189
|
readonly processedUserIds: readonly string[];
|
|
201
|
-
/**
|
|
190
|
+
/** Number of entity-hook calls that actually ran, whether successful or failed. */
|
|
202
191
|
readonly hookCallsAttempted: number;
|
|
203
|
-
/** Hook
|
|
192
|
+
/** Hook errors collected for operator visibility; the run continues. */
|
|
204
193
|
readonly errors: readonly ForgetCleanupError[];
|
|
205
194
|
/** Hooks that reported `{status:"incomplete"}` — partial success, user still Deleted. */
|
|
206
195
|
readonly incomplete: readonly ForgetCleanupIncomplete[];
|
|
@@ -276,8 +265,8 @@ export async function runForgetCleanup(
|
|
|
276
265
|
return { processedUserIds: [], hookCallsAttempted: 0, errors: [], incomplete: [] };
|
|
277
266
|
}
|
|
278
267
|
|
|
279
|
-
// Step 2:
|
|
280
|
-
// (entityName, deleteHook)
|
|
268
|
+
// Step 2: collect all EXT_USER_DATA usages once, i.e. the
|
|
269
|
+
// (entityName, deleteHook) pairs of all registered provider features.
|
|
281
270
|
const usages = registry.getExtensionUsages(EXT_USER_DATA);
|
|
282
271
|
const hookEntries: HookEntry[] = usages
|
|
283
272
|
.map((u): HookEntry | null => {
|
|
@@ -304,7 +293,7 @@ export async function runForgetCleanup(
|
|
|
304
293
|
const processedUserIds: string[] = [];
|
|
305
294
|
let hookCallsAttempted = 0;
|
|
306
295
|
|
|
307
|
-
// Step 3:
|
|
296
|
+
// Step 3: iterate per user, each in its own sub-transaction (see header).
|
|
308
297
|
for (const user of dueUsers) {
|
|
309
298
|
const userResult = await processUser({
|
|
310
299
|
db,
|
|
@@ -325,17 +314,14 @@ export async function runForgetCleanup(
|
|
|
325
314
|
if (userResult.success) {
|
|
326
315
|
processedUserIds.push(user.id);
|
|
327
316
|
|
|
328
|
-
//
|
|
329
|
-
//
|
|
317
|
+
// userEmail was cached BEFORE the transaction because the user hook
|
|
318
|
+
// anonymizes it inside the transaction.
|
|
330
319
|
//
|
|
331
|
-
//
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
// schon Deleted) → silent miss. console.warn ist die einzige
|
|
337
|
-
// Operator-Sichtbarkeit — runForgetCleanup-args fuehren AppContext.
|
|
338
|
-
// log aktuell nicht durch (pure-function-Pattern).
|
|
320
|
+
// The email is best-effort: a send failure must not abort the batch after the
|
|
321
|
+
// user's sub-transaction has committed. Otherwise the job would be marked
|
|
322
|
+
// failed, and a retry would find no eligible users, causing a silent miss.
|
|
323
|
+
// console.warn is the only operator-visible reporting path because the
|
|
324
|
+
// runForgetCleanup args do not carry AppContext.log (pure-function pattern).
|
|
339
325
|
if (sendDeletionExecutedEmail && userResult.userEmailBeforeDelete) {
|
|
340
326
|
try {
|
|
341
327
|
await sendDeletionExecutedEmail({
|
|
@@ -363,13 +349,14 @@ interface ProcessUserResult {
|
|
|
363
349
|
readonly hookCallsAttempted: number;
|
|
364
350
|
readonly errors: readonly ForgetCleanupError[];
|
|
365
351
|
readonly incomplete: readonly ForgetCleanupIncomplete[];
|
|
366
|
-
/**
|
|
367
|
-
*
|
|
368
|
-
*
|
|
352
|
+
/** The email address cached before the transaction because the user hook
|
|
353
|
+
* anonymizes it in the transaction. Null means the row was absent or the
|
|
354
|
+
* email was empty during the pre-transaction lookup. */
|
|
369
355
|
readonly userEmailBeforeDelete: string | null;
|
|
370
356
|
/** user.locale VOR Tx gecacht — Default-Mailer rendert in Empfaenger-Sprache. */
|
|
371
357
|
readonly userLocaleBeforeDelete: string | null;
|
|
372
|
-
/** Tenant
|
|
358
|
+
/** Tenant memberships captured before the transaction deletes them, for use by
|
|
359
|
+
* the email template. */
|
|
373
360
|
readonly tenantIdsBeforeDelete: readonly TenantId[];
|
|
374
361
|
}
|
|
375
362
|
|
|
@@ -391,11 +378,9 @@ async function processUser(args: {
|
|
|
391
378
|
const incomplete: Omit<ForgetCleanupIncomplete, "rolledBack">[] = [];
|
|
392
379
|
let hookCallsAttempted = 0;
|
|
393
380
|
|
|
394
|
-
//
|
|
395
|
-
//
|
|
396
|
-
//
|
|
397
|
-
// Memory-cache laesst Atom-5b-Callback nach success-flip den
|
|
398
|
-
// ORIGINAL-email an App-Author-Callback geben.
|
|
381
|
+
// Cache the email before the transaction because the user hook anonymizes
|
|
382
|
+
// email/displayName/passwordHash inside it. The cache lets the callback after
|
|
383
|
+
// a successful status flip receive the original email address.
|
|
399
384
|
const userPreTx = await fetchOne<{ email: string | null; locale: string | null }>(db, userTable, {
|
|
400
385
|
id: userId,
|
|
401
386
|
});
|
|
@@ -405,13 +390,12 @@ async function processUser(args: {
|
|
|
405
390
|
: null;
|
|
406
391
|
const userLocaleBeforeDelete = userPreTx?.locale ?? null;
|
|
407
392
|
|
|
408
|
-
// Memberships fuer diesen User holen — alle Tenants in denen er Mitglied ist.
|
|
409
393
|
const memberships = await selectMany<{ tenantId: TenantId }>(db, tenantMembershipsTable, {
|
|
410
394
|
userId,
|
|
411
395
|
});
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
//
|
|
396
|
+
// Capture memberships before the transaction deletes them so the email
|
|
397
|
+
// callback receives the original tenant list. Orphan users provide an empty
|
|
398
|
+
// list.
|
|
415
399
|
const tenantIdsBeforeDelete: readonly TenantId[] = memberships.map((m) => m.tenantId);
|
|
416
400
|
|
|
417
401
|
// Departed tenants: removeMember deletes only the projection row, so data
|
|
@@ -427,19 +411,17 @@ async function processUser(args: {
|
|
|
427
411
|
const tenantList: TenantId[] =
|
|
428
412
|
hookTenantIds.length > 0 ? hookTenantIds : [SYSTEM_TENANT_ID_FOR_ORPHANS];
|
|
429
413
|
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
// (
|
|
434
|
-
// writeHandler bereits IN der Outer-Tx laeuft — via `.savepoint`
|
|
435
|
-
// (SAVEPOINT). Siehe runInSubTransaction.
|
|
414
|
+
// Hooks and the status flip run atomically per user. A hook failure rolls back
|
|
415
|
+
// only that user, while other users remain commit-able. runInSubTransaction
|
|
416
|
+
// uses `.begin` (BEGIN) for a top-level connection and `.savepoint`
|
|
417
|
+
// (SAVEPOINT) when the dispatcher is already inside an outer transaction.
|
|
436
418
|
let txSucceeded = false;
|
|
437
419
|
let currentTenantId: TenantId | null = null;
|
|
438
420
|
let currentEntityName: string | null = null;
|
|
439
421
|
try {
|
|
440
422
|
await runInSubTransaction(db, async (tx) => {
|
|
441
423
|
// Discover + purge search docs for this subject BEFORE any delete
|
|
442
|
-
// hook runs
|
|
424
|
+
// hook runs. purgeSearchDocumentsForSubject finds candidate
|
|
443
425
|
// rows via a live SELECT on each entity's read-table — a hook that
|
|
444
426
|
// hard-deletes a row (UserDataDeleteStrategy "delete") makes that row
|
|
445
427
|
// invisible to a purge running afterward, permanently stranding its
|
|
@@ -548,10 +530,10 @@ async function processUser(args: {
|
|
|
548
530
|
eraseReason: "user-data-rights:forget",
|
|
549
531
|
},
|
|
550
532
|
);
|
|
551
|
-
//
|
|
552
|
-
//
|
|
553
|
-
//
|
|
554
|
-
//
|
|
533
|
+
// Null blind-index columns immediately after erasing the subject key; otherwise
|
|
534
|
+
// the deterministic HMAC would remain equality-matchable until the next
|
|
535
|
+
// rebuild. Keeping this in the sub-transaction makes the sweep rollback
|
|
536
|
+
// with the rest of the user's cleanup.
|
|
555
537
|
await nullBlindIndexesForSubject(
|
|
556
538
|
tx,
|
|
557
539
|
registry.features,
|
|
@@ -559,10 +541,9 @@ async function processUser(args: {
|
|
|
559
541
|
);
|
|
560
542
|
}
|
|
561
543
|
|
|
562
|
-
//
|
|
563
|
-
//
|
|
564
|
-
//
|
|
565
|
-
// Run retried.
|
|
544
|
+
// Flip the status in the same sub-transaction. If a hook throws, execution
|
|
545
|
+
// never reaches this point, so the user remains in DeletionRequested for
|
|
546
|
+
// the next retry.
|
|
566
547
|
await updateUserLifecycle(
|
|
567
548
|
tx,
|
|
568
549
|
userId,
|
|
@@ -572,9 +553,8 @@ async function processUser(args: {
|
|
|
572
553
|
txSucceeded = true;
|
|
573
554
|
});
|
|
574
555
|
} catch (e) {
|
|
575
|
-
//
|
|
576
|
-
//
|
|
577
|
-
// generisches "<sub-transaction>".
|
|
556
|
+
// Track the failing hook so operators see its tenant and entity instead of a
|
|
557
|
+
// generic sub-transaction error.
|
|
578
558
|
errors.push({
|
|
579
559
|
userId,
|
|
580
560
|
tenantId: currentTenantId ?? ("" as TenantId),
|
|
@@ -621,10 +601,9 @@ function resolveForgetFileBytesDeleter(
|
|
|
621
601
|
};
|
|
622
602
|
}
|
|
623
603
|
|
|
624
|
-
//
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
// User-Row und ignorieren tenantId.
|
|
604
|
+
// Use a pseudo-tenant for users with no active or historical memberships.
|
|
605
|
+
// Tenant-scoped hooks find nothing there, while tenant-agnostic hooks such as
|
|
606
|
+
// the user hook still operate on the global user row and ignore tenantId.
|
|
628
607
|
const SYSTEM_TENANT_ID_FOR_ORPHANS = "00000000-0000-0000-0000-000000000000" as TenantId;
|
|
629
608
|
|
|
630
609
|
// "single-user" requires BOTH the app config AND a runtime sole-member check —
|
|
@@ -653,7 +632,7 @@ async function resolveEffectiveTenantModel(
|
|
|
653
632
|
// removeMemberWrite only deletes the tenant-membership PROJECTION row — the
|
|
654
633
|
// event history survives forever, and a departed member's own tenant-scoped
|
|
655
634
|
// data rows (contributor-style records with no per-user column) can
|
|
656
|
-
// outlive their membership
|
|
635
|
+
// outlive their membership. A live count of 1 is therefore not
|
|
657
636
|
// enough: a tenant that ever had >1 distinct member must stay multi-user for
|
|
658
637
|
// the forget path even after everyone but one is removed, or the sole
|
|
659
638
|
// remaining member's forget request wipes the departed co-member's leftover
|
|
@@ -663,7 +642,7 @@ async function resolveEffectiveTenantModel(
|
|
|
663
642
|
// tenant SystemAdmin adds land under the actor's tenant_id.
|
|
664
643
|
//
|
|
665
644
|
// Counting created-ROWS against distinct userIds latched a tenant to
|
|
666
|
-
// multi-user as soon as its sole member was removed and re-added
|
|
645
|
+
// multi-user as soon as its sole member was removed and re-added —
|
|
667
646
|
// two created-events, one identity, no co-member ever. The fail-safe only
|
|
668
647
|
// needs to cover payloads whose userId cannot be READ, so the unreadable
|
|
669
648
|
// remainder is measured directly instead of inferred from the row count.
|
|
@@ -714,14 +693,13 @@ async function readHistoricalMembershipTenantIds(
|
|
|
714
693
|
return rows.map((row) => row.tenant_id as TenantId); // @cast-boundary db-row
|
|
715
694
|
}
|
|
716
695
|
|
|
717
|
-
//
|
|
718
|
-
// - "anonymize" / "blockDelete" → "anonymize"
|
|
719
|
-
//
|
|
720
|
-
// - "hardDelete" / "softDelete" / null → "delete" (
|
|
696
|
+
// Map retention.strategy to user-data-rights.UserDataDeleteStrategy.
|
|
697
|
+
// - "anonymize" / "blockDelete" → "anonymize": retention requirements keep
|
|
698
|
+
// the data physically present while removing its association with the person.
|
|
699
|
+
// - "hardDelete" / "softDelete" / null → "delete" (default).
|
|
721
700
|
//
|
|
722
|
-
//
|
|
723
|
-
//
|
|
724
|
-
// werden kann (siehe run-forget-cleanup.test.ts).
|
|
701
|
+
// Keeping this mapping in one function makes drift between retention strategies
|
|
702
|
+
// and user-data-rights hooks explicit and testable.
|
|
725
703
|
export function policyToStrategy(
|
|
726
704
|
retentionStrategy: "hardDelete" | "softDelete" | "anonymize" | "blockDelete" | null,
|
|
727
705
|
): UserDataDeleteStrategy {
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { createFallbackLogger, type Logger } from "@cosmicdrift/kumiko-framework/logging";
|
|
2
|
-
// User
|
|
3
|
-
// Art. 20 (
|
|
2
|
+
// User data export pipeline — GDPR Art. 15 (access) +
|
|
3
|
+
// Art. 20 (data portability).
|
|
4
4
|
//
|
|
5
|
-
// Pure
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// Pure pipeline function: invokes all EXT_USER_DATA export hooks for
|
|
6
|
+
// all of a user's tenant memberships and collects the results into a
|
|
7
|
+
// structured bundle.
|
|
8
8
|
//
|
|
9
|
-
// **Async ZIP +
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
9
|
+
// **Async ZIP + storage deferred:** This foundation returns the bundle
|
|
10
|
+
// inline as a JSON object. Apps with large file collections need a job
|
|
11
|
+
// wrapper that writes the bundle to S3 or local storage and produces
|
|
12
|
+
// signed URLs; that can be added when a real use case produces more than
|
|
13
|
+
// 10 MB of output. Until then, inline output covers 99% of apps
|
|
14
|
+
// (user profiles, file metadata, and activity history).
|
|
15
15
|
//
|
|
16
|
-
// **Cross-
|
|
17
|
-
// Tenant
|
|
18
|
-
// docs/plans/architecture/user-data-rights.md "Cross-Tenant-Semantik".
|
|
16
|
+
// **Cross-tenant iteration:** As with the forget path, data from Tenant A
|
|
17
|
+
// and Tenant B is included in the same bundle.
|
|
19
18
|
//
|
|
20
|
-
// **PII
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// **PII surface:** Hooks decide which fields enter the bundle.
|
|
20
|
+
// user-data-rights-defaults/hooks/user.userdata-hook explicitly exposes
|
|
21
|
+
// neither passwordHash nor roles (privileged columns).
|
|
23
22
|
|
|
24
23
|
import {
|
|
25
24
|
configuredPiiSubjectKms,
|
|
@@ -59,7 +58,7 @@ export interface RunUserExportArgs {
|
|
|
59
58
|
readonly registry: Registry;
|
|
60
59
|
readonly userId: string;
|
|
61
60
|
readonly now: Instant;
|
|
62
|
-
//
|
|
61
|
+
// Sourced from the owning job's ctx (_escapeHatchAuditSink,
|
|
63
62
|
// systemUser.id); attributes+audits any EXT_USER_DATA usage's declared
|
|
64
63
|
// escapeHatch when its export hook calls ctx.db.unsafeRaw().
|
|
65
64
|
readonly escapeHatchAuditSink?: EscapeHatchAuditSink;
|
|
@@ -71,33 +70,33 @@ export interface UserExportFileRef {
|
|
|
71
70
|
readonly fileRefId: string;
|
|
72
71
|
readonly storageKey: string;
|
|
73
72
|
readonly fileName: string;
|
|
74
|
-
/** Tenant
|
|
73
|
+
/** Tenant containing the file; the same fileRefId cannot be shared across tenants. */
|
|
75
74
|
readonly tenantId: TenantId;
|
|
76
75
|
/**
|
|
77
|
-
* ZIP-internal
|
|
78
|
-
*
|
|
79
|
-
* fileRefs[]
|
|
80
|
-
* path
|
|
76
|
+
* ZIP-internal path under which the file is placed in the export ZIP.
|
|
77
|
+
* Reader tools (compliance audit, self-service portal) use this field
|
|
78
|
+
* to link bundle.json fileRefs[] to the files/-paths. Guaranteed to be
|
|
79
|
+
* safe from path traversal via sanitizeZipFilename.
|
|
81
80
|
*/
|
|
82
81
|
readonly zipPath: string;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
export interface UserExportTenantSection {
|
|
86
85
|
readonly tenantId: TenantId;
|
|
87
|
-
/**
|
|
86
|
+
/** One snippet per entity ({entity, rows[]}); empty when the hook returns null. */
|
|
88
87
|
readonly entities: ReadonlyArray<UserDataExportSnippet>;
|
|
89
88
|
}
|
|
90
89
|
|
|
91
90
|
export interface UserExportBundle {
|
|
92
91
|
readonly userId: string;
|
|
93
|
-
/** ISO-8601
|
|
92
|
+
/** ISO-8601 generation timestamp for the audit trail. */
|
|
94
93
|
readonly generatedAt: string;
|
|
95
|
-
/**
|
|
94
|
+
/** One section for each tenant the user belongs to; orphan users have tenants=[]. */
|
|
96
95
|
readonly tenants: ReadonlyArray<UserExportTenantSection>;
|
|
97
96
|
/**
|
|
98
|
-
* Flat
|
|
99
|
-
*
|
|
100
|
-
*
|
|
97
|
+
* Flat list of all fileRefs from every tenant section. The later ZIP
|
|
98
|
+
* construction job iterates over this list and retrieves binaries from
|
|
99
|
+
* the storage provider. Until then, it is the operator's manifest.
|
|
101
100
|
*/
|
|
102
101
|
readonly fileRefs: ReadonlyArray<UserExportFileRef>;
|
|
103
102
|
}
|
|
@@ -134,39 +133,39 @@ function buildHookDb(
|
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
/**
|
|
137
|
-
* Pure function:
|
|
138
|
-
*
|
|
136
|
+
* Pure function: invokes all EXT_USER_DATA hooks for each tenant
|
|
137
|
+
* membership and collects snippets into a UserExportBundle.
|
|
139
138
|
*
|
|
140
|
-
* **Memory
|
|
139
|
+
* **Memory footprint:**
|
|
141
140
|
*
|
|
142
|
-
*
|
|
143
|
-
* - File
|
|
144
|
-
*
|
|
145
|
-
* - ZIP
|
|
146
|
-
* S3
|
|
147
|
-
* concurrent parts).
|
|
141
|
+
* The entire storage path is streaming-bound:
|
|
142
|
+
* - File binaries use provider.readStream and chunk streaming, scaling
|
|
143
|
+
* to arbitrary file sizes without a heap spike.
|
|
144
|
+
* - ZIP writing uses provider.writeStream; local storage uses
|
|
145
|
+
* fs.createWriteStream, while S3 uses lib-storage.Upload (multipart,
|
|
146
|
+
* with an approximately 20 MB heap bound at 4 concurrent parts).
|
|
148
147
|
*
|
|
149
|
-
* **
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
148
|
+
* **Only non-streaming path:** the bundle object itself (the bundle.json
|
|
149
|
+
* content). It is built entirely in memory before `bundleToZipEntries`
|
|
150
|
+
* yields it as a ZIP entry. Hooks return snippets as plain objects
|
|
151
|
+
* (see UserDataExportSnippet).
|
|
153
152
|
*
|
|
154
|
-
* **Threshold:**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
153
|
+
* **Threshold:** A web app with approximately 500 table rows per user
|
|
154
|
+
* produces about 500 KB of JSON. 50k rows produce about 50 MB. 100k+
|
|
155
|
+
* rows per user (for example, long-running rental-portal logs) create
|
|
156
|
+
* noticeable heap pressure.
|
|
157
157
|
*
|
|
158
|
-
* **
|
|
159
|
-
*
|
|
160
|
-
* JSON
|
|
161
|
-
*
|
|
158
|
+
* **If this becomes a problem:** refactor runUserExport to use an
|
|
159
|
+
* AsyncIterable; hooks would yield snippets instead of returning objects,
|
|
160
|
+
* and the bundle schema would use JSON Lines. bundleToZipEntries would
|
|
161
|
+
* stream entries line by line. This is a non-trivial schema change.
|
|
162
162
|
*
|
|
163
|
-
* **Operator
|
|
164
|
-
*
|
|
163
|
+
* **Operator signal:** if bundle.json exceeds 100 MB in the ZIP, trigger
|
|
164
|
+
* telemetry and evaluate a schema refactor.
|
|
165
165
|
*/
|
|
166
166
|
export async function runUserExport(args: RunUserExportArgs): Promise<UserExportBundle> {
|
|
167
167
|
const { db, registry, userId, now } = args;
|
|
168
168
|
|
|
169
|
-
// Memberships → Tenant-Liste fuer Hook-Iteration.
|
|
170
169
|
const memberships = await selectMany<{ tenantId: TenantId }>(db, tenantMembershipsTable, {
|
|
171
170
|
userId,
|
|
172
171
|
});
|
|
@@ -220,15 +219,15 @@ export async function runUserExport(args: RunUserExportArgs): Promise<UserExport
|
|
|
220
219
|
tenants.push({ tenantId, entities });
|
|
221
220
|
}
|
|
222
221
|
|
|
223
|
-
// Edge
|
|
224
|
-
//
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
222
|
+
// Edge case "0 memberships": tenant-agnostic hooks (for example, the
|
|
223
|
+
// user hook) would run multiple times during cross-tenant iteration.
|
|
224
|
+
// An orphan user with no membership would otherwise skip them entirely.
|
|
225
|
+
// As in the forget runner, add a special run with a pseudo-tenant so
|
|
226
|
+
// the global user row is included in the bundle. Tenant-scoped hooks
|
|
227
|
+
// are no-ops.
|
|
229
228
|
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
229
|
+
// Cross-tenant consistency must not silently break when memberships are
|
|
230
|
+
// empty.
|
|
232
231
|
if (tenantList.length === 0 && hookEntries.length > 0) {
|
|
233
232
|
const orphanEntities: UserDataExportSnippet[] = [];
|
|
234
233
|
for (const entry of hookEntries) {
|
|
@@ -268,15 +267,15 @@ export async function runUserExport(args: RunUserExportArgs): Promise<UserExport
|
|
|
268
267
|
};
|
|
269
268
|
}
|
|
270
269
|
|
|
271
|
-
// Art. 20
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
//
|
|
275
|
-
//
|
|
270
|
+
// Art. 20 requires the data, not its ciphertext: export hooks read raw
|
|
271
|
+
// rows outside the executor's decryption layer, so this central pass
|
|
272
|
+
// decrypts encrypted entity fields after each hook. Without a configured
|
|
273
|
+
// cipher, it exports an explicit marker instead of presenting the base64
|
|
274
|
+
// blob as a value (leak by confusion).
|
|
276
275
|
const ENCRYPTED_UNAVAILABLE = "[encrypted:unavailable]";
|
|
277
276
|
|
|
278
277
|
// Shared decrypt sweep, used for both `snippet.rows` and `snippet.fileRefs`
|
|
279
|
-
//
|
|
278
|
+
// `encryptedFields` is keyed by field NAME, so it stays a no-op
|
|
280
279
|
// for today's `fileRef` entity but covers a future generically-encrypted
|
|
281
280
|
// side-channel field too.
|
|
282
281
|
async function decryptRecords(
|
|
@@ -348,7 +347,6 @@ async function decryptSnippetFields(
|
|
|
348
347
|
return { ...snippet, rows, fileRefs };
|
|
349
348
|
}
|
|
350
349
|
|
|
351
|
-
// Pseudo-
|
|
352
|
-
//
|
|
353
|
-
// Hooks finden hier nichts (no-op).
|
|
350
|
+
// Pseudo-tenant for users without active memberships. It uses an
|
|
351
|
+
// RFC4122 null UUID; tenant-scoped hooks find no data here (no-op).
|
|
354
352
|
const SYSTEM_TENANT_ID_FOR_ORPHANS = "00000000-0000-0000-0000-000000000000" as TenantId;
|