@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
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// both the event log AND projection contain ciphertext.
|
|
11
11
|
// 4. inbound-message-received append (inline projection writes
|
|
12
12
|
// read_inbound_messages in the same TX). Savepoint-scoped
|
|
13
|
-
// (ctx.
|
|
13
|
+
// (ctx.events.tryAppend) — a losing concurrent ingest for the same
|
|
14
14
|
// messageAggId returns { duplicate: true } instead of failing the TX.
|
|
15
15
|
// 5. Thread rollup: mail-thread-updated append with a recalculated
|
|
16
16
|
// snapshot (count+1, max(lastMessageAt)) — the apply is a
|
|
@@ -116,7 +116,7 @@ export const ingestMessageHandler = {
|
|
|
116
116
|
data: { duplicate: true, inboundMessageAggregateId: messageAggId },
|
|
117
117
|
};
|
|
118
118
|
}
|
|
119
|
-
const existingEvents = await ctx.
|
|
119
|
+
const existingEvents = await ctx.events.load(messageAggId);
|
|
120
120
|
if (existingEvents.length > 0) {
|
|
121
121
|
// Stream existiert, Anchor fehlte → Anchor nachziehen, dann raus.
|
|
122
122
|
await ctx.db.insertOne(seenMessageTable, {
|
|
@@ -177,7 +177,7 @@ export const ingestMessageHandler = {
|
|
|
177
177
|
providerName: payload.providerName,
|
|
178
178
|
providerCursor: payload.providerCursor,
|
|
179
179
|
};
|
|
180
|
-
const messageAppend = await ctx.
|
|
180
|
+
const messageAppend = await ctx.events.tryAppend({
|
|
181
181
|
aggregateId: messageAggId,
|
|
182
182
|
aggregateType: INBOUND_MESSAGE_AGGREGATE_TYPE,
|
|
183
183
|
type: INBOUND_MESSAGE_RECEIVED_EVENT_QN,
|
|
@@ -188,8 +188,8 @@ export const ingestMessageHandler = {
|
|
|
188
188
|
// Lost the race against a concurrent ingest for the same messageAggId
|
|
189
189
|
// (Watch-Push vs. Poll-Reconciliation overlap). The winner already
|
|
190
190
|
// writes thread-rollup + seen-anchor for this message — steps 5+6
|
|
191
|
-
// skip here.
|
|
192
|
-
// the caught VersionConflictError (see ctx.
|
|
191
|
+
// skip here. events.tryAppend's savepoint keeps this TX usable despite
|
|
192
|
+
// the caught VersionConflictError (see ctx.events.tryAppend doc).
|
|
193
193
|
return {
|
|
194
194
|
isSuccess: true,
|
|
195
195
|
data: { duplicate: true, inboundMessageAggregateId: messageAggId },
|
|
@@ -197,7 +197,7 @@ export const ingestMessageHandler = {
|
|
|
197
197
|
}
|
|
198
198
|
// ---------------------------------------------------------------
|
|
199
199
|
// 5. Thread-Rollup. New snapshot (live COUNT + max(lastMessageAt)) via
|
|
200
|
-
// ctx.
|
|
200
|
+
// ctx.events.tryAppend in a bounded retry loop — the apply stays a
|
|
201
201
|
// dumb, replay-idempotent UPSERT. lastMessageAtIso is not PII (a
|
|
202
202
|
// timestamp), so it stays plaintext-comparable; subject is
|
|
203
203
|
// encrypted once up front (constant across retries, doesn't
|
|
@@ -209,9 +209,9 @@ export const ingestMessageHandler = {
|
|
|
209
209
|
// same thread arriving at once) converge on the true count.
|
|
210
210
|
// acquireNamespacedAdvisoryLock below serializes this whole
|
|
211
211
|
// countWhere+append section per threadAggId — transaction-scoped,
|
|
212
|
-
// so it survives
|
|
212
|
+
// so it survives events.tryAppend's inner SAVEPOINT and releases at
|
|
213
213
|
// the enclosing TX's commit/rollback. Without it, a concurrent
|
|
214
|
-
// commit landing between countWhere and
|
|
214
|
+
// commit landing between countWhere and events.tryAppend's own fresh
|
|
215
215
|
// version-read makes the append succeed with a stale count instead
|
|
216
216
|
// of a VersionConflictError, so the retry loop below never sees it
|
|
217
217
|
// as a conflict to retry (the original gap). Unlike step 4, a
|
|
@@ -232,7 +232,7 @@ export const ingestMessageHandler = {
|
|
|
232
232
|
await acquireNamespacedAdvisoryLock(threadRollupRunner, THREAD_ROLLUP_LOCK_NAMESPACE, threadAggId);
|
|
233
233
|
let threadAppendOk = false;
|
|
234
234
|
for (let attempt = 1; attempt <= THREAD_ROLLUP_MAX_ATTEMPTS && !threadAppendOk; attempt++) {
|
|
235
|
-
const threadEvents = await ctx.
|
|
235
|
+
const threadEvents = await ctx.events.load(threadAggId);
|
|
236
236
|
const lastThreadEvent = threadEvents[threadEvents.length - 1];
|
|
237
237
|
// @cast-boundary engine-payload — eigene events, shape via defineEvent
|
|
238
238
|
const previousThread = lastThreadEvent?.payload;
|
|
@@ -248,7 +248,7 @@ export const ingestMessageHandler = {
|
|
|
248
248
|
lastMessageAtIso: newLastAt,
|
|
249
249
|
messageCount,
|
|
250
250
|
};
|
|
251
|
-
const threadAppend = await ctx.
|
|
251
|
+
const threadAppend = await ctx.events.tryAppend({
|
|
252
252
|
aggregateId: threadAggId,
|
|
253
253
|
aggregateType: MAIL_THREAD_AGGREGATE_TYPE,
|
|
254
254
|
type: MAIL_THREAD_UPDATED_EVENT_QN,
|
|
@@ -62,7 +62,7 @@ export const updateAccountHandler = {
|
|
|
62
62
|
providerName: current.provider,
|
|
63
63
|
reason: auditReasonFor(event.user, payload.reason),
|
|
64
64
|
};
|
|
65
|
-
await ctx.
|
|
65
|
+
await ctx.events.unsafeAppend({
|
|
66
66
|
aggregateId: payload.accountId,
|
|
67
67
|
aggregateType: MAIL_ACCOUNT_AGGREGATE_TYPE,
|
|
68
68
|
type: MAIL_ACCOUNT_UPDATED_EVENT_QN,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// inbound-message → read_inbound_messages (INSERT-ignore, append-only)
|
|
4
4
|
// mail-thread → read_mail_threads (UPSERT, rollup)
|
|
5
5
|
//
|
|
6
|
-
// Apply runs in the same transaction as ctx.
|
|
6
|
+
// Apply runs in the same transaction as ctx.events.unsafeAppend, providing
|
|
7
7
|
// read-your-own-write without waiting for a dispatcher tick (as in
|
|
8
8
|
// billing-foundation).
|
|
9
9
|
//
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//
|
|
8
8
|
// **Documented deviation:** the poll was originally intended as an `r.job`
|
|
9
9
|
// cron trigger. JobContext previously had no dispatcher (see
|
|
10
|
-
// run-export-jobs.ts), but now has one (`ctx.write`/`ctx.queryAs`,
|
|
10
|
+
// run-export-jobs.ts), but now has one (`ctx.dispatch.write`/`ctx.dispatch.queryAs`,
|
|
11
11
|
// job-runner.ts). Converting the poll to a cron job is a separate migration
|
|
12
12
|
// because it changes the concurrency model, so this dispatcher contract
|
|
13
13
|
// remains in place. The app owner wires the supervisor in bin/server.ts:
|
|
@@ -37,7 +37,7 @@ export { setImapMailHostLookup } from "./imap-client.js";
|
|
|
37
37
|
// Credential-Read — per-Account-Slot, Worker-tauglich (slim ctx).
|
|
38
38
|
// =============================================================================
|
|
39
39
|
async function readCredentialDocument(ctx, account) {
|
|
40
|
-
const secrets = requireSecretsContext(ctx, FEATURE_NAME);
|
|
40
|
+
const secrets = requireSecretsContext({ secrets: ctx.secrets, auditUserId: ctx.auditUserId }, FEATURE_NAME);
|
|
41
41
|
const secret = await secrets.get(account.tenantId, inboundCredentialSecretKey(account.id));
|
|
42
42
|
if (!secret) {
|
|
43
43
|
throw new InboundAuthError(`${FEATURE_NAME}: no credential document in secret slot for account ${account.id} — ` +
|
package/dist/jobs/events.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// backed jobRun's event-store payloads (r.defineEvent + inline projections);
|
|
3
3
|
// now job-run-logger.ts parses against them before writing straight into
|
|
4
4
|
// jobRunsTable/jobRunLogsTable, so an out-of-dispatcher write still gets the
|
|
5
|
-
// same validation guarantee ctx.
|
|
5
|
+
// same validation guarantee ctx.events.append used to give it.
|
|
6
6
|
import * as z from "zod";
|
|
7
7
|
export const jobLogEntrySchema = z.object({
|
|
8
8
|
level: z.enum(["info", "warn", "error"]),
|
package/dist/jobs/feature.js
CHANGED
|
@@ -25,6 +25,7 @@ export function createJobsFeature(options = {}) {
|
|
|
25
25
|
throw new Error(`createJobsFeature: readRoles must not contain "anonymous" — job status and failures are only readable by members of the tenant.`);
|
|
26
26
|
}
|
|
27
27
|
return defineFeature("jobs", (r) => {
|
|
28
|
+
r.adoptsBundledLists();
|
|
28
29
|
r.describe("Persistence and operator tooling for background jobs registered via `r.job(...)`. Every job execution writes directly into `store_job_runs` (current status + duration) and `store_job_run_logs` (per-line log rows) from the BullMQ callbacks — no event stream in between (#2243). A daily `retention-cleanup` job deletes runs (and their logs) older than `retentionDays`; an hourly `stale-run-sweep` job marks runs stuck at status `running` past `staleRunTimeoutHours` as `failed` (#2246 — a crashed worker never fires the completion callback, so nothing else ever revisits the row). Exposes `jobs:write:trigger` (manual run) and `jobs:write:retry` (operator retry of a failed run), plus `jobs:query:list`, `jobs:query:details`, and `jobs:query:catalog` (manual jobs) for the operator UI. A job that declares `tenantVisibleFailure` also records its last failed attempt per tenant and subject in `store_tenant_job_failures`, which the tenant itself reads through `jobs:query:failures` — a translation key only, never the provider's message (fw#3079). A job that declares `tenantVisibleRun` also keeps its run state per tenant and subject in `store_tenant_job_runs` (queued, running, plus the latest completed or failed run, no payload, error text or logs), which the tenant reads through `jobs:query:tenant-runs` (fw#3616); the stale-run sweep fails its stuck `running` rows and drops `queued` rows older than `retentionDays`.");
|
|
29
30
|
r.uiHints({
|
|
30
31
|
displayLabel: "Jobs · Audit & Operator UI",
|
|
@@ -44,7 +44,7 @@ function defineConsumerWrite(name, description, transition) {
|
|
|
44
44
|
? rejected(JobErrors.consumerUnknown, "jobs.errors.consumerUnknown")
|
|
45
45
|
: rejected(JobErrors.consumerWrongStatus, "jobs.errors.consumerWrongStatus");
|
|
46
46
|
}
|
|
47
|
-
ctx.log?.error(`jobs:write:${name} failed: ${e instanceof Error ? e.message : String(e)}`, {
|
|
47
|
+
ctx.obs.log?.error(`jobs:write:${name} failed: ${e instanceof Error ? e.message : String(e)}`, {
|
|
48
48
|
consumer: event.payload.name,
|
|
49
49
|
instanceId: event.payload.instanceId,
|
|
50
50
|
});
|
|
@@ -29,6 +29,6 @@ export const projectionRebuildJob = async (rawPayload, ctx) => {
|
|
|
29
29
|
registry: ctx.registry,
|
|
30
30
|
...(skipApplyErrors === true && { errorPolicy: { skipApplyErrors: true } }),
|
|
31
31
|
});
|
|
32
|
-
ctx.log?.info?.(`[jobs:projection-rebuild] rebuilt ${projection}: ${result.eventsProcessed} events` +
|
|
32
|
+
ctx.obs.log?.info?.(`[jobs:projection-rebuild] rebuilt ${projection}: ${result.eventsProcessed} events` +
|
|
33
33
|
`${result.eventsSkipped > 0 ? ` (${result.eventsSkipped} quarantined)` : ""} in ${result.durationMs}ms`);
|
|
34
34
|
};
|
|
@@ -16,15 +16,15 @@ export const reindexEntityJob = async (rawPayload, ctx) => {
|
|
|
16
16
|
message: "[jobs:reindex-entity] ctx.registry missing — job context requires the registry.",
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
if (!ctx.searchAdapter) {
|
|
19
|
+
if (!ctx.services.searchAdapter) {
|
|
20
20
|
throw new InternalError({
|
|
21
|
-
message: "[jobs:reindex-entity] ctx.searchAdapter missing — mount a search adapter to run this job.",
|
|
21
|
+
message: "[jobs:reindex-entity] ctx.services.searchAdapter missing — mount a search adapter to run this job.",
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
24
|
// perTenant fan-out — the job-runner enqueues one child run per active
|
|
25
25
|
// tenant with ctx.systemUser scoped to it; a run without one (e.g.
|
|
26
26
|
// misconfigured manual dispatch outside the fan-out) has nothing scoped
|
|
27
|
-
// to reindex. ctx.
|
|
27
|
+
// to reindex. ctx.user.tenantId is never populated per-dispatch (only
|
|
28
28
|
// ctx.systemUser is), so it is not a valid fallback here.
|
|
29
29
|
const tenantId = ctx.systemUser?.tenantId;
|
|
30
30
|
if (tenantId === undefined) {
|
|
@@ -40,7 +40,7 @@ export const reindexEntityJob = async (rawPayload, ctx) => {
|
|
|
40
40
|
// filters `WHERE tenant_id = $1` itself with this same tenantId.
|
|
41
41
|
ctx.systemDb.assertTenantMatch(tenantId);
|
|
42
42
|
const db = ctx.systemDb.unsafeRaw("reindexEntity filters WHERE tenant_id = $1 itself with this job's tenantId");
|
|
43
|
-
const result = await reindexEntity(db, ctx.registry, ctx.searchAdapter, entity, tenantId);
|
|
44
|
-
ctx.log?.info?.(`[jobs:reindex-entity] tenant=${tenantId} reindexed ${entity}: ${result.indexedRows}/${result.scannedRows} rows indexed` +
|
|
43
|
+
const result = await reindexEntity(db, ctx.registry, ctx.services.searchAdapter, entity, tenantId);
|
|
44
|
+
ctx.obs.log?.info?.(`[jobs:reindex-entity] tenant=${tenantId} reindexed ${entity}: ${result.indexedRows}/${result.scannedRows} rows indexed` +
|
|
45
45
|
`${result.failures.length > 0 ? ` (${result.failures.length} failed)` : ""}`);
|
|
46
46
|
};
|
|
@@ -11,6 +11,6 @@ export function createRetentionCleanupJob(retentionDays) {
|
|
|
11
11
|
}
|
|
12
12
|
const db = ctx.systemDb.unsafeRaw("store_job_runs retention purges finished runs of every tenant"); // @cast-boundary db-operator — DbRunner narrows to DbConnection, jobs never run inside a DbTx
|
|
13
13
|
const result = await deleteStaleJobRuns(db, retentionDays);
|
|
14
|
-
ctx.log?.info?.(`[jobs:retention-cleanup] complete: ${JSON.stringify(result)}`);
|
|
14
|
+
ctx.obs.log?.info?.(`[jobs:retention-cleanup] complete: ${JSON.stringify(result)}`);
|
|
15
15
|
};
|
|
16
16
|
}
|
|
@@ -22,7 +22,7 @@ export const retryWrite = defineWriteHandler({
|
|
|
22
22
|
}
|
|
23
23
|
const db = ctx.systemDb.acknowledgeCrossTenant("cross-tenant job monitoring");
|
|
24
24
|
// @cast-boundary engine-payload — JobRunner attached by app-boot via ctx-extension
|
|
25
|
-
const jobRunner = ctx
|
|
25
|
+
const jobRunner = ctx.services.jobRunner;
|
|
26
26
|
const run = await fetchOne(db, jobRunsTable, { id: event.payload.runId });
|
|
27
27
|
if (!run) {
|
|
28
28
|
return writeFailure(new NotFoundError("jobRun", event.payload.runId, {
|
|
@@ -18,6 +18,6 @@ export function createStaleRunSweepJob(timeoutHours, queuedRetentionDays) {
|
|
|
18
18
|
}
|
|
19
19
|
const db = ctx.systemDb.unsafeRaw("stale-run sweep marks stuck runs of every tenant as failed"); // @cast-boundary db-operator — DbRunner narrows to DbConnection, jobs never run inside a DbTx
|
|
20
20
|
const result = await markStaleJobRunsFailed(db, timeoutHours, queuedRetentionDays);
|
|
21
|
-
ctx.log?.info?.(`[jobs:stale-run-sweep] complete: ${JSON.stringify(result)}`);
|
|
21
|
+
ctx.obs.log?.info?.(`[jobs:stale-run-sweep] complete: ${JSON.stringify(result)}`);
|
|
22
22
|
};
|
|
23
23
|
}
|
|
@@ -18,8 +18,8 @@ export const triggerWrite = defineWriteHandler({
|
|
|
18
18
|
access: { roles: ["SystemAdmin"] },
|
|
19
19
|
handler: async (event, ctx) => {
|
|
20
20
|
const registry = ctx.registry;
|
|
21
|
-
// `jobRunner` is
|
|
22
|
-
const jobRunner = ctx
|
|
21
|
+
// `services.jobRunner` is typed as the handleEvent-only JobRunnerRef; dispatch lives on the concrete runner.
|
|
22
|
+
const jobRunner = ctx.services.jobRunner; // @cast-boundary engine-bridge
|
|
23
23
|
const jobDef = registry.getJob(event.payload.jobName);
|
|
24
24
|
if (!jobDef) {
|
|
25
25
|
return writeFailure(new NotFoundError("job", event.payload.jobName, {
|
|
@@ -7,4 +7,5 @@ export type JobRunLoggerOptions = {
|
|
|
7
7
|
};
|
|
8
8
|
export type JobRunLoggerCallbacks = Pick<JobRunnerOptions, "onJobStart" | "onJobComplete" | "onJobFailed" | "onJobDeferred" | "onJobQueued" | "onJobDropped">;
|
|
9
9
|
export declare function encryptFailureError(error: string, triggeredById: string | null): Promise<string>;
|
|
10
|
+
export declare function isolateTenantRunWrite(write: () => Promise<void>, jobName: string, bullJobId: string): Promise<void>;
|
|
10
11
|
export declare function createJobRunLogger(opts: JobRunLoggerOptions): JobRunLoggerCallbacks;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { configuredPiiSubjectKms, encryptPiiValueForSubject, KeyErasedError, PII_ERASED_SENTINEL, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
2
2
|
import { acquireNamespacedAdvisoryLock, deleteMany, fetchOne, insertMany, insertOnConflictDoNothing, insertOne, transaction, updateMany, upsertOnConflict, } from "@cosmicdrift/kumiko-framework/db";
|
|
3
|
-
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { parseTenantId, SYSTEM_TENANT_ID, } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
5
|
+
import { isRowLevelSecurityViolation, runInRowLevelSecurityScope, tenantRowLevelSecurityScope, } from "@cosmicdrift/kumiko-framework/internal/row-level-security";
|
|
4
6
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
5
7
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
6
8
|
import { mapWithConcurrency } from "../shared/index.js";
|
|
@@ -27,6 +29,15 @@ const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
|
27
29
|
// Run rows hang on SYSTEM_TENANT_ID and a null trigger is a cron/system run;
|
|
28
30
|
// job-run-table.ts declares whenAbsent: "tenant" for exactly that case, so the
|
|
29
31
|
// logger must resolve the same subject the entity chain would.
|
|
32
|
+
// Job callbacks run outside any request scope; the tenant tables enforce their rows per tenant, so each write
|
|
33
|
+
// declares the tenant it belongs to.
|
|
34
|
+
function inTenantScope(tenantId, work) {
|
|
35
|
+
const parsed = parseTenantId(tenantId);
|
|
36
|
+
if (parsed === null) {
|
|
37
|
+
throw new InternalError({ message: `job run logger got a malformed tenant id "${tenantId}"` });
|
|
38
|
+
}
|
|
39
|
+
return runInRowLevelSecurityScope(tenantRowLevelSecurityScope(parsed), work);
|
|
40
|
+
}
|
|
30
41
|
function jobRunSubject(triggeredById) {
|
|
31
42
|
return triggeredById === null
|
|
32
43
|
? { kind: "tenant", tenantId: SYSTEM_TENANT_ID }
|
|
@@ -126,7 +137,7 @@ async function recordTenantJobFailure(db, jobName, outcome) {
|
|
|
126
137
|
// partial unique indexes (subject NULL vs. set) are no ON CONFLICT target.
|
|
127
138
|
// The lock makes the later of two racing final failures replace the earlier
|
|
128
139
|
// one; the indexes are the backstop against any writer that skips the lock.
|
|
129
|
-
await transaction(db, async (tx) => {
|
|
140
|
+
await inTenantScope(where.tenantId, () => transaction(db, async (tx) => {
|
|
130
141
|
await acquireNamespacedAdvisoryLock(tx, TENANT_JOB_FAILURE_LOCK_NAMESPACE, JSON.stringify([where["tenantId"], where["jobName"], where["subject"]]));
|
|
131
142
|
await deleteMany(tx, tenantJobFailuresTable, where);
|
|
132
143
|
await insertOne(tx, tenantJobFailuresTable, {
|
|
@@ -134,14 +145,14 @@ async function recordTenantJobFailure(db, jobName, outcome) {
|
|
|
134
145
|
messageKey,
|
|
135
146
|
failedAt: Temporal.Now.instant(),
|
|
136
147
|
});
|
|
137
|
-
});
|
|
148
|
+
}));
|
|
138
149
|
}
|
|
139
150
|
async function clearTenantJobFailure(db, jobName, outcome) {
|
|
140
151
|
const where = tenantJobFailureTarget(jobName, outcome);
|
|
141
152
|
// skip: no tenant-visible target — nothing was ever recorded
|
|
142
153
|
if (!where)
|
|
143
154
|
return;
|
|
144
|
-
await deleteMany(db, tenantJobFailuresTable, where);
|
|
155
|
+
await inTenantScope(where.tenantId, () => deleteMany(db, tenantJobFailuresTable, where));
|
|
145
156
|
}
|
|
146
157
|
function tenantRunKey(jobName, tenantId, subject) {
|
|
147
158
|
// skip: tenant-less run (cron resolves to SYSTEM_TENANT_ID) — no tenant-scoped query could read it
|
|
@@ -155,7 +166,7 @@ async function recordTenantRunQueued(db, jobName, bullJobId, meta) {
|
|
|
155
166
|
if (!key)
|
|
156
167
|
return;
|
|
157
168
|
const now = Temporal.Now.instant();
|
|
158
|
-
await transaction(db, async (tx) => {
|
|
169
|
+
await inTenantScope(key.tenantId, () => transaction(db, async (tx) => {
|
|
159
170
|
// A finished row under the same id is left over from before a Redis reset
|
|
160
171
|
// (ids restart at 1), not this job. Only rows queued before this BullMQ job
|
|
161
172
|
// was created qualify: a fast worker can finish before the hook runs (no
|
|
@@ -171,7 +182,7 @@ async function recordTenantRunQueued(db, jobName, bullJobId, meta) {
|
|
|
171
182
|
// DO NOTHING: a fast worker can report the start before this enqueue hook
|
|
172
183
|
// returns, and a repeated jobId (debounce, fan-out dedup) is the same job.
|
|
173
184
|
await insertOnConflictDoNothing(tx, tenantJobRunsTable, { ...key, bullJobId, status: "queued", queuedAt: now, updatedAt: now }, { conflictKeys: [...TENANT_RUN_CONFLICT_KEYS] });
|
|
174
|
-
});
|
|
185
|
+
}));
|
|
175
186
|
}
|
|
176
187
|
async function recordTenantRunStarted(db, jobName, bullJobId, meta) {
|
|
177
188
|
// skip: job did not opt in to tenantVisibleRun
|
|
@@ -182,10 +193,10 @@ async function recordTenantRunStarted(db, jobName, bullJobId, meta) {
|
|
|
182
193
|
if (!key)
|
|
183
194
|
return;
|
|
184
195
|
const now = Temporal.Now.instant();
|
|
185
|
-
await upsertOnConflict(db, tenantJobRunsTable, { ...key, bullJobId, status: "running", startedAt: now, updatedAt: now }, {
|
|
196
|
+
await inTenantScope(key.tenantId, () => upsertOnConflict(db, tenantJobRunsTable, { ...key, bullJobId, status: "running", startedAt: now, updatedAt: now }, {
|
|
186
197
|
conflictKeys: [...TENANT_RUN_CONFLICT_KEYS],
|
|
187
198
|
update: { status: "running", startedAt: now, finishedAt: null, updatedAt: now },
|
|
188
|
-
});
|
|
199
|
+
}));
|
|
189
200
|
}
|
|
190
201
|
async function recordTenantRunOutcome(db, jobName, bullJobId, outcome, result) {
|
|
191
202
|
// skip: job did not opt in to tenantVisibleRun
|
|
@@ -199,7 +210,7 @@ async function recordTenantRunOutcome(db, jobName, bullJobId, outcome, result) {
|
|
|
199
210
|
// A failed attempt BullMQ will retry goes back to waiting: the tenant keeps
|
|
200
211
|
// seeing pending work, not a failure the next attempt may still resolve.
|
|
201
212
|
if (result === "failed" && !outcome.finalAttempt) {
|
|
202
|
-
await updateMany(db, tenantJobRunsTable, { status: "queued", startedAt: null, finishedAt: null, updatedAt: now }, { tenantId: key.tenantId, jobName, bullJobId });
|
|
213
|
+
await inTenantScope(key.tenantId, () => updateMany(db, tenantJobRunsTable, { status: "queued", startedAt: null, finishedAt: null, updatedAt: now }, { tenantId: key.tenantId, jobName, bullJobId }));
|
|
203
214
|
// skip: retry pending, the row stays active until the final attempt
|
|
204
215
|
return;
|
|
205
216
|
}
|
|
@@ -207,7 +218,7 @@ async function recordTenantRunOutcome(db, jobName, bullJobId, outcome, result) {
|
|
|
207
218
|
// one it replaces. The per-key lock keeps two runs of the same subject that
|
|
208
219
|
// finish together from each missing the other's uncommitted row and leaving
|
|
209
220
|
// both behind.
|
|
210
|
-
await transaction(db, async (tx) => {
|
|
221
|
+
await inTenantScope(key.tenantId, () => transaction(db, async (tx) => {
|
|
211
222
|
await acquireNamespacedAdvisoryLock(tx, TENANT_JOB_RUN_LOCK_NAMESPACE, JSON.stringify([key.tenantId, key.jobName, key.subject]));
|
|
212
223
|
// Read under the lock: the run that finishes last in lock order must also carry the
|
|
213
224
|
// latest finishedAt, otherwise a stale timestamp makes it the row that gets pruned.
|
|
@@ -221,7 +232,7 @@ async function recordTenantRunOutcome(db, jobName, bullJobId, outcome, result) {
|
|
|
221
232
|
status: { in: ["completed", "failed"] },
|
|
222
233
|
bullJobId: { ne: bullJobId },
|
|
223
234
|
});
|
|
224
|
-
});
|
|
235
|
+
}));
|
|
225
236
|
}
|
|
226
237
|
async function requeueTenantRun(db, jobName, bullJobId, meta) {
|
|
227
238
|
// skip: job did not opt in to tenantVisibleRun
|
|
@@ -231,25 +242,29 @@ async function requeueTenantRun(db, jobName, bullJobId, meta) {
|
|
|
231
242
|
// skip: system-tenant run, no tenant-scoped query reads it
|
|
232
243
|
if (!key)
|
|
233
244
|
return;
|
|
234
|
-
await updateMany(db, tenantJobRunsTable, { status: "queued", startedAt: null, finishedAt: null, updatedAt: Temporal.Now.instant() }, { tenantId: key.tenantId, jobName, bullJobId });
|
|
245
|
+
await inTenantScope(key.tenantId, () => updateMany(db, tenantJobRunsTable, { status: "queued", startedAt: null, finishedAt: null, updatedAt: Temporal.Now.instant() }, { tenantId: key.tenantId, jobName, bullJobId }));
|
|
235
246
|
}
|
|
236
247
|
async function dropTenantRunQueued(db, jobName, bullJobId, meta) {
|
|
237
|
-
await deleteMany(db, tenantJobRunsTable, {
|
|
248
|
+
await inTenantScope(meta.tenantId, () => deleteMany(db, tenantJobRunsTable, {
|
|
238
249
|
tenantId: meta.tenantId,
|
|
239
250
|
jobName,
|
|
240
251
|
bullJobId,
|
|
241
252
|
status: "queued",
|
|
242
|
-
});
|
|
253
|
+
}));
|
|
243
254
|
}
|
|
244
255
|
const log = createFallbackLogger("job-run-logger");
|
|
245
256
|
// The tenant run state is an optional side write: a throw here must not
|
|
246
257
|
// fail the enqueue, skip the run-row write or turn a completed run into a
|
|
247
|
-
// BullMQ retry.
|
|
248
|
-
async function isolateTenantRunWrite(write, jobName, bullJobId) {
|
|
258
|
+
// BullMQ retry. A row-level-security rejection is logged louder (a missing or wrong tenant scope), never rethrown.
|
|
259
|
+
export async function isolateTenantRunWrite(write, jobName, bullJobId) {
|
|
249
260
|
try {
|
|
250
261
|
await write();
|
|
251
262
|
}
|
|
252
263
|
catch (e) {
|
|
264
|
+
if (isRowLevelSecurityViolation(e)) {
|
|
265
|
+
log.error("tenant job run state write rejected by row level security: missing or wrong tenant scope", { jobName, bullJobId });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
253
268
|
log.error("tenant job run state write failed", {
|
|
254
269
|
jobName,
|
|
255
270
|
bullJobId,
|
|
@@ -258,12 +273,17 @@ async function isolateTenantRunWrite(write, jobName, bullJobId) {
|
|
|
258
273
|
}
|
|
259
274
|
}
|
|
260
275
|
// The tenant failure record is an optional side write: a throw here must not
|
|
261
|
-
// skip the run-row update or turn a completed run into a BullMQ retry.
|
|
276
|
+
// skip the run-row update or turn a completed run into a BullMQ retry. A row-level-security rejection is
|
|
277
|
+
// logged louder (a missing or wrong tenant scope), never rethrown.
|
|
262
278
|
async function isolateTenantFailureWrite(write, jobName, bullJobId) {
|
|
263
279
|
try {
|
|
264
280
|
await write();
|
|
265
281
|
}
|
|
266
282
|
catch (e) {
|
|
283
|
+
if (isRowLevelSecurityViolation(e)) {
|
|
284
|
+
log.error("tenant job failure record write rejected by row level security: missing or wrong tenant scope", { jobName, bullJobId });
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
267
287
|
log.error("tenant job failure record write failed", {
|
|
268
288
|
jobName,
|
|
269
289
|
bullJobId,
|
|
@@ -340,7 +360,7 @@ export function createJobRunLogger(opts) {
|
|
|
340
360
|
const triggeredById = meta.triggeredById ?? null;
|
|
341
361
|
cachePut(bullJobId, runId, triggeredById);
|
|
342
362
|
// Parse against the registered schema so out-of-dispatcher writes
|
|
343
|
-
// get the same validation guarantee as ctx.
|
|
363
|
+
// get the same validation guarantee as ctx.events.append. A shape
|
|
344
364
|
// drift between feature + logger fails loudly at the source
|
|
345
365
|
// instead of silently landing on the table.
|
|
346
366
|
const payload = runStartedSchema.parse({
|
package/dist/ledger/web/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Client-safe ledger surface: QN constants, pure recurring helpers
|
|
2
2
|
// (projection + Soll/Ist merge), and Zod payload schemas for client-side
|
|
3
3
|
// pre-validation before dispatch. The full `../ledger` entry also re-exports
|
|
4
|
-
// the feature/handlers/executor, which pull
|
|
4
|
+
// the feature/handlers/executor, which pull the DB driver; importing it in a
|
|
5
5
|
// browser bundle fails on Node builtins. Client screens import dispatch QNs and
|
|
6
6
|
// pure forecast/merge helpers from this module, then dispatch via the renderer's
|
|
7
7
|
// useDispatcher.
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { type TemplateResolverApi } from "@cosmicdrift/kumiko-bundled-features/template-resolver";
|
|
2
2
|
import { type LegalRequiredBlock } from "../web/constants.ts";
|
|
3
3
|
export type LegalPagesBootCheckCtx = {
|
|
4
|
-
readonly
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
readonly services: {
|
|
5
|
+
readonly extensions: {
|
|
6
|
+
readonly templateResolver?: TemplateResolverApi;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
readonly obs?: {
|
|
10
|
+
readonly log?: {
|
|
11
|
+
readonly info?: (msg: string) => void;
|
|
12
|
+
readonly warn?: (msg: string) => void;
|
|
13
|
+
};
|
|
8
14
|
};
|
|
9
15
|
};
|
|
10
16
|
export declare function runLegalPagesBootCheck(ctx: LegalPagesBootCheckCtx, requiredBlocks?: readonly LegalRequiredBlock[]): Promise<void>;
|
|
@@ -19,7 +19,7 @@ export async function runLegalPagesBootCheck(ctx, requiredBlocks = LEGAL_REQUIRE
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
if (missing.length === 0) {
|
|
22
|
-
ctx.log?.info?.("legal-pages boot-check: alle Pflicht-Blocks vorhanden");
|
|
22
|
+
ctx.obs?.log?.info?.("legal-pages boot-check: alle Pflicht-Blocks vorhanden");
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
25
|
const message = `legal-pages: missing ${missing.length} required text-block(s) in SYSTEM_TENANT: ` +
|
|
@@ -28,6 +28,6 @@ export async function runLegalPagesBootCheck(ctx, requiredBlocks = LEGAL_REQUIRE
|
|
|
28
28
|
if (process.env["NODE_ENV"] === "production") {
|
|
29
29
|
throw new Error(`Boot-Validation failed: ${message}`);
|
|
30
30
|
}
|
|
31
|
-
ctx.log?.warn?.(message);
|
|
31
|
+
ctx.obs?.log?.warn?.(message);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -9,4 +9,6 @@ export type LegalContentSeedSource = {
|
|
|
9
9
|
export type LegalContentSeedFn = (deps: {
|
|
10
10
|
readonly db: DbConnection;
|
|
11
11
|
}) => Promise<void>;
|
|
12
|
-
export declare function legalContentSeed(source: LegalContentSeedSource
|
|
12
|
+
export declare function legalContentSeed(source: LegalContentSeedSource, options?: {
|
|
13
|
+
readonly folder?: string;
|
|
14
|
+
}): LegalContentSeedFn;
|
package/dist/legal-pages/seed.js
CHANGED
|
@@ -28,11 +28,11 @@ async function loadBlocksFromJson(path) {
|
|
|
28
28
|
}
|
|
29
29
|
// Boot seed for runProdApp's `seeds: [...]`. The JSON template is authoritative
|
|
30
30
|
// (ifExists "update"), so legally required text changes reach rows seeded once.
|
|
31
|
-
export function legalContentSeed(source) {
|
|
31
|
+
export function legalContentSeed(source, options) {
|
|
32
32
|
return async ({ db }) => {
|
|
33
33
|
const blocks = "blocks" in source
|
|
34
34
|
? legalContentBlocksSchema.parse(source.blocks)
|
|
35
35
|
: await loadBlocksFromJson(source.path);
|
|
36
|
-
await seedLegalContentFromJson(db, blocks);
|
|
36
|
+
await seedLegalContentFromJson(db, blocks, options);
|
|
37
37
|
};
|
|
38
38
|
}
|
|
@@ -9,7 +9,7 @@ export declare const MAIL_TRANSPORT_EXTENSION: "mailTransport";
|
|
|
9
9
|
* **Why not the full HandlerContext?** On the worker path (transport builds
|
|
10
10
|
* triggered by r.job, such as the user-data-rights forget/export cron) there
|
|
11
11
|
* is no per-request `config`/`tx`/`actor`; the wrapper builds the per-tenant
|
|
12
|
-
* ConfigAccessor from `ctx.configResolver`. A plugin reading `ctx.tx` or other
|
|
12
|
+
* ConfigAccessor from `ctx.services.configResolver`. A plugin reading `ctx.tx` or other
|
|
13
13
|
* request-only fields would break the worker path at runtime, and that would
|
|
14
14
|
* only show up with real SMTP in production. Casting
|
|
15
15
|
* `ctx as unknown as HandlerContext` satisfies the compiler but fails at
|
|
@@ -27,7 +27,7 @@ export declare const MAIL_TRANSPORT_EXTENSION: "mailTransport";
|
|
|
27
27
|
export type MailTransportContext = {
|
|
28
28
|
readonly config?: ConfigAccessor;
|
|
29
29
|
readonly registry?: Registry;
|
|
30
|
-
readonly secrets
|
|
30
|
+
readonly secrets: import("@cosmicdrift/kumiko-framework/secrets").SecretsContext | undefined;
|
|
31
31
|
readonly auditUserId?: string | undefined;
|
|
32
32
|
};
|
|
33
33
|
/**
|
|
@@ -4,6 +4,7 @@ import { access, createTenantConfig } from "@cosmicdrift/kumiko-framework/engine
|
|
|
4
4
|
const tenantAdminWrite = access.withSystem(access.roles("TenantAdmin"));
|
|
5
5
|
const tenantAdminRead = access.roles("TenantAdmin", "SystemAdmin");
|
|
6
6
|
// required: true ↔ the requireNonEmpty calls in buildSmtpTransport — keep in sync.
|
|
7
|
+
// authUser stays optional: empty means an unauthenticated relay.
|
|
7
8
|
export const SMTP_CONFIG_KEYS = {
|
|
8
9
|
host: createTenantConfig("text", {
|
|
9
10
|
required: true,
|
|
@@ -39,7 +40,6 @@ export const SMTP_CONFIG_KEYS = {
|
|
|
39
40
|
},
|
|
40
41
|
}),
|
|
41
42
|
authUser: createTenantConfig("text", {
|
|
42
|
-
required: true,
|
|
43
43
|
default: "",
|
|
44
44
|
write: tenantAdminWrite,
|
|
45
45
|
read: tenantAdminRead,
|
|
@@ -76,8 +76,7 @@ export function createMailTransportSmtpFeature() {
|
|
|
76
76
|
return `${plaintext.slice(0, 3)}...${plaintext.slice(-2)}`;
|
|
77
77
|
},
|
|
78
78
|
scope: "tenant",
|
|
79
|
-
//
|
|
80
|
-
required: true,
|
|
79
|
+
// Only needed once authUser is set (readPassword throws then), so readiness must not demand it.
|
|
81
80
|
});
|
|
82
81
|
const configKeys = r.config({ keys: SMTP_CONFIG_KEYS });
|
|
83
82
|
r.translations({ keys: MAIL_TRANSPORT_SMTP_I18N });
|
|
@@ -126,8 +125,10 @@ async function buildSmtpTransport(ctx, tenantId) {
|
|
|
126
125
|
const port = requireDefined(await ctxConfig(sharedMailTransportSmtpFeature.exports.configKeys.port), FEATURE_NAME, "port"); // @cast-boundary engine-payload
|
|
127
126
|
const secure = requireDefined(await ctxConfig(sharedMailTransportSmtpFeature.exports.configKeys.secure), FEATURE_NAME, "secure"); // @cast-boundary engine-payload
|
|
128
127
|
const from = requireNonEmpty(await ctxConfig(sharedMailTransportSmtpFeature.exports.configKeys.from), FEATURE_NAME, "from", SMTP_HINT);
|
|
129
|
-
const authUser =
|
|
130
|
-
const
|
|
128
|
+
const authUser = (await ctxConfig(sharedMailTransportSmtpFeature.exports.configKeys.authUser))?.trim();
|
|
129
|
+
const auth = authUser === undefined || authUser === ""
|
|
130
|
+
? undefined
|
|
131
|
+
: { user: authUser, pass: await readPassword(ctx, tenantId) };
|
|
131
132
|
let target;
|
|
132
133
|
try {
|
|
133
134
|
target = await resolveMailConnectTarget(host, {
|
|
@@ -155,12 +156,12 @@ async function buildSmtpTransport(ctx, tenantId) {
|
|
|
155
156
|
port,
|
|
156
157
|
secure,
|
|
157
158
|
from,
|
|
158
|
-
auth
|
|
159
|
+
...(auth && { auth }),
|
|
159
160
|
...(target.servername && { servername: target.servername }),
|
|
160
161
|
});
|
|
161
162
|
}
|
|
162
163
|
async function readPassword(ctx, tenantId) {
|
|
163
|
-
const secrets = requireSecretsContext(ctx, FEATURE_NAME);
|
|
164
|
+
const secrets = requireSecretsContext({ secrets: ctx.secrets, auditUserId: ctx.auditUserId }, FEATURE_NAME);
|
|
164
165
|
const branded = await secrets.get(tenantId, SMTP_PASSWORD);
|
|
165
166
|
return requireSecretSet(branded, FEATURE_NAME, SMTP_PASSWORD.name).reveal();
|
|
166
167
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MANAGED_PAGES_FEATURE: "managed-pages";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MANAGED_PAGES_FEATURE = "managed-pages";
|
|
@@ -4,6 +4,7 @@ import { RateLimitError } from "@cosmicdrift/kumiko-framework/errors";
|
|
|
4
4
|
import { cachedSecurePageResponse, EMPTY_BRANDING, renderSafeMarkdown, wrapInLayout, } from "../page-render/index.js";
|
|
5
5
|
import { rateLimitedTextResponse } from "../shared/index.js";
|
|
6
6
|
import { BRANDING_KEYS, BRANDING_QUERY_QN, CUSTOM_CSS_KEY, coerceBranding } from "./branding.js";
|
|
7
|
+
import { MANAGED_PAGES_FEATURE } from "./constants.js";
|
|
7
8
|
import { createBrandingQuery } from "./handlers/branding.query.js";
|
|
8
9
|
import { bySlugQuery } from "./handlers/by-slug.query.js";
|
|
9
10
|
import { byTenantPublishedQuery } from "./handlers/by-tenant-published.query.js";
|
|
@@ -79,7 +80,8 @@ export function createManagedPagesFeature(opts) {
|
|
|
79
80
|
const basePath = opts.basePath ?? "/p";
|
|
80
81
|
const defaultLang = opts.defaultLang ?? "en";
|
|
81
82
|
const allowCustomCss = opts.allowCustomCss ?? false;
|
|
82
|
-
return defineFeature(
|
|
83
|
+
return defineFeature(MANAGED_PAGES_FEATURE, (r) => {
|
|
84
|
+
r.adoptsBundledLists();
|
|
83
85
|
r.describe("Tenant-editable, server-rendered public pages with per-tenant branding. Stores one Markdown `page` per `(tenantId, slug, lang)` in the `read_pages` entity table with a `published` gate plus `description`/`ogImage` SEO meta. Registers an anonymous `GET {basePath}/:slug` route that resolves the tenant from the request Host via the app-supplied `resolveApexTenant`, serves only published pages (drafts → 404), renders Markdown through the hardened `page-render` core, and isolates per-tenant content with `Vary: Host`. Ships TenantAdmin/SystemAdmin admin screens (`entityList` + `entityEdit`) backed by convention CRUD handlers (`managed-pages:write:page:{create,update,delete}`, `managed-pages:query:page:{list,detail}`); the app wires nav/workspace onto `managed-pages:screen:page-list`. Also exposes `managed-pages:query:by-tenant-published` (anonymous, SQL-filtered on `published`) for sitemap/discovery consumers such as the `seo` feature. Branding (via `config`, scope tenant): `branding-{title,description,site-url,accent-color,logo-url,layout-preset,custom-css}` keys with write-time validation (hex color, https URLs), a `configEdit` self-service screen (`managed-pages:screen:branding-settings`), and a `managed-pages:query:branding` read that the render path applies as scoped `:root` CSS vars + a logo/title header. Also exposes `managed-pages:write:set` (idempotent slug-keyed upsert, SystemAdmin cross-tenant via `tenantIdOverride`) as a provisioning API. Requires `config` + `anonymousAccess` wired at app bootstrap.");
|
|
84
86
|
r.uiHints({
|
|
85
87
|
displayLabel: "Managed Pages · Public CMS",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { type BrandingTokens, brandingHeaderHtml, brandingStyleBlock, EMPTY_BRANDING, sanitizeTenantCss, TENANT_CONTENT_ATTR, tenantStyleBlock, } from "../page-render/index.ts";
|
|
2
2
|
export { BRANDING_QN, MANAGED_PAGES_CSS_FEATURE } from "./branding.ts";
|
|
3
|
+
export { MANAGED_PAGES_FEATURE } from "./constants.ts";
|
|
3
4
|
export { createManagedPagesCssFeature } from "./css-gate.ts";
|
|
4
5
|
export { createManagedPagesFeature, type ManagedPagesOptions, type ManagedPagesWrapLayout, } from "./feature.ts";
|
|
5
6
|
export { type PageRow, pageEntity, pagesTable } from "./table.ts";
|
|
@@ -13,6 +13,7 @@ export { brandingHeaderHtml, brandingStyleBlock, EMPTY_BRANDING, sanitizeTenantC
|
|
|
13
13
|
// (`config:write:set`) — single source for the `managed-pages:config:branding-*`
|
|
14
14
|
// strings, so apps + the per-tenant migration never hardcode them.
|
|
15
15
|
export { BRANDING_QN, MANAGED_PAGES_CSS_FEATURE } from "./branding.js";
|
|
16
|
+
export { MANAGED_PAGES_FEATURE } from "./constants.js";
|
|
16
17
|
export { createManagedPagesCssFeature } from "./css-gate.js";
|
|
17
18
|
export { createManagedPagesFeature, } from "./feature.js";
|
|
18
19
|
export { pageEntity, pagesTable } from "./table.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
-
import type
|
|
2
|
+
import { type SessionUser, type TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { type SeedIfExists } from "@cosmicdrift/kumiko-framework/seeding";
|
|
4
4
|
export type SeedPageOptions = {
|
|
5
5
|
readonly tenantId: TenantId;
|