@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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-samples",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.390.1",
|
|
4
4
|
"description": "Source trees of the Kumiko sample recipes, sample apps and bundled features in repo layout, for tooling such as few-shot corpus builds.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-bundled-features",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.390.1",
|
|
4
4
|
"description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
"runtime": "runtime",
|
|
19
19
|
"bunOnlyExports": [
|
|
20
20
|
"./template-resolver/testing",
|
|
21
|
-
"./tenant-lifecycle/testing"
|
|
21
|
+
"./tenant-lifecycle/testing",
|
|
22
|
+
"./tenant/testing",
|
|
23
|
+
"./user/testing"
|
|
22
24
|
]
|
|
23
25
|
},
|
|
24
26
|
"exports": {
|
|
@@ -101,12 +103,14 @@
|
|
|
101
103
|
"./user-profile/web": "./src/user-profile/web/index.ts",
|
|
102
104
|
"./auth-email-password": "./src/auth-email-password/index.ts",
|
|
103
105
|
"./auth-email-password/constants": "./src/auth-email-password/web/constants.ts",
|
|
106
|
+
"./auth-email-password/return-path": "./src/auth-email-password/web/return-path.ts",
|
|
104
107
|
"./auth-email-password/email-templates": "./src/auth-email-password/email-templates.ts",
|
|
105
108
|
"./auth-email-password/seeding": "./src/auth-email-password/seeding.ts",
|
|
106
109
|
"./auth-email-password/testing": "./src/auth-email-password/testing.ts",
|
|
107
110
|
"./auth-email-password/web": "./src/auth-email-password/web/index.ts",
|
|
108
111
|
"./shared/single-use-token-store": "./src/shared/single-use-token-store.ts",
|
|
109
112
|
"./shared/signed-token": "./src/shared/signed-token.ts",
|
|
113
|
+
"./shared/stream-tenant-backfill": "./src/shared/stream-tenant-backfill.ts",
|
|
110
114
|
"./shared/row-bound-grant": "./src/shared/row-bound-grant.ts",
|
|
111
115
|
"./shared/mail-template-body": "./src/shared/mail-template-body.ts",
|
|
112
116
|
"./delivery": "./src/delivery/index.ts",
|
|
@@ -160,27 +164,27 @@
|
|
|
160
164
|
"@cosmicdrift/kumiko-types": "workspace:*",
|
|
161
165
|
"@mollie/api-client": "^4.5.0",
|
|
162
166
|
"@node-rs/argon2": "^2.0.2",
|
|
163
|
-
"@tiptap/core": "3.31.3",
|
|
164
|
-
"@tiptap/extension-link": "3.31.3",
|
|
165
|
-
"@tiptap/extension-list": "3.31.3",
|
|
166
|
-
"@tiptap/extension-table": "3.31.3",
|
|
167
|
-
"@tiptap/html": "3.31.3",
|
|
168
|
-
"@tiptap/extensions": "3.31.3",
|
|
169
|
-
"@tiptap/pm": "3.31.3",
|
|
170
|
-
"@tiptap/react": "3.31.3",
|
|
171
|
-
"@tiptap/starter-kit": "3.31.3",
|
|
167
|
+
"@tiptap/core": "^3.31.3",
|
|
168
|
+
"@tiptap/extension-link": "^3.31.3",
|
|
169
|
+
"@tiptap/extension-list": "^3.31.3",
|
|
170
|
+
"@tiptap/extension-table": "^3.31.3",
|
|
171
|
+
"@tiptap/html": "^3.31.3",
|
|
172
|
+
"@tiptap/extensions": "^3.31.3",
|
|
173
|
+
"@tiptap/pm": "^3.31.3",
|
|
174
|
+
"@tiptap/react": "^3.31.3",
|
|
175
|
+
"@tiptap/starter-kit": "^3.31.3",
|
|
172
176
|
"@types/mailparser": "^3.4.6",
|
|
173
177
|
"@types/qrcode": "^1.5.5",
|
|
174
178
|
"clsx": "^2.1.1",
|
|
175
|
-
"happy-dom": "^
|
|
179
|
+
"happy-dom": "^21.0.4",
|
|
176
180
|
"hono": "^4.13.1",
|
|
177
181
|
"imapflow": "^1.7.8",
|
|
178
182
|
"ioredis": "^5.10.1",
|
|
179
183
|
"jose": "^6.2.3",
|
|
180
184
|
"lucide-react": "^1.14.0",
|
|
181
|
-
"mailparser": "^3.
|
|
185
|
+
"mailparser": "^3.12.4",
|
|
182
186
|
"marked": "^18.0.3",
|
|
183
|
-
"nodemailer": "^10.
|
|
187
|
+
"nodemailer": "^10.2.1",
|
|
184
188
|
"pdf-lib": "^1.17.1",
|
|
185
189
|
"qrcode": "^1.5.4",
|
|
186
190
|
"react": "^19.2.6",
|
|
@@ -190,7 +194,7 @@
|
|
|
190
194
|
"tailwind-merge": "^3.6.0",
|
|
191
195
|
"temporal-polyfill": "^0.3.2",
|
|
192
196
|
"uuid": "^14.0.0",
|
|
193
|
-
"zod": "^4.
|
|
197
|
+
"zod": "^4.6.7"
|
|
194
198
|
},
|
|
195
199
|
"publishConfig": {
|
|
196
200
|
"registry": "https://registry.npmjs.org",
|
|
@@ -516,6 +520,10 @@
|
|
|
516
520
|
"types": "./dist/auth-email-password/web/constants.d.ts",
|
|
517
521
|
"default": "./dist/auth-email-password/web/constants.js"
|
|
518
522
|
},
|
|
523
|
+
"./auth-email-password/return-path": {
|
|
524
|
+
"types": "./dist/auth-email-password/web/return-path.d.ts",
|
|
525
|
+
"default": "./dist/auth-email-password/web/return-path.js"
|
|
526
|
+
},
|
|
519
527
|
"./auth-email-password/seeding": {
|
|
520
528
|
"types": "./dist/auth-email-password/seeding.d.ts",
|
|
521
529
|
"default": "./dist/auth-email-password/seeding.js"
|
|
@@ -536,6 +544,10 @@
|
|
|
536
544
|
"types": "./dist/shared/signed-token.d.ts",
|
|
537
545
|
"default": "./dist/shared/signed-token.js"
|
|
538
546
|
},
|
|
547
|
+
"./shared/stream-tenant-backfill": {
|
|
548
|
+
"types": "./dist/shared/stream-tenant-backfill.d.ts",
|
|
549
|
+
"default": "./dist/shared/stream-tenant-backfill.js"
|
|
550
|
+
},
|
|
539
551
|
"./shared/row-bound-grant": {
|
|
540
552
|
"types": "./dist/shared/row-bound-grant.d.ts",
|
|
541
553
|
"default": "./dist/shared/row-bound-grant.js"
|
|
@@ -93,7 +93,7 @@ export function createAuditFeature(): FeatureDefinition {
|
|
|
93
93
|
await runEscapeHatchRetention({
|
|
94
94
|
db: ctx.db.unsafeRaw(),
|
|
95
95
|
registry: ctx.registry,
|
|
96
|
-
configResolver: ctx.configResolver,
|
|
96
|
+
configResolver: ctx.services.configResolver,
|
|
97
97
|
userId: SYSTEM_USER_ID,
|
|
98
98
|
});
|
|
99
99
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Narrow cross-feature query results (ctx.queryAs → Promise<unknown>) into
|
|
1
|
+
// Narrow cross-feature query results (ctx.dispatch.queryAs → Promise<unknown>) into
|
|
2
2
|
// the shape the auth handlers actually read. Replaces a bare
|
|
3
3
|
// `as AuthUserRow | null` at the system boundary — the coding standard
|
|
4
4
|
// requires a TypeGuard in place of unchecked casts from unknown.
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
type TenantId,
|
|
20
20
|
type WriteResult,
|
|
21
21
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
22
|
+
import { runInSystemRowLevelSecurityScope } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
22
23
|
import type { Dispatcher } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
23
24
|
import type { EnvelopeCipher } from "@cosmicdrift/kumiko-framework/secrets";
|
|
24
25
|
import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
@@ -293,7 +294,18 @@ function isMailingOutcome(outcome: BootstrapInviteOutcome): boolean {
|
|
|
293
294
|
return outcome === "invited" || outcome === "resent";
|
|
294
295
|
}
|
|
295
296
|
|
|
296
|
-
|
|
297
|
+
// A one-shot operator process, not a tenant request: it looks up users, memberships and invitations across tenants.
|
|
298
|
+
export function bootstrapTenants(
|
|
299
|
+
deps: BootstrapDeps,
|
|
300
|
+
plan: BootstrapPlan,
|
|
301
|
+
): Promise<BootstrapReport> {
|
|
302
|
+
return runInSystemRowLevelSecurityScope(
|
|
303
|
+
"boot tenant bootstrap creates tenants and memberships before any tenant exists",
|
|
304
|
+
() => bootstrapTenantsInScope(deps, plan),
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async function bootstrapTenantsInScope(
|
|
297
309
|
deps: BootstrapDeps,
|
|
298
310
|
plan: BootstrapPlan,
|
|
299
311
|
): Promise<BootstrapReport> {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.390.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "isSafeReturnPath has its own light subpath",
|
|
6
|
+
"detail": "Importing @cosmicdrift/kumiko-bundled-features/auth-email-password/return-path gives isSafeReturnPath, readReturnToFromSearch, RETURN_TO_QUERY_PARAM and MAX_RETURN_PATH_LENGTH without loading the web barrel, for example in Astro pages that run without the Kumiko runtime."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.389.0",
|
|
4
10
|
"type": "improvement",
|
|
@@ -105,7 +105,7 @@ export type EmailVerificationOptions = {
|
|
|
105
105
|
// Brute-force protection on the login handler. Omit for the defaults
|
|
106
106
|
// (5 failures → 15-minute lock). Set the dial knobs to override.
|
|
107
107
|
//
|
|
108
|
-
// Storage: Redis (keyed by userId). Without ctx.redis the handler skips
|
|
108
|
+
// Storage: Redis (keyed by userId). Without ctx.services.redis the handler skips
|
|
109
109
|
// lockout entirely — login still works, but brute-force protection falls
|
|
110
110
|
// back to the IP-rate-limiter. Counter is monotonic: only a successful
|
|
111
111
|
// login resets it, so after a lockout expires the next wrong password
|
|
@@ -149,7 +149,7 @@ export type AuthEmailPasswordOptions = {
|
|
|
149
149
|
};
|
|
150
150
|
|
|
151
151
|
// Auth feature — email+password login. Depends on the user feature for
|
|
152
|
-
// identity lookups (via ctx.queryAs) and on the tenant feature for
|
|
152
|
+
// identity lookups (via ctx.dispatch.queryAs) and on the tenant feature for
|
|
153
153
|
// membership resolution. No direct imports of foreign tables.
|
|
154
154
|
export function createAuthEmailPasswordFeature(
|
|
155
155
|
opts: AuthEmailPasswordOptions = {},
|
|
@@ -10,7 +10,7 @@ import { invalidCredentials } from "../errors.ts";
|
|
|
10
10
|
import { passwordSchema } from "../password-policy.ts";
|
|
11
11
|
|
|
12
12
|
// Change-password — authenticated. The user supplies their current password
|
|
13
|
-
// (re-auth) and the new one. The new hash is written via ctx.writeAs(system)
|
|
13
|
+
// (re-auth) and the new one. The new hash is written via ctx.dispatch.writeAs(system)
|
|
14
14
|
// against the user feature's update handler; field-access on passwordHash
|
|
15
15
|
// (privileged-only) lets the system identity through.
|
|
16
16
|
export const changePasswordWrite = defineWriteHandler({
|
|
@@ -28,8 +28,8 @@ export const changePasswordWrite = defineWriteHandler({
|
|
|
28
28
|
escapeHatch: {
|
|
29
29
|
grants: ["systemIdentity"],
|
|
30
30
|
reason:
|
|
31
|
-
"Reads the caller's own passwordHash (privileged-only field) via ctx.queryAs(SYSTEM, ...) " +
|
|
32
|
-
"and writes the new hash via ctx.writeAs(SYSTEM, user:update) — the caller's own " +
|
|
31
|
+
"Reads the caller's own passwordHash (privileged-only field) via ctx.dispatch.queryAs(SYSTEM, ...) " +
|
|
32
|
+
"and writes the new hash via ctx.dispatch.writeAs(SYSTEM, user:update) — the caller's own " +
|
|
33
33
|
"identity can't reach either, field-access on passwordHash is privileged-only.",
|
|
34
34
|
},
|
|
35
35
|
// Payload carries the old and new password.
|
|
@@ -40,7 +40,7 @@ export const changePasswordWrite = defineWriteHandler({
|
|
|
40
40
|
const systemUser = createSystemUser(event.user.tenantId);
|
|
41
41
|
|
|
42
42
|
// Load self with passwordHash — only visible to the privileged caller.
|
|
43
|
-
const me = (await ctx.queryAs(systemUser, UserQueries.findForAuth, {
|
|
43
|
+
const me = (await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
|
|
44
44
|
id: event.user.id,
|
|
45
45
|
})) as { id: number; passwordHash: string | null; version: number } | null; // @cast-boundary db-runner
|
|
46
46
|
|
|
@@ -56,14 +56,14 @@ export const changePasswordWrite = defineWriteHandler({
|
|
|
56
56
|
const newHash = await hashPassword(event.payload.newPassword);
|
|
57
57
|
|
|
58
58
|
// The user aggregate is systemStream: its event stream lives on
|
|
59
|
-
// SYSTEM_TENANT_ID deterministically. Previously scattered streams
|
|
60
|
-
//
|
|
59
|
+
// SYSTEM_TENANT_ID deterministically. Previously scattered streams are moved by the
|
|
60
|
+
// boot-time stream-tenant backfill (runStreamTenantBackfillOnBoot).
|
|
61
61
|
const writer = createSystemUser(SYSTEM_TENANT_ID);
|
|
62
62
|
|
|
63
63
|
// Apply via user feature's update handler — writeAs(system) satisfies
|
|
64
64
|
// the privileged-only write rule on passwordHash. Pass the current version
|
|
65
65
|
// through so optimistic locking still applies end-to-end.
|
|
66
|
-
const writeRes = await ctx.writeAs(writer, UserHandlers.update, {
|
|
66
|
+
const writeRes = await ctx.dispatch.writeAs(writer, UserHandlers.update, {
|
|
67
67
|
id: me.id,
|
|
68
68
|
version: me.version,
|
|
69
69
|
changes: { passwordHash: newHash },
|
package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts
CHANGED
|
@@ -40,10 +40,10 @@ export function createConfirmAccountUnlockHandler(opts: ConfirmAccountUnlockOpti
|
|
|
40
40
|
}),
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
|
-
if (!ctx.redis) {
|
|
43
|
+
if (!ctx.services.redis) {
|
|
44
44
|
return writeFailure(
|
|
45
45
|
new InternalError({
|
|
46
|
-
message: "account-unlock requires ctx.redis to clear lockout state",
|
|
46
|
+
message: "account-unlock requires ctx.services.redis to clear lockout state",
|
|
47
47
|
}),
|
|
48
48
|
);
|
|
49
49
|
}
|
|
@@ -54,7 +54,7 @@ export function createConfirmAccountUnlockHandler(opts: ConfirmAccountUnlockOpti
|
|
|
54
54
|
const verify = verifyUnlockToken(event.payload.token, opts.hmacSecret);
|
|
55
55
|
if (!verify.ok) return invalidUnlockToken();
|
|
56
56
|
|
|
57
|
-
await clearLockoutState(ctx.redis, verify.userId);
|
|
57
|
+
await clearLockoutState(ctx.services.redis, verify.userId);
|
|
58
58
|
return { isSuccess: true, data: { kind: "account-unlocked" as const } };
|
|
59
59
|
},
|
|
60
60
|
});
|
|
@@ -42,7 +42,7 @@ export type ConfirmTokenFlowSpec<TSuccessData> = {
|
|
|
42
42
|
// other token flows — "reset" vs "verify" keeps cross-flow replay
|
|
43
43
|
// impossible at both layers (HMAC-purpose AND burn-purpose).
|
|
44
44
|
readonly purpose: string;
|
|
45
|
-
// Used verbatim in the 5xx body when ctx.redis is missing — the feature
|
|
45
|
+
// Used verbatim in the 5xx body when ctx.services.redis is missing — the feature
|
|
46
46
|
// is misconfigured, not the caller's fault.
|
|
47
47
|
readonly redisRequiredMessage: string;
|
|
48
48
|
// Standard failure returned for every "token can't be consumed" path
|
|
@@ -74,10 +74,10 @@ export async function runConfirmTokenFlow<TSuccessData>(
|
|
|
74
74
|
reason:
|
|
75
75
|
"writes the confirming user's row via UserHandlers.update on the caller's handler context, using a SYSTEM_TENANT-scoped identity; the calling handler declares its own escapeHatch",
|
|
76
76
|
});
|
|
77
|
-
if (!ctx.redis) {
|
|
77
|
+
if (!ctx.services.redis) {
|
|
78
78
|
return writeFailure(new InternalError({ message: spec.redisRequiredMessage }));
|
|
79
79
|
}
|
|
80
|
-
const redis: Redis = ctx.redis;
|
|
80
|
+
const redis: Redis = ctx.services.redis;
|
|
81
81
|
|
|
82
82
|
const burn = await burnToken(redis, spec.purpose, userId, expiresAtMs);
|
|
83
83
|
if (burn === "already-used") return spec.invalidToken();
|
|
@@ -99,7 +99,7 @@ export async function runConfirmTokenFlow<TSuccessData>(
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const changes = await spec.buildChanges(me);
|
|
102
|
-
const writeRes = await ctx.writeAs(systemUser, UserHandlers.update, {
|
|
102
|
+
const writeRes = await ctx.dispatch.writeAs(systemUser, UserHandlers.update, {
|
|
103
103
|
id: me.id,
|
|
104
104
|
version: me.version,
|
|
105
105
|
changes,
|
|
@@ -141,7 +141,7 @@ async function loadValidatedUser(
|
|
|
141
141
|
"reads the confirming user's row via UserQueries.findForAuth on the caller's handler context, using a SYSTEM_TENANT-scoped identity; the calling handler declares its own escapeHatch",
|
|
142
142
|
});
|
|
143
143
|
const me = parseAuthUserRow(
|
|
144
|
-
await ctx.queryAs(systemUser, UserQueries.findForAuth, { id: userId }),
|
|
144
|
+
await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, { id: userId }),
|
|
145
145
|
);
|
|
146
146
|
if (!me || me.isDeleted || me.version === undefined || me.version < 1) return null;
|
|
147
147
|
return { ...me, version: me.version };
|
package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts
CHANGED
|
@@ -133,16 +133,16 @@ export function createInviteAcceptWithLoginHandler(opts: InviteAcceptWithLoginOp
|
|
|
133
133
|
agent: { expose: false },
|
|
134
134
|
// kumiko-lint-ignore complexity-budget reuses login.write.ts's gate chain (lockout/password/email/status/membership/mfa) plus invite-specific branches (email match, already-member check, invitation update, unburn-on-failure) — splitting would scatter gate order across functions without reducing risk
|
|
135
135
|
handler: async (event, ctx) => {
|
|
136
|
-
if (!ctx.redis) {
|
|
136
|
+
if (!ctx.services.redis) {
|
|
137
137
|
return writeFailure(
|
|
138
|
-
new InternalError({ message: "invite-accept-with-login requires ctx.redis" }),
|
|
138
|
+
new InternalError({ message: "invite-accept-with-login requires ctx.services.redis" }),
|
|
139
139
|
);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
const invitationId = await getInvitationIdForToken(ctx.redis, event.payload.token);
|
|
142
|
+
const invitationId = await getInvitationIdForToken(ctx.services.redis, event.payload.token);
|
|
143
143
|
if (!invitationId) return invalidInviteToken();
|
|
144
144
|
|
|
145
|
-
const burn = await burnInviteToken(ctx.redis, event.payload.token);
|
|
145
|
+
const burn = await burnInviteToken(ctx.services.redis, event.payload.token);
|
|
146
146
|
if (burn === "already-used") return invalidInviteToken();
|
|
147
147
|
|
|
148
148
|
type InvitationRow = {
|
|
@@ -242,6 +242,8 @@ export function createInviteAcceptWithLoginHandler(opts: InviteAcceptWithLoginOp
|
|
|
242
242
|
db: dbConn,
|
|
243
243
|
tenantId: invitationTenantId,
|
|
244
244
|
mode: "system",
|
|
245
|
+
reason:
|
|
246
|
+
"invitation tenant differs from the caller's tenant, the accept step updates the invitation row there",
|
|
245
247
|
});
|
|
246
248
|
const updateResult = await invitationExecutor.update(
|
|
247
249
|
{
|
|
@@ -254,7 +256,7 @@ export function createInviteAcceptWithLoginHandler(opts: InviteAcceptWithLoginOp
|
|
|
254
256
|
);
|
|
255
257
|
if (!updateResult.isSuccess) return updateResult;
|
|
256
258
|
|
|
257
|
-
await deleteInviteToken(ctx.redis, { invitationId, token: event.payload.token });
|
|
259
|
+
await deleteInviteToken(ctx.services.redis, { invitationId, token: event.payload.token });
|
|
258
260
|
|
|
259
261
|
// buildSessionRoles calls stripForbiddenMembershipRoles internally —
|
|
260
262
|
// a reserved role on an existing membership must never reach the session.
|
|
@@ -296,8 +298,8 @@ export function createInviteAcceptWithLoginHandler(opts: InviteAcceptWithLoginOp
|
|
|
296
298
|
},
|
|
297
299
|
};
|
|
298
300
|
} finally {
|
|
299
|
-
if (!committed && ctx.redis) {
|
|
300
|
-
await unburnInviteToken(ctx.redis, event.payload.token);
|
|
301
|
+
if (!committed && ctx.services.redis) {
|
|
302
|
+
await unburnInviteToken(ctx.services.redis, event.payload.token);
|
|
301
303
|
}
|
|
302
304
|
}
|
|
303
305
|
},
|
|
@@ -94,16 +94,18 @@ export function createInviteAcceptHandler() {
|
|
|
94
94
|
},
|
|
95
95
|
// kumiko-lint-ignore complexity-budget invite branches (auth/anon/burn) stay in one handler
|
|
96
96
|
handler: async (event, ctx) => {
|
|
97
|
-
if (!ctx.redis) {
|
|
97
|
+
if (!ctx.services.redis) {
|
|
98
98
|
return writeFailure(
|
|
99
|
-
new InternalError({
|
|
99
|
+
new InternalError({
|
|
100
|
+
message: "invite-accept requires ctx.services.redis for token consumption",
|
|
101
|
+
}),
|
|
100
102
|
);
|
|
101
103
|
}
|
|
102
104
|
|
|
103
|
-
const invitationId = await getInvitationIdForToken(ctx.redis, event.payload.token);
|
|
105
|
+
const invitationId = await getInvitationIdForToken(ctx.services.redis, event.payload.token);
|
|
104
106
|
if (!invitationId) return invalidInviteToken();
|
|
105
107
|
|
|
106
|
-
const burn = await burnInviteToken(ctx.redis, event.payload.token);
|
|
108
|
+
const burn = await burnInviteToken(ctx.services.redis, event.payload.token);
|
|
107
109
|
if (burn === "already-used") return invalidInviteToken();
|
|
108
110
|
|
|
109
111
|
type InvitationRow = {
|
|
@@ -177,6 +179,8 @@ export function createInviteAcceptHandler() {
|
|
|
177
179
|
db: dbConn,
|
|
178
180
|
tenantId: invitationTenantId,
|
|
179
181
|
mode: "system",
|
|
182
|
+
reason:
|
|
183
|
+
"invitation tenant differs from the caller's tenant, the accept step updates the invitation row there",
|
|
180
184
|
});
|
|
181
185
|
const updateResult = await invitationExecutor.update(
|
|
182
186
|
{
|
|
@@ -189,7 +193,7 @@ export function createInviteAcceptHandler() {
|
|
|
189
193
|
);
|
|
190
194
|
if (!updateResult.isSuccess) return updateResult;
|
|
191
195
|
|
|
192
|
-
await deleteInviteToken(ctx.redis, { invitationId, token: event.payload.token });
|
|
196
|
+
await deleteInviteToken(ctx.services.redis, { invitationId, token: event.payload.token });
|
|
193
197
|
|
|
194
198
|
committed = true;
|
|
195
199
|
return {
|
|
@@ -202,8 +206,8 @@ export function createInviteAcceptHandler() {
|
|
|
202
206
|
},
|
|
203
207
|
};
|
|
204
208
|
} finally {
|
|
205
|
-
if (!committed && ctx.redis) {
|
|
206
|
-
await unburnInviteToken(ctx.redis, event.payload.token);
|
|
209
|
+
if (!committed && ctx.services.redis) {
|
|
210
|
+
await unburnInviteToken(ctx.services.redis, event.payload.token);
|
|
207
211
|
}
|
|
208
212
|
}
|
|
209
213
|
},
|
|
@@ -168,9 +168,9 @@ async function issueInvitation(
|
|
|
168
168
|
ctx: HandlerContext,
|
|
169
169
|
refuseAccepted = false,
|
|
170
170
|
): Promise<WriteResult<InviteCreateData>> {
|
|
171
|
-
if (!ctx.redis) {
|
|
171
|
+
if (!ctx.services.redis) {
|
|
172
172
|
return writeFailure(
|
|
173
|
-
new InternalError({ message: "invite-create requires ctx.redis for token store" }),
|
|
173
|
+
new InternalError({ message: "invite-create requires ctx.services.redis for token store" }),
|
|
174
174
|
);
|
|
175
175
|
}
|
|
176
176
|
|
|
@@ -197,7 +197,7 @@ async function issueInvitation(
|
|
|
197
197
|
const existingVersion = existing["version"] as number; // @cast-boundary db-row
|
|
198
198
|
// At most one live invite token per invitation: invalidate
|
|
199
199
|
// whatever's there before minting the new one.
|
|
200
|
-
await invalidateExistingInviteToken(ctx.redis, invitationId);
|
|
200
|
+
await invalidateExistingInviteToken(ctx.services.redis, invitationId);
|
|
201
201
|
|
|
202
202
|
const updateResult = await executor.update(
|
|
203
203
|
{
|
|
@@ -237,7 +237,7 @@ async function issueInvitation(
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
const token = generateToken();
|
|
240
|
-
await storeInviteToken(ctx.redis, { invitationId, token, ttlSeconds });
|
|
240
|
+
await storeInviteToken(ctx.services.redis, { invitationId, token, ttlSeconds });
|
|
241
241
|
|
|
242
242
|
const locale = request.locale ?? resolveHandlerMailLocale(ctx, opts.locale);
|
|
243
243
|
|
|
@@ -52,9 +52,9 @@ export const inviteInfoQuery = defineQueryHandler({
|
|
|
52
52
|
"Anonymous, read-only invite lookup for the invite-acceptance page: reveals the invited " +
|
|
53
53
|
"email and whether an account already exists for it, without consuming the token.",
|
|
54
54
|
handler: async (query, ctx) => {
|
|
55
|
-
if (!ctx.redis) throwInvalidInviteToken();
|
|
55
|
+
if (!ctx.services.redis) throwInvalidInviteToken();
|
|
56
56
|
|
|
57
|
-
const invitationId = await getInvitationIdForToken(ctx.redis, query.payload.token);
|
|
57
|
+
const invitationId = await getInvitationIdForToken(ctx.services.redis, query.payload.token);
|
|
58
58
|
if (!invitationId) throwInvalidInviteToken();
|
|
59
59
|
|
|
60
60
|
type InvitationRow = {
|
package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts
CHANGED
|
@@ -103,16 +103,16 @@ export function createInviteSignupCompleteHandler(opts: InviteSignupCompleteOpti
|
|
|
103
103
|
reason: INVITE_SIGNUP_COMPLETE_ESCAPE_HATCH_REASON,
|
|
104
104
|
},
|
|
105
105
|
handler: async (event, ctx) => {
|
|
106
|
-
if (!ctx.redis) {
|
|
106
|
+
if (!ctx.services.redis) {
|
|
107
107
|
return writeFailure(
|
|
108
|
-
new InternalError({ message: "invite-signup-complete requires ctx.redis" }),
|
|
108
|
+
new InternalError({ message: "invite-signup-complete requires ctx.services.redis" }),
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
const invitationId = await getInvitationIdForToken(ctx.redis, event.payload.token);
|
|
112
|
+
const invitationId = await getInvitationIdForToken(ctx.services.redis, event.payload.token);
|
|
113
113
|
if (!invitationId) return invalidInviteToken();
|
|
114
114
|
|
|
115
|
-
const burn = await burnInviteToken(ctx.redis, event.payload.token);
|
|
115
|
+
const burn = await burnInviteToken(ctx.services.redis, event.payload.token);
|
|
116
116
|
if (burn === "already-used") return invalidInviteToken();
|
|
117
117
|
|
|
118
118
|
type InvitationRow = {
|
|
@@ -187,6 +187,8 @@ export function createInviteSignupCompleteHandler(opts: InviteSignupCompleteOpti
|
|
|
187
187
|
db: dbConn,
|
|
188
188
|
tenantId: invitationTenantId,
|
|
189
189
|
mode: "system",
|
|
190
|
+
reason:
|
|
191
|
+
"invitation tenant differs from the caller's tenant, the accept step updates the invitation row there",
|
|
190
192
|
});
|
|
191
193
|
const updateResult = await invitationExecutor.update(
|
|
192
194
|
{
|
|
@@ -214,7 +216,7 @@ export function createInviteSignupCompleteHandler(opts: InviteSignupCompleteOpti
|
|
|
214
216
|
mergedRoles,
|
|
215
217
|
);
|
|
216
218
|
|
|
217
|
-
await deleteInviteToken(ctx.redis, { invitationId, token: event.payload.token });
|
|
219
|
+
await deleteInviteToken(ctx.services.redis, { invitationId, token: event.payload.token });
|
|
218
220
|
|
|
219
221
|
if (mfaGate !== undefined) {
|
|
220
222
|
committed = true;
|
|
@@ -239,8 +241,8 @@ export function createInviteSignupCompleteHandler(opts: InviteSignupCompleteOpti
|
|
|
239
241
|
},
|
|
240
242
|
};
|
|
241
243
|
} finally {
|
|
242
|
-
if (!committed && ctx.redis) {
|
|
243
|
-
await unburnInviteToken(ctx.redis, event.payload.token);
|
|
244
|
+
if (!committed && ctx.services.redis) {
|
|
245
|
+
await unburnInviteToken(ctx.services.redis, event.payload.token);
|
|
244
246
|
}
|
|
245
247
|
}
|
|
246
248
|
},
|
|
@@ -35,7 +35,7 @@ export type LoginHandlerOptions = {
|
|
|
35
35
|
readonly strictEmailVerification?: boolean;
|
|
36
36
|
// Brute-force protection: after N wrong-password attempts the account
|
|
37
37
|
// locks for the configured duration. State lives in Redis (see
|
|
38
|
-
// lockout-store.ts) — if ctx.redis is unset, lockout is skipped and the
|
|
38
|
+
// lockout-store.ts) — if ctx.services.redis is unset, lockout is skipped and the
|
|
39
39
|
// handler falls back to classic invalid-credentials. Counter is monotonic
|
|
40
40
|
// and only resets on a successful login, so a re-lock after the cooldown
|
|
41
41
|
// happens on the FIRST miss, not the Nth (strict semantic — favours
|
|
@@ -123,10 +123,10 @@ export async function gateCountUnknownUser(
|
|
|
123
123
|
lockoutDurationMinutes: number,
|
|
124
124
|
): Promise<GateOutcome<undefined>> {
|
|
125
125
|
const subject = unknownEmailLockoutSubject(ctx, email);
|
|
126
|
-
if (!ctx.redis || subject === undefined) return ok(undefined);
|
|
126
|
+
if (!ctx.services.redis || subject === undefined) return ok(undefined);
|
|
127
127
|
const locked = await gateEnforceLockout(ctx, subject);
|
|
128
128
|
if (!locked.ok) return locked;
|
|
129
|
-
await recordFailedAttempt(ctx.redis, subject, maxFailedAttempts, lockoutDurationMinutes);
|
|
129
|
+
await recordFailedAttempt(ctx.services.redis, subject, maxFailedAttempts, lockoutDurationMinutes);
|
|
130
130
|
return ok(undefined);
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -138,8 +138,8 @@ export async function gateEnforceLockout(
|
|
|
138
138
|
ctx: HandlerContext,
|
|
139
139
|
userId: string,
|
|
140
140
|
): Promise<GateOutcome<undefined>> {
|
|
141
|
-
if (!ctx.redis) return ok(undefined);
|
|
142
|
-
const state = await getLockoutState(ctx.redis, userId);
|
|
141
|
+
if (!ctx.services.redis) return ok(undefined);
|
|
142
|
+
const state = await getLockoutState(ctx.services.redis, userId);
|
|
143
143
|
if (state?.lockedUntil !== null && state?.lockedUntil !== undefined) {
|
|
144
144
|
const now = Date.now();
|
|
145
145
|
if (state.lockedUntil > now) {
|
|
@@ -160,15 +160,20 @@ export async function gateVerifyPassword(
|
|
|
160
160
|
): Promise<GateOutcome<undefined>> {
|
|
161
161
|
const passwordOk = await verifyPassword(found.passwordHash, password);
|
|
162
162
|
if (!passwordOk) {
|
|
163
|
-
if (ctx.redis) {
|
|
164
|
-
await recordFailedAttempt(
|
|
163
|
+
if (ctx.services.redis) {
|
|
164
|
+
await recordFailedAttempt(
|
|
165
|
+
ctx.services.redis,
|
|
166
|
+
found.id,
|
|
167
|
+
maxFailedAttempts,
|
|
168
|
+
lockoutDurationMinutes,
|
|
169
|
+
);
|
|
165
170
|
}
|
|
166
171
|
return reject(invalidCredentials());
|
|
167
172
|
}
|
|
168
173
|
// Clear before MFA — MFA has its own attempt-cap; don't password-lock
|
|
169
174
|
// users who occasionally mistype across otherwise-successful logins.
|
|
170
|
-
if (ctx.redis) {
|
|
171
|
-
await clearLockoutState(ctx.redis, found.id);
|
|
175
|
+
if (ctx.services.redis) {
|
|
176
|
+
await clearLockoutState(ctx.services.redis, found.id);
|
|
172
177
|
}
|
|
173
178
|
return ok(undefined);
|
|
174
179
|
}
|
|
@@ -216,7 +221,7 @@ export async function gateResolveMembership(
|
|
|
216
221
|
const globalRoles = parseRoles(found.roles ?? null);
|
|
217
222
|
|
|
218
223
|
for (const candidate of candidates) {
|
|
219
|
-
const active = await ctx.resolveActiveMembership(found.id, candidate.tenantId);
|
|
224
|
+
const active = await ctx.auth.resolveActiveMembership(found.id, candidate.tenantId);
|
|
220
225
|
if (active.kind === "rejected") {
|
|
221
226
|
// A blocked principal is blocked for every tenant, so stop here;
|
|
222
227
|
// not_a_member/tenant_teardown are per-tenant — try the next candidate.
|
|
@@ -275,7 +280,7 @@ export async function gateBuildSession(
|
|
|
275
280
|
...(timezone !== null && timezone !== undefined && { timezone }),
|
|
276
281
|
...(locale !== null && locale !== undefined && { locale }),
|
|
277
282
|
};
|
|
278
|
-
const claims = await ctx.
|
|
283
|
+
const claims = await ctx.auth.resolveClaims(baseSession);
|
|
279
284
|
const session: SessionUser =
|
|
280
285
|
Object.keys(claims).length > 0 ? { ...baseSession, claims } : baseSession;
|
|
281
286
|
return { kind: "auth-session", session };
|
|
@@ -284,7 +289,7 @@ export async function gateBuildSession(
|
|
|
284
289
|
// Login — unauthenticated entry point. The route is wired public (no JWT
|
|
285
290
|
// middleware), dispatching with the framework's anonymous identity for the
|
|
286
291
|
// handler's access check. Everything inside the handler goes through
|
|
287
|
-
// ctx.queryAs(system, ...) so the user feature stays the single owner of its
|
|
292
|
+
// ctx.dispatch.queryAs(system, ...) so the user feature stays the single owner of its
|
|
288
293
|
// table.
|
|
289
294
|
export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
290
295
|
const strictVerification = opts.strictEmailVerification === true;
|
|
@@ -305,8 +310,8 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
305
310
|
grants: ["systemIdentity", "unsafeRaw"],
|
|
306
311
|
reason:
|
|
307
312
|
"Unauthenticated login has no caller identity yet — it looks up the user row by " +
|
|
308
|
-
"email via ctx.queryAs(SYSTEM, ...) and resolves tenant membership via " +
|
|
309
|
-
"ctx.resolveActiveMembership before a session exists. It also reads the MFA " +
|
|
313
|
+
"email via ctx.dispatch.queryAs(SYSTEM, ...) and resolves tenant membership via " +
|
|
314
|
+
"ctx.auth.resolveActiveMembership before a session exists. It also reads the MFA " +
|
|
310
315
|
"enrollment of the tenant named in the signed login/setup token, not the guest " +
|
|
311
316
|
"dispatch tenant, via the mfaStatusChecker callback.",
|
|
312
317
|
},
|
|
@@ -318,7 +323,9 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
318
323
|
const systemUser = createSystemUser(SYSTEM_USER_ID);
|
|
319
324
|
|
|
320
325
|
const foundRow = parseAuthUserRow(
|
|
321
|
-
await ctx.queryAs(systemUser, UserQueries.findForAuth, {
|
|
326
|
+
await ctx.dispatch.queryAs(systemUser, UserQueries.findForAuth, {
|
|
327
|
+
email: event.payload.email,
|
|
328
|
+
}),
|
|
322
329
|
);
|
|
323
330
|
if (!foundRow?.passwordHash || foundRow.isDeleted) {
|
|
324
331
|
const unknown = await gateCountUnknownUser(
|
|
@@ -352,7 +359,7 @@ export function createLoginHandler(opts: LoginHandlerOptions = {}) {
|
|
|
352
359
|
if (!statusGate.ok) return statusGate.result;
|
|
353
360
|
|
|
354
361
|
// Still needed for candidate ORDER (preferred tenant first) in gateResolveMembership.
|
|
355
|
-
const memberships = (await ctx.queryAs(systemUser, TENANT_MEMBERSHIPS_QUERY, {
|
|
362
|
+
const memberships = (await ctx.dispatch.queryAs(systemUser, TENANT_MEMBERSHIPS_QUERY, {
|
|
356
363
|
userId: found.id,
|
|
357
364
|
})) as Array<Membership>; // @cast-boundary db-runner
|
|
358
365
|
|
|
@@ -31,7 +31,7 @@ export function createResetPasswordHandler(opts: ResetPasswordOptions) {
|
|
|
31
31
|
grants: ["systemIdentity"],
|
|
32
32
|
reason:
|
|
33
33
|
"Anonymous token holder has no session — runConfirmTokenFlow loads and updates the " +
|
|
34
|
-
"target user via ctx.writeAs(SYSTEM, user:update) to set the new password hash.",
|
|
34
|
+
"target user via ctx.dispatch.writeAs(SYSTEM, user:update) to set the new password hash.",
|
|
35
35
|
},
|
|
36
36
|
agent: { expose: false },
|
|
37
37
|
handler: async (event, ctx) => {
|
|
@@ -51,7 +51,8 @@ export function createResetPasswordHandler(opts: ResetPasswordOptions) {
|
|
|
51
51
|
|
|
52
52
|
return runConfirmTokenFlow(ctx, verify.userId, verify.expiresAtMs, {
|
|
53
53
|
purpose: "reset",
|
|
54
|
-
redisRequiredMessage:
|
|
54
|
+
redisRequiredMessage:
|
|
55
|
+
"password-reset requires ctx.services.redis to enforce token single-use",
|
|
55
56
|
invalidToken: invalidResetToken,
|
|
56
57
|
buildChanges: async () => ({
|
|
57
58
|
passwordHash: await hashPassword(event.payload.newPassword),
|