@cosmicdrift/kumiko-samples 0.388.0 → 0.390.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/bundled-features/package.json +25 -13
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/index.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +42 -26
- package/packages/bundled-features/src/audit/feature.ts +2 -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 +18 -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/request-account-unlock.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +1 -0
- 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 +16 -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 +10 -2
- package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +16 -3
- package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -0
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +8 -2
- package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +23 -21
- package/packages/bundled-features/src/auth-email-password/web/default-auth-pages-gate.tsx +6 -3
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -1
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -0
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +16 -5
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +7 -1
- package/packages/bundled-features/src/auth-email-password/web/return-path.ts +35 -0
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +6 -1
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +11 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +12 -2
- package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
- package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
- package/packages/bundled-features/src/auth-mfa/config.ts +3 -7
- 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 +17 -8
- 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 +21 -8
- package/packages/bundled-features/src/cap-counter/changes.json +12 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
- package/packages/bundled-features/src/cap-counter/feature.ts +3 -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/feature.ts +1 -0
- 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/changes.json +6 -0
- 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 +22 -5
- package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/changes.json +12 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +9 -9
- 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/custom-fields/lib/parse-serialized-field.ts +1 -1
- 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/changes.json +12 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
- package/packages/bundled-features/src/delivery/feature.ts +3 -2
- package/packages/bundled-features/src/delivery/index.ts +5 -0
- package/packages/bundled-features/src/delivery/jobs.ts +11 -11
- package/packages/bundled-features/src/delivery/system-channel-context.ts +33 -0
- package/packages/bundled-features/src/delivery/testing.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/append-missing-extract-event.ts +84 -0
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +16 -1
- package/packages/bundled-features/src/document-ingest-foundation/index.ts +11 -0
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -0
- 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/host-routing/changes.json +6 -0
- package/packages/bundled-features/src/host-routing/validate-options.ts +5 -0
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
- package/packages/bundled-features/src/jobs/changes.json +6 -0
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +1 -0
- 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/ledger/web/index.ts +1 -1
- 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/legal-pages/seed.ts +5 -2
- package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/mail-transport-smtp/changes.json +12 -0
- package/packages/bundled-features/src/mail-transport-smtp/config-keys.ts +1 -1
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +13 -12
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/constants.ts +1 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +3 -1
- package/packages/bundled-features/src/managed-pages/index.ts +1 -0
- package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -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/changes.json +6 -0
- 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 +10 -3
- package/packages/bundled-features/src/seo/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/changes.json +19 -0
- package/packages/bundled-features/src/sessions/feature.ts +2 -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/index.ts +1 -1
- package/packages/bundled-features/src/sessions/screens.ts +8 -4
- package/packages/bundled-features/src/sessions/session-callbacks.ts +1 -9
- 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 +36 -4
- package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
- package/packages/bundled-features/src/shared/run-in-sub-transaction.ts +5 -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/changes.json +6 -0
- package/packages/bundled-features/src/step-dispatcher/deliver-dispatch-request.ts +17 -3
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
- package/packages/bundled-features/src/step-dispatcher/mail-runner.ts +2 -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/tags/feature.ts +1 -0
- package/packages/bundled-features/src/template-resolver/api.ts +3 -3
- package/packages/bundled-features/src/template-resolver/changes.json +54 -0
- package/packages/bundled-features/src/template-resolver/document-ops.ts +20 -1
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- 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/layout-source.query.ts +36 -11
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +32 -7
- package/packages/bundled-features/src/template-resolver/handlers/patch-draft.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test-status.query.ts +22 -19
- 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 +22 -5
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +68 -19
- 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/i18n.ts +6 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
- package/packages/bundled-features/src/template-resolver/web/qualified-names.ts +1 -0
- 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/feature.ts +1 -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/team-list.query.ts +3 -3
- 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/changes.json +6 -0
- 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 +42 -4
- 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/feature.ts +1 -0
- 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 +36 -0
- package/packages/bundled-features/src/user-data-rights/feature.ts +30 -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 +8 -1
- 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 +16 -0
- package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -0
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/feature.ts +1 -0
- 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 +34 -10
- package/packages/bundled-features/src/waitlist/import.ts +23 -3
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +8 -2
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
- package/packages/bundled-features/src/waitlist/options.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
- package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
- package/samples/apps/config-demo/src/app/server.ts +1 -1
- package/samples/apps/styleguide/src/app/client.tsx +2 -0
- package/samples/apps/styleguide/src/app/seed.ts +6 -4
- package/samples/apps/styleguide/src/app/server.ts +6 -3
- package/samples/apps/styleguide/src/features/widgets/feature.ts +15 -4
- package/samples/apps/styleguide/src/features/workboard/feature.ts +185 -0
- package/samples/apps/styleguide/src/features/workboard/i18n.ts +27 -0
- package/samples/apps/styleguide/src/features/workboard/seed.ts +121 -0
- package/samples/apps/styleguide/src/features/workboard/web.ts +13 -0
- package/samples/apps/ui-walkthrough/src/features/rental/i18n.ts +2 -0
- package/samples/apps/ui-walkthrough/src/features/rental/screens.ts +9 -0
- package/samples/apps/use-all-bundled/bin/main.ts +2 -0
- package/samples/apps/use-all-bundled/feature-manifest.json +17 -1
- package/samples/apps/use-all-bundled/kumiko/schema.ts +2 -0
- package/samples/apps/use-all-bundled/kumiko.config.ts +2 -0
- package/samples/apps/use-all-bundled/package.json +2 -1
- package/samples/apps/use-all-bundled/src/app/dev-app-options.ts +2 -0
- package/samples/apps/use-all-bundled/src/app/notes-feature.ts +2 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
- package/samples/apps/use-all-bundled/src/bundled-lists.ts +15 -0
- package/samples/apps/user-data-rights-demo/README.md +1 -1
- package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
- package/samples/recipes/api-worker-split/README.md +2 -1
- package/samples/recipes/api-worker-split/src/feature.ts +2 -1
- package/samples/recipes/cross-feature-events/README.md +5 -5
- package/samples/recipes/cross-feature-events/package.json +1 -1
- package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
- package/samples/recipes/event-sourcing/README.md +5 -5
- package/samples/recipes/event-sourcing/package.json +1 -1
- package/samples/recipes/event-sourcing/src/feature.ts +15 -15
- package/samples/recipes/files-post-processing/README.md +1 -1
- package/samples/recipes/files-post-processing/package.json +1 -1
- package/samples/recipes/files-post-processing/src/feature.ts +6 -4
- package/samples/recipes/golden-path/src/feature.ts +2 -1
- 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 +4 -0
- package/samples/recipes/lifecycle-hooks/src/feature.ts +26 -1
- package/samples/recipes/list-table-options/README.md +50 -0
- package/samples/recipes/list-table-options/package.json +11 -0
- package/samples/recipes/list-table-options/src/feature.ts +221 -0
- package/samples/recipes/managed-config/README.md +3 -3
- package/samples/recipes/pdf-audit-export/src/feature.ts +2 -1
- 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 +4 -3
- package/samples/recipes/state-machine/src/reducer.ts +1 -1
- package/samples/recipes/tenant-isolation/README.md +1 -0
- package/samples/recipes/tenant-isolation/src/feature.ts +36 -0
- package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
- package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
// - emitEvent with headers — Marten free key/value metadata
|
|
10
10
|
// - r.projection — single-stream read model (inline in the write TX)
|
|
11
11
|
// - r.multiStreamProjection — cross-aggregate async read model
|
|
12
|
-
// - ctx.
|
|
13
|
-
// - ctx.
|
|
14
|
-
// - ctx.queryProjection — tenant-scoped read of a projection table
|
|
15
|
-
// - ctx.
|
|
12
|
+
// - ctx.events.load with { asOf } — point-in-time aggregate state
|
|
13
|
+
// - ctx.events.archive / ctx.events.restore — Marten ArchiveStream
|
|
14
|
+
// - ctx.dispatch.queryProjection — tenant-scoped read of a projection table
|
|
15
|
+
// - ctx.events.snapshot + ctx.events.loadWithSnapshot — perf path
|
|
16
16
|
// for aggregates with long event tails (O(1) vs O(N) reduce), incl.
|
|
17
17
|
// the auto-snapshot policy ({ snapshotEvery, snapshotVersion })
|
|
18
18
|
// - streamAllEventsByType — memory-bounded iteration for ops/export jobs
|
|
@@ -339,7 +339,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
339
339
|
name: "invoice:archive",
|
|
340
340
|
schema: z.object({ id: z.uuid() }),
|
|
341
341
|
handler: async (event, ctx) => {
|
|
342
|
-
await ctx.
|
|
342
|
+
await ctx.events.archive(event.payload.id, { aggregateType: "showcase-invoice" });
|
|
343
343
|
return { isSuccess: true as const, data: { id: event.payload.id } };
|
|
344
344
|
},
|
|
345
345
|
access: { roles: ["Admin"] },
|
|
@@ -354,14 +354,14 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
354
354
|
handler: async (event, ctx) => {
|
|
355
355
|
// Rebuild the state from the current stream so the snapshot reflects
|
|
356
356
|
// the committed event log, not an in-memory guess. Use the upcaster-
|
|
357
|
-
// aware ctx.
|
|
358
|
-
const events = await ctx.
|
|
357
|
+
// aware ctx.events.load to stay consistent with read-time semantics.
|
|
358
|
+
const events = await ctx.events.load(event.payload.id);
|
|
359
359
|
const state: InvoiceState = { ...initialInvoiceState };
|
|
360
360
|
for (const evt of events) {
|
|
361
361
|
reduceInvoice(state, evt);
|
|
362
362
|
}
|
|
363
363
|
const version = events.length > 0 ? (events[events.length - 1]?.version ?? 0) : 0;
|
|
364
|
-
await ctx.
|
|
364
|
+
await ctx.events.snapshot({
|
|
365
365
|
aggregateId: event.payload.id,
|
|
366
366
|
aggregateType: "showcase-invoice",
|
|
367
367
|
version,
|
|
@@ -374,13 +374,13 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
374
374
|
|
|
375
375
|
// --- Query handlers ---
|
|
376
376
|
|
|
377
|
-
// Live aggregation via ctx.
|
|
377
|
+
// Live aggregation via ctx.events.load — reduces events into a state
|
|
378
378
|
// snapshot. Supports asOf for point-in-time reads.
|
|
379
379
|
r.queryHandler({
|
|
380
380
|
name: "invoice:state",
|
|
381
381
|
schema: z.object({ id: z.uuid(), asOf: z.iso.datetime().optional() }),
|
|
382
382
|
handler: async (query, ctx) => {
|
|
383
|
-
const events = await ctx.
|
|
383
|
+
const events = await ctx.events.load(query.payload.id, {
|
|
384
384
|
...(query.payload.asOf ? { asOf: Temporal.Instant.from(query.payload.asOf) } : {}),
|
|
385
385
|
});
|
|
386
386
|
const state: InvoiceState = { ...initialInvoiceState };
|
|
@@ -405,7 +405,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
405
405
|
name: "invoice:fast-state",
|
|
406
406
|
schema: z.object({ id: z.uuid() }),
|
|
407
407
|
handler: async (query, ctx) => {
|
|
408
|
-
const result = await ctx.
|
|
408
|
+
const result = await ctx.events.loadWithSnapshot<InvoiceStateRecord>(
|
|
409
409
|
query.payload.id,
|
|
410
410
|
(state, evt) => {
|
|
411
411
|
const next: InvoiceStateRecord = { ...state };
|
|
@@ -434,7 +434,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
434
434
|
},
|
|
435
435
|
});
|
|
436
436
|
|
|
437
|
-
// Read-model query via ctx.queryProjection — auto-tenant-scoped. The
|
|
437
|
+
// Read-model query via ctx.dispatch.queryProjection — auto-tenant-scoped. The
|
|
438
438
|
// helper collapses the zero-argument "return this projection" pattern
|
|
439
439
|
// into a single call; returns { rows } so the response shape stays
|
|
440
440
|
// consistent with other list handlers.
|
|
@@ -450,11 +450,11 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
450
450
|
}),
|
|
451
451
|
);
|
|
452
452
|
|
|
453
|
-
// Slow synthetic export — demonstrates ctx.signal honouring inside a
|
|
453
|
+
// Slow synthetic export — demonstrates ctx.lifecycle.signal honouring inside a
|
|
454
454
|
// long-running handler loop. Real-world equivalents: PDF batch export,
|
|
455
455
|
// bulk CSV download, multi-stream audit walk. Mobile clients that
|
|
456
456
|
// navigate away mid-export should NOT keep the server busy producing
|
|
457
|
-
// bytes nobody will read. The handler checks ctx.signal at every
|
|
457
|
+
// bytes nobody will read. The handler checks ctx.lifecycle.signal at every
|
|
458
458
|
// iteration; on abort, throws and the dispatcher returns an error to
|
|
459
459
|
// the (already-disconnected) client.
|
|
460
460
|
r.queryHandler({
|
|
@@ -463,7 +463,7 @@ export const invoiceFeature = defineFeature("showcase", (r) => {
|
|
|
463
463
|
handler: async (query, ctx) => {
|
|
464
464
|
const accumulated: number[] = [];
|
|
465
465
|
for (let i = 0; i < query.payload.chunks; i++) {
|
|
466
|
-
ctx.signal?.throwIfAborted();
|
|
466
|
+
ctx.lifecycle.signal?.throwIfAborted();
|
|
467
467
|
// Fake "work per chunk". Realistic handler would assemble a row,
|
|
468
468
|
// hit a remote service, etc.
|
|
469
469
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
@@ -5,7 +5,7 @@ File upload plus post-processing hooks.
|
|
|
5
5
|
## What it shows
|
|
6
6
|
|
|
7
7
|
- Declarative `variants` on `createImageField` (file-derivatives)
|
|
8
|
-
- Imperative `ctx.derivatives.variant()` path and the public variant route (`EXT_DERIVATIVE_PUBLIC_PREDICATE`)
|
|
8
|
+
- Imperative `ctx.services.derivatives.variant()` path and the public variant route (`EXT_DERIVATIVE_PUBLIC_PREDICATE`)
|
|
9
9
|
|
|
10
10
|
## Source
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-files-post-processing",
|
|
3
|
-
"description": "The three ways to generate an image variant: declarative field variants, imperative ctx.derivatives.variant() with region-blur, and the public route behind a default-deny predicate.",
|
|
3
|
+
"description": "The three ways to generate an image variant: declarative field variants, imperative ctx.services.derivatives.variant() with region-blur, and the public route behind a default-deny predicate.",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-bundled-features": "workspace:*",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// resolves + caches the variant behind the auth-gated
|
|
10
10
|
// `GET /api/files/:id/variant/:name` route, no handler code needed.
|
|
11
11
|
// 2. Imperative — the `set-cover` write-handler calls
|
|
12
|
-
// `ctx.derivatives.variant()` directly. This is for apps that track
|
|
12
|
+
// `ctx.services.derivatives.variant()` directly. This is for apps that track
|
|
13
13
|
// images as their own records (ordering, a chosen cover image) and for
|
|
14
14
|
// region-blur, whose coordinates are runtime data that can't live in a
|
|
15
15
|
// static field declaration.
|
|
@@ -88,13 +88,15 @@ export const filesPostProcessingFeature = defineFeature("files-post-processing",
|
|
|
88
88
|
.optional(),
|
|
89
89
|
}),
|
|
90
90
|
handler: async (event, ctx) => {
|
|
91
|
-
if (!ctx.derivatives) {
|
|
91
|
+
if (!ctx.services.derivatives) {
|
|
92
92
|
return writeFailure(
|
|
93
|
-
new InternalError({
|
|
93
|
+
new InternalError({
|
|
94
|
+
message: "ctx.services.derivatives missing — is file-derivatives mounted?",
|
|
95
|
+
}),
|
|
94
96
|
);
|
|
95
97
|
}
|
|
96
98
|
const { fileRefId, blurRegion } = event.payload;
|
|
97
|
-
const result = await ctx.derivatives.variant(
|
|
99
|
+
const result = await ctx.services.derivatives.variant(
|
|
98
100
|
fileRefId,
|
|
99
101
|
{
|
|
100
102
|
maxEdge: 800,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
emitEvent,
|
|
8
8
|
setFields,
|
|
9
9
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
10
|
-
import { failNotFound } from "@cosmicdrift/kumiko-framework/errors";
|
|
10
|
+
import { failNotFound, failUnprocessable } from "@cosmicdrift/kumiko-framework/errors";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
|
|
13
13
|
export const ticketEntity = createEntity({
|
|
@@ -43,6 +43,7 @@ export const helpdeskFeature = defineFeature("helpdesk", (r) => {
|
|
|
43
43
|
if (!ticket) {
|
|
44
44
|
return failNotFound("ticket", event.payload.id);
|
|
45
45
|
}
|
|
46
|
+
if (ticket["isClosed"]) return failUnprocessable("ticket_already_closed");
|
|
46
47
|
await emitEvent(ctx, ticketClosed, {
|
|
47
48
|
aggregateId: event.payload.id,
|
|
48
49
|
aggregateType: "ticket",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-sample-lane-routing",
|
|
3
|
-
"description": "runIn lane-routing: r.job pinned to api/worker deploy-lanes, event-chain via ctx.
|
|
3
|
+
"description": "runIn lane-routing: r.job pinned to api/worker deploy-lanes, event-chain via ctx.events.append, verified end-to-end through createAllInOneEntrypoint.",
|
|
4
4
|
"private": true,
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@cosmicdrift/kumiko-framework": "workspace:*"
|
|
@@ -100,9 +100,9 @@ A one-shot setup routine that runs on first boot or via the CLI:
|
|
|
100
100
|
// bin/seed-legal.ts
|
|
101
101
|
import { seedTextBlock } from "@cosmicdrift/kumiko-bundled-features/template-resolver/seeding";
|
|
102
102
|
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
103
|
-
import {
|
|
103
|
+
import { createDbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
104
104
|
|
|
105
|
-
const db =
|
|
105
|
+
const { db } = createDbConnection({ url: process.env.DATABASE_URL! });
|
|
106
106
|
|
|
107
107
|
await seedTextBlock(db, {
|
|
108
108
|
tenantId: SYSTEM_TENANT_ID,
|
|
@@ -27,6 +27,10 @@ back.
|
|
|
27
27
|
- **`r.hook({ type: "postDelete", target: { allOf: entityRef }, handler })`** — fires after commit.
|
|
28
28
|
External side effects (logs, notifications) belong here because
|
|
29
29
|
failures cannot roll the delete back.
|
|
30
|
+
- **`ctx.lifecycle.afterRollback(fn)`** — the counterpart of `ctx.lifecycle.afterCommit`: runs `fn` when
|
|
31
|
+
the handler transaction (or the enclosing savepoint of a nested `ctx.dispatch.write`) does not commit.
|
|
32
|
+
Use it to give back something reserved outside the transaction, like the slot in
|
|
33
|
+
`article:reserve-slot`. A failed COMMIT triggers it too, so write the callback idempotent.
|
|
30
34
|
|
|
31
35
|
## When to reach for it
|
|
32
36
|
|
|
@@ -13,7 +13,12 @@ import {
|
|
|
13
13
|
defineFeature,
|
|
14
14
|
type SaveContext,
|
|
15
15
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
ConflictError,
|
|
18
|
+
UnprocessableError,
|
|
19
|
+
writeFailure,
|
|
20
|
+
} from "@cosmicdrift/kumiko-framework/errors";
|
|
21
|
+
import * as z from "zod";
|
|
17
22
|
|
|
18
23
|
export const articleEntity = createEntity({
|
|
19
24
|
table: "read_sample_articles",
|
|
@@ -35,6 +40,9 @@ export const articleEntity = createEntity({
|
|
|
35
40
|
// Collect hook events for testing
|
|
36
41
|
export const hookLog: { type: string; data: Record<string, unknown> }[] = [];
|
|
37
42
|
|
|
43
|
+
// Stands in for a quota held in an external system, outside the handler transaction.
|
|
44
|
+
export const reservedSlots = new Set<string>();
|
|
45
|
+
|
|
38
46
|
const editorWrite = { access: { roles: ["Admin", "Editor"] } } as const;
|
|
39
47
|
|
|
40
48
|
export const articlesFeature = defineFeature("blog", (r) => {
|
|
@@ -48,6 +56,23 @@ export const articlesFeature = defineFeature("blog", (r) => {
|
|
|
48
56
|
);
|
|
49
57
|
r.writeHandler(defineEntityDeleteHandler("article", articleEntity, editorWrite));
|
|
50
58
|
|
|
59
|
+
// --- scheduleAfterRollback: give back a reservation made outside the transaction when it does
|
|
60
|
+
// not commit. The callback must be idempotent: a failed COMMIT also triggers it.
|
|
61
|
+
r.writeHandler({
|
|
62
|
+
name: "article:reserve-slot",
|
|
63
|
+
schema: z.object({ slot: z.string(), reject: z.boolean() }),
|
|
64
|
+
access: editorWrite.access,
|
|
65
|
+
handler: async (event, ctx) => {
|
|
66
|
+
const { slot, reject } = event.payload;
|
|
67
|
+
reservedSlots.add(slot);
|
|
68
|
+
ctx.lifecycle.afterRollback?.(async () => {
|
|
69
|
+
reservedSlots.delete(slot);
|
|
70
|
+
});
|
|
71
|
+
if (reject) return writeFailure(new UnprocessableError("slot_rejected"));
|
|
72
|
+
return { isSuccess: true as const, data: {} };
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
51
76
|
// --- Validation hook on create: reject banned words + length ---
|
|
52
77
|
r.hook({
|
|
53
78
|
type: "validation",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# List table options
|
|
2
|
+
|
|
3
|
+
Opt-in table behaviour for entity, projection and related lists. A list without
|
|
4
|
+
the `table` block renders and behaves as before.
|
|
5
|
+
|
|
6
|
+
## What it shows
|
|
7
|
+
|
|
8
|
+
- `table.rememberState`: search, sort, page size, facets and date range survive
|
|
9
|
+
leaving and re-entering the list within the browser session.
|
|
10
|
+
- `table.resetAll`: one button resets everything that differs from the defaults.
|
|
11
|
+
- `table.columnPicker`: the view menu lets the user hide and reorder columns;
|
|
12
|
+
`hidden: true` on a column starts it hidden.
|
|
13
|
+
- `table.density`: row density default and an optional user choice.
|
|
14
|
+
- `table.pinning`: the user pins columns left or right in the view menu;
|
|
15
|
+
`pinned: "left"` on a column starts it pinned.
|
|
16
|
+
- `table.resizing`: columns can be resized (table-layout fixed); `width` and
|
|
17
|
+
`minWidth` on a column are start values in px.
|
|
18
|
+
- `table.keyboardNavigation`: rows are one tab stop, arrow keys move between rows,
|
|
19
|
+
Space selects the focused row.
|
|
20
|
+
- `table.export`: an Export button downloads the whole filtered list as an XLSX
|
|
21
|
+
file; `maxRows` lowers the default limit of 10000 rows.
|
|
22
|
+
Reference columns export their label and timestamps use the browser time zone.
|
|
23
|
+
- `bulkActions`: row checkboxes and a selection bar. "Delete" runs the delete
|
|
24
|
+
row action once per selected row; done tasks are skipped.
|
|
25
|
+
- `table.grouping`: the server groups the list by status; each header shows the
|
|
26
|
+
group's count and estimate sum, "done" starts collapsed and the view menu
|
|
27
|
+
offers grouping by priority or none.
|
|
28
|
+
- `table.totals`: a footer row with the estimate sum over the filtered list.
|
|
29
|
+
- `facets.estimateHours: { display: "range" }`: from/to inputs for a number field.
|
|
30
|
+
- The same block on a `projectionList`, and `task:workload`, a hand-written list
|
|
31
|
+
query that keeps grouping and totals by accepting `aggregate`
|
|
32
|
+
(`entityListSchema`) and answering it with `answerListAggregate`.
|
|
33
|
+
|
|
34
|
+
## Source
|
|
35
|
+
|
|
36
|
+
Feature entry point: `src/feature.ts`.
|
|
37
|
+
|
|
38
|
+
## Tests
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd samples/recipes/list-table-options
|
|
42
|
+
bun test
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The test boots the feature through the boot validator and checks that a
|
|
46
|
+
misspelled key, a hidden column without the picker, an unknown density, a pinned column
|
|
47
|
+
without `pinning`, a width without `resizing` and a bulk action on a navigate
|
|
48
|
+
row action are rejected.
|
|
49
|
+
`workload.integration.test.ts` runs the hand-written query over HTTP and checks
|
|
50
|
+
the grouped sums and the filtered total.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cosmicdrift/kumiko-sample-list-table-options",
|
|
3
|
+
"description": "Opt-in table behaviour of a list: remembered search/sort/filters, one reset button, column picker with hidden columns, row density, server-side groups with sums, a totals row and a number range facet.",
|
|
4
|
+
"private": true,
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@cosmicdrift/kumiko-framework": "workspace:*"
|
|
7
|
+
},
|
|
8
|
+
"devDependencies": {
|
|
9
|
+
"@cosmicdrift/kumiko-testing": "workspace:*"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import {
|
|
2
|
+
answerListAggregate,
|
|
3
|
+
buildEntityTable,
|
|
4
|
+
createEventStoreExecutor,
|
|
5
|
+
} from "@cosmicdrift/kumiko-framework/db";
|
|
6
|
+
import {
|
|
7
|
+
createDateField,
|
|
8
|
+
createEntity,
|
|
9
|
+
createNumberField,
|
|
10
|
+
createSelectField,
|
|
11
|
+
createTextField,
|
|
12
|
+
defineFeature,
|
|
13
|
+
entityListSchema,
|
|
14
|
+
type FeatureDefinition,
|
|
15
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
16
|
+
import type { ListTableOptions } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
17
|
+
import { z } from "zod";
|
|
18
|
+
|
|
19
|
+
export const taskEntity = createEntity({
|
|
20
|
+
table: "read_list_table_tasks",
|
|
21
|
+
fields: {
|
|
22
|
+
title: createTextField({
|
|
23
|
+
personal: false,
|
|
24
|
+
reason: "is_business_data",
|
|
25
|
+
required: true,
|
|
26
|
+
searchable: true,
|
|
27
|
+
sortable: true,
|
|
28
|
+
}),
|
|
29
|
+
status: createSelectField({
|
|
30
|
+
options: ["open", "inProgress", "blocked", "done"] as const,
|
|
31
|
+
default: "open",
|
|
32
|
+
sortable: true,
|
|
33
|
+
filterable: true,
|
|
34
|
+
}),
|
|
35
|
+
priority: createSelectField({
|
|
36
|
+
options: ["low", "medium", "high"] as const,
|
|
37
|
+
default: "medium",
|
|
38
|
+
sortable: true,
|
|
39
|
+
filterable: true,
|
|
40
|
+
}),
|
|
41
|
+
dueDate: createDateField({ sortable: true }),
|
|
42
|
+
assignee: createTextField({ personal: false, reason: "is_business_data", sortable: true }),
|
|
43
|
+
estimateHours: createNumberField({ sortable: true, filterable: true }),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const openAccess = {
|
|
48
|
+
access: {
|
|
49
|
+
openToAll: { reason: "recipe: any signed-in user manages the tasks; no per-user ownership" },
|
|
50
|
+
},
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
// Overridable so a test can feed the boot validator a block that must be rejected.
|
|
54
|
+
export function createTaskBoardFeature(
|
|
55
|
+
taskTable: ListTableOptions = {
|
|
56
|
+
rememberState: true,
|
|
57
|
+
resetAll: true,
|
|
58
|
+
columnPicker: true,
|
|
59
|
+
density: { default: "comfortable", userChoice: true },
|
|
60
|
+
pinning: true,
|
|
61
|
+
resizing: true,
|
|
62
|
+
keyboardNavigation: true,
|
|
63
|
+
export: { xlsx: true, maxRows: 5000 },
|
|
64
|
+
grouping: { field: "status", collapsedWhen: "done", userChoice: true, fields: ["priority"] },
|
|
65
|
+
totals: [{ field: "estimateHours", kind: "sum" }],
|
|
66
|
+
},
|
|
67
|
+
): FeatureDefinition {
|
|
68
|
+
return defineFeature("task-board", (r) => {
|
|
69
|
+
r.crud("task", taskEntity, { write: openAccess, read: openAccess });
|
|
70
|
+
|
|
71
|
+
r.screen({
|
|
72
|
+
id: "task-list",
|
|
73
|
+
type: "entityList",
|
|
74
|
+
entity: "task",
|
|
75
|
+
columns: [
|
|
76
|
+
// Starts pinned left; width and minWidth are start values for resizing.
|
|
77
|
+
{ field: "title", pinned: "left", width: 280, minWidth: 160 },
|
|
78
|
+
"status",
|
|
79
|
+
"priority",
|
|
80
|
+
{ field: "dueDate", hideOnNarrow: true },
|
|
81
|
+
{ field: "assignee", hideOnNarrow: true },
|
|
82
|
+
// Starts hidden; the user can add it back in the view menu.
|
|
83
|
+
{ field: "estimateHours", hidden: true, hideOnNarrow: true },
|
|
84
|
+
],
|
|
85
|
+
facets: { status: { display: "chips" }, priority: {}, estimateHours: { display: "range" } },
|
|
86
|
+
defaultSort: { field: "dueDate", dir: "asc" },
|
|
87
|
+
searchable: true,
|
|
88
|
+
table: taskTable,
|
|
89
|
+
rowActions: [
|
|
90
|
+
{ kind: "navigate", id: "open", label: "Open", screen: "task-edit", rowClick: true },
|
|
91
|
+
{
|
|
92
|
+
kind: "writeHandler",
|
|
93
|
+
id: "delete",
|
|
94
|
+
label: "Delete",
|
|
95
|
+
handler: "task-board:write:task:delete",
|
|
96
|
+
style: "danger",
|
|
97
|
+
// Done tasks are skipped by the bulk run and counted as skipped.
|
|
98
|
+
visible: { field: "status", ne: "done" },
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
// Row checkboxes and a selection bar; each selected row gets its own delete write.
|
|
102
|
+
bulkActions: [{ id: "delete", rowAction: "delete" }],
|
|
103
|
+
access: openAccess.access,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
r.screen({
|
|
107
|
+
id: "task-edit",
|
|
108
|
+
type: "entityEdit",
|
|
109
|
+
entity: "task",
|
|
110
|
+
dormant: true,
|
|
111
|
+
listScreenId: "task-list",
|
|
112
|
+
layout: {
|
|
113
|
+
sections: [
|
|
114
|
+
{
|
|
115
|
+
columns: 2,
|
|
116
|
+
fields: ["title", "assignee", "status", "priority", "dueDate", "estimateHours"],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// The same block works on a list over any query.
|
|
123
|
+
r.queryHandler({
|
|
124
|
+
name: "task:overdue",
|
|
125
|
+
schema: z.object({}),
|
|
126
|
+
handler: async () => ({
|
|
127
|
+
rows: [{ id: "t1", title: "Quarterly VAT filing", assignee: "Ines", daysLate: 3 }],
|
|
128
|
+
nextCursor: null,
|
|
129
|
+
}),
|
|
130
|
+
access: openAccess.access,
|
|
131
|
+
outputSchema: z.object({
|
|
132
|
+
rows: z.array(
|
|
133
|
+
z.object({
|
|
134
|
+
id: z.string(),
|
|
135
|
+
title: z.string(),
|
|
136
|
+
assignee: z.string(),
|
|
137
|
+
daysLate: z.number(),
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
nextCursor: z.string().nullable(),
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
r.screen({
|
|
145
|
+
id: "task-overdue",
|
|
146
|
+
type: "projectionList",
|
|
147
|
+
query: "task-board:query:task:overdue",
|
|
148
|
+
columns: ["title", "assignee", "daysLate"],
|
|
149
|
+
table: { rememberState: true, density: { userChoice: true } },
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// A hand-written list query keeps grouping and totals when it accepts
|
|
153
|
+
// `aggregate` (entityListSchema carries it) and answers it on the server.
|
|
154
|
+
const taskExecutor = createEventStoreExecutor(
|
|
155
|
+
buildEntityTable("task", taskEntity),
|
|
156
|
+
taskEntity,
|
|
157
|
+
{
|
|
158
|
+
entityName: "task",
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
r.queryHandler({
|
|
162
|
+
name: "task:workload",
|
|
163
|
+
schema: entityListSchema,
|
|
164
|
+
handler: async (query, ctx) => {
|
|
165
|
+
const parentVisibility = { entities: ctx.registry.getAllEntities() };
|
|
166
|
+
const listPayload = query.payload;
|
|
167
|
+
if (listPayload.aggregate !== undefined) {
|
|
168
|
+
return answerListAggregate(
|
|
169
|
+
taskExecutor,
|
|
170
|
+
{ ...listPayload, aggregate: listPayload.aggregate },
|
|
171
|
+
query.user,
|
|
172
|
+
ctx.db,
|
|
173
|
+
{ parentVisibility },
|
|
174
|
+
{
|
|
175
|
+
entity: taskEntity,
|
|
176
|
+
resolveEntity: (name) => ctx.registry.getEntity(name),
|
|
177
|
+
viewer: { user: query.user, parentVisibility },
|
|
178
|
+
},
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return taskExecutor.list(listPayload, query.user, ctx.db, { parentVisibility });
|
|
182
|
+
},
|
|
183
|
+
access: openAccess.access,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
r.screen({
|
|
187
|
+
id: "task-workload",
|
|
188
|
+
type: "projectionList",
|
|
189
|
+
query: "task-board:query:task:workload",
|
|
190
|
+
columns: ["title", "assignee", "status", "estimateHours"],
|
|
191
|
+
defaultSort: { field: "title", dir: "asc" },
|
|
192
|
+
table: {
|
|
193
|
+
grouping: { field: "assignee" },
|
|
194
|
+
totals: [{ field: "estimateHours", kind: "sum" }],
|
|
195
|
+
},
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
r.nav({ id: "tasks", label: "task-board:nav.tasks", icon: "list", order: 10 });
|
|
199
|
+
r.nav({
|
|
200
|
+
id: "task-list",
|
|
201
|
+
label: "task-board:nav.list",
|
|
202
|
+
parent: "task-board:nav:tasks",
|
|
203
|
+
screen: "task-board:screen:task-list",
|
|
204
|
+
order: 10,
|
|
205
|
+
});
|
|
206
|
+
r.nav({
|
|
207
|
+
id: "task-overdue",
|
|
208
|
+
label: "task-board:nav.overdue",
|
|
209
|
+
parent: "task-board:nav:tasks",
|
|
210
|
+
screen: "task-board:screen:task-overdue",
|
|
211
|
+
order: 20,
|
|
212
|
+
});
|
|
213
|
+
r.nav({
|
|
214
|
+
id: "task-workload",
|
|
215
|
+
label: "task-board:nav.workload",
|
|
216
|
+
parent: "task-board:nav:tasks",
|
|
217
|
+
screen: "task-board:screen:task-workload",
|
|
218
|
+
order: 30,
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
}
|
|
@@ -43,7 +43,7 @@ r.config({
|
|
|
43
43
|
|
|
44
44
|
- **`backing: "secrets"`** — the value lives in the secrets store (envelope
|
|
45
45
|
encryption, KEK rotation, audit-on-read) instead of `config_values`. Set/read/
|
|
46
|
-
reset dispatch through `ctx.secrets`; the query handlers mask the value while
|
|
46
|
+
reset dispatch through `ctx.services.secrets`; the query handlers mask the value while
|
|
47
47
|
the owning feature still reads the revealed plaintext via `ctx.config`. Only
|
|
48
48
|
valid for `scope: "system"` — secrets are flat per `(tenant, key)` with no
|
|
49
49
|
cascade, so a tenant override would be incoherent and is rejected at boot.
|
|
@@ -72,7 +72,7 @@ await runProdApp({
|
|
|
72
72
|
envSchema, // composeEnvSchema({ core: frameworkCoreEnvSchema, features })
|
|
73
73
|
extraContext: ({ db, registry }) => ({
|
|
74
74
|
configResolver: resolver,
|
|
75
|
-
|
|
75
|
+
configAccessorFactory: createConfigAccessorFactory(registry, resolver),
|
|
76
76
|
// Required whenever a key declares backing:"secrets" — without it the set
|
|
77
77
|
// and read paths fail loud, never silently miss.
|
|
78
78
|
secrets: createSecretsContext({ db, masterKeyProvider, registry }),
|
|
@@ -82,7 +82,7 @@ await runProdApp({
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Register the `secrets` feature too so its `tenant_secrets` table is migrated;
|
|
85
|
-
the config backing only needs the `ctx.secrets` context shown above.
|
|
85
|
+
the config backing only needs the `ctx.services.secrets` context shown above.
|
|
86
86
|
|
|
87
87
|
## Vs. the neighbouring recipes
|
|
88
88
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
createTextField,
|
|
16
16
|
defineFeature,
|
|
17
17
|
defineWriteHandler,
|
|
18
|
+
MAX_LIST_LIMIT,
|
|
18
19
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
19
20
|
import { UnprocessableError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
20
21
|
import {
|
|
@@ -51,7 +52,7 @@ function toFileNameSegment(text: string): string {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// One row above the limit tells "exactly full" from "cut off"; entity lists cap at 200 rows.
|
|
54
|
-
const AUDIT_ROW_LIMIT =
|
|
55
|
+
const AUDIT_ROW_LIMIT = MAX_LIST_LIMIT - 1;
|
|
55
56
|
|
|
56
57
|
const access = { access: { roles: ["Admin", "User"] } } as const;
|
|
57
58
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Search Sample
|
|
2
2
|
// Shows: searchable fields, searchWeight, search via InMemory SearchAdapter.
|
|
3
|
-
// Each handler builds its executor inline so `ctx.searchAdapter` is read fresh
|
|
3
|
+
// Each handler builds its executor inline so `ctx.services.searchAdapter` is read fresh
|
|
4
4
|
// per call — tests swap the adapter between runs.
|
|
5
5
|
|
|
6
6
|
import { buildEntityTable, createEventStoreExecutor } from "@cosmicdrift/kumiko-framework/db";
|
|
@@ -38,7 +38,7 @@ export const productFeature = defineFeature("shop", (r) => {
|
|
|
38
38
|
}),
|
|
39
39
|
handler: async (event, ctx) => {
|
|
40
40
|
const crud = createEventStoreExecutor(productTable, productEntity, {
|
|
41
|
-
searchAdapter: ctx.searchAdapter,
|
|
41
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
42
42
|
entityName: "product",
|
|
43
43
|
});
|
|
44
44
|
return crud.create(event.payload, event.user, ctx.db);
|
|
@@ -54,7 +54,7 @@ export const productFeature = defineFeature("shop", (r) => {
|
|
|
54
54
|
}),
|
|
55
55
|
handler: async (query, ctx) => {
|
|
56
56
|
const crud = createEventStoreExecutor(productTable, productEntity, {
|
|
57
|
-
searchAdapter: ctx.searchAdapter,
|
|
57
|
+
searchAdapter: ctx.services.searchAdapter,
|
|
58
58
|
entityName: "product",
|
|
59
59
|
});
|
|
60
60
|
return crud.list(query.payload, query.user, ctx.db);
|
|
@@ -4,7 +4,7 @@ Tenant-owned secrets with envelope encryption, DEK cache, and KEK rotation.
|
|
|
4
4
|
|
|
5
5
|
## What it shows
|
|
6
6
|
|
|
7
|
-
- `r.secret` + `ctx.secrets.get` (audited reads)
|
|
7
|
+
- `r.secret` + `ctx.services.secrets.get` (audited reads)
|
|
8
8
|
- Charge-style handler that never returns the plaintext key
|
|
9
9
|
|
|
10
10
|
## Source
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// A miniature "billing" feature that reads a Stripe API key from ctx.secrets
|
|
1
|
+
// A miniature "billing" feature that reads a Stripe API key from ctx.services.secrets
|
|
2
2
|
// and simulates a charge. The point is to show how feature code uses the
|
|
3
3
|
// plaintext secret without ever exposing it to clients — the charge endpoint
|
|
4
4
|
// returns { chargeId } but never the apiKey.
|
|
@@ -27,7 +27,10 @@ const chargeWrite = defineWriteHandler({
|
|
|
27
27
|
}),
|
|
28
28
|
access: { roles: ["TenantAdmin", "User"] },
|
|
29
29
|
handler: async (event, ctx) => {
|
|
30
|
-
const secrets = requireSecretsContext(
|
|
30
|
+
const secrets = requireSecretsContext(
|
|
31
|
+
{ secrets: ctx.services.secrets, user: ctx.user },
|
|
32
|
+
"billing:write:charge",
|
|
33
|
+
);
|
|
31
34
|
// requireSecretsContext returned a wrapper that auto-fills the audit
|
|
32
35
|
// context (userId + "billing:write:charge") on every .get — the read
|
|
33
36
|
// lands in tenant_secret_reads without the handler having to remember.
|