@cosmicdrift/kumiko-samples 0.389.0 → 0.390.1
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 +27 -15
- package/packages/bundled-features/src/audit/feature.ts +1 -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 +6 -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/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 +12 -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 +2 -2
- package/packages/bundled-features/src/auth-email-password/handlers/verify-email.write.ts +3 -2
- package/packages/bundled-features/src/auth-email-password/seeding.ts +14 -9
- package/packages/bundled-features/src/auth-foundation/feature.ts +13 -4
- 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 +9 -3
- 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 +1 -1
- package/packages/bundled-features/src/cap-counter/constants.ts +1 -1
- package/packages/bundled-features/src/cap-counter/feature.ts +2 -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/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/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 +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +8 -8
- 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/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/delivery-service.ts +6 -5
- package/packages/bundled-features/src/delivery/feature.ts +2 -2
- package/packages/bundled-features/src/delivery/jobs.ts +11 -11
- package/packages/bundled-features/src/delivery/testing.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +1 -1
- 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/inbound-mail-foundation/events.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +1 -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/changes.json +6 -0
- 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/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/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/lib/boot-check.ts +12 -8
- package/packages/bundled-features/src/mail-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/mail-transport-smtp/changes.json +6 -0
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +4 -1
- package/packages/bundled-features/src/managed-pages/seeding.ts +13 -8
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +1 -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/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 +1 -1
- package/packages/bundled-features/src/seo/feature.ts +1 -1
- package/packages/bundled-features/src/sessions/changes.json +12 -0
- package/packages/bundled-features/src/sessions/feature.ts +1 -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/screens.ts +8 -4
- 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 +12 -2
- package/packages/bundled-features/src/shared/require-for-tenant.ts +7 -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/feature.ts +1 -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/template-resolver/api.ts +3 -3
- package/packages/bundled-features/src/template-resolver/changes.json +18 -0
- 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/overview.query.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/send-test-shared.ts +1 -1
- 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 +12 -2
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +18 -13
- 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/placeholder-line-editor.tsx +19 -6
- 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/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/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/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 +7 -3
- 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/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 +24 -0
- package/packages/bundled-features/src/user-data-rights/feature.ts +29 -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 +2 -0
- 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 +10 -0
- package/packages/bundled-features/src/user-data-rights/web/index.ts +6 -1
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +8 -6
- 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 +9 -4
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +6 -1
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +7 -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/package.json +1 -1
- 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/server.ts +2 -2
- package/samples/apps/ui-walkthrough/package.json +1 -1
- package/samples/apps/use-all-bundled/feature-manifest.json +1 -1
- package/samples/apps/use-all-bundled/package.json +1 -1
- package/samples/apps/use-all-bundled/src/app/seed.ts +12 -2
- package/samples/apps/user-data-rights-demo/README.md +1 -1
- package/samples/apps/user-data-rights-demo/package.json +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/lane-routing/package.json +1 -1
- package/samples/recipes/legal-pages/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/README.md +2 -2
- package/samples/recipes/lifecycle-hooks/src/feature.ts +1 -1
- package/samples/recipes/managed-config/README.md +3 -3
- 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 +1 -1
- package/samples/recipes/state-machine/src/reducer.ts +1 -1
- package/samples/recipes/workflow-engine/src/resume-loop.ts +4 -4
- package/samples/recipes/workflow-engine/src/workflow-runner.ts +2 -2
|
@@ -257,7 +257,7 @@ async function appendDenialAuditEvent(
|
|
|
257
257
|
new InternalError({
|
|
258
258
|
message:
|
|
259
259
|
"[crypto-shredding] forget-subject denial audit event cannot be appended without " +
|
|
260
|
-
"ctx.
|
|
260
|
+
"ctx.db.outsideTransaction — dispatch wiring is missing the outside-tx db source.",
|
|
261
261
|
}),
|
|
262
262
|
);
|
|
263
263
|
}
|
|
@@ -340,11 +340,11 @@ async function sweepDerivedSubjectData(
|
|
|
340
340
|
|
|
341
341
|
// Derived search index still holds plaintext — purge next to the
|
|
342
342
|
// blind-index sweep. No adapter → no-op (apps without search).
|
|
343
|
-
if (ctx.searchAdapter) {
|
|
343
|
+
if (ctx.services.searchAdapter) {
|
|
344
344
|
await purgeSearchDocumentsForSubject(
|
|
345
345
|
crossTenantSubjectRunner,
|
|
346
346
|
ctx.registry.features,
|
|
347
|
-
ctx.searchAdapter,
|
|
347
|
+
ctx.services.searchAdapter,
|
|
348
348
|
subjectKey,
|
|
349
349
|
subject,
|
|
350
350
|
);
|
|
@@ -432,7 +432,7 @@ async function appendSubjectForgotten(
|
|
|
432
432
|
outsideRunner: DbRunner | undefined,
|
|
433
433
|
): Promise<void> {
|
|
434
434
|
if (!outsideRunner) {
|
|
435
|
-
await ctx.
|
|
435
|
+
await ctx.events.unsafeAppend({
|
|
436
436
|
aggregateId,
|
|
437
437
|
aggregateType: CRYPTO_SHREDDING_AGGREGATE_TYPE,
|
|
438
438
|
type: SUBJECT_FORGOTTEN_EVENT_NAME,
|
|
@@ -521,7 +521,7 @@ export const forgetSubjectWrite = defineWriteHandler({
|
|
|
521
521
|
subjectKey,
|
|
522
522
|
raw.kind,
|
|
523
523
|
denialReasonOf(tenantScopeDenial),
|
|
524
|
-
ctx.
|
|
524
|
+
ctx.db.outsideTransaction?.unsafeRaw(),
|
|
525
525
|
);
|
|
526
526
|
return auditFailure ?? tenantScopeDenial;
|
|
527
527
|
}
|
|
@@ -537,7 +537,7 @@ export const forgetSubjectWrite = defineWriteHandler({
|
|
|
537
537
|
subjectKey,
|
|
538
538
|
raw.kind,
|
|
539
539
|
TARGET_RECORD_RETENTION_BLOCK_DELETE,
|
|
540
|
-
ctx.
|
|
540
|
+
ctx.db.outsideTransaction?.unsafeRaw(),
|
|
541
541
|
);
|
|
542
542
|
return auditFailure ?? retentionDenial;
|
|
543
543
|
}
|
|
@@ -575,7 +575,7 @@ export const forgetSubjectWrite = defineWriteHandler({
|
|
|
575
575
|
await closeUserLoginDoor(
|
|
576
576
|
ctx,
|
|
577
577
|
raw.userId,
|
|
578
|
-
ctx.
|
|
578
|
+
ctx.db.outsideTransaction?.unsafeRaw(),
|
|
579
579
|
ctx.db.unsafeRaw(),
|
|
580
580
|
);
|
|
581
581
|
}
|
|
@@ -599,7 +599,7 @@ export const forgetSubjectWrite = defineWriteHandler({
|
|
|
599
599
|
forgottenBy: event.user.id,
|
|
600
600
|
...(cleanupError !== undefined && { cleanupError }),
|
|
601
601
|
},
|
|
602
|
-
ctx.
|
|
602
|
+
ctx.db.outsideTransaction?.unsafeRaw(),
|
|
603
603
|
);
|
|
604
604
|
|
|
605
605
|
if (cleanupError !== undefined) {
|
|
@@ -5,16 +5,16 @@ import type { DeleteFileBytes } from "../shared/index.ts";
|
|
|
5
5
|
|
|
6
6
|
const ERROR_CONTEXT = "crypto-shredding:forget-subject";
|
|
7
7
|
|
|
8
|
-
// The upload resolver, not ctx.files: ctx.files is bound to the acting user's tenant, while a
|
|
8
|
+
// The upload resolver, not ctx.services.files: ctx.services.files is bound to the acting user's tenant, while a
|
|
9
9
|
// subject's rows can live in any tenant. Resolved eagerly so a missing provider surfaces as
|
|
10
10
|
// missing_file_storage instead of a failed delete.
|
|
11
11
|
export function resolveFileBytesDeleter(
|
|
12
12
|
ctx: HandlerContext,
|
|
13
13
|
): (tenantId: TenantId) => Promise<DeleteFileBytes | undefined> {
|
|
14
14
|
return async (tenantId) => {
|
|
15
|
-
const resolver = ctx._fileProviderResolver;
|
|
15
|
+
const resolver = ctx.services._fileProviderResolver;
|
|
16
16
|
if (resolver === undefined) {
|
|
17
|
-
const own = tenantId === ctx.user.tenantId ? ctx.files : undefined;
|
|
17
|
+
const own = tenantId === ctx.user.tenantId ? ctx.services.files : undefined;
|
|
18
18
|
return own && ((key) => deleteFileRefBytes(own, { storageKey: key }, ERROR_CONTEXT));
|
|
19
19
|
}
|
|
20
20
|
try {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.390.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "wireCustomFieldsUserDataRightsFor is exported from ./custom-fields",
|
|
6
|
+
"detail": "The helper that adds custom fields to the user-data-rights export is documented as an app call but was missing from the subpath exports. It is exported again together with its options type."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.370.0",
|
|
4
10
|
"type": "breaking",
|
|
@@ -59,7 +59,7 @@ export const clearCustomFieldHandler: WriteHandlerDef = {
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
await ctx.
|
|
62
|
+
await ctx.events.unsafeAppend({
|
|
63
63
|
aggregateId: payload.entityId,
|
|
64
64
|
aggregateType: payload.entityName,
|
|
65
65
|
type: createCustomFieldsFeature().exports.clearedEvent.name,
|
|
@@ -34,7 +34,7 @@ export const deleteSystemFieldHandler: WriteHandlerDef = {
|
|
|
34
34
|
// Cascade-cleanup event — host-entity MSPs remove orphaned values from their
|
|
35
35
|
// customFields jsonb in the same transaction for atomicity.
|
|
36
36
|
if (result.isSuccess) {
|
|
37
|
-
await ctx.
|
|
37
|
+
await ctx.events.unsafeAppend({
|
|
38
38
|
aggregateId,
|
|
39
39
|
aggregateType: FIELD_DEFINITION_AGGREGATE_TYPE,
|
|
40
40
|
type: createCustomFieldsFeature().exports.fieldDefinitionDeletedEvent.name,
|
|
@@ -55,7 +55,7 @@ export function createDeleteTenantFieldHandler(
|
|
|
55
55
|
// registered via wireCustomFieldsFor consume it and remove orphaned values
|
|
56
56
|
// from their customFields jsonb. The same transaction makes cleanup atomic.
|
|
57
57
|
if (result.isSuccess) {
|
|
58
|
-
await ctx.
|
|
58
|
+
await ctx.events.unsafeAppend({
|
|
59
59
|
aggregateId,
|
|
60
60
|
aggregateType: FIELD_DEFINITION_AGGREGATE_TYPE,
|
|
61
61
|
type: createCustomFieldsFeature().exports.fieldDefinitionDeletedEvent.name,
|
|
@@ -97,7 +97,7 @@ export const setCustomFieldHandler: WriteHandlerDef = {
|
|
|
97
97
|
// cross-feature augmentation for this event type; the qualified string
|
|
98
98
|
// name is used directly. The value is ALWAYS included in the event
|
|
99
99
|
// (custom fields carry no PII), so replay fully reproduces the projection.
|
|
100
|
-
await ctx.
|
|
100
|
+
await ctx.events.unsafeAppend({
|
|
101
101
|
aggregateId: payload.entityId,
|
|
102
102
|
aggregateType: payload.entityName,
|
|
103
103
|
type: createCustomFieldsFeature().exports.setEvent.name,
|
|
@@ -37,3 +37,7 @@ export {
|
|
|
37
37
|
type SupportedFieldType,
|
|
38
38
|
} from "./web/constants.ts";
|
|
39
39
|
export { customFieldsField, wireCustomFieldsFor } from "./wire-for-entity.ts";
|
|
40
|
+
export {
|
|
41
|
+
type WireCustomFieldsUserDataRightsOptions,
|
|
42
|
+
wireCustomFieldsUserDataRightsFor,
|
|
43
|
+
} from "./wire-user-data-rights.ts";
|
|
@@ -101,7 +101,7 @@ export function createDataRetentionFeature(): FeatureDefinition {
|
|
|
101
101
|
tenantId,
|
|
102
102
|
preloadedTenantPreset: tenantPreset,
|
|
103
103
|
now: T.Now.instant(),
|
|
104
|
-
files: ctx.files,
|
|
104
|
+
files: ctx.services.files,
|
|
105
105
|
});
|
|
106
106
|
if (result.skipped.length > 0) {
|
|
107
107
|
// biome-ignore lint/suspicious/noConsole: operator-visibility for skipped entities
|
|
@@ -254,7 +254,12 @@ async function forgetRowAndEraseKeys(args: {
|
|
|
254
254
|
readonly registry: Registry;
|
|
255
255
|
}): Promise<{ readonly isSuccess: boolean }> {
|
|
256
256
|
return runInSubTransaction(args.db, async (sp) => {
|
|
257
|
-
const tdb = createTenantDb({
|
|
257
|
+
const tdb = createTenantDb({
|
|
258
|
+
db: sp,
|
|
259
|
+
tenantId: args.tenantId,
|
|
260
|
+
mode: "system",
|
|
261
|
+
reason: "retention sweep forgets rows of the tenant being swept, outside any request scope",
|
|
262
|
+
});
|
|
258
263
|
const result = await args.executor.forget({ id: args.rowId }, args.systemUser, tdb);
|
|
259
264
|
if (!result.isSuccess) return { isSuccess: false };
|
|
260
265
|
await eraseHardDeletedRowKeys({
|
|
@@ -508,7 +513,12 @@ export async function runRetentionCleanup(
|
|
|
508
513
|
// the daily cron converges (hardDelete removes rows, softDelete's isDeleted
|
|
509
514
|
// filter shrinks the set). A single batched forget/delete event is a follow-up.
|
|
510
515
|
const systemUser = createSystemUser(tenantId);
|
|
511
|
-
const tdb = createTenantDb({
|
|
516
|
+
const tdb = createTenantDb({
|
|
517
|
+
db,
|
|
518
|
+
tenantId,
|
|
519
|
+
mode: "system",
|
|
520
|
+
reason: "retention sweep forgets rows of the tenant being swept, outside any request scope",
|
|
521
|
+
});
|
|
512
522
|
const fileHolders = collectSingleFileHolders(registry);
|
|
513
523
|
|
|
514
524
|
// Pre-load every override row for this tenant ONCE — N entities × M
|
|
@@ -63,7 +63,7 @@ async function writeAttemptEvent(
|
|
|
63
63
|
): Promise<void> {
|
|
64
64
|
const { tenantId, ...rest } = entry;
|
|
65
65
|
// Masked here, the only place attempt events are written, so no event or projection row ever holds the full address.
|
|
66
|
-
// Schema-parse to match ctx.
|
|
66
|
+
// Schema-parse to match ctx.events.append's guarantee: a payload drift between
|
|
67
67
|
// service/job + feature-registration fails loudly here instead of landing on
|
|
68
68
|
// the events-table and crashing a consumer later.
|
|
69
69
|
const payload = deliveryAttemptSchema.parse({
|
|
@@ -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,
|
|
@@ -206,10 +206,10 @@ export function createDeliveryFeature(options?: DeliveryFeatureOptions): Feature
|
|
|
206
206
|
const pruned = await runAttemptLogRetention(db, {
|
|
207
207
|
olderThanDays: attemptLogRetentionDays,
|
|
208
208
|
});
|
|
209
|
-
ctx.log.info("delivery attempt log pruned", pruned);
|
|
209
|
+
ctx.obs.log.info("delivery attempt log pruned", pruned);
|
|
210
210
|
} catch (error) {
|
|
211
211
|
if (!(error instanceof ConsumerLagError)) throw error;
|
|
212
|
-
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", {
|
|
213
213
|
consumer: error.laggingConsumer,
|
|
214
214
|
});
|
|
215
215
|
}
|
|
@@ -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,
|
|
@@ -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;
|
|
@@ -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).
|
|
@@ -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
|