@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
|
@@ -12,7 +12,7 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle.ts";
|
|
|
12
12
|
|
|
13
13
|
// POST /api/user/restrict — GDPR Art. 18 account freeze.
|
|
14
14
|
// Flips status=Active → Restricted and revokes all live sessions
|
|
15
|
-
// for the user via cross-feature ctx.writeAs(sessions.revokeAllForUser).
|
|
15
|
+
// for the user via cross-feature ctx.dispatch.writeAs(sessions.revokeAllForUser).
|
|
16
16
|
//
|
|
17
17
|
// Write API behaviour:
|
|
18
18
|
// - Login is blocked: login.write.ts checks status=Restricted (Atom 3).
|
|
@@ -53,7 +53,7 @@ export const restrictAccountWrite = defineWriteHandler({
|
|
|
53
53
|
},
|
|
54
54
|
escapeHatch: {
|
|
55
55
|
reason:
|
|
56
|
-
"Revokes all live sessions of the (possibly self-)restricted user via ctx.writeAs(SYSTEM, " +
|
|
56
|
+
"Revokes all live sessions of the (possibly self-)restricted user via ctx.dispatch.writeAs(SYSTEM, " +
|
|
57
57
|
"sessions:write:user-session:revoke-all-for-user) — that handler's own access gate needs " +
|
|
58
58
|
"privileged roles the acting caller doesn't necessarily have. It also checks the target " +
|
|
59
59
|
"user's membership in the admin's tenant and appends the user lifecycle status change on " +
|
|
@@ -112,10 +112,10 @@ export const restrictAccountWrite = defineWriteHandler({
|
|
|
112
112
|
|
|
113
113
|
// Cross-feature: all live sessions are revoked; otherwise the user could
|
|
114
114
|
// continue writing with an existing JWT until token expiry.
|
|
115
|
-
// ctx.writeAs(systemUser, ...) gives the privileged handler the
|
|
115
|
+
// ctx.dispatch.writeAs(systemUser, ...) gives the privileged handler the
|
|
116
116
|
// System user roles required by its access gate.
|
|
117
117
|
const systemUser = createSystemUser(event.user.tenantId);
|
|
118
|
-
await ctx.writeAs(systemUser, "sessions:write:user-session:revoke-all-for-user", {
|
|
118
|
+
await ctx.dispatch.writeAs(systemUser, "sessions:write:user-session:revoke-all-for-user", {
|
|
119
119
|
userId: targetUserId,
|
|
120
120
|
});
|
|
121
121
|
|
|
@@ -17,7 +17,11 @@ import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
|
17
17
|
import * as z from "zod";
|
|
18
18
|
import { resolveAppTenantModel } from "../lib/resolve-tenant-model.ts";
|
|
19
19
|
import { makeTenantStorageProviderResolver } from "../lib/storage-provider-resolver.ts";
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
FORGET_CLEANUP_HANDLER_NAME,
|
|
22
|
+
runForgetCleanup,
|
|
23
|
+
type SendDeletionExecutedEmailFn,
|
|
24
|
+
} from "../run-forget-cleanup.ts";
|
|
21
25
|
|
|
22
26
|
export type RunForgetCleanupOptions = {
|
|
23
27
|
readonly sendDeletionExecutedEmail?: SendDeletionExecutedEmailFn;
|
|
@@ -56,7 +60,7 @@ export function createRunForgetCleanupHandler(opts: RunForgetCleanupOptions = {}
|
|
|
56
60
|
const forgetDb = runner as DbConnection; // @cast-boundary db-runner — DbRunner narrows to DbConnection, config reads tolerate the outer tx
|
|
57
61
|
const tenantModel = await resolveAppTenantModel({
|
|
58
62
|
registry: ctx.registry,
|
|
59
|
-
configResolver: ctx.configResolver,
|
|
63
|
+
configResolver: ctx.services.configResolver,
|
|
60
64
|
db: forgetDb,
|
|
61
65
|
userId: ctx.user.id,
|
|
62
66
|
});
|
|
@@ -65,14 +69,16 @@ export function createRunForgetCleanupHandler(opts: RunForgetCleanupOptions = {}
|
|
|
65
69
|
registry: ctx.registry,
|
|
66
70
|
now: T.Now.instant(),
|
|
67
71
|
tenantModel,
|
|
68
|
-
...(ctx.searchAdapter !== undefined && {
|
|
72
|
+
...(ctx.services.searchAdapter !== undefined && {
|
|
73
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
74
|
+
}),
|
|
69
75
|
buildStorageProvider: makeTenantStorageProviderResolver({
|
|
70
76
|
registry: ctx.registry,
|
|
71
|
-
configResolver: ctx.configResolver,
|
|
72
|
-
secrets: ctx.secrets,
|
|
77
|
+
configResolver: ctx.services.configResolver,
|
|
78
|
+
secrets: ctx.services.secrets,
|
|
73
79
|
db: forgetDb,
|
|
74
80
|
userId: ctx.user.id,
|
|
75
|
-
handlerName:
|
|
81
|
+
handlerName: FORGET_CLEANUP_HANDLER_NAME,
|
|
76
82
|
}),
|
|
77
83
|
...(opts.sendDeletionExecutedEmail && {
|
|
78
84
|
sendDeletionExecutedEmail: opts.sendDeletionExecutedEmail,
|
|
@@ -31,7 +31,7 @@ export function makeTenantMailTransportResolver(
|
|
|
31
31
|
return async (tenantId) => {
|
|
32
32
|
if (!ctx.configResolver) {
|
|
33
33
|
throw new Error(
|
|
34
|
-
`${ctx.handlerName}: ctx.configResolver missing — cannot resolve the mail transport for tenant ${tenantId}`,
|
|
34
|
+
`${ctx.handlerName}: ctx.services.configResolver missing — cannot resolve the mail transport for tenant ${tenantId}`,
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
const config = createConfigAccessor(
|
|
@@ -30,7 +30,7 @@ export function makeTenantStorageProviderResolver(
|
|
|
30
30
|
return async (tenantId) => {
|
|
31
31
|
if (!ctx.configResolver) {
|
|
32
32
|
throw new InternalError({
|
|
33
|
-
message: `[${ctx.handlerName}] ctx.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
|
|
33
|
+
message: `[${ctx.handlerName}] ctx.services.configResolver missing — cannot resolve the file provider for tenant ${tenantId}`,
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
const config = createConfigAccessor(
|
|
@@ -62,7 +62,12 @@ export async function updateUserLifecycle(
|
|
|
62
62
|
// (restrict/lift-restriction/grace-period/cancel/forget) that holds no row
|
|
63
63
|
// version to pass as `payload.version`, which update() requires otherwise.
|
|
64
64
|
// Callers needing a guard use `options.expect` instead.
|
|
65
|
-
const tenantDb = createTenantDb({
|
|
65
|
+
const tenantDb = createTenantDb({
|
|
66
|
+
db: conn,
|
|
67
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
68
|
+
mode: "system",
|
|
69
|
+
reason: "User is tenant-agnostic, lifecycle transitions run in the system tenant",
|
|
70
|
+
});
|
|
66
71
|
const result = await userExecutor.update(
|
|
67
72
|
{ id: userId, changes },
|
|
68
73
|
createSystemUser(SYSTEM_TENANT_ID),
|
|
@@ -54,7 +54,7 @@ import { generateDownloadToken } from "./token-helpers.ts";
|
|
|
54
54
|
type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
55
55
|
|
|
56
56
|
// Gauge short name (feature.ts registers it via r.metric + resolves ctx.meter
|
|
57
|
-
// into a MetricsHandle — jobs get no bound ctx.metrics, see feature.ts).
|
|
57
|
+
// into a MetricsHandle — jobs get no bound ctx.obs.metrics, see feature.ts).
|
|
58
58
|
// No "_seconds"/"_total" suffix: validateMetricName rejects both on a gauge.
|
|
59
59
|
// Unit lives in the r.metric() `unit: "seconds"` declaration instead.
|
|
60
60
|
export const EXPORT_CLEANUP_BACKLOG_AGE_METRIC = "export_cleanup_backlog_age";
|
|
@@ -727,7 +727,13 @@ function reportCleanupLastRun(metrics: MetricsHandle | undefined, now: Instant):
|
|
|
727
727
|
// `requestedFromTenantId` preserves stream identity and keeps the stream counter consistent.
|
|
728
728
|
// @wrapper-known semantic-alias
|
|
729
729
|
function systemTenantDb(db: DbConnection, tenantId: TenantId) {
|
|
730
|
-
return createTenantDb({
|
|
730
|
+
return createTenantDb({
|
|
731
|
+
db,
|
|
732
|
+
tenantId,
|
|
733
|
+
mode: "system",
|
|
734
|
+
reason:
|
|
735
|
+
"export jobs run per user across tenants, requestedFromTenantId keeps the stream identity",
|
|
736
|
+
});
|
|
731
737
|
}
|
|
732
738
|
|
|
733
739
|
function buildExportStorageKey(job: JobRow): string {
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
type UserDataDeleteStrategy,
|
|
57
57
|
type UserDataStorageProvider,
|
|
58
58
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
59
|
+
import { runInSystemRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
59
60
|
import {
|
|
60
61
|
createEscapeHatchReporter,
|
|
61
62
|
UNATTRIBUTED_ACTOR,
|
|
@@ -243,13 +244,23 @@ const HOOK_ORDER_DEFAULT = EXT_USER_DATA_ORDER.DEFAULT;
|
|
|
243
244
|
// console.warn so a crafted reason can't forge extra log lines, and cap the
|
|
244
245
|
// length against a log-flood. The unsanitized reason still lands in the
|
|
245
246
|
// returned `incomplete` entry untouched.
|
|
247
|
+
// Secret-read audit discriminator, not a QN; the value must stay stable for the audit trail.
|
|
248
|
+
export const FORGET_CLEANUP_HANDLER_NAME = "user-data-rights:run-forget-cleanup" as const;
|
|
249
|
+
|
|
246
250
|
export function sanitizeReasonForLog(reason: string): string {
|
|
247
251
|
return reason.replace(/[\r\n]+/g, " ").slice(0, 500);
|
|
248
252
|
}
|
|
249
253
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
): Promise<RunForgetCleanupResult> {
|
|
254
|
+
// Cross-tenant by design: the due-user and membership reads on the given runner and raw hook
|
|
255
|
+
// statements must see every tenant, whatever RLS scope the caller (job, operator handler) runs in.
|
|
256
|
+
export function runForgetCleanup(args: RunForgetCleanupArgs): Promise<RunForgetCleanupResult> {
|
|
257
|
+
return runInSystemRowLevelSecurityScope(
|
|
258
|
+
"forget cleanup anonymizes users across all tenants",
|
|
259
|
+
() => forgetDueUsers(args),
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async function forgetDueUsers(args: RunForgetCleanupArgs): Promise<RunForgetCleanupResult> {
|
|
253
264
|
const { db, registry, now, sendDeletionExecutedEmail, buildStorageProvider } = args;
|
|
254
265
|
const kms = args.kms ?? configuredPiiSubjectKms();
|
|
255
266
|
const appTenantModel: TenantUserModel = args.tenantModel ?? "multi-user";
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
type UserDataExportHook,
|
|
44
44
|
type UserDataExportSnippet,
|
|
45
45
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
46
|
+
import { runInSystemRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
46
47
|
import {
|
|
47
48
|
createEscapeHatchReporter,
|
|
48
49
|
UNATTRIBUTED_ACTOR,
|
|
@@ -163,7 +164,16 @@ function buildHookDb(
|
|
|
163
164
|
* **Operator signal:** if bundle.json exceeds 100 MB in the ZIP, trigger
|
|
164
165
|
* telemetry and evaluate a schema refactor.
|
|
165
166
|
*/
|
|
166
|
-
|
|
167
|
+
// Cross-tenant by design: the membership read on the given runner and raw hook statements
|
|
168
|
+
// must see every tenant of the user, whatever RLS scope the caller runs in.
|
|
169
|
+
export function runUserExport(args: RunUserExportArgs): Promise<UserExportBundle> {
|
|
170
|
+
return runInSystemRowLevelSecurityScope(
|
|
171
|
+
"user data export collects the subject's rows from all tenants",
|
|
172
|
+
() => exportUserData(args),
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function exportUserData(args: RunUserExportArgs): Promise<UserExportBundle> {
|
|
167
177
|
const { db, registry, userId, now } = args;
|
|
168
178
|
|
|
169
179
|
const memberships = await selectMany<{ tenantId: TenantId }>(db, tenantMembershipsTable, {
|
|
@@ -38,6 +38,16 @@ export const UserDataRightsQueries = {
|
|
|
38
38
|
// `overrides.set(TENANT_MODEL_CONFIG_KEY, "single-user")`.
|
|
39
39
|
export const TENANT_MODEL_CONFIG_KEY = "user-data-rights:config:tenant-model" as const;
|
|
40
40
|
|
|
41
|
+
export const UserDataRightsJobs = {
|
|
42
|
+
runExportJobs: "user-data-rights:job:run-export-jobs",
|
|
43
|
+
runForgetCleanup: "user-data-rights:job:run-forget-cleanup",
|
|
44
|
+
} as const;
|
|
45
|
+
|
|
46
|
+
export const UserDataRightsScreens = {
|
|
47
|
+
exportJobList: "user-data-rights:screen:export-job-list",
|
|
48
|
+
downloadAttemptList: "user-data-rights:screen:download-attempt-list",
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
41
51
|
export const UserDataRightsHandlers = {
|
|
42
52
|
requestExport: "user-data-rights:write:request-export",
|
|
43
53
|
requestDeletion: "user-data-rights:write:request-deletion",
|
|
@@ -7,7 +7,12 @@
|
|
|
7
7
|
export { type UserDataRightsClientOptions, userDataRightsClient } from "./client-plugin.tsx";
|
|
8
8
|
export type { ConfirmAccountDeletionScreenProps } from "./confirm-deletion-screen.tsx";
|
|
9
9
|
export { ConfirmAccountDeletionScreen } from "./confirm-deletion-screen.tsx";
|
|
10
|
-
export {
|
|
10
|
+
export {
|
|
11
|
+
UserDataRightsHandlers,
|
|
12
|
+
UserDataRightsJobs,
|
|
13
|
+
UserDataRightsQueries,
|
|
14
|
+
UserDataRightsScreens,
|
|
15
|
+
} from "./constants.ts";
|
|
11
16
|
export { defaultTranslations } from "./default-translations.ts";
|
|
12
17
|
export { formatDate } from "./privacy-center-screen.tsx";
|
|
13
18
|
export { makePublicDeletionGate, type PublicDeletionRoutes } from "./public-deletion-gate.tsx";
|
|
@@ -24,7 +24,7 @@ function invalidCredentials() {
|
|
|
24
24
|
|
|
25
25
|
// Change-email — authenticated, self-only. The user confirms with their
|
|
26
26
|
// current password (re-authentication as with change-password); the new
|
|
27
|
-
// address is written through the user-update handler via ctx.writeAs(system)
|
|
27
|
+
// address is written through the user-update handler via ctx.dispatch.writeAs(system)
|
|
28
28
|
// because `email` is field-level privileged and would otherwise be silently
|
|
29
29
|
// stripped during self-updates. emailVerified is set to false — the app then
|
|
30
30
|
// triggers the verification flow (request-email-verification is public; the
|
|
@@ -45,8 +45,8 @@ export const changeEmailWrite = defineWriteHandler({
|
|
|
45
45
|
escapeHatch: {
|
|
46
46
|
grants: ["systemIdentity"],
|
|
47
47
|
reason:
|
|
48
|
-
"Reads the caller's own passwordHash via ctx.queryAs(SYSTEM, user:findForAuth), checks " +
|
|
49
|
-
"email uniqueness the same way, and writes the new email via ctx.writeAs(SYSTEM, " +
|
|
48
|
+
"Reads the caller's own passwordHash via ctx.dispatch.queryAs(SYSTEM, user:findForAuth), checks " +
|
|
49
|
+
"email uniqueness the same way, and writes the new email via ctx.dispatch.writeAs(SYSTEM, " +
|
|
50
50
|
"user:update) — field-access on passwordHash/email is privileged-only.",
|
|
51
51
|
},
|
|
52
52
|
// Payload carries the current password.
|
|
@@ -56,7 +56,7 @@ export const changeEmailWrite = defineWriteHandler({
|
|
|
56
56
|
handler: async (event, ctx) => {
|
|
57
57
|
const systemUser = createSystemUser(event.user.tenantId);
|
|
58
58
|
|
|
59
|
-
const me = (await ctx.queryAs(systemUser, UserQueries.findForAuth, {
|
|
59
|
+
const me = (await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
|
|
60
60
|
id: event.user.id,
|
|
61
61
|
})) as { id: string; email: string; passwordHash: string | null; version: number } | null; // @cast-boundary db-runner
|
|
62
62
|
|
|
@@ -79,7 +79,9 @@ export const changeEmailWrite = defineWriteHandler({
|
|
|
79
79
|
);
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
const existing = await ctx.queryAs(systemUser, UserQueries.findForAuth, {
|
|
82
|
+
const existing = await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
|
|
83
|
+
email: newEmail,
|
|
84
|
+
});
|
|
83
85
|
if (existing !== null) {
|
|
84
86
|
return writeFailure(
|
|
85
87
|
new UnprocessableError(UserErrors.emailAlreadyExists, {
|
|
@@ -93,7 +95,7 @@ export const changeEmailWrite = defineWriteHandler({
|
|
|
93
95
|
// backfillUserStreamTenants.
|
|
94
96
|
const writer = createSystemUser(SYSTEM_TENANT_ID);
|
|
95
97
|
|
|
96
|
-
const writeRes = await ctx.writeAs(writer, UserHandlers.update, {
|
|
98
|
+
const writeRes = await ctx.dispatch.writeAs(writer, UserHandlers.update, {
|
|
97
99
|
id: me.id,
|
|
98
100
|
version: me.version,
|
|
99
101
|
changes: { email: newEmail, emailVerified: false },
|
|
@@ -21,7 +21,7 @@ const PurgeSchema = z.object({ confirmCount: z.number().int().min(0) }).strict()
|
|
|
21
21
|
const ExportSchema = z.object({}).strict();
|
|
22
22
|
|
|
23
23
|
async function appendAudit(ctx: HandlerContext, type: string, count: number): Promise<void> {
|
|
24
|
-
await ctx.
|
|
24
|
+
await ctx.events.unsafeAppend({
|
|
25
25
|
aggregateId: generateId(),
|
|
26
26
|
aggregateType: WAITLIST_AUDIT_AGGREGATE_TYPE,
|
|
27
27
|
type,
|
|
@@ -111,7 +111,7 @@ async function ensureOwnTenant(
|
|
|
111
111
|
|
|
112
112
|
const newTenantId = parseTenantId(crypto.randomUUID());
|
|
113
113
|
if (!newTenantId) throw new InternalError({ message: "waitlist:invite: tenant id mint failed" });
|
|
114
|
-
const created = await ctx.writeAs(createSystemUser(newTenantId), TenantHandlers.create, {
|
|
114
|
+
const created = await ctx.dispatch.writeAs(createSystemUser(newTenantId), TenantHandlers.create, {
|
|
115
115
|
id: newTenantId,
|
|
116
116
|
key,
|
|
117
117
|
name: entry.company ?? entry.name,
|
|
@@ -167,7 +167,7 @@ export function createInviteHandler(invite: WaitlistInviteOptions = DEFAULT_INVI
|
|
|
167
167
|
// inviteCreate is admin-only (no system role); the system variant with an
|
|
168
168
|
// empty globalRoles list is the only in-process path, and a waitlist
|
|
169
169
|
// invite must never grant a global role.
|
|
170
|
-
const invited = await ctx.writeAs(
|
|
170
|
+
const invited = await ctx.dispatch.writeAs(
|
|
171
171
|
createSystemUser(target.tenantId),
|
|
172
172
|
AuthHandlers.systemInviteCreate,
|
|
173
173
|
{
|
|
@@ -83,7 +83,7 @@ async function hasOpenEntryForEmail(email: string, ctx: HandlerContext): Promise
|
|
|
83
83
|
// resolver does not extend the lock. Postgres aborts the whole transaction after a failed
|
|
84
84
|
// statement, even one whose JS error is caught here, so the resolver gets a ctx whose
|
|
85
85
|
// systemDb is bound to its own savepoint: a failing statement rolls back only that.
|
|
86
|
-
// Statements through any other ctx handle (ctx.query, ctx.write) stay in the handler transaction.
|
|
86
|
+
// Statements through any other ctx handle (ctx.dispatch.query, ctx.dispatch.write) stay in the handler transaction.
|
|
87
87
|
async function resolveAdminRecipient(
|
|
88
88
|
opts: WaitlistOptions,
|
|
89
89
|
ctx: HandlerContext,
|
|
@@ -93,12 +93,17 @@ async function resolveAdminRecipient(
|
|
|
93
93
|
try {
|
|
94
94
|
return await runInSavepointIfSupported(systemDb.unsafeRaw(SUBMIT_LOCK_REASON), async (sp) => {
|
|
95
95
|
const savepointSystemDb = createSystemDbView(
|
|
96
|
-
createTenantDb({
|
|
96
|
+
createTenantDb({
|
|
97
|
+
db: sp,
|
|
98
|
+
tenantId,
|
|
99
|
+
mode: "system",
|
|
100
|
+
reason: "waitlist entries are system-tenant rows submitted by an anonymous guest",
|
|
101
|
+
}),
|
|
97
102
|
);
|
|
98
103
|
return (await opts.notifyRecipient?.({ ...ctx, systemDb: savepointSystemDb })) || null;
|
|
99
104
|
});
|
|
100
105
|
} catch (err) {
|
|
101
|
-
ctx.log?.warn("[waitlist:submit] admin notice recipient resolution failed", {
|
|
106
|
+
ctx.obs.log?.warn("[waitlist:submit] admin notice recipient resolution failed", {
|
|
102
107
|
error: err instanceof Error ? err.message : String(err),
|
|
103
108
|
});
|
|
104
109
|
return null;
|
|
@@ -116,7 +121,7 @@ async function sendBestEffort(
|
|
|
116
121
|
}
|
|
117
122
|
await send(ctx.notify);
|
|
118
123
|
} catch (err) {
|
|
119
|
-
ctx.log?.warn(`[waitlist:submit] ${label} mail failed`, {
|
|
124
|
+
ctx.obs.log?.warn(`[waitlist:submit] ${label} mail failed`, {
|
|
120
125
|
error: err instanceof Error ? err.message : String(err),
|
|
121
126
|
});
|
|
122
127
|
}
|
|
@@ -271,7 +271,12 @@ export async function migrateLegacyWaitlist(
|
|
|
271
271
|
continue;
|
|
272
272
|
}
|
|
273
273
|
await transaction(db, async (tx) => {
|
|
274
|
-
const tenantDb = createTenantDb({
|
|
274
|
+
const tenantDb = createTenantDb({
|
|
275
|
+
db: tx,
|
|
276
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
277
|
+
mode: "system",
|
|
278
|
+
reason: "legacy waitlist import writes system-tenant rows",
|
|
279
|
+
});
|
|
275
280
|
const outcome = await importWaitlistEntry(tenantDb, entry, { dryRun: options.dryRun });
|
|
276
281
|
if (outcome === "created") created += 1;
|
|
277
282
|
else skipped += 1;
|
|
@@ -69,7 +69,13 @@ export const linkWaitlistOnSignup: MultiStreamApplyFn = async (event, tx) => {
|
|
|
69
69
|
// skip: another membership came first.
|
|
70
70
|
if (first && first.id !== event.aggregateId) return;
|
|
71
71
|
|
|
72
|
-
const db = createTenantDb({
|
|
72
|
+
const db = createTenantDb({
|
|
73
|
+
db: tx,
|
|
74
|
+
tenantId: SYSTEM_TENANT_ID,
|
|
75
|
+
mode: "system",
|
|
76
|
+
reason:
|
|
77
|
+
"linking a signup to its waitlist entry updates system-tenant rows from a membership event",
|
|
78
|
+
});
|
|
73
79
|
const user = await fetchOne<{ email?: unknown }>(tx, userTable, { id: membership.userId });
|
|
74
80
|
// skip: user row unreadable (erased or not projected yet).
|
|
75
81
|
if (typeof user?.email !== "string") return;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.390.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Woken workflow runs are due on the next resume tick",
|
|
6
|
+
"detail": "The event subscriber stamped wakeAt with the app clock, while the resume job selects due rows with the database clock. With an app clock ahead of the database, a woken row was not due yet and waited for a later tick. wakeAt is now set with the database clock, like the due-runs query."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.370.0",
|
|
4
10
|
"type": "breaking",
|
|
@@ -8,7 +8,7 @@ export type DueRow = { readonly runId: string; readonly stepIndex: number };
|
|
|
8
8
|
export const DUE_RUNS_BATCH_SIZE = 100;
|
|
9
9
|
|
|
10
10
|
// Dedup happens solely via resume-run's VersionConflictError-checked claim
|
|
11
|
-
// (ctx.
|
|
11
|
+
// (ctx.events.tryAppend on WORKFLOW_RESUMED) — a second dispatch for the same
|
|
12
12
|
// row loses that race and no-ops. No row locks: in an autocommit statement
|
|
13
13
|
// they would be released right after the SELECT and guard nothing.
|
|
14
14
|
// `now()` stays the DB clock so job-host clock skew cannot mark rows due early.
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// existing resume-due-runs job picks the row up on its next
|
|
24
24
|
// cron tick — the resume path remains unchanged.
|
|
25
25
|
|
|
26
|
-
import { selectMany, updateMany } from "@cosmicdrift/kumiko-framework/db";
|
|
26
|
+
import { selectMany, sql, updateMany } from "@cosmicdrift/kumiko-framework/db";
|
|
27
27
|
import type {
|
|
28
28
|
FeatureRegistrar,
|
|
29
29
|
MultiStreamApplyFn,
|
|
@@ -32,7 +32,6 @@ import type {
|
|
|
32
32
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
33
33
|
import { evaluateEventMatch } from "@cosmicdrift/kumiko-framework/engine";
|
|
34
34
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
35
|
-
import { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
36
35
|
import type { TriggerEventRef } from "./runner.ts";
|
|
37
36
|
import { workflowRunPendingTable } from "./tables.ts";
|
|
38
37
|
|
|
@@ -92,7 +91,9 @@ const wakeupApply: MultiStreamApplyFn = async (event, tx) => {
|
|
|
92
91
|
aggregateId: event.aggregateId,
|
|
93
92
|
version: event.version,
|
|
94
93
|
} satisfies TriggerEventRef,
|
|
95
|
-
|
|
94
|
+
// DB clock, like the due comparison in due-runs.ts: an app-clock stamp ahead of the
|
|
95
|
+
// database clock would leave the row not-yet-due for the next resume tick.
|
|
96
|
+
wakeAt: sql`now()`,
|
|
96
97
|
},
|
|
97
98
|
{ tenantId: row.tenantId, runId: row.runId, stepIndex: row.stepIndex },
|
|
98
99
|
);
|
|
@@ -144,7 +144,8 @@ export function registerEventTrigger(r: FeatureRegistrar, workflow: WorkflowDefi
|
|
|
144
144
|
version: event.version,
|
|
145
145
|
},
|
|
146
146
|
...(idempotencyKey && { idempotencyKey }),
|
|
147
|
-
|
|
147
|
+
// The MSP context is still flat; the runner reads the namespaced shape.
|
|
148
|
+
handlerCtx: { events: { unsafeAppend: ctx.unsafeAppendEvent } },
|
|
148
149
|
});
|
|
149
150
|
} catch (error) {
|
|
150
151
|
// Failures outside the step loop (run-started append) have no step yet, so 0.
|
|
@@ -76,7 +76,7 @@ export function createWorkflowRunnerFeature() {
|
|
|
76
76
|
|
|
77
77
|
for (const row of dueRows) {
|
|
78
78
|
try {
|
|
79
|
-
await ctx.write("workflow-runner:write:resume-run", {
|
|
79
|
+
await ctx.dispatch.write("workflow-runner:write:resume-run", {
|
|
80
80
|
runId: row.runId,
|
|
81
81
|
stepIndex: row.stepIndex,
|
|
82
82
|
});
|
|
@@ -84,7 +84,7 @@ export function createWorkflowRunnerFeature() {
|
|
|
84
84
|
// skip: one row's dispatch failing (infra hiccup, not a business
|
|
85
85
|
// outcome — resume-run itself never throws for expected cases)
|
|
86
86
|
// must not stop the rest of this tenant's due rows from resuming.
|
|
87
|
-
ctx.log.warn(
|
|
87
|
+
ctx.obs.log.warn(
|
|
88
88
|
`[workflow-runner:resume-due-runs] runId=${row.runId} stepIndex=${row.stepIndex} dispatch failed: ${String(error)}`,
|
|
89
89
|
);
|
|
90
90
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// 1. Fingerprint check FIRST — before claiming, before any pipeline
|
|
8
8
|
// work. A changed workflow definition fails loud (WORKFLOW_RUN_FAILED,
|
|
9
9
|
// reason "workflow_definition_changed"), never a silent skip.
|
|
10
|
-
// 2. Claim via a savepoint-scoped WORKFLOW_RESUMED append (ctx.
|
|
10
|
+
// 2. Claim via a savepoint-scoped WORKFLOW_RESUMED append (ctx.events.tryAppend,
|
|
11
11
|
// not unsafeAppendEvent — a losing VersionConflict must not poison this
|
|
12
12
|
// handler's own transaction). A losing claim means another worker beat
|
|
13
13
|
// us to this row; silent no-op.
|
|
@@ -78,7 +78,7 @@ async function appendRunFailed(
|
|
|
78
78
|
runId: string,
|
|
79
79
|
failedPayload: WorkflowRunFailedPayload,
|
|
80
80
|
): Promise<void> {
|
|
81
|
-
await ctx.
|
|
81
|
+
await ctx.events.unsafeAppend({
|
|
82
82
|
aggregateId: runId,
|
|
83
83
|
aggregateType: WORKFLOW_AGGREGATE_TYPE,
|
|
84
84
|
type: WORKFLOW_RUN_FAILED_TYPE,
|
|
@@ -176,7 +176,7 @@ function readResumedPayload(payload: unknown): {
|
|
|
176
176
|
// mistake attempt 1's WORKFLOW_RESUMED for attempt 2's and skip the second
|
|
177
177
|
// retry entirely.
|
|
178
178
|
function isRunAlreadySettled(
|
|
179
|
-
runEvents: Awaited<ReturnType<HandlerContext["
|
|
179
|
+
runEvents: Awaited<ReturnType<HandlerContext["events"]["load"]>>,
|
|
180
180
|
stepIndex: number,
|
|
181
181
|
expectedRetryAttempt: number | undefined,
|
|
182
182
|
): boolean {
|
|
@@ -192,7 +192,7 @@ function isRunAlreadySettled(
|
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
-
type LoadedEvents = Awaited<ReturnType<HandlerContext["
|
|
195
|
+
type LoadedEvents = Awaited<ReturnType<HandlerContext["events"]["load"]>>;
|
|
196
196
|
|
|
197
197
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
198
198
|
return typeof value === "object" && value !== null;
|
|
@@ -229,7 +229,7 @@ function readTriggerSource(startedPayload: unknown): TriggerSource | null {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
async function loadReferencedEvent(ctx: HandlerContext, ref: TriggerEventRef) {
|
|
232
|
-
const events = await ctx.
|
|
232
|
+
const events = await ctx.events.load(ref.aggregateId, { atVersion: ref.version });
|
|
233
233
|
return events.find((e) => e.id === ref.eventId);
|
|
234
234
|
}
|
|
235
235
|
|
|
@@ -385,7 +385,7 @@ async function runResumedPipeline(ctx: HandlerContext, input: ResumedPipelineInp
|
|
|
385
385
|
workflowName,
|
|
386
386
|
stepIndex: steps.length,
|
|
387
387
|
};
|
|
388
|
-
await ctx.
|
|
388
|
+
await ctx.events.unsafeAppend({
|
|
389
389
|
aggregateId: runId,
|
|
390
390
|
aggregateType: WORKFLOW_AGGREGATE_TYPE,
|
|
391
391
|
type: WORKFLOW_RUN_COMPLETED_TYPE,
|
|
@@ -448,7 +448,7 @@ export const resumeRunHandler: WriteHandlerDef = {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
const { workflowName } = pending;
|
|
451
|
-
const runEvents = await ctx.
|
|
451
|
+
const runEvents = await ctx.events.load(runId);
|
|
452
452
|
|
|
453
453
|
// Before the workflow resolution: a racing tick on an already-settled run
|
|
454
454
|
// must not append run-failed after run-completed when the definition has
|
|
@@ -469,7 +469,7 @@ export const resumeRunHandler: WriteHandlerDef = {
|
|
|
469
469
|
}
|
|
470
470
|
const workflow = resolved.workflow;
|
|
471
471
|
|
|
472
|
-
const claim = await ctx.
|
|
472
|
+
const claim = await ctx.events.tryAppend({
|
|
473
473
|
aggregateId: runId,
|
|
474
474
|
aggregateType: WORKFLOW_AGGREGATE_TYPE,
|
|
475
475
|
type: WORKFLOW_RESUMED_TYPE,
|
|
@@ -75,7 +75,9 @@ export type WorkflowRunFailedPayload = {
|
|
|
75
75
|
readonly reason?: string;
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
export type WorkflowRunContext =
|
|
78
|
+
export type WorkflowRunContext = {
|
|
79
|
+
readonly events: Pick<HandlerContext["events"], "unsafeAppend">;
|
|
80
|
+
};
|
|
79
81
|
|
|
80
82
|
export class WorkflowSuspensionUnsupportedError extends Error {
|
|
81
83
|
constructor(
|
|
@@ -116,7 +118,7 @@ export async function startAndRunWorkflow(args: {
|
|
|
116
118
|
...(args.idempotencyKey && { idempotencyKey: args.idempotencyKey }),
|
|
117
119
|
};
|
|
118
120
|
|
|
119
|
-
await args.handlerCtx.
|
|
121
|
+
await args.handlerCtx.events.unsafeAppend({
|
|
120
122
|
aggregateId: args.runId,
|
|
121
123
|
aggregateType: WORKFLOW_AGGREGATE_TYPE,
|
|
122
124
|
type: WORKFLOW_RUN_STARTED_TYPE,
|
|
@@ -127,7 +129,7 @@ export async function startAndRunWorkflow(args: {
|
|
|
127
129
|
|
|
128
130
|
// @cast-boundary msp-to-handler-ctx — event-trigger hands over the narrower
|
|
129
131
|
// MultiStreamApplyContext (no query/write/writeAs). The step vocabulary this
|
|
130
|
-
// runner drives (wait, compute, return) only ever calls
|
|
132
|
+
// runner drives (wait, compute, return) only ever calls events.unsafeAppend.
|
|
131
133
|
const stepCtx = args.handlerCtx as unknown as HandlerContext;
|
|
132
134
|
const outcome = await runStepList(
|
|
133
135
|
steps,
|
|
@@ -158,7 +160,7 @@ export async function startAndRunWorkflow(args: {
|
|
|
158
160
|
stepIndex: steps.length,
|
|
159
161
|
};
|
|
160
162
|
|
|
161
|
-
await args.handlerCtx.
|
|
163
|
+
await args.handlerCtx.events.unsafeAppend({
|
|
162
164
|
aggregateId: args.runId,
|
|
163
165
|
aggregateType: WORKFLOW_AGGREGATE_TYPE,
|
|
164
166
|
type: WORKFLOW_RUN_COMPLETED_TYPE,
|
|
@@ -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": {
|