@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
|
@@ -40,18 +40,32 @@ import type { EmailMessage } from "@cosmicdrift/kumiko-bundled-features/channel-
|
|
|
40
40
|
import {
|
|
41
41
|
createMailFoundationFeature,
|
|
42
42
|
createTransportForTenant,
|
|
43
|
+
type MailTransportContext,
|
|
43
44
|
} from "@cosmicdrift/kumiko-bundled-features/mail-foundation";
|
|
44
45
|
import {
|
|
45
46
|
access,
|
|
46
47
|
createTenantConfig,
|
|
47
48
|
defineFeature,
|
|
48
|
-
type
|
|
49
|
+
type ReservationContext,
|
|
49
50
|
type WriteHandlerDef,
|
|
50
51
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
51
52
|
import { z } from "zod";
|
|
52
53
|
import { DEMO_TIER_MAP, TIER_NAMES, type TierName } from "./tier-map";
|
|
53
54
|
|
|
54
55
|
const FEATURE_NAME = "newsletter";
|
|
56
|
+
|
|
57
|
+
// The transport factory takes a narrow context (it also runs in the worker), so the handler's
|
|
58
|
+
// secrets live under services and are handed over explicitly.
|
|
59
|
+
function mailContextOf(
|
|
60
|
+
ctx: Pick<ReservationContext, "config" | "registry" | "services" | "user">,
|
|
61
|
+
): MailTransportContext {
|
|
62
|
+
return {
|
|
63
|
+
config: ctx.config,
|
|
64
|
+
registry: ctx.registry,
|
|
65
|
+
secrets: ctx.services.secrets,
|
|
66
|
+
auditUserId: ctx.user.id,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
55
69
|
const NEWSLETTER_CAP = "newsletters-per-month";
|
|
56
70
|
|
|
57
71
|
// =============================================================================
|
|
@@ -71,7 +85,7 @@ const innerSendHandler: WriteHandlerDef = {
|
|
|
71
85
|
handler: async (event, ctx) => {
|
|
72
86
|
const payload = event.payload as z.infer<typeof sendSchema>;
|
|
73
87
|
const transport = await createTransportForTenant(
|
|
74
|
-
ctx,
|
|
88
|
+
mailContextOf(ctx),
|
|
75
89
|
event.user.tenantId,
|
|
76
90
|
"newsletter:write:send",
|
|
77
91
|
);
|
|
@@ -108,7 +122,7 @@ function isValidTierName(value: string): value is TierName {
|
|
|
108
122
|
return (TIER_NAMES as readonly string[]).includes(value);
|
|
109
123
|
}
|
|
110
124
|
|
|
111
|
-
async function resolveTier(ctx:
|
|
125
|
+
async function resolveTier(ctx: ReservationContext): Promise<TierName> {
|
|
112
126
|
const tenantId = ctx.user?.tenantId;
|
|
113
127
|
if (tenantId) {
|
|
114
128
|
const sub = await getSubscriptionForTenant(ctx, tenantId);
|
|
@@ -131,10 +145,10 @@ async function resolveTier(ctx: HandlerContext): Promise<TierName> {
|
|
|
131
145
|
|
|
132
146
|
/** Notifier-Factory: bauen einen SoftHitNotifier der die Warnung über
|
|
133
147
|
* dieselbe mail-foundation an den Tenant-Admin schickt. */
|
|
134
|
-
function buildSoftHitNotifier(ctx:
|
|
148
|
+
function buildSoftHitNotifier(ctx: ReservationContext): SoftHitNotifier {
|
|
135
149
|
return async (info) => {
|
|
136
150
|
const transport = await createTransportForTenant(
|
|
137
|
-
ctx,
|
|
151
|
+
mailContextOf(ctx),
|
|
138
152
|
info.tenantId,
|
|
139
153
|
"newsletter:soft-hit-notifier",
|
|
140
154
|
);
|
|
@@ -26,6 +26,6 @@ await runDevApp({
|
|
|
26
26
|
watchDirs: ["./src", "../../../packages/*/src"],
|
|
27
27
|
extraContext: ({ registry }) => ({
|
|
28
28
|
configResolver: resolver,
|
|
29
|
-
|
|
29
|
+
configAccessorFactory: createConfigAccessorFactory(registry, resolver),
|
|
30
30
|
}),
|
|
31
31
|
});
|
|
@@ -39,7 +39,7 @@ await runDevApp({
|
|
|
39
39
|
aiSettingsFeature,
|
|
40
40
|
workboardFeature,
|
|
41
41
|
],
|
|
42
|
-
// Aktiviert die Upload-Routes (/api/files) + ctx.files für den Avatar-
|
|
42
|
+
// Aktiviert die Upload-Routes (/api/files) + ctx.services.files für den Avatar-
|
|
43
43
|
// Stresstest. In-Memory reicht für die Demo (nicht-persistent).
|
|
44
44
|
files: { storageProvider: createInMemoryFileProvider() },
|
|
45
45
|
port: Number.parseInt(process.env["PORT"] ?? "4180", 10),
|
|
@@ -49,6 +49,6 @@ await runDevApp({
|
|
|
49
49
|
seeds: [seedStyleguideItems, seedAiConnections, seedWorkboardTasks],
|
|
50
50
|
extraContext: ({ registry }) => ({
|
|
51
51
|
configResolver,
|
|
52
|
-
|
|
52
|
+
configAccessorFactory: createConfigAccessorFactory(registry, configResolver),
|
|
53
53
|
}),
|
|
54
54
|
});
|
|
@@ -920,7 +920,7 @@
|
|
|
920
920
|
},
|
|
921
921
|
{
|
|
922
922
|
"name": "file-derivatives",
|
|
923
|
-
"description": "Declares the `derivativeRenderer` extension point. `ctx.derivatives.variant(fileRefId, spec, name)` derives a variant of a tracked FileRef the first time it's requested and reuses the stored result afterwards (derive-on-first-use, keyed by a hash of the spec). Mount at least one `derivatives-*` renderer feature alongside this one — without a registered renderer for the FileRef's MIME type, every `variant(...)` call throws. Also declares the `derivativePublicPredicate` extension point (`r.useExtension(EXT_DERIVATIVE_PUBLIC_PREDICATE, '<entityType>', { isPublic })`) and, when `createFileDerivativesFeature({resolveApexTenant})` is passed a host-resolver, mounts an anonymous `GET {basePath}/:fileRefId/:variant` route that serves any variant name the FileRef's field declared in its `variants` for a FileRef whose entityType has a registered predicate returning true — default-deny (404) otherwise, same as an unknown FileRef or an undeclared variant name. The route's only rate-limit (`per: \"ip\"`) depends on the server's `trustedProxyHops` matching the deployment's actual reverse-proxy count — at the default 0 it ignores `x-forwarded-for` entirely, so every caller shares one bucket until that's configured. `publicTenantResolution: \"fileRef\"` keeps resolveApexTenant as the host gate but reads the variant from the FileRef row's own tenant instead of the host's, so one shared platform host serves every tenant's public variants — each FileRef-tenant's own `isPublic` predicate still default-denies. Also declares the `derivativeOverlayResolver` extension point (`r.useExtension(EXT_DERIVATIVE_OVERLAY_RESOLVER, '<entityType>', { resolve })`), used to turn a variant's `overlays[].dataToken` into the actual QR payload for that FileRef's entityType before the variant is rendered — a variant declaring a `qr` overlay throws at request-time if no resolver is registered for the FileRef's entityType.",
|
|
923
|
+
"description": "Declares the `derivativeRenderer` extension point. `ctx.services.derivatives.variant(fileRefId, spec, name)` derives a variant of a tracked FileRef the first time it's requested and reuses the stored result afterwards (derive-on-first-use, keyed by a hash of the spec). Mount at least one `derivatives-*` renderer feature alongside this one — without a registered renderer for the FileRef's MIME type, every `variant(...)` call throws. Also declares the `derivativePublicPredicate` extension point (`r.useExtension(EXT_DERIVATIVE_PUBLIC_PREDICATE, '<entityType>', { isPublic })`) and, when `createFileDerivativesFeature({resolveApexTenant})` is passed a host-resolver, mounts an anonymous `GET {basePath}/:fileRefId/:variant` route that serves any variant name the FileRef's field declared in its `variants` for a FileRef whose entityType has a registered predicate returning true — default-deny (404) otherwise, same as an unknown FileRef or an undeclared variant name. The route's only rate-limit (`per: \"ip\"`) depends on the server's `trustedProxyHops` matching the deployment's actual reverse-proxy count — at the default 0 it ignores `x-forwarded-for` entirely, so every caller shares one bucket until that's configured. `publicTenantResolution: \"fileRef\"` keeps resolveApexTenant as the host gate but reads the variant from the FileRef row's own tenant instead of the host's, so one shared platform host serves every tenant's public variants — each FileRef-tenant's own `isPublic` predicate still default-denies. Also declares the `derivativeOverlayResolver` extension point (`r.useExtension(EXT_DERIVATIVE_OVERLAY_RESOLVER, '<entityType>', { resolve })`), used to turn a variant's `overlays[].dataToken` into the actual QR payload for that FileRef's entityType before the variant is rendered — a variant declaring a `qr` overlay throws at request-time if no resolver is registered for the FileRef's entityType.",
|
|
924
924
|
"toggleableDefault": null,
|
|
925
925
|
"requires": [
|
|
926
926
|
"file-foundation",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@cosmicdrift/kumiko-types": "workspace:*",
|
|
27
27
|
"react": "^19.2.6",
|
|
28
28
|
"react-dom": "^19.2.6",
|
|
29
|
-
"zod": "^4.
|
|
29
|
+
"zod": "^4.6.7",
|
|
30
30
|
"@cosmicdrift/kumiko-locale-de": "workspace:*",
|
|
31
31
|
"@cosmicdrift/kumiko-locale-es": "workspace:*",
|
|
32
32
|
"@cosmicdrift/kumiko-locale-fr": "workspace:*",
|
|
@@ -93,7 +93,12 @@ async function seedTierAssignment(
|
|
|
93
93
|
adminUserId: string,
|
|
94
94
|
): Promise<void> {
|
|
95
95
|
const by: SessionUser = { ...TestUsers.systemAdmin, tenantId, id: adminUserId };
|
|
96
|
-
const tdb = createTenantDb({
|
|
96
|
+
const tdb = createTenantDb({
|
|
97
|
+
db,
|
|
98
|
+
tenantId,
|
|
99
|
+
mode: "system",
|
|
100
|
+
reason: "demo seed writes tier assignments for the tenant being provisioned",
|
|
101
|
+
});
|
|
97
102
|
const result = await tierAssignmentExecutor.create(
|
|
98
103
|
{ id: tierAssignmentAggregateId(tenantId), tier, source: "manual", tenantId },
|
|
99
104
|
by,
|
|
@@ -117,7 +122,12 @@ async function seedTagsAndNotes(
|
|
|
117
122
|
adminUserId: string,
|
|
118
123
|
): Promise<void> {
|
|
119
124
|
const by: SessionUser = { ...TestUsers.systemAdmin, tenantId, id: adminUserId };
|
|
120
|
-
const tdb = createTenantDb({
|
|
125
|
+
const tdb = createTenantDb({
|
|
126
|
+
db,
|
|
127
|
+
tenantId,
|
|
128
|
+
mode: "system",
|
|
129
|
+
reason: "demo seed writes tier assignments for the tenant being provisioned",
|
|
130
|
+
});
|
|
121
131
|
|
|
122
132
|
const created = async (
|
|
123
133
|
label: string,
|
|
@@ -16,7 +16,7 @@ DSGVO requests work end-to-end:
|
|
|
16
16
|
|
|
17
17
|
| Article | Endpoint / Runner | What it does |
|
|
18
18
|
|---------|-----------|----------------|
|
|
19
|
-
| **Art. 15** | `user-data-rights:query:my-audit-log` | User sees own framework events (auth, deletion-request, restriction). Domain entities like todos appear in the **export bundle** (Art. 20), not the audit-log — only handlers using `ctx.
|
|
19
|
+
| **Art. 15** | `user-data-rights:query:my-audit-log` | User sees own framework events (auth, deletion-request, restriction). Domain entities like todos appear in the **export bundle** (Art. 20), not the audit-log — only handlers using `ctx.events.append` show up here. |
|
|
20
20
|
| **Art. 15+20** | `user-data-rights:write:request-export` | ZIP with user-profile + fileRefs + todos + signed download magic-link |
|
|
21
21
|
| **Art. 17** | `user-data-rights:write:request-deletion` | Soft-delete with grace period; cron anonymizes user + deletes todos |
|
|
22
22
|
| **Art. 18** | `user-data-rights:write:restrict-account` | Auth-middleware blocks logins until lift-restriction |
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"@cosmicdrift/kumiko-bundled-features": "workspace:*",
|
|
11
11
|
"@cosmicdrift/kumiko-dev-server": "workspace:*",
|
|
12
12
|
"@cosmicdrift/kumiko-framework": "workspace:*",
|
|
13
|
-
"zod": "^4.
|
|
13
|
+
"zod": "^4.6.7",
|
|
14
14
|
"@cosmicdrift/kumiko-locale-de": "workspace:*"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
// 3. todoDeleteHook DELETEt alle Rows mit author_id = userId
|
|
26
26
|
// 4. user wird anonymisiert (display_name="(deleted)", email=null)
|
|
27
27
|
|
|
28
|
+
import { UserDataRightsScreens } from "@cosmicdrift/kumiko-bundled-features/user-data-rights";
|
|
28
29
|
import {
|
|
29
30
|
deleteMany,
|
|
30
31
|
deriveEntityTableMeta,
|
|
@@ -182,7 +183,7 @@ export const todosFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
182
183
|
id: "gdpr-export-jobs",
|
|
183
184
|
label: "GDPR · Export Jobs",
|
|
184
185
|
icon: "shield",
|
|
185
|
-
screen:
|
|
186
|
+
screen: UserDataRightsScreens.exportJobList,
|
|
186
187
|
access: { roles: ["SystemAdmin"] },
|
|
187
188
|
order: 90,
|
|
188
189
|
});
|
|
@@ -190,7 +191,7 @@ export const todosFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
190
191
|
id: "gdpr-download-attempts",
|
|
191
192
|
label: "GDPR · Download Attempts",
|
|
192
193
|
icon: "download",
|
|
193
|
-
screen:
|
|
194
|
+
screen: UserDataRightsScreens.downloadAttemptList,
|
|
194
195
|
access: { roles: ["SystemAdmin"] },
|
|
195
196
|
order: 91,
|
|
196
197
|
});
|
|
@@ -60,7 +60,8 @@ row and the `read_fulfillments` row appear.
|
|
|
60
60
|
is re-run after `ms` on the same queue job: no retry attempt is used, no failure is logged
|
|
61
61
|
or recorded, and the run log shows only the runs that actually executed. `maxDefers`
|
|
62
62
|
(default 100) bounds it; beyond that the run fails. It needs a queued run: an inline boot
|
|
63
|
-
gate throws.
|
|
63
|
+
gate throws. `context.canDefer` is false in both cases, so the job checks it and calls the
|
|
64
|
+
carrier anyway instead of failing.
|
|
64
65
|
|
|
65
66
|
## Test
|
|
66
67
|
|
|
@@ -131,8 +131,9 @@ export function createApiWorkerSplitFeature() {
|
|
|
131
131
|
}
|
|
132
132
|
const { customerName } = processOrderPayloadSchema.parse(payload);
|
|
133
133
|
// Busy is not a failure: ctx.defer re-runs the job later without burning a retry.
|
|
134
|
+
// Once the defer budget is used up (or the run is inline), try anyway and let a failure retry.
|
|
134
135
|
const retryAfterMs = carrierRetryAfterMs();
|
|
135
|
-
if (retryAfterMs > 0) return context.defer(retryAfterMs);
|
|
136
|
+
if (retryAfterMs > 0 && context.canDefer) return context.defer(retryAfterMs);
|
|
136
137
|
const result = await fulfillWrite({
|
|
137
138
|
handlerQn: "orders:write:fulfillment:create",
|
|
138
139
|
payload: {
|
|
@@ -11,10 +11,10 @@ Three building blocks:
|
|
|
11
11
|
|
|
12
12
|
- **`r.defineEvent("name", zodSchema)`** declares the name and payload.
|
|
13
13
|
The qualified name (e.g. `pubsub-orders:event:order-placed`) comes
|
|
14
|
-
back as `.name` — pass it directly to `ctx.
|
|
15
|
-
aren't registered are rejected by `ctx.
|
|
14
|
+
back as `.name` — pass it directly to `ctx.events.append`. Events that
|
|
15
|
+
aren't registered are rejected by `ctx.events.append` at the emit site.
|
|
16
16
|
|
|
17
|
-
- **`ctx.
|
|
17
|
+
- **`ctx.events.append({ aggregateId, aggregateType, type, payload })`**
|
|
18
18
|
appends the event onto the aggregate stream within the same TX.
|
|
19
19
|
Version lineage is continued automatically — no synthetic
|
|
20
20
|
`"pubsub"` stream anymore, the event truly lives on the aggregate.
|
|
@@ -32,7 +32,7 @@ Three building blocks:
|
|
|
32
32
|
HTTP → writeHandler
|
|
33
33
|
↓ TX begin
|
|
34
34
|
orderExecutor.create(...) ← business row + auto-event (pubsubOrder.created, v1)
|
|
35
|
-
ctx.
|
|
35
|
+
ctx.events.append({...}) ← domain event (pubsub-orders:event:order-placed, v2)
|
|
36
36
|
↓ TX commit
|
|
37
37
|
(async, post-commit)
|
|
38
38
|
event-dispatcher runOnce
|
|
@@ -42,7 +42,7 @@ HTTP → writeHandler
|
|
|
42
42
|
|
|
43
43
|
## Migration note
|
|
44
44
|
|
|
45
|
-
`r.postEvent` was removed in Sprint E.2. `ctx.
|
|
45
|
+
`r.postEvent` was removed in Sprint E.2. `ctx.events.append` +
|
|
46
46
|
`r.multiStreamProjection` is the unified Marten API for CRUD events,
|
|
47
47
|
domain events, and cross-feature reactions.
|
|
48
48
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-cross-feature-events",
|
|
3
|
-
"description": "Feature appends a typed domain event via ctx.
|
|
3
|
+
"description": "Feature appends a typed domain event via ctx.events.append; a r.multiStreamProjection reacts asynchronously.",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-framework": "workspace:*"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Cross-Feature Reactions Sample (Marten gold-standard path)
|
|
2
2
|
//
|
|
3
3
|
// Demonstrates the Sprint-E pattern for reacting to events across features:
|
|
4
|
-
// a writeHandler emits a DOMAIN event via `ctx.
|
|
4
|
+
// a writeHandler emits a DOMAIN event via `ctx.events.append(...)` onto the
|
|
5
5
|
// aggregate's own stream, and a `r.multiStreamProjection` picks it up
|
|
6
6
|
// asynchronously through the event-dispatcher.
|
|
7
7
|
//
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
//
|
|
10
10
|
// 1. `r.defineEvent("short-name", zodSchema)` declares the event shape.
|
|
11
11
|
// Returns a def whose `.name` is the fully-qualified event name —
|
|
12
|
-
// pass that straight to `ctx.
|
|
12
|
+
// pass that straight to `ctx.events.append` so there's no string to
|
|
13
13
|
// hand-type (and no way to emit an unregistered event: appendEvent
|
|
14
14
|
// rejects at the emit site).
|
|
15
15
|
//
|
|
16
|
-
// 2. `ctx.
|
|
16
|
+
// 2. `ctx.events.append({ aggregateId, aggregateType, type, payload })`
|
|
17
17
|
// writes the event onto the aggregate's own stream inside the same
|
|
18
18
|
// transaction as the business write. Commit both or roll back both —
|
|
19
19
|
// no lost events, no orphan events. The event carries real
|
|
@@ -67,7 +67,7 @@ export const pubsubOrderFeature = defineFeature("pubsubOrders", (r) => {
|
|
|
67
67
|
r.entity({ name: "pubsub-order", ...orderEntity });
|
|
68
68
|
|
|
69
69
|
// Define the event shape once. The returned `.name` is the qualified
|
|
70
|
-
// name ("pubsub-orders:event:order-placed") — pass it to ctx.
|
|
70
|
+
// name ("pubsub-orders:event:order-placed") — pass it to ctx.events.append.
|
|
71
71
|
const orderPlaced = r.defineEvent(
|
|
72
72
|
"order-placed",
|
|
73
73
|
z.object({ id: z.string(), customer: z.string(), product: z.string() }),
|
|
@@ -9,12 +9,12 @@ blocks Kumiko provides.
|
|
|
9
9
|
| API | Purpose |
|
|
10
10
|
|---|---|
|
|
11
11
|
| `r.defineEvent(name, schema, { version, migrations })` | Event schema with version number + upcaster chain |
|
|
12
|
-
| `ctx.
|
|
12
|
+
| `ctx.events.append({ aggregateId, aggregateType, type, payload })` | Domain event on aggregate stream |
|
|
13
13
|
| `r.projection(...)` | Single-stream read model, inline in the write TX |
|
|
14
14
|
| `r.multiStreamProjection(...)` | Cross-aggregate read model, async via dispatcher |
|
|
15
|
-
| `ctx.
|
|
16
|
-
| `ctx.
|
|
17
|
-
| `ctx.queryProjection(name)` | Tenant-scoped read-model query |
|
|
15
|
+
| `ctx.events.load(id, { asOf })` | Live aggregation with point-in-time |
|
|
16
|
+
| `ctx.events.archive(id, { aggregateType })` | Archive a stream (Marten `ArchiveStream`) |
|
|
17
|
+
| `ctx.dispatch.queryProjection(name)` | Tenant-scoped read-model query |
|
|
18
18
|
|
|
19
19
|
## Domain
|
|
20
20
|
|
|
@@ -45,7 +45,7 @@ HTTP POST /api/write { type: "showcase:write:invoice:approve", ... }
|
|
|
45
45
|
↓
|
|
46
46
|
writeHandler("invoice:approve")
|
|
47
47
|
↓ TX begin
|
|
48
|
-
ctx.
|
|
48
|
+
ctx.events.append({ type: "showcase:event:invoice-approved", ... })
|
|
49
49
|
r.projection("invoice-detail") ← updates status = "approved"
|
|
50
50
|
↓ TX commit
|
|
51
51
|
(async)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-event-sourcing",
|
|
3
|
-
"description": "Production-pattern ES sample: r.defineEvent (versioned + migrations) + ctx.
|
|
3
|
+
"description": "Production-pattern ES sample: r.defineEvent (versioned + migrations) + ctx.events.append + r.projection + r.multiStreamProjection + ctx.events.load (asOf) + ctx.events.archive + ctx.dispatch.queryProjection.",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-framework": "workspace:*"
|
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
// - emitEvent with headers — Marten free key/value metadata
|
|
10
10
|
// - r.projection — single-stream read model (inline in the write TX)
|
|
11
11
|
// - r.multiStreamProjection — cross-aggregate async read model
|
|
12
|
-
// - ctx.
|
|
13
|
-
// - ctx.
|
|
14
|
-
// - ctx.queryProjection — tenant-scoped read of a projection table
|
|
15
|
-
// - ctx.
|
|
12
|
+
// - ctx.events.load with { asOf } — point-in-time aggregate state
|
|
13
|
+
// - ctx.events.archive / ctx.events.restore — Marten ArchiveStream
|
|
14
|
+
// - ctx.dispatch.queryProjection — tenant-scoped read of a projection table
|
|
15
|
+
// - ctx.events.snapshot + ctx.events.loadWithSnapshot — perf path
|
|
16
16
|
// for aggregates with long event tails (O(1) vs O(N) reduce), incl.
|
|
17
17
|
// the auto-snapshot policy ({ snapshotEvery, snapshotVersion })
|
|
18
18
|
// - streamAllEventsByType — memory-bounded iteration for ops/export jobs
|
|
@@ -339,7 +339,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
339
339
|
name: "invoice:archive",
|
|
340
340
|
schema: z.object({ id: z.uuid() }),
|
|
341
341
|
handler: async (event, ctx) => {
|
|
342
|
-
await ctx.
|
|
342
|
+
await ctx.events.archive(event.payload.id, { aggregateType: "showcase-invoice" });
|
|
343
343
|
return { isSuccess: true as const, data: { id: event.payload.id } };
|
|
344
344
|
},
|
|
345
345
|
access: { roles: ["Admin"] },
|
|
@@ -354,14 +354,14 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
354
354
|
handler: async (event, ctx) => {
|
|
355
355
|
// Rebuild the state from the current stream so the snapshot reflects
|
|
356
356
|
// the committed event log, not an in-memory guess. Use the upcaster-
|
|
357
|
-
// aware ctx.
|
|
358
|
-
const events = await ctx.
|
|
357
|
+
// aware ctx.events.load to stay consistent with read-time semantics.
|
|
358
|
+
const events = await ctx.events.load(event.payload.id);
|
|
359
359
|
const state: InvoiceState = { ...initialInvoiceState };
|
|
360
360
|
for (const evt of events) {
|
|
361
361
|
reduceInvoice(state, evt);
|
|
362
362
|
}
|
|
363
363
|
const version = events.length > 0 ? (events[events.length - 1]?.version ?? 0) : 0;
|
|
364
|
-
await ctx.
|
|
364
|
+
await ctx.events.snapshot({
|
|
365
365
|
aggregateId: event.payload.id,
|
|
366
366
|
aggregateType: "showcase-invoice",
|
|
367
367
|
version,
|
|
@@ -374,13 +374,13 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
374
374
|
|
|
375
375
|
// --- Query handlers ---
|
|
376
376
|
|
|
377
|
-
// Live aggregation via ctx.
|
|
377
|
+
// Live aggregation via ctx.events.load — reduces events into a state
|
|
378
378
|
// snapshot. Supports asOf for point-in-time reads.
|
|
379
379
|
r.queryHandler({
|
|
380
380
|
name: "invoice:state",
|
|
381
381
|
schema: z.object({ id: z.uuid(), asOf: z.iso.datetime().optional() }),
|
|
382
382
|
handler: async (query, ctx) => {
|
|
383
|
-
const events = await ctx.
|
|
383
|
+
const events = await ctx.events.load(query.payload.id, {
|
|
384
384
|
...(query.payload.asOf ? { asOf: Temporal.Instant.from(query.payload.asOf) } : {}),
|
|
385
385
|
});
|
|
386
386
|
const state: InvoiceState = { ...initialInvoiceState };
|
|
@@ -405,7 +405,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
405
405
|
name: "invoice:fast-state",
|
|
406
406
|
schema: z.object({ id: z.uuid() }),
|
|
407
407
|
handler: async (query, ctx) => {
|
|
408
|
-
const result = await ctx.
|
|
408
|
+
const result = await ctx.events.loadWithSnapshot<InvoiceStateRecord>(
|
|
409
409
|
query.payload.id,
|
|
410
410
|
(state, evt) => {
|
|
411
411
|
const next: InvoiceStateRecord = { ...state };
|
|
@@ -434,7 +434,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
434
434
|
},
|
|
435
435
|
});
|
|
436
436
|
|
|
437
|
-
// Read-model query via ctx.queryProjection — auto-tenant-scoped. The
|
|
437
|
+
// Read-model query via ctx.dispatch.queryProjection — auto-tenant-scoped. The
|
|
438
438
|
// helper collapses the zero-argument "return this projection" pattern
|
|
439
439
|
// into a single call; returns { rows } so the response shape stays
|
|
440
440
|
// consistent with other list handlers.
|
|
@@ -450,11 +450,11 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
450
450
|
}),
|
|
451
451
|
);
|
|
452
452
|
|
|
453
|
-
// Slow synthetic export — demonstrates ctx.signal honouring inside a
|
|
453
|
+
// Slow synthetic export — demonstrates ctx.lifecycle.signal honouring inside a
|
|
454
454
|
// long-running handler loop. Real-world equivalents: PDF batch export,
|
|
455
455
|
// bulk CSV download, multi-stream audit walk. Mobile clients that
|
|
456
456
|
// navigate away mid-export should NOT keep the server busy producing
|
|
457
|
-
// bytes nobody will read. The handler checks ctx.signal at every
|
|
457
|
+
// bytes nobody will read. The handler checks ctx.lifecycle.signal at every
|
|
458
458
|
// iteration; on abort, throws and the dispatcher returns an error to
|
|
459
459
|
// the (already-disconnected) client.
|
|
460
460
|
r.queryHandler({
|
|
@@ -463,7 +463,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
463
463
|
handler: async (query, ctx) => {
|
|
464
464
|
const accumulated: number[] = [];
|
|
465
465
|
for (let i = 0; i < query.payload.chunks; i++) {
|
|
466
|
-
ctx.signal?.throwIfAborted();
|
|
466
|
+
ctx.lifecycle.signal?.throwIfAborted();
|
|
467
467
|
// Fake "work per chunk". Realistic handler would assemble a row,
|
|
468
468
|
// hit a remote service, etc.
|
|
469
469
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -5,7 +5,7 @@ File upload plus post-processing hooks.
|
|
|
5
5
|
## What it shows
|
|
6
6
|
|
|
7
7
|
- Declarative `variants` on `createImageField` (file-derivatives)
|
|
8
|
-
- Imperative `ctx.derivatives.variant()` path and the public variant route (`EXT_DERIVATIVE_PUBLIC_PREDICATE`)
|
|
8
|
+
- Imperative `ctx.services.derivatives.variant()` path and the public variant route (`EXT_DERIVATIVE_PUBLIC_PREDICATE`)
|
|
9
9
|
|
|
10
10
|
## Source
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-files-post-processing",
|
|
3
|
-
"description": "The three ways to generate an image variant: declarative field variants, imperative ctx.derivatives.variant() with region-blur, and the public route behind a default-deny predicate.",
|
|
3
|
+
"description": "The three ways to generate an image variant: declarative field variants, imperative ctx.services.derivatives.variant() with region-blur, and the public route behind a default-deny predicate.",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-bundled-features": "workspace:*",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// resolves + caches the variant behind the auth-gated
|
|
10
10
|
// `GET /api/files/:id/variant/:name` route, no handler code needed.
|
|
11
11
|
// 2. Imperative — the `set-cover` write-handler calls
|
|
12
|
-
// `ctx.derivatives.variant()` directly. This is for apps that track
|
|
12
|
+
// `ctx.services.derivatives.variant()` directly. This is for apps that track
|
|
13
13
|
// images as their own records (ordering, a chosen cover image) and for
|
|
14
14
|
// region-blur, whose coordinates are runtime data that can't live in a
|
|
15
15
|
// static field declaration.
|
|
@@ -88,13 +88,15 @@ export const filesPostProcessingFeature = defineFeature("files-post-processing",
|
|
|
88
88
|
.optional(),
|
|
89
89
|
}),
|
|
90
90
|
handler: async (event, ctx) => {
|
|
91
|
-
if (!ctx.derivatives) {
|
|
91
|
+
if (!ctx.services.derivatives) {
|
|
92
92
|
return writeFailure(
|
|
93
|
-
new InternalError({
|
|
93
|
+
new InternalError({
|
|
94
|
+
message: "ctx.services.derivatives missing — is file-derivatives mounted?",
|
|
95
|
+
}),
|
|
94
96
|
);
|
|
95
97
|
}
|
|
96
98
|
const { fileRefId, blurRegion } = event.payload;
|
|
97
|
-
const result = await ctx.derivatives.variant(
|
|
99
|
+
const result = await ctx.services.derivatives.variant(
|
|
98
100
|
fileRefId,
|
|
99
101
|
{
|
|
100
102
|
maxEdge: 800,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-lane-routing",
|
|
3
|
-
"description": "runIn lane-routing: r.job pinned to api/worker deploy-lanes, event-chain via ctx.
|
|
3
|
+
"description": "runIn lane-routing: r.job pinned to api/worker deploy-lanes, event-chain via ctx.events.append, verified end-to-end through createAllInOneEntrypoint.",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-framework": "workspace:*"
|
|
@@ -100,9 +100,9 @@ A one-shot setup routine that runs on first boot or via the CLI:
|
|
|
100
100
|
// bin/seed-legal.ts
|
|
101
101
|
import { seedTextBlock } from "@cosmicdrift/kumiko-bundled-features/template-resolver/seeding";
|
|
102
102
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
103
|
-
import {
|
|
103
|
+
import { createDbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
104
104
|
|
|
105
|
-
const db =
|
|
105
|
+
const { db } = createDbConnection({ url: process.env.DATABASE_URL! });
|
|
106
106
|
|
|
107
107
|
await seedTextBlock(db, {
|
|
108
108
|
tenantId: SYSTEM_TENANT_ID,
|
|
@@ -27,8 +27,8 @@ back.
|
|
|
27
27
|
- **`r.hook({ type: "postDelete", target: { allOf: entityRef }, handler })`** — fires after commit.
|
|
28
28
|
External side effects (logs, notifications) belong here because
|
|
29
29
|
failures cannot roll the delete back.
|
|
30
|
-
- **`ctx.
|
|
31
|
-
the handler transaction (or the enclosing savepoint of a nested `ctx.write`) does not commit.
|
|
30
|
+
- **`ctx.lifecycle.afterRollback(fn)`** — the counterpart of `ctx.lifecycle.afterCommit`: runs `fn` when
|
|
31
|
+
the handler transaction (or the enclosing savepoint of a nested `ctx.dispatch.write`) does not commit.
|
|
32
32
|
Use it to give back something reserved outside the transaction, like the slot in
|
|
33
33
|
`article:reserve-slot`. A failed COMMIT triggers it too, so write the callback idempotent.
|
|
34
34
|
|
|
@@ -65,7 +65,7 @@ export const articlesFeature = defineFeature("blog", (r) => {
|
|
|
65
65
|
handler: async (event, ctx) => {
|
|
66
66
|
const { slot, reject } = event.payload;
|
|
67
67
|
reservedSlots.add(slot);
|
|
68
|
-
ctx.
|
|
68
|
+
ctx.lifecycle.afterRollback?.(async () => {
|
|
69
69
|
reservedSlots.delete(slot);
|
|
70
70
|
});
|
|
71
71
|
if (reject) return writeFailure(new UnprocessableError("slot_rejected"));
|
|
@@ -43,7 +43,7 @@ r.config({
|
|
|
43
43
|
|
|
44
44
|
- **`backing: "secrets"`** — the value lives in the secrets store (envelope
|
|
45
45
|
encryption, KEK rotation, audit-on-read) instead of `config_values`. Set/read/
|
|
46
|
-
reset dispatch through `ctx.secrets`; the query handlers mask the value while
|
|
46
|
+
reset dispatch through `ctx.services.secrets`; the query handlers mask the value while
|
|
47
47
|
the owning feature still reads the revealed plaintext via `ctx.config`. Only
|
|
48
48
|
valid for `scope: "system"` — secrets are flat per `(tenant, key)` with no
|
|
49
49
|
cascade, so a tenant override would be incoherent and is rejected at boot.
|
|
@@ -72,7 +72,7 @@ await runProdApp({
|
|
|
72
72
|
envSchema, // composeEnvSchema({ core: frameworkCoreEnvSchema, features })
|
|
73
73
|
extraContext: ({ db, registry }) => ({
|
|
74
74
|
configResolver: resolver,
|
|
75
|
-
|
|
75
|
+
configAccessorFactory: createConfigAccessorFactory(registry, resolver),
|
|
76
76
|
// Required whenever a key declares backing:"secrets" — without it the set
|
|
77
77
|
// and read paths fail loud, never silently miss.
|
|
78
78
|
secrets: createSecretsContext({ db, masterKeyProvider, registry }),
|
|
@@ -82,7 +82,7 @@ await runProdApp({
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Register the `secrets` feature too so its `tenant_secrets` table is migrated;
|
|
85
|
-
the config backing only needs the `ctx.secrets` context shown above.
|
|
85
|
+
the config backing only needs the `ctx.services.secrets` context shown above.
|
|
86
86
|
|
|
87
87
|
## Vs. the neighbouring recipes
|
|
88
88
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Search Sample
|
|
2
2
|
// Shows: searchable fields, searchWeight, search via InMemory SearchAdapter.
|
|
3
|
-
// Each handler builds its executor inline so `ctx.searchAdapter` is read fresh
|
|
3
|
+
// Each handler builds its executor inline so `ctx.services.searchAdapter` is read fresh
|
|
4
4
|
// per call — tests swap the adapter between runs.
|
|
5
5
|
|
|
6
6
|
import { buildEntityTable, createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
@@ -38,7 +38,7 @@ export const productFeature = defineFeature("shop", (r) => {
|
|
|
38
38
|
}),
|
|
39
39
|
handler: async (event, ctx) => {
|
|
40
40
|
const crud = createEventStoreExecutor(productTable, productEntity, {
|
|
41
|
-
searchAdapter: ctx.searchAdapter,
|
|
41
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
42
42
|
entityName: "product",
|
|
43
43
|
});
|
|
44
44
|
return crud.create(event.payload, event.user, ctx.db);
|
|
@@ -54,7 +54,7 @@ export const productFeature = defineFeature("shop", (r) => {
|
|
|
54
54
|
}),
|
|
55
55
|
handler: async (query, ctx) => {
|
|
56
56
|
const crud = createEventStoreExecutor(productTable, productEntity, {
|
|
57
|
-
searchAdapter: ctx.searchAdapter,
|
|
57
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
58
58
|
entityName: "product",
|
|
59
59
|
});
|
|
60
60
|
return crud.list(query.payload, query.user, ctx.db);
|
|
@@ -4,7 +4,7 @@ Tenant-owned secrets with envelope encryption, DEK cache, and KEK rotation.
|
|
|
4
4
|
|
|
5
5
|
## What it shows
|
|
6
6
|
|
|
7
|
-
- `r.secret` + `ctx.secrets.get` (audited reads)
|
|
7
|
+
- `r.secret` + `ctx.services.secrets.get` (audited reads)
|
|
8
8
|
- Charge-style handler that never returns the plaintext key
|
|
9
9
|
|
|
10
10
|
## Source
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// A miniature "billing" feature that reads a Stripe API key from ctx.secrets
|
|
1
|
+
// A miniature "billing" feature that reads a Stripe API key from ctx.services.secrets
|
|
2
2
|
// and simulates a charge. The point is to show how feature code uses the
|
|
3
3
|
// plaintext secret without ever exposing it to clients — the charge endpoint
|
|
4
4
|
// returns { chargeId } but never the apiKey.
|
|
@@ -27,7 +27,10 @@ const chargeWrite = defineWriteHandler({
|
|
|
27
27
|
}),
|
|
28
28
|
access: { roles: ["TenantAdmin", "User"] },
|
|
29
29
|
handler: async (event, ctx) => {
|
|
30
|
-
const secrets = requireSecretsContext(
|
|
30
|
+
const secrets = requireSecretsContext(
|
|
31
|
+
{ secrets: ctx.services.secrets, user: ctx.user },
|
|
32
|
+
"billing:write:charge",
|
|
33
|
+
);
|
|
31
34
|
// requireSecretsContext returned a wrapper that auto-fills the audit
|
|
32
35
|
// context (userId + "billing:write:charge") on every .get — the read
|
|
33
36
|
// lands in tenant_secret_reads without the handler having to remember.
|
|
@@ -23,7 +23,7 @@ export const INVOICE_EVENTS = {
|
|
|
23
23
|
// Qualifier used by the reducer to match events from the stream.
|
|
24
24
|
// `<const TShort>` preserves the literal-string at the call-site, so
|
|
25
25
|
// `eventName(INVOICE_EVENTS.sent)` carries `"billing:event:invoice-sent"`
|
|
26
|
-
// as a literal type. That literal is what `ctx.
|
|
26
|
+
// as a literal type. That literal is what `ctx.events.append({ type, ... })`
|
|
27
27
|
// strict-checks against the augmented `KumikoEventTypeMap`.
|
|
28
28
|
export function eventName<const TShort extends string>(
|
|
29
29
|
short: TShort,
|