@cosmicdrift/kumiko-bundled-features 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/dist/agent-tools/index.d.ts +1 -1
- package/dist/agent-tools/index.js +1 -1
- package/dist/agent-tools/tool-dispatch.d.ts +8 -3
- package/dist/agent-tools/tool-dispatch.js +36 -25
- package/dist/audit/feature.js +2 -1
- package/dist/auth-email-password/auth-user-row.js +1 -1
- package/dist/auth-email-password/bootstrap.js +6 -1
- package/dist/auth-email-password/feature.js +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +7 -7
- package/dist/auth-email-password/handlers/confirm-account-unlock.write.js +3 -3
- package/dist/auth-email-password/handlers/confirm-token-flow.js +4 -4
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +8 -7
- package/dist/auth-email-password/handlers/invite-accept.write.js +10 -7
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
- package/dist/auth-email-password/handlers/invite-info.query.js +2 -2
- package/dist/auth-email-password/handlers/invite-signup-complete.write.js +8 -7
- package/dist/auth-email-password/handlers/login.write.js +17 -15
- package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -0
- package/dist/auth-email-password/handlers/request-account-unlock.write.js +1 -0
- package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -0
- package/dist/auth-email-password/handlers/request-email-verification.write.js +1 -0
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -0
- package/dist/auth-email-password/handlers/request-password-reset.write.js +1 -0
- package/dist/auth-email-password/handlers/reset-password.write.js +2 -2
- package/dist/auth-email-password/handlers/signup-confirm.write.js +12 -12
- package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -0
- package/dist/auth-email-password/handlers/signup-request.write.js +16 -8
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +4 -2
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +2 -0
- package/dist/auth-email-password/handlers/token-request-handler.js +7 -2
- package/dist/auth-email-password/handlers/verify-email.write.js +2 -2
- package/dist/auth-email-password/magic-link-mail.d.ts +2 -0
- package/dist/auth-email-password/magic-link-mail.js +7 -3
- package/dist/auth-email-password/seeding.d.ts +1 -1
- package/dist/auth-email-password/seeding.js +9 -8
- package/dist/auth-email-password/signup-activation.d.ts +1 -0
- package/dist/auth-email-password/signup-activation.js +1 -0
- package/dist/auth-email-password/web/auth-client.d.ts +2 -2
- package/dist/auth-email-password/web/auth-client.js +8 -4
- package/dist/auth-email-password/web/auth-redirect.d.ts +5 -1
- package/dist/auth-email-password/web/auth-redirect.js +20 -25
- package/dist/auth-email-password/web/default-auth-pages-gate.js +6 -3
- package/dist/auth-email-password/web/forgot-password-screen.d.ts +4 -1
- package/dist/auth-email-password/web/forgot-password-screen.js +7 -2
- package/dist/auth-email-password/web/index.d.ts +1 -0
- package/dist/auth-email-password/web/index.js +1 -0
- package/dist/auth-email-password/web/login-screen.js +11 -6
- package/dist/auth-email-password/web/reset-password-screen.js +7 -1
- package/dist/auth-email-password/web/return-path.d.ts +4 -0
- package/dist/auth-email-password/web/return-path.js +34 -0
- package/dist/auth-email-password/web/session.js +6 -1
- package/dist/auth-email-password/web/signup-complete-screen.js +11 -3
- package/dist/auth-email-password/web/signup-screen.d.ts +4 -1
- package/dist/auth-email-password/web/signup-screen.js +8 -3
- package/dist/auth-foundation/feature.js +12 -4
- package/dist/auth-mfa/config.d.ts +1 -1
- package/dist/auth-mfa/config.js +3 -7
- package/dist/auth-mfa/handlers/disable.write.js +5 -5
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +19 -14
- package/dist/auth-mfa/handlers/enable-confirm.write.js +3 -3
- package/dist/auth-mfa/handlers/enable-start-preauth.write.js +6 -1
- package/dist/auth-mfa/handlers/enable-start.write.js +1 -1
- package/dist/auth-mfa/handlers/reencrypt.job.js +14 -9
- package/dist/auth-mfa/handlers/regenerate-recovery.write.js +4 -4
- package/dist/auth-mfa/handlers/verify.write.js +19 -14
- package/dist/auth-mfa/mfa-code-verifier.js +9 -4
- package/dist/auth-mfa/mfa-status-checker.js +13 -6
- package/dist/auth-mfa/verify-factor.js +1 -1
- package/dist/billing-foundation/billing-info-query.js +2 -2
- package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -1
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +5 -5
- package/dist/billing-foundation/consumer-protection/register-consent.js +1 -1
- package/dist/billing-foundation/consumer-protection/register-termination.js +1 -1
- package/dist/billing-foundation/consumer-protection/termination-open-cancels.d.ts +4 -0
- package/dist/billing-foundation/consumer-protection/termination-open-cancels.js +14 -0
- package/dist/billing-foundation/consumer-protection/termination-provider-lock.d.ts +2 -0
- package/dist/billing-foundation/consumer-protection/termination-provider-lock.js +3 -0
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +4 -4
- package/dist/billing-foundation/consumer-protection/termination-record.js +11 -48
- package/dist/billing-foundation/consumer-protection/termination-request.js +10 -8
- package/dist/billing-foundation/consumer-protection/termination-revoke.js +10 -12
- package/dist/billing-foundation/events.js +1 -1
- package/dist/billing-foundation/feature.js +3 -3
- package/dist/billing-foundation/get-subscription-for-tenant.d.ts +1 -1
- package/dist/billing-foundation/handlers/process-event.write.js +5 -5
- package/dist/billing-foundation/handlers/process-payment-event.write.js +3 -3
- package/dist/billing-foundation/plan-catalog.js +2 -2
- package/dist/billing-foundation/projection.js +1 -1
- package/dist/billing-foundation/types.js +1 -1
- package/dist/cap-counter/aggregate-id.d.ts +1 -1
- package/dist/cap-counter/aggregate-id.js +1 -1
- package/dist/cap-counter/book-cap-usage.d.ts +2 -2
- package/dist/cap-counter/book-cap-usage.js +3 -3
- package/dist/cap-counter/cap-reservation.js +16 -7
- package/dist/cap-counter/constants.js +1 -1
- package/dist/cap-counter/feature.js +3 -2
- package/dist/cap-counter/handlers/increment-rolling.write.js +1 -1
- package/dist/cap-counter/reservation-chain.js +1 -1
- package/dist/cap-counter/with-cap-enforcement.d.ts +3 -3
- package/dist/cap-counter/with-cap-enforcement.js +1 -1
- package/dist/cap-overview/feature.js +1 -0
- package/dist/cap-overview/handlers/caps-usage.query.js +1 -1
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +1 -1
- package/dist/compliance-profiles/seeding.d.ts +1 -1
- package/dist/compliance-profiles/seeding.js +9 -3
- package/dist/config/feature.js +6 -6
- package/dist/config/handlers/cascade.query.js +1 -1
- package/dist/config/handlers/readiness.query.js +1 -1
- package/dist/config/handlers/reencrypt.job.js +23 -13
- package/dist/config/handlers/reset.write.js +3 -3
- package/dist/config/handlers/selected-extensions.query.js +1 -1
- package/dist/config/handlers/set.write.js +15 -5
- package/dist/config/handlers/values.query.js +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.js +9 -9
- package/dist/crypto-shredding/resolve-file-bytes-deleter.js +3 -3
- package/dist/custom-fields/handlers/clear-custom-field.write.js +1 -1
- package/dist/custom-fields/handlers/delete-system-field.write.js +1 -1
- package/dist/custom-fields/handlers/delete-tenant-field.write.js +1 -1
- package/dist/custom-fields/handlers/set-custom-field.write.js +1 -1
- package/dist/custom-fields/index.d.ts +1 -0
- package/dist/custom-fields/index.js +1 -0
- package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
- package/dist/data-retention/feature.js +1 -1
- package/dist/data-retention/run-retention-cleanup.js +12 -2
- package/dist/delivery/attempt-log.js +1 -1
- package/dist/delivery/delivery-service.js +5 -5
- package/dist/delivery/feature.js +3 -2
- package/dist/delivery/index.d.ts +1 -0
- package/dist/delivery/index.js +1 -0
- package/dist/delivery/jobs.js +11 -11
- package/dist/delivery/system-channel-context.d.ts +15 -0
- package/dist/delivery/system-channel-context.js +5 -0
- package/dist/document-ingest-foundation/append-missing-extract-event.d.ts +21 -0
- package/dist/document-ingest-foundation/append-missing-extract-event.js +42 -0
- package/dist/document-ingest-foundation/events.d.ts +11 -0
- package/dist/document-ingest-foundation/events.js +12 -1
- package/dist/document-ingest-foundation/index.d.ts +2 -1
- package/dist/document-ingest-foundation/index.js +2 -1
- package/dist/feature-toggles/feature.js +1 -0
- package/dist/feature-toggles/handlers/registered.query.js +1 -1
- package/dist/feature-toggles/handlers/set.write.js +1 -1
- package/dist/feature-toggles/toggle-sync-signal.js +1 -1
- package/dist/file-derivatives/feature.d.ts +1 -1
- package/dist/file-derivatives/feature.js +1 -1
- package/dist/file-derivatives/handlers/public-variant-by-file-ref.query.js +1 -1
- package/dist/file-derivatives/handlers/public-variant.query.js +5 -5
- package/dist/file-foundation/feature.js +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +4 -2
- package/dist/file-foundation/read-file-ref-content.js +3 -3
- package/dist/file-provider-s3/feature.js +1 -1
- package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +8 -8
- package/dist/form-draft/feature.js +1 -1
- package/dist/form-draft/handlers/cleanup.job.js +22 -12
- package/dist/form-draft/handlers/discard.write.js +3 -3
- package/dist/form-draft/handlers/get.query.js +1 -1
- package/dist/foundation-shared/config-helpers.d.ts +1 -1
- package/dist/foundation-shared/config-helpers.js +1 -1
- package/dist/host-routing/validate-options.js +3 -0
- package/dist/inbound-mail-foundation/events.js +1 -1
- package/dist/inbound-mail-foundation/feature.js +2 -1
- package/dist/inbound-mail-foundation/handlers/account-state.d.ts +3 -1
- package/dist/inbound-mail-foundation/handlers/account-state.js +1 -1
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +10 -10
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +1 -1
- package/dist/inbound-mail-foundation/projection.js +1 -1
- package/dist/inbound-mail-foundation/watch-supervisor.js +1 -1
- package/dist/inbound-provider-imap/feature.js +1 -1
- package/dist/jobs/events.js +1 -1
- package/dist/jobs/feature.js +1 -0
- package/dist/jobs/handlers/consumer.write.js +1 -1
- package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
- package/dist/jobs/handlers/reindex-entity.job.js +5 -5
- package/dist/jobs/handlers/retention-cleanup.job.js +1 -1
- package/dist/jobs/handlers/retry.write.js +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.js +1 -1
- package/dist/jobs/handlers/trigger.write.js +2 -2
- package/dist/jobs/job-run-logger.d.ts +1 -0
- package/dist/jobs/job-run-logger.js +38 -18
- package/dist/ledger/web/index.js +1 -1
- package/dist/legal-pages/lib/boot-check.d.ts +10 -4
- package/dist/legal-pages/lib/boot-check.js +2 -2
- package/dist/legal-pages/seed.d.ts +3 -1
- package/dist/legal-pages/seed.js +2 -2
- package/dist/mail-foundation/feature.d.ts +2 -2
- package/dist/mail-transport-smtp/config-keys.js +1 -1
- package/dist/mail-transport-smtp/feature.js +7 -6
- package/dist/managed-pages/constants.d.ts +1 -0
- package/dist/managed-pages/constants.js +1 -0
- package/dist/managed-pages/feature.js +3 -1
- package/dist/managed-pages/index.d.ts +1 -0
- package/dist/managed-pages/index.js +1 -0
- package/dist/managed-pages/seeding.d.ts +1 -1
- package/dist/managed-pages/seeding.js +10 -4
- package/dist/notes-history/handlers/add-note.write.js +2 -2
- package/dist/personal-access-tokens/feature.js +2 -1
- package/dist/personal-access-tokens/handlers/create.write.js +2 -2
- package/dist/personal-access-tokens/handlers/revoke.write.js +1 -1
- package/dist/rate-limiting/handlers/status.query.js +2 -2
- package/dist/readiness/handlers/status.query.js +1 -1
- package/dist/renderer-foundation/api.d.ts +4 -2
- package/dist/renderer-foundation/api.js +2 -2
- package/dist/secrets/announce-secret-change.d.ts +6 -2
- package/dist/secrets/announce-secret-change.js +3 -3
- package/dist/secrets/feature.d.ts +1 -1
- package/dist/secrets/feature.js +8 -8
- package/dist/secrets/handlers/delete.write.js +1 -1
- package/dist/secrets/handlers/rotate.job.js +13 -8
- package/dist/secrets/handlers/set.write.js +1 -1
- package/dist/secrets/secrets-context.js +16 -6
- package/dist/secrets/write-gate.js +8 -2
- package/dist/seo/feature.js +1 -1
- package/dist/sessions/feature.js +2 -1
- package/dist/sessions/handlers/cleanup.job.js +2 -2
- package/dist/sessions/handlers/detail.query.d.ts +1 -0
- package/dist/sessions/handlers/detail.query.js +5 -2
- package/dist/sessions/handlers/list.query.d.ts +1 -0
- package/dist/sessions/handlers/list.query.js +5 -2
- package/dist/sessions/handlers/mine.query.d.ts +1 -0
- package/dist/sessions/handlers/mine.query.js +5 -2
- package/dist/sessions/handlers/revoke-all-for-user.write.js +3 -3
- package/dist/sessions/handlers/revoke-all-others.write.js +1 -1
- package/dist/sessions/handlers/revoke.write.js +1 -1
- package/dist/sessions/index.d.ts +1 -1
- package/dist/sessions/index.js +1 -1
- package/dist/sessions/screens.js +8 -4
- package/dist/sessions/session-callbacks.d.ts +0 -2
- package/dist/sessions/session-callbacks.js +1 -3
- package/dist/sessions/session-revoked-event.js +1 -1
- package/dist/sessions/web/session-device-cell.js +10 -12
- package/dist/sessions/web/user-agent.d.ts +1 -0
- package/dist/sessions/web/user-agent.js +11 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/purge-files-reaching-subject.d.ts +1 -0
- package/dist/shared/purge-files-reaching-subject.js +31 -4
- package/dist/shared/require-for-tenant.d.ts +1 -1
- package/dist/shared/require-for-tenant.js +6 -4
- package/dist/shared/run-in-sub-transaction.js +5 -5
- package/dist/shared/stream-tenant-backfill.d.ts +28 -0
- package/dist/shared/stream-tenant-backfill.js +119 -0
- package/dist/shared/stream-tenant-derived-state.d.ts +25 -0
- package/dist/shared/stream-tenant-derived-state.js +68 -0
- package/dist/step-dispatcher/deliver-dispatch-request.js +16 -3
- package/dist/step-dispatcher/feature.js +1 -1
- package/dist/step-dispatcher/mail-runner.d.ts +1 -0
- package/dist/subscription-dodo/plugin-methods.js +1 -1
- package/dist/subscription-dodo/runtime.js +4 -4
- package/dist/subscription-stripe/plugin-methods.js +3 -3
- package/dist/subscription-stripe/runtime.js +4 -4
- package/dist/tags/feature.js +1 -0
- package/dist/template-resolver/api.d.ts +4 -2
- package/dist/template-resolver/api.js +2 -2
- package/dist/template-resolver/document-ops.d.ts +5 -0
- package/dist/template-resolver/document-ops.js +18 -2
- package/dist/template-resolver/feature.js +2 -2
- package/dist/template-resolver/handlers/bundle-import.d.ts +4 -3
- package/dist/template-resolver/handlers/export-bundle.query.js +1 -1
- package/dist/template-resolver/handlers/layout-source.query.d.ts +4 -0
- package/dist/template-resolver/handlers/layout-source.query.js +26 -6
- package/dist/template-resolver/handlers/overview.query.js +17 -9
- package/dist/template-resolver/handlers/patch-draft.write.js +1 -1
- package/dist/template-resolver/handlers/send-test-shared.js +1 -1
- package/dist/template-resolver/handlers/send-test-status.query.d.ts +2 -1
- package/dist/template-resolver/handlers/send-test-status.query.js +18 -15
- package/dist/template-resolver/handlers/send-test.write.js +2 -2
- package/dist/template-resolver/handlers/tenant-override-selection.d.ts +3 -1
- package/dist/template-resolver/handlers/tenant-override-selection.js +1 -1
- package/dist/template-resolver/handlers/tenant-overrides.query.js +1 -1
- package/dist/template-resolver/handlers/usage.query.js +1 -1
- package/dist/template-resolver/seeding.d.ts +1 -0
- package/dist/template-resolver/seeding.js +17 -4
- package/dist/template-resolver/store-template-resource.d.ts +7 -6
- package/dist/template-resolver/store-template-resource.js +48 -8
- package/dist/template-resolver/template-resources.d.ts +2 -2
- package/dist/template-resolver/template-resources.js +2 -2
- package/dist/template-resolver/testing.js +2 -2
- package/dist/template-resolver/web/document-insert-fields.d.ts +4 -0
- package/dist/template-resolver/web/document-insert-fields.js +69 -0
- package/dist/template-resolver/web/document-insert-model.d.ts +43 -0
- package/dist/template-resolver/web/document-insert-model.js +106 -0
- package/dist/template-resolver/web/document-insert-panel.d.ts +13 -0
- package/dist/template-resolver/web/document-insert-panel.js +49 -0
- package/dist/template-resolver/web/document-tiptap-bubble.d.ts +4 -0
- package/dist/template-resolver/web/document-tiptap-bubble.js +85 -0
- package/dist/template-resolver/web/document-tiptap-content.d.ts +3 -0
- package/dist/template-resolver/web/document-tiptap-content.js +12 -0
- package/dist/template-resolver/web/document-tiptap-context.d.ts +41 -0
- package/dist/template-resolver/web/document-tiptap-context.js +43 -0
- package/dist/template-resolver/web/document-tiptap-editor.js +50 -495
- package/dist/template-resolver/web/document-tiptap-helpers.d.ts +44 -0
- package/dist/template-resolver/web/document-tiptap-helpers.js +129 -0
- package/dist/template-resolver/web/document-tiptap-layout.d.ts +4 -0
- package/dist/template-resolver/web/document-tiptap-layout.js +24 -0
- package/dist/template-resolver/web/document-tiptap-toolbar-groups.d.ts +6 -0
- package/dist/template-resolver/web/document-tiptap-toolbar-groups.js +33 -0
- package/dist/template-resolver/web/document-tiptap-toolbar.d.ts +3 -0
- package/dist/template-resolver/web/document-tiptap-toolbar.js +91 -0
- package/dist/template-resolver/web/editor-handle.d.ts +1 -0
- package/dist/template-resolver/web/editor-handle.js +15 -0
- package/dist/template-resolver/web/i18n.js +3 -0
- package/dist/template-resolver/web/placeholder-line-editor.js +16 -5
- package/dist/template-resolver/web/qualified-names.d.ts +1 -0
- package/dist/template-resolver/web/qualified-names.js +1 -0
- package/dist/template-resolver/web/use-document-tiptap-model.d.ts +54 -0
- package/dist/template-resolver/web/use-document-tiptap-model.js +96 -0
- package/dist/tenant/feature.js +1 -0
- package/dist/tenant/handlers/cancel-invitation.write.js +1 -1
- package/dist/tenant/handlers/memberships.query.js +1 -1
- package/dist/tenant/handlers/remove-member.write.js +2 -2
- package/dist/tenant/handlers/team-list.query.js +3 -3
- package/dist/tenant/handlers/update-member-roles.write.js +1 -1
- package/dist/tenant/index.d.ts +1 -0
- package/dist/tenant/index.js +1 -0
- package/dist/tenant/invited-membership.js +6 -1
- package/dist/tenant/schema/tenant.d.ts +2 -2
- package/dist/tenant/schema/tenant.js +4 -1
- package/dist/tenant/seeding.d.ts +2 -2
- package/dist/tenant/seeding.js +52 -52
- package/dist/tenant/stream-tenant-backfill.d.ts +21 -0
- package/dist/tenant/stream-tenant-backfill.js +63 -0
- package/dist/tenant/testing.d.ts +12 -0
- package/dist/tenant/testing.js +33 -0
- package/dist/tenant-handover/events.js +1 -1
- package/dist/tenant-handover/handlers/claim.write.js +1 -1
- package/dist/tenant-lifecycle/feature.js +5 -5
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +14 -5
- package/dist/tenant-lifecycle/handlers/request-destruction.write.js +12 -4
- package/dist/tenant-lifecycle/run-tenant-destroy.js +23 -13
- package/dist/tenant-lifecycle/stages.js +28 -2
- package/dist/tenant-lifecycle/testing.d.ts +1 -1
- package/dist/tenant-lifecycle/testing.js +6 -5
- package/dist/tier-engine/entity.js +1 -1
- package/dist/tier-engine/feature.js +8 -3
- package/dist/tier-engine/handlers/active-tier.query.js +3 -3
- package/dist/tier-engine/handlers/get-tenant-tier.query.js +6 -1
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +4 -4
- package/dist/user/db/queries/stream-tenant-backfill.d.ts +20 -1
- package/dist/user/db/queries/stream-tenant-backfill.js +40 -77
- package/dist/user/feature.js +1 -0
- package/dist/user/handlers/find-for-auth.query.js +2 -2
- package/dist/user/handlers/update-roles.js +2 -2
- package/dist/user/index.d.ts +1 -1
- package/dist/user/index.js +1 -1
- package/dist/user/invited-global-roles.js +6 -1
- package/dist/user/principal-status.js +1 -1
- package/dist/user/seeding.d.ts +1 -1
- package/dist/user/seeding.js +20 -5
- package/dist/user/testing.d.ts +3 -0
- package/dist/user/testing.js +10 -0
- package/dist/user-data-rights/audit-download.js +12 -2
- package/dist/user-data-rights/feature.js +30 -26
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +1 -1
- package/dist/user-data-rights/handlers/deletion-grace-period.js +1 -1
- package/dist/user-data-rights/handlers/download-by-job.query.js +2 -2
- package/dist/user-data-rights/handlers/download-by-token.query.js +2 -2
- package/dist/user-data-rights/handlers/request-deletion.write.js +1 -1
- package/dist/user-data-rights/handlers/restrict-account.write.js +4 -4
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -6
- package/dist/user-data-rights/index.d.ts +1 -1
- package/dist/user-data-rights/index.js +1 -1
- package/dist/user-data-rights/lib/mail-transport-resolver.js +1 -1
- package/dist/user-data-rights/lib/storage-provider-resolver.js +1 -1
- package/dist/user-data-rights/lib/update-user-lifecycle.js +6 -1
- package/dist/user-data-rights/run-export-jobs.js +7 -2
- package/dist/user-data-rights/run-forget-cleanup.d.ts +1 -0
- package/dist/user-data-rights/run-forget-cleanup.js +9 -1
- package/dist/user-data-rights/run-user-export.js +7 -1
- package/dist/user-data-rights/web/constants.d.ts +14 -0
- package/dist/user-data-rights/web/constants.js +14 -0
- package/dist/user-data-rights/web/index.d.ts +1 -0
- package/dist/user-data-rights/web/index.js +1 -0
- package/dist/user-profile/handlers/change-email.write.js +8 -6
- package/dist/waitlist/feature.js +1 -0
- package/dist/waitlist/handlers/export-purge.write.js +1 -1
- package/dist/waitlist/handlers/invite.write.js +2 -2
- package/dist/waitlist/handlers/reject.write.js +1 -1
- package/dist/waitlist/handlers/submit.write.js +20 -11
- package/dist/waitlist/import.d.ts +2 -1
- package/dist/waitlist/import.js +22 -2
- package/dist/waitlist/legacy-migrate.js +8 -3
- package/dist/waitlist/link-on-signup.js +6 -1
- package/dist/waitlist/options.d.ts +2 -1
- package/dist/workflow-runner/db/queries/due-runs.js +1 -1
- package/dist/workflow-runner/event-subscriber.js +4 -3
- package/dist/workflow-runner/event-trigger.js +2 -1
- package/dist/workflow-runner/feature.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +6 -6
- package/dist/workflow-runner/runner.d.ts +3 -1
- package/dist/workflow-runner/runner.js +3 -3
- package/package.json +41 -23
- package/src/agent-tools/changes.json +6 -0
- package/src/auth-email-password/changes.json +18 -0
- package/src/auth-mfa/changes.json +6 -0
- package/src/billing-foundation/changes.json +6 -0
- package/src/cap-counter/changes.json +12 -0
- package/src/config/changes.json +6 -0
- package/src/crypto-shredding/changes.json +12 -0
- package/src/custom-fields/changes.json +6 -0
- package/src/delivery/changes.json +12 -0
- package/src/document-ingest-foundation/changes.json +6 -0
- package/src/host-routing/changes.json +6 -0
- package/src/jobs/changes.json +6 -0
- package/src/mail-transport-smtp/changes.json +12 -0
- package/src/managed-pages/changes.json +6 -0
- package/src/secrets/changes.json +6 -0
- package/src/sessions/changes.json +19 -0
- package/src/step-dispatcher/changes.json +6 -0
- package/src/template-resolver/changes.json +54 -0
- package/src/tenant/changes.json +19 -0
- package/src/tenant-lifecycle/changes.json +6 -0
- package/src/user-data-rights/changes.json +36 -0
- package/src/waitlist/changes.json +18 -0
- package/src/workflow-runner/changes.json +6 -0
|
@@ -11,6 +11,8 @@ import { CONTRACT_TERMINATION_DECLARED_EVENT_QN, CONTRACT_TERMINATION_REVOKED_EV
|
|
|
11
11
|
import { getSubscriptionForTenant } from "../get-subscription-for-tenant.js";
|
|
12
12
|
import { SubscriptionFoundationHandlers } from "../web/constants.js";
|
|
13
13
|
import { notifyOperator } from "./termination-notify.js";
|
|
14
|
+
import { hasOtherOpenProviderCancel } from "./termination-open-cancels.js";
|
|
15
|
+
import { TERMINATION_PROVIDER_LOCK_KEY, TERMINATION_PROVIDER_LOCK_NAMESPACE, } from "./termination-provider-lock.js";
|
|
14
16
|
import { findRecordedTermination, hasRevocation, resumeAtProvider } from "./termination-record.js";
|
|
15
17
|
export const REVOCATION_PATHS = {
|
|
16
18
|
de: "/legal/kuendigen/zuruecknehmen",
|
|
@@ -66,7 +68,7 @@ export function createRevokeContractTerminationHandler() {
|
|
|
66
68
|
handler: async (event, ctx) => {
|
|
67
69
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
68
70
|
const payload = event.payload;
|
|
69
|
-
const result = await ctx.writeAs(createSystemUser(payload.tenantId), SubscriptionFoundationHandlers.recordContractTerminationRevocation, { requestId: payload.requestId, token: payload.token });
|
|
71
|
+
const result = await ctx.dispatch.writeAs(createSystemUser(payload.tenantId), SubscriptionFoundationHandlers.recordContractTerminationRevocation, { requestId: payload.requestId, token: payload.token });
|
|
70
72
|
return {
|
|
71
73
|
isSuccess: true,
|
|
72
74
|
data: { outcome: result.isSuccess ? parseOutcome(result.data) : "invalid" },
|
|
@@ -84,11 +86,13 @@ export function createRecordContractTerminationRevocationHandler(options, consum
|
|
|
84
86
|
// @cast-boundary engine-payload — dispatcher-zod-validated payload
|
|
85
87
|
const payload = event.payload;
|
|
86
88
|
const tenantId = event.user.tenantId;
|
|
89
|
+
// The provider cancel/resume and the stream append must not interleave across declarations of the same subscription.
|
|
90
|
+
await ctx.db.advisoryXactLock(TERMINATION_PROVIDER_LOCK_NAMESPACE, TERMINATION_PROVIDER_LOCK_KEY);
|
|
87
91
|
const outcome = (value) => ({
|
|
88
92
|
isSuccess: true,
|
|
89
93
|
data: { outcome: value },
|
|
90
94
|
});
|
|
91
|
-
const stream = await ctx.fetchForWriting({
|
|
95
|
+
const stream = await ctx.events.fetchForWriting({
|
|
92
96
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
93
97
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
94
98
|
});
|
|
@@ -111,22 +115,16 @@ export function createRecordContractTerminationRevocationHandler(options, consum
|
|
|
111
115
|
return outcome("too-late");
|
|
112
116
|
}
|
|
113
117
|
const reasons = ["revoked"];
|
|
114
|
-
if (requested?.providerCancel === "period-end"
|
|
118
|
+
if (requested?.providerCancel === "period-end" &&
|
|
119
|
+
!hasOtherOpenProviderCancel(stream.events, payload.requestId)) {
|
|
115
120
|
reasons.push(...(await resumeAtProvider(ctx, subscription)));
|
|
116
121
|
}
|
|
117
122
|
// Re-fetch after the external call, like record-contract-termination.
|
|
118
|
-
const streamAfterProviderCall = await ctx.fetchForWriting({
|
|
123
|
+
const streamAfterProviderCall = await ctx.events.fetchForWriting({
|
|
119
124
|
aggregateId: subscriptionAggregateId(tenantId),
|
|
120
125
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
121
126
|
});
|
|
122
|
-
|
|
123
|
-
const requestedMeanwhile = requested === undefined
|
|
124
|
-
? findRecordedTermination(streamAfterProviderCall.events, payload.requestId)
|
|
125
|
-
: undefined;
|
|
126
|
-
if (requestedMeanwhile?.providerCancel === "period-end") {
|
|
127
|
-
reasons.push(...(await resumeAtProvider(ctx, await getSubscriptionForTenant(ctx, tenantId))));
|
|
128
|
-
}
|
|
129
|
-
const providerCancel = (requested ?? requestedMeanwhile)?.providerCancel;
|
|
127
|
+
const providerCancel = requested?.providerCancel;
|
|
130
128
|
const revoked = {
|
|
131
129
|
requestId: payload.requestId,
|
|
132
130
|
revokedAtIso: now.toString(),
|
|
@@ -16,7 +16,7 @@ import { CONSENT_LOCALES } from "./web/consumer-protection/consent-locale.js";
|
|
|
16
16
|
// one stream; the subscriptionAggregateId helper provides the stream ID.
|
|
17
17
|
export const SUBSCRIPTION_AGGREGATE_TYPE = "subscription";
|
|
18
18
|
// Event-name constants — short form (for r.defineEvent) and qualified
|
|
19
|
-
// FQN (for ctx.
|
|
19
|
+
// FQN (for ctx.events.unsafeAppend and projection-apply keys).
|
|
20
20
|
export const SUBSCRIPTION_CREATED_EVENT_SHORT = "subscription-created";
|
|
21
21
|
export const SUBSCRIPTION_UPDATED_EVENT_SHORT = "subscription-updated";
|
|
22
22
|
export const SUBSCRIPTION_CANCELED_EVENT_SHORT = "subscription-canceled";
|
|
@@ -122,7 +122,7 @@ export function createBillingFoundationFeature(options = {}) {
|
|
|
122
122
|
// providerCustomerId is tenantOwned ciphertext, not plaintext personal data.
|
|
123
123
|
r.defineEvent(PAYMENT_RECEIVED_EVENT_SHORT, paymentEventPayloadSchema, { piiFields: "none" });
|
|
124
124
|
// Inline projection: materialized current state in `read_subscriptions`.
|
|
125
|
-
// Apply runs in the same TX as ctx.
|
|
125
|
+
// Apply runs in the same TX as ctx.events.unsafeAppend — read-your-own-
|
|
126
126
|
// write without a dispatcher tick.
|
|
127
127
|
r.projection({
|
|
128
128
|
name: "subscription",
|
|
@@ -203,11 +203,11 @@ export function createBillingFoundationFeature(options = {}) {
|
|
|
203
203
|
perTenant: true,
|
|
204
204
|
runOnBoot: true,
|
|
205
205
|
handler: async (_payload, ctx) => {
|
|
206
|
-
const result = await ctx.write(SubscriptionFoundationHandlers.syncSubscription, {});
|
|
206
|
+
const result = await ctx.dispatch.write(SubscriptionFoundationHandlers.syncSubscription, {});
|
|
207
207
|
if (!result.isSuccess) {
|
|
208
208
|
throw new Error(`billing-foundation:sync-subscriptions: sync-subscription write failed: ${JSON.stringify(result.error)}`);
|
|
209
209
|
}
|
|
210
|
-
ctx.log.info(`[billing-foundation:sync-subscriptions] ${JSON.stringify(result.data)}`);
|
|
210
|
+
ctx.obs.log.info(`[billing-foundation:sync-subscriptions] ${JSON.stringify(result.data)}`);
|
|
211
211
|
},
|
|
212
212
|
});
|
|
213
213
|
r.useExtension(EXT_TENANT_DATA, "subscription", {
|
|
@@ -18,4 +18,4 @@ export type SubscriptionView = {
|
|
|
18
18
|
/** Returns the tenant's only subscription row (deterministic
|
|
19
19
|
* aggregate ID), or null if the tenant has never subscribed. The status
|
|
20
20
|
* may be active/canceled/past_due/etc.; the caller decides what to do. */
|
|
21
|
-
export declare function getSubscriptionForTenant(ctx: HandlerContext, tenantId: string): Promise<SubscriptionView | null>;
|
|
21
|
+
export declare function getSubscriptionForTenant(ctx: Pick<HandlerContext, "db">, tenantId: string): Promise<SubscriptionView | null>;
|
|
@@ -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
|
import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
15
15
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
@@ -126,7 +126,7 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
|
|
|
126
126
|
// streams, optimize via snapshot or a per-tenant dedup table as the
|
|
127
127
|
// idempotency anchor (like cap-counter).
|
|
128
128
|
// ---------------------------------------------------------------
|
|
129
|
-
const existingEvents = await ctx.
|
|
129
|
+
const existingEvents = await ctx.events.load(aggId);
|
|
130
130
|
const alreadySeen = existingEvents.some((e) => {
|
|
131
131
|
const headers = e.metadata.headers ?? {};
|
|
132
132
|
return (headers["providerEventId"] === payload.providerEventId &&
|
|
@@ -139,7 +139,7 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
|
|
|
139
139
|
// late delivery: appending it would roll the row back (a canceled subscription
|
|
140
140
|
// would come back active). Invoice events carry the live state, not a snapshot.
|
|
141
141
|
if (isLateStateEvent(existingEvents, payload)) {
|
|
142
|
-
ctx.log?.warn(`[billing-foundation:process-event] dropped late ${payload.type} ${payload.providerEventId}: a newer provider event is already stored`);
|
|
142
|
+
ctx.obs.log?.warn(`[billing-foundation:process-event] dropped late ${payload.type} ${payload.providerEventId}: a newer provider event is already stored`);
|
|
143
143
|
return { duplicate: false, stale: true, subscriptionAggregateId: aggId };
|
|
144
144
|
}
|
|
145
145
|
// ---------------------------------------------------------------
|
|
@@ -196,7 +196,7 @@ export async function appendSubscriptionEvent(ctx, tenantId, rawPayload, now = T
|
|
|
196
196
|
providerEventId: payload.providerEventId,
|
|
197
197
|
providerName: payload.providerName,
|
|
198
198
|
};
|
|
199
|
-
await ctx.
|
|
199
|
+
await ctx.events.unsafeAppend({
|
|
200
200
|
aggregateId: aggId,
|
|
201
201
|
aggregateType: SUBSCRIPTION_AGGREGATE_TYPE,
|
|
202
202
|
type: esEventType,
|
|
@@ -240,7 +240,7 @@ export function createProcessEventHandler(now) {
|
|
|
240
240
|
const payload = event.payload;
|
|
241
241
|
const current = await getSubscriptionForTenant(ctx, event.user.tenantId);
|
|
242
242
|
if (isStaleSubscriptionEvent(current, payload, now())) {
|
|
243
|
-
ctx.log?.warn(`[billing-foundation:process-event] dropped stale ${payload.type} ${payload.providerEventId}: tenant already tracks another subscription`);
|
|
243
|
+
ctx.obs.log?.warn(`[billing-foundation:process-event] dropped stale ${payload.type} ${payload.providerEventId}: tenant already tracks another subscription`);
|
|
244
244
|
return {
|
|
245
245
|
isSuccess: true,
|
|
246
246
|
data: {
|
|
@@ -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
|
import { configuredPiiSubjectKms, encryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
@@ -41,7 +41,7 @@ export const processPaymentEventHandler = {
|
|
|
41
41
|
// (a tenant with hundreds of purchases scans hundreds of events;
|
|
42
42
|
// optimize via a per-tenant providerEventId-index if that bites).
|
|
43
43
|
// ---------------------------------------------------------------
|
|
44
|
-
const existingEvents = await ctx.
|
|
44
|
+
const existingEvents = await ctx.events.load(aggId);
|
|
45
45
|
const alreadySeen = existingEvents.some((e) => {
|
|
46
46
|
const headers = e.metadata.headers ?? {};
|
|
47
47
|
return (headers["providerEventId"] === payload.providerEventId &&
|
|
@@ -81,7 +81,7 @@ export const processPaymentEventHandler = {
|
|
|
81
81
|
providerCheckoutId: payload.providerCheckoutId,
|
|
82
82
|
}),
|
|
83
83
|
};
|
|
84
|
-
await ctx.
|
|
84
|
+
await ctx.events.unsafeAppend({
|
|
85
85
|
aggregateId: aggId,
|
|
86
86
|
aggregateType: PAYMENT_AGGREGATE_TYPE,
|
|
87
87
|
type: PAYMENT_RECEIVED_EVENT_QN,
|
|
@@ -50,7 +50,7 @@ export async function resolvePlanPrices(ctx, plugin, catalog) {
|
|
|
50
50
|
}
|
|
51
51
|
catch (error) {
|
|
52
52
|
const message = error instanceof Error ? error.message : String(error);
|
|
53
|
-
ctx.log?.warn("billing-foundation: retrievePrices threw, all plan prices unavailable", {
|
|
53
|
+
ctx.obs.log?.warn("billing-foundation: retrievePrices threw, all plan prices unavailable", {
|
|
54
54
|
message,
|
|
55
55
|
});
|
|
56
56
|
return result;
|
|
@@ -67,7 +67,7 @@ export async function resolvePlanPrices(ctx, plugin, catalog) {
|
|
|
67
67
|
result.set(tier, { priceId: chosen.priceId, price: chosen });
|
|
68
68
|
}
|
|
69
69
|
else if (active.length > 1) {
|
|
70
|
-
ctx.log?.warn(`billing-foundation: tier "${tier}" has ${active.length} active flat-amount prices — expected exactly one, price stays unavailable`);
|
|
70
|
+
ctx.obs.log?.warn(`billing-foundation: tier "${tier}" has ${active.length} active flat-amount prices — expected exactly one, price stays unavailable`);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
return result;
|
|
@@ -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;
|
|
@@ -16,7 +16,7 @@ export declare function capCounterAggregateId(tenantId: string, capName: string,
|
|
|
16
16
|
* different uuidv5 namespace, different output UUID. This also prevents an
|
|
17
17
|
* accidental calendar increment from landing on the rolling stream.
|
|
18
18
|
*
|
|
19
|
-
* **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.write`) calls
|
|
19
|
+
* **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.dispatch.write`) calls
|
|
20
20
|
* this with tenantId + capName and appends increment events to the stream.
|
|
21
21
|
* Race-free because the event store appends with an auto-incrementing version.
|
|
22
22
|
*/
|
|
@@ -30,7 +30,7 @@ export function capCounterAggregateId(tenantId, capName, periodStartIso) {
|
|
|
30
30
|
* different uuidv5 namespace, different output UUID. This also prevents an
|
|
31
31
|
* accidental calendar increment from landing on the rolling stream.
|
|
32
32
|
*
|
|
33
|
-
* **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.write`) calls
|
|
33
|
+
* **Caller:** `CapCounterHandlers.incrementRolling` (via `ctx.dispatch.write`) calls
|
|
34
34
|
* this with tenantId + capName and appends increment events to the stream.
|
|
35
35
|
* Race-free because the event store appends with an auto-incrementing version.
|
|
36
36
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
-
import { type HandlerContext, type JobContext, type SessionUser, type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
|
+
import { type HandlerContext, type JobContext, type ReservationContext, type SessionUser, type TenantId, type WriteResult } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
export declare function retryCounterWriteOnVersionConflict(writeAttempt: () => Promise<WriteResult>): Promise<WriteResult>;
|
|
4
4
|
export declare function retryOnStreamVersionConflict<T>(attempt: () => Promise<T>): Promise<T>;
|
|
5
5
|
export type BookCapUsageOptions = {
|
|
@@ -9,7 +9,7 @@ export type BookCapUsageOptions = {
|
|
|
9
9
|
readonly outsideTransaction?: boolean;
|
|
10
10
|
readonly guardCurrentValue?: (currentValue: number) => void;
|
|
11
11
|
};
|
|
12
|
-
export type CapCounterContext = HandlerContext | JobContext;
|
|
12
|
+
export type CapCounterContext = HandlerContext | JobContext | ReservationContext;
|
|
13
13
|
export type CapCounterScope = {
|
|
14
14
|
readonly user: SessionUser;
|
|
15
15
|
readonly readDb: TenantDb;
|
|
@@ -60,7 +60,7 @@ function assertDbServesUser(db, user) {
|
|
|
60
60
|
return db;
|
|
61
61
|
}
|
|
62
62
|
// Jobs write in place (their db is not transaction-bound); systemScope handlers reach both dbs via
|
|
63
|
-
// systemDb, because their ctx.db / ctx.
|
|
63
|
+
// systemDb, because their ctx.db / ctx.db.outsideTransaction are fail-closed proxies.
|
|
64
64
|
export function resolveCapCounterScope(ctx, outsideTransaction) {
|
|
65
65
|
// A job context spread into a handler-shaped one (user set) is a handler context: its user, not
|
|
66
66
|
// the job's systemUser, names the metered tenant.
|
|
@@ -83,10 +83,10 @@ export function resolveCapCounterScope(ctx, outsideTransaction) {
|
|
|
83
83
|
: readDb;
|
|
84
84
|
return { user, readDb, writeDb };
|
|
85
85
|
}
|
|
86
|
-
const writeDb = outsideTransaction ? ctx.
|
|
86
|
+
const writeDb = outsideTransaction ? ctx.db.outsideTransaction : ctx.db;
|
|
87
87
|
if (!writeDb) {
|
|
88
88
|
throw new Error(outsideTransaction
|
|
89
|
-
? "cap-counter: outsideTransaction requested but ctx.
|
|
89
|
+
? "cap-counter: outsideTransaction requested but ctx.db.outsideTransaction is undefined"
|
|
90
90
|
: "cap-counter: ctx.db missing — run inside a handler context");
|
|
91
91
|
}
|
|
92
92
|
return {
|
|
@@ -186,6 +186,16 @@ function bookCalendarDelta(txDb, user, period, delta) {
|
|
|
186
186
|
return Promise.resolve({ isSuccess: true, data: {} });
|
|
187
187
|
return applyCapDeltaOn(txDb, user, { ...period, amount: Math.abs(delta) }, delta > 0 ? "add" : "subtract");
|
|
188
188
|
}
|
|
189
|
+
// Asked after a missed or lost claim. A released tombstone committed since the first read does not
|
|
190
|
+
// count: the caller's own tombstone insert then conflicts with it, and the retry claims that row.
|
|
191
|
+
async function hasSettledTombstone(txDb, user, id) {
|
|
192
|
+
const settled = await txDb.fetchOne(capReservationsTable, {
|
|
193
|
+
id,
|
|
194
|
+
tenantId: user.tenantId,
|
|
195
|
+
settledAt: { ne: null },
|
|
196
|
+
});
|
|
197
|
+
return settled !== undefined;
|
|
198
|
+
}
|
|
189
199
|
async function settleInTransaction(txDb, user, id, options) {
|
|
190
200
|
const ownRow = { id, tenantId: user.tenantId };
|
|
191
201
|
const open = await txDb.fetchOne(capReservationsTable, {
|
|
@@ -205,12 +215,11 @@ async function settleInTransaction(txDb, user, id, options) {
|
|
|
205
215
|
await txDb.updateMany(capReservationsTable, { settledAt: now, expiresAt: now.add({ minutes: CAP_SETTLED_TOMBSTONE_TTL_MINUTES }) }, ownRow);
|
|
206
216
|
return booked;
|
|
207
217
|
}
|
|
208
|
-
|
|
209
|
-
if (await txDb.fetchOne(capReservationsTable, ownRow)) {
|
|
218
|
+
if (await hasSettledTombstone(txDb, user, id))
|
|
210
219
|
return NOTHING_TO_RELEASE;
|
|
211
|
-
}
|
|
212
220
|
// Row swept or released: its amount was given back, so the full actual amount is booked and a
|
|
213
|
-
// tombstone makes a repeated settle a no-op. A parallel settle inserting first fails
|
|
221
|
+
// tombstone makes a repeated settle a no-op. A parallel settle or consume inserting first fails
|
|
222
|
+
// this insert.
|
|
214
223
|
// Booking first: a lost counter race returns a result instead of throwing, so the transaction
|
|
215
224
|
// commits and a tombstone written before it would turn the retry into a no-op.
|
|
216
225
|
const booked = await bookCalendarDelta(txDb, user, options, options.actualAmount);
|
|
@@ -286,7 +295,7 @@ async function consumeClaimedRow(txDb, user, claimed, options) {
|
|
|
286
295
|
return NOTHING_TO_RELEASE;
|
|
287
296
|
}
|
|
288
297
|
// Row already expired away: the units are booked directly and a released tombstone remembers them
|
|
289
|
-
// so a later settle subtracts them. A parallel consume inserting first fails this insert.
|
|
298
|
+
// so a later settle subtracts them. A parallel consume or settle inserting first fails this insert.
|
|
290
299
|
async function consumeWithoutRow(txDb, user, id, options) {
|
|
291
300
|
// Booking first, for the same reason as settle's missing-row path.
|
|
292
301
|
const booked = await bookCalendarDelta(txDb, user, options, options.units);
|
|
@@ -320,7 +329,7 @@ async function consumeInTransaction(txDb, user, id, options) {
|
|
|
320
329
|
const claimed = open && (await claimReservationRow(txDb, user, open));
|
|
321
330
|
if (claimed)
|
|
322
331
|
return consumeClaimedRow(txDb, user, claimed, options);
|
|
323
|
-
if (await txDb
|
|
332
|
+
if (await hasSettledTombstone(txDb, user, id)) {
|
|
324
333
|
throw new CapReservationSettledError(options.capName);
|
|
325
334
|
}
|
|
326
335
|
return consumeWithoutRow(txDb, user, id, options);
|
|
@@ -364,7 +373,7 @@ export async function releaseExpiredCapReservations(ctx, capName, outsideTransac
|
|
|
364
373
|
await releaseCapReservation(ctx, id, outsideTransaction);
|
|
365
374
|
}
|
|
366
375
|
catch (error) {
|
|
367
|
-
ctx.log?.error("releasing an expired cap reservation failed", { id, capName, error });
|
|
376
|
+
ctx.obs.log?.error("releasing an expired cap reservation failed", { id, capName, error });
|
|
368
377
|
}
|
|
369
378
|
}
|
|
370
379
|
}
|
|
@@ -10,7 +10,7 @@ export const CAP_COUNTER_ROLLING_AGGREGATE_TYPE = "cap-counter-rolling";
|
|
|
10
10
|
// Custom event types for rolling-window counters. The pair is symmetric:
|
|
11
11
|
// _SHORT — matches `r.defineEvent(short, schema)` in the registrar
|
|
12
12
|
// (the framework automatically prefixes it to the QN)
|
|
13
|
-
// _QN — qualified form for `ctx.
|
|
13
|
+
// _QN — qualified form for `ctx.events.unsafeAppend({type})`, the
|
|
14
14
|
// `events.type` column, and `registry.getEvent(qn)` lookups
|
|
15
15
|
// Both MUST stay consistent; the feature test pins against drift.
|
|
16
16
|
export const ROLLING_INCREMENTED_EVENT_SHORT = "rolling-incremented";
|
|
@@ -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.
|
|
@@ -54,6 +54,7 @@ import { CAP_COUNTER_I18N } from "./i18n.js";
|
|
|
54
54
|
import { capReservationsTableMeta } from "./tables.js";
|
|
55
55
|
export function createCapCounterFeature() {
|
|
56
56
|
return defineFeature(CAP_COUNTER_FEATURE, (r) => {
|
|
57
|
+
r.adoptsBundledLists();
|
|
57
58
|
r.describe("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.");
|
|
58
59
|
r.uiHints({
|
|
59
60
|
displayLabel: "Cap Counter · Usage Limits",
|
|
@@ -65,7 +66,7 @@ export function createCapCounterFeature() {
|
|
|
65
66
|
reason: "coordination.cap_reservation_ledger",
|
|
66
67
|
});
|
|
67
68
|
// Custom domain event for the rolling counter. r.defineEvent registers the
|
|
68
|
-
// schema with the registry; ctx.
|
|
69
|
+
// schema with the registry; ctx.events.unsafeAppend in the handler reuses the
|
|
69
70
|
// same schema for append-time validation. QN after prefixing:
|
|
70
71
|
// "cap-counter:event:rolling-incremented" (see
|
|
71
72
|
// ROLLING_INCREMENTED_EVENT_QN).
|
|
@@ -63,7 +63,7 @@ export const incrementRollingCapHandler = {
|
|
|
63
63
|
// unsafeAppendEvent: the strict appendEvent is bound to an app's own event map, which
|
|
64
64
|
// bundled-features cannot see. Schema validation still runs because r.defineEvent
|
|
65
65
|
// registered the schema.
|
|
66
|
-
await ctx.
|
|
66
|
+
await ctx.events.unsafeAppend({
|
|
67
67
|
aggregateId,
|
|
68
68
|
aggregateType: CAP_COUNTER_ROLLING_AGGREGATE_TYPE,
|
|
69
69
|
type: ROLLING_INCREMENTED_EVENT_QN,
|
|
@@ -22,7 +22,7 @@ export function chainReservations(inner, own) {
|
|
|
22
22
|
await innerHandle.release();
|
|
23
23
|
}
|
|
24
24
|
catch (releaseError) {
|
|
25
|
-
ctx.log?.error("releasing the inner cap reservation failed", { error: releaseError });
|
|
25
|
+
ctx.obs.log?.error("releasing the inner cap reservation failed", { error: releaseError });
|
|
26
26
|
}
|
|
27
27
|
throw error;
|
|
28
28
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReservationContext, WriteEvent, WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { type CapToleranceProfileName, type SoftHitNotifier } from "./enforce-cap.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Dynamic cap definition resolved for each handler invocation. A typical caller reads the tenant
|
|
@@ -20,7 +20,7 @@ export type CalendarCapDef = {
|
|
|
20
20
|
* to compute the cap-spec for THIS request (e.g. limit derived from
|
|
21
21
|
* tenant-tier). Sync OR async — async lets the caller fetch the
|
|
22
22
|
* tier from DB. */
|
|
23
|
-
export type CalendarCapResolver = (event: WriteEvent, ctx:
|
|
23
|
+
export type CalendarCapResolver = (event: WriteEvent, ctx: ReservationContext) => Promise<CalendarCapDef> | CalendarCapDef;
|
|
24
24
|
/**
|
|
25
25
|
* Wrap a write-handler with calendar-period cap-enforcement.
|
|
26
26
|
*
|
|
@@ -40,7 +40,7 @@ export type RollingCapDef = {
|
|
|
40
40
|
readonly amount?: number;
|
|
41
41
|
readonly notify: SoftHitNotifier;
|
|
42
42
|
};
|
|
43
|
-
export type RollingCapResolver = (event: WriteEvent, ctx:
|
|
43
|
+
export type RollingCapResolver = (event: WriteEvent, ctx: ReservationContext) => Promise<RollingCapDef> | RollingCapDef;
|
|
44
44
|
/**
|
|
45
45
|
* Wrap a write-handler with rolling-window cap-enforcement.
|
|
46
46
|
*
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// after a rollback, failure result or failed COMMIT only gives back what is still booked, so an
|
|
13
13
|
// unknown COMMIT outcome cannot under-count. No connection is held across the handler. Rows a
|
|
14
14
|
// crashed process left behind expire after CAP_RESERVATION_TTL_MINUTES and are given back before the
|
|
15
|
-
// next reserve of the same cap. Reached through a nested ctx.write the cap is not reserved, so the
|
|
15
|
+
// next reserve of the same cap. Reached through a nested ctx.dispatch.write the cap is not reserved, so the
|
|
16
16
|
// dispatcher rejects that call unless the top-level batch reserved the same handler.
|
|
17
17
|
//
|
|
18
18
|
// The wrapper spreads the wrapped handler and chains its reserveBeforeTransaction: inner reserves
|
|
@@ -36,6 +36,7 @@ export function createCapOverviewFeature(opts) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
return defineFeature(CAP_OVERVIEW_FEATURE, (r) => {
|
|
39
|
+
r.adoptsBundledLists();
|
|
39
40
|
r.describe("Read-only visibility into per-tenant tier assignment and cap usage. SystemAdmin gets a platform-wide tenant list with usage bars; tenant admins (or the roles in `usageVisibleTo`) get their own tenant's usage as dashboard cards. Reads tier-engine, billing-foundation, and tenant data plus app-owned usage tables via caller-supplied CapSpec callbacks — never writes.");
|
|
40
41
|
r.uiHints({
|
|
41
42
|
displayLabel: "Cap Overview · Tier & Usage Visibility",
|
|
@@ -51,7 +51,7 @@ export function createCapsUsageQuery(caps, usageRoles) {
|
|
|
51
51
|
const tier = checkedRows[0]?.tier ?? "";
|
|
52
52
|
// Override path: limits must read the TARGET tenant's config, not the
|
|
53
53
|
// SystemAdmin caller's.
|
|
54
|
-
const config = override === undefined ? ctx.config : ctx.
|
|
54
|
+
const config = override === undefined ? ctx.config : ctx.tenants.config?.(targetTenantId);
|
|
55
55
|
const rows = await Promise.all(caps.map(async (cap) => {
|
|
56
56
|
const used = await cap.usage(db, targetTenantId);
|
|
57
57
|
const limit = await cap.limit(tier, { config });
|
|
@@ -163,7 +163,7 @@ export function createTenantCapsListQuery(caps, listCaps) {
|
|
|
163
163
|
const perTenantEntries = await Promise.all(pageTenantIds.map(async (tenantId) => [tenantId, await cap.usage(db, tenantId)]));
|
|
164
164
|
usageByCap.set(cap.id, new Map(perTenantEntries));
|
|
165
165
|
}));
|
|
166
|
-
const limitByTenantAndCap = await resolveLimitsByTenantAndCap(listedCaps, page, ctx.
|
|
166
|
+
const limitByTenantAndCap = await resolveLimitsByTenantAndCap(listedCaps, page, ctx.tenants.config);
|
|
167
167
|
const rows = page.map((row) => {
|
|
168
168
|
const capFields = {};
|
|
169
169
|
for (const cap of listedCaps) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComplianceProfileKey, ComplianceProfileOverride } from "@cosmicdrift/kumiko-framework/compliance";
|
|
2
2
|
import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import type
|
|
3
|
+
import { type SessionUser, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { type SeedIfExists } from "@cosmicdrift/kumiko-framework/seeding";
|
|
5
5
|
export type SeedComplianceProfileOptions = {
|
|
6
6
|
readonly tenantId: TenantId;
|
|
@@ -10,14 +10,20 @@
|
|
|
10
10
|
//
|
|
11
11
|
// The pattern matches seedTextBlock in template-resolver/seeding.ts.
|
|
12
12
|
import { createEventStoreExecutor, createTenantDb, fetchOne, } from "@cosmicdrift/kumiko-framework/db";
|
|
13
|
-
import {
|
|
13
|
+
import { createSystemUser, } from "@cosmicdrift/kumiko-framework/engine";
|
|
14
|
+
import { runEventStoreSeed } from "@cosmicdrift/kumiko-framework/seeding";
|
|
14
15
|
import { tenantComplianceProfileEntity, tenantComplianceProfileTable, } from "./schema/profile-selection.js";
|
|
15
16
|
const executor = createEventStoreExecutor(tenantComplianceProfileTable, tenantComplianceProfileEntity, { entityName: "tenant-compliance-profile" });
|
|
16
17
|
export async function seedComplianceProfile(db, opts) {
|
|
17
18
|
// user.tenantId must equal opts.tenantId so the Event Store stream
|
|
18
19
|
// and projection remain in the same tenant bucket.
|
|
19
|
-
const by = opts.by ??
|
|
20
|
-
const tdb = createTenantDb({
|
|
20
|
+
const by = opts.by ?? createSystemUser(opts.tenantId);
|
|
21
|
+
const tdb = createTenantDb({
|
|
22
|
+
db,
|
|
23
|
+
tenantId: opts.tenantId,
|
|
24
|
+
mode: "system",
|
|
25
|
+
reason: "seeding writes the compliance profile into an explicitly named tenant",
|
|
26
|
+
});
|
|
21
27
|
const overrideJson = opts.override !== undefined ? JSON.stringify(opts.override) : null;
|
|
22
28
|
const existing = (await fetchOne(db, tenantComplianceProfileTable, {
|
|
23
29
|
tenantId: opts.tenantId,
|
package/dist/config/feature.js
CHANGED
|
@@ -64,14 +64,14 @@ export function createConfigAccessorFactory(registry, resolver) {
|
|
|
64
64
|
// session reads "config feature not wired into AppContext" instead of a
|
|
65
65
|
// generic "configResolver missing".
|
|
66
66
|
export function requireConfigResolver(ctx, handlerName) {
|
|
67
|
-
if (!ctx.configResolver) {
|
|
67
|
+
if (!ctx.services.configResolver) {
|
|
68
68
|
throw new InternalError({
|
|
69
|
-
message: `[${handlerName}] ctx.configResolver missing — pass createConfigAccessorFactory's ` +
|
|
69
|
+
message: `[${handlerName}] ctx.services.configResolver missing — pass createConfigAccessorFactory's ` +
|
|
70
70
|
`output via extraContext.configAccessorFactory and the resolver via ` +
|
|
71
71
|
`extraContext.configResolver at boot.`,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
return ctx.configResolver;
|
|
74
|
+
return ctx.services.configResolver;
|
|
75
75
|
}
|
|
76
76
|
// Config declares r.systemScope() (raw ctx.db is unfiltered across tenants
|
|
77
77
|
// in that mode), so config's own query handlers must go through ctx.systemDb's
|
|
@@ -101,13 +101,13 @@ export function requireSystemDb(ctx, handlerName, tenantId) {
|
|
|
101
101
|
// if a handler tries to write an encrypted key without the provider in
|
|
102
102
|
// place, pointing at the exact wiring gap).
|
|
103
103
|
export function requireConfigEncryption(ctx, handlerName) {
|
|
104
|
-
if (!ctx.configEncryption) {
|
|
104
|
+
if (!ctx.services.configEncryption) {
|
|
105
105
|
throw new InternalError({
|
|
106
|
-
message: `[${handlerName}] ctx.configEncryption missing — at least one config key declares ` +
|
|
106
|
+
message: `[${handlerName}] ctx.services.configEncryption missing — at least one config key declares ` +
|
|
107
107
|
`encrypted: true, so the boot wiring must pass an EnvelopeCipher via ` +
|
|
108
108
|
`extraContext.configEncryption (run{Prod,Dev}App wire it automatically from ` +
|
|
109
109
|
`KUMIKO_SECRETS_MASTER_KEY_V<n>; same instance the resolver was built with).`,
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
|
-
return ctx.configEncryption;
|
|
112
|
+
return ctx.services.configEncryption;
|
|
113
113
|
}
|
|
@@ -38,7 +38,7 @@ export const cascadeQuery = defineQueryHandler({
|
|
|
38
38
|
keyDefs.set(key, keyDefAtScope(keyDef, query.payload.scope, query.user.roles));
|
|
39
39
|
filteredKeys.push(key);
|
|
40
40
|
}
|
|
41
|
-
const cascades = await resolver.getCascadeBatch(filteredKeys, keyDefs, query.user.tenantId, query.user.id, db, ctx.secrets);
|
|
41
|
+
const cascades = await resolver.getCascadeBatch(filteredKeys, keyDefs, query.user.tenantId, query.user.id, db, ctx.services.secrets);
|
|
42
42
|
const result = {};
|
|
43
43
|
for (const [key, rawCascade] of cascades) {
|
|
44
44
|
const keyDef = keyDefs.get(key);
|
|
@@ -58,7 +58,7 @@ export async function collectMissingRequiredConfig(ctx, callerQn, user, db, gate
|
|
|
58
58
|
candidates.set(qualifiedKey, keyDef);
|
|
59
59
|
}
|
|
60
60
|
const missing = [];
|
|
61
|
-
const cascades = await resolver.getCascadeBatch([...candidates.keys()], candidates, user.tenantId, user.id, db, ctx.secrets);
|
|
61
|
+
const cascades = await resolver.getCascadeBatch([...candidates.keys()], candidates, user.tenantId, user.id, db, ctx.services.secrets);
|
|
62
62
|
for (const [qualifiedKey, keyDef] of candidates) {
|
|
63
63
|
// Deliberately the unredacted cascade value: readiness asks "does this
|
|
64
64
|
// tenant functionally have the value?", and an inheritedToTenant:false key
|