@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
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts
CHANGED
|
@@ -39,6 +39,10 @@ import {
|
|
|
39
39
|
FALLBACK_CONSENT_LOCALE,
|
|
40
40
|
} from "../web/consumer-protection/consent-locale.ts";
|
|
41
41
|
import { notifyOperator } from "./termination-notify.ts";
|
|
42
|
+
import {
|
|
43
|
+
TERMINATION_PROVIDER_LOCK_KEY,
|
|
44
|
+
TERMINATION_PROVIDER_LOCK_NAMESPACE,
|
|
45
|
+
} from "./termination-provider-lock.ts";
|
|
42
46
|
import type { OperatorNoticeReason } from "./termination-texts.ts";
|
|
43
47
|
import { isWithinWithdrawalPeriod } from "./withdrawal-period.ts";
|
|
44
48
|
|
|
@@ -141,7 +145,7 @@ async function cancelAtProvider(
|
|
|
141
145
|
when,
|
|
142
146
|
});
|
|
143
147
|
} catch (error) {
|
|
144
|
-
ctx.log?.warn(
|
|
148
|
+
ctx.obs.log?.warn(
|
|
145
149
|
`[billing-foundation:record-contract-termination] provider cancel failed for tenant ${tenantId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
146
150
|
);
|
|
147
151
|
return { providerCancel: "none", effectiveAtIso: null, noticeReason: "provider_error" };
|
|
@@ -165,7 +169,7 @@ export function createDeclareContractTerminationHandler(): WriteHandlerDef {
|
|
|
165
169
|
handler: async (event, ctx) => {
|
|
166
170
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
167
171
|
const payload = event.payload as z.infer<typeof declareContractTerminationSchema>;
|
|
168
|
-
const stream = await ctx.fetchForWriting({
|
|
172
|
+
const stream = await ctx.events.fetchForWriting({
|
|
169
173
|
aggregateId: subscriptionAggregateId(event.user.tenantId),
|
|
170
174
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
171
175
|
});
|
|
@@ -189,38 +193,6 @@ function revokedRecordResult(receivedAtIso: string) {
|
|
|
189
193
|
};
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
// A revocation that landed during the provider cancel found nothing to take back yet.
|
|
193
|
-
async function takeBackLateRevokedCancel(
|
|
194
|
-
ctx: HandlerContext,
|
|
195
|
-
consumerProtection: ConsumerProtectionOptions,
|
|
196
|
-
args: {
|
|
197
|
-
readonly payload: z.infer<typeof recordContractTerminationSchema>;
|
|
198
|
-
readonly tenantId: string;
|
|
199
|
-
readonly receivedAtIso: string;
|
|
200
|
-
readonly providerCancel: ProviderCancelOutcome;
|
|
201
|
-
},
|
|
202
|
-
): Promise<void> {
|
|
203
|
-
const { payload, tenantId, receivedAtIso, providerCancel } = args;
|
|
204
|
-
// skip: nothing was cancelled at the provider, nothing to resume
|
|
205
|
-
if (providerCancel !== "period-end") return;
|
|
206
|
-
const reasons = await resumeAtProvider(ctx, await getSubscriptionForTenant(ctx, tenantId));
|
|
207
|
-
// skip: resumed cleanly, the revocation notice already tells the operator
|
|
208
|
-
if (reasons.length === 0) return;
|
|
209
|
-
await notifyOperator(ctx, consumerProtection, {
|
|
210
|
-
locale: payload.locale ?? FALLBACK_CONSENT_LOCALE,
|
|
211
|
-
requestId: payload.requestId,
|
|
212
|
-
receivedAtIso,
|
|
213
|
-
channel: "public",
|
|
214
|
-
declaration: {
|
|
215
|
-
declarationType: payload.declarationType,
|
|
216
|
-
terminationKind: payload.terminationKind,
|
|
217
|
-
},
|
|
218
|
-
reasons,
|
|
219
|
-
tenantId,
|
|
220
|
-
providerCancel,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
|
|
224
196
|
export function createRecordContractTerminationHandler(
|
|
225
197
|
options: ResolvedBillingFoundationOptions,
|
|
226
198
|
consumerProtection: ConsumerProtectionOptions,
|
|
@@ -234,6 +206,11 @@ export function createRecordContractTerminationHandler(
|
|
|
234
206
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
235
207
|
const payload = event.payload as z.infer<typeof recordContractTerminationSchema>;
|
|
236
208
|
const tenantId = event.user.tenantId;
|
|
209
|
+
// The provider cancel/resume and the stream append must not interleave across declarations of the same subscription.
|
|
210
|
+
await ctx.db.advisoryXactLock(
|
|
211
|
+
TERMINATION_PROVIDER_LOCK_NAMESPACE,
|
|
212
|
+
TERMINATION_PROVIDER_LOCK_KEY,
|
|
213
|
+
);
|
|
237
214
|
if (payload.channel === "public" && payload.when === SubscriptionCancelTimings.immediately) {
|
|
238
215
|
throw new UnprocessableError("immediate_cancel_not_allowed", {
|
|
239
216
|
message:
|
|
@@ -242,7 +219,7 @@ export function createRecordContractTerminationHandler(
|
|
|
242
219
|
}
|
|
243
220
|
|
|
244
221
|
const receivedAtIso = payload.receivedAtIso ?? options.now().toString();
|
|
245
|
-
const stream = await ctx.fetchForWriting({
|
|
222
|
+
const stream = await ctx.events.fetchForWriting({
|
|
246
223
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
247
224
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
248
225
|
});
|
|
@@ -277,30 +254,12 @@ export function createRecordContractTerminationHandler(
|
|
|
277
254
|
effectiveAtIso,
|
|
278
255
|
providerCancel,
|
|
279
256
|
};
|
|
280
|
-
// Re-fetch after the external call:
|
|
281
|
-
//
|
|
282
|
-
|
|
283
|
-
const streamAfterProviderCall = await ctx.fetchForWriting({
|
|
257
|
+
// Re-fetch after the external call: webhooks append to this stream without the lock, so a
|
|
258
|
+
// stale expectedVersion would fail the append although the provider already cancelled.
|
|
259
|
+
const streamAfterProviderCall = await ctx.events.fetchForWriting({
|
|
284
260
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
285
261
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
286
262
|
});
|
|
287
|
-
// A concurrent run with the same requestId may have recorded while we were
|
|
288
|
-
// at the provider; a second append would duplicate the event and the notice.
|
|
289
|
-
const recordedMeanwhile = findRecordedTermination(
|
|
290
|
-
streamAfterProviderCall.events,
|
|
291
|
-
payload.requestId,
|
|
292
|
-
);
|
|
293
|
-
if (recordedMeanwhile) return recordedTerminationResult(recordedMeanwhile);
|
|
294
|
-
// A revocation that landed during the provider call found no cancel to take back.
|
|
295
|
-
if (hasRevocation(streamAfterProviderCall.events, payload.requestId)) {
|
|
296
|
-
await takeBackLateRevokedCancel(ctx, consumerProtection, {
|
|
297
|
-
payload,
|
|
298
|
-
tenantId,
|
|
299
|
-
receivedAtIso,
|
|
300
|
-
providerCancel,
|
|
301
|
-
});
|
|
302
|
-
return revokedRecordResult(receivedAtIso);
|
|
303
|
-
}
|
|
304
263
|
await streamAfterProviderCall.appendOne({
|
|
305
264
|
type: CONTRACT_TERMINATION_REQUESTED_EVENT_QN,
|
|
306
265
|
payload: recorded,
|
|
@@ -389,7 +348,7 @@ export async function resumeAtProvider(
|
|
|
389
348
|
providerSubscriptionId: subscription.providerSubscriptionId,
|
|
390
349
|
});
|
|
391
350
|
} catch (error) {
|
|
392
|
-
ctx.log?.warn(
|
|
351
|
+
ctx.obs.log?.warn(
|
|
393
352
|
`[billing-foundation:revoke-contract-termination] provider resume failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
394
353
|
);
|
|
395
354
|
return ["revocation_resume_failed"];
|
|
@@ -414,7 +373,7 @@ export function createRecordUnmatchedContractTerminationHandler(): WriteHandlerD
|
|
|
414
373
|
handler: async (event, ctx) => {
|
|
415
374
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
416
375
|
const payload = event.payload as z.infer<typeof recordUnmatchedContractTerminationSchema>;
|
|
417
|
-
const stream = await ctx.fetchForWriting({
|
|
376
|
+
const stream = await ctx.events.fetchForWriting({
|
|
418
377
|
aggregateId: terminationUnmatchedAggregateId(payload.requestId),
|
|
419
378
|
aggregateType: CONTRACT_TERMINATION_UNMATCHED_AGGREGATE_TYPE,
|
|
420
379
|
});
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts
CHANGED
|
@@ -160,11 +160,13 @@ async function findTerminableTenantIds(
|
|
|
160
160
|
"anonymous declarant has no session — finds the contract by email via user:find-for-auth and tenant:memberships as a system user; the calling handler declares its own escapeHatch",
|
|
161
161
|
});
|
|
162
162
|
const user = authUserRowSchema.safeParse(
|
|
163
|
-
await ctx.queryAs(createSystemUser(requestTenantId), UserQueries.findForAuth, {
|
|
163
|
+
await ctx.dispatch.queryAs(createSystemUser(requestTenantId), UserQueries.findForAuth, {
|
|
164
|
+
email,
|
|
165
|
+
}),
|
|
164
166
|
);
|
|
165
167
|
if (!user.success || user.data.isDeleted === true) return [];
|
|
166
168
|
const memberships = membershipRowsSchema.safeParse(
|
|
167
|
-
await ctx.queryAs(createSystemUser(requestTenantId), TenantQueries.memberships, {
|
|
169
|
+
await ctx.dispatch.queryAs(createSystemUser(requestTenantId), TenantQueries.memberships, {
|
|
168
170
|
userId: user.data.id,
|
|
169
171
|
}),
|
|
170
172
|
);
|
|
@@ -180,7 +182,7 @@ async function findTerminableTenantIds(
|
|
|
180
182
|
const terminable: string[] = [];
|
|
181
183
|
for (const tenantId of adminTenantIds) {
|
|
182
184
|
const probe = terminableResultSchema.safeParse(
|
|
183
|
-
await ctx.queryAs(
|
|
185
|
+
await ctx.dispatch.queryAs(
|
|
184
186
|
createSystemUser(tenantId),
|
|
185
187
|
SubscriptionFoundationQueries.terminableSubscription,
|
|
186
188
|
{},
|
|
@@ -205,7 +207,7 @@ export function createRequestContractTerminationHandler(
|
|
|
205
207
|
...(consumerProtection.terminationScope === "platform" && { tenantlessAnonymous: true }),
|
|
206
208
|
escapeHatch: {
|
|
207
209
|
reason:
|
|
208
|
-
"Anonymous declarant has no session — finds the contract by email via ctx.queryAs(system, user:find-for-auth) and tenant:memberships; the tenant written to comes only from those lookups.",
|
|
210
|
+
"Anonymous declarant has no session — finds the contract by email via ctx.dispatch.queryAs(system, user:find-for-auth) and tenant:memberships; the tenant written to comes only from those lookups.",
|
|
209
211
|
},
|
|
210
212
|
handler: async (event, ctx) => {
|
|
211
213
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
@@ -228,7 +230,7 @@ export function createRequestContractTerminationHandler(
|
|
|
228
230
|
if (candidates.length === 1 && onlyCandidate !== undefined) {
|
|
229
231
|
matchedTenantId = onlyCandidate;
|
|
230
232
|
const revocation = createRevocationToken();
|
|
231
|
-
const declared = await ctx.writeAs(
|
|
233
|
+
const declared = await ctx.dispatch.writeAs(
|
|
232
234
|
createSystemUser(onlyCandidate),
|
|
233
235
|
SubscriptionFoundationHandlers.declareContractTermination,
|
|
234
236
|
{
|
|
@@ -253,7 +255,7 @@ export function createRequestContractTerminationHandler(
|
|
|
253
255
|
}
|
|
254
256
|
} else {
|
|
255
257
|
reasons.push(candidates.length === 0 ? "unmatched" : "ambiguous");
|
|
256
|
-
const recorded = await ctx.writeAs(
|
|
258
|
+
const recorded = await ctx.dispatch.writeAs(
|
|
257
259
|
createSystemUser(SYSTEM_TENANT_ID),
|
|
258
260
|
SubscriptionFoundationHandlers.recordUnmatchedContractTermination,
|
|
259
261
|
{
|
|
@@ -318,7 +320,7 @@ export function createTerminateContractHandler(
|
|
|
318
320
|
const { declarationType, terminationKind } = payload;
|
|
319
321
|
|
|
320
322
|
const caller = authUserRowSchema.safeParse(
|
|
321
|
-
await ctx.queryAs(createSystemUser(tenantId), UserQueries.findForAuth, {
|
|
323
|
+
await ctx.dispatch.queryAs(createSystemUser(tenantId), UserQueries.findForAuth, {
|
|
322
324
|
id: event.user.id,
|
|
323
325
|
}),
|
|
324
326
|
);
|
|
@@ -332,7 +334,7 @@ export function createTerminateContractHandler(
|
|
|
332
334
|
});
|
|
333
335
|
}
|
|
334
336
|
|
|
335
|
-
const recorded = await ctx.writeAs(
|
|
337
|
+
const recorded = await ctx.dispatch.writeAs(
|
|
336
338
|
createSystemUser(tenantId),
|
|
337
339
|
SubscriptionFoundationHandlers.recordContractTermination,
|
|
338
340
|
{
|
package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts
CHANGED
|
@@ -24,6 +24,11 @@ import type { ConsumerProtectionOptions, ResolvedBillingFoundationOptions } from
|
|
|
24
24
|
import { SubscriptionFoundationHandlers } from "../web/constants.ts";
|
|
25
25
|
import type { ConsentLocale } from "./consent-text.ts";
|
|
26
26
|
import { notifyOperator } from "./termination-notify.ts";
|
|
27
|
+
import { hasOtherOpenProviderCancel } from "./termination-open-cancels.ts";
|
|
28
|
+
import {
|
|
29
|
+
TERMINATION_PROVIDER_LOCK_KEY,
|
|
30
|
+
TERMINATION_PROVIDER_LOCK_NAMESPACE,
|
|
31
|
+
} from "./termination-provider-lock.ts";
|
|
27
32
|
import { findRecordedTermination, hasRevocation, resumeAtProvider } from "./termination-record.ts";
|
|
28
33
|
import type { OperatorNoticeReason } from "./termination-texts.ts";
|
|
29
34
|
|
|
@@ -103,7 +108,7 @@ export function createRevokeContractTerminationHandler(): WriteHandlerDef {
|
|
|
103
108
|
handler: async (event, ctx) => {
|
|
104
109
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
105
110
|
const payload = event.payload as z.infer<typeof revokeContractTerminationSchema>;
|
|
106
|
-
const result = await ctx.writeAs(
|
|
111
|
+
const result = await ctx.dispatch.writeAs(
|
|
107
112
|
createSystemUser(payload.tenantId),
|
|
108
113
|
SubscriptionFoundationHandlers.recordContractTerminationRevocation,
|
|
109
114
|
{ requestId: payload.requestId, token: payload.token },
|
|
@@ -129,11 +134,16 @@ export function createRecordContractTerminationRevocationHandler(
|
|
|
129
134
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
130
135
|
const payload = event.payload as z.infer<typeof recordContractTerminationRevocationSchema>;
|
|
131
136
|
const tenantId = event.user.tenantId;
|
|
137
|
+
// The provider cancel/resume and the stream append must not interleave across declarations of the same subscription.
|
|
138
|
+
await ctx.db.advisoryXactLock(
|
|
139
|
+
TERMINATION_PROVIDER_LOCK_NAMESPACE,
|
|
140
|
+
TERMINATION_PROVIDER_LOCK_KEY,
|
|
141
|
+
);
|
|
132
142
|
const outcome = (value: RevocationOutcome) => ({
|
|
133
143
|
isSuccess: true as const,
|
|
134
144
|
data: { outcome: value },
|
|
135
145
|
});
|
|
136
|
-
const stream = await ctx.fetchForWriting({
|
|
146
|
+
const stream = await ctx.events.fetchForWriting({
|
|
137
147
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
138
148
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
139
149
|
});
|
|
@@ -162,25 +172,18 @@ export function createRecordContractTerminationRevocationHandler(
|
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
const reasons: OperatorNoticeReason[] = ["revoked"];
|
|
165
|
-
if (
|
|
175
|
+
if (
|
|
176
|
+
requested?.providerCancel === "period-end" &&
|
|
177
|
+
!hasOtherOpenProviderCancel(stream.events, payload.requestId)
|
|
178
|
+
) {
|
|
166
179
|
reasons.push(...(await resumeAtProvider(ctx, subscription)));
|
|
167
180
|
}
|
|
168
181
|
// Re-fetch after the external call, like record-contract-termination.
|
|
169
|
-
const streamAfterProviderCall = await ctx.fetchForWriting({
|
|
182
|
+
const streamAfterProviderCall = await ctx.events.fetchForWriting({
|
|
170
183
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
171
184
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
172
185
|
});
|
|
173
|
-
|
|
174
|
-
const requestedMeanwhile =
|
|
175
|
-
requested === undefined
|
|
176
|
-
? findRecordedTermination(streamAfterProviderCall.events, payload.requestId)
|
|
177
|
-
: undefined;
|
|
178
|
-
if (requestedMeanwhile?.providerCancel === "period-end") {
|
|
179
|
-
reasons.push(
|
|
180
|
-
...(await resumeAtProvider(ctx, await getSubscriptionForTenant(ctx, tenantId))),
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
const providerCancel = (requested ?? requestedMeanwhile)?.providerCancel;
|
|
186
|
+
const providerCancel = requested?.providerCancel;
|
|
184
187
|
const revoked: ContractTerminationRevokedPayload = {
|
|
185
188
|
requestId: payload.requestId,
|
|
186
189
|
revokedAtIso: now.toString(),
|
|
@@ -19,7 +19,7 @@ import { CONSENT_LOCALES } from "./web/consumer-protection/consent-locale.ts";
|
|
|
19
19
|
export const SUBSCRIPTION_AGGREGATE_TYPE = "subscription" as const;
|
|
20
20
|
|
|
21
21
|
// Event-name constants — short form (for r.defineEvent) and qualified
|
|
22
|
-
// FQN (for ctx.
|
|
22
|
+
// FQN (for ctx.events.unsafeAppend and projection-apply keys).
|
|
23
23
|
export const SUBSCRIPTION_CREATED_EVENT_SHORT = "subscription-created" as const;
|
|
24
24
|
export const SUBSCRIPTION_UPDATED_EVENT_SHORT = "subscription-updated" as const;
|
|
25
25
|
export const SUBSCRIPTION_CANCELED_EVENT_SHORT = "subscription-canceled" as const;
|
|
@@ -172,7 +172,7 @@ export function createBillingFoundationFeature<TTier extends string = string>(
|
|
|
172
172
|
r.defineEvent(PAYMENT_RECEIVED_EVENT_SHORT, paymentEventPayloadSchema, { piiFields: "none" });
|
|
173
173
|
|
|
174
174
|
// Inline projection: materialized current state in `read_subscriptions`.
|
|
175
|
-
// Apply runs in the same TX as ctx.
|
|
175
|
+
// Apply runs in the same TX as ctx.events.unsafeAppend — read-your-own-
|
|
176
176
|
// write without a dispatcher tick.
|
|
177
177
|
r.projection({
|
|
178
178
|
name: "subscription",
|
|
@@ -260,13 +260,18 @@ export function createBillingFoundationFeature<TTier extends string = string>(
|
|
|
260
260
|
perTenant: true,
|
|
261
261
|
runOnBoot: true,
|
|
262
262
|
handler: async (_payload, ctx) => {
|
|
263
|
-
const result = await ctx.write(
|
|
263
|
+
const result = await ctx.dispatch.write(
|
|
264
|
+
SubscriptionFoundationHandlers.syncSubscription,
|
|
265
|
+
{},
|
|
266
|
+
);
|
|
264
267
|
if (!result.isSuccess) {
|
|
265
268
|
throw new Error(
|
|
266
269
|
`billing-foundation:sync-subscriptions: sync-subscription write failed: ${JSON.stringify(result.error)}`,
|
|
267
270
|
);
|
|
268
271
|
}
|
|
269
|
-
ctx.log.info(
|
|
272
|
+
ctx.obs.log.info(
|
|
273
|
+
`[billing-foundation:sync-subscriptions] ${JSON.stringify(result.data)}`,
|
|
274
|
+
);
|
|
270
275
|
},
|
|
271
276
|
});
|
|
272
277
|
|
|
@@ -29,7 +29,7 @@ export type SubscriptionView = {
|
|
|
29
29
|
* aggregate ID), or null if the tenant has never subscribed. The status
|
|
30
30
|
* may be active/canceled/past_due/etc.; the caller decides what to do. */
|
|
31
31
|
export async function getSubscriptionForTenant(
|
|
32
|
-
ctx: HandlerContext,
|
|
32
|
+
ctx: Pick<HandlerContext, "db">,
|
|
33
33
|
tenantId: string,
|
|
34
34
|
): Promise<SubscriptionView | null> {
|
|
35
35
|
const aggId = subscriptionAggregateId(tenantId);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// with no second append.
|
|
10
10
|
// 2. Type mapping: SubscriptionEvent.type (= normalized by the plugin)
|
|
11
11
|
// → one of the 5 ES event types.
|
|
12
|
-
// 3. ctx.
|
|
12
|
+
// 3. ctx.events.unsafeAppend — the inline projection materializes the
|
|
13
13
|
// `read_subscriptions` row in the same TX.
|
|
14
14
|
|
|
15
15
|
import {
|
|
@@ -175,7 +175,7 @@ export async function appendSubscriptionEvent(
|
|
|
175
175
|
// streams, optimize via snapshot or a per-tenant dedup table as the
|
|
176
176
|
// idempotency anchor (like cap-counter).
|
|
177
177
|
// ---------------------------------------------------------------
|
|
178
|
-
const existingEvents = await ctx.
|
|
178
|
+
const existingEvents = await ctx.events.load(aggId);
|
|
179
179
|
const alreadySeen = existingEvents.some((e) => {
|
|
180
180
|
const headers = e.metadata.headers ?? {};
|
|
181
181
|
return (
|
|
@@ -191,7 +191,7 @@ export async function appendSubscriptionEvent(
|
|
|
191
191
|
// late delivery: appending it would roll the row back (a canceled subscription
|
|
192
192
|
// would come back active). Invoice events carry the live state, not a snapshot.
|
|
193
193
|
if (isLateStateEvent(existingEvents, payload)) {
|
|
194
|
-
ctx.log?.warn(
|
|
194
|
+
ctx.obs.log?.warn(
|
|
195
195
|
`[billing-foundation:process-event] dropped late ${payload.type} ${payload.providerEventId}: a newer provider event is already stored`,
|
|
196
196
|
);
|
|
197
197
|
return { duplicate: false, stale: true, subscriptionAggregateId: aggId };
|
|
@@ -260,7 +260,7 @@ export async function appendSubscriptionEvent(
|
|
|
260
260
|
providerEventId: payload.providerEventId,
|
|
261
261
|
providerName: payload.providerName,
|
|
262
262
|
};
|
|
263
|
-
await ctx.
|
|
263
|
+
await ctx.events.unsafeAppend({
|
|
264
264
|
aggregateId: aggId,
|
|
265
265
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
266
266
|
type: esEventType,
|
|
@@ -311,7 +311,7 @@ export function createProcessEventHandler(now: () => Temporal.Instant): WriteHan
|
|
|
311
311
|
const payload = event.payload as ProcessEventPayload;
|
|
312
312
|
const current = await getSubscriptionForTenant(ctx, event.user.tenantId);
|
|
313
313
|
if (isStaleSubscriptionEvent(current, payload, now())) {
|
|
314
|
-
ctx.log?.warn(
|
|
314
|
+
ctx.obs.log?.warn(
|
|
315
315
|
`[billing-foundation:process-event] dropped stale ${payload.type} ${payload.providerEventId}: tenant already tracks another subscription`,
|
|
316
316
|
);
|
|
317
317
|
return {
|
package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// 1. Idempotency-check: loads the payment-stream and scans for an
|
|
7
7
|
// already-seen `metadata.providerEventId`. A provider-replay with
|
|
8
8
|
// the same event-id → duplicate=true, no second append.
|
|
9
|
-
// 2. ctx.
|
|
9
|
+
// 2. ctx.events.unsafeAppend — inline-projection materializes a new
|
|
10
10
|
// `read_payments`-row (INSERT-once, not UPSERT — every payment is
|
|
11
11
|
// its own fact, not a state-update).
|
|
12
12
|
|
|
@@ -55,7 +55,7 @@ export const processPaymentEventHandler: WriteHandlerDef = {
|
|
|
55
55
|
// (a tenant with hundreds of purchases scans hundreds of events;
|
|
56
56
|
// optimize via a per-tenant providerEventId-index if that bites).
|
|
57
57
|
// ---------------------------------------------------------------
|
|
58
|
-
const existingEvents = await ctx.
|
|
58
|
+
const existingEvents = await ctx.events.load(aggId);
|
|
59
59
|
const alreadySeen = existingEvents.some((e) => {
|
|
60
60
|
const headers = e.metadata.headers ?? {};
|
|
61
61
|
return (
|
|
@@ -106,7 +106,7 @@ export const processPaymentEventHandler: WriteHandlerDef = {
|
|
|
106
106
|
providerCheckoutId: payload.providerCheckoutId,
|
|
107
107
|
}),
|
|
108
108
|
};
|
|
109
|
-
await ctx.
|
|
109
|
+
await ctx.events.unsafeAppend({
|
|
110
110
|
aggregateId: aggId,
|
|
111
111
|
aggregateType: PAYMENT_AGGREGATE_TYPE,
|
|
112
112
|
type: PAYMENT_RECEIVED_EVENT_QN,
|
|
@@ -77,7 +77,7 @@ export async function resolvePlanPrices(
|
|
|
77
77
|
prices = await plugin.retrievePrices(ctx, allPriceIds);
|
|
78
78
|
} catch (error) {
|
|
79
79
|
const message = error instanceof Error ? error.message : String(error);
|
|
80
|
-
ctx.log?.warn("billing-foundation: retrievePrices threw, all plan prices unavailable", {
|
|
80
|
+
ctx.obs.log?.warn("billing-foundation: retrievePrices threw, all plan prices unavailable", {
|
|
81
81
|
message,
|
|
82
82
|
});
|
|
83
83
|
return result;
|
|
@@ -93,7 +93,7 @@ export async function resolvePlanPrices(
|
|
|
93
93
|
const chosen = active[0];
|
|
94
94
|
if (chosen) result.set(tier, { priceId: chosen.priceId, price: chosen });
|
|
95
95
|
} else if (active.length > 1) {
|
|
96
|
-
ctx.log?.warn(
|
|
96
|
+
ctx.obs.log?.warn(
|
|
97
97
|
`billing-foundation: tier "${tier}" has ${active.length} active flat-amount prices — expected exactly one, price stays unavailable`,
|
|
98
98
|
);
|
|
99
99
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Inline projection for `read_subscriptions`. Materializes the 5
|
|
2
2
|
// subscription events into one row per tenant.
|
|
3
3
|
//
|
|
4
|
-
// Apply runs in the same transaction as ctx.
|
|
4
|
+
// Apply runs in the same transaction as ctx.events.unsafeAppend, so the caller
|
|
5
5
|
// sees its write state immediately (no dispatcher tick required). The PK is
|
|
6
6
|
// event.aggregateId (= deterministic uuidv5 per tenant), so replays collide
|
|
7
7
|
// on the PK instead of creating duplicate rows.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// 2. **Post-tenant-resolution:** `createPortalSession` +
|
|
19
19
|
// `cancelSubscription` are called from regular write handlers with
|
|
20
20
|
// a fully resolved HandlerContext. The plugin can use ctx.config +
|
|
21
|
-
// ctx.secrets for tenant-specific configuration (e.g. tenant-owned
|
|
21
|
+
// ctx.services.secrets for tenant-specific configuration (e.g. tenant-owned
|
|
22
22
|
// customer-id mappings).
|
|
23
23
|
//
|
|
24
24
|
// Foundation uses only the common subset of provider functionality;
|
|
@@ -38,7 +38,7 @@ export function capCounterAggregateId(
|
|
|
38
38
|
* different uuidv5 namespace, different output UUID. This also prevents an
|
|
39
39
|
* accidental calendar increment from landing on the rolling stream.
|
|
40
40
|
*
|
|
41
|
-
* **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.write`) calls
|
|
41
|
+
* **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.dispatch.write`) calls
|
|
42
42
|
* this with tenantId + capName and appends increment events to the stream.
|
|
43
43
|
* Race-free because the event store appends with an auto-incrementing version.
|
|
44
44
|
*/
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
createEntityExecutor,
|
|
6
6
|
type HandlerContext,
|
|
7
7
|
type JobContext,
|
|
8
|
+
type ReservationContext,
|
|
8
9
|
type SessionUser,
|
|
9
10
|
type TenantId,
|
|
10
11
|
type WriteResult,
|
|
@@ -84,7 +85,7 @@ export type BookCapUsageOptions = {
|
|
|
84
85
|
readonly guardCurrentValue?: (currentValue: number) => void;
|
|
85
86
|
};
|
|
86
87
|
|
|
87
|
-
export type CapCounterContext = HandlerContext | JobContext;
|
|
88
|
+
export type CapCounterContext = HandlerContext | JobContext | ReservationContext;
|
|
88
89
|
|
|
89
90
|
export type CapCounterScope = {
|
|
90
91
|
readonly user: SessionUser;
|
|
@@ -104,7 +105,7 @@ function assertDbServesUser(db: TenantDb, user: SessionUser): TenantDb {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
// Jobs write in place (their db is not transaction-bound); systemScope handlers reach both dbs via
|
|
107
|
-
// systemDb, because their ctx.db / ctx.
|
|
108
|
+
// systemDb, because their ctx.db / ctx.db.outsideTransaction are fail-closed proxies.
|
|
108
109
|
export function resolveCapCounterScope(
|
|
109
110
|
ctx: CapCounterContext,
|
|
110
111
|
outsideTransaction: boolean,
|
|
@@ -130,11 +131,11 @@ export function resolveCapCounterScope(
|
|
|
130
131
|
: readDb;
|
|
131
132
|
return { user, readDb, writeDb };
|
|
132
133
|
}
|
|
133
|
-
const writeDb = outsideTransaction ? ctx.
|
|
134
|
+
const writeDb = outsideTransaction ? ctx.db.outsideTransaction : ctx.db;
|
|
134
135
|
if (!writeDb) {
|
|
135
136
|
throw new Error(
|
|
136
137
|
outsideTransaction
|
|
137
|
-
? "cap-counter: outsideTransaction requested but ctx.
|
|
138
|
+
? "cap-counter: outsideTransaction requested but ctx.db.outsideTransaction is undefined"
|
|
138
139
|
: "cap-counter: ctx.db missing — run inside a handler context",
|
|
139
140
|
);
|
|
140
141
|
}
|
|
@@ -350,6 +350,21 @@ function bookCalendarDelta(
|
|
|
350
350
|
);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
+
// Asked after a missed or lost claim. A released tombstone committed since the first read does not
|
|
354
|
+
// count: the caller's own tombstone insert then conflicts with it, and the retry claims that row.
|
|
355
|
+
async function hasSettledTombstone(
|
|
356
|
+
txDb: TenantDb,
|
|
357
|
+
user: SessionUser,
|
|
358
|
+
id: string,
|
|
359
|
+
): Promise<boolean> {
|
|
360
|
+
const settled = await txDb.fetchOne<CapReservationRow>(capReservationsTable, {
|
|
361
|
+
id,
|
|
362
|
+
tenantId: user.tenantId,
|
|
363
|
+
settledAt: { ne: null },
|
|
364
|
+
});
|
|
365
|
+
return settled !== undefined;
|
|
366
|
+
}
|
|
367
|
+
|
|
353
368
|
async function settleInTransaction(
|
|
354
369
|
txDb: TenantDb,
|
|
355
370
|
user: SessionUser,
|
|
@@ -386,12 +401,10 @@ async function settleInTransaction(
|
|
|
386
401
|
);
|
|
387
402
|
return booked;
|
|
388
403
|
}
|
|
389
|
-
|
|
390
|
-
if (await txDb.fetchOne<CapReservationRow>(capReservationsTable, ownRow)) {
|
|
391
|
-
return NOTHING_TO_RELEASE;
|
|
392
|
-
}
|
|
404
|
+
if (await hasSettledTombstone(txDb, user, id)) return NOTHING_TO_RELEASE;
|
|
393
405
|
// Row swept or released: its amount was given back, so the full actual amount is booked and a
|
|
394
|
-
// tombstone makes a repeated settle a no-op. A parallel settle inserting first fails
|
|
406
|
+
// tombstone makes a repeated settle a no-op. A parallel settle or consume inserting first fails
|
|
407
|
+
// this insert.
|
|
395
408
|
// Booking first: a lost counter race returns a result instead of throwing, so the transaction
|
|
396
409
|
// commits and a tombstone written before it would turn the retry into a no-op.
|
|
397
410
|
const booked = await bookCalendarDelta(txDb, user, options, options.actualAmount);
|
|
@@ -497,7 +510,7 @@ async function consumeClaimedRow(
|
|
|
497
510
|
}
|
|
498
511
|
|
|
499
512
|
// Row already expired away: the units are booked directly and a released tombstone remembers them
|
|
500
|
-
// so a later settle subtracts them. A parallel consume inserting first fails this insert.
|
|
513
|
+
// so a later settle subtracts them. A parallel consume or settle inserting first fails this insert.
|
|
501
514
|
async function consumeWithoutRow(
|
|
502
515
|
txDb: TenantDb,
|
|
503
516
|
user: SessionUser,
|
|
@@ -542,7 +555,7 @@ async function consumeInTransaction(
|
|
|
542
555
|
}
|
|
543
556
|
const claimed = open && (await claimReservationRow(txDb, user, open));
|
|
544
557
|
if (claimed) return consumeClaimedRow(txDb, user, claimed, options);
|
|
545
|
-
if (await txDb
|
|
558
|
+
if (await hasSettledTombstone(txDb, user, id)) {
|
|
546
559
|
throw new CapReservationSettledError(options.capName);
|
|
547
560
|
}
|
|
548
561
|
return consumeWithoutRow(txDb, user, id, options);
|
|
@@ -599,7 +612,7 @@ export async function releaseExpiredCapReservations(
|
|
|
599
612
|
try {
|
|
600
613
|
await releaseCapReservation(ctx, id, outsideTransaction);
|
|
601
614
|
} catch (error) {
|
|
602
|
-
ctx.log?.error("releasing an expired cap reservation failed", { id, capName, error });
|
|
615
|
+
ctx.obs.log?.error("releasing an expired cap reservation failed", { id, capName, error });
|
|
603
616
|
}
|
|
604
617
|
}
|
|
605
618
|
}
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.389.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Settle and consume racing on a swept reservation book their units",
|
|
6
|
+
"detail": "After the sweep deleted a reservation's row, a consume could commit its released tombstone between the two reads of a parallel settle or consume. That call took the released tombstone for a settled one, so settle reported success without booking its amount and a second consume was rejected with cap_reservation_settled. Only a settled tombstone counts as settled now; a released one runs into the insert conflict and the retry books against it."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.389.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "Settle after a sweep survives a lost counter race",
|
|
12
|
+
"detail": "The amount is booked first and the tombstone written afterwards, so a version conflict on the counter retries the whole settle instead of leaving a tombstone that turns the retry into a no-op and drops the usage from the cap."
|
|
13
|
+
},
|
|
2
14
|
{
|
|
3
15
|
"version": "0.388.0",
|
|
4
16
|
"type": "fix",
|
|
@@ -13,7 +13,7 @@ export const CAP_COUNTER_ROLLING_AGGREGATE_TYPE = "cap-counter-rolling" as const
|
|
|
13
13
|
// Custom event types for rolling-window counters. The pair is symmetric:
|
|
14
14
|
// _SHORT — matches `r.defineEvent(short, schema)` in the registrar
|
|
15
15
|
// (the framework automatically prefixes it to the QN)
|
|
16
|
-
// _QN — qualified form for `ctx.
|
|
16
|
+
// _QN — qualified form for `ctx.events.unsafeAppend({type})`, the
|
|
17
17
|
// `events.type` column, and `registry.getEvent(qn)` lookups
|
|
18
18
|
// Both MUST stay consistent; the feature test pins against drift.
|
|
19
19
|
export const ROLLING_INCREMENTED_EVENT_SHORT = "rolling-incremented" as const;
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
// ai-foundation are BYOK by default and currently have no platform pool
|
|
32
32
|
// to count. cap-counter is generic: an app owner who wants to use the
|
|
33
33
|
// counter calls `bookCapUsage(...)` /
|
|
34
|
-
// `ctx.write(CapCounterHandlers.incrementRolling, ...)` from their own handler.
|
|
34
|
+
// `ctx.dispatch.write(CapCounterHandlers.incrementRolling, ...)` from their own handler.
|
|
35
35
|
// - **No reset cron for the calendar period.** It works without one: the
|
|
36
36
|
// periodStartIso part of the aggregate id rolls over to a fresh counter
|
|
37
37
|
// at the period boundary. Old rows remain for auditing.
|
|
@@ -71,6 +71,7 @@ export function createCapCounterFeature() {
|
|
|
71
71
|
return defineFeature(
|
|
72
72
|
CAP_COUNTER_FEATURE,
|
|
73
73
|
(r) => {
|
|
74
|
+
r.adoptsBundledLists();
|
|
74
75
|
r.describe(
|
|
75
76
|
"Tracks per-tenant usage against configurable limits using two complementary storage models: calendar-period counters (one projection row per tenant/capName/period, reset implicitly by period rollover) and rolling-window counters (append-only event stream, no projection). Use `enforceCap` / `enforceRollingCap` (or the `withCapEnforcement` / `withRollingCapEnforcement` handler wrappers) in your write-handlers to check limits with soft-warn and hard-block tolerances; call `enforceCapAndMaybeNotify` when you also want to trigger a delivery notification on soft-threshold hits.",
|
|
76
77
|
);
|
|
@@ -85,7 +86,7 @@ export function createCapCounterFeature() {
|
|
|
85
86
|
});
|
|
86
87
|
|
|
87
88
|
// Custom domain event for the rolling counter. r.defineEvent registers the
|
|
88
|
-
// schema with the registry; ctx.
|
|
89
|
+
// schema with the registry; ctx.events.unsafeAppend in the handler reuses the
|
|
89
90
|
// same schema for append-time validation. QN after prefixing:
|
|
90
91
|
// "cap-counter:event:rolling-incremented" (see
|
|
91
92
|
// ROLLING_INCREMENTED_EVENT_QN).
|
|
@@ -71,7 +71,7 @@ export const incrementRollingCapHandler: WriteHandlerDef = {
|
|
|
71
71
|
// unsafeAppendEvent: the strict appendEvent is bound to an app's own event map, which
|
|
72
72
|
// bundled-features cannot see. Schema validation still runs because r.defineEvent
|
|
73
73
|
// registered the schema.
|
|
74
|
-
await ctx.
|
|
74
|
+
await ctx.events.unsafeAppend({
|
|
75
75
|
aggregateId,
|
|
76
76
|
aggregateType: CAP_COUNTER_ROLLING_AGGREGATE_TYPE,
|
|
77
77
|
type: ROLLING_INCREMENTED_EVENT_QN,
|
|
@@ -30,7 +30,7 @@ export function chainReservations(
|
|
|
30
30
|
try {
|
|
31
31
|
await innerHandle.release();
|
|
32
32
|
} catch (releaseError) {
|
|
33
|
-
ctx.log?.error("releasing the inner cap reservation failed", { error: releaseError });
|
|
33
|
+
ctx.obs.log?.error("releasing the inner cap reservation failed", { error: releaseError });
|
|
34
34
|
}
|
|
35
35
|
throw error;
|
|
36
36
|
}
|