@cosmicdrift/kumiko-samples 0.389.0 → 0.390.1
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/package.json +1 -1
- package/packages/bundled-features/package.json +27 -15
- package/packages/bundled-features/src/audit/feature.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +13 -1
- package/packages/bundled-features/src/auth-email-password/changes.json +6 -0
- package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +7 -7
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.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 +9 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +11 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -4
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +9 -7
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +23 -16
- package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +12 -8
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +4 -2
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
- package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +24 -14
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +6 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +3 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +14 -9
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -4
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +25 -14
- package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +10 -4
- package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +9 -3
- package/packages/bundled-features/src/auth-mfa/verify-factor.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +5 -5
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +10 -7
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +15 -12
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-open-cancels.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-provider-lock.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +17 -58
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +10 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +18 -15
- package/packages/bundled-features/src/billing-foundation/events.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/feature.ts +8 -3
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +5 -5
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/types.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +5 -4
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +1 -1
- package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
- package/packages/bundled-features/src/cap-counter/feature.ts +2 -2
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
- package/packages/bundled-features/src/cap-counter/reservation-chain.ts +1 -1
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +4 -4
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +12 -7
- package/packages/bundled-features/src/config/feature.ts +6 -6
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +23 -13
- package/packages/bundled-features/src/config/handlers/reset.write.ts +3 -3
- package/packages/bundled-features/src/config/handlers/selected-extensions.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +8 -8
- package/packages/bundled-features/src/crypto-shredding/resolve-file-bytes-deleter.ts +3 -3
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/index.ts +4 -0
- package/packages/bundled-features/src/data-retention/feature.ts +1 -1
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +12 -2
- package/packages/bundled-features/src/delivery/attempt-log.ts +1 -1
- package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
- package/packages/bundled-features/src/delivery/feature.ts +2 -2
- package/packages/bundled-features/src/delivery/jobs.ts +11 -11
- package/packages/bundled-features/src/delivery/testing.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +5 -1
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +7 -5
- package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -5
- package/packages/bundled-features/src/file-provider-s3/feature.ts +4 -1
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -8
- package/packages/bundled-features/src/form-draft/feature.ts +1 -1
- package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +22 -12
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +3 -3
- package/packages/bundled-features/src/form-draft/handlers/get.query.ts +1 -1
- package/packages/bundled-features/src/foundation-shared/config-helpers.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/changes.json +6 -0
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
- package/packages/bundled-features/src/jobs/changes.json +6 -0
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/consumer.write.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +11 -5
- package/packages/bundled-features/src/jobs/handlers/retention-cleanup.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +2 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +139 -90
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
- package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/mail-transport-smtp/changes.json +6 -0
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +4 -1
- package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/rate-limiting/handlers/status.query.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +4 -1
- package/packages/bundled-features/src/renderer-foundation/api.ts +3 -3
- package/packages/bundled-features/src/secrets/announce-secret-change.ts +4 -4
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +4 -3
- package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +13 -8
- package/packages/bundled-features/src/secrets/handlers/set.write.ts +4 -3
- package/packages/bundled-features/src/secrets/secrets-context.ts +16 -6
- package/packages/bundled-features/src/secrets/write-gate.ts +1 -1
- package/packages/bundled-features/src/seo/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/changes.json +12 -0
- package/packages/bundled-features/src/sessions/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/cleanup.job.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/detail.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/mine.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +3 -3
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/screens.ts +8 -4
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +1 -1
- package/packages/bundled-features/src/sessions/web/session-device-cell.tsx +10 -12
- package/packages/bundled-features/src/sessions/web/user-agent.ts +12 -0
- package/packages/bundled-features/src/shared/index.ts +15 -0
- package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +12 -2
- package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
- package/packages/bundled-features/src/shared/stream-tenant-backfill.ts +201 -0
- package/packages/bundled-features/src/shared/stream-tenant-derived-state.ts +107 -0
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +1 -1
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +7 -7
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +5 -3
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +7 -7
- package/packages/bundled-features/src/template-resolver/api.ts +3 -3
- package/packages/bundled-features/src/template-resolver/changes.json +18 -0
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +5 -4
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +6 -2
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +4 -2
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/seeding.ts +12 -2
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +18 -13
- package/packages/bundled-features/src/template-resolver/template-resources.ts +4 -4
- package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
- package/packages/bundled-features/src/template-resolver/web/document-insert-fields.tsx +222 -0
- package/packages/bundled-features/src/template-resolver/web/document-insert-model.ts +164 -0
- package/packages/bundled-features/src/template-resolver/web/document-insert-panel.tsx +115 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-bubble.tsx +177 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-content.tsx +38 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-context.ts +119 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +51 -1098
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-helpers.ts +161 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-layout.tsx +83 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx +153 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar.tsx +182 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +18 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
- package/packages/bundled-features/src/template-resolver/web/use-document-tiptap-model.ts +138 -0
- package/packages/bundled-features/src/tenant/changes.json +19 -0
- package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -2
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -1
- package/packages/bundled-features/src/tenant/index.ts +5 -0
- package/packages/bundled-features/src/tenant/invited-membership.ts +6 -1
- package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -1
- package/packages/bundled-features/src/tenant/seeding.ts +65 -62
- package/packages/bundled-features/src/tenant/stream-tenant-backfill.ts +115 -0
- package/packages/bundled-features/src/tenant/testing.ts +77 -0
- package/packages/bundled-features/src/tenant-handover/events.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +5 -5
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +22 -10
- package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +22 -11
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -12
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +7 -3
- package/packages/bundled-features/src/tenant-lifecycle/testing.ts +9 -8
- package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
- package/packages/bundled-features/src/tier-engine/feature.ts +8 -3
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +3 -3
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +7 -1
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +3 -3
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +71 -97
- package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +2 -2
- package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -2
- package/packages/bundled-features/src/user/index.ts +2 -0
- package/packages/bundled-features/src/user/invited-global-roles.ts +6 -1
- package/packages/bundled-features/src/user/principal-status.ts +1 -1
- package/packages/bundled-features/src/user/seeding.ts +24 -6
- package/packages/bundled-features/src/user/testing.ts +22 -0
- package/packages/bundled-features/src/user-data-rights/COMPLIANCE.md +1 -1
- package/packages/bundled-features/src/user-data-rights/README.md +1 -1
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +12 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +24 -0
- package/packages/bundled-features/src/user-data-rights/feature.ts +29 -24
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +12 -6
- package/packages/bundled-features/src/user-data-rights/index.ts +2 -0
- package/packages/bundled-features/src/user-data-rights/lib/mail-transport-resolver.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/storage-provider-resolver.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +6 -1
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +8 -2
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +14 -3
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +11 -1
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +10 -0
- package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -1
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +2 -2
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +9 -4
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +6 -1
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
- package/samples/apps/cap-billing-demo/package.json +1 -1
- package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
- package/samples/apps/config-demo/src/app/server.ts +1 -1
- package/samples/apps/styleguide/src/app/server.ts +2 -2
- package/samples/apps/ui-walkthrough/package.json +1 -1
- package/samples/apps/use-all-bundled/feature-manifest.json +1 -1
- package/samples/apps/use-all-bundled/package.json +1 -1
- package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
- package/samples/apps/user-data-rights-demo/README.md +1 -1
- package/samples/apps/user-data-rights-demo/package.json +1 -1
- package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
- package/samples/recipes/api-worker-split/README.md +2 -1
- package/samples/recipes/api-worker-split/src/feature.ts +2 -1
- package/samples/recipes/cross-feature-events/README.md +5 -5
- package/samples/recipes/cross-feature-events/package.json +1 -1
- package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
- package/samples/recipes/event-sourcing/README.md +5 -5
- package/samples/recipes/event-sourcing/package.json +1 -1
- package/samples/recipes/event-sourcing/src/feature.ts +15 -15
- package/samples/recipes/files-post-processing/README.md +1 -1
- package/samples/recipes/files-post-processing/package.json +1 -1
- package/samples/recipes/files-post-processing/src/feature.ts +6 -4
- package/samples/recipes/lane-routing/package.json +1 -1
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/src/feature.ts +1 -1
- package/samples/recipes/managed-config/README.md +3 -3
- package/samples/recipes/search/README.md +1 -1
- package/samples/recipes/search/src/feature.ts +3 -3
- package/samples/recipes/secrets-demo/README.md +1 -1
- package/samples/recipes/secrets-demo/src/feature.ts +5 -2
- package/samples/recipes/state-machine/src/events.ts +1 -1
- package/samples/recipes/state-machine/src/reducer.ts +1 -1
- package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
- package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
|
@@ -1,32 +1,20 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// against the empty SYSTEM stream. The raw UPDATE breaks as soon as an
|
|
7
|
-
// aggregate has BOTH a legacy stream and newer SYSTEM events
|
|
8
|
-
// (split stream — e.g. a skipOptimisticLock
|
|
9
|
-
// lifecycle write appended v1 on SYSTEM while v1..n live on the old
|
|
10
|
-
// tenant): retenanting then trips events_aggregate_version_uq.
|
|
11
|
-
//
|
|
12
|
-
// This tool merges per aggregate: all user events, ordered by global event
|
|
13
|
-
// id, are renumbered 1..n and retenanted to SYSTEM_TENANT_ID — two-phase
|
|
14
|
-
// version writes (negative interim values) dodge the unique index while the
|
|
15
|
-
// per-aggregate transaction is in flight. Snapshots of migrated aggregates
|
|
16
|
-
// are dropped (version numbering changed; the next snapshotting load
|
|
17
|
-
// recreates them); archived-stream markers move with the stream.
|
|
18
|
-
//
|
|
19
|
-
// Idempotent: the candidate query only matches aggregates that still have
|
|
20
|
-
// non-SYSTEM events. One failing aggregate does not abort the run — the
|
|
21
|
-
// rest of the estate migrates, failures are reported.
|
|
22
|
-
//
|
|
23
|
-
// After a run, rebuild the user projection so read_users.tenant_id reflects
|
|
24
|
-
// the stream move: rebuildProjection("user:projection:user-entity", ...) or
|
|
25
|
-
// the jobs:job:projection-rebuild job.
|
|
1
|
+
// Moves legacy user streams onto SYSTEM_TENANT_ID (user is a systemStream
|
|
2
|
+
// entity). After a run, rebuild the user projection so read_users.tenant_id
|
|
3
|
+
// reflects the move: rebuildProjection("user:projection:user-entity", ...) or
|
|
4
|
+
// the jobs:job:projection-rebuild job. `backfillUserStreamTenantsWithDerivedState`
|
|
5
|
+
// does the move, the old-bucket search cleanup, the rebuild and the reindex in one call.
|
|
26
6
|
|
|
27
7
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
8
|
+
import type { Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
9
|
+
import type { SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
10
|
+
import {
|
|
11
|
+
backfillAggregateStreamTenants,
|
|
12
|
+
createSearchBucketCleanup,
|
|
13
|
+
type DerivedStateMode,
|
|
14
|
+
rebuildAndReindex,
|
|
15
|
+
type StreamBackfillStepFailure,
|
|
16
|
+
stepFailures,
|
|
17
|
+
} from "../../../shared/index.ts";
|
|
30
18
|
|
|
31
19
|
export type UserStreamBackfillResult = {
|
|
32
20
|
readonly aggregatesMigrated: number;
|
|
@@ -34,80 +22,66 @@ export type UserStreamBackfillResult = {
|
|
|
34
22
|
readonly failed: ReadonlyArray<{ readonly aggregateId: string; readonly error: string }>;
|
|
35
23
|
};
|
|
36
24
|
|
|
37
|
-
|
|
25
|
+
const USER_AGGREGATE = "user";
|
|
26
|
+
const USER_PROJECTION = "user:projection:user-entity";
|
|
27
|
+
|
|
28
|
+
export function backfillUserStreamTenants(
|
|
38
29
|
db: DbConnection,
|
|
30
|
+
options: { readonly scanTenantIds?: readonly string[] } = {},
|
|
39
31
|
): Promise<UserStreamBackfillResult> {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
let aggregatesMigrated = 0;
|
|
48
|
-
let eventsMigrated = 0;
|
|
49
|
-
const failed: Array<{ aggregateId: string; error: string }> = [];
|
|
50
|
-
|
|
51
|
-
for (const { aggregate_id } of candidates) {
|
|
52
|
-
try {
|
|
53
|
-
eventsMigrated += await migrateAggregate(db, aggregate_id);
|
|
54
|
-
aggregatesMigrated++;
|
|
55
|
-
} catch (e) {
|
|
56
|
-
failed.push({
|
|
57
|
-
aggregateId: aggregate_id,
|
|
58
|
-
error: e instanceof Error ? e.message : String(e),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return { aggregatesMigrated, eventsMigrated, failed };
|
|
32
|
+
return backfillAggregateStreamTenants(db, {
|
|
33
|
+
aggregateType: USER_AGGREGATE,
|
|
34
|
+
target: { kind: "system" },
|
|
35
|
+
...options,
|
|
36
|
+
});
|
|
63
37
|
}
|
|
64
38
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
`SELECT "id" FROM "kumiko_events"
|
|
72
|
-
WHERE "aggregate_type" = 'user' AND "aggregate_id" = $1::uuid
|
|
73
|
-
ORDER BY "id" ASC
|
|
74
|
-
FOR UPDATE`,
|
|
75
|
-
[aggregateId],
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
// Negative interim versions — real versions are always >= 1, so
|
|
79
|
-
// nothing can collide with events_aggregate_version_uq mid-migration.
|
|
80
|
-
for (const [i, ev] of events.entries()) {
|
|
81
|
-
await raw.unsafe(
|
|
82
|
-
`UPDATE "kumiko_events" SET "tenant_id" = $1::uuid, "version" = $2 WHERE "id" = $3`,
|
|
83
|
-
[SYSTEM_TENANT_ID, -(i + 1), ev.id],
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
// Final contiguous 1..n.
|
|
87
|
-
for (const [i, ev] of events.entries()) {
|
|
88
|
-
await raw.unsafe(`UPDATE "kumiko_events" SET "version" = $1 WHERE "id" = $2`, [i + 1, ev.id]);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Version numbering changed → any snapshot of this aggregate is stale.
|
|
92
|
-
await raw.unsafe(`DELETE FROM "kumiko_snapshots" WHERE "aggregate_id" = $1::uuid`, [
|
|
93
|
-
aggregateId,
|
|
94
|
-
]);
|
|
95
|
-
|
|
96
|
-
// Archived markers key on (tenant_id, aggregate_id) — move them with the
|
|
97
|
-
// stream, keeping an existing SYSTEM marker if both exist.
|
|
98
|
-
await raw.unsafe(
|
|
99
|
-
`INSERT INTO "kumiko_archived_streams" ("tenant_id", "aggregate_id", "aggregate_type", "archived_at", "archived_by", "reason")
|
|
100
|
-
SELECT $1::uuid, "aggregate_id", "aggregate_type", "archived_at", "archived_by", "reason"
|
|
101
|
-
FROM "kumiko_archived_streams"
|
|
102
|
-
WHERE "aggregate_id" = $2::uuid AND "tenant_id" <> $1::uuid
|
|
103
|
-
ON CONFLICT DO NOTHING`,
|
|
104
|
-
[SYSTEM_TENANT_ID, aggregateId],
|
|
105
|
-
);
|
|
106
|
-
await raw.unsafe(
|
|
107
|
-
`DELETE FROM "kumiko_archived_streams" WHERE "aggregate_id" = $1::uuid AND "tenant_id" <> $2::uuid`,
|
|
108
|
-
[aggregateId, SYSTEM_TENANT_ID],
|
|
109
|
-
);
|
|
39
|
+
export type UserStreamBackfillWithDerivedStateResult = {
|
|
40
|
+
readonly streams: UserStreamBackfillResult;
|
|
41
|
+
readonly projectionsRebuilt: readonly string[];
|
|
42
|
+
readonly reindexedUsers: number;
|
|
43
|
+
readonly failures: readonly StreamBackfillStepFailure[];
|
|
44
|
+
};
|
|
110
45
|
|
|
111
|
-
|
|
46
|
+
export async function backfillUserStreamTenantsWithDerivedState(deps: {
|
|
47
|
+
readonly db: DbConnection;
|
|
48
|
+
readonly registry: Registry;
|
|
49
|
+
readonly searchAdapter?: SearchAdapter;
|
|
50
|
+
readonly derived?: DerivedStateMode;
|
|
51
|
+
readonly scanTenantIds?: readonly string[];
|
|
52
|
+
readonly onFirstCandidate?: () => Promise<"proceed" | "skip">;
|
|
53
|
+
}): Promise<UserStreamBackfillWithDerivedStateResult> {
|
|
54
|
+
const {
|
|
55
|
+
db,
|
|
56
|
+
registry,
|
|
57
|
+
searchAdapter,
|
|
58
|
+
derived = "if-moved",
|
|
59
|
+
scanTenantIds,
|
|
60
|
+
onFirstCandidate,
|
|
61
|
+
} = deps;
|
|
62
|
+
const searchCleanup = createSearchBucketCleanup(searchAdapter, USER_AGGREGATE);
|
|
63
|
+
const streams = await backfillAggregateStreamTenants(db, {
|
|
64
|
+
aggregateType: USER_AGGREGATE,
|
|
65
|
+
target: { kind: "system" },
|
|
66
|
+
beforeMove: searchCleanup.beforeMove,
|
|
67
|
+
...(scanTenantIds && { scanTenantIds }),
|
|
68
|
+
...(onFirstCandidate && { onFirstCandidate }),
|
|
69
|
+
});
|
|
70
|
+
const failures = stepFailures("user-streams", streams.failed);
|
|
71
|
+
if (streams.aggregatesMigrated === 0 && derived === "if-moved") {
|
|
72
|
+
return { streams, projectionsRebuilt: [], reindexedUsers: 0, failures };
|
|
73
|
+
}
|
|
74
|
+
const rebuilt = await rebuildAndReindex({
|
|
75
|
+
db,
|
|
76
|
+
registry,
|
|
77
|
+
...(searchAdapter && { searchAdapter }),
|
|
78
|
+
projections: [USER_PROJECTION],
|
|
79
|
+
searchEntity: USER_AGGREGATE,
|
|
112
80
|
});
|
|
81
|
+
return {
|
|
82
|
+
streams,
|
|
83
|
+
projectionsRebuilt: rebuilt.projectionsRebuilt,
|
|
84
|
+
reindexedUsers: rebuilt.reindexedRows,
|
|
85
|
+
failures: [...failures, ...rebuilt.failures],
|
|
86
|
+
};
|
|
113
87
|
}
|
|
@@ -6,14 +6,14 @@ import { userTable } from "../schema/user.ts";
|
|
|
6
6
|
|
|
7
7
|
// Privileged auth lookup: returns the full user row — including passwordHash —
|
|
8
8
|
// by email OR id (exactly one, enforced by the schema). Used by the auth
|
|
9
|
-
// features via ctx.queryAs(systemUser, ...).
|
|
9
|
+
// features via ctx.dispatch.queryAs(systemUser, ...).
|
|
10
10
|
//
|
|
11
11
|
// Access is system-only, NOT access.privileged: every query handler is
|
|
12
12
|
// reachable via POST /api/query, and "SystemAdmin" is an assignable app
|
|
13
13
|
// role — privileged would hand any SystemAdmin the raw passwordHash over
|
|
14
14
|
// HTTP. The "system" role can't be minted into a session (only
|
|
15
15
|
// createSystemUser() carries it), so system-only keeps this handler
|
|
16
|
-
// internal to ctx.queryAs(systemUser, ...) callers.
|
|
16
|
+
// internal to ctx.dispatch.queryAs(systemUser, ...) callers.
|
|
17
17
|
export const findForAuthQuery = defineQueryHandler({
|
|
18
18
|
name: "user:find-for-auth",
|
|
19
19
|
schema: z
|
|
@@ -164,7 +164,7 @@ export async function applyUserRolesUpdate(
|
|
|
164
164
|
|
|
165
165
|
const revoker = ctx.registry.getWriteHandler(REVOKE_ALL_SESSIONS_QN);
|
|
166
166
|
if (revoker) {
|
|
167
|
-
const revokeResult = await ctx.writeAs(
|
|
167
|
+
const revokeResult = await ctx.dispatch.writeAs(
|
|
168
168
|
createSystemUser(event.user.tenantId ?? SYSTEM_TENANT_ID),
|
|
169
169
|
REVOKE_ALL_SESSIONS_QN,
|
|
170
170
|
{
|
|
@@ -176,7 +176,7 @@ export async function applyUserRolesUpdate(
|
|
|
176
176
|
// skip: sessions feature not mounted — no live JWTs to revoke; role update
|
|
177
177
|
// still commits. Apps that mount user without sessions accept stale claims
|
|
178
178
|
// until JWT TTL.
|
|
179
|
-
ctx.log?.warn(
|
|
179
|
+
ctx.obs.log?.warn(
|
|
180
180
|
"user:update-roles: sessions revoke-all handler missing; role change commits without session invalidation",
|
|
181
181
|
{
|
|
182
182
|
userId: event.payload.id,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export { UserCommandSchemas } from "./command-schemas.ts";
|
|
2
2
|
export {
|
|
3
3
|
backfillUserStreamTenants,
|
|
4
|
+
backfillUserStreamTenantsWithDerivedState,
|
|
4
5
|
type UserStreamBackfillResult,
|
|
6
|
+
type UserStreamBackfillWithDerivedStateResult,
|
|
5
7
|
} from "./db/queries/stream-tenant-backfill.ts";
|
|
6
8
|
export { createUserFeature } from "./feature.ts";
|
|
7
9
|
export {
|
|
@@ -45,7 +45,12 @@ export async function grantInvitedGlobalRoles(
|
|
|
45
45
|
const updateResult = await userExecutor.update(
|
|
46
46
|
{ id: existing.id, version: existing.version, changes: { roles } },
|
|
47
47
|
createSystemUser(SYSTEM_TENANT_ID),
|
|
48
|
-
createTenantDb({
|
|
48
|
+
createTenantDb({
|
|
49
|
+
db,
|
|
50
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
51
|
+
mode: "system",
|
|
52
|
+
reason: "global roles live on the tenant-agnostic user row in the system tenant",
|
|
53
|
+
}),
|
|
49
54
|
);
|
|
50
55
|
if (!updateResult.isSuccess) return updateResult;
|
|
51
56
|
return grantedGlobalRoles(roles);
|
|
@@ -39,7 +39,7 @@ export const principalStatusPlugin: PrincipalStatusPlugin = {
|
|
|
39
39
|
return isPrincipalRowBlocked(row) ? "blocked" : "active";
|
|
40
40
|
},
|
|
41
41
|
|
|
42
|
-
// ctx.queryAsMember's global-roles/timezone/locale source — same fields
|
|
42
|
+
// ctx.dispatch.queryAsMember's global-roles/timezone/locale source — same fields
|
|
43
43
|
// login mints a session from (see login.write.ts gateBuildSession).
|
|
44
44
|
async resolveProfile(userId, { db }): Promise<PrincipalProfile | null> {
|
|
45
45
|
const row = await fetchOne<{ roles: unknown; timezone: string | null; locale: string | null }>(
|
|
@@ -17,8 +17,11 @@ import {
|
|
|
17
17
|
type DbConnection,
|
|
18
18
|
fetchOne,
|
|
19
19
|
} from "@cosmicdrift/kumiko-framework/db";
|
|
20
|
-
import
|
|
21
|
-
|
|
20
|
+
import {
|
|
21
|
+
createSystemUser,
|
|
22
|
+
type SessionUser,
|
|
23
|
+
SYSTEM_TENANT_ID,
|
|
24
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
22
25
|
// kumiko-lint-ignore cross-feature-import shares the postSave-hooks fixture contract with tenant/seeding
|
|
23
26
|
import { fireEntityPostSave, type SeedTenantHooks } from "../tenant/seeding.ts";
|
|
24
27
|
import { userEntity, userTable } from "./schema/user.ts";
|
|
@@ -61,11 +64,16 @@ export async function seedUser(
|
|
|
61
64
|
// that opt in. Omit to keep today's hook-less behavior.
|
|
62
65
|
hooks?: SeedTenantHooks,
|
|
63
66
|
): Promise<{ id: string }> {
|
|
64
|
-
const by = options.by ??
|
|
67
|
+
const by = options.by ?? createSystemUser(SYSTEM_TENANT_ID);
|
|
65
68
|
// executor.create expects a TenantDb (with an .insert() API). Although
|
|
66
69
|
// User is tenant-agnostic (it has no tenant_id column), the runtime
|
|
67
70
|
// interface requires this wrapper.
|
|
68
|
-
const tdb = createTenantDb({
|
|
71
|
+
const tdb = createTenantDb({
|
|
72
|
+
db,
|
|
73
|
+
tenantId: by.tenantId,
|
|
74
|
+
mode: "system",
|
|
75
|
+
reason: "User is tenant-agnostic, seeding writes it through a system-mode handle",
|
|
76
|
+
});
|
|
69
77
|
|
|
70
78
|
const existing = await fetchOne(db, userTable, { email: options.email, isDeleted: false });
|
|
71
79
|
// @cast-boundary db-row: users.id is a uuid column (string), and fetchOne
|
|
@@ -111,7 +119,12 @@ export async function reconcileSeededUserEmailVerified(
|
|
|
111
119
|
// @cast-boundary db-row: users.id is a uuid column (string), and fetchOne
|
|
112
120
|
// returns the projection row as Record<string, unknown>.
|
|
113
121
|
const id = existingRow["id"] as string;
|
|
114
|
-
const tdb = createTenantDb({
|
|
122
|
+
const tdb = createTenantDb({
|
|
123
|
+
db,
|
|
124
|
+
tenantId: by.tenantId,
|
|
125
|
+
mode: "system",
|
|
126
|
+
reason: "User is tenant-agnostic, seeding writes it through a system-mode handle",
|
|
127
|
+
});
|
|
115
128
|
const result = await userExecutor.update(
|
|
116
129
|
{ id, version: existingRow["version"] as number, changes: { emailVerified: true } },
|
|
117
130
|
by,
|
|
@@ -157,7 +170,12 @@ export async function rewriteSeededUserEmail(
|
|
|
157
170
|
// skip: the stored blind index already matches, a rewrite would only append a no-op event.
|
|
158
171
|
if (indexed) return;
|
|
159
172
|
}
|
|
160
|
-
const tdb = createTenantDb({
|
|
173
|
+
const tdb = createTenantDb({
|
|
174
|
+
db,
|
|
175
|
+
tenantId: by.tenantId,
|
|
176
|
+
mode: "system",
|
|
177
|
+
reason: "User is tenant-agnostic, seeding writes it through a system-mode handle",
|
|
178
|
+
});
|
|
161
179
|
const result = await userExecutor.update({ id, version, changes: { email } }, by, tdb);
|
|
162
180
|
// version_conflict: a concurrent write already changed the row — fine for a seed helper.
|
|
163
181
|
// unique_violation: a duplicate row already holds the blind index; the repair
|
|
@@ -2,4 +2,26 @@
|
|
|
2
2
|
// reason: helpers are shared between tests and Dev-Server bootstrap,
|
|
3
3
|
// and /seeding is the stable home.
|
|
4
4
|
|
|
5
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
6
|
+
import {
|
|
7
|
+
deleteRowViaEventStore,
|
|
8
|
+
updateRowViaEventStore,
|
|
9
|
+
} from "@cosmicdrift/kumiko-testing/helpers";
|
|
10
|
+
import { userEntity, userTable } from "./schema/user.ts";
|
|
11
|
+
|
|
5
12
|
export * from "./seeding.ts";
|
|
13
|
+
|
|
14
|
+
const userTarget = { table: userTable, entity: userEntity, entityName: "user" } as const;
|
|
15
|
+
|
|
16
|
+
// The user stream is systemStream, so the default system-tenant actor addresses it.
|
|
17
|
+
export function updateUserViaEventStore(
|
|
18
|
+
db: DbConnection,
|
|
19
|
+
userId: string,
|
|
20
|
+
changes: Readonly<Record<string, unknown>>,
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
return updateRowViaEventStore(db, userTarget, { id: userId }, changes);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function softDeleteUserViaEventStore(db: DbConnection, userId: string): Promise<void> {
|
|
26
|
+
return deleteRowViaEventStore(db, userTarget, { id: userId });
|
|
27
|
+
}
|
|
@@ -18,7 +18,7 @@ Dieses Dokument bündelt die **technischen Fakten** zur DSGVO-Pipeline
|
|
|
18
18
|
|
|
19
19
|
| Artikel | Endpoint / Runner | Wer ruft auf | Was passiert |
|
|
20
20
|
|---------|-------------------|--------------|--------------|
|
|
21
|
-
| **Art. 15 (Auskunft, light)** | `user-data-rights:query:my-audit-log` | User | Liest eigene Framework-Events aus `kumiko_events` (account-weit über alle Memberships). Filterbar nach `eventType`, `aggregateType`, `from`/`to`. Domain-Entities ohne `ctx.
|
|
21
|
+
| **Art. 15 (Auskunft, light)** | `user-data-rights:query:my-audit-log` | User | Liest eigene Framework-Events aus `kumiko_events` (account-weit über alle Memberships). Filterbar nach `eventType`, `aggregateType`, `from`/`to`. Domain-Entities ohne `ctx.events.append` erscheinen NICHT — die kommen im Export-Bundle (Art. 20). |
|
|
22
22
|
| **Art. 15 + 20 (Auskunft + Portabilität)** | `user-data-rights:write:request-export` | User | Async Job → ZIP mit user-Profil + fileRefs + alle EXT_USER_DATA-Provider-Daten (cross-tenant) + signed Magic-Link per Email. Idempotent: nur 1 active Job pro User (`ACTIVE_JOB_CONSTRAINT`). |
|
|
23
23
|
| **Art. 15 + 20** | `GET /user-export/by-token` (interstitial) + `POST /user-export/by-token` (exchange) | Anonym (Magic-Link-Pfad) | Email-Link trägt den Token als URL-Fragment (`#token=`, nie an den Server gesendet, issue #1271); Interstitial-Script liest das Fragment client-seitig und tauscht es per POST-Body gegen die signed Storage-URL. Kein `?token=`-Query-Fallback (#1512) — Query-Param landet unveränderlich in Proxy-/Access-Logs. Multi-use innerhalb TTL. Audit: `lastUsedAt`, `lastUsedFromIp`, `lastUsedUserAgent`. |
|
|
24
24
|
| **Art. 15 + 20** | `GET /user-export/by-job/:jobId` | User (Session-Auth) | UI-Klick-Pfad. Cross-tenant-same-user: User in Tenant B kann Job aus Tenant A laden wenn er der Owner ist. |
|
|
@@ -44,7 +44,7 @@ Hook-Signaturen in `framework/src/engine/extensions/user-data.ts`:
|
|
|
44
44
|
|
|
45
45
|
| Article | Handler | Zweck |
|
|
46
46
|
|---------|---------|-------|
|
|
47
|
-
| Art. 15 | `user-data-rights:query:my-audit-log` | User sieht eigene Framework-Events (account-weit über alle Memberships). Domain-Entities ohne `ctx.
|
|
47
|
+
| Art. 15 | `user-data-rights:query:my-audit-log` | User sieht eigene Framework-Events (account-weit über alle Memberships). Domain-Entities ohne `ctx.events.append` erscheinen NICHT — nur im Export-Bundle. |
|
|
48
48
|
| Art. 15+20 | `user-data-rights:write:request-export` | Async Job → ZIP mit user-Profil + fileRefs + alle EXT_USER_DATA-Provider-Daten + signed Magic-Link |
|
|
49
49
|
| Art. 17 | `user-data-rights:write:request-deletion` | Soft-Delete mit Grace-Period, anschließend Cron anonymisiert User + cleant Domain-Entities |
|
|
50
50
|
| Art. 17 | `user-data-rights:write:cancel-deletion` | User widerruft seinen Forget-Request während der Grace |
|
|
@@ -53,7 +53,12 @@ export interface RecordDownloadUseArgs {
|
|
|
53
53
|
export async function recordDownloadUse(args: RecordDownloadUseArgs): Promise<void> {
|
|
54
54
|
const { db, tokenId, tokenVersion, tokenUseCount, tenantId, now, ip, userAgent } = args;
|
|
55
55
|
const executor = createSystemUser(tenantId);
|
|
56
|
-
const tdb = createTenantDb({
|
|
56
|
+
const tdb = createTenantDb({
|
|
57
|
+
db,
|
|
58
|
+
tenantId,
|
|
59
|
+
mode: "system",
|
|
60
|
+
reason: "download audit writes into the token's tenant, outside a request scope",
|
|
61
|
+
});
|
|
57
62
|
|
|
58
63
|
await tokenCrud
|
|
59
64
|
.update(
|
|
@@ -102,7 +107,12 @@ export interface RecordInvalidAttemptArgs {
|
|
|
102
107
|
export async function recordInvalidAttempt(args: RecordInvalidAttemptArgs): Promise<void> {
|
|
103
108
|
const { db, tenantId, now } = args;
|
|
104
109
|
const executor = createSystemUser(tenantId);
|
|
105
|
-
const tdb = createTenantDb({
|
|
110
|
+
const tdb = createTenantDb({
|
|
111
|
+
db,
|
|
112
|
+
tenantId,
|
|
113
|
+
mode: "system",
|
|
114
|
+
reason: "download audit writes into the token's tenant, outside a request scope",
|
|
115
|
+
});
|
|
106
116
|
await attemptCrud
|
|
107
117
|
.create(
|
|
108
118
|
{
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.390.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "export cleanup stays quiet when metrics are off",
|
|
6
|
+
"detail": "The cleanup gauges are not registered without metrics, so the job logged \"Gauge not registered\" on every pass."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.390.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "User data export and forget cleanup run in system scope",
|
|
12
|
+
"detail": "runUserExport and runForgetCleanup walk every tenant of a user, so they enter the system scope themselves instead of depending on the caller's scope. A direct call outside a job no longer exports or erases nothing under row level security \"on\"."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.390.0",
|
|
16
|
+
"type": "improvement",
|
|
17
|
+
"title": "QN constants for the forget-cleanup job and the download-attempt list screen",
|
|
18
|
+
"detail": "UserDataRightsJobs.runForgetCleanup (\"user-data-rights:job:run-forget-cleanup\") and UserDataRightsScreens.downloadAttemptList (\"user-data-rights:screen:download-attempt-list\") are exported next to the existing QN constants, from the feature barrel and from user-data-rights/web. Use the screen constant in an app's r.nav instead of the literal string."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"version": "0.390.0",
|
|
22
|
+
"type": "improvement",
|
|
23
|
+
"title": "QN constants for the export job and the export job list screen",
|
|
24
|
+
"detail": "UserDataRightsJobs.runExportJobs (\"user-data-rights:job:run-export-jobs\") and UserDataRightsScreens.exportJobList (\"user-data-rights:screen:export-job-list\") are exported next to UserDataRightsQueries and UserDataRightsHandlers, from the feature barrel and from user-data-rights/web. Use the screen constant in an app's r.nav instead of the literal string."
|
|
25
|
+
},
|
|
2
26
|
{
|
|
3
27
|
"version": "0.389.0",
|
|
4
28
|
"type": "improvement",
|
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
13
13
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
14
14
|
import {
|
|
15
|
-
createMetricsHandle,
|
|
16
15
|
createNoopMetricsHandle,
|
|
16
|
+
createSafeMetricsHandle,
|
|
17
17
|
} from "@cosmicdrift/kumiko-framework/observability";
|
|
18
18
|
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
19
19
|
import { createConfigAccessor } from "../config/index.ts";
|
|
@@ -62,6 +62,7 @@ import {
|
|
|
62
62
|
type SendExportReadyEmailFn,
|
|
63
63
|
} from "./run-export-jobs.ts";
|
|
64
64
|
import {
|
|
65
|
+
FORGET_CLEANUP_HANDLER_NAME,
|
|
65
66
|
runForgetCleanup,
|
|
66
67
|
type SendDeletionExecutedEmailFn,
|
|
67
68
|
sanitizeReasonForLog,
|
|
@@ -80,6 +81,7 @@ import {
|
|
|
80
81
|
STATUS_OPTION_KEY_PREFIX,
|
|
81
82
|
USER_ME_QUERY,
|
|
82
83
|
UserDataRightsHandlers,
|
|
84
|
+
UserDataRightsScreens,
|
|
83
85
|
} from "./web/constants.ts";
|
|
84
86
|
import { USER_DATA_RIGHTS_I18N } from "./web/i18n.ts";
|
|
85
87
|
|
|
@@ -334,7 +336,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
334
336
|
id: "export-job-list",
|
|
335
337
|
label: "user-data-rights:nav.exportJobs",
|
|
336
338
|
icon: "download",
|
|
337
|
-
screen:
|
|
339
|
+
screen: UserDataRightsScreens.exportJobList,
|
|
338
340
|
order: 25,
|
|
339
341
|
access: { roles: ["SystemAdmin"] },
|
|
340
342
|
});
|
|
@@ -620,11 +622,12 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
620
622
|
const exportDb =
|
|
621
623
|
ctx.db.unsafeRaw() as import("@cosmicdrift/kumiko-framework/db").DbConnection; // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
622
624
|
const exportRegistry = ctx.registry;
|
|
623
|
-
// JobContext carries ctx.meter (raw), not ctx.metrics — that bound
|
|
625
|
+
// JobContext carries ctx.meter (raw), not ctx.obs.metrics — that bound
|
|
624
626
|
// handle only exists on HandlerContext (built per write/query call
|
|
625
|
-
// in dispatch-shared.ts). Build the
|
|
626
|
-
|
|
627
|
-
|
|
627
|
+
// in dispatch-shared.ts). Build the handle explicitly; the safe variant is a no-op when
|
|
628
|
+
// metrics are off and the gauges were never registered on the meter.
|
|
629
|
+
const exportMetrics = ctx.obs.meter
|
|
630
|
+
? createSafeMetricsHandle(ctx.obs.meter, "user-data-rights")
|
|
628
631
|
: createNoopMetricsHandle();
|
|
629
632
|
|
|
630
633
|
// Without explicit send*Email options, a mounted mail transport sends the
|
|
@@ -634,7 +637,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
634
637
|
// The cron runs across tenants, so each mail needs a notify function
|
|
635
638
|
// bound to the recipient's tenant for template overrides and channel
|
|
636
639
|
// configuration.
|
|
637
|
-
const notifyFactory = ctx._notifyFactory;
|
|
640
|
+
const notifyFactory = ctx.services._notifyFactory;
|
|
638
641
|
const exportNotifyFor =
|
|
639
642
|
notifyFactory !== undefined && isGdprMailDeliveryAvailable(exportRegistry)
|
|
640
643
|
? (tenantId: TenantId) => notifyFactory(createSystemUser(tenantId), tenantId)
|
|
@@ -671,8 +674,8 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
671
674
|
// createFileProviderForTenant throws and every export lands on failed.
|
|
672
675
|
buildStorageProvider: makeTenantStorageProviderResolver({
|
|
673
676
|
registry: exportRegistry,
|
|
674
|
-
configResolver: ctx.configResolver,
|
|
675
|
-
secrets: ctx.secrets,
|
|
677
|
+
configResolver: ctx.services.configResolver,
|
|
678
|
+
secrets: ctx.services.secrets,
|
|
676
679
|
db: exportDb,
|
|
677
680
|
userId: exportUserId,
|
|
678
681
|
handlerName: "user-data-rights:run-export-jobs",
|
|
@@ -686,8 +689,8 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
686
689
|
...(opts.appExportDownloadUrl !== undefined && {
|
|
687
690
|
appExportDownloadUrl: opts.appExportDownloadUrl,
|
|
688
691
|
}),
|
|
689
|
-
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
690
|
-
escapeHatchAuditLog: ctx.log,
|
|
692
|
+
escapeHatchAuditSink: ctx.services._escapeHatchAuditSink,
|
|
693
|
+
escapeHatchAuditLog: ctx.obs.log,
|
|
691
694
|
actor: ctx.systemUser.id,
|
|
692
695
|
metrics: exportMetrics,
|
|
693
696
|
});
|
|
@@ -716,7 +719,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
716
719
|
const forgetRegistry = ctx.registry;
|
|
717
720
|
const tenantModel = await resolveAppTenantModel({
|
|
718
721
|
registry: forgetRegistry,
|
|
719
|
-
configResolver: ctx.configResolver,
|
|
722
|
+
configResolver: ctx.services.configResolver,
|
|
720
723
|
db: forgetDb,
|
|
721
724
|
userId: forgetUserId,
|
|
722
725
|
});
|
|
@@ -724,7 +727,7 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
724
727
|
// Without an app callback the default mail goes out when an email channel or a
|
|
725
728
|
// mail transport, plus a mail renderer, are mounted. The user is already erased
|
|
726
729
|
// here, so it bypasses the delivery log: an attempt row would be new personal data.
|
|
727
|
-
const forgetConfigResolver = ctx.configResolver;
|
|
730
|
+
const forgetConfigResolver = ctx.services.configResolver;
|
|
728
731
|
const sendDeletionExecutedEmail =
|
|
729
732
|
opts.sendDeletionExecutedEmail ??
|
|
730
733
|
((isGdprMailDeliveryAvailable(forgetRegistry) ||
|
|
@@ -734,14 +737,14 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
734
737
|
{
|
|
735
738
|
registry: forgetRegistry,
|
|
736
739
|
db: forgetDb,
|
|
737
|
-
...(ctx.secrets !== undefined && { secrets: ctx.secrets }),
|
|
740
|
+
...(ctx.services.secrets !== undefined && { secrets: ctx.services.secrets }),
|
|
738
741
|
resolveTransport: makeTenantMailTransportResolver({
|
|
739
742
|
registry: forgetRegistry,
|
|
740
743
|
configResolver: forgetConfigResolver,
|
|
741
|
-
secrets: ctx.secrets,
|
|
744
|
+
secrets: ctx.services.secrets,
|
|
742
745
|
db: forgetDb,
|
|
743
746
|
userId: forgetUserId,
|
|
744
|
-
handlerName:
|
|
747
|
+
handlerName: FORGET_CLEANUP_HANDLER_NAME,
|
|
745
748
|
}),
|
|
746
749
|
configFor: (tenantId) =>
|
|
747
750
|
forgetConfigResolver &&
|
|
@@ -762,29 +765,31 @@ export function createUserDataRightsFeature(opts: UserDataRightsOptions = {}): F
|
|
|
762
765
|
registry: forgetRegistry,
|
|
763
766
|
now: T.Now.instant(),
|
|
764
767
|
tenantModel,
|
|
765
|
-
...(ctx.searchAdapter !== undefined && {
|
|
768
|
+
...(ctx.services.searchAdapter !== undefined && {
|
|
769
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
770
|
+
}),
|
|
766
771
|
// Same per-tenant provider resolution as the export cron — forget
|
|
767
772
|
// deletes binaries from the store upload + export use.
|
|
768
773
|
buildStorageProvider: makeTenantStorageProviderResolver({
|
|
769
774
|
registry: forgetRegistry,
|
|
770
|
-
configResolver: ctx.configResolver,
|
|
771
|
-
secrets: ctx.secrets,
|
|
775
|
+
configResolver: ctx.services.configResolver,
|
|
776
|
+
secrets: ctx.services.secrets,
|
|
772
777
|
db: forgetDb,
|
|
773
778
|
userId: forgetUserId,
|
|
774
|
-
handlerName:
|
|
779
|
+
handlerName: FORGET_CLEANUP_HANDLER_NAME,
|
|
775
780
|
}),
|
|
776
781
|
...(sendDeletionExecutedEmail && { sendDeletionExecutedEmail }),
|
|
777
|
-
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
778
|
-
escapeHatchAuditLog: ctx.log,
|
|
782
|
+
escapeHatchAuditSink: ctx.services._escapeHatchAuditSink,
|
|
783
|
+
escapeHatchAuditLog: ctx.obs.log,
|
|
779
784
|
actor: ctx.systemUser.id,
|
|
780
785
|
});
|
|
781
786
|
|
|
782
787
|
// Persist each incomplete report on the job-run log. The write
|
|
783
788
|
// handler returns incomplete[] in its response; the cron path used to
|
|
784
789
|
// discard the array (only a console.warn for rolledBack cases).
|
|
785
|
-
// ctx.log feeds createJobRunLogger → store_job_run_logs.
|
|
790
|
+
// ctx.obs.log feeds createJobRunLogger → store_job_run_logs.
|
|
786
791
|
for (const entry of forgetResult.incomplete) {
|
|
787
|
-
ctx.log?.warn?.(
|
|
792
|
+
ctx.obs.log?.warn?.(
|
|
788
793
|
`[user-data-rights:run-forget-cleanup] incomplete hook ` +
|
|
789
794
|
`userId=${entry.userId} tenantId=${entry.tenantId} ` +
|
|
790
795
|
`entityName=${entry.entityName} rolledBack=${entry.rolledBack} ` +
|
package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts
CHANGED
|
@@ -59,7 +59,7 @@ export function createConfirmDeletionByTokenHandler(opts: ConfirmDeletionByToken
|
|
|
59
59
|
access: { roles: ["anonymous", "Member", "User", "TenantAdmin", "SystemAdmin"] },
|
|
60
60
|
escapeHatch: {
|
|
61
61
|
reason:
|
|
62
|
-
"resolveGracePeriod reads the tenant compliance profile via ctx.queryAs(SYSTEM, " +
|
|
62
|
+
"resolveGracePeriod reads the tenant compliance profile via ctx.dispatch.queryAs(SYSTEM, " +
|
|
63
63
|
"...) to compute the grace period end — the anonymous token holder has no read access " +
|
|
64
64
|
"to that tenant-config projection. startDeletionGracePeriod also appends the user " +
|
|
65
65
|
"lifecycle event on the SYSTEM_TENANT_ID user stream.",
|
|
@@ -29,7 +29,7 @@ export async function resolveGracePeriod(
|
|
|
29
29
|
reason:
|
|
30
30
|
"reads the tenant compliance profile via queryAs(SYSTEM, ...) to compute the grace period end; the calling user's own identity has no read access to that tenant-config projection",
|
|
31
31
|
});
|
|
32
|
-
const profile = (await ctx.queryAs(
|
|
32
|
+
const profile = (await ctx.dispatch.queryAs(
|
|
33
33
|
createSystemUser(tenantId),
|
|
34
34
|
"compliance-profiles:query:for-tenant",
|
|
35
35
|
{},
|
|
@@ -149,8 +149,8 @@ export const downloadByJobQuery = defineQueryHandler({
|
|
|
149
149
|
// can differ from query.user.tenantId).
|
|
150
150
|
const provider = await makeTenantStorageProviderResolver({
|
|
151
151
|
registry: ctx.registry,
|
|
152
|
-
configResolver: ctx.configResolver,
|
|
153
|
-
secrets: ctx.secrets,
|
|
152
|
+
configResolver: ctx.services.configResolver,
|
|
153
|
+
secrets: ctx.services.secrets,
|
|
154
154
|
// @cast-boundary db-runner — helpers use only the query API that
|
|
155
155
|
// DbConnection and DbTx share.
|
|
156
156
|
db: runner as import("@cosmicdrift/kumiko-types/db-connection").DbConnection,
|
|
@@ -201,8 +201,8 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
201
201
|
// has none under resolverTrust: "authoritative").
|
|
202
202
|
const provider = await makeTenantStorageProviderResolver({
|
|
203
203
|
registry: ctx.registry,
|
|
204
|
-
configResolver: ctx.configResolver,
|
|
205
|
-
secrets: ctx.secrets,
|
|
204
|
+
configResolver: ctx.services.configResolver,
|
|
205
|
+
secrets: ctx.services.secrets,
|
|
206
206
|
// @cast-boundary db-runner — helpers use only the query API that
|
|
207
207
|
// DbConnection and DbTx share.
|
|
208
208
|
db: runner as import("@cosmicdrift/kumiko-types/db-connection").DbConnection,
|
|
@@ -50,7 +50,7 @@ export function createRequestDeletionHandler(opts: RequestDeletionOptions = {})
|
|
|
50
50
|
escapeHatch: {
|
|
51
51
|
grants: ["systemIdentity", "unsafeRaw"],
|
|
52
52
|
reason:
|
|
53
|
-
"resolveGracePeriod reads the tenant compliance profile via ctx.queryAs(SYSTEM, " +
|
|
53
|
+
"resolveGracePeriod reads the tenant compliance profile via ctx.dispatch.queryAs(SYSTEM, " +
|
|
54
54
|
"...) to compute the grace period end — the calling user's own identity has no read " +
|
|
55
55
|
"access to that tenant-config projection. startDeletionGracePeriod also appends the " +
|
|
56
56
|
"user lifecycle event on the SYSTEM_TENANT_ID user stream.",
|