@cosmicdrift/kumiko-samples 0.386.0 → 0.388.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/LICENSE +21 -57
- package/README.md +1 -1
- package/package.json +2 -2
- package/packages/bundled-features/package.json +9 -2
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +9 -9
- package/packages/bundled-features/src/audit/changes.json +12 -0
- package/packages/bundled-features/src/audit/handlers/audit-events.ts +1 -1
- package/packages/bundled-features/src/audit/screens.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
- package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.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 +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
- package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
- package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
- package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
- package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
- package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
- package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
- package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
- package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
- package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
- package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
- package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
- package/packages/bundled-features/src/auth-mfa/feature-exports.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/feature.ts +6 -0
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/web/i18n.ts +9 -4
- package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
- package/packages/bundled-features/src/auth-mfa/web/mfa-code-or-recovery-field.tsx +68 -0
- package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +15 -6
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +71 -24
- package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
- package/packages/bundled-features/src/billing-foundation/changes.json +55 -0
- package/packages/bundled-features/src/billing-foundation/checkout-core.ts +32 -42
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +8 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +14 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +3 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +35 -19
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +12 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +14 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +5 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +8 -10
- package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
- package/packages/bundled-features/src/billing-foundation/events.ts +23 -21
- package/packages/bundled-features/src/billing-foundation/feature.ts +45 -44
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
- package/packages/bundled-features/src/billing-foundation/handlers/billing-plans.query.ts +71 -6
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +19 -17
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/handlers/plan-switch-preview.query.ts +65 -0
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +29 -22
- package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +44 -8
- package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +72 -82
- package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +14 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +62 -27
- package/packages/bundled-features/src/billing-foundation/plan-switch.ts +84 -0
- package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
- package/packages/bundled-features/src/billing-foundation/types.ts +144 -66
- package/packages/bundled-features/src/billing-foundation/validate-options.ts +19 -0
- package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +123 -51
- package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +89 -42
- package/packages/bundled-features/src/billing-foundation/web/constants.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-price.ts +55 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +224 -0
- package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +5 -1
- package/packages/bundled-features/src/cap-counter/changes.json +18 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +66 -68
- package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
- package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
- package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
- package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -2
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/channel-email/changes.json +6 -0
- package/packages/bundled-features/src/channel-email/feature.ts +1 -1
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
- package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
- package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
- package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
- package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
- package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
- package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
- package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
- package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
- package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
- package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +12 -0
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +4 -2
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +4 -2
- package/packages/bundled-features/src/config/resolver.ts +4 -4
- package/packages/bundled-features/src/config/table.ts +10 -10
- package/packages/bundled-features/src/config/write-helpers.ts +28 -9
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +23 -21
- package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
- package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
- package/packages/bundled-features/src/custom-fields/events.ts +7 -8
- package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
- package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
- package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
- package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
- package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
- package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
- package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
- package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
- package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
- package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
- package/packages/bundled-features/src/data-retention/feature.ts +11 -18
- package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
- package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
- package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
- package/packages/bundled-features/src/data-retention/presets.ts +18 -23
- package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
- package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
- package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
- package/packages/bundled-features/src/delivery/changes.json +30 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +9 -3
- package/packages/bundled-features/src/delivery/db/queries/preferences.ts +2 -2
- package/packages/bundled-features/src/delivery/delivery-service.ts +35 -5
- package/packages/bundled-features/src/delivery/feature.ts +3 -3
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
- package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +20 -1
- package/packages/bundled-features/src/delivery/handlers/unsubscribe-page-chrome.query.ts +1 -1
- package/packages/bundled-features/src/delivery/tables.ts +9 -8
- package/packages/bundled-features/src/delivery/unsubscribe-link.ts +3 -1
- package/packages/bundled-features/src/delivery/unsubscribe-page-strings.ts +1 -0
- package/packages/bundled-features/src/delivery/unsubscribe-page.ts +6 -0
- package/packages/bundled-features/src/delivery/unsubscribe.ts +40 -19
- package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
- package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
- package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
- package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
- package/packages/bundled-features/src/file-foundation/index.ts +1 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -1
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/env-named-feature.ts +0 -2
- package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
- package/packages/bundled-features/src/files/README.md +1 -1
- package/packages/bundled-features/src/files/feature.ts +6 -6
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/folders/feature.ts +1 -1
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
- package/packages/bundled-features/src/folders/web/tree.ts +5 -5
- package/packages/bundled-features/src/form-draft/changes.json +6 -0
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +3 -3
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
- package/packages/bundled-features/src/form-draft/decrypt-draft.ts +4 -1
- package/packages/bundled-features/src/form-draft/entity.ts +3 -3
- package/packages/bundled-features/src/form-draft/feature.ts +4 -5
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
- package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
- package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
- package/packages/bundled-features/src/host-routing/README.md +1 -1
- package/packages/bundled-features/src/host-routing/changes.json +6 -0
- package/packages/bundled-features/src/host-routing/dispatch.ts +16 -17
- package/packages/bundled-features/src/host-routing/options.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
- package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
- package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +27 -15
- package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +0 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
- package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
- package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +0 -3
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +70 -70
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +101 -84
- package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
- package/packages/bundled-features/src/jobs/changes.json +13 -0
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +20 -11
- package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +45 -40
- package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +63 -59
- package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
- package/packages/bundled-features/src/jobs/job-run-logger.ts +19 -19
- package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
- package/packages/bundled-features/src/jobs/screens.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
- package/packages/bundled-features/src/ledger/entity.ts +5 -5
- package/packages/bundled-features/src/ledger/feature.ts +3 -3
- package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
- package/packages/bundled-features/src/ledger/recurring.ts +12 -12
- package/packages/bundled-features/src/ledger/schemas.ts +1 -1
- package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
- package/packages/bundled-features/src/ledger/web/index.ts +7 -7
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
- package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
- package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
- package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
- package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/mail-foundation/feature.ts +31 -30
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
- package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
- package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
- package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
- package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
- package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
- package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
- package/packages/bundled-features/src/managed-pages/table.ts +16 -15
- package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
- package/packages/bundled-features/src/notes-history/entity.ts +4 -4
- package/packages/bundled-features/src/notes-history/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
- package/packages/bundled-features/src/page-render/layout.ts +9 -9
- package/packages/bundled-features/src/page-render/markdown.ts +11 -11
- package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +9 -6
- package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
- package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
- package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
- package/packages/bundled-features/src/renderer-pdf/changes.json +6 -0
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +27 -10
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
- package/packages/bundled-features/src/seo/feature.ts +3 -3
- package/packages/bundled-features/src/sessions/constants.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +16 -11
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
- package/packages/bundled-features/src/sessions/screens.ts +2 -2
- package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
- package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
- package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +11 -1
- package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
- package/packages/bundled-features/src/shared/index.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +10 -3
- package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
- package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
- package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
- package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
- package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
- package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -0
- package/packages/bundled-features/src/subscription-dodo/changes.json +14 -0
- package/packages/bundled-features/src/subscription-dodo/client.ts +45 -0
- package/packages/bundled-features/src/subscription-dodo/constants.ts +24 -0
- package/packages/bundled-features/src/subscription-dodo/feature.ts +207 -0
- package/packages/bundled-features/src/subscription-dodo/index.ts +16 -0
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +345 -0
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +126 -0
- package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +262 -0
- package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
- package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
- package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
- package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
- package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
- package/packages/bundled-features/src/subscription-stripe/changes.json +12 -0
- package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
- package/packages/bundled-features/src/subscription-stripe/feature.ts +45 -43
- package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +9 -8
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
- package/packages/bundled-features/src/tags/feature.ts +3 -3
- package/packages/bundled-features/src/tags/screens.ts +1 -1
- package/packages/bundled-features/src/template-resolver/api.ts +41 -14
- package/packages/bundled-features/src/template-resolver/changes.json +48 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +1 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +27 -5
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +8 -7
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -20
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +15 -5
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-counts.query.ts +9 -5
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +8 -6
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +44 -5
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +37 -26
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +7 -2
- package/packages/bundled-features/src/template-resolver/seeding.ts +36 -18
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +22 -4
- package/packages/bundled-features/src/template-resolver/table.ts +13 -14
- package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +0 -11
- package/packages/bundled-features/src/template-resolver/template-meta.ts +12 -4
- package/packages/bundled-features/src/template-resolver/template-preview.ts +11 -3
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
- package/packages/bundled-features/src/template-resolver/web/constants.ts +7 -5
- package/packages/bundled-features/src/template-resolver/web/index.ts +7 -0
- package/packages/bundled-features/src/template-resolver/web/mail-layout-constants.ts +8 -0
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/feature.ts +7 -7
- package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +10 -4
- package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +8 -5
- package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
- package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
- package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
- package/packages/bundled-features/src/tenant/screens.ts +1 -1
- package/packages/bundled-features/src/tenant/seeding.ts +19 -18
- package/packages/bundled-features/src/tenant/testing.ts +5 -6
- package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
- package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
- package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
- package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
- package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/constants.ts +5 -0
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +7 -3
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
- package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
- package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
- package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +12 -9
- package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
- package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
- package/packages/bundled-features/src/tier-engine/web/constants.ts +3 -3
- package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
- package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
- package/packages/bundled-features/src/user/schema/user.ts +45 -44
- package/packages/bundled-features/src/user/seeding.ts +40 -40
- package/packages/bundled-features/src/user/testing.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
- package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
- package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +169 -280
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +87 -108
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
- package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
- package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
- package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
- package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
- package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
- package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
- package/packages/bundled-features/src/user-profile/index.ts +3 -3
- package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +2 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +5 -5
- package/packages/bundled-features/src/waitlist/import.ts +5 -0
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +22 -3
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +20 -10
- package/packages/bundled-features/src/waitlist/options.ts +3 -0
- package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
- package/samples/apps/styleguide/src/features/widgets/i18n.ts +11 -0
- package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +24 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +5 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +90 -15
- package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +16 -6
- package/samples/apps/use-all-bundled/feature-manifest.json +120 -3
- package/samples/apps/use-all-bundled/src/run-config.ts +9 -3
- package/samples/recipes/auth-mfa/README.md +5 -0
- package/samples/recipes/auth-mfa/package.json +5 -2
- package/samples/recipes/auth-mfa/src/web/confirmation-code-step.tsx +49 -0
- package/samples/recipes/auth-mfa/tsconfig.json +8 -0
- package/samples/recipes/env-composition/README.md +3 -1
- package/samples/recipes/env-composition/src/env.ts +2 -0
- package/samples/recipes/legal-pages/README.md +1 -1
- package/samples/recipes/rate-limiting/README.md +18 -0
- package/samples/recipes/rate-limiting/src/feature.ts +12 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +0 -12
|
@@ -16,16 +16,14 @@ const { table } = createEntityExecutor("cap-counter", capCounterEntity);
|
|
|
16
16
|
// =============================================================================
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Cap-
|
|
20
|
-
* `project_pricing_byok_caps` §3 + `docs/plans/marketing/produkt/
|
|
21
|
-
* pricing.md` Cap-Verhalten-Block.
|
|
19
|
+
* Cap-tolerance profiles, asymmetric by cap type.
|
|
22
20
|
*
|
|
23
|
-
* **Soft** =
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* **Soft** = notification threshold (multiplier × limit). Once reached,
|
|
22
|
+
* a warning is issued (`lastSoftWarnedAt` sets the flag); caller code emits
|
|
23
|
+
* the actual notification.
|
|
26
24
|
*
|
|
27
|
-
* **Hard** =
|
|
28
|
-
*
|
|
25
|
+
* **Hard** = write-block threshold (multiplier × limit). `enforceCap` throws
|
|
26
|
+
* a `cap_exceeded` error, which the dispatcher maps to 429 with an upgrade hint.
|
|
29
27
|
*/
|
|
30
28
|
export type CapToleranceProfile = {
|
|
31
29
|
readonly soft: number;
|
|
@@ -33,13 +31,13 @@ export type CapToleranceProfile = {
|
|
|
33
31
|
};
|
|
34
32
|
|
|
35
33
|
export const CAP_TOLERANCES = {
|
|
36
|
-
/** Mails /
|
|
34
|
+
/** Mails / tokens — inexpensive, with BYOK available from Pro. Generous burst buffer. */
|
|
37
35
|
burstable: { soft: 1.1, hard: 1.2 },
|
|
38
36
|
/** DB-Storage / File-Storage — teuer + persistent. Strikter Cut. */
|
|
39
37
|
storage: { soft: 1.0, hard: 1.05 },
|
|
40
|
-
/**
|
|
38
|
+
/** App count and platform slots — purchased capacity, with no buffer. */
|
|
41
39
|
hardSlot: { soft: 1.0, hard: 1.0 },
|
|
42
|
-
/** Egress —
|
|
40
|
+
/** Egress — bursty traffic is legitimate; only extreme spikes are blocked. */
|
|
43
41
|
egress: { soft: 1.1, hard: 1.3 },
|
|
44
42
|
} as const satisfies Readonly<Record<string, CapToleranceProfile>>;
|
|
45
43
|
|
|
@@ -73,8 +71,9 @@ export function assertBelowHardCap(
|
|
|
73
71
|
},
|
|
74
72
|
): void {
|
|
75
73
|
const tolerance = CAP_TOLERANCES[cap.profile];
|
|
74
|
+
// Validated on every call so a misconfigured period fails deterministically, not only at the limit.
|
|
75
|
+
const resetAt = resolvePeriodEndIso(cap.periodStartIso, cap.periodEndIso);
|
|
76
76
|
if (valueSeenByLastUnit >= cap.limit * tolerance.hard) {
|
|
77
|
-
const resetAt = resolvePeriodEndIso(cap.periodStartIso, cap.periodEndIso);
|
|
78
77
|
throw new CapExceededError(cap.capName, cap.limit, valueSeenByLastUnit, tolerance, {
|
|
79
78
|
...(resetAt !== undefined && { resetAt }),
|
|
80
79
|
...(cap.i18nKey !== undefined && { i18nKey: cap.i18nKey }),
|
|
@@ -176,28 +175,26 @@ export async function enforceCap(
|
|
|
176
175
|
// =============================================================================
|
|
177
176
|
|
|
178
177
|
/**
|
|
179
|
-
* Synchronous read-and-check of the calling tenant's
|
|
180
|
-
*
|
|
181
|
-
* `windowDays`
|
|
178
|
+
* Synchronous read-and-check of the calling tenant's rolling-window
|
|
179
|
+
* counter for `capName`. Reads increment events from the last
|
|
180
|
+
* `windowDays` in the event store and sums their `amount`. Returns:
|
|
182
181
|
* - "ok" when sum < soft-threshold
|
|
183
182
|
* - "soft-hit" when soft ≤ sum < hard.
|
|
184
183
|
*
|
|
185
184
|
* **Throws** `CapExceededError` when sum ≥ hard-threshold.
|
|
186
185
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* `lastSoftWarnedAt
|
|
190
|
-
*
|
|
191
|
-
* memoization
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* `false` (= "wir tracken's nicht").
|
|
186
|
+
* **The `crossed` flag is intentionally absent:** Unlike the calendar
|
|
187
|
+
* counter, the rolling aggregate has no projection row with a
|
|
188
|
+
* `lastSoftWarnedAt` flag. The caller must deduplicate notification storms
|
|
189
|
+
* using its own mechanism, such as a cache-table key or request-lifetime
|
|
190
|
+
* memoization. The result shape still matches `EnforceCapResult` so the same
|
|
191
|
+
* caller code works with both functions; `crossed` is always `false` here
|
|
192
|
+
* (= "not tracked").
|
|
195
193
|
*
|
|
196
|
-
* **Performance
|
|
197
|
-
* (tenant, capName).
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* vorgezogen.
|
|
194
|
+
* **Performance note:** The read sums ALL events in the window for
|
|
195
|
+
* (tenant, capName). With 10k+ events per tenant in the window, this could
|
|
196
|
+
* become slow; migrate to a multi-stream projection with pre-aggregated
|
|
197
|
+
* daily buckets if needed.
|
|
201
198
|
*/
|
|
202
199
|
export async function enforceRollingCap(
|
|
203
200
|
ctx: CapCounterContext,
|
|
@@ -324,20 +321,20 @@ export function calendarMonthPeriodEndIso(periodStartIso: string): string {
|
|
|
324
321
|
}
|
|
325
322
|
|
|
326
323
|
// =============================================================================
|
|
327
|
-
// Notification-
|
|
328
|
-
// enforceRollingCap
|
|
329
|
-
// soft-hit
|
|
330
|
-
//
|
|
324
|
+
// Notification-wiring helpers — convenience wrappers for `enforceCap` and
|
|
325
|
+
// `enforceRollingCap` that execute a caller-supplied delivery emitter when
|
|
326
|
+
// a soft-hit threshold is crossed. The cap counter does not know about the
|
|
327
|
+
// delivery feature directly; the caller injects the emitter.
|
|
331
328
|
// =============================================================================
|
|
332
329
|
|
|
333
330
|
/**
|
|
334
|
-
* Soft-hit
|
|
335
|
-
* delivery
|
|
336
|
-
*
|
|
337
|
-
* (`crossed === true
|
|
331
|
+
* Soft-hit notifier callback. The caller supplies the function that emits
|
|
332
|
+
* a delivery event (for example, `delivery.send({to, template, payload})`).
|
|
333
|
+
* It is called exactly once per period for the calendar counter
|
|
334
|
+
* (`crossed === true`, deduplicated via `markSoftWarnedHandler`).
|
|
338
335
|
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
336
|
+
* For the rolling counter, `crossed` is always `false`; the caller must
|
|
337
|
+
* deduplicate notifications itself or intentionally send one per request.
|
|
341
338
|
*/
|
|
342
339
|
export type SoftHitNotifier = (info: {
|
|
343
340
|
readonly capName: string;
|
|
@@ -347,14 +344,14 @@ export type SoftHitNotifier = (info: {
|
|
|
347
344
|
}) => Promise<void> | void;
|
|
348
345
|
|
|
349
346
|
/**
|
|
350
|
-
* Calendar-
|
|
351
|
-
*
|
|
352
|
-
* `mark-soft-warned
|
|
353
|
-
*
|
|
347
|
+
* Calendar-period enforcement with automatic soft-hit notification.
|
|
348
|
+
* Calls `enforceCap`; when `crossed: true`, it calls the notifier and the
|
|
349
|
+
* `mark-soft-warned` handler, which flips `lastSoftWarnedAt` so that the
|
|
350
|
+
* same period does not trigger another notification.
|
|
354
351
|
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
352
|
+
* Returns the `EnforceCapResult` unchanged, allowing the caller to branch
|
|
353
|
+
* on the result, such as showing a UI toast in addition to the email
|
|
354
|
+
* notification.
|
|
358
355
|
*/
|
|
359
356
|
export async function enforceCapAndMaybeNotify(
|
|
360
357
|
ctx: CapCounterContext,
|
|
@@ -400,14 +397,14 @@ export async function enforceCapAndMaybeNotify(
|
|
|
400
397
|
}
|
|
401
398
|
|
|
402
399
|
/**
|
|
403
|
-
* Rolling-
|
|
400
|
+
* Rolling-window enforcement that always sends a notification on a soft hit.
|
|
404
401
|
*
|
|
405
|
-
* **
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
402
|
+
* **Storm risk:** The rolling counter does NOT track `lastSoftWarnedAt`
|
|
403
|
+
* (there is no projection row). On every call while the counter is in the
|
|
404
|
+
* soft range, the notifier fires. The caller must deduplicate, for example
|
|
405
|
+
* with a cache entry `lastNotified[capName]` and a TTL, or call
|
|
406
|
+
* `enforceRollingCapAndMaybeNotify` only once per day or hour
|
|
407
|
+
* (an hourly cron instead of per request).
|
|
411
408
|
*/
|
|
412
409
|
export async function enforceRollingCapAndMaybeNotify(
|
|
413
410
|
ctx: CapCounterContext,
|
|
@@ -439,7 +436,7 @@ export async function enforceRollingCapAndMaybeNotify(
|
|
|
439
436
|
}
|
|
440
437
|
|
|
441
438
|
// =============================================================================
|
|
442
|
-
// Stock
|
|
439
|
+
// Stock cap — live-counted cardinality checked against the tier limit
|
|
443
440
|
// =============================================================================
|
|
444
441
|
|
|
445
442
|
export type StockCapResult =
|
|
@@ -447,24 +444,25 @@ export type StockCapResult =
|
|
|
447
444
|
| { readonly state: "exceeded"; readonly current: number; readonly limit: number };
|
|
448
445
|
|
|
449
446
|
/**
|
|
450
|
-
* Stock
|
|
451
|
-
*
|
|
447
|
+
* Stock cap: checks a cardinality counted LIVE by the caller (for example,
|
|
448
|
+
* the number of existing components for a tenant) against a tier limit.
|
|
452
449
|
*
|
|
453
|
-
*
|
|
454
|
-
*
|
|
455
|
-
* (`count(*) WHERE tenant_id = …`)
|
|
456
|
-
* (
|
|
457
|
-
*
|
|
450
|
+
* Unlike {@link enforceCap}/{@link enforceRollingCap}, there is NO stored
|
|
451
|
+
* counter: the caller counts the projection itself
|
|
452
|
+
* (`count(*) WHERE tenant_id = …`) and passes `current`. This avoids drift
|
|
453
|
+
* (a delete immediately frees the slot), requires no counter table or
|
|
454
|
+
* increment/decrement bookkeeping, and measures a stock rather than a flow.
|
|
458
455
|
*
|
|
459
|
-
* **TOCTOU
|
|
460
|
-
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
456
|
+
* **TOCTOU caveat:** Counting and writing are not atomic—two concurrent
|
|
457
|
+
* creates can both see `ok` and exceed the limit by one (the same race as
|
|
458
|
+
* with {@link enforceCap}). Strictly hard slots need additional
|
|
459
|
+
* serialization in the create path, such as a unique index on
|
|
460
|
+
* tenantId+slot.
|
|
463
461
|
*
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
*
|
|
462
|
+
* Pure function—it does NOT throw or map an HTTP status. Reaching a stock
|
|
463
|
+
* limit means "upgrade required", not "retry later" (429); the caller
|
|
464
|
+
* chooses the response, typically an app-specific 422/`upgrade_required`
|
|
465
|
+
* with i18n. With `hardSlot` (soft=hard=1.0), the boundary is exactly `limit`.
|
|
468
466
|
*/
|
|
469
467
|
export function enforceStockCap(options: {
|
|
470
468
|
readonly current: number;
|
|
@@ -5,38 +5,38 @@ import {
|
|
|
5
5
|
createTimestampField,
|
|
6
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
7
7
|
|
|
8
|
-
// cap-counter —
|
|
9
|
-
//
|
|
8
|
+
// cap-counter — one row per (tenantId, capName, periodStart). tenantId
|
|
9
|
+
// is added automatically as a base column (Kumiko multi-tenant default).
|
|
10
10
|
//
|
|
11
|
-
// **Identity
|
|
12
|
-
// - aggregate-id: UUID (Kumiko
|
|
13
|
-
// - public natural
|
|
14
|
-
//
|
|
15
|
-
//
|
|
11
|
+
// **Identity model:**
|
|
12
|
+
// - aggregate-id: UUID (required by Kumiko ES)
|
|
13
|
+
// - public natural key: (tenantId, capName, periodStart) — the counter
|
|
14
|
+
// engine derives a deterministic uuidv5 from it so increments use the
|
|
15
|
+
// same stream instead of creating two rows per tenant and cap
|
|
16
16
|
//
|
|
17
|
-
// **
|
|
18
|
-
// - capName:
|
|
19
|
-
// "db-storage-bytes").
|
|
20
|
-
//
|
|
21
|
-
// - value:
|
|
22
|
-
//
|
|
23
|
-
// - periodStart:
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// now() - 7d`
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
//
|
|
17
|
+
// **Fields:**
|
|
18
|
+
// - capName: the domain concept ("platform-mails", "ai-tokens-7day",
|
|
19
|
+
// "db-storage-bytes"). Free-form string — the app defines its cap
|
|
20
|
+
// names; the engine does not enumerate them.
|
|
21
|
+
// - value: the current counter value. Tokens, MB, count, etc. — the unit
|
|
22
|
+
// belongs to the app; the engine only counts numbers.
|
|
23
|
+
// - periodStart: timestamp when the current counter period began.
|
|
24
|
+
// A calendar-month reset sets a new periodStart (= new aggregate
|
|
25
|
+
// identity). A rolling window (e.g. AI tokens over 7 days) does not
|
|
26
|
+
// reset periodStart and filters on read with `WHERE timestamp >
|
|
27
|
+
// now() - 7d` over the event-store stream — this is the caller's
|
|
28
|
+
// responsibility.
|
|
29
|
+
// - lastSoftWarnedAt: prevents notification storms. Once the soft cap at
|
|
30
|
+
// 110% is reached, subsequent increments must not each send an email to
|
|
31
|
+
// the admin. There is at most one soft warning per period, so this
|
|
32
|
+
// nullable column is reset to null when the period resets.
|
|
32
33
|
//
|
|
33
|
-
// **
|
|
34
|
-
// - softLimit / hardLimit / cap-
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
// - userId / aggregate
|
|
38
|
-
//
|
|
39
|
-
// auslöst).
|
|
34
|
+
// **What is intentionally not in the entity:**
|
|
35
|
+
// - softLimit / hardLimit / cap-tolerance multipliers — these come from
|
|
36
|
+
// the app's tier map when enforceCap is called. The counter knows
|
|
37
|
+
// nothing about the tier, only how much is being counted.
|
|
38
|
+
// - userId / aggregate reference — counters are scoped to the platform
|
|
39
|
+
// tenant, not to a user (even when a user triggers the increment).
|
|
40
40
|
export const capCounterEntity = createEntity({
|
|
41
41
|
table: "read_cap_counters",
|
|
42
42
|
description:
|
|
@@ -1,49 +1,48 @@
|
|
|
1
|
-
// cap-counter — Counter
|
|
1
|
+
// cap-counter — Counter Storage + Increment API + Soft-Warn State.
|
|
2
2
|
//
|
|
3
|
-
// **
|
|
4
|
-
// 1. r.entity({ name: "cap-counter" }) —
|
|
5
|
-
// period)
|
|
6
|
-
// 2. increment
|
|
7
|
-
// aggregate-id (try-create / executor-update)
|
|
8
|
-
// event-store optimistic
|
|
9
|
-
// 3. increment-rolling
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// 4. mark-soft-warned
|
|
13
|
-
//
|
|
14
|
-
// 5. get-counter
|
|
15
|
-
// (
|
|
16
|
-
// 6. enforceCap + enforceRollingCap
|
|
17
|
-
//
|
|
18
|
-
//
|
|
3
|
+
// **What this feature provides:**
|
|
4
|
+
// 1. r.entity({ name: "cap-counter" }) — counter rows per (tenant, capName,
|
|
5
|
+
// period) for calendar-period caps (mail/month, egress/month).
|
|
6
|
+
// 2. increment handler — atomic counter increment via deterministic
|
|
7
|
+
// aggregate-id (try-create / executor-update), made race-free by the
|
|
8
|
+
// event-store optimistic lock.
|
|
9
|
+
// 3. increment-rolling handler — append-only custom-event stream for
|
|
10
|
+
// rolling-window caps (AI-tokens-7d, egress-24h). No projection; the
|
|
11
|
+
// value is read from the event stream.
|
|
12
|
+
// 4. mark-soft-warned handler — flips the anti-notification-storm flag
|
|
13
|
+
// (calendar-period counters only).
|
|
14
|
+
// 5. get-counter query — synchronous read of the current counter value
|
|
15
|
+
// (calendar period).
|
|
16
|
+
// 6. enforceCap + enforceRollingCap helpers (see enforce-cap.ts) —
|
|
17
|
+
// pre-save wrappers with asymmetric soft and hard tolerances per
|
|
18
|
+
// cap profile.
|
|
19
19
|
//
|
|
20
|
-
// **Calendar vs.
|
|
20
|
+
// **Calendar vs. rolling — when to use which:**
|
|
21
21
|
// - **Calendar period** (bookCapUsage + enforceCap): the cap resets at
|
|
22
|
-
// period start (
|
|
23
|
-
// projection
|
|
22
|
+
// period start (the first of the month, etc.). The counter is one row in
|
|
23
|
+
// the projection, enabling fast reads.
|
|
24
24
|
// - **Rolling window** (CapCounterHandlers.incrementRolling + enforceRollingCap):
|
|
25
|
-
// the cap rolls continuously (e.g. "last 7 days"). Values expire
|
|
26
|
-
//
|
|
27
|
-
//
|
|
25
|
+
// the cap rolls continuously (e.g. "last 7 days"). Values expire event
|
|
26
|
+
// by event without a reset. There is no projection; reads sum the events
|
|
27
|
+
// from the last N days.
|
|
28
28
|
//
|
|
29
29
|
// **What this feature does NOT do:**
|
|
30
30
|
// - **No foundation wiring.** mail-foundation / file-foundation /
|
|
31
|
-
// ai-foundation are BYOK by default
|
|
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
34
|
// `ctx.write(CapCounterHandlers.incrementRolling, ...)` from their own handler.
|
|
35
|
-
// - **No reset cron for the calendar period.**
|
|
36
|
-
// periodStartIso part of the aggregate id rolls over to a fresh
|
|
37
|
-
//
|
|
38
|
-
// - **
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
// nicht direkt.
|
|
35
|
+
// - **No reset cron for the calendar period.** It works without one: the
|
|
36
|
+
// periodStartIso part of the aggregate id rolls over to a fresh counter
|
|
37
|
+
// at the period boundary. Old rows remain for auditing.
|
|
38
|
+
// - **No notification path is hard-wired.** cap-counter is decoupled —
|
|
39
|
+
// `enforceCapAndMaybeNotify` (see enforce-cap.ts) is a convenience helper
|
|
40
|
+
// that invokes a caller-supplied emit callback; cap-counter does not know
|
|
41
|
+
// the delivery feature directly.
|
|
43
42
|
//
|
|
44
|
-
// **Boot
|
|
45
|
-
// config,
|
|
46
|
-
//
|
|
43
|
+
// **Boot dependencies:** none. cap-counter is plain vanilla — it needs no
|
|
44
|
+
// config, secrets, or tenant feature. Tenant scoping comes from the framework
|
|
45
|
+
// default (Base-Column tenantId).
|
|
47
46
|
|
|
48
47
|
import {
|
|
49
48
|
access,
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
// increment-rolling — Rolling-
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// increment-rolling — Rolling-window counter increment via a custom
|
|
2
|
+
// event instead of CRUD/projection. Designed for caps whose values
|
|
3
|
+
// continuously renew (AI tokens over 7 days, egress over 24 hours).
|
|
4
4
|
//
|
|
5
|
-
// **
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// `amount`-Feld; CRUD-Events tragen nur den kumulativen value.
|
|
5
|
+
// **Why not r.entity / CRUD like the calendar counter:** The calendar
|
|
6
|
+
// counter stores the cumulative value in its projection row. At the
|
|
7
|
+
// period rollover, the aggregate ID changes to the new period start;
|
|
8
|
+
// that is the "reset". A rolling window has no rollover point—the
|
|
9
|
+
// counter continuously rolls out. A single cumulative value would be
|
|
10
|
+
// incorrect because it would grow monotonically without expiration.
|
|
11
|
+
// The correct read semantics are SUM of all increment amounts from the
|
|
12
|
+
// last N days. Therefore, each increment must be a separate event with
|
|
13
|
+
// its own `amount` field; CRUD events carry only the cumulative value.
|
|
15
14
|
//
|
|
16
|
-
// **Aggregate
|
|
17
|
-
// `rollingCapAggregateId`.
|
|
18
|
-
//
|
|
19
|
-
// N
|
|
15
|
+
// **Aggregate stream:** one stream per (tenant, capName)—see
|
|
16
|
+
// `rollingCapAggregateId`. All increments use the same stream in
|
|
17
|
+
// monotonically increasing version order. enforceRollingCap reads the
|
|
18
|
+
// last N days from this stream.
|
|
20
19
|
|
|
21
20
|
import type { WriteHandlerDef } from "@cosmicdrift/kumiko-framework/engine";
|
|
22
21
|
import * as z from "zod";
|
|
@@ -33,8 +32,8 @@ const incrementRollingSchema = z.object({
|
|
|
33
32
|
});
|
|
34
33
|
type IncrementRollingPayload = z.infer<typeof incrementRollingSchema>;
|
|
35
34
|
|
|
36
|
-
/** Schema
|
|
37
|
-
*
|
|
35
|
+
/** Schema of the emitted custom event. Identical to the input schema:
|
|
36
|
+
* the caller supplies amount, which we append unchanged to the stream. */
|
|
38
37
|
export const rollingIncrementedSchema = incrementRollingSchema;
|
|
39
38
|
|
|
40
39
|
/** Payload of the event that gives a reserved rolling amount back. */
|
|
@@ -44,27 +43,28 @@ export const rollingReleasedSchema = incrementRollingSchema.extend({
|
|
|
44
43
|
incrementedAt: z.string().optional(),
|
|
45
44
|
});
|
|
46
45
|
|
|
47
|
-
// Rolling-
|
|
48
|
-
//
|
|
49
|
-
//
|
|
46
|
+
// Rolling-increment handler — append-only. Race-free: two parallel
|
|
47
|
+
// increments for (tenant, cap) attach to the same aggregate stream at
|
|
48
|
+
// different versions, and the event store orders them.
|
|
50
49
|
//
|
|
51
|
-
// **
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
50
|
+
// **No version_conflict path** as with the calendar counter: nothing
|
|
51
|
+
// reads projection state before writing. The expectedVersion is
|
|
52
|
+
// implicitly "next-after-current", which the event store determines
|
|
53
|
+
// atomically.
|
|
55
54
|
export const incrementRollingCapHandler: WriteHandlerDef = {
|
|
56
55
|
name: "increment-rolling",
|
|
57
56
|
agent: { expose: false },
|
|
58
57
|
schema: incrementRollingSchema,
|
|
59
|
-
// Internal handler —
|
|
60
|
-
//
|
|
61
|
-
//
|
|
58
|
+
// Internal handler — a system caller (platform foundations after a
|
|
59
|
+
// successful side effect) invokes it. Tenant end users must never
|
|
60
|
+
// call it directly. The audit row shows which subsystem call site
|
|
61
|
+
// attached the operation.
|
|
62
62
|
access: { roles: ["SystemAdmin"] },
|
|
63
63
|
handler: async (event, ctx) => {
|
|
64
64
|
// @cast-boundary engine-payload — dispatcher hands handler the
|
|
65
65
|
// already-Zod-validated payload as `unknown`; cast to the typed
|
|
66
|
-
// shape
|
|
67
|
-
// existing increment.write.ts
|
|
66
|
+
// shape declared via incrementRollingSchema. Mirror the cast in
|
|
67
|
+
// existing increment.write.ts—the same dispatcher boundary.
|
|
68
68
|
const payload = event.payload as IncrementRollingPayload;
|
|
69
69
|
const aggregateId = rollingCapAggregateId(event.user.tenantId, payload.capName);
|
|
70
70
|
|
|
@@ -4,12 +4,11 @@ import { markCapSoftWarned } from "../book-cap-usage.ts";
|
|
|
4
4
|
|
|
5
5
|
// mark-soft-warned — sets lastSoftWarnedAt on the counter so subsequent
|
|
6
6
|
// soft-cap-hits in the same period don't re-trigger notifications.
|
|
7
|
-
//
|
|
7
|
+
// This prevents notification storms.
|
|
8
8
|
//
|
|
9
|
-
// **Caller
|
|
10
|
-
// soft threshold
|
|
11
|
-
// emits
|
|
12
|
-
// emit-side is app-specific; this handler only sets the flag.
|
|
9
|
+
// **Caller pattern:** enforceCap-Helper checks whether the value crosses the
|
|
10
|
+
// soft threshold while lastSoftWarnedAt is null, then calls this handler and
|
|
11
|
+
// emits the application-specific notification. This handler only sets the flag.
|
|
13
12
|
const markSoftWarnedSchema = z.object({
|
|
14
13
|
capName: z.string().min(1).max(100),
|
|
15
14
|
periodStartIso: z.string().min(1),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generic stock-cap write-guard factory. Extracted from the near-identical
|
|
2
|
-
// cap-guard.ts app copies in show-pony and publicstatus
|
|
2
|
+
// cap-guard.ts app copies in show-pony and publicstatus — the
|
|
3
3
|
// only per-app variable was the Caps shape and how to resolve it for a
|
|
4
4
|
// tenant, so both are now factory parameters.
|
|
5
5
|
|
|
@@ -40,6 +40,13 @@ export type StockCapSpec<TCaps> = {
|
|
|
40
40
|
readonly field: string;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
+
// Lock on the counted set, not on `code`: two handlers capping the same rows under
|
|
44
|
+
// different codes must still serialize against each other.
|
|
45
|
+
function countedSetKey(spec: Pick<StockCapSpec<unknown>, "table" | "where">): string {
|
|
46
|
+
const where = Object.entries(spec.where ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
47
|
+
return `${spec.table.tableName}:${JSON.stringify(where)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
43
50
|
export type StockCapGuard<TCaps> = {
|
|
44
51
|
readonly checkStockCap: (
|
|
45
52
|
db: TenantDb,
|
|
@@ -81,7 +88,7 @@ export function createStockCapGuard<TCaps>(
|
|
|
81
88
|
// ctx.db is the handler's transaction, so the xact lock releases with its commit or
|
|
82
89
|
// rollback. A handler wrapped in several caps must list them in the same order
|
|
83
90
|
// everywhere; Postgres aborts one side of an inverse order with a deadlock error.
|
|
84
|
-
await ctx.db.advisoryXactLock(STOCK_CAP_LOCK_NAMESPACE, spec
|
|
91
|
+
await ctx.db.advisoryXactLock(STOCK_CAP_LOCK_NAMESPACE, countedSetKey(spec));
|
|
85
92
|
const failure = await checkStockCap(ctx.db, spec, { config: ctx.config });
|
|
86
93
|
return failure ?? handler.handler(event, ctx);
|
|
87
94
|
},
|
|
@@ -44,11 +44,9 @@ import { chainReservations } from "./reservation-chain.ts";
|
|
|
44
44
|
// =============================================================================
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* events); für byte/token-cap übergibt der Caller die Größe aus
|
|
51
|
-
* `event.payload`.
|
|
47
|
+
* Dynamic cap definition resolved for each handler invocation. A typical caller reads the tenant
|
|
48
|
+
* tier from `ctx` and maps it to a limit. `amount` defaults to 1 (counting events); for byte or
|
|
49
|
+
* token caps, the caller supplies the size from `event.payload`.
|
|
52
50
|
*/
|
|
53
51
|
export type CalendarCapDef = {
|
|
54
52
|
readonly capName: string;
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
defineQueryHandler,
|
|
4
4
|
mayOverrideTenant,
|
|
5
5
|
type QueryHandlerDefinition,
|
|
6
|
-
type TenantId,
|
|
7
6
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
7
|
import {
|
|
9
8
|
AccessDeniedError,
|
|
@@ -77,8 +76,7 @@ export function createCapsUsageQuery(
|
|
|
77
76
|
|
|
78
77
|
// Override path: limits must read the TARGET tenant's config, not the
|
|
79
78
|
// SystemAdmin caller's.
|
|
80
|
-
const config =
|
|
81
|
-
override === undefined ? ctx.config : ctx.configFor?.(targetTenantId as TenantId);
|
|
79
|
+
const config = override === undefined ? ctx.config : ctx.configFor?.(targetTenantId);
|
|
82
80
|
|
|
83
81
|
const rows: CapUsageWithMeta[] = await Promise.all(
|
|
84
82
|
caps.map(async (cap) => {
|
|
@@ -86,7 +86,7 @@ async function resolveLimitsByTenantAndCap(
|
|
|
86
86
|
): Promise<ReadonlyMap<string, number | null>> {
|
|
87
87
|
const entries = await Promise.all(
|
|
88
88
|
tenants.flatMap(({ tenantId, tier }) => {
|
|
89
|
-
const context: CapLimitContext = { config: configFor?.(tenantId
|
|
89
|
+
const context: CapLimitContext = { config: configFor?.(tenantId) };
|
|
90
90
|
return caps.map(
|
|
91
91
|
async (cap) => [tenantCapKey(tenantId, cap.id), await cap.limit(tier, context)] as const,
|
|
92
92
|
);
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.388.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "Corrected description of an email channel without app transport",
|
|
6
|
+
"detail": "Without an app transport only notifications declared with tenantTransport true are delivered; all others fail with missing_credentials, so such apps must mark every outgoing notification or keep an app transport."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.376.0",
|
|
4
10
|
"type": "improvement",
|
|
@@ -12,7 +12,7 @@ export function createChannelEmailFeature(options: EmailChannelOptions) {
|
|
|
12
12
|
|
|
13
13
|
const feature = defineFeature("channel-email", (r) => {
|
|
14
14
|
r.describe(
|
|
15
|
-
"Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport` and a `resolveEmail` function that maps a user ID to their email address. Mails render from stored templates (`renderer-mail-html`): a notification routed to this channel needs `template` + `meta` on `r.notification`, otherwise its delivery fails. Pass `tenantTransport: tenantMailTransport` (from `mail-foundation`) to send the notifications declared with `tenantTransport: true` through the sending tenant's own configured provider: a broken provider fails the send, a tenant without one uses the app `transport`; every other notification (password reset, sign-in) always goes through the app `transport`. `transport` may be left out when `tenantTransport` is set
|
|
15
|
+
"Wires an `EmailTransport` (typically `mail-transport-smtp` in production, `createInMemoryTransport()` in tests) into the delivery system as the `email` channel. Requires `delivery`; pass an `EmailChannelOptions` with a `transport` and a `resolveEmail` function that maps a user ID to their email address. Mails render from stored templates (`renderer-mail-html`): a notification routed to this channel needs `template` + `meta` on `r.notification`, otherwise its delivery fails. Pass `tenantTransport: tenantMailTransport` (from `mail-foundation`) to send the notifications declared with `tenantTransport: true` through the sending tenant's own configured provider: a broken provider fails the send, a tenant without one uses the app `transport`; every other notification (password reset, sign-in) always goes through the app `transport`. `transport` may be left out when `tenantTransport` is set, but then only notifications declared with `tenantTransport: true` are delivered: every other notification (password reset, sign-in, invitations) fails with `missing_credentials`, so an app without an app SMTP server must mark every notification that should go out or keep an app `transport`. Notifications declared with `unsubscribe: true` get a signed `data.unsubscribeUrl` from delivery, which turns into the `List-Unsubscribe` headers.",
|
|
16
16
|
);
|
|
17
17
|
r.uiHints({
|
|
18
18
|
displayLabel: "Email Channel",
|