@cosmicdrift/kumiko-samples 0.388.0 → 0.390.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/bundled-features/package.json +25 -13
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/index.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +42 -26
- package/packages/bundled-features/src/audit/feature.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +13 -1
- package/packages/bundled-features/src/auth-email-password/changes.json +18 -0
- package/packages/bundled-features/src/auth-email-password/feature.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +7 -7
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-account-unlock.write.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +9 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +11 -7
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -4
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +9 -7
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +23 -16
- package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +1 -0
- package/packages/bundled-features/src/auth-email-password/handlers/reset-password.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +16 -8
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +4 -2
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +10 -2
- package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +16 -3
- package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -0
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +8 -2
- package/packages/bundled-features/src/auth-email-password/web/auth-redirect.ts +23 -21
- package/packages/bundled-features/src/auth-email-password/web/default-auth-pages-gate.tsx +6 -3
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -1
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -0
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +16 -5
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +7 -1
- package/packages/bundled-features/src/auth-email-password/web/return-path.ts +35 -0
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +6 -1
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +11 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +12 -2
- package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
- package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
- package/packages/bundled-features/src/auth-mfa/config.ts +3 -7
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +24 -14
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +9 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +6 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start.write.ts +3 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +14 -9
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -4
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +25 -14
- package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +10 -4
- package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +17 -8
- package/packages/bundled-features/src/auth-mfa/verify-factor.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/changes.json +6 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +5 -5
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +10 -7
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +15 -12
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-open-cancels.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-provider-lock.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +17 -58
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +10 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +18 -15
- package/packages/bundled-features/src/billing-foundation/events.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/feature.ts +8 -3
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +5 -5
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/types.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/cap-counter/book-cap-usage.ts +5 -4
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +21 -8
- package/packages/bundled-features/src/cap-counter/changes.json +12 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
- package/packages/bundled-features/src/cap-counter/feature.ts +3 -2
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +1 -1
- package/packages/bundled-features/src/cap-counter/reservation-chain.ts +1 -1
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +4 -4
- package/packages/bundled-features/src/cap-overview/feature.ts +1 -0
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -1
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +12 -7
- package/packages/bundled-features/src/config/changes.json +6 -0
- package/packages/bundled-features/src/config/feature.ts +6 -6
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +23 -13
- package/packages/bundled-features/src/config/handlers/reset.write.ts +3 -3
- package/packages/bundled-features/src/config/handlers/selected-extensions.query.ts +1 -1
- package/packages/bundled-features/src/config/handlers/set.write.ts +22 -5
- package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/changes.json +12 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +9 -9
- package/packages/bundled-features/src/crypto-shredding/resolve-file-bytes-deleter.ts +3 -3
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +1 -1
- package/packages/bundled-features/src/custom-fields/index.ts +4 -0
- package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
- package/packages/bundled-features/src/data-retention/feature.ts +1 -1
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +12 -2
- package/packages/bundled-features/src/delivery/attempt-log.ts +1 -1
- package/packages/bundled-features/src/delivery/changes.json +12 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +6 -5
- package/packages/bundled-features/src/delivery/feature.ts +3 -2
- package/packages/bundled-features/src/delivery/index.ts +5 -0
- package/packages/bundled-features/src/delivery/jobs.ts +11 -11
- package/packages/bundled-features/src/delivery/system-channel-context.ts +33 -0
- package/packages/bundled-features/src/delivery/testing.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/append-missing-extract-event.ts +84 -0
- package/packages/bundled-features/src/document-ingest-foundation/changes.json +6 -0
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +16 -1
- package/packages/bundled-features/src/document-ingest-foundation/index.ts +11 -0
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -0
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +5 -1
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +7 -5
- package/packages/bundled-features/src/file-foundation/feature.ts +1 -1
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -5
- package/packages/bundled-features/src/file-provider-s3/feature.ts +4 -1
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +8 -8
- package/packages/bundled-features/src/form-draft/feature.ts +1 -1
- package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +22 -12
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +3 -3
- package/packages/bundled-features/src/form-draft/handlers/get.query.ts +1 -1
- package/packages/bundled-features/src/foundation-shared/config-helpers.ts +1 -1
- package/packages/bundled-features/src/host-routing/changes.json +6 -0
- package/packages/bundled-features/src/host-routing/validate-options.ts +5 -0
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +4 -1
- package/packages/bundled-features/src/jobs/changes.json +6 -0
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +1 -0
- package/packages/bundled-features/src/jobs/handlers/consumer.write.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +11 -5
- package/packages/bundled-features/src/jobs/handlers/retention-cleanup.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/trigger.write.ts +2 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +139 -90
- package/packages/bundled-features/src/ledger/web/index.ts +1 -1
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
- package/packages/bundled-features/src/legal-pages/seed.ts +5 -2
- package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/mail-transport-smtp/changes.json +12 -0
- package/packages/bundled-features/src/mail-transport-smtp/config-keys.ts +1 -1
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +13 -12
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/constants.ts +1 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +3 -1
- package/packages/bundled-features/src/managed-pages/index.ts +1 -0
- package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -1
- package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/rate-limiting/handlers/status.query.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +4 -1
- package/packages/bundled-features/src/renderer-foundation/api.ts +3 -3
- package/packages/bundled-features/src/secrets/announce-secret-change.ts +4 -4
- package/packages/bundled-features/src/secrets/changes.json +6 -0
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +4 -3
- package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +13 -8
- package/packages/bundled-features/src/secrets/handlers/set.write.ts +4 -3
- package/packages/bundled-features/src/secrets/secrets-context.ts +16 -6
- package/packages/bundled-features/src/secrets/write-gate.ts +10 -3
- package/packages/bundled-features/src/seo/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/changes.json +19 -0
- package/packages/bundled-features/src/sessions/feature.ts +2 -1
- package/packages/bundled-features/src/sessions/handlers/cleanup.job.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/detail.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/mine.query.ts +5 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +3 -3
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/index.ts +1 -1
- package/packages/bundled-features/src/sessions/screens.ts +8 -4
- package/packages/bundled-features/src/sessions/session-callbacks.ts +1 -9
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +1 -1
- package/packages/bundled-features/src/sessions/web/session-device-cell.tsx +10 -12
- package/packages/bundled-features/src/sessions/web/user-agent.ts +12 -0
- package/packages/bundled-features/src/shared/index.ts +15 -0
- package/packages/bundled-features/src/shared/purge-files-reaching-subject.ts +36 -4
- package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -5
- package/packages/bundled-features/src/shared/run-in-sub-transaction.ts +5 -5
- package/packages/bundled-features/src/shared/stream-tenant-backfill.ts +201 -0
- package/packages/bundled-features/src/shared/stream-tenant-derived-state.ts +107 -0
- package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
- package/packages/bundled-features/src/step-dispatcher/deliver-dispatch-request.ts +17 -3
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -1
- package/packages/bundled-features/src/step-dispatcher/mail-runner.ts +2 -1
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +1 -1
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +7 -7
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +5 -3
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +7 -7
- package/packages/bundled-features/src/tags/feature.ts +1 -0
- package/packages/bundled-features/src/template-resolver/api.ts +3 -3
- package/packages/bundled-features/src/template-resolver/changes.json +54 -0
- package/packages/bundled-features/src/template-resolver/document-ops.ts +20 -1
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +5 -4
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +36 -11
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +32 -7
- package/packages/bundled-features/src/template-resolver/handlers/patch-draft.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test-status.query.ts +22 -19
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +6 -2
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +4 -2
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/seeding.ts +22 -5
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +68 -19
- package/packages/bundled-features/src/template-resolver/template-resources.ts +4 -4
- package/packages/bundled-features/src/template-resolver/testing.ts +2 -2
- package/packages/bundled-features/src/template-resolver/web/document-insert-fields.tsx +222 -0
- package/packages/bundled-features/src/template-resolver/web/document-insert-model.ts +164 -0
- package/packages/bundled-features/src/template-resolver/web/document-insert-panel.tsx +115 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-bubble.tsx +177 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-content.tsx +38 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-context.ts +119 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +51 -1098
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-helpers.ts +161 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-layout.tsx +83 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar-groups.tsx +153 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-toolbar.tsx +182 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +18 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +6 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +19 -6
- package/packages/bundled-features/src/template-resolver/web/qualified-names.ts +1 -0
- package/packages/bundled-features/src/template-resolver/web/use-document-tiptap-model.ts +138 -0
- package/packages/bundled-features/src/tenant/changes.json +19 -0
- package/packages/bundled-features/src/tenant/feature.ts +1 -0
- package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -2
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -3
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -1
- package/packages/bundled-features/src/tenant/index.ts +5 -0
- package/packages/bundled-features/src/tenant/invited-membership.ts +6 -1
- package/packages/bundled-features/src/tenant/schema/tenant.ts +4 -1
- package/packages/bundled-features/src/tenant/seeding.ts +65 -62
- package/packages/bundled-features/src/tenant/stream-tenant-backfill.ts +115 -0
- package/packages/bundled-features/src/tenant/testing.ts +77 -0
- package/packages/bundled-features/src/tenant-handover/events.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +5 -5
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +22 -10
- package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +22 -11
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +23 -12
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +42 -4
- package/packages/bundled-features/src/tenant-lifecycle/testing.ts +9 -8
- package/packages/bundled-features/src/tier-engine/entity.ts +1 -1
- package/packages/bundled-features/src/tier-engine/feature.ts +8 -3
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +3 -3
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +7 -1
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +3 -3
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +71 -97
- package/packages/bundled-features/src/user/feature.ts +1 -0
- package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +2 -2
- package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -2
- package/packages/bundled-features/src/user/index.ts +2 -0
- package/packages/bundled-features/src/user/invited-global-roles.ts +6 -1
- package/packages/bundled-features/src/user/principal-status.ts +1 -1
- package/packages/bundled-features/src/user/seeding.ts +24 -6
- package/packages/bundled-features/src/user/testing.ts +22 -0
- package/packages/bundled-features/src/user-data-rights/COMPLIANCE.md +1 -1
- package/packages/bundled-features/src/user-data-rights/README.md +1 -1
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +12 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +36 -0
- package/packages/bundled-features/src/user-data-rights/feature.ts +30 -24
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/deletion-grace-period.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +12 -6
- package/packages/bundled-features/src/user-data-rights/index.ts +8 -1
- package/packages/bundled-features/src/user-data-rights/lib/mail-transport-resolver.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/storage-provider-resolver.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +6 -1
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +8 -2
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +14 -3
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +11 -1
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +16 -0
- package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -0
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/feature.ts +1 -0
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +2 -2
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +34 -10
- package/packages/bundled-features/src/waitlist/import.ts +23 -3
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +8 -2
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -1
- package/packages/bundled-features/src/waitlist/options.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/db/queries/due-runs.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -3
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +2 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +8 -8
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -4
- package/samples/apps/cap-billing-demo/src/feature.ts +19 -5
- package/samples/apps/config-demo/src/app/server.ts +1 -1
- package/samples/apps/styleguide/src/app/client.tsx +2 -0
- package/samples/apps/styleguide/src/app/seed.ts +6 -4
- package/samples/apps/styleguide/src/app/server.ts +6 -3
- package/samples/apps/styleguide/src/features/widgets/feature.ts +15 -4
- package/samples/apps/styleguide/src/features/workboard/feature.ts +185 -0
- package/samples/apps/styleguide/src/features/workboard/i18n.ts +27 -0
- package/samples/apps/styleguide/src/features/workboard/seed.ts +121 -0
- package/samples/apps/styleguide/src/features/workboard/web.ts +13 -0
- package/samples/apps/ui-walkthrough/src/features/rental/i18n.ts +2 -0
- package/samples/apps/ui-walkthrough/src/features/rental/screens.ts +9 -0
- package/samples/apps/use-all-bundled/bin/main.ts +2 -0
- package/samples/apps/use-all-bundled/feature-manifest.json +17 -1
- package/samples/apps/use-all-bundled/kumiko/schema.ts +2 -0
- package/samples/apps/use-all-bundled/kumiko.config.ts +2 -0
- package/samples/apps/use-all-bundled/package.json +2 -1
- package/samples/apps/use-all-bundled/src/app/dev-app-options.ts +2 -0
- package/samples/apps/use-all-bundled/src/app/notes-feature.ts +2 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
- package/samples/apps/use-all-bundled/src/bundled-lists.ts +15 -0
- package/samples/apps/user-data-rights-demo/README.md +1 -1
- package/samples/apps/user-data-rights-demo/src/feature.ts +3 -2
- package/samples/recipes/api-worker-split/README.md +2 -1
- package/samples/recipes/api-worker-split/src/feature.ts +2 -1
- package/samples/recipes/cross-feature-events/README.md +5 -5
- package/samples/recipes/cross-feature-events/package.json +1 -1
- package/samples/recipes/cross-feature-events/src/feature.ts +4 -4
- package/samples/recipes/event-sourcing/README.md +5 -5
- package/samples/recipes/event-sourcing/package.json +1 -1
- package/samples/recipes/event-sourcing/src/feature.ts +15 -15
- package/samples/recipes/files-post-processing/README.md +1 -1
- package/samples/recipes/files-post-processing/package.json +1 -1
- package/samples/recipes/files-post-processing/src/feature.ts +6 -4
- package/samples/recipes/golden-path/src/feature.ts +2 -1
- package/samples/recipes/lane-routing/package.json +1 -1
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/README.md +4 -0
- package/samples/recipes/lifecycle-hooks/src/feature.ts +26 -1
- package/samples/recipes/list-table-options/README.md +50 -0
- package/samples/recipes/list-table-options/package.json +11 -0
- package/samples/recipes/list-table-options/src/feature.ts +221 -0
- package/samples/recipes/managed-config/README.md +3 -3
- package/samples/recipes/pdf-audit-export/src/feature.ts +2 -1
- package/samples/recipes/search/README.md +1 -1
- package/samples/recipes/search/src/feature.ts +3 -3
- package/samples/recipes/secrets-demo/README.md +1 -1
- package/samples/recipes/secrets-demo/src/feature.ts +5 -2
- package/samples/recipes/state-machine/src/events.ts +4 -3
- package/samples/recipes/state-machine/src/reducer.ts +1 -1
- package/samples/recipes/tenant-isolation/README.md +1 -0
- package/samples/recipes/tenant-isolation/src/feature.ts +36 -0
- package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
- package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
16
16
|
import {
|
|
17
17
|
assertNotificationVariables,
|
|
18
|
+
attachOutsideTransaction,
|
|
18
19
|
bridgeStub,
|
|
19
20
|
createSystemUser,
|
|
20
21
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -98,7 +99,7 @@ export type DeliveryServiceOptions = {
|
|
|
98
99
|
// inline delivery (job-less setups, unit tests).
|
|
99
100
|
readonly jobRunner?: NotifyJobDispatcher;
|
|
100
101
|
// Credentials source for inline-delivered chat channels (webhook URL / bot
|
|
101
|
-
// token). Jobs get ctx.secrets from the job runner instead.
|
|
102
|
+
// token). Jobs get ctx.services.secrets from the job runner instead.
|
|
102
103
|
readonly secrets?: SecretsContext;
|
|
103
104
|
// Seals the recipient address inside queued job payloads (tenant scope). Absent means the
|
|
104
105
|
// payload carries the plaintext address.
|
|
@@ -277,7 +278,7 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
277
278
|
sink: escapeHatchAuditSink,
|
|
278
279
|
log: logError,
|
|
279
280
|
});
|
|
280
|
-
// ctx.db/
|
|
281
|
+
// ctx.db/db.outsideTransaction are tenant-filtered even for a systemScope handler:
|
|
281
282
|
// the dispatcher closes them off entirely there, so "system" mode would hand this
|
|
282
283
|
// path a cross-tenant read the dispatcher never allows. Cross-tenant reach only
|
|
283
284
|
// exists through the grant-carrying systemDb view below.
|
|
@@ -299,6 +300,7 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
299
300
|
db: run.db,
|
|
300
301
|
tenantId,
|
|
301
302
|
mode: "system",
|
|
303
|
+
reason: "delivery fan-out of a systemScope handler resolves recipients across tenants",
|
|
302
304
|
grants: {
|
|
303
305
|
report,
|
|
304
306
|
unsafeRaw: { reason: `r.systemScope() handler "${tenantUserIdsQuery}"` },
|
|
@@ -306,13 +308,12 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
306
308
|
})
|
|
307
309
|
: undefined;
|
|
308
310
|
// Hand-built context, not routed through the dispatcher — this needs both
|
|
309
|
-
// db
|
|
311
|
+
// db (with its outsideTransaction) wired directly.
|
|
310
312
|
// @cast-boundary engine-payload — generic query-handler return for typed convention
|
|
311
313
|
return (await handler.handler(
|
|
312
314
|
{ type: tenantUserIdsQuery, payload: { tenantId }, user: systemUser },
|
|
313
315
|
{
|
|
314
|
-
db: tenantDb,
|
|
315
|
-
dbOutsideTransaction: tenantDb,
|
|
316
|
+
db: attachOutsideTransaction(tenantDb, tenantDb),
|
|
316
317
|
systemDb: systemDbSourceTenantDb
|
|
317
318
|
? createSystemDbView(systemDbSourceTenantDb, undefined, report)
|
|
318
319
|
: undefined,
|
|
@@ -62,6 +62,7 @@ export function createDeliveryFeature(options?: DeliveryFeatureOptions): Feature
|
|
|
62
62
|
const resolvedAccess = options?.access ?? access.admin;
|
|
63
63
|
const attemptLogRetentionDays = resolveAttemptLogRetentionDays(options?.attemptLogRetentionDays);
|
|
64
64
|
return defineFeature("delivery", (r) => {
|
|
65
|
+
r.adoptsBundledLists();
|
|
65
66
|
r.describe(
|
|
66
67
|
"The notification dispatch core: call `ctx.notify(notificationType, { to, route, data, priority, idempotencyKey })` from any handler to fan out a notification across all registered channels (email, in-app, push). Inside a write handler the delivery runs after the write has committed (the call returns `{ kind: \"deferred\" }`, a rolled-back write sends nothing); `immediate: true` delivers synchronously and returns `{ kind: \"dispatched\", deliveries }`. It stores per-user channel preferences in the `notification-preference` entity, opt-outs for no-account recipient addresses in `notification-address-opt-out` (keyed by a blind-index hash, never the plaintext address), logs every attempt to `store_delivery_attempts`, and enforces idempotency and rate-limiting \u2014 add `channel-email`, `channel-in-app`, or `channel-push` on top to actually send anything. Unsubscribe/resubscribe links are served by `createUnsubscribeRoutes({ secret })` mounted via the app's `extraRoutes` at `/api/delivery/unsubscribe` and `/api/delivery/resubscribe`: `GET /unsubscribe?token=` renders a confirmation page (no write), `POST /unsubscribe` performs the opt-out (RFC 8058 one-click, token from the form body or query), `POST /resubscribe` undoes it (JSON `{token}` or form body only, never the query — reachable by a mail-client link prefetcher) — sign links with `signUnsubscribeToken` / `signAddressUnsubscribeToken` using the same secret. `channel-email` sets `List-Unsubscribe` / `List-Unsubscribe-Post` automatically when a message's `data.unsubscribeUrl` points at the unsubscribe route. A notification declared with `unsubscribe: true` gets that link from delivery itself when the service has `unsubscribeLinks` (the `runProdApp` and `runDevApp` option `unsubscribe: { secret }` mounts the routes and wires it; base URL from `auth.publicOrigin`, `auth.mail.baseUrl` or `APP_BASE_URL`): user recipients get a user token, `route` recipients an address token (needs a blind-index key, otherwise no link); a `data.unsubscribeUrl` from the caller wins. `unsubscribe.tenantOrigin: (ctx) => origin | null` points the link at the tenant's own host (subdomain or custom domain, must serve this app's `/api`); null, a throw or a non-https value fall back to the app origin. The token carries the mail's locale, so the confirmation and result pages render in the mail's language. An app serving its own (e.g. legacy) unsubscribe links renders the same result page with `renderUnsubscribeResultPage({ kind, locale })`. To react to a no-account opt-out (e.g. remove the app's own subscriber row), declare `r.job(name, { trigger: { on: DeliveryHandlers.unsubscribeAddress } }, handler)` on a lane the app consumes: it runs after each committed opt-out, also an idempotent repeat, with `unsubscribeAddressPayloadSchema` (`addressHash`, `notificationType`, `channel`) as payload and the tenant in `ctx.triggeredBy.tenantId`; match the subscriber via `hashUnsubscribeAddress(email)`. Declare `unsubscribeUrl` as an optional template variable if the mail body shows it.",
|
|
67
68
|
);
|
|
@@ -205,10 +206,10 @@ export function createDeliveryFeature(options?: DeliveryFeatureOptions): Feature
|
|
|
205
206
|
const pruned = await runAttemptLogRetention(db, {
|
|
206
207
|
olderThanDays: attemptLogRetentionDays,
|
|
207
208
|
});
|
|
208
|
-
ctx.log.info("delivery attempt log pruned", pruned);
|
|
209
|
+
ctx.obs.log.info("delivery attempt log pruned", pruned);
|
|
209
210
|
} catch (error) {
|
|
210
211
|
if (!(error instanceof ConsumerLagError)) throw error;
|
|
211
|
-
ctx.log.warn("delivery attempt log retention postponed: an event consumer lags", {
|
|
212
|
+
ctx.obs.log.warn("delivery attempt log retention postponed: an event consumer lags", {
|
|
212
213
|
consumer: error.laggingConsumer,
|
|
213
214
|
});
|
|
214
215
|
}
|
|
@@ -73,6 +73,11 @@ export {
|
|
|
73
73
|
} from "./handlers/unsubscribe-address.write.ts";
|
|
74
74
|
export { currentNotificationType } from "./notification-type-aliases.ts";
|
|
75
75
|
export { createDeliveryNotifyFactory } from "./notify-factory.ts";
|
|
76
|
+
export {
|
|
77
|
+
buildSystemChannelContext,
|
|
78
|
+
type SystemChannelContextDeps,
|
|
79
|
+
type SystemChannelContextOptions,
|
|
80
|
+
} from "./system-channel-context.ts";
|
|
76
81
|
export {
|
|
77
82
|
deliveryAttemptsTable,
|
|
78
83
|
notificationAddressOptOutEntity,
|
|
@@ -150,21 +150,21 @@ function entryFor(
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
// Assumes ctx.redis is the handle the service claimed on; runProdApp wires the same
|
|
153
|
+
// Assumes ctx.services.redis is the handle the service claimed on; runProdApp wires the same
|
|
154
154
|
// instance into both.
|
|
155
155
|
async function releaseIdempotencyKey(ctx: JobContext, p: RenderJobPayload): Promise<void> {
|
|
156
156
|
// skip: no idempotency key was claimed for this notification
|
|
157
157
|
if (!p.idempotencyKey) return;
|
|
158
|
-
if (!ctx.redis) {
|
|
159
|
-
ctx.log.warn("delivery: idempotency key not released, job context has no redis", {
|
|
158
|
+
if (!ctx.services.redis) {
|
|
159
|
+
ctx.obs.log.warn("delivery: idempotency key not released, job context has no redis", {
|
|
160
160
|
notificationType: p.notificationType,
|
|
161
161
|
});
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
164
|
try {
|
|
165
|
-
await ctx.redis.del(deliveryIdempotencyRedisKey(p.tenantId, p.idempotencyKey));
|
|
165
|
+
await ctx.services.redis.del(deliveryIdempotencyRedisKey(p.tenantId, p.idempotencyKey));
|
|
166
166
|
} catch (err) {
|
|
167
|
-
ctx.log.warn(`delivery: idempotency key release failed: ${redactedMessageOf(err)}`, {
|
|
167
|
+
ctx.obs.log.warn(`delivery: idempotency key release failed: ${redactedMessageOf(err)}`, {
|
|
168
168
|
notificationType: p.notificationType,
|
|
169
169
|
});
|
|
170
170
|
}
|
|
@@ -206,7 +206,7 @@ async function addressForAttemptLog(
|
|
|
206
206
|
return await openRecipientAddress(
|
|
207
207
|
p,
|
|
208
208
|
p.tenantId as TenantId, // @cast-boundary engine-payload — job payload string is the stream tenant
|
|
209
|
-
ctx.configEncryption,
|
|
209
|
+
ctx.services.configEncryption,
|
|
210
210
|
);
|
|
211
211
|
} catch {
|
|
212
212
|
return null;
|
|
@@ -225,7 +225,7 @@ async function failAttempt(
|
|
|
225
225
|
err: unknown,
|
|
226
226
|
): Promise<never> {
|
|
227
227
|
const message = redactedMessageOf(err);
|
|
228
|
-
ctx.log.error(`delivery.${p.channelName} ${code}: ${message}`, {
|
|
228
|
+
ctx.obs.log.error(`delivery.${p.channelName} ${code}: ${message}`, {
|
|
229
229
|
notificationType: p.notificationType,
|
|
230
230
|
channel: p.channelName,
|
|
231
231
|
});
|
|
@@ -255,7 +255,7 @@ async function renderStep(p: RenderJobPayload, ctx: JobContext): Promise<void> {
|
|
|
255
255
|
registry,
|
|
256
256
|
undefined,
|
|
257
257
|
tenantId,
|
|
258
|
-
ctx.secrets,
|
|
258
|
+
ctx.services.secrets,
|
|
259
259
|
ctx.config,
|
|
260
260
|
(fileTenantId) => fileContextForTenant(ctx, ctx.systemUser.tenantId, fileTenantId),
|
|
261
261
|
);
|
|
@@ -270,7 +270,7 @@ async function renderStep(p: RenderJobPayload, ctx: JobContext): Promise<void> {
|
|
|
270
270
|
return failAttempt(ctx, db, registry, p, "render_failed", err);
|
|
271
271
|
}
|
|
272
272
|
try {
|
|
273
|
-
const jobRunner = ctx
|
|
273
|
+
const jobRunner = ctx.services.jobRunner as JobRunner; // @cast-boundary engine-bridge — dispatch lives on the concrete runner
|
|
274
274
|
await jobRunner.dispatch(
|
|
275
275
|
DeliveryJobs.send,
|
|
276
276
|
{ ...p, rendered: toSendRendered(rendered) },
|
|
@@ -298,13 +298,13 @@ async function sendStep(p: z.infer<typeof sendJobPayloadSchema>, ctx: JobContext
|
|
|
298
298
|
registry,
|
|
299
299
|
undefined,
|
|
300
300
|
tenantId,
|
|
301
|
-
ctx.secrets,
|
|
301
|
+
ctx.services.secrets,
|
|
302
302
|
ctx.config,
|
|
303
303
|
(fileTenantId) => fileContextForTenant(ctx, ctx.systemUser.tenantId, fileTenantId),
|
|
304
304
|
);
|
|
305
305
|
|
|
306
306
|
try {
|
|
307
|
-
const address = await openRecipientAddress(p, tenantId, ctx.configEncryption);
|
|
307
|
+
const address = await openRecipientAddress(p, tenantId, ctx.services.configEncryption);
|
|
308
308
|
const result = await channel.send(address, toMessage(p), channelCtx, p.rendered);
|
|
309
309
|
await appendAttemptEvent(
|
|
310
310
|
db,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import type { ConfigAccessor, Registry } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { SYSTEM_TENANT_ID } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
|
+
import type { FileContext } from "@cosmicdrift/kumiko-framework/files";
|
|
5
|
+
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
6
|
+
import { buildChannelContext } from "./channel-context.ts";
|
|
7
|
+
import type { ChannelContext } from "./types.ts";
|
|
8
|
+
|
|
9
|
+
export type SystemChannelContextDeps = {
|
|
10
|
+
readonly db: DbConnection;
|
|
11
|
+
readonly registry: Registry;
|
|
12
|
+
readonly filesForTenant: (tenantId: string) => FileContext | undefined;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type SystemChannelContextOptions = {
|
|
16
|
+
readonly config?: ConfigAccessor;
|
|
17
|
+
readonly secrets?: SecretsContext;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function buildSystemChannelContext(
|
|
21
|
+
deps: SystemChannelContextDeps,
|
|
22
|
+
options: SystemChannelContextOptions = {},
|
|
23
|
+
): ChannelContext {
|
|
24
|
+
return buildChannelContext(
|
|
25
|
+
deps.db,
|
|
26
|
+
deps.registry,
|
|
27
|
+
undefined,
|
|
28
|
+
SYSTEM_TENANT_ID,
|
|
29
|
+
options.secrets,
|
|
30
|
+
options.config,
|
|
31
|
+
deps.filesForTenant,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -25,7 +25,7 @@ export type CreateDeliveryTestContextOptions = {
|
|
|
25
25
|
readonly log?: Logger;
|
|
26
26
|
readonly jobRunner?: JobRunner;
|
|
27
27
|
readonly secrets?: SecretsContext;
|
|
28
|
-
// Seals queued job payload addresses and, like the boot wiring, doubles as the jobs' ctx.configEncryption.
|
|
28
|
+
// Seals queued job payload addresses and, like the boot wiring, doubles as the jobs' ctx.services.configEncryption.
|
|
29
29
|
readonly addressCipher?: EnvelopeCipher;
|
|
30
30
|
readonly idempotencyRedis?: Redis;
|
|
31
31
|
readonly filesForTenant?: (tenantId: string) => FileContext | undefined;
|
package/packages/bundled-features/src/document-ingest-foundation/append-missing-extract-event.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import type { Registry, SessionUser } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
|
+
import {
|
|
5
|
+
appendEventInTenantDb,
|
|
6
|
+
getStreamVersionInTenantDb,
|
|
7
|
+
} from "@cosmicdrift/kumiko-framework/event-store";
|
|
8
|
+
import {
|
|
9
|
+
DOCUMENT_EXTRACT_AGGREGATE_TYPE,
|
|
10
|
+
documentExtractFailedPayloadSchema,
|
|
11
|
+
documentExtractReadyPayloadSchema,
|
|
12
|
+
} from "./events.ts";
|
|
13
|
+
|
|
14
|
+
export type ExistingDocumentExtract = {
|
|
15
|
+
readonly id: string;
|
|
16
|
+
readonly meta: { readonly error?: unknown } | null;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type AppendMissingExtractEventDeps = {
|
|
20
|
+
readonly tenantDb: TenantDb;
|
|
21
|
+
readonly registry: Registry;
|
|
22
|
+
readonly actor: SessionUser;
|
|
23
|
+
readonly tenantId: string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type AppendMissingExtractEventOptions = {
|
|
27
|
+
readonly readyEventQn: string;
|
|
28
|
+
readonly failedEventQn: string;
|
|
29
|
+
readonly mapError?: (error: string) => string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type AppendMissingExtractEventResult =
|
|
33
|
+
| "appended-ready"
|
|
34
|
+
| "appended-failed"
|
|
35
|
+
| "already-terminal";
|
|
36
|
+
|
|
37
|
+
// Version 1 of the stream is the extract's created event; anything beyond it is the terminal event.
|
|
38
|
+
const CREATED_ONLY_STREAM_VERSION = 1;
|
|
39
|
+
const UNKNOWN_EXTRACT_ERROR = "unknown extract error";
|
|
40
|
+
|
|
41
|
+
// A previous delivery created the extract but died before appending its ready/failed event.
|
|
42
|
+
// Without the event, consumers waiting on it hang forever.
|
|
43
|
+
export async function appendMissingDocumentExtractEvent(
|
|
44
|
+
deps: AppendMissingExtractEventDeps,
|
|
45
|
+
existing: ExistingDocumentExtract,
|
|
46
|
+
fileRefId: string,
|
|
47
|
+
options: AppendMissingExtractEventOptions,
|
|
48
|
+
): Promise<AppendMissingExtractEventResult> {
|
|
49
|
+
if (deps.tenantDb.tenantId !== deps.tenantId) {
|
|
50
|
+
throw new InternalError({
|
|
51
|
+
message: `document-ingest-foundation: tenantDb is bound to tenant ${deps.tenantDb.tenantId}, not ${deps.tenantId}`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const version = await getStreamVersionInTenantDb(deps.tenantDb, existing.id);
|
|
55
|
+
// A missing stream means a wrong id; appending would make the terminal event the first event of a new stream.
|
|
56
|
+
if (version === 0) {
|
|
57
|
+
throw new InternalError({
|
|
58
|
+
message: `document-ingest-foundation: no document extract stream for id ${existing.id}`,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// skip: the terminal event was already written, nothing to catch up
|
|
62
|
+
if (version > CREATED_ONLY_STREAM_VERSION) return "already-terminal";
|
|
63
|
+
const metaError = existing.meta?.error;
|
|
64
|
+
const failed = typeof metaError === "string";
|
|
65
|
+
await appendEventInTenantDb(
|
|
66
|
+
deps.tenantDb,
|
|
67
|
+
{
|
|
68
|
+
aggregateId: existing.id,
|
|
69
|
+
aggregateType: DOCUMENT_EXTRACT_AGGREGATE_TYPE,
|
|
70
|
+
expectedVersion: version,
|
|
71
|
+
type: failed ? options.failedEventQn : options.readyEventQn,
|
|
72
|
+
payload: failed
|
|
73
|
+
? documentExtractFailedPayloadSchema.parse({
|
|
74
|
+
documentExtractId: existing.id,
|
|
75
|
+
fileRefId,
|
|
76
|
+
error: options.mapError?.(metaError) || metaError || UNKNOWN_EXTRACT_ERROR,
|
|
77
|
+
})
|
|
78
|
+
: documentExtractReadyPayloadSchema.parse({ documentExtractId: existing.id, fileRefId }),
|
|
79
|
+
metadata: { userId: deps.actor.id },
|
|
80
|
+
},
|
|
81
|
+
{ registry: deps.registry },
|
|
82
|
+
);
|
|
83
|
+
return failed ? "appended-failed" : "appended-ready";
|
|
84
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.389.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "appendMissingDocumentExtractEvent and shared ready/failed payload schemas",
|
|
6
|
+
"detail": "A provider whose job died after creating the extract calls appendMissingDocumentExtractEvent(deps, existing, fileRefId, { readyEventQn, failedEventQn, mapError }) to append the missing terminal event; it returns appended-ready, appended-failed or already-terminal. documentExtractReadyPayloadSchema and documentExtractFailedPayloadSchema are exported so providers register their own event names with the shared payload shape."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.379.0",
|
|
4
10
|
"type": "fix",
|
|
@@ -14,7 +14,7 @@ export const DOCUMENT_EXTRACT_AGGREGATE_TYPE = "document-extract";
|
|
|
14
14
|
|
|
15
15
|
export const DOCUMENT_INGEST_REQUESTED_EVENT_SHORT = "documentIngest.requested" as const;
|
|
16
16
|
|
|
17
|
-
// Qualified name — what ctx.
|
|
17
|
+
// Qualified name — what ctx.events.unsafeAppend's `type` actually needs
|
|
18
18
|
// (r.defineEvent registers under qn(toKebab(feature), "event",
|
|
19
19
|
// toKebab(shortName)); appendEvent looks up the registry by QN, not the
|
|
20
20
|
// short name — see MSP-apply "event not registered" error otherwise).
|
|
@@ -47,3 +47,18 @@ export const documentIngestSkippedPayloadSchema = z.object({
|
|
|
47
47
|
reason: z.enum(["file-too-large", "unsupported-mime-type"]),
|
|
48
48
|
});
|
|
49
49
|
export type DocumentIngestSkippedPayload = z.infer<typeof documentIngestSkippedPayloadSchema>;
|
|
50
|
+
|
|
51
|
+
// Terminal events of a document-extract stream. The QNs belong to the provider that registers
|
|
52
|
+
// them; the foundation only owns the shared payload shape.
|
|
53
|
+
export const documentExtractReadyPayloadSchema = z.object({
|
|
54
|
+
documentExtractId: z.string().min(1),
|
|
55
|
+
fileRefId: z.string().min(1),
|
|
56
|
+
});
|
|
57
|
+
export type DocumentExtractReadyPayload = z.infer<typeof documentExtractReadyPayloadSchema>;
|
|
58
|
+
|
|
59
|
+
export const documentExtractFailedPayloadSchema = z.object({
|
|
60
|
+
documentExtractId: z.string().min(1),
|
|
61
|
+
fileRefId: z.string().min(1),
|
|
62
|
+
error: z.string().min(1),
|
|
63
|
+
});
|
|
64
|
+
export type DocumentExtractFailedPayload = z.infer<typeof documentExtractFailedPayloadSchema>;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// Public API of the document-ingest-foundation bundled-feature.
|
|
2
2
|
|
|
3
3
|
export { documentIngestAggregateId } from "./aggregate-id.ts";
|
|
4
|
+
export {
|
|
5
|
+
type AppendMissingExtractEventDeps,
|
|
6
|
+
type AppendMissingExtractEventOptions,
|
|
7
|
+
type AppendMissingExtractEventResult,
|
|
8
|
+
appendMissingDocumentExtractEvent,
|
|
9
|
+
type ExistingDocumentExtract,
|
|
10
|
+
} from "./append-missing-extract-event.ts";
|
|
4
11
|
export {
|
|
5
12
|
type DocumentExtractMeta,
|
|
6
13
|
documentExtractEntity,
|
|
@@ -14,8 +21,12 @@ export {
|
|
|
14
21
|
DOCUMENT_INGEST_REQUESTED_EVENT_SHORT,
|
|
15
22
|
DOCUMENT_INGEST_SKIPPED_EVENT_QN,
|
|
16
23
|
DOCUMENT_INGEST_SKIPPED_EVENT_SHORT,
|
|
24
|
+
type DocumentExtractFailedPayload,
|
|
25
|
+
type DocumentExtractReadyPayload,
|
|
17
26
|
type DocumentIngestRequestedPayload,
|
|
18
27
|
type DocumentIngestSkippedPayload,
|
|
28
|
+
documentExtractFailedPayloadSchema,
|
|
29
|
+
documentExtractReadyPayloadSchema,
|
|
19
30
|
documentIngestRequestedPayloadSchema,
|
|
20
31
|
documentIngestSkippedPayloadSchema,
|
|
21
32
|
} from "./events.ts";
|
|
@@ -44,6 +44,7 @@ export function createFeatureTogglesFeature(
|
|
|
44
44
|
options: FeatureTogglesOptions = {},
|
|
45
45
|
): FeatureDefinition {
|
|
46
46
|
return defineFeature("feature-toggles", (r) => {
|
|
47
|
+
r.adoptsBundledLists();
|
|
47
48
|
r.describe(
|
|
48
49
|
'Persists per-feature enabled/disabled state in the `store_global_feature_state` table and exposes a `set` write-handler plus `list`/`registered` query-handlers so operators can flip features at runtime without redeploying. Each API instance keeps an in-memory `GlobalFeatureToggleRuntime` snapshot (initialize it via `createFeatureToggleRuntime`, pass a `() => runtime` accessor to `createFeatureTogglesFeature`) that the dispatcher gate reads on every request; a `toggle-cache-sync` multi-stream projection with `delivery: "shared"` syncs the snapshot across instances whenever a `toggle-set` event is appended.',
|
|
49
50
|
);
|
|
@@ -43,7 +43,7 @@ export const registeredQuery = defineQueryHandler({
|
|
|
43
43
|
// The per-tenant signature requires SYSTEM_TENANT_ID; the app resolver
|
|
44
44
|
// returns the union of all tier features. The sentinel convention is
|
|
45
45
|
// documented in DispatcherOptions.effectiveFeatures.
|
|
46
|
-
const effective = ctx.effectiveFeatures?.(SYSTEM_TENANT_ID);
|
|
46
|
+
const effective = ctx.services.effectiveFeatures?.(SYSTEM_TENANT_ID);
|
|
47
47
|
|
|
48
48
|
const rows: Array<{
|
|
49
49
|
id: string;
|
|
@@ -160,7 +160,7 @@ export function createSetWriteHandler(getRuntime: (() => GlobalFeatureToggleRunt
|
|
|
160
160
|
// its config-changed events.
|
|
161
161
|
// unsafeAppendEvent: the strict appendEvent is bound to an app's own event map, which
|
|
162
162
|
// bundled-features cannot see. Schema validation still runs via r.defineEvent("toggle-set", ...).
|
|
163
|
-
await ctx.
|
|
163
|
+
await ctx.events.unsafeAppend({
|
|
164
164
|
aggregateId: SYSTEM_TENANT_ID,
|
|
165
165
|
aggregateType: FEATURE_TOGGLE_AGGREGATE_TYPE,
|
|
166
166
|
type: FEATURE_TOGGLE_SET_EVENT_NAME,
|
|
@@ -52,7 +52,7 @@ export function createRedisToggleSyncSignal(
|
|
|
52
52
|
onMessage(listener) {
|
|
53
53
|
signal.onMessage((_channel, payload) => {
|
|
54
54
|
if (!isTogglePayload(payload)) {
|
|
55
|
-
// biome-ignore lint/suspicious/noConsole: ops-visible fallback — no ctx.log in a raw PubSubSignal handler.
|
|
55
|
+
// biome-ignore lint/suspicious/noConsole: ops-visible fallback — no ctx.obs.log in a raw PubSubSignal handler.
|
|
56
56
|
console.error(
|
|
57
57
|
`[kumiko:feature-toggles] dropping malformed cache-sync message on "${toggleSyncChannel}"`,
|
|
58
58
|
);
|
|
@@ -53,7 +53,7 @@ export type FileDerivativesOptions = {
|
|
|
53
53
|
* Without this option, neither the httpRoute NOR the `publicVariant`
|
|
54
54
|
* query is registered — it is unreachable via the httpRoute path and via
|
|
55
55
|
* the generic `/api` query dispatch. Existing consumers that only need
|
|
56
|
-
* `ctx.derivatives` are unaffected. See {@link PublicVariantResolveApexTenant}
|
|
56
|
+
* `ctx.services.derivatives` are unaffected. See {@link PublicVariantResolveApexTenant}
|
|
57
57
|
* for the in-memory/sync contract. */
|
|
58
58
|
readonly resolveApexTenant?: PublicVariantResolveApexTenant;
|
|
59
59
|
/** Base path of the public variant route. Default "/media". */
|
|
@@ -104,7 +104,7 @@ export function createFileDerivativesFeature(opts: FileDerivativesOptions = {}):
|
|
|
104
104
|
FEATURE_NAME,
|
|
105
105
|
(r) => {
|
|
106
106
|
r.describe(
|
|
107
|
-
"Declares the `derivativeRenderer` extension point. `ctx.derivatives.variant(fileRefId, spec, name)` derives a variant of a tracked FileRef the first time it's requested and reuses the stored result afterwards (derive-on-first-use, keyed by a hash of the spec). Mount at least one `derivatives-*` renderer feature alongside this one — without a registered renderer for the FileRef's MIME type, every `variant(...)` call throws. Also declares the `derivativePublicPredicate` extension point (`r.useExtension(EXT_DERIVATIVE_PUBLIC_PREDICATE, '<entityType>', { isPublic })`) and, when `createFileDerivativesFeature({resolveApexTenant})` is passed a host-resolver, mounts an anonymous `GET {basePath}/:fileRefId/:variant` route that serves any variant name the FileRef's field declared in its `variants` for a FileRef whose entityType has a registered predicate returning true — default-deny (404) otherwise, same as an unknown FileRef or an undeclared variant name. The route's only rate-limit (`per: \"ip\"`) depends on the server's `trustedProxyHops` matching the deployment's actual reverse-proxy count — at the default 0 it ignores `x-forwarded-for` entirely, so every caller shares one bucket until that's configured. `publicTenantResolution: \"fileRef\"` keeps resolveApexTenant as the host gate but reads the variant from the FileRef row's own tenant instead of the host's, so one shared platform host serves every tenant's public variants — each FileRef-tenant's own `isPublic` predicate still default-denies. Also declares the `derivativeOverlayResolver` extension point (`r.useExtension(EXT_DERIVATIVE_OVERLAY_RESOLVER, '<entityType>', { resolve })`), used to turn a variant's `overlays[].dataToken` into the actual QR payload for that FileRef's entityType before the variant is rendered — a variant declaring a `qr` overlay throws at request-time if no resolver is registered for the FileRef's entityType.",
|
|
107
|
+
"Declares the `derivativeRenderer` extension point. `ctx.services.derivatives.variant(fileRefId, spec, name)` derives a variant of a tracked FileRef the first time it's requested and reuses the stored result afterwards (derive-on-first-use, keyed by a hash of the spec). Mount at least one `derivatives-*` renderer feature alongside this one — without a registered renderer for the FileRef's MIME type, every `variant(...)` call throws. Also declares the `derivativePublicPredicate` extension point (`r.useExtension(EXT_DERIVATIVE_PUBLIC_PREDICATE, '<entityType>', { isPublic })`) and, when `createFileDerivativesFeature({resolveApexTenant})` is passed a host-resolver, mounts an anonymous `GET {basePath}/:fileRefId/:variant` route that serves any variant name the FileRef's field declared in its `variants` for a FileRef whose entityType has a registered predicate returning true — default-deny (404) otherwise, same as an unknown FileRef or an undeclared variant name. The route's only rate-limit (`per: \"ip\"`) depends on the server's `trustedProxyHops` matching the deployment's actual reverse-proxy count — at the default 0 it ignores `x-forwarded-for` entirely, so every caller shares one bucket until that's configured. `publicTenantResolution: \"fileRef\"` keeps resolveApexTenant as the host gate but reads the variant from the FileRef row's own tenant instead of the host's, so one shared platform host serves every tenant's public variants — each FileRef-tenant's own `isPublic` predicate still default-denies. Also declares the `derivativeOverlayResolver` extension point (`r.useExtension(EXT_DERIVATIVE_OVERLAY_RESOLVER, '<entityType>', { resolve })`), used to turn a variant's `overlays[].dataToken` into the actual QR payload for that FileRef's entityType before the variant is rendered — a variant declaring a `qr` overlay throws at request-time if no resolver is registered for the FileRef's entityType.",
|
|
108
108
|
);
|
|
109
109
|
r.uiHints({
|
|
110
110
|
displayLabel: "File Derivatives",
|
package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts
CHANGED
|
@@ -52,6 +52,10 @@ export const publicVariantByFileRefQuery = defineQueryHandler({
|
|
|
52
52
|
const tenant = await fetchOne<TenantGateRow>(db, tenantTable, { id: row.tenantId });
|
|
53
53
|
if (!tenant || !isTenantServingPublicContent(tenant)) return null;
|
|
54
54
|
|
|
55
|
-
return ctx.queryAs(
|
|
55
|
+
return ctx.dispatch.queryAs(
|
|
56
|
+
createAnonymousUser(row.tenantId),
|
|
57
|
+
PUBLIC_VARIANT_QN,
|
|
58
|
+
query.payload,
|
|
59
|
+
);
|
|
56
60
|
},
|
|
57
61
|
});
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
// consumer's `resolverTrust`/anonymousAccess setup, not the host.
|
|
19
19
|
//
|
|
20
20
|
// In `publicTenantResolution: "fileRef"` mode, this handler is also the
|
|
21
|
-
// `ctx.queryAs(createAnonymousUser(fileRefTenantId), ...)` target of
|
|
21
|
+
// `ctx.dispatch.queryAs(createAnonymousUser(fileRefTenantId), ...)` target of
|
|
22
22
|
// publicVariantByFileRefQuery — tenantId there comes from the FileRef row,
|
|
23
23
|
// but this handler's own isPublic gate runs exactly the same either way.
|
|
24
24
|
|
|
@@ -133,14 +133,16 @@ export const publicVariantQuery = defineQueryHandler({
|
|
|
133
133
|
);
|
|
134
134
|
if (!isPublic) return null;
|
|
135
135
|
|
|
136
|
-
// ctx.files/ctx.derivatives are optional on HandlerContext (only wired
|
|
136
|
+
// ctx.services.files/ctx.services.derivatives are optional on HandlerContext (only wired
|
|
137
137
|
// when file-foundation resolved a provider) — narrow via a plain guard,
|
|
138
138
|
// never `!`/`as`, and surface a real config error (not a silent deny)
|
|
139
139
|
// when the feature isn't actually wired.
|
|
140
|
-
const {
|
|
140
|
+
const {
|
|
141
|
+
services: { files, derivatives },
|
|
142
|
+
} = ctx;
|
|
141
143
|
if (!files || !derivatives) {
|
|
142
144
|
throw new Error(
|
|
143
|
-
"file-derivatives requires ctx.files/ctx.derivatives — is file-foundation mounted?",
|
|
145
|
+
"file-derivatives requires ctx.services.files/ctx.services.derivatives — is file-foundation mounted?",
|
|
144
146
|
);
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -151,7 +153,7 @@ export const publicVariantQuery = defineQueryHandler({
|
|
|
151
153
|
|
|
152
154
|
// ponytail: fully buffered (no streaming), +33% over the wire from
|
|
153
155
|
// base64 — HttpRouteHandlerDeps only exposes {app, systemQuery} (no
|
|
154
|
-
// direct ctx.files access from the Hono handler itself), and the local
|
|
156
|
+
// direct ctx.services.files access from the Hono handler itself), and the local
|
|
155
157
|
// dev storage provider has no getSignedUrl (a redirect design wouldn't
|
|
156
158
|
// work in dev). Upgrade path: a binary-passthrough dispatcher primitive
|
|
157
159
|
// would be its own, larger issue.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// extension point + the per-tenant `provider` config key. Provider RESOLUTION
|
|
5
5
|
// (createFileProviderForTenant) + the plugin types now live in the framework
|
|
6
6
|
// (packages/framework/src/files/provider-resolver.ts) so the upload routes,
|
|
7
|
-
// `ctx.files` and the GDPR jobs resolve through ONE path — uploads, export and
|
|
7
|
+
// `ctx.services.files` and the GDPR jobs resolve through ONE path — uploads, export and
|
|
8
8
|
// erasure hit the same store by construction. This feature re-exports the moved
|
|
9
9
|
// symbols unchanged so existing imports keep working.
|
|
10
10
|
//
|
|
@@ -22,19 +22,21 @@ type FileRefRow = {
|
|
|
22
22
|
readonly size: number | bigint | string;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
// Tenant-bound on purpose: without a resolver `ctx.files` only reaches the storage of the
|
|
25
|
+
// Tenant-bound on purpose: without a resolver `ctx.services.files` only reaches the storage of the
|
|
26
26
|
// context's own tenant (`contextTenantId`), so a request for another tenant's files is
|
|
27
27
|
// undefined instead of silently reading through the wrong tenant's storage.
|
|
28
28
|
export function fileContextForTenant(
|
|
29
29
|
ctx: {
|
|
30
|
-
readonly
|
|
31
|
-
|
|
30
|
+
readonly services: {
|
|
31
|
+
readonly _fileProviderResolver?: FileProviderResolver | undefined;
|
|
32
|
+
readonly files?: FileContext | undefined;
|
|
33
|
+
};
|
|
32
34
|
},
|
|
33
35
|
contextTenantId: string,
|
|
34
36
|
tenantId: string,
|
|
35
37
|
): FileContext | undefined {
|
|
36
|
-
const resolver = ctx._fileProviderResolver;
|
|
37
|
-
if (resolver === undefined) return tenantId === contextTenantId ? ctx.files : undefined;
|
|
38
|
+
const resolver = ctx.services._fileProviderResolver;
|
|
39
|
+
if (resolver === undefined) return tenantId === contextTenantId ? ctx.services.files : undefined;
|
|
38
40
|
return fileContextFromResolver(resolver, tenantId);
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -201,7 +201,10 @@ async function buildS3Provider(
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
async function readSecretAccessKey(ctx: FileProviderContext, tenantId: string): Promise<string> {
|
|
204
|
-
const secrets = requireSecretsContext(
|
|
204
|
+
const secrets = requireSecretsContext(
|
|
205
|
+
{ secrets: ctx.secrets, auditUserId: ctx.auditUserId },
|
|
206
|
+
FEATURE_NAME,
|
|
207
|
+
);
|
|
205
208
|
const branded = await secrets.get(tenantId, S3_SECRET_ACCESS_KEY);
|
|
206
209
|
return requireSecretSet(branded, FEATURE_NAME, S3_SECRET_ACCESS_KEY.name).reveal();
|
|
207
210
|
}
|
package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts
CHANGED
|
@@ -56,9 +56,9 @@ export async function sweepOrphanedDerivativesJob(
|
|
|
56
56
|
db: DbConnection,
|
|
57
57
|
): Promise<void> {
|
|
58
58
|
const payload = sweepOrphanedDerivativesPayloadSchema.parse(rawPayload ?? {});
|
|
59
|
-
const maybeResolver = ctx._fileProviderResolver;
|
|
59
|
+
const maybeResolver = ctx.services._fileProviderResolver;
|
|
60
60
|
if (!maybeResolver) {
|
|
61
|
-
ctx.log?.warn(
|
|
61
|
+
ctx.obs.log?.warn(
|
|
62
62
|
"[files-tenant-data:sweep] no _fileProviderResolver wired — nothing to sweep, skipping run",
|
|
63
63
|
);
|
|
64
64
|
return;
|
|
@@ -96,7 +96,7 @@ export async function sweepOrphanedDerivativesJob(
|
|
|
96
96
|
try {
|
|
97
97
|
provider = await resolver(tenantId);
|
|
98
98
|
} catch (err) {
|
|
99
|
-
ctx.log?.warn?.(
|
|
99
|
+
ctx.obs.log?.warn?.(
|
|
100
100
|
`[files-tenant-data:sweep] no file provider resolvable for tenant=${tenantId}, skipping: ${err instanceof Error ? err.message : String(err)}`,
|
|
101
101
|
);
|
|
102
102
|
continue;
|
|
@@ -105,7 +105,7 @@ export async function sweepOrphanedDerivativesJob(
|
|
|
105
105
|
try {
|
|
106
106
|
keys = await provider.list(`${tenantId}/`);
|
|
107
107
|
} catch (err) {
|
|
108
|
-
ctx.log?.warn?.(
|
|
108
|
+
ctx.obs.log?.warn?.(
|
|
109
109
|
`[files-tenant-data:sweep] provider.list() failed for tenant=${tenantId}, skipping: ${err instanceof Error ? err.message : String(err)}`,
|
|
110
110
|
);
|
|
111
111
|
continue;
|
|
@@ -140,7 +140,7 @@ export async function sweepOrphanedDerivativesJob(
|
|
|
140
140
|
{ limit: 1 },
|
|
141
141
|
);
|
|
142
142
|
if (owners.length > 0) return "skipped";
|
|
143
|
-
// Binaries written through ctx.files.ref(key) (and their derive()d
|
|
143
|
+
// Binaries written through ctx.services.files.ref(key) (and their derive()d
|
|
144
144
|
// variants) never get a fileRef row; an original still sitting in storage
|
|
145
145
|
// means the derivative is not an orphan.
|
|
146
146
|
if (await candidate.provider.exists(candidate.originalKey)) return "skipped";
|
|
@@ -158,15 +158,15 @@ export async function sweepOrphanedDerivativesJob(
|
|
|
158
158
|
migrateRow,
|
|
159
159
|
maxFailures,
|
|
160
160
|
deadlineAt: deadline,
|
|
161
|
-
signal: ctx.signal,
|
|
161
|
+
signal: ctx.lifecycle.signal,
|
|
162
162
|
onRowError: (candidate, err) => {
|
|
163
|
-
ctx.log?.warn?.(
|
|
163
|
+
ctx.obs.log?.warn?.(
|
|
164
164
|
`[files-tenant-data:sweep] failed to delete key=${candidate.key} tenant=${candidate.tenantId}: ${err instanceof Error ? err.message : String(err)}`,
|
|
165
165
|
);
|
|
166
166
|
},
|
|
167
167
|
});
|
|
168
168
|
|
|
169
|
-
ctx.log?.info?.(
|
|
169
|
+
ctx.obs.log?.info?.(
|
|
170
170
|
`[files-tenant-data:sweep] complete dryRun=${dryRun} deleted=${outcome.migrated} wouldDelete=${dryRunWouldDelete} skipped=${outcome.skipped} failed=${outcome.failed} batches=${outcome.batchesProcessed} stoppedReason=${outcome.stoppedReason}`,
|
|
171
171
|
);
|
|
172
172
|
}
|
|
@@ -27,7 +27,7 @@ function registerFormDraft(r: FeatureRegistrar<typeof FORM_DRAFT_FEATURE_NAME>):
|
|
|
27
27
|
});
|
|
28
28
|
|
|
29
29
|
// config is optional: the retention-days key only resolves when the
|
|
30
|
-
// config feature is mounted (ctx.configResolver present) — the cleanup
|
|
30
|
+
// config feature is mounted (ctx.services.configResolver present) — the cleanup
|
|
31
31
|
// job already falls back to FORM_DRAFT_DEFAULT_RETENTION_DAYS otherwise
|
|
32
32
|
// (see handlers/cleanup.job.ts). A hard r.requires({ features: ["config"] }) would break
|
|
33
33
|
// every app that mounts form-draft without config, which is a breaking
|