@cosmicdrift/kumiko-samples 0.386.0 → 0.388.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -57
- package/README.md +1 -1
- package/package.json +2 -2
- package/packages/bundled-features/package.json +9 -2
- package/packages/bundled-features/src/agent-tools/changes.json +6 -0
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -1
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +9 -9
- package/packages/bundled-features/src/audit/changes.json +12 -0
- package/packages/bundled-features/src/audit/handlers/audit-events.ts +1 -1
- package/packages/bundled-features/src/audit/screens.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/auth-user-row.ts +10 -10
- package/packages/bundled-features/src/auth-email-password/feature.ts +6 -6
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/handlers/confirm-token-flow.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept-with-login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-accept.write.ts +18 -18
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +12 -12
- package/packages/bundled-features/src/auth-email-password/handlers/invite-info.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/invite-signup-complete.write.ts +24 -26
- package/packages/bundled-features/src/auth-email-password/handlers/login.write.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/self-registration-status.query.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +34 -37
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +6 -7
- package/packages/bundled-features/src/auth-email-password/handlers/switch-tenant-mfa-gate.write.ts +1 -5
- package/packages/bundled-features/src/auth-email-password/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/lockout-store.ts +1 -1
- package/packages/bundled-features/src/auth-email-password/seeding.ts +63 -66
- package/packages/bundled-features/src/auth-email-password/signup-token-store.ts +8 -8
- package/packages/bundled-features/src/auth-email-password/testing.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-client.ts +57 -57
- package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +15 -16
- package/packages/bundled-features/src/auth-email-password/web/client-plugin.ts +11 -12
- package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +1 -1
- package/packages/bundled-features/src/auth-email-password/web/constants.ts +3 -3
- package/packages/bundled-features/src/auth-email-password/web/default-topbar-actions.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +11 -11
- package/packages/bundled-features/src/auth-email-password/web/index.ts +5 -5
- package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +17 -17
- package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +3 -4
- package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +16 -17
- package/packages/bundled-features/src/auth-email-password/web/session.tsx +34 -34
- package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +3 -3
- package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +14 -14
- package/packages/bundled-features/src/auth-email-password/web/tenant-switcher.tsx +26 -27
- package/packages/bundled-features/src/auth-email-password/web/use-shell-user.ts +12 -13
- package/packages/bundled-features/src/auth-email-password/web/user-menu.tsx +17 -18
- package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +11 -12
- package/packages/bundled-features/src/auth-foundation/boot-checks.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/resolve-anonymous-access.ts +4 -4
- package/packages/bundled-features/src/auth-foundation/types.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/changes.json +6 -0
- package/packages/bundled-features/src/auth-mfa/feature-exports.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/feature.ts +6 -0
- package/packages/bundled-features/src/auth-mfa/handlers/disable.write.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm-preauth.write.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/handlers/enable-confirm.write.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/handlers/enable-start-preauth.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/reencrypt.job.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/handlers/regenerate-recovery.write.ts +5 -5
- package/packages/bundled-features/src/auth-mfa/handlers/verify.write.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/token-secrets.ts +1 -1
- package/packages/bundled-features/src/auth-mfa/web/client-plugin.ts +2 -2
- package/packages/bundled-features/src/auth-mfa/web/i18n.ts +9 -4
- package/packages/bundled-features/src/auth-mfa/web/index.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/web/mfa-client.ts +17 -16
- package/packages/bundled-features/src/auth-mfa/web/mfa-code-or-recovery-field.tsx +68 -0
- package/packages/bundled-features/src/auth-mfa/web/mfa-disable-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-regenerate-recovery-dialog.tsx +23 -13
- package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +15 -6
- package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +71 -24
- package/packages/bundled-features/src/billing-foundation/aggregate-id.ts +10 -11
- package/packages/bundled-features/src/billing-foundation/changes.json +55 -0
- package/packages/bundled-features/src/billing-foundation/checkout-core.ts +32 -42
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +8 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +14 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +3 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +35 -19
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +12 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-revoke.ts +14 -8
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +5 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +8 -10
- package/packages/bundled-features/src/billing-foundation/entities.ts +27 -27
- package/packages/bundled-features/src/billing-foundation/events.ts +23 -21
- package/packages/bundled-features/src/billing-foundation/feature.ts +45 -44
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +3 -5
- package/packages/bundled-features/src/billing-foundation/handlers/billing-plans.query.ts +71 -6
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +19 -17
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +11 -12
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +4 -4
- package/packages/bundled-features/src/billing-foundation/handlers/plan-switch-preview.query.ts +65 -0
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +29 -22
- package/packages/bundled-features/src/billing-foundation/handlers/start-plan-checkout.write.ts +44 -8
- package/packages/bundled-features/src/billing-foundation/handlers/switch-plan.write.ts +72 -82
- package/packages/bundled-features/src/billing-foundation/handlers/sync-subscription.write.ts +14 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +22 -0
- package/packages/bundled-features/src/billing-foundation/index.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/plan-catalog.ts +62 -27
- package/packages/bundled-features/src/billing-foundation/plan-switch.ts +84 -0
- package/packages/bundled-features/src/billing-foundation/projection.ts +38 -37
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/tenant-destroy-hook.ts +7 -8
- package/packages/bundled-features/src/billing-foundation/types.ts +144 -66
- package/packages/bundled-features/src/billing-foundation/validate-options.ts +19 -0
- package/packages/bundled-features/src/billing-foundation/web/billing-plans-panel.tsx +123 -51
- package/packages/bundled-features/src/billing-foundation/web/checkout-consent-dialog.tsx +89 -42
- package/packages/bundled-features/src/billing-foundation/web/constants.ts +4 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-price.ts +55 -0
- package/packages/bundled-features/src/billing-foundation/web/plan-switch-confirm-dialog.tsx +224 -0
- package/packages/bundled-features/src/billing-foundation/webhook-handler.ts +1 -1
- package/packages/bundled-features/src/cap-counter/aggregate-id.ts +8 -9
- package/packages/bundled-features/src/cap-counter/cap-reservation.ts +5 -1
- package/packages/bundled-features/src/cap-counter/changes.json +18 -0
- package/packages/bundled-features/src/cap-counter/constants.ts +11 -12
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +66 -68
- package/packages/bundled-features/src/cap-counter/entity.ts +29 -29
- package/packages/bundled-features/src/cap-counter/feature.ts +34 -35
- package/packages/bundled-features/src/cap-counter/handlers/increment-rolling.write.ts +31 -31
- package/packages/bundled-features/src/cap-counter/handlers/mark-soft-warned.write.ts +4 -5
- package/packages/bundled-features/src/cap-counter/stock-cap-guard.ts +9 -2
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -5
- package/packages/bundled-features/src/cap-overview/handlers/caps-usage.query.ts +1 -3
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +1 -1
- package/packages/bundled-features/src/channel-email/changes.json +6 -0
- package/packages/bundled-features/src/channel-email/feature.ts +1 -1
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +34 -31
- package/packages/bundled-features/src/channel-in-app/handlers/unread-count.query.ts +3 -4
- package/packages/bundled-features/src/compliance-profiles/feature.ts +15 -16
- package/packages/bundled-features/src/compliance-profiles/handlers/for-tenant.query.ts +3 -6
- package/packages/bundled-features/src/compliance-profiles/handlers/list-profiles.query.ts +5 -5
- package/packages/bundled-features/src/compliance-profiles/handlers/needs-profile.query.ts +4 -4
- package/packages/bundled-features/src/compliance-profiles/handlers/set-profile.write.ts +13 -14
- package/packages/bundled-features/src/compliance-profiles/handlers/sub-processors.query.ts +9 -11
- package/packages/bundled-features/src/compliance-profiles/resolve-for-tenant.ts +7 -7
- package/packages/bundled-features/src/compliance-profiles/schema/profile-selection.ts +18 -23
- package/packages/bundled-features/src/compliance-profiles/seeding.ts +14 -15
- package/packages/bundled-features/src/compliance-profiles-ops/handlers/tenants-missing-profile.query.ts +3 -3
- package/packages/bundled-features/src/compliance-profiles-ops/index.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +12 -0
- package/packages/bundled-features/src/config/handlers/cascade.query.ts +4 -2
- package/packages/bundled-features/src/config/handlers/readiness.query.ts +7 -7
- package/packages/bundled-features/src/config/handlers/reencrypt.job.ts +4 -5
- package/packages/bundled-features/src/config/handlers/set.write.ts +4 -4
- package/packages/bundled-features/src/config/handlers/values.query.ts +4 -2
- package/packages/bundled-features/src/config/resolver.ts +4 -4
- package/packages/bundled-features/src/config/table.ts +10 -10
- package/packages/bundled-features/src/config/write-helpers.ts +28 -9
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +23 -21
- package/packages/bundled-features/src/custom-fields/aggregate-id.ts +16 -17
- package/packages/bundled-features/src/custom-fields/entity.ts +49 -47
- package/packages/bundled-features/src/custom-fields/events.ts +7 -8
- package/packages/bundled-features/src/custom-fields/feature.ts +70 -72
- package/packages/bundled-features/src/custom-fields/handlers/clear-custom-field.write.ts +9 -8
- package/packages/bundled-features/src/custom-fields/handlers/define-tenant-field.write.ts +28 -26
- package/packages/bundled-features/src/custom-fields/handlers/delete-system-field.write.ts +6 -7
- package/packages/bundled-features/src/custom-fields/handlers/delete-tenant-field.write.ts +12 -13
- package/packages/bundled-features/src/custom-fields/handlers/set-custom-field.write.ts +25 -28
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +6 -6
- package/packages/bundled-features/src/custom-fields/lib/field-access.ts +2 -2
- package/packages/bundled-features/src/custom-fields/lib/parse-serialized-field.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/quota.ts +1 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -12
- package/packages/bundled-features/src/custom-fields/run-retention.ts +3 -3
- package/packages/bundled-features/src/custom-fields/schemas.ts +14 -14
- package/packages/bundled-features/src/custom-fields/web/client-plugin.tsx +2 -5
- package/packages/bundled-features/src/custom-fields/web/constants.ts +2 -2
- package/packages/bundled-features/src/custom-fields/web/custom-fields-form-section.tsx +26 -26
- package/packages/bundled-features/src/custom-fields/wire-for-entity.ts +65 -67
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +3 -3
- package/packages/bundled-features/src/data-retention/_internal/parse-override.ts +2 -3
- package/packages/bundled-features/src/data-retention/feature.ts +11 -18
- package/packages/bundled-features/src/data-retention/handlers/policy-for.query.ts +6 -6
- package/packages/bundled-features/src/data-retention/keep-for.ts +17 -17
- package/packages/bundled-features/src/data-retention/override-schema.ts +13 -15
- package/packages/bundled-features/src/data-retention/presets.ts +18 -23
- package/packages/bundled-features/src/data-retention/resolve-for-tenant.ts +1 -1
- package/packages/bundled-features/src/data-retention/resolve-tenant-preset.ts +17 -16
- package/packages/bundled-features/src/data-retention/resolver.ts +33 -32
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +71 -58
- package/packages/bundled-features/src/data-retention/schema/tenant-retention-override.ts +13 -11
- package/packages/bundled-features/src/delivery/changes.json +30 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +9 -3
- package/packages/bundled-features/src/delivery/db/queries/preferences.ts +2 -2
- package/packages/bundled-features/src/delivery/delivery-service.ts +35 -5
- package/packages/bundled-features/src/delivery/feature.ts +3 -3
- package/packages/bundled-features/src/delivery/handlers/log.query.ts +1 -1
- package/packages/bundled-features/src/delivery/handlers/preferences.query.ts +20 -1
- package/packages/bundled-features/src/delivery/handlers/unsubscribe-page-chrome.query.ts +1 -1
- package/packages/bundled-features/src/delivery/tables.ts +9 -8
- package/packages/bundled-features/src/delivery/unsubscribe-link.ts +3 -1
- package/packages/bundled-features/src/delivery/unsubscribe-page-strings.ts +1 -0
- package/packages/bundled-features/src/delivery/unsubscribe-page.ts +6 -0
- package/packages/bundled-features/src/delivery/unsubscribe.ts +40 -19
- package/packages/bundled-features/src/delivery/upsert-preference.ts +3 -4
- package/packages/bundled-features/src/document-ingest-foundation/entity.ts +5 -5
- package/packages/bundled-features/src/document-ingest-foundation/events.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/feature.ts +2 -2
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +1 -1
- package/packages/bundled-features/src/document-ingest-foundation/tenant-destroy-hook.ts +4 -4
- package/packages/bundled-features/src/feature-toggles/compose-tier-resolver.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/feature.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/global-feature-state-table.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/handlers/registered.query.ts +5 -5
- package/packages/bundled-features/src/feature-toggles/toggle-runtime.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +1 -1
- package/packages/bundled-features/src/feature-toggles/web/constants.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/feature.ts +2 -2
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-derivatives/presets.ts +1 -1
- package/packages/bundled-features/src/file-foundation/feature.ts +6 -6
- package/packages/bundled-features/src/file-foundation/index.ts +1 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +7 -1
- package/packages/bundled-features/src/file-provider-inmemory/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/env-named-feature.ts +0 -2
- package/packages/bundled-features/src/file-provider-s3/feature.ts +16 -16
- package/packages/bundled-features/src/file-provider-s3/lib/s3-provider.ts +6 -6
- package/packages/bundled-features/src/files/README.md +1 -1
- package/packages/bundled-features/src/files/feature.ts +6 -6
- package/packages/bundled-features/src/files-tenant-data/handlers/sweep-orphaned-derivatives.job.ts +5 -5
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +24 -26
- package/packages/bundled-features/src/files-tenant-data/index.ts +1 -1
- package/packages/bundled-features/src/folders/feature.ts +1 -1
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +2 -2
- package/packages/bundled-features/src/folders/web/tree.ts +5 -5
- package/packages/bundled-features/src/form-draft/changes.json +6 -0
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +3 -3
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +1 -1
- package/packages/bundled-features/src/form-draft/decrypt-draft.ts +4 -1
- package/packages/bundled-features/src/form-draft/entity.ts +3 -3
- package/packages/bundled-features/src/form-draft/feature.ts +4 -5
- package/packages/bundled-features/src/form-draft/handlers/discard.write.ts +1 -1
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +6 -6
- package/packages/bundled-features/src/form-draft/schemas.ts +3 -3
- package/packages/bundled-features/src/form-draft/web/constants.ts +0 -2
- package/packages/bundled-features/src/host-routing/README.md +1 -1
- package/packages/bundled-features/src/host-routing/changes.json +6 -0
- package/packages/bundled-features/src/host-routing/dispatch.ts +16 -17
- package/packages/bundled-features/src/host-routing/options.ts +2 -1
- package/packages/bundled-features/src/inbound-mail-foundation/aggregate-id.ts +19 -19
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +18 -0
- package/packages/bundled-features/src/inbound-mail-foundation/connect-routes.ts +4 -3
- package/packages/bundled-features/src/inbound-mail-foundation/constants.ts +30 -33
- package/packages/bundled-features/src/inbound-mail-foundation/entities.ts +47 -48
- package/packages/bundled-features/src/inbound-mail-foundation/events.ts +54 -50
- package/packages/bundled-features/src/inbound-mail-foundation/feature.ts +39 -39
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/account-state.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +25 -26
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/disconnect-account.write.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +50 -51
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +8 -8
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +10 -10
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +3 -4
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/update-account.write.ts +27 -15
- package/packages/bundled-features/src/inbound-mail-foundation/i18n.ts +0 -1
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +16 -15
- package/packages/bundled-features/src/inbound-mail-foundation/projection.ts +31 -29
- package/packages/bundled-features/src/inbound-mail-foundation/provider-factory.ts +9 -9
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +45 -41
- package/packages/bundled-features/src/inbound-mail-foundation/screens.ts +0 -3
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +14 -16
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +70 -70
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +101 -84
- package/packages/bundled-features/src/inbound-provider-imap/credential-document.ts +13 -13
- package/packages/bundled-features/src/inbound-provider-imap/feature.ts +42 -40
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +20 -19
- package/packages/bundled-features/src/inbound-provider-inmemory/feature.ts +21 -21
- package/packages/bundled-features/src/jobs/changes.json +13 -0
- package/packages/bundled-features/src/jobs/db/queries/retention.ts +2 -2
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +3 -3
- package/packages/bundled-features/src/jobs/events.ts +1 -1
- package/packages/bundled-features/src/jobs/feature.ts +20 -11
- package/packages/bundled-features/src/jobs/handlers/detail.query.ts +4 -4
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/projection-rebuild.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/reindex-entity.job.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/retry.write.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/tenant-failures.query.ts +45 -40
- package/packages/bundled-features/src/jobs/handlers/tenant-runs.query.ts +63 -59
- package/packages/bundled-features/src/jobs/job-instant.ts +1 -1
- package/packages/bundled-features/src/jobs/job-run-logger.ts +19 -19
- package/packages/bundled-features/src/jobs/job-run-table.ts +3 -3
- package/packages/bundled-features/src/jobs/screens.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-failure-table.ts +1 -1
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +1 -1
- package/packages/bundled-features/src/ledger/entity.ts +5 -5
- package/packages/bundled-features/src/ledger/feature.ts +3 -3
- package/packages/bundled-features/src/ledger/handlers/confirm-schedule-period.write.ts +3 -3
- package/packages/bundled-features/src/ledger/recurring.ts +12 -12
- package/packages/bundled-features/src/ledger/schemas.ts +1 -1
- package/packages/bundled-features/src/ledger/web/constants.ts +2 -2
- package/packages/bundled-features/src/ledger/web/index.ts +7 -7
- package/packages/bundled-features/src/legal-pages/README.md +1 -1
- package/packages/bundled-features/src/legal-pages/feature.ts +40 -41
- package/packages/bundled-features/src/legal-pages/markdown.ts +3 -3
- package/packages/bundled-features/src/legal-pages/web/client-plugin.ts +23 -24
- package/packages/bundled-features/src/legal-pages/web/index.ts +0 -2
- package/packages/bundled-features/src/mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/mail-foundation/feature.ts +31 -30
- package/packages/bundled-features/src/mail-transport-inmemory/feature.ts +27 -28
- package/packages/bundled-features/src/mail-transport-smtp/feature.ts +20 -20
- package/packages/bundled-features/src/managed-pages/branding.ts +6 -6
- package/packages/bundled-features/src/managed-pages/feature.ts +62 -62
- package/packages/bundled-features/src/managed-pages/handlers/by-slug.query.ts +5 -6
- package/packages/bundled-features/src/managed-pages/handlers/by-tenant-published.query.ts +8 -8
- package/packages/bundled-features/src/managed-pages/handlers/set.write.ts +5 -7
- package/packages/bundled-features/src/managed-pages/screens/page-screens.ts +11 -11
- package/packages/bundled-features/src/managed-pages/seeding.ts +8 -8
- package/packages/bundled-features/src/managed-pages/table.ts +16 -15
- package/packages/bundled-features/src/managed-pages/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/managed-pages/web/i18n.ts +1 -1
- package/packages/bundled-features/src/notes-history/entity.ts +4 -4
- package/packages/bundled-features/src/notes-history/feature.ts +2 -2
- package/packages/bundled-features/src/notes-history-user-data/hooks.ts +1 -1
- package/packages/bundled-features/src/page-render/layout.ts +9 -9
- package/packages/bundled-features/src/page-render/markdown.ts +11 -11
- package/packages/bundled-features/src/page-render/security-headers.ts +7 -6
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +9 -6
- package/packages/bundled-features/src/personal-access-tokens/roles.ts +2 -2
- package/packages/bundled-features/src/readiness/handlers/status.query.ts +3 -3
- package/packages/bundled-features/src/renderer-foundation/api.ts +12 -13
- package/packages/bundled-features/src/renderer-foundation/constants.ts +2 -2
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/types.ts +11 -12
- package/packages/bundled-features/src/renderer-pdf/changes.json +6 -0
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +27 -10
- package/packages/bundled-features/src/secrets/feature.ts +9 -9
- package/packages/bundled-features/src/secrets/web/constants.ts +2 -2
- package/packages/bundled-features/src/seo/feature.ts +3 -3
- package/packages/bundled-features/src/sessions/constants.ts +1 -1
- package/packages/bundled-features/src/sessions/feature.ts +16 -11
- package/packages/bundled-features/src/sessions/handlers/revoke-all-for-user.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke-all-others.write.ts +2 -2
- package/packages/bundled-features/src/sessions/handlers/revoke.write.ts +1 -1
- package/packages/bundled-features/src/sessions/schema/user-session.ts +4 -4
- package/packages/bundled-features/src/sessions/screens.ts +2 -2
- package/packages/bundled-features/src/sessions/session-callbacks.ts +2 -2
- package/packages/bundled-features/src/sessions/session-revoked-event.ts +2 -2
- package/packages/bundled-features/src/shared/assert-no-where-rule-in-ownership-write.ts +1 -1
- package/packages/bundled-features/src/shared/decrypt-stored-pii.ts +11 -1
- package/packages/bundled-features/src/shared/encrypt-for-direct-write.ts +1 -1
- package/packages/bundled-features/src/shared/index.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +10 -3
- package/packages/bundled-features/src/shared/map-with-concurrency.ts +5 -4
- package/packages/bundled-features/src/shared/parent-visibility.ts +2 -2
- package/packages/bundled-features/src/shared/password-hashing.ts +1 -1
- package/packages/bundled-features/src/shared/signup-handover.ts +1 -2
- package/packages/bundled-features/src/shared/single-use-token-store.ts +1 -1
- package/packages/bundled-features/src/step-dispatcher/changes.json +6 -0
- package/packages/bundled-features/src/step-dispatcher/feature.ts +1 -0
- package/packages/bundled-features/src/subscription-dodo/changes.json +14 -0
- package/packages/bundled-features/src/subscription-dodo/client.ts +45 -0
- package/packages/bundled-features/src/subscription-dodo/constants.ts +24 -0
- package/packages/bundled-features/src/subscription-dodo/feature.ts +207 -0
- package/packages/bundled-features/src/subscription-dodo/index.ts +16 -0
- package/packages/bundled-features/src/subscription-dodo/plugin-methods.ts +345 -0
- package/packages/bundled-features/src/subscription-dodo/runtime.ts +126 -0
- package/packages/bundled-features/src/subscription-dodo/verify-webhook.ts +262 -0
- package/packages/bundled-features/src/subscription-mollie/constants.ts +15 -17
- package/packages/bundled-features/src/subscription-mollie/feature.ts +41 -42
- package/packages/bundled-features/src/subscription-mollie/index.ts +3 -3
- package/packages/bundled-features/src/subscription-mollie/plugin-methods.ts +17 -16
- package/packages/bundled-features/src/subscription-mollie/verify-webhook.ts +34 -34
- package/packages/bundled-features/src/subscription-stripe/changes.json +12 -0
- package/packages/bundled-features/src/subscription-stripe/constants.ts +4 -6
- package/packages/bundled-features/src/subscription-stripe/feature.ts +45 -43
- package/packages/bundled-features/src/subscription-stripe/index.ts +6 -6
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +22 -27
- package/packages/bundled-features/src/subscription-stripe/runtime.ts +9 -8
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +62 -64
- package/packages/bundled-features/src/tags/feature.ts +3 -3
- package/packages/bundled-features/src/tags/screens.ts +1 -1
- package/packages/bundled-features/src/template-resolver/api.ts +41 -14
- package/packages/bundled-features/src/template-resolver/changes.json +48 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +1 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +27 -5
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/document-base.ts +3 -2
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +8 -7
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -20
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +15 -5
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +2 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +3 -3
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-counts.query.ts +9 -5
- package/packages/bundled-features/src/template-resolver/handlers/tenant-override-selection.ts +8 -6
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +44 -5
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +3 -4
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +4 -5
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +37 -26
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +7 -2
- package/packages/bundled-features/src/template-resolver/seeding.ts +36 -18
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +22 -4
- package/packages/bundled-features/src/template-resolver/table.ts +13 -14
- package/packages/bundled-features/src/template-resolver/template-delivery-policy.ts +0 -11
- package/packages/bundled-features/src/template-resolver/template-meta.ts +12 -4
- package/packages/bundled-features/src/template-resolver/template-preview.ts +11 -3
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +2 -2
- package/packages/bundled-features/src/template-resolver/web/constants.ts +7 -5
- package/packages/bundled-features/src/template-resolver/web/index.ts +7 -0
- package/packages/bundled-features/src/template-resolver/web/mail-layout-constants.ts +8 -0
- package/packages/bundled-features/src/tenant/changes.json +6 -0
- package/packages/bundled-features/src/tenant/feature.ts +7 -7
- package/packages/bundled-features/src/tenant/handlers/member-directory.query.ts +10 -4
- package/packages/bundled-features/src/tenant/handlers/members.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/memberships.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +1 -1
- package/packages/bundled-features/src/tenant/handlers/tenant-directory.query.ts +8 -5
- package/packages/bundled-features/src/tenant/invitation-table.ts +37 -40
- package/packages/bundled-features/src/tenant/membership-table.ts +3 -3
- package/packages/bundled-features/src/tenant/schema/tenant.ts +7 -9
- package/packages/bundled-features/src/tenant/screens.ts +1 -1
- package/packages/bundled-features/src/tenant/seeding.ts +19 -18
- package/packages/bundled-features/src/tenant/testing.ts +5 -6
- package/packages/bundled-features/src/tenant/web/constants.ts +2 -2
- package/packages/bundled-features/src/tenant-handover/events.ts +5 -7
- package/packages/bundled-features/src/tenant-handover/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/grant.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/handlers/claim.write.ts +1 -1
- package/packages/bundled-features/src/tenant-handover/move-entity-graph.ts +8 -9
- package/packages/bundled-features/src/tenant-handover/signup-handover-provider.ts +1 -2
- package/packages/bundled-features/src/tenant-handover/transfer-graph.ts +4 -5
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/changes.json +6 -0
- package/packages/bundled-features/src/tenant-lifecycle/constants.ts +5 -0
- package/packages/bundled-features/src/tenant-lifecycle/feature.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/run-tenant-destroy.ts +7 -3
- package/packages/bundled-features/src/tenant-lifecycle/stages.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/constants.ts +1 -1
- package/packages/bundled-features/src/tenant-settings/tenant-defaults.ts +12 -5
- package/packages/bundled-features/src/tier-engine/aggregate-id.ts +15 -16
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/compose-app.ts +14 -14
- package/packages/bundled-features/src/tier-engine/entity.ts +18 -19
- package/packages/bundled-features/src/tier-engine/feature.ts +88 -87
- package/packages/bundled-features/src/tier-engine/handlers/active-tier.query.ts +6 -7
- package/packages/bundled-features/src/tier-engine/handlers/get-tenant-tier.query.ts +4 -4
- package/packages/bundled-features/src/tier-engine/handlers/set-tenant-tier.write.ts +12 -9
- package/packages/bundled-features/src/tier-engine/tier-resolver.ts +2 -2
- package/packages/bundled-features/src/tier-engine/trial.ts +13 -13
- package/packages/bundled-features/src/tier-engine/web/constants.ts +3 -3
- package/packages/bundled-features/src/tier-engine/web/index.ts +4 -4
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +8 -8
- package/packages/bundled-features/src/user/handlers/create.write.ts +4 -4
- package/packages/bundled-features/src/user/schema/user.ts +45 -44
- package/packages/bundled-features/src/user/seeding.ts +40 -40
- package/packages/bundled-features/src/user/testing.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/audit-download.ts +27 -28
- package/packages/bundled-features/src/user-data-rights/boot-checks.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/deletion-token.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/feature.ts +112 -113
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +9 -9
- package/packages/bundled-features/src/user-data-rights/handlers/confirm-deletion-by-token.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-job.query.ts +29 -30
- package/packages/bundled-features/src/user-data-rights/handlers/download-by-token.query.ts +40 -42
- package/packages/bundled-features/src/user-data-rights/handlers/export-status.query.ts +8 -9
- package/packages/bundled-features/src/user-data-rights/handlers/lift-restriction.write.ts +7 -8
- package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/handlers/my-audit-log.query.ts +4 -4
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts +30 -29
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +15 -18
- package/packages/bundled-features/src/user-data-rights/handlers/request-export.write.ts +42 -41
- package/packages/bundled-features/src/user-data-rights/handlers/restrict-account.write.ts +22 -19
- package/packages/bundled-features/src/user-data-rights/handlers/run-forget-cleanup.write.ts +8 -8
- package/packages/bundled-features/src/user-data-rights/index.ts +8 -10
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +3 -3
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +24 -27
- package/packages/bundled-features/src/user-data-rights/run-export-jobs.ts +169 -280
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +87 -108
- package/packages/bundled-features/src/user-data-rights/run-user-export.ts +67 -69
- package/packages/bundled-features/src/user-data-rights/schema/download-attempt.ts +11 -11
- package/packages/bundled-features/src/user-data-rights/schema/download-token.ts +37 -41
- package/packages/bundled-features/src/user-data-rights/schema/export-job.ts +65 -67
- package/packages/bundled-features/src/user-data-rights/token-helpers.ts +21 -25
- package/packages/bundled-features/src/user-data-rights/web/client-plugin.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/web/confirm-deletion-screen.tsx +6 -5
- package/packages/bundled-features/src/user-data-rights/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/index.ts +5 -5
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +14 -13
- package/packages/bundled-features/src/user-data-rights/web/public-deletion-gate.tsx +13 -12
- package/packages/bundled-features/src/user-data-rights/web/request-deletion-screen.tsx +6 -6
- package/packages/bundled-features/src/user-data-rights/zip-path.ts +41 -40
- package/packages/bundled-features/src/user-data-rights-defaults/feature.ts +15 -15
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.ts +3 -5
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/file-ref.userdata-hook.ts +45 -51
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/job-run.userdata-hook.ts +2 -2
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/notification-preference.userdata-hook.ts +1 -1
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user-session.userdata-hook.ts +3 -3
- package/packages/bundled-features/src/user-data-rights-defaults/hooks/user.userdata-hook.ts +32 -31
- package/packages/bundled-features/src/user-profile/handlers/change-email.write.ts +13 -13
- package/packages/bundled-features/src/user-profile/index.ts +3 -3
- package/packages/bundled-features/src/user-profile/web/client-plugin.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/constants.ts +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +1 -1
- package/packages/bundled-features/src/waitlist/changes.json +18 -0
- package/packages/bundled-features/src/waitlist/handlers/export-purge.write.ts +2 -1
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +5 -5
- package/packages/bundled-features/src/waitlist/import.ts +5 -0
- package/packages/bundled-features/src/waitlist/legacy-migrate.ts +22 -3
- package/packages/bundled-features/src/waitlist/link-on-signup.ts +20 -10
- package/packages/bundled-features/src/waitlist/options.ts +3 -0
- package/packages/bundled-features/src/workflow-runner/aggregate-id.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/event-subscriber.ts +4 -5
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +4 -15
- package/packages/bundled-features/src/workflow-runner/feature.ts +2 -2
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +4 -4
- package/packages/bundled-features/src/workflow-runner/pending-projection.ts +6 -7
- package/packages/bundled-features/src/workflow-runner/runner.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/tables.ts +6 -6
- package/packages/bundled-features/src/workflow-runner/workflow-registry.ts +1 -1
- package/samples/apps/styleguide/src/features/widgets/i18n.ts +11 -0
- package/samples/apps/styleguide/src/features/widgets/web/Widgets.tsx +24 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-constants.ts +5 -0
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +90 -15
- package/samples/apps/use-all-bundled/e2e-billing-gallery/server.ts +16 -6
- package/samples/apps/use-all-bundled/feature-manifest.json +120 -3
- package/samples/apps/use-all-bundled/src/run-config.ts +9 -3
- package/samples/recipes/auth-mfa/README.md +5 -0
- package/samples/recipes/auth-mfa/package.json +5 -2
- package/samples/recipes/auth-mfa/src/web/confirmation-code-step.tsx +49 -0
- package/samples/recipes/auth-mfa/tsconfig.json +8 -0
- package/samples/recipes/env-composition/README.md +3 -1
- package/samples/recipes/env-composition/src/env.ts +2 -0
- package/samples/recipes/legal-pages/README.md +1 -1
- package/samples/recipes/rate-limiting/README.md +18 -0
- package/samples/recipes/rate-limiting/src/feature.ts +12 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/stored-user-email.ts +0 -12
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
// GET /api/query → user-data-rights:query:download-by-token
|
|
1
|
+
// GET /api/query → user-data-rights:query:download-by-token.
|
|
2
2
|
//
|
|
3
|
-
// **Magic-
|
|
4
|
-
// `#token=<plain>` (URL fragment
|
|
5
|
-
//
|
|
3
|
+
// **Magic-link path** (anonymous): The user clicks an email link with
|
|
4
|
+
// `#token=<plain>` (URL fragment). The worker persisted the token hash in the database
|
|
5
|
+
// when marking the job done. Verification:
|
|
6
6
|
//
|
|
7
7
|
// 1. hashDownloadToken(plain) → fetchOne in download-tokens
|
|
8
|
-
// 2. expiresAt > now (
|
|
8
|
+
// 2. expiresAt > now (multiple uses within the TTL)
|
|
9
9
|
// 3. job.status === "done"
|
|
10
|
-
// 4. job.downloadStorageKey != null (storage
|
|
11
|
-
// 5. provider.getSignedUrl
|
|
12
|
-
// 6. Audit
|
|
10
|
+
// 4. job.downloadStorageKey != null (storage was not cleared)
|
|
11
|
+
// 5. provider.getSignedUrl is required (501 when the provider is local-fs)
|
|
12
|
+
// 6. Audit update: useCount + 1, lastUsedAt, IP, UA (best-effort, race-tolerant)
|
|
13
13
|
// 7. Return {url, expiresAt}
|
|
14
14
|
//
|
|
15
|
-
// **
|
|
16
|
-
// - Token-
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// Tenant-Filter nötig.
|
|
15
|
+
// **Security:**
|
|
16
|
+
// - Token-hash comparison via DB-fetchOne is not constant-time, but timing attacks
|
|
17
|
+
// against SHA256 bytes require >>10k requests plus stable latency and are not
|
|
18
|
+
// exploitable in a web-app context.
|
|
19
|
+
// - Invalid tokens return 404 without leaking existence, using the same code path
|
|
20
|
+
// as a token that was not found.
|
|
21
|
+
// - The token is tenant-agnostic: UUID + SHA256 makes it globally unique, so no
|
|
22
|
+
// tenant filter is needed.
|
|
24
23
|
//
|
|
25
|
-
// **r.httpRoute
|
|
26
|
-
//
|
|
27
|
-
//
|
|
24
|
+
// **r.httpRoute wrapper** (see feature.ts) redirects to signedUrl with a 302. The user
|
|
25
|
+
// clicks the email link once, the browser follows the redirect, and the download
|
|
26
|
+
// starts. This query handler returns only the JSON.
|
|
28
27
|
|
|
29
28
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
30
29
|
import {
|
|
@@ -43,7 +42,7 @@ import { hashDownloadToken } from "../token-helpers.ts";
|
|
|
43
42
|
|
|
44
43
|
type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
45
44
|
|
|
46
|
-
const SIGNED_URL_TTL_SECONDS = 300; // 5 min —
|
|
45
|
+
const SIGNED_URL_TTL_SECONDS = 300; // 5 min — short enough to limit replay, long enough for slow connections
|
|
47
46
|
|
|
48
47
|
const DOWNLOAD_BY_TOKEN_REASON =
|
|
49
48
|
"the download token, its job and audit rows are keyed by token hash / job id, not the caller's tenant — the anonymous magic-link path has no tenant context at all";
|
|
@@ -82,12 +81,11 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
82
81
|
personalData: "public-intake",
|
|
83
82
|
},
|
|
84
83
|
agent: { expose: false },
|
|
85
|
-
// Brute-
|
|
86
|
-
// 32-byte
|
|
87
|
-
// defense
|
|
88
|
-
// 30 attempts/min/IP
|
|
89
|
-
//
|
|
90
|
-
// Memory `feedback_security_default_on`.
|
|
84
|
+
// Brute-force protection for token-hash probing. This anonymous endpoint uses a
|
|
85
|
+
// 32-byte random token with a 256-bit search space, but rate limiting provides
|
|
86
|
+
// defense in depth and protects against storm patterns that generate database load.
|
|
87
|
+
// 30 attempts/min/IP allows legitimate users several retries after connection
|
|
88
|
+
// failures while blocking automated probing loops.
|
|
91
89
|
rateLimit: { per: "ip+handler", limit: 30, windowSeconds: 60 },
|
|
92
90
|
escapeHatch: {
|
|
93
91
|
grants: ["unsafeRaw"],
|
|
@@ -105,9 +103,9 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
105
103
|
});
|
|
106
104
|
|
|
107
105
|
if (!tokenRow) {
|
|
108
|
-
// Invalid token — 404
|
|
109
|
-
//
|
|
110
|
-
//
|
|
106
|
+
// Invalid token — 404 without leaking existence. Use a generic NotFoundError
|
|
107
|
+
// so all failure paths have the same external shape, preventing probing that
|
|
108
|
+
// distinguishes "Token does not exist" from "Job has failed".
|
|
111
109
|
throw new NotFoundError("export-download", undefined, {
|
|
112
110
|
i18nKey: "userDataRights.errors.download.notFound",
|
|
113
111
|
});
|
|
@@ -116,17 +114,17 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
116
114
|
const auditIp = query.payload.auditMeta?.ip ?? null;
|
|
117
115
|
const auditUa = query.payload.auditMeta?.userAgent ?? null;
|
|
118
116
|
|
|
119
|
-
// Step 2: TTL
|
|
117
|
+
// Step 2: TTL check.
|
|
120
118
|
//
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
119
|
+
// 410 Gone would be semantically right (the download existed and is now
|
|
120
|
+
// gone), but the framework has no GoneError class. NotFoundError with
|
|
121
|
+
// i18nKey "expired" is the compromise: the UI renders by i18nKey, not by
|
|
122
|
+
// HTTP status, so the user sees "Your download has expired" instead of a
|
|
123
|
+
// generic "not found".
|
|
126
124
|
if (tokenRow.expiresAt.epochMilliseconds <= now.epochMilliseconds) {
|
|
127
|
-
//
|
|
128
|
-
// tenantId
|
|
129
|
-
//
|
|
125
|
+
// Auditing cannot be skipped yet because jobRow has not been loaded and the
|
|
126
|
+
// tenantId is unknown. Load the job to obtain audit context; if it is also
|
|
127
|
+
// missing, skipping the audit is acceptable (best-effort).
|
|
130
128
|
const jobForAudit = await fetchOne<{ requestedFromTenantId: string }>(
|
|
131
129
|
runner,
|
|
132
130
|
exportJobsTable,
|
|
@@ -240,9 +238,9 @@ export const downloadByTokenQuery = defineQueryHandler({
|
|
|
240
238
|
now.epochMilliseconds + SIGNED_URL_TTL_SECONDS * 1000,
|
|
241
239
|
);
|
|
242
240
|
|
|
243
|
-
// Step 6:
|
|
244
|
-
//
|
|
245
|
-
//
|
|
241
|
+
// Step 6: Best-effort audit update. auditMeta comes from the httpRoute wrapper
|
|
242
|
+
// (trusted source). Direct API callers can falsify it, but the audit data is not
|
|
243
|
+
// security-sensitive.
|
|
246
244
|
await recordDownloadUse({
|
|
247
245
|
db: runner,
|
|
248
246
|
tokenId: tokenRow.id,
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
// GET /api/user/export-status
|
|
1
|
+
// GET /api/user/export-status — user polling.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
3
|
+
// Returns the calling user's most recent ExportJob (active jobs first,
|
|
4
|
+
// otherwise the newest done/failed job).
|
|
5
5
|
//
|
|
6
|
-
// **Cross-User-Isolation:**
|
|
7
|
-
//
|
|
8
|
-
// uses ctx.db.unsafeRaw
|
|
9
|
-
// "Cross-Tenant-Semantik").
|
|
6
|
+
// **Cross-User-Isolation:** The filter is `userId === query.user.id`, so no
|
|
7
|
+
// user can view another user's job status, even by guessing an ID. The
|
|
8
|
+
// pre-check uses ctx.db.unsafeRaw because ExportJob is tenant-agnostic.
|
|
10
9
|
//
|
|
11
|
-
// **Read-Only-Endpoint:**
|
|
12
|
-
//
|
|
10
|
+
// **Read-Only-Endpoint:** Polling only; it does not change state. The endpoint
|
|
11
|
+
// is idempotent and cacheable. The UI typically polls every 2-5s while running.
|
|
13
12
|
|
|
14
13
|
import { selectMany } from "@cosmicdrift/kumiko-framework/db";
|
|
15
14
|
import { defineQueryHandler } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -8,7 +8,7 @@ import { updateUserLifecycle } from "../lib/update-user-lifecycle.ts";
|
|
|
8
8
|
const LIFT_RESTRICTION_ESCAPE_HATCH_REASON =
|
|
9
9
|
"checks the target user's membership in the admin's tenant and appends the user lifecycle status change on the SYSTEM_TENANT_ID user stream, both via DbRunner helpers outside the admin's own tenant scope.";
|
|
10
10
|
|
|
11
|
-
// POST /api/user/lift-restriction
|
|
11
|
+
// POST /api/user/lift-restriction — GDPR Art. 18 Reverse.
|
|
12
12
|
//
|
|
13
13
|
// Operator-only. A Restricted user's own session is unconditionally
|
|
14
14
|
// rejected by sessionChecker (BLOCKED_STATUSES) the moment their status
|
|
@@ -16,16 +16,15 @@ const LIFT_RESTRICTION_ESCAPE_HATCH_REASON =
|
|
|
16
16
|
// endpoint. There is no self-service path today, so lifting a restriction
|
|
17
17
|
// always targets someone else's account by id.
|
|
18
18
|
//
|
|
19
|
-
// Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even
|
|
20
|
-
//
|
|
21
|
-
// from tenant A could unrestrict/reactivate a user who has never been a
|
|
22
|
-
//
|
|
23
|
-
// the
|
|
24
|
-
// `event.user.tenantId` (row existence only — no active-status field).
|
|
19
|
+
// Tenant scope: `access.admin` includes TenantAdmin (tenant-scoped) even though the User-entity is
|
|
20
|
+
// global — see user-data-rights.md "Cross-Tenant Semantics". Without a membership check, a
|
|
21
|
+
// TenantAdmin from tenant A could unrestrict/reactivate a user who has never been a member of
|
|
22
|
+
// tenant A. Only SystemAdmin (platform-wide) skips the check; the target must have a membership row
|
|
23
|
+
// in the acting admin's `event.user.tenantId` (row existence only — no active-status field).
|
|
25
24
|
//
|
|
26
25
|
// State-Transitions:
|
|
27
26
|
// Restricted → Active ✓
|
|
28
|
-
// Active → ... ✗ 422 not_restricted (
|
|
27
|
+
// Active → ... ✗ 422 not_restricted (Idempotency Guard)
|
|
29
28
|
// DeletionRequested → ... ✗ 422 not_restricted
|
|
30
29
|
// Deleted → ... ✗ 422 not_restricted
|
|
31
30
|
export const liftRestrictionWrite = defineWriteHandler({
|
package/packages/bundled-features/src/user-data-rights/handlers/list-download-attempts.query.ts
CHANGED
|
@@ -5,8 +5,8 @@ import * as z from "zod";
|
|
|
5
5
|
import { decryptStoredPii } from "../../shared/index.ts";
|
|
6
6
|
import { downloadAttemptsTable } from "../schema/download-attempt.ts";
|
|
7
7
|
|
|
8
|
-
// Operator
|
|
9
|
-
//
|
|
8
|
+
// Operator query for invalid download attempts. Provides tenant-isolated visibility for data
|
|
9
|
+
// protection officers investigating brute-force probing.
|
|
10
10
|
|
|
11
11
|
const MAX_LIMIT = 100;
|
|
12
12
|
const REQUEST_ID = "user-data-rights:list-download-attempts";
|
|
@@ -5,10 +5,10 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
|
5
5
|
import * as z from "zod";
|
|
6
6
|
import { redactPiiCiphertext } from "../../shared/index.ts";
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
8
|
+
// GDPR Art. 15 self-disclosure: users may read only their own audit log.
|
|
9
|
+
// `createdBy = ctx.user.id` is hard-coded to prevent cross-user snooping;
|
|
10
|
+
// no `tenantId` filter is applied because the user is entitled to an
|
|
11
|
+
// account-wide view across all memberships, as in the forget path.
|
|
12
12
|
const MAX_LIMIT = 100;
|
|
13
13
|
|
|
14
14
|
const MY_AUDIT_LOG_REASON =
|
package/packages/bundled-features/src/user-data-rights/handlers/request-deletion-by-email.write.ts
CHANGED
|
@@ -4,20 +4,20 @@ import { USER_STATUS, userTable } from "../../user/index.ts";
|
|
|
4
4
|
import { signDeletionToken } from "../deletion-token.ts";
|
|
5
5
|
import { updateUserLifecycle } from "../lib/update-user-lifecycle.ts";
|
|
6
6
|
|
|
7
|
-
// TTL
|
|
8
|
-
//
|
|
7
|
+
// Verification-link TTL: 60 minutes — long enough for an email round trip,
|
|
8
|
+
// but short enough that an intercepted link does not remain valid indefinitely.
|
|
9
9
|
const DELETION_VERIFY_TTL_MINUTES = 60;
|
|
10
10
|
|
|
11
|
-
// Apex
|
|
12
|
-
//
|
|
13
|
-
//
|
|
11
|
+
// Sends the Apex magic link for an anonymous deletion request. The handler
|
|
12
|
+
// does NOT return the link (content-enumeration safety); it invokes the
|
|
13
|
+
// callback directly, analogous to sendDeletionRequestedEmail.
|
|
14
14
|
//
|
|
15
|
-
//
|
|
16
|
-
// delivery.notify / Job),
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
15
|
+
// IMPORTANT (timing oracle): The callback MUST be non-blocking (enqueue, e.g.
|
|
16
|
+
// delivery.notify / Job), not send synchronously. It runs only for an
|
|
17
|
+
// existing active user; a synchronous send makes response latency measurably
|
|
18
|
+
// longer for real accounts than for nonexistent ones, enabling enumeration by
|
|
19
|
+
// timing. A fast enqueue effectively equalizes this.
|
|
20
|
+
// Complete timing equalization (always performing equivalent work) is deferred.
|
|
21
21
|
export type SendDeletionVerificationEmailFn = (args: {
|
|
22
22
|
readonly email: string;
|
|
23
23
|
readonly verifyUrl: string;
|
|
@@ -25,12 +25,12 @@ export type SendDeletionVerificationEmailFn = (args: {
|
|
|
25
25
|
}) => Promise<void>;
|
|
26
26
|
|
|
27
27
|
export type RequestDeletionByEmailOptions = {
|
|
28
|
-
/** HMAC
|
|
29
|
-
*
|
|
28
|
+
/** HMAC secret used to sign the verification token. Without a secret, the flow
|
|
29
|
+
* is disabled (the handler silently responds with success and no link). */
|
|
30
30
|
readonly deletionTokenSecret?: string;
|
|
31
|
-
/**
|
|
32
|
-
* "https://app.example.com/delete-account/confirm".
|
|
33
|
-
*
|
|
31
|
+
/** Base URL of the Apex confirmation screen, e.g.
|
|
32
|
+
* "https://app.example.com/delete-account/confirm". The handler appends the
|
|
33
|
+
* token as a URL fragment (`#token=<token>`). Without a URL, no link is sent. */
|
|
34
34
|
readonly deletionVerifyUrl?: string;
|
|
35
35
|
readonly sendDeletionVerificationEmail?: SendDeletionVerificationEmailFn;
|
|
36
36
|
};
|
|
@@ -38,7 +38,7 @@ export type RequestDeletionByEmailOptions = {
|
|
|
38
38
|
// Token goes in the URL fragment, not a query param — fragments never leave
|
|
39
39
|
// the browser (not sent to the server, so they never land in proxy/access
|
|
40
40
|
// logs, unlike `?token=`). Same convention as the export-download link
|
|
41
|
-
// (feature.ts
|
|
41
|
+
// (feature.ts). Preserves any existing query params on `base`
|
|
42
42
|
// (`?lang=de` stays a query param; only the token is a fragment).
|
|
43
43
|
const APPEND_LIFECYCLE_EVENT_REASON =
|
|
44
44
|
"appends the user lifecycle event on the SYSTEM_TENANT_ID user stream";
|
|
@@ -49,14 +49,14 @@ export function buildDeletionVerifyUrl(base: string, token: string): string {
|
|
|
49
49
|
return url.toString();
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
// (
|
|
52
|
+
// Anonymous Apex flow step 1: request account deletion by email.
|
|
53
|
+
// This is especially relevant for GDPR when the user can no longer log in
|
|
54
|
+
// (lockout). Email → magic link → confirm-deletion-by-token.
|
|
55
55
|
//
|
|
56
|
-
// Enumeration-safe (content):
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// Timing
|
|
56
|
+
// Enumeration-safe (content): always returns the same success shape,
|
|
57
|
+
// regardless of whether the email exists, the user is active, or the flow is
|
|
58
|
+
// configured. A link is generated only for an existing, active user.
|
|
59
|
+
// Timing safety depends on the non-blocking callback (see the type doc above).
|
|
60
60
|
export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmailOptions = {}) {
|
|
61
61
|
return defineWriteHandler({
|
|
62
62
|
name: "request-deletion-by-email",
|
|
@@ -66,7 +66,7 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
|
|
|
66
66
|
"Mails an account-deletion verification link to the given address, for a user who wants to be erased but can no longer log in; it answers identically whether or not the address belongs to an active account and never returns the link.",
|
|
67
67
|
// Mails a deletion link to any address the caller names.
|
|
68
68
|
agent: { risk: "high" },
|
|
69
|
-
// Defense
|
|
69
|
+
// Defense in depth against email probing on the anonymous endpoint.
|
|
70
70
|
rateLimit: { per: "ip+handler", limit: 10, windowSeconds: 60 },
|
|
71
71
|
escapeHatch: {
|
|
72
72
|
grants: ["unsafeRaw", "globalWrites"],
|
|
@@ -75,7 +75,8 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
|
|
|
75
75
|
handler: async (event, ctx) => {
|
|
76
76
|
const success = { isSuccess: true as const, data: { kind: "requested" as const } };
|
|
77
77
|
|
|
78
|
-
// not
|
|
78
|
+
// Safe when not configured: without a secret or URL, no link is sent, but the response is
|
|
79
|
+
// unchanged.
|
|
79
80
|
if (!opts.deletionTokenSecret || !opts.deletionVerifyUrl) return success;
|
|
80
81
|
|
|
81
82
|
const userRow = await ctx.db
|
|
@@ -88,9 +89,9 @@ export function createRequestDeletionByEmailHandler(opts: RequestDeletionByEmail
|
|
|
88
89
|
return success;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
// Replay
|
|
92
|
-
// user
|
|
93
|
-
//
|
|
92
|
+
// Replay protection: each request gets a fresh requestId, which is stored on
|
|
93
|
+
// the user row and incorporated into the token HMAC purpose. cancel
|
|
94
|
+
// nulls it, so a token replayed after cancellation no longer verifies.
|
|
94
95
|
const requestId = crypto.randomUUID();
|
|
95
96
|
await updateUserLifecycle(ctx.db.unsafeRaw(), userRow["id"], {
|
|
96
97
|
pendingDeletionRequestId: requestId,
|
|
@@ -9,13 +9,12 @@ import {
|
|
|
9
9
|
} from "../lib/default-mailers.ts";
|
|
10
10
|
import { resolveGracePeriod, startDeletionGracePeriod } from "./deletion-grace-period.ts";
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
// password-reset
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
// klicken muessen.
|
|
12
|
+
// Sends a best-effort email after the deletion-requested status flip, following the
|
|
13
|
+
// password-reset callback pattern in auth-routes.ts. Send failures are caught and logged
|
|
14
|
+
// with console.warn so the status change remains successful: the user's action is complete
|
|
15
|
+
// once the database row is updated, while email delivery is confirmation rather than a
|
|
16
|
+
// prerequisite. This prevents users from having to click "Delete account" again when
|
|
17
|
+
// email delivery fails.
|
|
19
18
|
export type SendDeletionRequestedEmailFn = (args: {
|
|
20
19
|
readonly userId: string;
|
|
21
20
|
readonly userEmail: string;
|
|
@@ -28,16 +27,15 @@ export type SendDeletionRequestedEmailFn = (args: {
|
|
|
28
27
|
|
|
29
28
|
export type RequestDeletionOptions = {
|
|
30
29
|
readonly sendDeletionRequestedEmail?: SendDeletionRequestedEmailFn;
|
|
31
|
-
/** Branding
|
|
32
|
-
*
|
|
30
|
+
/** Branding for the default email when sendDeletionRequestedEmail is not set
|
|
31
|
+
* and mail-foundation is mounted. */
|
|
33
32
|
readonly mailDefaults?: GdprMailDefaults;
|
|
34
33
|
};
|
|
35
34
|
|
|
36
|
-
// POST /api/user/request-deletion
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
// user-data-rights.md "Cross-Tenant-Semantik".
|
|
35
|
+
// POST /api/user/request-deletion — GDPR Art. 17 erasure request.
|
|
36
|
+
// Changes status=Active → deletionRequested and sets gracePeriodEnd from the compliance
|
|
37
|
+
// profile using the shared startDeletionGracePeriod logic. The operation applies to the
|
|
38
|
+
// account-wide user row, so the user identity is global across tenants.
|
|
41
39
|
export function createRequestDeletionHandler(opts: RequestDeletionOptions = {}) {
|
|
42
40
|
return defineWriteHandler({
|
|
43
41
|
name: "request-deletion",
|
|
@@ -78,8 +76,7 @@ export function createRequestDeletionHandler(opts: RequestDeletionOptions = {})
|
|
|
78
76
|
? makeDefaultDeletionRequestedEmail(ctx.notify, opts.mailDefaults)
|
|
79
77
|
: undefined);
|
|
80
78
|
|
|
81
|
-
// Best-effort
|
|
82
|
-
// killen — siehe Type-Doc oben.
|
|
79
|
+
// Best-effort email notification: a send failure must not abort the write.
|
|
83
80
|
if (send && userEmail.length > 0) {
|
|
84
81
|
try {
|
|
85
82
|
await send({
|
|
@@ -97,8 +94,8 @@ export function createRequestDeletionHandler(opts: RequestDeletionOptions = {})
|
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
//
|
|
101
|
-
//
|
|
97
|
+
// Returns the absolute gracePeriodEnd timestamp so the frontend, audit, and cleanup
|
|
98
|
+
// runner all use the same value.
|
|
102
99
|
return {
|
|
103
100
|
isSuccess: true as const,
|
|
104
101
|
data: {
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
// POST /api/user/request-export
|
|
1
|
+
// POST /api/user/request-export — GDPR Art. 15 and 20 trigger.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// (
|
|
3
|
+
// The user triggers an export job, persisted through
|
|
4
|
+
// createEventStoreExecutor (event-sourced pattern; ES has no CRUD).
|
|
5
|
+
// Idempotency uses two stages:
|
|
5
6
|
//
|
|
6
|
-
// 1) **App-side-
|
|
7
|
-
// (status pending|running)
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// 1) **App-side pre-check (primary path):** fetch active jobs with
|
|
8
|
+
// (status pending|running) for the userId. If one exists, return
|
|
9
|
+
// `{jobId, isExisting: true}` without creating an event, so a click
|
|
10
|
+
// storm produces one audit event rather than N.
|
|
10
11
|
//
|
|
11
|
-
// 2) **DB
|
|
12
|
-
//
|
|
13
|
-
// constraint_name === "read_export_jobs_one_active_per_user".
|
|
14
|
-
// Catch
|
|
15
|
-
// Window <1ms, in Production extrem selten.
|
|
12
|
+
// 2) **DB constraint as race protection (secondary path):** if a parallel
|
|
13
|
+
// click occurs between fetchOne and crud.create, the transaction raises
|
|
14
|
+
// 23505 with constraint_name === "read_export_jobs_one_active_per_user".
|
|
15
|
+
// Catch, re-fetch, and return the existing job with isExisting=true.
|
|
16
16
|
//
|
|
17
|
-
// **Cross-
|
|
18
|
-
// across all memberships)
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
17
|
+
// **Cross-tenant semantics:** ExportJob is tenant-agnostic (one job per
|
|
18
|
+
// userId across all memberships), so the pre-check uses ctx.db.unsafeRaw
|
|
19
|
+
// (no TenantDb filter) and a click from tenant B can find the job from tenant
|
|
20
|
+
// A. `requestedFromTenantId` persists the tenant from the first click; the
|
|
21
|
+
// worker reads its compliance profile from this tenant for job TTL, stale
|
|
22
|
+
// handling, and cleanup.
|
|
22
23
|
|
|
23
24
|
import { createEventStoreExecutor, fetchOne } from "@cosmicdrift/kumiko-framework/db";
|
|
24
25
|
import { defineWriteHandler, type SaveContext } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -40,11 +41,11 @@ const REQUEST_EXPORT_REASON =
|
|
|
40
41
|
"export jobs are keyed by userId across all of the user's tenant memberships, not the caller's current tenant";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
44
|
+
* `createEventStoreExecutor.create` catches 23505 internally and returns
|
|
45
|
+
* `WriteFailure(UniqueViolationError)` rather than throwing. Check the
|
|
46
|
+
* failure code and constraintName to distinguish the app-side and race
|
|
47
|
+
* paths; propagate other failures (validation, version conflicts, ...)
|
|
48
|
+
* unchanged.
|
|
48
49
|
*/
|
|
49
50
|
function isActiveJobConflict(failure: WriteFailure): boolean {
|
|
50
51
|
const error = failure.error as {
|
|
@@ -82,9 +83,9 @@ export const requestExportWrite = defineWriteHandler({
|
|
|
82
83
|
// TenantDb-Wrapper would hide cross-tenant jobs.
|
|
83
84
|
const existing = await findActiveJob(exportJobRunner, userId);
|
|
84
85
|
if (existing) {
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
86
|
+
// The returned status is a snapshot and can become stale between fetchOne
|
|
87
|
+
// and the response if the worker changes the state concurrently. The
|
|
88
|
+
// window is minimal; clients poll export-status.query for live status.
|
|
88
89
|
return {
|
|
89
90
|
isSuccess: true as const,
|
|
90
91
|
data: {
|
|
@@ -95,9 +96,9 @@ export const requestExportWrite = defineWriteHandler({
|
|
|
95
96
|
};
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
99
|
+
// No active job exists, so create one through crud.create. Persist the
|
|
100
|
+
// user's current tenant in requestedFromTenantId so the worker reads the
|
|
101
|
+
// compliance profile from that tenant.
|
|
101
102
|
const result = await crud.create(
|
|
102
103
|
{
|
|
103
104
|
userId,
|
|
@@ -110,16 +111,16 @@ export const requestExportWrite = defineWriteHandler({
|
|
|
110
111
|
);
|
|
111
112
|
|
|
112
113
|
if (!result.isSuccess) {
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
114
|
+
// A parallel click may create an active job between findActiveJob and
|
|
115
|
+
// crud.create, causing UniqueViolationError for our constraint. Re-fetch
|
|
116
|
+
// and return the existing job with isExisting=true. Propagate other
|
|
117
|
+
// failures (validation, version conflicts, ...) unchanged.
|
|
117
118
|
if (isActiveJobConflict(result)) {
|
|
118
119
|
const winner = await findActiveJob(exportJobRunner, userId);
|
|
119
120
|
if (!winner) {
|
|
120
|
-
//
|
|
121
|
-
// active
|
|
122
|
-
//
|
|
121
|
+
// This should be impossible: a constraint violation without an existing
|
|
122
|
+
// active job would mean the constraint matched something else.
|
|
123
|
+
// Return the original failure so the inconsistency is visible.
|
|
123
124
|
return result;
|
|
124
125
|
}
|
|
125
126
|
return {
|
|
@@ -134,17 +135,17 @@ export const requestExportWrite = defineWriteHandler({
|
|
|
134
135
|
return result;
|
|
135
136
|
}
|
|
136
137
|
|
|
137
|
-
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
138
|
+
// SaveContext.id is EntityId (number | string), while exportJobEntity has
|
|
139
|
+
// idType:"uuid" and therefore guarantees a string. String() protects
|
|
140
|
+
// against drift if the entity is migrated to serial.
|
|
140
141
|
const created = result.data as SaveContext; // @cast-boundary engine-payload
|
|
141
142
|
return {
|
|
142
143
|
isSuccess: true as const,
|
|
143
144
|
data: {
|
|
144
145
|
jobId: String(created.id),
|
|
145
|
-
//
|
|
146
|
-
// export-status.query
|
|
147
|
-
// initial UI
|
|
146
|
+
// This is a snapshot: a just-created job is pending. Live status comes
|
|
147
|
+
// from export-status.query; this value is only a convenience for the
|
|
148
|
+
// initial UI render ("Anfrage angenommen, currently pending").
|
|
148
149
|
status: EXPORT_JOB_STATUS.Pending,
|
|
149
150
|
isExisting: false,
|
|
150
151
|
},
|