@cosmicdrift/kumiko-samples 0.389.0 → 0.390.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/package.json +1 -1
- package/packages/bundled-features/package.json +25 -13
- 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/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/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/use-all-bundled/feature-manifest.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/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
|
@@ -4,4 +4,81 @@
|
|
|
4
4
|
// stable contract here; test-specific options do not belong in this module,
|
|
5
5
|
// because they could break development boots when a lockout-test option is added.
|
|
6
6
|
|
|
7
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
8
|
+
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
9
|
+
import { TestUsers } from "@cosmicdrift/kumiko-framework/seeding";
|
|
10
|
+
import {
|
|
11
|
+
deleteRowViaEventStore,
|
|
12
|
+
updateRowViaEventStore,
|
|
13
|
+
} from "@cosmicdrift/kumiko-testing/helpers";
|
|
14
|
+
import { tenantInvitationEntity, tenantInvitationsTable } from "./invitation-table.ts";
|
|
15
|
+
import { tenantMembershipEntity, tenantMembershipsTable } from "./membership-table.ts";
|
|
16
|
+
import { tenantEntity, tenantTable } from "./schema/tenant.ts";
|
|
17
|
+
|
|
7
18
|
export * from "./seeding.ts";
|
|
19
|
+
|
|
20
|
+
const membershipTarget = {
|
|
21
|
+
table: tenantMembershipsTable,
|
|
22
|
+
entity: tenantMembershipEntity,
|
|
23
|
+
entityName: "tenant-membership",
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
const tenantTarget = { table: tenantTable, entity: tenantEntity, entityName: "tenant" } as const;
|
|
27
|
+
|
|
28
|
+
type MembershipKey = { readonly userId: string; readonly tenantId: TenantId };
|
|
29
|
+
|
|
30
|
+
// The membership stream lives on the membership's own tenant, so actor and stream tenant both use it.
|
|
31
|
+
export function setMembershipRolesViaEventStore(
|
|
32
|
+
db: DbConnection,
|
|
33
|
+
params: MembershipKey & { readonly roles: readonly string[] },
|
|
34
|
+
): Promise<void> {
|
|
35
|
+
const { userId, tenantId, roles } = params;
|
|
36
|
+
return updateRowViaEventStore(
|
|
37
|
+
db,
|
|
38
|
+
membershipTarget,
|
|
39
|
+
{ userId, tenantId },
|
|
40
|
+
{ roles: JSON.stringify(roles) },
|
|
41
|
+
{ tenantId, streamTenantId: tenantId },
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function deleteMembershipViaEventStore(db: DbConnection, key: MembershipKey): Promise<void> {
|
|
46
|
+
return deleteRowViaEventStore(
|
|
47
|
+
db,
|
|
48
|
+
membershipTarget,
|
|
49
|
+
{ userId: key.userId, tenantId: key.tenantId },
|
|
50
|
+
{ tenantId: key.tenantId, streamTenantId: key.tenantId },
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Tenant streams live on the creator's tenant, which is the seed SystemAdmin's for every tenant a test creates.
|
|
55
|
+
export function updateTenantViaEventStore(
|
|
56
|
+
db: DbConnection,
|
|
57
|
+
tenantId: TenantId,
|
|
58
|
+
changes: Readonly<Record<string, unknown>>,
|
|
59
|
+
actorTenantId: TenantId = TestUsers.systemAdmin.tenantId,
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
return updateRowViaEventStore(db, tenantTarget, { id: tenantId }, changes, {
|
|
62
|
+
tenantId: actorTenantId,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Invitation streams live on the inviting tenant.
|
|
67
|
+
export function updateInvitationViaEventStore(
|
|
68
|
+
db: DbConnection,
|
|
69
|
+
email: string,
|
|
70
|
+
changes: Readonly<Record<string, unknown>>,
|
|
71
|
+
tenantId: TenantId,
|
|
72
|
+
): Promise<void> {
|
|
73
|
+
return updateRowViaEventStore(
|
|
74
|
+
db,
|
|
75
|
+
{
|
|
76
|
+
table: tenantInvitationsTable,
|
|
77
|
+
entity: tenantInvitationEntity,
|
|
78
|
+
entityName: "tenant-invitation",
|
|
79
|
+
},
|
|
80
|
+
{ email },
|
|
81
|
+
changes,
|
|
82
|
+
{ tenantId },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -8,7 +8,7 @@ import * as z from "zod";
|
|
|
8
8
|
//
|
|
9
9
|
// _SHORT — r.defineEvent(short, schema) in feature.ts, framework prefixes it
|
|
10
10
|
// to the QN below.
|
|
11
|
-
// _QN — ctx.
|
|
11
|
+
// _QN — ctx.events.unsafeAppend({ type })'s `type` field.
|
|
12
12
|
export const TENANT_HANDOVER_CLAIMED_EVENT_SHORT = "claimed" as const;
|
|
13
13
|
export const TENANT_HANDOVER_CLAIMED_EVENT_QN = "tenant-handover:event:claimed" as const;
|
|
14
14
|
export const TENANT_HANDOVER_CLAIM_AGGREGATE_TYPE = "tenant-handover-claim" as const;
|
|
@@ -92,7 +92,7 @@ export function createClaimTenantHandoverHandler(opts: ClaimTenantHandoverOption
|
|
|
92
92
|
// Own aggregate, decoupled from the transferred root's stream (see
|
|
93
93
|
// events.ts) — a fresh id needs no predecessor version and can't
|
|
94
94
|
// conflict with a concurrent write on the entity it describes.
|
|
95
|
-
await ctx.
|
|
95
|
+
await ctx.events.unsafeAppend({
|
|
96
96
|
aggregateId: generateId(),
|
|
97
97
|
aggregateType: TENANT_HANDOVER_CLAIM_AGGREGATE_TYPE,
|
|
98
98
|
type: TENANT_HANDOVER_CLAIMED_EVENT_QN,
|
|
@@ -150,11 +150,11 @@ export function createTenantLifecycleFeature(): FeatureDefinition {
|
|
|
150
150
|
db: ctx.db.unsafeRaw() as import("@cosmicdrift/kumiko-framework/db").DbConnection, // @cast-boundary db-operator — jobs never run inside a DbTx
|
|
151
151
|
registry: ctx.registry,
|
|
152
152
|
now,
|
|
153
|
-
log: (message) => ctx.log?.warn(message),
|
|
154
|
-
fileProviderResolver: ctx._fileProviderResolver,
|
|
155
|
-
searchAdapter: ctx.searchAdapter,
|
|
156
|
-
escapeHatchAuditSink: ctx._escapeHatchAuditSink,
|
|
157
|
-
escapeHatchAuditLog: ctx.log,
|
|
153
|
+
log: (message) => ctx.obs.log?.warn(message),
|
|
154
|
+
fileProviderResolver: ctx.services._fileProviderResolver,
|
|
155
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
156
|
+
escapeHatchAuditSink: ctx.services._escapeHatchAuditSink,
|
|
157
|
+
escapeHatchAuditLog: ctx.obs.log,
|
|
158
158
|
actor: ctx.systemUser.id,
|
|
159
159
|
});
|
|
160
160
|
},
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createEventStoreExecutor, fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
-
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
+
import { appendDomainEventCore } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
4
5
|
import * as z from "zod";
|
|
5
6
|
import { isWithinGracePeriod } from "../../shared/index.ts";
|
|
6
7
|
import { type TenantLifecycleStatus, tenantEntity, tenantTable } from "../../tenant/index.ts";
|
|
7
|
-
import { DESTRUCTION_CANCELLED_EVENT_QN } from "../constants.ts";
|
|
8
|
+
import { DESTRUCTION_CANCELLED_EVENT_QN, TENANT_LIFECYCLE_FEATURE } from "../constants.ts";
|
|
8
9
|
import { invalidateTenantLifecycleGate } from "../lifecycle-gate.ts";
|
|
9
10
|
|
|
10
11
|
const crud = createEventStoreExecutor(tenantTable, tenantEntity, { entityName: "tenant" });
|
|
@@ -15,7 +16,7 @@ type TenantLifecycleRow = {
|
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
const CANCEL_DESTRUCTION_TENANT_ROW_REASON =
|
|
18
|
-
"reads the caller's tenant row
|
|
19
|
+
"reads the caller's tenant row (a global row on the system tenant) and appends to the tenant stream on the system tenant";
|
|
19
20
|
|
|
20
21
|
export const cancelDestructionWrite = defineWriteHandler({
|
|
21
22
|
name: "cancel-destruction",
|
|
@@ -29,7 +30,8 @@ export const cancelDestructionWrite = defineWriteHandler({
|
|
|
29
30
|
},
|
|
30
31
|
handler: async (event, ctx) => {
|
|
31
32
|
const tenantId = event.user.tenantId;
|
|
32
|
-
const
|
|
33
|
+
const runner = ctx.db.unsafeRaw();
|
|
34
|
+
const row = await fetchOne<TenantLifecycleRow>(runner, tenantTable, {
|
|
33
35
|
id: tenantId,
|
|
34
36
|
});
|
|
35
37
|
if (!row) {
|
|
@@ -64,12 +66,22 @@ export const cancelDestructionWrite = defineWriteHandler({
|
|
|
64
66
|
if (!update.isSuccess) return update;
|
|
65
67
|
invalidateTenantLifecycleGate(tenantId);
|
|
66
68
|
|
|
67
|
-
await
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
await appendDomainEventCore(
|
|
70
|
+
{
|
|
71
|
+
registry: ctx.registry,
|
|
72
|
+
db: runner,
|
|
73
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
74
|
+
userId: String(event.user.id),
|
|
75
|
+
callSiteLabel: "tenant-lifecycle cancel-destruction",
|
|
76
|
+
callerFeature: TENANT_LIFECYCLE_FEATURE,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
aggregateId: tenantId,
|
|
80
|
+
aggregateType: "tenant",
|
|
81
|
+
type: DESTRUCTION_CANCELLED_EVENT_QN,
|
|
82
|
+
payload: { cancelledBy: event.user.id },
|
|
83
|
+
},
|
|
84
|
+
);
|
|
73
85
|
|
|
74
86
|
const gracePeriodEnd: string | null = null;
|
|
75
87
|
return {
|
package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { addDurationSpec } from "@cosmicdrift/kumiko-framework/compliance";
|
|
2
2
|
import { createEventStoreExecutor, fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import { defineWriteHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { defineWriteHandler, SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
+
import { appendDomainEventCore } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
5
6
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
6
7
|
import * as z from "zod";
|
|
7
8
|
import { resolveProfileForTenant } from "../../compliance-profiles/index.ts";
|
|
8
9
|
import { type TenantLifecycleStatus, tenantEntity, tenantTable } from "../../tenant/index.ts";
|
|
9
|
-
import { DESTRUCTION_REQUESTED_EVENT_QN } from "../constants.ts";
|
|
10
|
+
import { DESTRUCTION_REQUESTED_EVENT_QN, TENANT_LIFECYCLE_FEATURE } from "../constants.ts";
|
|
10
11
|
import { revokeTenantSessions } from "../lib/revoke-tenant-sessions.ts";
|
|
11
12
|
import { invalidateTenantLifecycleGate } from "../lifecycle-gate.ts";
|
|
12
13
|
|
|
@@ -18,7 +19,7 @@ type TenantLifecycleRow = {
|
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const REQUEST_DESTRUCTION_TENANT_ROW_REASON =
|
|
21
|
-
"reads the caller's tenant row
|
|
22
|
+
"reads the caller's tenant row (a global row on the system tenant), its compliance profile, appends to the tenant stream on the system tenant and revokes its sessions via DbRunner helpers";
|
|
22
23
|
|
|
23
24
|
export const requestDestructionWrite = defineWriteHandler({
|
|
24
25
|
name: "request-destruction",
|
|
@@ -67,15 +68,25 @@ export const requestDestructionWrite = defineWriteHandler({
|
|
|
67
68
|
if (!update.isSuccess) return update;
|
|
68
69
|
invalidateTenantLifecycleGate(tenantId);
|
|
69
70
|
|
|
70
|
-
await
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
await appendDomainEventCore(
|
|
72
|
+
{
|
|
73
|
+
registry: ctx.registry,
|
|
74
|
+
db: runner,
|
|
75
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
76
|
+
userId: String(event.user.id),
|
|
77
|
+
callSiteLabel: "tenant-lifecycle request-destruction",
|
|
78
|
+
callerFeature: TENANT_LIFECYCLE_FEATURE,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
aggregateId: tenantId,
|
|
82
|
+
aggregateType: "tenant",
|
|
83
|
+
type: DESTRUCTION_REQUESTED_EVENT_QN,
|
|
84
|
+
payload: {
|
|
85
|
+
requestedBy: event.user.id,
|
|
86
|
+
gracePeriodEnd: gracePeriodEnd.toString(),
|
|
87
|
+
},
|
|
77
88
|
},
|
|
78
|
-
|
|
89
|
+
);
|
|
79
90
|
|
|
80
91
|
if (await ctx.hasFeature("sessions")) {
|
|
81
92
|
await revokeTenantSessions(runner, tenantId);
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
createSystemUser,
|
|
11
11
|
type EscapeHatchAuditSink,
|
|
12
12
|
type Registry,
|
|
13
|
+
SYSTEM_TENANT_ID,
|
|
13
14
|
type TenantId,
|
|
14
15
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
15
16
|
import {
|
|
@@ -133,7 +134,7 @@ async function appendTenantStageEvent(
|
|
|
133
134
|
await append(db, {
|
|
134
135
|
aggregateId: tenantId,
|
|
135
136
|
aggregateType: TENANT_AGGREGATE_TYPE,
|
|
136
|
-
tenantId,
|
|
137
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
137
138
|
expectedVersion,
|
|
138
139
|
type,
|
|
139
140
|
payload,
|
|
@@ -153,14 +154,14 @@ async function appendTenantStageEventIdempotent(
|
|
|
153
154
|
alreadyRecorded: boolean,
|
|
154
155
|
): Promise<number> {
|
|
155
156
|
if (alreadyRecorded) {
|
|
156
|
-
const events = await loadAggregate(db, tenantId,
|
|
157
|
+
const events = await loadAggregate(db, tenantId, SYSTEM_TENANT_ID);
|
|
157
158
|
return lastEventVersion(events);
|
|
158
159
|
}
|
|
159
160
|
try {
|
|
160
161
|
await appendTenantStageEvent(db, tenantId, type, payload, expectedVersion);
|
|
161
162
|
} catch (err) {
|
|
162
163
|
if (!(err instanceof VersionConflictError)) throw err;
|
|
163
|
-
const events = await loadAggregate(db, tenantId,
|
|
164
|
+
const events = await loadAggregate(db, tenantId, SYSTEM_TENANT_ID);
|
|
164
165
|
const stage = String(payload["stage"] ?? "");
|
|
165
166
|
if (stageOutcomeRecorded(events, stage, TENANT_DESTRUCTION_STAGE_SUCCEEDED_EVENT_SHORT)) {
|
|
166
167
|
// skip: idempotent retry — stage already recorded before version conflict
|
|
@@ -168,13 +169,18 @@ async function appendTenantStageEventIdempotent(
|
|
|
168
169
|
}
|
|
169
170
|
throw err;
|
|
170
171
|
}
|
|
171
|
-
const events = await loadAggregate(db, tenantId,
|
|
172
|
+
const events = await loadAggregate(db, tenantId, SYSTEM_TENANT_ID);
|
|
172
173
|
return lastEventVersion(events);
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
async function markTenantDestroyFailed(db: DbRunner, tenantId: TenantId): Promise<void> {
|
|
176
177
|
const user = createSystemUser(tenantId);
|
|
177
|
-
const scopedDb = createTenantDb({
|
|
178
|
+
const scopedDb = createTenantDb({
|
|
179
|
+
db,
|
|
180
|
+
tenantId,
|
|
181
|
+
mode: "system",
|
|
182
|
+
reason: "tenant destruction updates the tenant being destroyed, outside its request scope",
|
|
183
|
+
});
|
|
178
184
|
await tenantCrud.update(
|
|
179
185
|
{
|
|
180
186
|
id: tenantId,
|
|
@@ -200,7 +206,7 @@ async function haltPipelineOnAbandon(args: {
|
|
|
200
206
|
// stage's own run() may have appended to this same aggregate (e.g.
|
|
201
207
|
// tombstoneTenantRow's tenantCrud.update) before throwing, so any version
|
|
202
208
|
// captured earlier in the pipeline is stale and would VersionConflict here.
|
|
203
|
-
const eventsBeforeAbandon = await loadAggregate(args.db, args.tenantId,
|
|
209
|
+
const eventsBeforeAbandon = await loadAggregate(args.db, args.tenantId, SYSTEM_TENANT_ID);
|
|
204
210
|
await appendTenantStageEvent(
|
|
205
211
|
args.db,
|
|
206
212
|
args.tenantId,
|
|
@@ -208,7 +214,7 @@ async function haltPipelineOnAbandon(args: {
|
|
|
208
214
|
{ stage: args.stage, attempts: args.attempts, error: args.error },
|
|
209
215
|
lastEventVersion(eventsBeforeAbandon),
|
|
210
216
|
);
|
|
211
|
-
const eventsAfterAbandon = await loadAggregate(args.db, args.tenantId,
|
|
217
|
+
const eventsAfterAbandon = await loadAggregate(args.db, args.tenantId, SYSTEM_TENANT_ID);
|
|
212
218
|
const versionAfterAbandon = lastEventVersion(eventsAfterAbandon);
|
|
213
219
|
await appendTenantStageEvent(
|
|
214
220
|
args.db,
|
|
@@ -229,7 +235,7 @@ async function maybeAppendDestructionCompleted(
|
|
|
229
235
|
// skip: pipeline still has pending stages
|
|
230
236
|
return;
|
|
231
237
|
}
|
|
232
|
-
const events = await loadAggregate(db, tenantId,
|
|
238
|
+
const events = await loadAggregate(db, tenantId, SYSTEM_TENANT_ID);
|
|
233
239
|
if (events.some((event) => event.type === TENANT_DESTRUCTION_COMPLETED_EVENT_QN)) {
|
|
234
240
|
// skip: idempotent — completion event already recorded
|
|
235
241
|
return;
|
|
@@ -293,7 +299,7 @@ export async function runNextDestructionStage(args: {
|
|
|
293
299
|
readonly actor?: string;
|
|
294
300
|
readonly deadlineAt?: number;
|
|
295
301
|
}): Promise<StageTickResult> {
|
|
296
|
-
const events = await loadAggregate(args.db, args.tenantId,
|
|
302
|
+
const events = await loadAggregate(args.db, args.tenantId, SYSTEM_TENANT_ID);
|
|
297
303
|
const { completed, abandoned, attemptsByStage } = replayStageState(events);
|
|
298
304
|
|
|
299
305
|
if (abandoned.size > 0) {
|
|
@@ -339,7 +345,7 @@ export async function runNextDestructionStage(args: {
|
|
|
339
345
|
// tombstoneTenantRow updates the tenant row on the same stream) — reload
|
|
340
346
|
// the version fresh instead of trusting the pre-run `version`, or this
|
|
341
347
|
// append throws VersionConflictError on every stage whose run() writes.
|
|
342
|
-
const eventsAfterRun = await loadAggregate(args.db, args.tenantId,
|
|
348
|
+
const eventsAfterRun = await loadAggregate(args.db, args.tenantId, SYSTEM_TENANT_ID);
|
|
343
349
|
// A tick that neither finishes nor processes anything would otherwise loop
|
|
344
350
|
// forever, because progressed events do not count as attempts.
|
|
345
351
|
if (!outcome.done && outcome.processed === 0) {
|
|
@@ -429,10 +435,15 @@ export async function runTenantDestructionSweep(args: {
|
|
|
429
435
|
let triggered = 0;
|
|
430
436
|
for (const row of due) {
|
|
431
437
|
const tenantId = row.id;
|
|
432
|
-
const events = await loadAggregate(args.db, tenantId,
|
|
438
|
+
const events = await loadAggregate(args.db, tenantId, SYSTEM_TENANT_ID);
|
|
433
439
|
const version = lastEventVersion(events);
|
|
434
440
|
const user = createSystemUser(tenantId);
|
|
435
|
-
const scopedDb = createTenantDb({
|
|
441
|
+
const scopedDb = createTenantDb({
|
|
442
|
+
db: args.db,
|
|
443
|
+
tenantId,
|
|
444
|
+
mode: "system",
|
|
445
|
+
reason: "tenant destruction updates the tenant being destroyed, outside its request scope",
|
|
446
|
+
});
|
|
436
447
|
// Version-guarded (no skipOptimisticLock): a concurrent cancel-destruction
|
|
437
448
|
// between the `due` select above and this write also appends to the same
|
|
438
449
|
// tenant stream, so an unguarded overwrite here would silently revert the
|
|
@@ -57,8 +57,7 @@ export type DestructionStageCtx = {
|
|
|
57
57
|
// "destroyTenant" hooks) — undefined when no file-provider is wired,
|
|
58
58
|
// which those hooks must treat as "nothing to clean up", not an error.
|
|
59
59
|
readonly fileProviderResolver?: FileProviderResolver;
|
|
60
|
-
//
|
|
61
|
-
// index, not the destroyed tenant's, so the subject-keys stage purges them.
|
|
60
|
+
// The tenant record's derived search docs (its name) sit in the SYSTEM bucket; the subject-keys stage purges them.
|
|
62
61
|
readonly searchAdapter?: SearchAdapter;
|
|
63
62
|
// Sourced from the owning job's ctx (_escapeHatchAuditSink,
|
|
64
63
|
// systemUser.id); runTenantDataHooks uses them to attribute+audit any
|
|
@@ -241,7 +240,12 @@ async function purgeTenantCache(ctx: DestructionStageCtx): Promise<StageRunOutco
|
|
|
241
240
|
async function tombstoneTenantRow(ctx: DestructionStageCtx): Promise<StageRunOutcome> {
|
|
242
241
|
const now = getTemporal().Now.instant();
|
|
243
242
|
const user = createSystemUser(ctx.tenantId);
|
|
244
|
-
const db = createTenantDb({
|
|
243
|
+
const db = createTenantDb({
|
|
244
|
+
db: ctx.db,
|
|
245
|
+
tenantId: ctx.tenantId,
|
|
246
|
+
mode: "system",
|
|
247
|
+
reason: "destruction stage tombstones the row of the tenant being destroyed",
|
|
248
|
+
});
|
|
245
249
|
// Per-row forget() through the executor, not a bulk deleteMany: memberships
|
|
246
250
|
// are an ES-managed projection (add/remove/update-roles all go through
|
|
247
251
|
// tenantMembershipCrud), so a store table write here is eventless — a future
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import type
|
|
3
|
+
import { SYSTEM_TENANT_ID, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { append, loadAggregate } from "@cosmicdrift/kumiko-framework/event-store";
|
|
5
5
|
import type { SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
6
6
|
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
7
|
-
import {
|
|
7
|
+
import { updateRowViaEventStore } from "@cosmicdrift/kumiko-testing/helpers";
|
|
8
8
|
import type { TestStack } from "@cosmicdrift/kumiko-testing/stack";
|
|
9
|
-
import { tenantTable } from "../tenant/index.ts";
|
|
9
|
+
import { tenantEntity, tenantTable } from "../tenant/index.ts";
|
|
10
10
|
import { TENANT_AGGREGATE_TYPE, TENANT_DESTRUCTION_STARTED_EVENT_QN } from "./constants.ts";
|
|
11
11
|
import { runTenantDestructionSweep } from "./run-tenant-destroy.ts";
|
|
12
12
|
|
|
@@ -14,17 +14,18 @@ import { runTenantDestructionSweep } from "./run-tenant-destroy.ts";
|
|
|
14
14
|
// features wired) by seeding the same "destroying" state it would produce.
|
|
15
15
|
export async function seedDestroyingTenant(db: DbConnection, tenantId: TenantId): Promise<void> {
|
|
16
16
|
const now = getTemporal().Now.instant();
|
|
17
|
-
await
|
|
17
|
+
await updateRowViaEventStore(
|
|
18
18
|
db,
|
|
19
|
-
tenantTable,
|
|
20
|
-
{ status: "destroying", destroyStartedAt: now },
|
|
19
|
+
{ table: tenantTable, entity: tenantEntity, entityName: "tenant" },
|
|
21
20
|
{ id: tenantId },
|
|
21
|
+
{ status: "destroying", destroyStartedAt: now },
|
|
22
|
+
{ tenantId: SYSTEM_TENANT_ID },
|
|
22
23
|
);
|
|
23
24
|
await append(db, {
|
|
24
25
|
aggregateId: tenantId,
|
|
25
26
|
aggregateType: TENANT_AGGREGATE_TYPE,
|
|
26
|
-
tenantId,
|
|
27
|
-
expectedVersion: (await loadAggregate(db, tenantId,
|
|
27
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
28
|
+
expectedVersion: (await loadAggregate(db, tenantId, SYSTEM_TENANT_ID)).at(-1)?.version ?? 0,
|
|
28
29
|
type: TENANT_DESTRUCTION_STARTED_EVENT_QN,
|
|
29
30
|
payload: { startedAt: now.toString() },
|
|
30
31
|
metadata: { userId: "system", requestId: "test:destruction-started" },
|
|
@@ -21,7 +21,7 @@ import { type TierAssignmentSource, TierAssignmentSources } from "./web/constant
|
|
|
21
21
|
// **Intentionally omitted from the entity**
|
|
22
22
|
// - tenantId: added automatically as a base column.
|
|
23
23
|
// - validFrom/validTo: redundant for event sourcing (events carry timestamps
|
|
24
|
-
// natively; time travel uses `ctx.
|
|
24
|
+
// natively; time travel uses `ctx.events.load({ asOf })`).
|
|
25
25
|
// - addOns: represented by a separate `tier-add-on` entity (1:n relation),
|
|
26
26
|
// allowing event-sourced Add/Remove events with clear audit granularity
|
|
27
27
|
// instead of replacing a JSON array.
|
|
@@ -95,7 +95,7 @@ const tierAssignmentTable = buildEntityTable("tier-assignment", tierAssignmentEn
|
|
|
95
95
|
const log = createFallbackLogger("tier-engine");
|
|
96
96
|
|
|
97
97
|
// Event-store executor for direct writes from the auto-default-tier hook.
|
|
98
|
-
// Like tenant/seeding.ts, the hook receives AppContext (not ctx.write), but it
|
|
98
|
+
// Like tenant/seeding.ts, the hook receives AppContext (not ctx.dispatch.write), but it
|
|
99
99
|
// must write atomically with tenant creation in the same transaction. Call the
|
|
100
100
|
// executor directly rather than going through the dispatcher.
|
|
101
101
|
const tierAssignmentExecutor = createEventStoreExecutor(tierAssignmentTable, tierAssignmentEntity, {
|
|
@@ -406,7 +406,7 @@ export function createTierEngineFeature<
|
|
|
406
406
|
|
|
407
407
|
// Auto-default-tier-on-tenant-signup: the hook fires inTransaction (atomic
|
|
408
408
|
// with the tenant-create rollback) and writes the tier-assignment via the
|
|
409
|
-
// direct executor (not ctx.write — the hook has an AppContext). Same
|
|
409
|
+
// direct executor (not ctx.dispatch.write — the hook has an AppContext). Same
|
|
410
410
|
// pattern as seedTenant in tenant/seeding.ts.
|
|
411
411
|
//
|
|
412
412
|
// **Idempotency:** deterministic aggregate id derived from tenantId. A
|
|
@@ -468,7 +468,12 @@ export function createTierEngineFeature<
|
|
|
468
468
|
tenantId: newTenantId,
|
|
469
469
|
roles: ["SystemAdmin"],
|
|
470
470
|
};
|
|
471
|
-
const tdb = createTenantDb({
|
|
471
|
+
const tdb = createTenantDb({
|
|
472
|
+
db: rawDb,
|
|
473
|
+
tenantId: newTenantId,
|
|
474
|
+
mode: "system",
|
|
475
|
+
reason: "tier assignment for a newly created tenant is written in that tenant",
|
|
476
|
+
});
|
|
472
477
|
|
|
473
478
|
await tierAssignmentExecutor.create(
|
|
474
479
|
{ id: aggregateId, tier: defaultTier, source: TierAssignmentSources.default },
|
|
@@ -3,11 +3,11 @@ import * as z from "zod";
|
|
|
3
3
|
|
|
4
4
|
// get-active-tier — returns the current tier assignment for the calling tenant.
|
|
5
5
|
//
|
|
6
|
-
// Wraps `ctx.queryProjection` for the most common engine query: which tier is
|
|
6
|
+
// Wraps `ctx.dispatch.queryProjection` for the most common engine query: which tier is
|
|
7
7
|
// currently active. Returns null when no tier is assigned; the caller (composeApp)
|
|
8
8
|
// maps null to the default tier.
|
|
9
9
|
//
|
|
10
|
-
// **Tenant scope:** `ctx.queryProjection` automatically filters by tenantId.
|
|
10
|
+
// **Tenant scope:** `ctx.dispatch.queryProjection` automatically filters by tenantId.
|
|
11
11
|
// By convention, each platform tenant has exactly one tier-assignment row.
|
|
12
12
|
export const getActiveTierQuery: QueryHandlerDef = {
|
|
13
13
|
name: "get-active-tier",
|
|
@@ -16,7 +16,7 @@ export const getActiveTierQuery: QueryHandlerDef = {
|
|
|
16
16
|
schema: z.object({}),
|
|
17
17
|
access: { roles: ["TenantAdmin", "SystemAdmin"] },
|
|
18
18
|
handler: async (_query, ctx) => {
|
|
19
|
-
const rows = await ctx.queryProjection<Record<string, unknown>>(
|
|
19
|
+
const rows = await ctx.dispatch.queryProjection<Record<string, unknown>>(
|
|
20
20
|
"tier-engine:projection:tier-assignment-entity",
|
|
21
21
|
);
|
|
22
22
|
return rows[0] ?? null;
|
|
@@ -24,7 +24,13 @@ export const getTenantTierQuery = defineQueryHandler({
|
|
|
24
24
|
},
|
|
25
25
|
handler: async (query, ctx) => {
|
|
26
26
|
const tenantId = query.payload.tenantId as TenantId; // @cast-boundary engine-bridge
|
|
27
|
-
const tdb = createTenantDb({
|
|
27
|
+
const tdb = createTenantDb({
|
|
28
|
+
db: ctx.db.unsafeRaw(),
|
|
29
|
+
tenantId,
|
|
30
|
+
mode: "system",
|
|
31
|
+
reason:
|
|
32
|
+
"tier lookup reads the assignment of the queried tenant, the escape hatch is declared on the handler",
|
|
33
|
+
});
|
|
28
34
|
const row = await fetchOne<TierAssignmentRow>(tdb, tierAssignmentTable, { tenantId });
|
|
29
35
|
return row ?? null;
|
|
30
36
|
},
|
|
@@ -14,7 +14,7 @@ import { type TierAssignmentSource, TierAssignmentSources } from "../web/constan
|
|
|
14
14
|
// billing. This is cross-tenant, so it is restricted to SystemAdmin (not
|
|
15
15
|
// TenantAdmin, which would allow free self-upgrades).
|
|
16
16
|
//
|
|
17
|
-
// Cross-tenant mechanics: ctx.
|
|
17
|
+
// Cross-tenant mechanics: ctx.tenants.writeTarget(tenantId) yields a tenant-mode db bound to the target plus
|
|
18
18
|
// the streamTenantId, so the event lands in the target's stream with the operator as actor.
|
|
19
19
|
//
|
|
20
20
|
// `source` defaults to manual and marks the grant so the billing sync skips it; passing
|
|
@@ -25,7 +25,7 @@ import { type TierAssignmentSource, TierAssignmentSources } from "../web/constan
|
|
|
25
25
|
// `tier-assignment:postSave` entity hook, and a per-handler postSave would not
|
|
26
26
|
// fire either because this handler returns no lifecycle result (kind "save").
|
|
27
27
|
// Without an explicit update the feature gate would stay on the old tier, so
|
|
28
|
-
// `opts.onAssigned(tenantId, tier)` runs after commit (ctx.
|
|
28
|
+
// `opts.onAssigned(tenantId, tier)` runs after commit (ctx.lifecycle.afterCommit).
|
|
29
29
|
// feature.ts uses it to update its cache and notify the other processes; in
|
|
30
30
|
// storage-only mode without a tierMap it is a no-op.
|
|
31
31
|
|
|
@@ -70,7 +70,7 @@ export function createSetTenantTierWrite(opts: SetTenantTierOptions = {}) {
|
|
|
70
70
|
// Without a commit sink (unit harness) there is no transaction to wait for.
|
|
71
71
|
const afterCommit = (tenantId: TenantId, tier: string): void => {
|
|
72
72
|
const notify = async (): Promise<void> => opts.onAssigned?.(tenantId, tier);
|
|
73
|
-
if (ctx.
|
|
73
|
+
if (ctx.lifecycle.afterCommit) ctx.lifecycle.afterCommit(notify);
|
|
74
74
|
else opts.onAssigned?.(tenantId, tier);
|
|
75
75
|
};
|
|
76
76
|
const tenantId = event.payload.tenantId as TenantId; // @cast-boundary engine-bridge
|