@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,30 +1,30 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Three-layer retention resolver.
|
|
2
2
|
//
|
|
3
|
-
// Layer 1: Entity
|
|
4
|
-
// Layer 2: Tenant
|
|
5
|
-
// Layer 3: Tenant
|
|
3
|
+
// Layer 1: Entity default — Feature author sets r.entity({retention})
|
|
4
|
+
// Layer 2: Tenant preset — Tenant admin selects a bundle (RETENTION_PRESETS)
|
|
5
|
+
// Layer 3: Tenant override — per (tenantId, entityName) JSON override
|
|
6
6
|
//
|
|
7
|
-
//
|
|
7
|
+
// Resolution order:
|
|
8
8
|
// effective = override(tenantId, entityName)
|
|
9
9
|
// ?? preset[tenant.retentionPreset][entityName]
|
|
10
|
-
// ?? entity.retention (
|
|
11
|
-
// ?? null (
|
|
10
|
+
// ?? entity.retention (code default from EntityDefinition)
|
|
11
|
+
// ?? null (no automatic action)
|
|
12
12
|
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// blockDelete-
|
|
13
|
+
// The cleanup job resolves this for each (tenantId, entityName) and
|
|
14
|
+
// decides what to do (hardDelete / softDelete / anonymize /
|
|
15
|
+
// blockDelete with a grace-period check).
|
|
16
16
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
17
|
+
// The cross-feature API r.exposesApi("retention.policyFor")
|
|
18
|
+
// makes this consumable by user-data-rights — the forget flow
|
|
19
|
+
// queries blockDelete fields and anonymizes them instead of deleting them.
|
|
20
20
|
|
|
21
21
|
import type { EntityDefinition, RetentionDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
22
22
|
import { RETENTION_PRESETS, type RetentionPresetKey } from "./presets.ts";
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
25
|
+
* Raw override from the database table (the config column as a JSON string).
|
|
26
|
+
* Unlike RetentionDef, every property is optional because an override may
|
|
27
|
+
* replace individual properties.
|
|
28
28
|
*/
|
|
29
29
|
export interface RetentionOverride {
|
|
30
30
|
readonly keepFor?: string;
|
|
@@ -33,13 +33,13 @@ export interface RetentionOverride {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Effective policy after the resolver ran. `source` records WHICH layer
|
|
37
|
+
* supplied the value, as an audit trail for the DPO.
|
|
38
38
|
*
|
|
39
|
-
* `override-incomplete`
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* `override-incomplete` means a tenant override is set but fills keepFor
|
|
40
|
+
* neither itself nor through the preset/entity fallback. The cleanup job
|
|
41
|
+
* logs a warning and skips instead of deleting everything at once with a
|
|
42
|
+
* default "0d".
|
|
43
43
|
*/
|
|
44
44
|
export interface EffectiveRetentionPolicy {
|
|
45
45
|
readonly entityName: string;
|
|
@@ -55,15 +55,15 @@ export interface ResolveRetentionPolicyArgs {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* Resolves the three layers. This is a pure function with no database
|
|
59
|
+
* access; the caller supplies all inputs (the cleanup job aggregates them
|
|
60
|
+
* per tenant beforehand).
|
|
61
61
|
*/
|
|
62
62
|
export function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): EffectiveRetentionPolicy {
|
|
63
63
|
const { entityName, entityDef, tenantPreset, tenantOverride } = args;
|
|
64
64
|
|
|
65
|
-
// Layer 3: Override wins,
|
|
66
|
-
//
|
|
65
|
+
// Layer 3: Override wins, but it may omit fields, which then
|
|
66
|
+
// fall back to the individual properties from Layer 2 or 1.
|
|
67
67
|
if (tenantOverride !== null) {
|
|
68
68
|
const baseFromPreset =
|
|
69
69
|
tenantPreset !== null ? (RETENTION_PRESETS[tenantPreset]?.[entityName] ?? null) : null;
|
|
@@ -73,10 +73,11 @@ export function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): Effect
|
|
|
73
73
|
const keepFor = tenantOverride.keepFor ?? base?.keepFor;
|
|
74
74
|
const strategy = tenantOverride.strategy ?? base?.strategy;
|
|
75
75
|
|
|
76
|
-
// keepFor + strategy
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
76
|
+
// keepFor + strategy are required for every active policy. If
|
|
77
|
+
// neither the override nor the base supplies them, the override is
|
|
78
|
+
// semantically incomplete — the cleanup job must warn instead of
|
|
79
|
+
// immediately deleting everything with the default "0d". The source
|
|
80
|
+
// marker documents this.
|
|
80
81
|
if (keepFor === undefined || strategy === undefined) {
|
|
81
82
|
return { entityName, policy: null, source: "override-incomplete" };
|
|
82
83
|
}
|
|
@@ -102,6 +103,6 @@ export function resolveRetentionPolicy(args: ResolveRetentionPolicyArgs): Effect
|
|
|
102
103
|
return { entityName, policy: entityDef.retention, source: "entity-default" };
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
//
|
|
106
|
+
// No policy is defined, so the cleanup job skips this entity for this tenant.
|
|
106
107
|
return { entityName, policy: null, source: "none" };
|
|
107
108
|
}
|
|
@@ -1,42 +1,41 @@
|
|
|
1
|
-
// Retention-Cleanup-Runner
|
|
2
|
-
//
|
|
1
|
+
// Retention-Cleanup-Runner — pure function, called by the retention-cleanup cron
|
|
2
|
+
// for each fan-out tenant.
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// ist:
|
|
4
|
+
// Iterates all implicit entity projections, resolves the effective retention
|
|
5
|
+
// policy per entity (3-layer resolver, see resolver.ts), and applies the
|
|
6
|
+
// strategy to rows whose reference timestamp is older than the keepFor cutoff:
|
|
8
7
|
//
|
|
9
|
-
// - hardDelete → executor.forget
|
|
8
|
+
// - hardDelete → executor.forget per row (event → rebuild-safe hard purge).
|
|
10
9
|
// Entities with file/image/files/images fields: bytes first
|
|
11
10
|
// (incl. derivatives), then the fileRef row(s), then the row;
|
|
12
11
|
// shared fileRefs (other owner, other row of the same table)
|
|
13
12
|
// stay untouched.
|
|
14
|
-
// - softDelete → executor.delete
|
|
15
|
-
//
|
|
16
|
-
// - anonymize → executor.update
|
|
17
|
-
// anonymize
|
|
18
|
-
// - blockDelete →
|
|
19
|
-
// user-forget
|
|
20
|
-
// anonymize
|
|
21
|
-
//
|
|
13
|
+
// - softDelete → executor.delete per row (event → rebuild-safe soft-delete),
|
|
14
|
+
// only on not-yet-deleted rows (isDeleted:false filter)
|
|
15
|
+
// - anonymize → executor.update per row with the values of the per-field
|
|
16
|
+
// anonymize functions; row remains (fields.ts contract)
|
|
17
|
+
// - blockDelete → untouched during keepFor (retention requirement;
|
|
18
|
+
// user-forget triggers anonymize). AFTER expiration, the
|
|
19
|
+
// anonymize functions run — business data remains, the person-
|
|
20
|
+
// identifying link is removed (fields.ts:44-47).
|
|
22
21
|
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
22
|
+
// Via the executor instead of batch-deleteMany/updateMany: an eventless batch write
|
|
23
|
+
// to the (erased) projection table is wiped/resurrected during a projection rebuild.
|
|
24
|
+
// Cost: N events per cleanup instead of one batch statement (per-row ceiling, with
|
|
25
|
+
// a batch-event variant later).
|
|
27
26
|
//
|
|
28
|
-
// **
|
|
29
|
-
// (keyed
|
|
30
|
-
//
|
|
31
|
-
// reference
|
|
32
|
-
//
|
|
27
|
+
// **Stricter than soft-delete-cleanup:** this cron hard-deletes LIVE rows
|
|
28
|
+
// (keyed on reference, default createdAt), not already-soft-deleted ones.
|
|
29
|
+
// Therefore the column-existence check comes before every WHERE — a missing/mistyped
|
|
30
|
+
// reference column would otherwise produce a malformed/all-matching WHERE and
|
|
31
|
+
// delete everything.
|
|
33
32
|
//
|
|
34
|
-
// **anonymize
|
|
35
|
-
// row-
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
33
|
+
// **anonymize idempotence without a marker column:** the anonymize functions are
|
|
34
|
+
// row-independent (`() => unknown`) — evaluated once per entity, they produce
|
|
35
|
+
// the target values. A row that already carries them is skipped, so the
|
|
36
|
+
// daily rerun appends zero events. The WHERE continues to match anonymized
|
|
37
|
+
// rows (reference remains old), hence id-cursor paging instead of a single
|
|
38
|
+
// page — otherwise completed rows clog the batchLimit window.
|
|
40
39
|
|
|
41
40
|
import {
|
|
42
41
|
collectPiiSubjectFields,
|
|
@@ -68,6 +67,7 @@ import {
|
|
|
68
67
|
type SessionUser,
|
|
69
68
|
type TenantId,
|
|
70
69
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
70
|
+
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
71
71
|
import {
|
|
72
72
|
type FileContext,
|
|
73
73
|
fileRefEntity,
|
|
@@ -89,13 +89,13 @@ import { tenantRetentionOverrideTable } from "./schema/tenant-retention-override
|
|
|
89
89
|
const DEFAULT_BATCH_LIMIT = 1000;
|
|
90
90
|
const DEFAULT_REFERENCE_FIELD = "createdAt";
|
|
91
91
|
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
// entity.fields
|
|
95
|
-
// (table-builder.ts: inserted_at/modified_at).
|
|
96
|
-
//
|
|
97
|
-
//
|
|
98
|
-
//
|
|
92
|
+
// The boot validator (boot-validator/pii-retention.ts FRAMEWORK_TIMESTAMP_FIELDS)
|
|
93
|
+
// permits these aliases as retention.reference even when they are not declared in
|
|
94
|
+
// entity.fields, but the physical columns use different names
|
|
95
|
+
// (table-builder.ts: inserted_at/modified_at). Map them to the actual entity field
|
|
96
|
+
// during cleanup; otherwise the column existence check below would cause the cron
|
|
97
|
+
// to do nothing silently. deletedAt/lastSeenAt are real fields (softDelete and
|
|
98
|
+
// session respectively) and need no translation.
|
|
99
99
|
const FRAMEWORK_REFERENCE_ALIAS: Readonly<Record<string, string>> = {
|
|
100
100
|
createdAt: "insertedAt",
|
|
101
101
|
updatedAt: "modifiedAt",
|
|
@@ -108,14 +108,16 @@ export interface RunRetentionCleanupArgs {
|
|
|
108
108
|
/** Layer-2 preset from resolveTenantRetentionPreset, resolved once per tenant
|
|
109
109
|
* by the caller rather than per entity here. null = layers 1 and 3 only. */
|
|
110
110
|
readonly preloadedTenantPreset: RetentionPresetKey | null;
|
|
111
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Injects the current time so tests can pin it without mocking the date (pattern keep-for.ts).
|
|
113
|
+
*/
|
|
112
114
|
readonly now: Instant;
|
|
113
115
|
readonly batchLimit?: number;
|
|
114
116
|
/** Storage access for hardDelete entities with file/image/files/images fields.
|
|
115
117
|
* Missing while fileRefs are due → the row stays (skipped "missing_file_storage"),
|
|
116
118
|
* fail-closed instead of orphaning bytes. */
|
|
117
119
|
readonly files?: FileContext;
|
|
118
|
-
/** KMS for erasing a hardDeleted row's own subject key
|
|
120
|
+
/** KMS for erasing a hardDeleted row's own subject key. Defaults
|
|
119
121
|
* to configuredPiiSubjectKms() (same fallback as run-forget-cleanup.ts);
|
|
120
122
|
* undefined/no-KMS-configured skips erasure entirely — the row is still
|
|
121
123
|
* hard-deleted, only crypto-shredding of its DEK is not attempted. */
|
|
@@ -137,7 +139,10 @@ export interface RunRetentionCleanupResult {
|
|
|
137
139
|
readonly softDeleted: number;
|
|
138
140
|
/** Rows deren anonymize-Feld-Funktionen angewendet wurden (anonymize + abgelaufene blockDelete). */
|
|
139
141
|
readonly anonymized: number;
|
|
140
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* Anomalies where the policy references a missing table column or anonymize has no anonymize
|
|
144
|
+
* fields.
|
|
145
|
+
*/
|
|
141
146
|
readonly skipped: readonly RetentionCleanupSkip[];
|
|
142
147
|
}
|
|
143
148
|
|
|
@@ -159,7 +164,7 @@ async function purgeMatchingRows(
|
|
|
159
164
|
return count;
|
|
160
165
|
}
|
|
161
166
|
|
|
162
|
-
// --- hardDelete crypto-shredding
|
|
167
|
+
// --- hardDelete crypto-shredding ---
|
|
163
168
|
//
|
|
164
169
|
// recordOwned/self-pii resolution (subject-resolver.ts) only ever reads
|
|
165
170
|
// `row.id` for these two kinds — resolveRecordSubject uses opts.entityName +
|
|
@@ -243,17 +248,14 @@ async function forgetRowAndEraseKeys(args: {
|
|
|
243
248
|
readonly entityName: string;
|
|
244
249
|
readonly tenantId: TenantId;
|
|
245
250
|
readonly systemUser: SessionUser;
|
|
251
|
+
readonly rowId: EntityId;
|
|
246
252
|
readonly row: Record<string, unknown>;
|
|
247
253
|
readonly kms: KmsAdapter | undefined;
|
|
248
254
|
readonly registry: Registry;
|
|
249
255
|
}): Promise<{ readonly isSuccess: boolean }> {
|
|
250
256
|
return runInSubTransaction(args.db, async (sp) => {
|
|
251
257
|
const tdb = createTenantDb({ db: sp, tenantId: args.tenantId, mode: "system" });
|
|
252
|
-
const result = await args.executor.forget(
|
|
253
|
-
{ id: args.row["id"] as EntityId },
|
|
254
|
-
args.systemUser,
|
|
255
|
-
tdb,
|
|
256
|
-
);
|
|
258
|
+
const result = await args.executor.forget({ id: args.rowId }, args.systemUser, tdb);
|
|
257
259
|
if (!result.isSuccess) return { isSuccess: false };
|
|
258
260
|
await eraseHardDeletedRowKeys({
|
|
259
261
|
kms: args.kms,
|
|
@@ -268,7 +270,7 @@ async function forgetRowAndEraseKeys(args: {
|
|
|
268
270
|
});
|
|
269
271
|
}
|
|
270
272
|
|
|
271
|
-
// --- hardDelete + file-fields
|
|
273
|
+
// --- hardDelete + file-fields ---
|
|
272
274
|
|
|
273
275
|
// Throws (unsafe key, missing list permission) must fail this row closed,
|
|
274
276
|
// not abort the whole tenant run.
|
|
@@ -298,6 +300,7 @@ async function purgeHardDeleteRowWithFiles(args: {
|
|
|
298
300
|
readonly tenantId: TenantId;
|
|
299
301
|
readonly entityName: string;
|
|
300
302
|
readonly entity: EntityDefinition;
|
|
303
|
+
readonly rowId: EntityId;
|
|
301
304
|
readonly row: Record<string, unknown>;
|
|
302
305
|
readonly files: FileContext | undefined;
|
|
303
306
|
readonly entityExecutor: ReturnType<typeof createEventStoreExecutor>;
|
|
@@ -308,8 +311,7 @@ async function purgeHardDeleteRowWithFiles(args: {
|
|
|
308
311
|
readonly registry: Registry;
|
|
309
312
|
readonly onSkip: (reason: "missing_file_storage" | "file_delete_failed") => void;
|
|
310
313
|
}): Promise<boolean> {
|
|
311
|
-
const
|
|
312
|
-
const logContext = `tenant=${args.tenantId} entity=${args.entityName} row=${rowId}`;
|
|
314
|
+
const logContext = `tenant=${args.tenantId} entity=${args.entityName} row=${String(args.rowId)}`;
|
|
313
315
|
const files = args.files;
|
|
314
316
|
const outcome = await purgeRowFileRefs({
|
|
315
317
|
db: args.db,
|
|
@@ -341,6 +343,7 @@ async function purgeHardDeleteRowWithFiles(args: {
|
|
|
341
343
|
entityName: args.entityName,
|
|
342
344
|
tenantId: args.tenantId,
|
|
343
345
|
systemUser: args.systemUser,
|
|
346
|
+
rowId: args.rowId,
|
|
344
347
|
row: args.row,
|
|
345
348
|
kms: args.kms,
|
|
346
349
|
registry: args.registry,
|
|
@@ -392,7 +395,14 @@ async function purgeHardDeleteRowsWithFiles(args: {
|
|
|
392
395
|
});
|
|
393
396
|
if (rows.length === 0) break;
|
|
394
397
|
for (const row of rows) {
|
|
395
|
-
|
|
398
|
+
const rawId = row["id"];
|
|
399
|
+
if (typeof rawId !== "string" && typeof rawId !== "number") {
|
|
400
|
+
throw new InternalError({
|
|
401
|
+
message: `data-retention: ${args.entityName} row has no usable id (got ${typeof rawId})`,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
const rowId = rawId;
|
|
405
|
+
cursor = rowId;
|
|
396
406
|
const ok = await purgeHardDeleteRowWithFiles({
|
|
397
407
|
db: args.db,
|
|
398
408
|
table: args.table,
|
|
@@ -400,6 +410,7 @@ async function purgeHardDeleteRowsWithFiles(args: {
|
|
|
400
410
|
tenantId: args.tenantId,
|
|
401
411
|
entityName: args.entityName,
|
|
402
412
|
entity: args.entity,
|
|
413
|
+
rowId,
|
|
403
414
|
row,
|
|
404
415
|
files: args.files,
|
|
405
416
|
entityExecutor: args.entityExecutor,
|
|
@@ -490,9 +501,9 @@ export async function runRetentionCleanup(
|
|
|
490
501
|
|
|
491
502
|
// Retention writes go through the executor (events) so a projection rebuild
|
|
492
503
|
// replays the cleanup — a batch deleteMany/updateMany on the (erased)
|
|
493
|
-
// projection table is eventless and gets wiped/resurrected on rebuild
|
|
494
|
-
//
|
|
495
|
-
//
|
|
504
|
+
// projection table is eventless and gets wiped/resurrected on rebuild.
|
|
505
|
+
// The cron acts as the system actor; the WHERE already tenant-scopes, so a
|
|
506
|
+
// system-mode TenantDb (no extra filter) is correct.
|
|
496
507
|
// ponytail: per-row events, one batchLimit-sized page per entity per run —
|
|
497
508
|
// the daily cron converges (hardDelete removes rows, softDelete's isDeleted
|
|
498
509
|
// filter shrinks the set). A single batched forget/delete event is a follow-up.
|
|
@@ -511,8 +522,9 @@ export async function runRetentionCleanup(
|
|
|
511
522
|
const overrideByEntity = new Map(overrideRows.map((r) => [r.entityName, { config: r.config }]));
|
|
512
523
|
|
|
513
524
|
for (const proj of registry.getAllProjections().values()) {
|
|
514
|
-
//
|
|
515
|
-
//
|
|
525
|
+
// Only implicit entity projections with a table are targets, as in
|
|
526
|
+
// soft-delete-cleanup. Custom projections and unmanaged tables (such as
|
|
527
|
+
// sessions) are excluded.
|
|
516
528
|
if (proj.isImplicit !== true || typeof proj.source !== "string" || !proj.table) continue;
|
|
517
529
|
const entityName = proj.source;
|
|
518
530
|
|
|
@@ -539,9 +551,9 @@ export async function runRetentionCleanup(
|
|
|
539
551
|
|
|
540
552
|
const cutoff = computeCutoff(policy.keepFor, now);
|
|
541
553
|
const where: WhereObject = { [referenceField]: { lt: cutoff } };
|
|
542
|
-
//
|
|
543
|
-
// soft-delete-cleanup.
|
|
544
|
-
//
|
|
554
|
+
// Apply tenant scoping only when the table has a tenantId column, as in
|
|
555
|
+
// soft-delete-cleanup. Without this filter, one tenant could affect another
|
|
556
|
+
// tenant's rows.
|
|
545
557
|
if (propertyOf(proj.table, "tenantId") !== undefined) {
|
|
546
558
|
where["tenantId"] = tenantId;
|
|
547
559
|
}
|
|
@@ -559,6 +571,7 @@ export async function runRetentionCleanup(
|
|
|
559
571
|
entityName,
|
|
560
572
|
tenantId,
|
|
561
573
|
systemUser,
|
|
574
|
+
rowId: id,
|
|
562
575
|
row: { id },
|
|
563
576
|
kms,
|
|
564
577
|
registry,
|
|
@@ -5,21 +5,23 @@ import {
|
|
|
5
5
|
createTextField,
|
|
6
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
7
|
|
|
8
|
-
// tenantRetentionOverride — Layer 3
|
|
8
|
+
// tenantRetentionOverride — Layer 3 in the three-layer resolver.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// -
|
|
13
|
-
// - Pilot
|
|
14
|
-
// -
|
|
10
|
+
// One optional override config per (tenantId, entityName) replaces the
|
|
11
|
+
// preset (Layer 2) for that entity only. Use cases:
|
|
12
|
+
// - Law firm in DE: caseFile 6y blockDelete (not in the preset)
|
|
13
|
+
// - Pilot tenant retaining data longer for testing
|
|
14
|
+
// - Industry-specific shorter retention periods
|
|
15
15
|
//
|
|
16
|
-
// reason
|
|
16
|
+
// reason is required so the decision is auditable by the DPO and supervisory
|
|
17
|
+
// authorities.
|
|
17
18
|
//
|
|
18
|
-
// config
|
|
19
|
-
//
|
|
19
|
+
// config is a JSON string with `{ keepFor, strategy, reference? }`. The Zod
|
|
20
|
+
// schema validates it when set-override is called.
|
|
20
21
|
//
|
|
21
|
-
// Tenant-
|
|
22
|
-
//
|
|
22
|
+
// Tenant one-to-many: each tenant can have any number of entity overrides.
|
|
23
|
+
// A UNIQUE index on (tenantId, entityName) allows at most one override per
|
|
24
|
+
// entity.
|
|
23
25
|
export const tenantRetentionOverrideEntity = createEntity({
|
|
24
26
|
table: "read_tenant_retention_overrides",
|
|
25
27
|
fields: {
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
export type DeliveryFeatureOptions = {
|
|
47
47
|
// Access gate for the delivery-log screen and the `log` query it reads —
|
|
48
48
|
// both move together so the nav entry a role sees always matches a
|
|
49
|
-
// callable handler
|
|
49
|
+
// callable handler. Default access.admin includes TenantAdmin,
|
|
50
50
|
// who in a line-of-business app is the operator, not the platform — pass
|
|
51
51
|
// access.systemAdmin to keep this platform-only and out of their nav.
|
|
52
52
|
readonly access?: readonly string[];
|
|
@@ -99,10 +99,10 @@ export function createDeliveryFeature(options?: DeliveryFeatureOptions): Feature
|
|
|
99
99
|
// boot-validator accepts the projection below because at least one
|
|
100
100
|
// apply-key is a registered domain-event (DELIVERY_ATTEMPT_EVENT).
|
|
101
101
|
// recipientAddress is the real PII (email address); recipientId stays
|
|
102
|
-
// plaintext — pseudonymous fk, same line as config.userId
|
|
102
|
+
// plaintext — pseudonymous fk, same line as config.userId.
|
|
103
103
|
// recipientId is nullable (system-triggered sends to non-users), so the
|
|
104
104
|
// address falls back to the tenant key instead of the plaintext it used
|
|
105
|
-
// to be — still shreddable, one tenant wide
|
|
105
|
+
// to be — still shreddable, one tenant wide.
|
|
106
106
|
r.defineEvent("attempt", deliveryAttemptSchema, {
|
|
107
107
|
piiFields: {
|
|
108
108
|
recipientAddress: { personal: { of: "recipientId", whenAbsent: "tenant" } },
|
|
@@ -208,7 +208,7 @@ export const logQuery = definePagedQueryHandler({
|
|
|
208
208
|
const nextCursor =
|
|
209
209
|
rows.length === limit && lastRow ? encodeSortCursor(sortColumn, lastRow) : null;
|
|
210
210
|
|
|
211
|
-
// recipientAddress is stored encrypted under the recipient's DEK
|
|
211
|
+
// recipientAddress is stored encrypted under the recipient's DEK
|
|
212
212
|
// — decrypt for the admin log view; forgotten subjects show [[erased]].
|
|
213
213
|
// The notificationType/recipientAddress → type/recipient rename happens
|
|
214
214
|
// here, not in the client: a projectionList screen has no entity to
|
|
@@ -53,14 +53,15 @@ export const deliveryAttemptsTable = pgTable(
|
|
|
53
53
|
(t) => [index(DELIVERY_ATTEMPTS_TENANT_CREATED_INDEX).on(t.tenantId, t.createdAt)],
|
|
54
54
|
);
|
|
55
55
|
|
|
56
|
-
// **Unmanaged table** —
|
|
57
|
-
// - id
|
|
58
|
-
// -
|
|
59
|
-
//
|
|
60
|
-
// - created_at
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
//
|
|
56
|
+
// **Unmanaged table** — deliberately has no createEntity because:
|
|
57
|
+
// - id comes from the aggregate stream (no gen_random_uuid()-DEFAULT)
|
|
58
|
+
// - no version/inserted_by/modified_by/modified_at: no in-place edits or
|
|
59
|
+
// audit columns are needed (idempotent-on-replay via PK conflict)
|
|
60
|
+
// - created_at rather than inserted_at reflects historical naming drift, not
|
|
61
|
+
// a bug
|
|
62
|
+
// The app is responsible for tenant scoping in queries and replay idempotency.
|
|
63
|
+
// pgTable remains the source of truth for the query API; the pgTable is derived
|
|
64
|
+
// from this metadata.
|
|
64
65
|
export const deliveryAttemptsTableMeta: EntityTableMeta = defineUnmanagedTable({
|
|
65
66
|
tableName: "store_delivery_attempts",
|
|
66
67
|
indexes: [{ name: DELIVERY_ATTEMPTS_TENANT_CREATED_INDEX, columns: ["tenant_id", "created_at"] }],
|
|
@@ -31,13 +31,13 @@ const localeClaimSchema = z
|
|
|
31
31
|
.regex(/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{1,8})*$/)
|
|
32
32
|
.optional();
|
|
33
33
|
|
|
34
|
-
// Shape
|
|
35
|
-
//
|
|
34
|
+
// Shape of verified JWT payloads. `tenantId` arrives from jose as a string and
|
|
35
|
+
// becomes a branded `TenantId` only after successful `parse()`—never via a blind cast.
|
|
36
36
|
//
|
|
37
37
|
// `kind` is explicitly typed as "absent or undefined, nothing else" here
|
|
38
38
|
// (not bare `.optional()`, which would also accept any other value on an
|
|
39
|
-
// unrelated key) so an address-token payload
|
|
40
|
-
// `kind: "address"
|
|
39
|
+
// unrelated key) so an address-token payload—which always carries
|
|
40
|
+
// `kind: "address"`—fails this schema instead of silently parsing with
|
|
41
41
|
// `addressHash` mistaken for a userId.
|
|
42
42
|
const unsubscribeJwtPayloadSchema = z.object({
|
|
43
43
|
kind: z.undefined().optional(),
|
|
@@ -28,10 +28,9 @@ const executor = createEventStoreExecutor(
|
|
|
28
28
|
{ entityName: "notification-preference" },
|
|
29
29
|
);
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// im Helper (db-row-boundary), nicht 4× pro Callsite.
|
|
31
|
+
// Concrete lookup result shape containing only the fields needed by the upsert update path. Avoids
|
|
32
|
+
// `row["x"] as T` index-access casts; the generic parameter on fetchOne centralizes the cast in the
|
|
33
|
+
// helper (`db-row-boundary`) instead of repeating it at each call site.
|
|
35
34
|
type PreferenceLookupRow = {
|
|
36
35
|
readonly id: string;
|
|
37
36
|
readonly version: number;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
createTextField,
|
|
7
7
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// (MVP) page shape — LiteParse fills this in.
|
|
10
10
|
// Kept minimal on purpose: downstream themes (provider, recipe) extend it.
|
|
11
11
|
// Stored as JSON.stringify(IngestPage[]) in the encrypted `pages` column —
|
|
12
12
|
// jsonb has no encryption support in the engine, and this column holds the
|
|
@@ -29,7 +29,7 @@ export type DocumentExtractMeta = {
|
|
|
29
29
|
|
|
30
30
|
// documentExtract — implicit entity-projection (r.entity, NOT r.projection):
|
|
31
31
|
// an explicit r.projection would make `forget` non-rebuild-safe and require
|
|
32
|
-
// archiveStream instead of the regular forget path
|
|
32
|
+
// archiveStream instead of the regular forget path.
|
|
33
33
|
// fileRefId points at the source file_refs row; storageKey duplicates it for
|
|
34
34
|
// direct provider access without a join.
|
|
35
35
|
export const documentExtractEntity = createEntity({
|
|
@@ -42,11 +42,11 @@ export const documentExtractEntity = createEntity({
|
|
|
42
42
|
reason: "technical_reference",
|
|
43
43
|
}),
|
|
44
44
|
// Holds the full extracted document text (PII). `personal: "tenant"`,
|
|
45
|
-
// NOT `encrypted: true
|
|
45
|
+
// NOT `encrypted: true`: the master-key path has no erasure subject,
|
|
46
46
|
// so nothing here would ever be shreddable. Tenant-subject ciphertext
|
|
47
|
-
// dies with eraseSubjectKeys on tenant-destroy
|
|
47
|
+
// dies with eraseSubjectKeys on tenant-destroy. Whose
|
|
48
48
|
// subject a third party named inside a document is stays open, same as
|
|
49
|
-
// inbound-mail-foundation
|
|
49
|
+
// inbound-mail-foundation. meta is provider telemetry only and
|
|
50
50
|
// stays plaintext jsonb.
|
|
51
51
|
pages: createLongTextField({
|
|
52
52
|
personal: "tenant",
|
|
@@ -4,8 +4,8 @@ import * as z from "zod";
|
|
|
4
4
|
// stream per fileRef with aggregateId derived from the fileRefId
|
|
5
5
|
// (documentIngestAggregateId), never the fileRefId itself: foreign events on
|
|
6
6
|
// the fileRef stream would break the fileRef's optimistic lock. Not backed by an r.entity;
|
|
7
|
-
// this is a pure saga-anchor event (kumiko-
|
|
8
|
-
//
|
|
7
|
+
// this is a pure saga-anchor event that the worker job (kumiko-enterprise LiteParse provider)
|
|
8
|
+
// subscribes to.
|
|
9
9
|
export const DOCUMENT_INGEST_AGGREGATE_TYPE = "document-ingest";
|
|
10
10
|
|
|
11
11
|
// Stream name of stored extract events; differs from the camelCase r.entity name
|
|
@@ -166,7 +166,7 @@ export function createDocumentIngestFoundationFeature() {
|
|
|
166
166
|
default: "deu+eng",
|
|
167
167
|
// Tesseract -l argument syntax: one or more 3-letter language codes
|
|
168
168
|
// joined by "+" (e.g. "deu+eng"). Unconstrained free text here would
|
|
169
|
-
// reach the OCR provider call unvalidated
|
|
169
|
+
// reach the OCR provider call unvalidated.
|
|
170
170
|
pattern: { regex: "^[a-z]{3}(\\+[a-z]{3})*$" },
|
|
171
171
|
write: access.roles("TenantAdmin", "SystemAdmin"),
|
|
172
172
|
read: access.roles("TenantAdmin", "SystemAdmin", "User"),
|
|
@@ -178,7 +178,7 @@ export function createDocumentIngestFoundationFeature() {
|
|
|
178
178
|
default: 50,
|
|
179
179
|
// Without a bound a tenant could set 0 (silently kills ingest) or an
|
|
180
180
|
// unbounded value (OCR worker runs per-file for as long as the PDF
|
|
181
|
-
// has pages — job-queue starvation)
|
|
181
|
+
// has pages — job-queue starvation).
|
|
182
182
|
bounds: { min: 1, max: 500 },
|
|
183
183
|
write: access.roles("TenantAdmin", "SystemAdmin"),
|
|
184
184
|
read: access.roles("TenantAdmin", "SystemAdmin", "User"),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// longText (jsonb has no encryption support), so writers must persist
|
|
3
3
|
// JSON.stringify(IngestPage[]) and readers must parse back. Centralising
|
|
4
4
|
// the codec here keeps liteparse + recipe consumers lockstep with the
|
|
5
|
-
// entity shape
|
|
5
|
+
// entity shape.
|
|
6
6
|
|
|
7
7
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
8
8
|
import type { IngestPage } from "./entity.ts";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Tenant-destroy hook for the documentExtract entity-projection
|
|
1
|
+
// Tenant-destroy hook for the documentExtract entity-projection.
|
|
2
2
|
//
|
|
3
3
|
// Per-row forget() through the executor, not a bulk deleteMany: documentExtract
|
|
4
4
|
// is an ES-managed implicit projection, so a store-table write here would be
|
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// resurrect every row this hook removed. forget() (Art. 17 hard-purge) is
|
|
7
7
|
// replayed by the implicit projection itself, which keeps the erasure
|
|
8
8
|
// rebuild-safe — the same reasoning as tenant-lifecycle's membership stage,
|
|
9
|
-
// and the reason the feature owns an r.entity instead of an r.projection
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// and the reason the feature owns an r.entity instead of an r.projection.
|
|
10
|
+
// The `pages` ciphertext dies separately when the pipeline's later
|
|
11
|
+
// `subject-keys` stage erases the tenant subject key.
|
|
12
12
|
|
|
13
13
|
import { createSystemUser, type TenantDataDestroyHook } from "@cosmicdrift/kumiko-framework/engine";
|
|
14
14
|
import { documentExtractsTable } from "./entity.ts";
|
|
@@ -66,7 +66,7 @@ export function composeTierResolverWithGlobalToggles(
|
|
|
66
66
|
// Non-toggleable (always-on) names must keep reaching `result` through the
|
|
67
67
|
// globalCascade loop below even if this resolver's SYSTEM_TENANT_ID answer
|
|
68
68
|
// satisfies the emptiness guard above without also merging always-on into
|
|
69
|
-
// every per-tenant tierSet
|
|
69
|
+
// every per-tenant tierSet.
|
|
70
70
|
tierManaged = new Set(
|
|
71
71
|
[...managed].filter((name) => {
|
|
72
72
|
const feature = registry.getFeature(name);
|