@cosmicdrift/kumiko-samples 0.367.0 → 0.369.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 +16 -2
- package/packages/bundled-features/src/audit/handlers/details.query.ts +1 -1
- package/packages/bundled-features/src/audit/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +10 -3
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +10 -3
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +6 -2
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/seeding.ts +2 -2
- package/packages/bundled-features/src/auth-foundation/feature.ts +43 -34
- package/packages/bundled-features/src/auth-foundation/index.ts +1 -0
- package/packages/bundled-features/src/auth-mfa/db/queries.ts +5 -2
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/status.query.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/mfa-code-verifier.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/mfa-status-checker.ts +1 -1
- package/packages/bundled-features/src/auth-mfa-user-data/index.ts +19 -10
- package/packages/bundled-features/src/billing-foundation/billing-info-query.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/feature.ts +138 -135
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/projection.ts +3 -2
- package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +52 -1
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +203 -15
- package/packages/bundled-features/src/cap-counter/changes.json +13 -0
- package/packages/bundled-features/src/cap-counter/feature.ts +84 -73
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +3 -4
- package/packages/bundled-features/src/cap-counter/index.ts +9 -1
- package/packages/bundled-features/src/cap-counter/tables.ts +10 -3
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -2
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +6 -2
- package/packages/bundled-features/src/cap-overview/handlers/tenant-options.query.ts +1 -1
- package/packages/bundled-features/src/channel-email/changes.json +7 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +39 -8
- package/packages/bundled-features/src/channel-email/feature.ts +1 -1
- package/packages/bundled-features/src/channel-email/index.ts +5 -1
- package/packages/bundled-features/src/channel-in-app/handlers/inbox.query.ts +1 -1
- package/packages/bundled-features/src/channel-in-app/handlers/mark-all-read.write.ts +1 -1
- package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +1 -1
- package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +1 -1
- package/packages/bundled-features/src/channel-in-app/tables.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +1 -2
- package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +8 -4
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +26 -17
- package/packages/bundled-features/src/config/db/queries/resolver.ts +1 -1
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +2 -2
- package/packages/bundled-features/src/config/resolver.ts +1 -1
- package/packages/bundled-features/src/config/web/i18n.ts +1 -2
- package/packages/bundled-features/src/config/write-helpers.ts +1 -1
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +9 -7
- package/packages/bundled-features/src/custom-fields/db/queries/field-access.ts +1 -1
- package/packages/bundled-features/src/custom-fields/db/queries/projection.ts +1 -1
- package/packages/bundled-features/src/custom-fields/db/queries/quota.ts +1 -1
- package/packages/bundled-features/src/custom-fields/db/queries/retention.ts +6 -2
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +1 -1
- package/packages/bundled-features/src/custom-fields/feature.ts +11 -14
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +2 -2
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +4 -4
- package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +16 -10
- package/packages/bundled-features/src/delivery/address-opt-out.ts +5 -2
- package/packages/bundled-features/src/delivery/attempt-log-retention.ts +1 -1
- package/packages/bundled-features/src/delivery/changes.json +6 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +1 -1
- package/packages/bundled-features/src/delivery/db/queries/address-opt-outs.ts +1 -1
- package/packages/bundled-features/src/delivery/db/queries/preferences.ts +1 -1
- package/packages/bundled-features/src/delivery/delivery-service.ts +16 -6
- package/packages/bundled-features/src/delivery/feature.ts +2 -2
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +3 -2
- package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +1 -1
- package/packages/bundled-features/src/delivery/index.ts +8 -1
- package/packages/bundled-features/src/delivery/unsubscribe.ts +66 -37
- package/packages/bundled-features/src/delivery/upsert-preference.ts +5 -2
- package/packages/bundled-features/src/derivatives-sharp/feature.ts +21 -12
- package/packages/bundled-features/src/derivatives-sharp/index.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +112 -103
- package/packages/bundled-features/src/document-ingest-foundation/forget-extract-with-file-ref.ts +3 -3
- package/packages/bundled-features/src/document-ingest-foundation/index.ts +4 -1
- package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/set.write.ts +3 -5
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +134 -129
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant-by-file-ref.query.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-foundation/feature.ts +38 -29
- package/packages/bundled-features/src/file-foundation/index.ts +1 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +1 -1
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +32 -21
- package/packages/bundled-features/src/file-provider-inmemory/index.ts +6 -1
- package/packages/bundled-features/src/file-provider-s3/changes.json +10 -1
- package/packages/bundled-features/src/file-provider-s3/env-feature.ts +62 -0
- package/packages/bundled-features/src/file-provider-s3/feature.ts +107 -73
- package/packages/bundled-features/src/file-provider-s3/index.ts +6 -1
- package/packages/bundled-features/src/file-provider-s3/lib/index.ts +3 -0
- package/packages/bundled-features/src/{files-provider-s3 → file-provider-s3/lib}/s3-provider.ts +19 -19
- package/packages/bundled-features/src/file-provider-s3-env/index.ts +7 -2
- package/packages/bundled-features/src/files-provider-s3/index.ts +11 -3
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +1 -1
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +1 -2
- package/packages/bundled-features/src/folders/feature.ts +8 -3
- package/packages/bundled-features/src/folders-user-data/index.ts +27 -18
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +1 -1
- package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +1 -1
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +3 -2
- package/packages/bundled-features/src/form-draft/feature.ts +6 -1
- package/packages/bundled-features/src/form-draft/handlers/cleanup.job.ts +2 -2
- package/packages/bundled-features/src/form-draft/index.ts +1 -1
- package/packages/bundled-features/src/form-draft/lookup.ts +1 -1
- package/packages/bundled-features/src/form-draft-user-data/index.ts +19 -10
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +165 -154
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +1 -2
- package/packages/bundled-features/src/inbound-mail-foundation/index.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +5 -2
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +3 -2
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +1 -1
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +22 -13
- package/packages/bundled-features/src/inbound-provider-imap/index.ts +6 -1
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +20 -12
- package/packages/bundled-features/src/inbound-provider-inmemory/index.ts +1 -0
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +1 -1
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/detail.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +7 -2
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +1 -1
- package/packages/bundled-features/src/jobs/job-run-logger.ts +11 -10
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
- package/packages/bundled-features/src/ledger/feature.ts +6 -3
- package/packages/bundled-features/src/mail-foundation/feature.ts +66 -46
- package/packages/bundled-features/src/mail-foundation/i18n.ts +8 -0
- package/packages/bundled-features/src/mail-foundation/index.ts +1 -0
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +31 -17
- package/packages/bundled-features/src/mail-transport-inmemory/index.ts +6 -1
- package/packages/bundled-features/src/mail-transport-smtp/changes.json +7 -0
- package/packages/bundled-features/src/mail-transport-smtp/config-keys.ts +67 -0
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +67 -82
- package/packages/bundled-features/src/mail-transport-smtp/i18n.ts +28 -0
- package/packages/bundled-features/src/mail-transport-smtp/index.ts +1 -0
- package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +1 -1
- package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +1 -2
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +1 -2
- package/packages/bundled-features/src/managed-pages/seeding.ts +8 -4
- package/packages/bundled-features/src/metrics/compute.ts +1 -1
- package/packages/bundled-features/src/metrics/filters.ts +1 -1
- package/packages/bundled-features/src/metrics/validate.ts +1 -1
- package/packages/bundled-features/src/notes-history/feature.ts +6 -3
- package/packages/bundled-features/src/notes-history/handlers/add-note.write.ts +1 -1
- package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
- package/packages/bundled-features/src/notes-history-user-data/index.ts +27 -18
- package/packages/bundled-features/src/personal-access-tokens/changes.json +6 -0
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +17 -2
- package/packages/bundled-features/src/personal-access-tokens/handlers/create.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/resolver.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -2
- package/packages/bundled-features/src/personal-access-tokens/roles.ts +8 -6
- package/packages/bundled-features/src/personal-access-tokens/screens.ts +2 -0
- package/packages/bundled-features/src/readiness/feature.ts +25 -16
- package/packages/bundled-features/src/readiness/index.ts +1 -1
- package/packages/bundled-features/src/secrets/changes.json +6 -0
- package/packages/bundled-features/src/secrets/db/queries/read.ts +1 -1
- package/packages/bundled-features/src/secrets/feature.ts +26 -6
- package/packages/bundled-features/src/secrets/handlers/delete.write.ts +5 -2
- package/packages/bundled-features/src/secrets/handlers/rotate.job.ts +2 -2
- package/packages/bundled-features/src/secrets/handlers/set.write.ts +5 -2
- package/packages/bundled-features/src/secrets/secrets-context.ts +4 -3
- package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/detail.query.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/mine.query.ts +1 -1
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +1 -1
- 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/session-callbacks.ts +14 -7
- package/packages/bundled-features/src/sessions/testing.ts +1 -1
- package/packages/bundled-features/src/tags/feature.ts +6 -3
- package/packages/bundled-features/src/template-resolver/api.ts +1 -1
- package/packages/bundled-features/src/template-resolver/constants.ts +0 -1
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/by-slug.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/by-tenant.query.ts +1 -2
- package/packages/bundled-features/src/template-resolver/handlers/collection-item.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/collection-list.query.ts +1 -2
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +4 -2
- package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +1 -1
- package/packages/bundled-features/src/template-resolver/seeding.ts +3 -3
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +1 -1
- package/packages/bundled-features/src/template-resolver/template-preview.ts +1 -1
- package/packages/bundled-features/src/template-resolver/template-resources.ts +1 -1
- package/packages/bundled-features/src/template-resolver/testing.ts +1 -1
- package/packages/bundled-features/src/template-resolver-user-data/index.ts +23 -14
- package/packages/bundled-features/src/tenant/handlers/active-tenant-ids.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/add-member.write.ts +1 -2
- package/packages/bundled-features/src/tenant/handlers/cancel-invitation.write.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/invitations.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/me.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/remove-member.write.ts +2 -1
- package/packages/bundled-features/src/tenant/handlers/resolve-user-ids.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/update-member-roles.write.ts +1 -2
- package/packages/bundled-features/src/tenant/invited-membership.ts +2 -2
- package/packages/bundled-features/src/tenant/last-tenant-admin.ts +1 -2
- package/packages/bundled-features/src/tenant/seeding.ts +12 -12
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +1 -2
- package/packages/bundled-features/src/tenant-lifecycle/handlers/request-destruction.write.ts +1 -2
- package/packages/bundled-features/src/tenant-lifecycle/lib/revoke-tenant-sessions.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/lifecycle-gate.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +3 -3
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +10 -5
- package/packages/bundled-features/src/tenant-lifecycle/testing.ts +3 -3
- package/packages/bundled-features/src/tenant-settings/feature.ts +34 -29
- package/packages/bundled-features/src/tier-engine/feature.ts +426 -428
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +2 -3
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +5 -2
- package/packages/bundled-features/src/user/changes.json +6 -0
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +1 -1
- package/packages/bundled-features/src/user/handlers/create.write.ts +1 -2
- package/packages/bundled-features/src/user/handlers/find-for-auth.query.ts +1 -1
- package/packages/bundled-features/src/user/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/user/handlers/update-roles.ts +2 -1
- package/packages/bundled-features/src/user/index.ts +5 -1
- package/packages/bundled-features/src/user/invited-global-roles.ts +2 -2
- package/packages/bundled-features/src/user/principal-status.ts +13 -3
- package/packages/bundled-features/src/user/resolve-display-names.ts +1 -1
- package/packages/bundled-features/src/user/seeding.ts +5 -5
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +6 -0
- package/packages/bundled-features/src/user-data-rights/db/queries/export-jobs.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +7 -4
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +1 -2
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +40 -3
- package/packages/bundled-features/src/user-data-rights/lib/deny-if-target-outside-admin-tenant.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +7 -2
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +7 -3
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +11 -5
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +10 -5
- package/packages/bundled-features/src/waitlist/handlers/invite.write.ts +1 -2
- package/packages/bundled-features/src/waitlist/handlers/reject.write.ts +1 -1
- package/packages/bundled-features/src/waitlist/lib.ts +5 -2
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/feature.ts +58 -47
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/index.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +1 -1
- package/samples/apps/admin-console/src/run-config.ts +2 -2
- package/samples/apps/cap-billing-demo/package.json +3 -0
- package/samples/apps/cap-billing-demo/src/feature.ts +2 -2
- package/samples/apps/cap-billing-demo/src/run-config.ts +8 -8
- package/samples/apps/marketing-demo/src/app/seed.ts +1 -1
- package/samples/apps/marketing-demo/src/db/queries/seed-counts.ts +1 -1
- package/samples/apps/showcase/package.json +3 -0
- package/samples/apps/showcase/src/app/seed-items.ts +1 -1
- package/samples/apps/styleguide/src/app/seed.ts +2 -2
- package/samples/apps/styleguide/src/features/ai-settings/seed.ts +2 -2
- package/samples/apps/ui-walkthrough/scripts/emit-e2e-data.ts +1 -1
- package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +1 -1
- package/samples/apps/ui-walkthrough/src/app/seed-rental.ts +2 -2
- package/samples/apps/ui-walkthrough/src/app/seed-vehicles.ts +1 -1
- package/samples/apps/ui-walkthrough/src/app/seed.ts +1 -1
- package/samples/apps/ui-walkthrough/src/db/queries/row-counts.ts +1 -1
- package/samples/apps/ui-walkthrough/src/db/queries/task-counts.ts +1 -1
- package/samples/apps/use-all-bundled/feature-manifest.json +16 -16
- package/samples/apps/use-all-bundled/package.json +3 -0
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +2 -2
- package/samples/apps/use-all-bundled/src/app/auth-constants.ts +1 -1
- package/samples/apps/use-all-bundled/src/app/cap-overview-caps.ts +2 -2
- package/samples/apps/use-all-bundled/src/app/dev-app-options.ts +1 -1
- package/samples/apps/use-all-bundled/src/app/seed-documents.ts +1 -1
- package/samples/apps/use-all-bundled/src/app/seed.ts +11 -5
- package/samples/apps/use-all-bundled/src/run-config.ts +55 -56
- package/samples/apps/user-data-rights-demo/README.md +2 -2
- package/samples/apps/user-data-rights-demo/package.json +3 -0
- package/samples/apps/user-data-rights-demo/src/run-config.ts +7 -7
- package/samples/apps/workspaces/package.json +3 -0
- package/samples/recipes/access-control/package.json +3 -0
- package/samples/recipes/anonymous-access/package.json +3 -0
- package/samples/recipes/anonymous-access-multitenant/package.json +3 -0
- package/samples/recipes/apex-landing/package.json +1 -0
- package/samples/recipes/apex-surface-auth/package.json +3 -0
- package/samples/recipes/apex-surface-auth/src/feature.ts +2 -2
- package/samples/recipes/api-worker-split/package.json +3 -0
- package/samples/recipes/api-worker-split/src/feature.ts +1 -2
- package/samples/recipes/auth-claims/package.json +3 -0
- package/samples/recipes/auth-foundation-providers/src/feature.ts +1 -1
- package/samples/recipes/auth-mfa/package.json +3 -0
- package/samples/recipes/basic-entity/package.json +3 -0
- package/samples/recipes/chat-channels/package.json +3 -0
- package/samples/recipes/compliance-profiles-demo/package.json +3 -0
- package/samples/recipes/cross-feature-events/package.json +3 -1
- package/samples/recipes/cross-feature-events/src/feature.ts +1 -1
- package/samples/recipes/crypto-shredding-hr/package.json +3 -0
- package/samples/recipes/currencies-global/package.json +3 -0
- package/samples/recipes/currencies-per-tenant/package.json +3 -0
- package/samples/recipes/custom-fields-basic/package.json +3 -0
- package/samples/recipes/custom-handlers/package.json +3 -0
- package/samples/recipes/dashboard-stats/package.json +3 -0
- package/samples/recipes/delivery-notifications/package.json +3 -0
- package/samples/recipes/e2e-generator/package.json +3 -0
- package/samples/recipes/embedded/package.json +3 -0
- package/samples/recipes/embedded-entity-form/package.json +1 -0
- package/samples/recipes/encrypted-tenant-config/README.md +1 -1
- package/samples/recipes/encrypted-tenant-config/package.json +3 -0
- package/samples/recipes/error-contract/package.json +3 -0
- package/samples/recipes/event-sourcing/package.json +3 -1
- package/samples/recipes/event-sourcing/src/feature.ts +16 -20
- package/samples/recipes/event-sourcing/tsconfig.json +2 -6
- package/samples/recipes/feature-toggles/package.json +3 -0
- package/samples/recipes/field-access/package.json +3 -0
- package/samples/recipes/files-post-processing/package.json +1 -0
- package/samples/recipes/golden-path/README.md +30 -0
- package/samples/recipes/golden-path/package.json +11 -0
- package/samples/recipes/golden-path/src/feature.ts +70 -0
- package/samples/recipes/idempotency/package.json +3 -0
- package/samples/recipes/inbound-mail-triage/package.json +3 -0
- package/samples/recipes/lane-routing/package.json +3 -0
- package/samples/recipes/ledger-banking/package.json +3 -0
- package/samples/recipes/ledger-invoicing/package.json +3 -0
- package/samples/recipes/legal-pages/package.json +3 -0
- package/samples/recipes/lifecycle-hooks/package.json +3 -0
- package/samples/recipes/managed-config/package.json +3 -0
- package/samples/recipes/managed-pages/package.json +3 -0
- package/samples/recipes/notes-history-basic/package.json +3 -0
- package/samples/recipes/ownership/package.json +3 -0
- package/samples/recipes/pdf-audit-export/package.json +1 -0
- package/samples/recipes/pipeline-basics/package.json +3 -0
- package/samples/recipes/public-share-token/package.json +3 -0
- package/samples/recipes/public-share-token/src/feature.ts +1 -1
- package/samples/recipes/rate-limiting/package.json +3 -0
- package/samples/recipes/realtime-sse/package.json +3 -0
- package/samples/recipes/record-detail-layout/package.json +1 -0
- package/samples/recipes/reference-data/package.json +3 -0
- package/samples/recipes/relations/package.json +3 -0
- package/samples/recipes/screens-nav/package.json +3 -0
- package/samples/recipes/search/package.json +3 -0
- package/samples/recipes/secrets-demo/package.json +3 -0
- package/samples/recipes/security-baseline/src/feature.ts +2 -2
- package/samples/recipes/session-revocation/README.md +1 -1
- package/samples/recipes/session-revocation/package.json +3 -0
- package/samples/recipes/session-revocation/src/feature.ts +1 -1
- package/samples/recipes/state-machine/package.json +3 -1
- package/samples/recipes/state-machine/src/entities/invoice.ts +2 -2
- package/samples/recipes/state-machine/src/events.ts +19 -0
- package/samples/recipes/state-machine/src/feature.ts +25 -30
- package/samples/recipes/state-machine/src/handlers/_helpers.ts +13 -14
- package/samples/recipes/state-machine/src/handlers/create.ts +1 -1
- package/samples/recipes/state-machine/src/handlers/transitions.ts +129 -122
- package/samples/recipes/state-machine/src/reducer.ts +1 -1
- package/samples/recipes/state-machine/tsconfig.json +2 -6
- package/samples/recipes/tags-basic/package.json +3 -0
- package/samples/recipes/tenant-isolation/package.json +3 -0
- package/samples/recipes/tenant-settings/package.json +3 -0
- package/samples/recipes/tier-admin/package.json +3 -0
- package/samples/recipes/timezones/package.json +3 -0
- package/samples/recipes/tsconfig.base.json +1 -1
- package/samples/recipes/user-data-rights/package.json +3 -0
- package/samples/recipes/user-data-rights/src/feature.ts +6 -2
- package/samples/recipes/user-profile/package.json +3 -0
- package/samples/recipes/user-profile/src/feature.ts +2 -2
- package/samples/recipes/webhook-step/package.json +3 -0
- package/samples/recipes/wizard-form/package.json +1 -0
- package/samples/recipes/workflow-engine/package.json +3 -0
- package/samples/recipes/workflow-engine/src/db/queries/suspended-runs.ts +5 -2
- package/samples/recipes/writeform-section/package.json +1 -0
- package/packages/bundled-features/src/file-provider-s3-env/feature.ts +0 -58
- /package/packages/bundled-features/src/{files-provider-s3 → file-provider-s3/lib}/env-helper.ts +0 -0
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
// Rows weiter (reference bleibt alt), deshalb id-Cursor-Paging statt einer
|
|
39
39
|
// einzelnen Page — sonst verstopfen erledigte Rows das batchLimit-Fenster.
|
|
40
40
|
|
|
41
|
-
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
42
41
|
import {
|
|
43
42
|
collectPiiSubjectFields,
|
|
44
43
|
configuredPiiSubjectKms,
|
|
44
|
+
isRecordKeyedField,
|
|
45
45
|
isSelfPiiField,
|
|
46
46
|
type KmsAdapter,
|
|
47
47
|
resolveSubjectForField,
|
|
@@ -53,6 +53,7 @@ import {
|
|
|
53
53
|
createTenantDb,
|
|
54
54
|
type DbRunner,
|
|
55
55
|
nullBlindIndexesForSubject,
|
|
56
|
+
selectMany,
|
|
56
57
|
type TenantDb,
|
|
57
58
|
type WhereObject,
|
|
58
59
|
WritePurposes,
|
|
@@ -160,10 +161,17 @@ async function purgeMatchingRows(
|
|
|
160
161
|
// (purgeMatchingRows's hardDelete select) is enough; the with-files path's
|
|
161
162
|
// full row works the same way.
|
|
162
163
|
//
|
|
163
|
-
// userOwned/tenantOwned are explicitly excluded:
|
|
164
|
+
// userOwned/tenantOwned are explicitly excluded (except a whenAbsent: "self" field
|
|
165
|
+
// of an ownerless row, which resolves to this row's own record key): hardDelete only destroys
|
|
164
166
|
// THIS row's own data, but the owning user's/tenant's key may still protect
|
|
165
167
|
// OTHER live rows that reference the same subject — erasing it here would
|
|
166
168
|
// be a silent cross-row data-loss bug, not a retention fix.
|
|
169
|
+
function mayResolveToOwnRecordKey(field: EntityDefinition["fields"][string]): boolean {
|
|
170
|
+
if ("tenantOwned" in field && field.tenantOwned === true) return false;
|
|
171
|
+
if ("userOwned" in field && field.userOwned !== undefined) return isRecordKeyedField(field);
|
|
172
|
+
return ("recordOwned" in field && field.recordOwned === true) || isSelfPiiField(field);
|
|
173
|
+
}
|
|
174
|
+
|
|
167
175
|
function collectHardDeleteEraseSubjects(
|
|
168
176
|
entity: EntityDefinition,
|
|
169
177
|
entityName: string,
|
|
@@ -173,13 +181,11 @@ function collectHardDeleteEraseSubjects(
|
|
|
173
181
|
const subjectsByKey = new Map<string, SubjectId>();
|
|
174
182
|
for (const fieldName of collectPiiSubjectFields(entity)) {
|
|
175
183
|
const field = entity.fields[fieldName];
|
|
176
|
-
if (!field) continue;
|
|
177
|
-
const isRecordOwned = "recordOwned" in field && field.recordOwned === true;
|
|
178
|
-
const isUserOwned = "userOwned" in field && field.userOwned !== undefined;
|
|
179
|
-
const isTenantOwned = "tenantOwned" in field && field.tenantOwned === true;
|
|
180
|
-
if (isUserOwned || isTenantOwned) continue; // never erase — see header comment
|
|
181
|
-
if (!isRecordOwned && !isSelfPiiField(field)) continue;
|
|
184
|
+
if (!field || !mayResolveToOwnRecordKey(field)) continue;
|
|
182
185
|
const subject = resolveSubjectForField(entity, fieldName, row, { tenantId, entityName });
|
|
186
|
+
// whenAbsent: "self" resolves to the owner's key while the row has an owner — never erased here
|
|
187
|
+
const isUserOwned = "userOwned" in field && field.userOwned !== undefined;
|
|
188
|
+
if (isUserOwned && subject?.kind !== "record") continue;
|
|
183
189
|
if (subject) subjectsByKey.set(subjectIdToKey(subject), subject);
|
|
184
190
|
}
|
|
185
191
|
return Array.from(subjectsByKey.values());
|
|
@@ -236,7 +242,7 @@ async function forgetRowAndEraseKeys(args: {
|
|
|
236
242
|
readonly registry: Registry;
|
|
237
243
|
}): Promise<{ readonly isSuccess: boolean }> {
|
|
238
244
|
return runInSubTransaction(args.db, async (sp) => {
|
|
239
|
-
const tdb = createTenantDb(sp, args.tenantId, "system");
|
|
245
|
+
const tdb = createTenantDb({ db: sp, tenantId: args.tenantId, mode: "system" });
|
|
240
246
|
const result = await args.executor.forget(
|
|
241
247
|
{ id: args.row["id"] as EntityId },
|
|
242
248
|
args.systemUser,
|
|
@@ -666,7 +672,7 @@ export async function runRetentionCleanup(
|
|
|
666
672
|
// the daily cron converges (hardDelete removes rows, softDelete's isDeleted
|
|
667
673
|
// filter shrinks the set). A single batched forget/delete event is a follow-up.
|
|
668
674
|
const systemUser = createSystemUser(tenantId);
|
|
669
|
-
const tdb = createTenantDb(db, tenantId, "system");
|
|
675
|
+
const tdb = createTenantDb({ db, tenantId, mode: "system" });
|
|
670
676
|
const fileHolders = collectSingleFileHolders(registry);
|
|
671
677
|
|
|
672
678
|
// Pre-load every override row for this tenant ONCE — N entities × M
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
1
|
import { computeBlindIndex, configuredBlindIndexKey } from "@cosmicdrift/kumiko-framework/crypto";
|
|
3
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createEventStoreExecutor,
|
|
4
|
+
fetchOne,
|
|
5
|
+
type TenantDb,
|
|
6
|
+
} from "@cosmicdrift/kumiko-framework/db";
|
|
4
7
|
import type { SessionUser, TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
|
|
5
8
|
import { ConflictError, writeFailure } from "@cosmicdrift/kumiko-framework/errors";
|
|
6
9
|
import { generateDeterministicId } from "@cosmicdrift/kumiko-framework/utils";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { deleteManyBatched } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { deleteManyBatched } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import { type PruneEventsOptions, pruneEvents } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
4
4
|
import { deliveryAttemptsTable } from "./tables.js";
|
|
5
5
|
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.369.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "Unsubscribe links in dev and a reusable result page",
|
|
6
|
+
"detail": "runDevApp accepts unsubscribe: { secret, tenantOrigin? } like runProdApp, mounts the unsubscribe routes and signs the links (base URL from auth.mail.baseUrl or APP_BASE_URL). Both share resolveUnsubscribeWiring from server-runtime. delivery exports renderUnsubscribeResultPage({ kind: \"unsubscribed\" | \"resubscribed\" | \"failed\", locale, resubscribeToken? }), the page the unsubscribe routes answer with, for apps that serve their own (e.g. legacy) unsubscribe links; without a resubscribeToken it offers no undo button."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.367.0",
|
|
4
10
|
"type": "improvement",
|
|
@@ -148,7 +148,7 @@ export function buildChannelContext(
|
|
|
148
148
|
filesForTenant?: (tenantId: string) => FileContext | undefined,
|
|
149
149
|
): ChannelContext {
|
|
150
150
|
return {
|
|
151
|
-
db: createTenantDb(db, tenantId),
|
|
151
|
+
db: createTenantDb({ db, tenantId }),
|
|
152
152
|
registry,
|
|
153
153
|
sseBroker,
|
|
154
154
|
tenantId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
import { notificationAddressOptOutsTable } from "../../tables.js";
|
|
5
5
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
4
4
|
|
|
5
5
|
export type NotificationPreferenceRow = {
|
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
16
16
|
import {
|
|
17
17
|
assertNotificationVariables,
|
|
18
|
+
bridgeStub,
|
|
18
19
|
createSystemUser,
|
|
19
20
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
20
21
|
import type { FileContext } from "@cosmicdrift/kumiko-framework/files";
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
createEscapeHatchReporter,
|
|
25
26
|
} from "@cosmicdrift/kumiko-framework/pipeline";
|
|
26
27
|
import type { SecretsContext } from "@cosmicdrift/kumiko-framework/secrets";
|
|
27
|
-
import { bridgeStub } from "@cosmicdrift/kumiko-framework/testing/handler-context";
|
|
28
28
|
import { generateId } from "@cosmicdrift/kumiko-framework/utils";
|
|
29
29
|
import type { Redis } from "ioredis";
|
|
30
30
|
import { fileContextFromResolver } from "../file-foundation/index.js";
|
|
@@ -273,17 +273,27 @@ export function createDeliveryService(options: DeliveryServiceOptions): Delivery
|
|
|
273
273
|
// path a cross-tenant read the dispatcher never allows. Cross-tenant reach only
|
|
274
274
|
// exists through the grant-carrying systemDb view below.
|
|
275
275
|
const isSystem = registry.isHandlerSystemScoped(tenantUserIdsQuery);
|
|
276
|
-
const tenantDb = createTenantDb(
|
|
277
|
-
|
|
276
|
+
const tenantDb = createTenantDb({
|
|
277
|
+
db: run.db,
|
|
278
|
+
tenantId,
|
|
279
|
+
mode: "tenant",
|
|
280
|
+
grants: {
|
|
281
|
+
report,
|
|
282
|
+
},
|
|
278
283
|
});
|
|
279
284
|
// The dispatcher grants a systemScope handler's ctx.systemDb.unsafeRaw() ungated —
|
|
280
285
|
// the grant comes from the handler's own r.systemScope() registration, not from this
|
|
281
286
|
// caller. Only the systemDb view's own source TenantDb carries the grant, so it
|
|
282
287
|
// can't leak onto ctx.db.
|
|
283
288
|
const systemDbSourceTenantDb = isSystem
|
|
284
|
-
? createTenantDb(
|
|
285
|
-
|
|
286
|
-
|
|
289
|
+
? createTenantDb({
|
|
290
|
+
db: run.db,
|
|
291
|
+
tenantId,
|
|
292
|
+
mode: "system",
|
|
293
|
+
grants: {
|
|
294
|
+
report,
|
|
295
|
+
unsafeRaw: { reason: `r.systemScope() handler "${tenantUserIdsQuery}"` },
|
|
296
|
+
},
|
|
287
297
|
})
|
|
288
298
|
: undefined;
|
|
289
299
|
// Hand-built context, not routed through the dispatcher — this needs both
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { updateMany, upsertByPk } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
1
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { updateMany, upsertByPk } from "@cosmicdrift/kumiko-framework/db";
|
|
3
3
|
import {
|
|
4
4
|
access,
|
|
5
5
|
defineFeature,
|
|
@@ -61,7 +61,7 @@ export function createDeliveryFeature(options?: DeliveryFeatureOptions): Feature
|
|
|
61
61
|
const attemptLogRetentionDays = resolveAttemptLogRetentionDays(options?.attemptLogRetentionDays);
|
|
62
62
|
return defineFeature("delivery", (r) => {
|
|
63
63
|
r.describe(
|
|
64
|
-
"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` (`runProdApp` 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. 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.",
|
|
64
|
+
"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.",
|
|
65
65
|
);
|
|
66
66
|
r.uiHints({
|
|
67
67
|
displayLabel: "Notifications \u00b7 Dispatch Core",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { selectMany, type WhereObject } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
1
|
import {
|
|
3
2
|
decodeKeysetCursor,
|
|
4
3
|
encodeKeysetCursor,
|
|
4
|
+
selectMany,
|
|
5
5
|
type TenantDb,
|
|
6
|
+
type WhereObject,
|
|
6
7
|
} from "@cosmicdrift/kumiko-framework/db";
|
|
7
8
|
import {
|
|
8
9
|
access,
|
|
@@ -66,7 +67,7 @@ function isDeliveryLogSortField(value: string): value is DeliveryLogSortField {
|
|
|
66
67
|
type DeliveryLogSortColumn = (typeof DELIVERY_LOG_SORT_COLUMNS)[DeliveryLogSortField];
|
|
67
68
|
|
|
68
69
|
// createdAt round-trips through Temporal.Instant (matches how the WHERE
|
|
69
|
-
// builder coerces timestamptz values — see
|
|
70
|
+
// builder coerces timestamptz values — see db/query.ts prepareValue);
|
|
70
71
|
// every other whitelisted column is a plain text column. Keyed by the
|
|
71
72
|
// PHYSICAL column, not the display alias — row only has the DB field names
|
|
72
73
|
// (e.g. notificationType), never the alias (e.g. "type").
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { selectMany } from "@cosmicdrift/kumiko-framework/
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
2
2
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
3
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
4
4
|
import * as z from "zod";
|
|
@@ -20,7 +20,11 @@ export {
|
|
|
20
20
|
DEFAULT_ATTEMPT_LOG_RETENTION_DAYS,
|
|
21
21
|
runAttemptLogRetention,
|
|
22
22
|
} from "./attempt-log-retention.js";
|
|
23
|
-
export {
|
|
23
|
+
export {
|
|
24
|
+
buildChannelContext,
|
|
25
|
+
createEmailAttachmentReader,
|
|
26
|
+
createTemplateMailRenderer,
|
|
27
|
+
} from "./channel-context.js";
|
|
24
28
|
export {
|
|
25
29
|
type ChatWebhookChannelOptions,
|
|
26
30
|
chatMessageText,
|
|
@@ -67,6 +71,7 @@ export {
|
|
|
67
71
|
type UnsubscribeAddressPayload,
|
|
68
72
|
unsubscribeAddressPayloadSchema,
|
|
69
73
|
} from "./handlers/unsubscribe-address.write.js";
|
|
74
|
+
export { currentNotificationType } from "./notification-type-aliases.js";
|
|
70
75
|
export { createDeliveryNotifyFactory } from "./notify-factory.js";
|
|
71
76
|
export {
|
|
72
77
|
deliveryAttemptsTable,
|
|
@@ -93,8 +98,10 @@ export {
|
|
|
93
98
|
type AddressUnsubscribeTokenPayload,
|
|
94
99
|
createUnsubscribeRoutes,
|
|
95
100
|
DELIVERY_UNSUBSCRIBE_ONE_CLICK_HEADER_VALUE,
|
|
101
|
+
renderUnsubscribeResultPage,
|
|
96
102
|
signAddressUnsubscribeToken,
|
|
97
103
|
signUnsubscribeToken,
|
|
104
|
+
type UnsubscribeResultPage,
|
|
98
105
|
type UnsubscribeRouteOptions,
|
|
99
106
|
type UnsubscribeTokenPayload,
|
|
100
107
|
} from "./unsubscribe.js";
|
|
@@ -415,33 +415,48 @@ function confirmationPage(token: string, locale: string | undefined): string {
|
|
|
415
415
|
);
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
418
|
+
export type UnsubscribeResultPage =
|
|
419
|
+
// Without a resubscribeToken the page offers no undo button.
|
|
420
|
+
| { readonly kind: "unsubscribed"; readonly resubscribeToken?: string }
|
|
421
|
+
| { readonly kind: "resubscribed" }
|
|
422
|
+
| { readonly kind: "failed" };
|
|
423
|
+
|
|
424
|
+
/** The plain result page the unsubscribe routes answer with, for apps serving their own
|
|
425
|
+
* (e.g. legacy) unsubscribe links. Texts come from the mail translations of `locale`. */
|
|
426
|
+
export function renderUnsubscribeResultPage(
|
|
427
|
+
page: UnsubscribeResultPage & { readonly locale?: string | undefined },
|
|
428
|
+
): string {
|
|
429
|
+
const lang = pageLocale(page.locale);
|
|
430
|
+
switch (page.kind) {
|
|
431
|
+
case "unsubscribed": {
|
|
432
|
+
const undoForm =
|
|
433
|
+
page.resubscribeToken === undefined
|
|
434
|
+
? ""
|
|
435
|
+
: `
|
|
424
436
|
<form method="post" action="${DELIVERY_RESUBSCRIBE_PATH}">
|
|
425
|
-
<input type="hidden" name="token" value="${escapeHtmlAttr(resubscribeToken)}">
|
|
437
|
+
<input type="hidden" name="token" value="${escapeHtmlAttr(page.resubscribeToken)}">
|
|
426
438
|
<button type="submit">${escapeHtmlAttr(mailT(lang, "mail.unsubscribe.undoButton"))}</button>
|
|
427
|
-
</form
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
439
|
+
</form>`;
|
|
440
|
+
return htmlPage(
|
|
441
|
+
lang,
|
|
442
|
+
mailT(lang, "mail.unsubscribe.doneTitle"),
|
|
443
|
+
`<p>${escapeHtmlAttr(mailT(lang, "mail.unsubscribe.doneMessage"))}</p>${undoForm}`,
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
case "resubscribed":
|
|
447
|
+
return htmlPage(
|
|
448
|
+
lang,
|
|
449
|
+
mailT(lang, "mail.unsubscribe.resubscribedTitle"),
|
|
450
|
+
`<p>${escapeHtmlAttr(mailT(lang, "mail.unsubscribe.resubscribedMessage"))}</p>`,
|
|
451
|
+
);
|
|
452
|
+
case "failed": {
|
|
453
|
+
const message = mailT(lang, "mail.unsubscribe.failed");
|
|
454
|
+
return htmlPage(lang, message, `<p>${escapeHtmlAttr(message)}</p>`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
438
457
|
}
|
|
439
458
|
|
|
440
|
-
|
|
441
|
-
const lang = pageLocale(locale);
|
|
442
|
-
const message = mailT(lang, "mail.unsubscribe.failed");
|
|
443
|
-
return htmlPage(lang, message, `<p>${escapeHtmlAttr(message)}</p>`);
|
|
444
|
-
}
|
|
459
|
+
const NO_STORE_HEADERS = { "Cache-Control": "no-store" } as const;
|
|
445
460
|
|
|
446
461
|
function isFormRequest(contentType: string | undefined): boolean {
|
|
447
462
|
return contentType?.includes("application/x-www-form-urlencoded") === true;
|
|
@@ -516,12 +531,20 @@ export function createUnsubscribeRoutes(
|
|
|
516
531
|
// (e.g. events-table missing, DB down) behind a misleading 400.
|
|
517
532
|
const succeeded = await dispatchUnsubscribeWrite(verified, deps);
|
|
518
533
|
if (!succeeded) {
|
|
519
|
-
return c.html(
|
|
534
|
+
return c.html(
|
|
535
|
+
renderUnsubscribeResultPage({ kind: "failed", locale: verified.locale }),
|
|
536
|
+
500,
|
|
537
|
+
NO_STORE_HEADERS,
|
|
538
|
+
);
|
|
520
539
|
}
|
|
521
540
|
return c.html(
|
|
522
|
-
|
|
541
|
+
renderUnsubscribeResultPage({
|
|
542
|
+
kind: "unsubscribed",
|
|
543
|
+
resubscribeToken: await signResubscribeToken(verified, encodedSecret),
|
|
544
|
+
locale: verified.locale,
|
|
545
|
+
}),
|
|
523
546
|
200,
|
|
524
|
-
|
|
547
|
+
NO_STORE_HEADERS,
|
|
525
548
|
);
|
|
526
549
|
},
|
|
527
550
|
});
|
|
@@ -540,22 +563,28 @@ export function createUnsubscribeRoutes(
|
|
|
540
563
|
// The undo button on the unsubscribed page posts a form; a browser should land on a page.
|
|
541
564
|
if (isFormRequest(c.req.header("content-type"))) {
|
|
542
565
|
return outcome === "success"
|
|
543
|
-
? c.html(
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
566
|
+
? c.html(
|
|
567
|
+
renderUnsubscribeResultPage({ kind: "resubscribed", locale: verified.locale }),
|
|
568
|
+
200,
|
|
569
|
+
NO_STORE_HEADERS,
|
|
570
|
+
)
|
|
571
|
+
: c.html(
|
|
572
|
+
renderUnsubscribeResultPage({ kind: "failed", locale: verified.locale }),
|
|
573
|
+
outcome === "limit_reached" ? 409 : 500,
|
|
574
|
+
NO_STORE_HEADERS,
|
|
575
|
+
);
|
|
547
576
|
}
|
|
548
577
|
if (outcome === "limit_reached") {
|
|
549
|
-
return c.json(
|
|
550
|
-
|
|
551
|
-
|
|
578
|
+
return c.json(
|
|
579
|
+
{ error: { code: DeliveryErrors.resubscribeLimitReached } },
|
|
580
|
+
409,
|
|
581
|
+
NO_STORE_HEADERS,
|
|
582
|
+
);
|
|
552
583
|
}
|
|
553
584
|
if (outcome === "failed") {
|
|
554
|
-
return c.json({ error: { code: "resubscribe_failed" } }, 500,
|
|
555
|
-
"Cache-Control": "no-store",
|
|
556
|
-
});
|
|
585
|
+
return c.json({ error: { code: "resubscribe_failed" } }, 500, NO_STORE_HEADERS);
|
|
557
586
|
}
|
|
558
|
-
return c.json({ isSuccess: true }, 200,
|
|
587
|
+
return c.json({ isSuccess: true }, 200, NO_STORE_HEADERS);
|
|
559
588
|
},
|
|
560
589
|
});
|
|
561
590
|
|
|
@@ -13,8 +13,11 @@
|
|
|
13
13
|
// fall through to update, or no-op if the winner already set the target
|
|
14
14
|
// `enabled` state.
|
|
15
15
|
|
|
16
|
-
import {
|
|
17
|
-
|
|
16
|
+
import {
|
|
17
|
+
createEventStoreExecutor,
|
|
18
|
+
fetchOne,
|
|
19
|
+
type TenantDb,
|
|
20
|
+
} from "@cosmicdrift/kumiko-framework/db";
|
|
18
21
|
import type { SessionUser, TenantId, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
|
|
19
22
|
import { generateDeterministicId } from "@cosmicdrift/kumiko-framework/utils";
|
|
20
23
|
import { notificationPreferenceEntity, notificationPreferencesTable } from "./tables.js";
|
|
@@ -6,17 +6,26 @@ import { renderImage } from "./render.js";
|
|
|
6
6
|
|
|
7
7
|
const FEATURE_NAME = "derivatives-sharp";
|
|
8
8
|
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
export function createDerivativesSharpFeature() {
|
|
10
|
+
return defineFeature(
|
|
11
|
+
FEATURE_NAME,
|
|
12
|
+
(r) => {
|
|
13
|
+
r.describe(
|
|
14
|
+
"Registers an `image/*` renderer against the `derivativeRenderer` extension point declared by `file-derivatives`, backed by sharp. Supports resize/fit (cover, inside, contain), format conversion with quality (webp, avif, jpeg), whole-image blur, and blurring individual regions burned in before resize (see BlurRegion). EXIF orientation is applied and then normalized away, and all other EXIF (including GPS) is dropped. Server-only: sharp is a native binding — never import this from client code.",
|
|
15
|
+
);
|
|
16
|
+
r.uiHints({
|
|
17
|
+
displayLabel: "File Derivatives · Sharp",
|
|
18
|
+
category: "storage",
|
|
19
|
+
recommended: false,
|
|
20
|
+
});
|
|
21
|
+
r.requires({ features: ["file-derivatives"] });
|
|
22
|
+
|
|
23
|
+
const plugin: DerivativeRendererPlugin = { render: renderImage };
|
|
24
|
+
r.useExtension(EXT_DERIVATIVE_RENDERER, "image/*", plugin);
|
|
25
|
+
},
|
|
26
|
+
{ dedupeOptions: {} },
|
|
12
27
|
);
|
|
13
|
-
|
|
14
|
-
displayLabel: "File Derivatives · Sharp",
|
|
15
|
-
category: "storage",
|
|
16
|
-
recommended: false,
|
|
17
|
-
});
|
|
18
|
-
r.requires({ features: ["file-derivatives"] });
|
|
28
|
+
}
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
});
|
|
30
|
+
/** @deprecated Use createDerivativesSharpFeature(). Run `bun kumiko-upgrade --apply`. Removed in the first release after 2026-10-29. */
|
|
31
|
+
export const derivativesSharpFeature = createDerivativesSharpFeature();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { derivativesSharpFeature } from "./feature.js";
|
|
1
|
+
export { createDerivativesSharpFeature, derivativesSharpFeature } from "./feature.js";
|
|
2
2
|
export { imageMetadata } from "./render.js";
|