@cosmicdrift/kumiko-bundled-features 0.385.0 → 0.387.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/dist/agent-tools/tool-catalog.js +1 -1
- package/dist/agent-tools/tool-dispatch.js +0 -2
- package/dist/audit/screens.js +1 -1
- package/dist/auth-email-password/feature.js +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +3 -3
- package/dist/auth-email-password/handlers/confirm-token-flow.js +5 -5
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.js +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.js +18 -18
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +10 -10
- package/dist/auth-email-password/handlers/invite-info.query.js +1 -1
- package/dist/auth-email-password/handlers/invite-signup-complete.write.js +24 -26
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.js +1 -1
- package/dist/auth-email-password/handlers/self-registration-status.query.js +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +34 -37
- package/dist/auth-email-password/handlers/signup-request.write.js +6 -7
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.js +1 -4
- package/dist/auth-email-password/index.js +5 -5
- package/dist/auth-email-password/lockout-store.js +1 -1
- package/dist/auth-email-password/seeding.d.ts +42 -45
- package/dist/auth-email-password/seeding.js +51 -53
- package/dist/auth-email-password/signup-token-store.js +8 -8
- package/dist/auth-email-password/testing.js +3 -3
- package/dist/auth-email-password/web/auth-client.d.ts +2 -2
- package/dist/auth-email-password/web/auth-client.js +36 -37
- package/dist/auth-email-password/web/auth-form-primitives.js +10 -10
- package/dist/auth-email-password/web/auth-gate.js +15 -16
- package/dist/auth-email-password/web/client-plugin.d.ts +7 -7
- package/dist/auth-email-password/web/client-plugin.js +4 -5
- package/dist/auth-email-password/web/confirm-account-unlock-screen.js +1 -1
- package/dist/auth-email-password/web/constants.js +3 -3
- package/dist/auth-email-password/web/default-topbar-actions.d.ts +8 -8
- package/dist/auth-email-password/web/default-topbar-actions.js +6 -6
- package/dist/auth-email-password/web/forgot-password-screen.d.ts +2 -2
- package/dist/auth-email-password/web/forgot-password-screen.js +9 -9
- package/dist/auth-email-password/web/index.js +5 -5
- package/dist/auth-email-password/web/invite-accept-screen.d.ts +1 -1
- package/dist/auth-email-password/web/invite-accept-screen.js +16 -16
- package/dist/auth-email-password/web/login-screen.d.ts +1 -1
- package/dist/auth-email-password/web/login-screen.js +2 -3
- package/dist/auth-email-password/web/reset-password-screen.d.ts +4 -4
- package/dist/auth-email-password/web/reset-password-screen.js +12 -13
- package/dist/auth-email-password/web/session.d.ts +10 -9
- package/dist/auth-email-password/web/session.js +28 -29
- package/dist/auth-email-password/web/signup-complete-screen.js +3 -3
- package/dist/auth-email-password/web/signup-screen.d.ts +1 -1
- package/dist/auth-email-password/web/signup-screen.js +13 -13
- package/dist/auth-email-password/web/tenant-switcher.d.ts +4 -4
- package/dist/auth-email-password/web/tenant-switcher.js +21 -22
- package/dist/auth-email-password/web/use-shell-user.js +12 -13
- package/dist/auth-email-password/web/user-menu.d.ts +4 -4
- package/dist/auth-email-password/web/user-menu.js +12 -13
- package/dist/auth-email-password/web/verify-email-screen.d.ts +2 -2
- package/dist/auth-email-password/web/verify-email-screen.js +9 -10
- package/dist/auth-foundation/boot-checks.js +4 -4
- package/dist/auth-foundation/resolve-anonymous-access.d.ts +1 -1
- package/dist/auth-foundation/resolve-anonymous-access.js +3 -3
- package/dist/auth-foundation/types.js +2 -2
- package/dist/auth-mfa/handlers/disable.write.js +3 -3
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.js +4 -4
- package/dist/auth-mfa/handlers/enable-confirm.write.js +2 -2
- package/dist/auth-mfa/handlers/enable-start-preauth.write.js +1 -1
- package/dist/auth-mfa/handlers/reencrypt.job.js +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.js +5 -5
- package/dist/auth-mfa/handlers/verify.write.js +1 -1
- package/dist/auth-mfa/token-secrets.js +1 -1
- package/dist/auth-mfa/web/client-plugin.d.ts +1 -1
- package/dist/auth-mfa/web/client-plugin.js +1 -1
- package/dist/auth-mfa/web/i18n.js +3 -3
- package/dist/auth-mfa/web/index.js +4 -4
- package/dist/auth-mfa/web/mfa-client.js +17 -16
- package/dist/auth-mfa/web/mfa-verify-screen.js +6 -6
- package/dist/billing-foundation/aggregate-id.d.ts +5 -5
- package/dist/billing-foundation/aggregate-id.js +10 -11
- package/dist/billing-foundation/entities.js +27 -27
- package/dist/billing-foundation/events.js +18 -17
- package/dist/billing-foundation/feature.js +42 -43
- package/dist/billing-foundation/get-subscription-for-tenant.d.ts +3 -3
- package/dist/billing-foundation/get-subscription-for-tenant.js +3 -5
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +18 -17
- package/dist/billing-foundation/handlers/create-portal-session.write.js +11 -12
- package/dist/billing-foundation/handlers/list-subscriptions.query.js +4 -4
- package/dist/billing-foundation/handlers/process-event.write.js +20 -20
- package/dist/billing-foundation/projection.d.ts +15 -11
- package/dist/billing-foundation/projection.js +38 -37
- package/dist/billing-foundation/subscription-tier-sync.js +3 -3
- package/dist/billing-foundation/tenant-destroy-hook.d.ts +2 -2
- package/dist/billing-foundation/tenant-destroy-hook.js +7 -7
- package/dist/billing-foundation/types.d.ts +40 -41
- package/dist/billing-foundation/types.js +20 -20
- package/dist/billing-foundation/web/billing-plans-panel.js +4 -4
- package/dist/cap-counter/aggregate-id.js +8 -9
- package/dist/cap-counter/constants.js +11 -12
- package/dist/cap-counter/enforce-cap.d.ts +59 -62
- package/dist/cap-counter/enforce-cap.js +47 -48
- package/dist/cap-counter/entity.js +29 -29
- package/dist/cap-counter/feature.js +34 -35
- package/dist/cap-counter/handlers/increment-rolling.write.d.ts +2 -2
- package/dist/cap-counter/handlers/increment-rolling.write.js +31 -31
- package/dist/cap-counter/handlers/mark-soft-warned.write.js +4 -5
- package/dist/cap-counter/stock-cap-guard.js +1 -1
- package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
- package/dist/channel-email/smtp-transport.d.ts +18 -17
- package/dist/channel-email/smtp-transport.js +24 -21
- package/dist/channel-in-app/handlers/unread-count.query.js +3 -4
- package/dist/compliance-profiles/feature.js +4 -4
- package/dist/compliance-profiles/handlers/for-tenant.query.js +3 -6
- package/dist/compliance-profiles/handlers/list-profiles.query.js +5 -5
- package/dist/compliance-profiles/handlers/needs-profile.query.js +4 -4
- package/dist/compliance-profiles/handlers/set-profile.write.js +13 -14
- package/dist/compliance-profiles/handlers/sub-processors.query.js +9 -11
- package/dist/compliance-profiles/resolve-for-tenant.js +7 -7
- package/dist/compliance-profiles/schema/profile-selection.js +18 -23
- package/dist/compliance-profiles/seeding.js +14 -15
- package/dist/compliance-profiles-ops/handlers/tenants-missing-profile.query.js +3 -3
- package/dist/compliance-profiles-ops/index.js +2 -2
- package/dist/config/handlers/readiness.query.d.ts +4 -4
- package/dist/config/handlers/readiness.query.js +1 -1
- package/dist/config/handlers/reencrypt.job.js +4 -5
- package/dist/config/handlers/set.write.js +4 -4
- package/dist/config/resolver.js +4 -4
- package/dist/config/table.js +10 -10
- package/dist/config/write-helpers.js +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.js +12 -11
- package/dist/custom-fields/aggregate-id.d.ts +12 -13
- package/dist/custom-fields/aggregate-id.js +16 -17
- package/dist/custom-fields/entity.js +49 -47
- package/dist/custom-fields/events.js +7 -8
- package/dist/custom-fields/feature.d.ts +14 -14
- package/dist/custom-fields/feature.js +53 -54
- package/dist/custom-fields/handlers/clear-custom-field.write.d.ts +3 -2
- package/dist/custom-fields/handlers/clear-custom-field.write.js +9 -8
- package/dist/custom-fields/handlers/define-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/define-tenant-field.write.js +2 -2
- package/dist/custom-fields/handlers/delete-system-field.write.js +6 -7
- package/dist/custom-fields/handlers/delete-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/delete-tenant-field.write.js +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.d.ts +3 -3
- package/dist/custom-fields/handlers/set-custom-field.write.js +25 -28
- package/dist/custom-fields/handlers/update-tenant-field.write.d.ts +1 -1
- package/dist/custom-fields/handlers/update-tenant-field.write.js +1 -1
- package/dist/custom-fields/lib/field-access.js +2 -2
- package/dist/custom-fields/lib/parse-serialized-field.js +1 -1
- package/dist/custom-fields/lib/quota.js +1 -1
- package/dist/custom-fields/lib/value-schema.js +10 -12
- package/dist/custom-fields/run-retention.js +3 -3
- package/dist/custom-fields/schemas.js +14 -14
- package/dist/custom-fields/web/client-plugin.js +2 -5
- package/dist/custom-fields/web/constants.js +2 -2
- package/dist/custom-fields/web/custom-fields-form-section.d.ts +4 -4
- package/dist/custom-fields/web/custom-fields-form-section.js +22 -22
- package/dist/custom-fields/wire-for-entity.js +65 -67
- package/dist/custom-fields/wire-user-data-rights.js +3 -3
- package/dist/data-retention/_internal/parse-override.js +2 -3
- package/dist/data-retention/feature.js +11 -18
- package/dist/data-retention/handlers/policy-for.query.js +6 -6
- package/dist/data-retention/keep-for.d.ts +7 -7
- package/dist/data-retention/keep-for.js +15 -15
- package/dist/data-retention/override-schema.d.ts +3 -4
- package/dist/data-retention/override-schema.js +13 -15
- package/dist/data-retention/presets.d.ts +4 -5
- package/dist/data-retention/presets.js +18 -23
- package/dist/data-retention/resolve-tenant-preset.js +17 -16
- package/dist/data-retention/resolver.d.ts +12 -12
- package/dist/data-retention/resolver.js +24 -23
- package/dist/data-retention/run-retention-cleanup.d.ts +8 -3
- package/dist/data-retention/run-retention-cleanup.js +61 -51
- package/dist/data-retention/schema/tenant-retention-override.js +13 -11
- package/dist/delivery/feature.js +2 -2
- package/dist/delivery/handlers/log.query.js +1 -1
- package/dist/delivery/tables.js +9 -8
- package/dist/delivery/unsubscribe.js +4 -4
- package/dist/document-ingest-foundation/entity.js +4 -4
- package/dist/document-ingest-foundation/events.js +2 -2
- package/dist/document-ingest-foundation/feature.js +2 -2
- package/dist/document-ingest-foundation/pages.js +1 -1
- package/dist/document-ingest-foundation/tenant-destroy-hook.js +4 -4
- package/dist/feature-toggles/compose-tier-resolver.js +1 -1
- package/dist/feature-toggles/feature.js +1 -1
- package/dist/feature-toggles/global-feature-state-table.js +1 -1
- package/dist/feature-toggles/handlers/registered.query.js +5 -5
- package/dist/feature-toggles/toggle-sync-signal.js +1 -1
- package/dist/feature-toggles/web/constants.js +1 -1
- package/dist/file-derivatives/feature.d.ts +1 -1
- package/dist/file-derivatives/feature.js +1 -1
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-derivatives/presets.js +1 -1
- package/dist/file-foundation/feature.js +6 -6
- package/dist/file-provider-inmemory/feature.d.ts +1 -1
- package/dist/file-provider-inmemory/feature.js +16 -16
- package/dist/file-provider-s3/feature.js +16 -16
- package/dist/file-provider-s3/lib/s3-provider.js +6 -6
- package/dist/files/feature.js +6 -6
- package/dist/files-tenant-data/handlers/sweep-orphaned-derivatives.job.js +4 -4
- package/dist/files-tenant-data/hooks.js +24 -26
- package/dist/files-tenant-data/index.js +1 -1
- package/dist/folders/feature.d.ts +1 -1
- package/dist/folders/web/folder-manager.js +2 -2
- package/dist/folders/web/tree.js +5 -5
- package/dist/form-draft/db/queries/cleanup.js +1 -1
- package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
- package/dist/form-draft/entity.js +3 -3
- package/dist/form-draft/feature.js +4 -5
- package/dist/form-draft/handlers/discard.write.js +1 -1
- package/dist/form-draft/release-file-refs.js +6 -6
- package/dist/form-draft/schemas.js +3 -3
- package/dist/form-draft/web/constants.js +0 -2
- package/dist/inbound-mail-foundation/aggregate-id.d.ts +13 -13
- package/dist/inbound-mail-foundation/aggregate-id.js +19 -19
- package/dist/inbound-mail-foundation/connect-routes.js +4 -3
- package/dist/inbound-mail-foundation/constants.d.ts +22 -24
- package/dist/inbound-mail-foundation/constants.js +30 -33
- package/dist/inbound-mail-foundation/entities.d.ts +12 -12
- package/dist/inbound-mail-foundation/entities.js +47 -48
- package/dist/inbound-mail-foundation/events.d.ts +4 -3
- package/dist/inbound-mail-foundation/events.js +50 -47
- package/dist/inbound-mail-foundation/feature.js +39 -39
- package/dist/inbound-mail-foundation/handlers/account-state.js +8 -8
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +25 -26
- package/dist/inbound-mail-foundation/handlers/disconnect-account.write.js +7 -7
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +50 -51
- package/dist/inbound-mail-foundation/handlers/list-accounts.query.js +8 -8
- package/dist/inbound-mail-foundation/handlers/list-messages.query.js +10 -10
- package/dist/inbound-mail-foundation/handlers/scope-visibility.js +3 -4
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +11 -11
- package/dist/inbound-mail-foundation/oauth-state.d.ts +5 -5
- package/dist/inbound-mail-foundation/oauth-state.js +11 -10
- package/dist/inbound-mail-foundation/projection.d.ts +4 -4
- package/dist/inbound-mail-foundation/projection.js +31 -29
- package/dist/inbound-mail-foundation/provider-factory.d.ts +4 -4
- package/dist/inbound-mail-foundation/provider-factory.js +9 -9
- package/dist/inbound-mail-foundation/retention-sweep.d.ts +5 -3
- package/dist/inbound-mail-foundation/retention-sweep.js +38 -36
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
- package/dist/inbound-mail-foundation/types.d.ts +45 -46
- package/dist/inbound-mail-foundation/types.js +21 -21
- package/dist/inbound-mail-foundation/watch-supervisor.d.ts +10 -10
- package/dist/inbound-mail-foundation/watch-supervisor.js +57 -56
- package/dist/inbound-provider-imap/credential-document.js +13 -13
- package/dist/inbound-provider-imap/feature.d.ts +2 -2
- package/dist/inbound-provider-imap/feature.js +42 -40
- package/dist/inbound-provider-imap/imap-client.d.ts +12 -11
- package/dist/inbound-provider-imap/imap-client.js +20 -19
- package/dist/inbound-provider-inmemory/feature.d.ts +9 -9
- package/dist/inbound-provider-inmemory/feature.js +19 -19
- package/dist/jobs/db/queries/retention.js +2 -2
- package/dist/jobs/db/queries/stale-run-sweep.js +3 -3
- package/dist/jobs/events.js +1 -1
- package/dist/jobs/feature.js +5 -6
- package/dist/jobs/handlers/detail.query.js +4 -4
- package/dist/jobs/handlers/list.query.js +1 -1
- package/dist/jobs/handlers/projection-rebuild.job.js +1 -1
- package/dist/jobs/handlers/reindex-entity.job.js +1 -1
- package/dist/jobs/handlers/retry.write.js +1 -1
- package/dist/jobs/job-instant.js +1 -1
- package/dist/jobs/job-run-logger.js +10 -11
- package/dist/jobs/job-run-table.js +3 -3
- package/dist/jobs/screens.js +1 -1
- package/dist/jobs/tenant-job-failure-table.js +1 -1
- package/dist/jobs/tenant-job-run-table.js +1 -1
- package/dist/ledger/entity.js +5 -5
- package/dist/ledger/feature.js +3 -3
- package/dist/ledger/handlers/confirm-schedule-period.write.js +3 -3
- package/dist/ledger/recurring.js +12 -12
- package/dist/ledger/schemas.js +1 -1
- package/dist/ledger/web/constants.js +2 -2
- package/dist/ledger/web/index.js +7 -7
- package/dist/legal-pages/feature.d.ts +4 -4
- package/dist/legal-pages/feature.js +25 -26
- package/dist/legal-pages/markdown.js +3 -3
- package/dist/legal-pages/web/client-plugin.js +23 -24
- package/dist/legal-pages/web/index.js +0 -1
- package/dist/mail-foundation/feature.d.ts +2 -2
- package/dist/mail-foundation/feature.js +24 -25
- package/dist/mail-transport-inmemory/feature.d.ts +4 -4
- package/dist/mail-transport-inmemory/feature.js +27 -28
- package/dist/mail-transport-smtp/feature.js +20 -20
- package/dist/managed-pages/branding.js +6 -6
- package/dist/managed-pages/feature.d.ts +26 -26
- package/dist/managed-pages/feature.js +34 -34
- package/dist/managed-pages/handlers/by-slug.query.js +5 -6
- package/dist/managed-pages/handlers/by-tenant-published.query.js +2 -2
- package/dist/managed-pages/handlers/set.write.js +5 -7
- package/dist/managed-pages/screens/page-screens.js +11 -11
- package/dist/managed-pages/seeding.js +8 -8
- package/dist/managed-pages/table.js +13 -12
- package/dist/managed-pages/web/client-plugin.d.ts +1 -1
- package/dist/managed-pages/web/client-plugin.js +5 -5
- package/dist/managed-pages/web/i18n.js +1 -1
- package/dist/notes-history/entity.js +4 -4
- package/dist/notes-history/feature.d.ts +2 -2
- package/dist/notes-history-user-data/hooks.js +1 -1
- package/dist/page-render/layout.js +9 -9
- package/dist/page-render/markdown.js +11 -11
- package/dist/page-render/security-headers.js +7 -6
- package/dist/personal-access-tokens/feature.js +5 -5
- package/dist/personal-access-tokens/roles.js +2 -2
- package/dist/readiness/handlers/status.query.js +3 -3
- package/dist/renderer-foundation/api.js +12 -13
- package/dist/renderer-foundation/constants.js +2 -2
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/secrets/feature.js +9 -9
- package/dist/secrets/web/constants.js +2 -2
- package/dist/seo/feature.js +3 -3
- package/dist/sessions/constants.d.ts +1 -1
- package/dist/sessions/constants.js +1 -1
- package/dist/sessions/feature.js +8 -8
- package/dist/sessions/handlers/revoke-all-for-user.write.js +2 -2
- package/dist/sessions/handlers/revoke-all-others.write.js +2 -2
- package/dist/sessions/handlers/revoke.write.js +1 -1
- package/dist/sessions/schema/user-session.js +4 -4
- package/dist/sessions/screens.js +2 -2
- package/dist/sessions/session-callbacks.js +2 -2
- package/dist/sessions/session-revoked-event.js +2 -2
- package/dist/shared/assert-no-where-rule-in-ownership-write.js +1 -1
- package/dist/shared/decrypt-stored-pii.js +1 -1
- package/dist/shared/encrypt-for-direct-write.js +1 -1
- package/dist/shared/map-with-concurrency.js +5 -4
- package/dist/shared/parent-visibility.js +2 -2
- package/dist/shared/password-hashing.js +1 -1
- package/dist/shared/signup-handover.js +1 -2
- package/dist/shared/single-use-token-store.js +1 -1
- package/dist/subscription-mollie/constants.js +15 -17
- package/dist/subscription-mollie/feature.d.ts +17 -18
- package/dist/subscription-mollie/feature.js +24 -24
- package/dist/subscription-mollie/index.js +3 -3
- package/dist/subscription-mollie/plugin-methods.d.ts +2 -2
- package/dist/subscription-mollie/plugin-methods.js +15 -14
- package/dist/subscription-mollie/verify-webhook.d.ts +10 -10
- package/dist/subscription-mollie/verify-webhook.js +27 -27
- package/dist/subscription-stripe/constants.js +4 -6
- package/dist/subscription-stripe/feature.d.ts +14 -14
- package/dist/subscription-stripe/feature.js +35 -34
- package/dist/subscription-stripe/index.js +6 -6
- package/dist/subscription-stripe/plugin-methods.js +18 -18
- package/dist/subscription-stripe/runtime.d.ts +1 -1
- package/dist/subscription-stripe/runtime.js +1 -1
- package/dist/subscription-stripe/verify-webhook.d.ts +11 -11
- package/dist/subscription-stripe/verify-webhook.js +59 -61
- package/dist/tags/feature.d.ts +3 -3
- package/dist/tags/screens.js +1 -1
- package/dist/template-resolver/api.js +10 -10
- package/dist/template-resolver/handlers/find-by-id.query.js +5 -5
- package/dist/template-resolver/handlers/list.query.js +1 -1
- package/dist/template-resolver/handlers/shared.js +2 -2
- package/dist/template-resolver/handlers/template-write.js +3 -3
- package/dist/template-resolver/handlers/upsert-system.write.js +3 -4
- package/dist/template-resolver/handlers/upsert-tenant.write.js +4 -5
- package/dist/template-resolver/seeding.js +4 -4
- package/dist/template-resolver/table.js +10 -11
- package/dist/template-resolver/web/client-plugin.js +2 -2
- package/dist/tenant/feature.js +7 -7
- package/dist/tenant/handlers/member-directory.query.js +1 -1
- package/dist/tenant/handlers/members.query.js +1 -1
- package/dist/tenant/handlers/team-list.query.js +1 -1
- package/dist/tenant/handlers/tenant-directory.query.js +1 -1
- package/dist/tenant/invitation-table.js +37 -40
- package/dist/tenant/membership-table.js +3 -3
- package/dist/tenant/schema/tenant.js +7 -9
- package/dist/tenant/screens.js +1 -1
- package/dist/tenant/seeding.d.ts +6 -5
- package/dist/tenant/seeding.js +12 -12
- package/dist/tenant/testing.js +5 -6
- package/dist/tenant/web/constants.js +2 -2
- package/dist/tenant-handover/events.js +5 -7
- package/dist/tenant-handover/feature.js +1 -1
- package/dist/tenant-handover/grant.js +1 -1
- package/dist/tenant-handover/handlers/claim.write.js +1 -1
- package/dist/tenant-handover/move-entity-graph.js +8 -9
- package/dist/tenant-handover/signup-handover-provider.js +1 -2
- package/dist/tenant-handover/transfer-graph.js +4 -5
- package/dist/tenant-lifecycle/boot-checks.js +1 -1
- package/dist/tenant-lifecycle/feature.js +1 -1
- package/dist/tenant-settings/constants.js +1 -1
- package/dist/tenant-settings/tenant-defaults.d.ts +1 -1
- package/dist/tenant-settings/tenant-defaults.js +9 -2
- package/dist/tier-engine/aggregate-id.d.ts +11 -11
- package/dist/tier-engine/aggregate-id.js +15 -16
- package/dist/tier-engine/compose-app.d.ts +14 -14
- package/dist/tier-engine/compose-app.js +1 -1
- package/dist/tier-engine/entity.js +18 -19
- package/dist/tier-engine/feature.d.ts +19 -17
- package/dist/tier-engine/feature.js +68 -69
- package/dist/tier-engine/handlers/active-tier.query.js +6 -7
- package/dist/tier-engine/handlers/get-tenant-tier.query.js +4 -4
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +5 -5
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +3 -3
- package/dist/tier-engine/tier-resolver.js +2 -2
- package/dist/tier-engine/trial.js +8 -8
- package/dist/tier-engine/web/index.js +4 -4
- package/dist/user/db/queries/stream-tenant-backfill.js +8 -8
- package/dist/user/handlers/create.write.js +4 -4
- package/dist/user/schema/user.d.ts +11 -11
- package/dist/user/schema/user.js +45 -44
- package/dist/user/seeding.d.ts +15 -15
- package/dist/user/seeding.js +28 -28
- package/dist/user/testing.js +3 -3
- package/dist/user-data-rights/audit-download.d.ts +8 -8
- package/dist/user-data-rights/audit-download.js +19 -20
- package/dist/user-data-rights/boot-checks.js +1 -1
- package/dist/user-data-rights/deletion-token.js +1 -1
- package/dist/user-data-rights/feature.d.ts +49 -50
- package/dist/user-data-rights/feature.js +54 -53
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +9 -9
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.js +8 -8
- package/dist/user-data-rights/handlers/download-by-job.query.js +29 -30
- package/dist/user-data-rights/handlers/download-by-token.query.js +40 -42
- package/dist/user-data-rights/handlers/export-status.query.js +8 -9
- package/dist/user-data-rights/handlers/lift-restriction.write.js +7 -8
- package/dist/user-data-rights/handlers/list-download-attempts.query.js +2 -2
- package/dist/user-data-rights/handlers/my-audit-log.query.js +4 -4
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +5 -5
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.js +16 -15
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/request-deletion.write.js +7 -9
- package/dist/user-data-rights/handlers/request-export.write.js +42 -41
- package/dist/user-data-rights/handlers/restrict-account.write.js +22 -19
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.js +8 -8
- package/dist/user-data-rights/index.js +8 -10
- package/dist/user-data-rights/lib/default-mailers.js +3 -3
- package/dist/user-data-rights/lib/update-user-lifecycle.js +14 -17
- package/dist/user-data-rights/run-export-jobs.d.ts +38 -42
- package/dist/user-data-rights/run-export-jobs.js +127 -233
- package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
- package/dist/user-data-rights/run-forget-cleanup.js +63 -82
- package/dist/user-data-rights/run-user-export.d.ts +34 -33
- package/dist/user-data-rights/run-user-export.js +55 -57
- package/dist/user-data-rights/schema/download-attempt.js +11 -11
- package/dist/user-data-rights/schema/download-token.js +37 -41
- package/dist/user-data-rights/schema/export-job.d.ts +4 -4
- package/dist/user-data-rights/schema/export-job.js +65 -67
- package/dist/user-data-rights/token-helpers.d.ts +8 -9
- package/dist/user-data-rights/token-helpers.js +21 -25
- package/dist/user-data-rights/web/client-plugin.d.ts +5 -5
- package/dist/user-data-rights/web/client-plugin.js +1 -1
- package/dist/user-data-rights/web/confirm-deletion-screen.js +6 -5
- package/dist/user-data-rights/web/constants.js +1 -1
- package/dist/user-data-rights/web/index.js +5 -5
- package/dist/user-data-rights/web/privacy-center-screen.js +14 -13
- package/dist/user-data-rights/web/public-deletion-gate.d.ts +4 -4
- package/dist/user-data-rights/web/request-deletion-screen.js +6 -6
- package/dist/user-data-rights/zip-path.d.ts +15 -15
- package/dist/user-data-rights/zip-path.js +41 -40
- package/dist/user-data-rights-defaults/feature.js +15 -15
- package/dist/user-data-rights-defaults/hooks/delivery-attempt.userdata-hook.js +3 -5
- package/dist/user-data-rights-defaults/hooks/file-ref.userdata-hook.js +45 -51
- package/dist/user-data-rights-defaults/hooks/job-run.userdata-hook.js +2 -2
- package/dist/user-data-rights-defaults/hooks/notification-preference.userdata-hook.js +1 -1
- package/dist/user-data-rights-defaults/hooks/user-session.userdata-hook.js +3 -3
- package/dist/user-data-rights-defaults/hooks/user.userdata-hook.js +32 -31
- package/dist/user-profile/handlers/change-email.write.js +13 -13
- package/dist/user-profile/index.js +3 -3
- package/dist/user-profile/web/client-plugin.js +1 -1
- package/dist/user-profile/web/constants.js +1 -1
- package/dist/user-profile/web/profile-screen.js +1 -1
- package/dist/workflow-runner/aggregate-id.d.ts +1 -1
- package/dist/workflow-runner/aggregate-id.js +1 -1
- package/dist/workflow-runner/event-subscriber.js +4 -5
- package/dist/workflow-runner/event-trigger.js +4 -17
- package/dist/workflow-runner/feature.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +4 -4
- package/dist/workflow-runner/pending-projection.js +6 -7
- package/dist/workflow-runner/runner.js +6 -6
- package/dist/workflow-runner/tables.js +6 -6
- package/dist/workflow-runner/workflow-registry.js +1 -1
- package/package.json +10 -10
|
@@ -1,48 +1,23 @@
|
|
|
1
|
-
// Async
|
|
1
|
+
// Async export-job worker — a pure pipeline function.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// A cron-triggered pass (see the r.job wrap in feature.ts) processes all
|
|
4
|
+
// pending jobs. Each job is claimed with optimistic locking through the version
|
|
5
|
+
// column: when two worker replicas pick the same job, the second one gets a
|
|
6
|
+
// VersionConflictError and skips it. The export bundle is built by
|
|
7
|
+
// run-user-export.ts, which also owns the cross-tenant iteration over the
|
|
8
|
+
// user's memberships. The requesting tenant's compliance profile supplies
|
|
9
|
+
// exportDownloadTtl for expiresAt, and the storage provider is resolved per
|
|
10
|
+
// tenant (S3 or local). The ZIP is streamed into storage atomically (tmp+rename
|
|
11
|
+
// locally, multipart upload on S3), so a throw anywhere in the pipeline marks
|
|
12
|
+
// the job failed without leaving a half-written ZIP at the final path.
|
|
6
13
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// kriegt VersionConflictError + skippt.
|
|
14
|
+
// Before pickup, a stale pass fails running jobs whose
|
|
15
|
+
// startedAt + exportStaleTimeoutMinutes lies in the past, so worker crashes,
|
|
16
|
+
// OOM kills and replica restarts do not leave jobs stuck in running.
|
|
11
17
|
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// 3. Profile-Resolution: `resolveProfileForTenant(requestedFromTenantId)`
|
|
17
|
-
// liefert effective compliance-profile. exportDownloadTtl wird
|
|
18
|
-
// fuer expiresAt-Berechnung gebraucht.
|
|
19
|
-
//
|
|
20
|
-
// 4. Storage-Provider builden: `buildStorageProvider(ctx, requestedFromTenantId)`
|
|
21
|
-
// — file-foundation's createFileProviderForTenant-Pattern.
|
|
22
|
-
// Pro Tenant unterschiedlicher Provider moeglich (S3 vs lokal).
|
|
23
|
-
//
|
|
24
|
-
// 5. ZIP-Stream: `createZipStream(bundleAsZipEntries(bundle))` →
|
|
25
|
-
// AsyncIterable<Uint8Array>. Wir wrappen das in einen tracking-
|
|
26
|
-
// Iterator der bytesWritten zaehlt.
|
|
27
|
-
//
|
|
28
|
-
// 6. Storage-Write: provider.writeStream(storageKey, zipStream).
|
|
29
|
-
// Atomar via tmp+rename (lokal) bzw. multipart-upload (S3).
|
|
30
|
-
//
|
|
31
|
-
// 7. Job=done: `crud.update({status: "done", completedAt, expiresAt,
|
|
32
|
-
// downloadStorageKey, bytesWritten})`.
|
|
33
|
-
//
|
|
34
|
-
// Bei Throw zwischen 2-7: Job=failed mit errorMessage. Storage-Write
|
|
35
|
-
// ist atomar (writeStream rollback bei Error im Source-Stream), keine
|
|
36
|
-
// halb-fertigen ZIPs am final-Pfad.
|
|
37
|
-
//
|
|
38
|
-
// **Stale-Detection:** vor dem Pickup-Pass laeuft ein separater
|
|
39
|
-
// Stale-Pass: Jobs in `running` mit `startedAt + exportStaleTimeoutMinutes
|
|
40
|
-
// < now` werden auf `failed` geflippt. Worker-Crashes / OOM-Kills /
|
|
41
|
-
// Replica-Restarts hinterlassen sonst stuck-running-Jobs.
|
|
42
|
-
//
|
|
43
|
-
// **Storage-Cleanup:** dritter Pass loescht abgelaufene downloadStorageKeys
|
|
44
|
-
// (`expiresAt + exportStorageCleanupGraceHours < now`) — abgelaufene ZIPs
|
|
45
|
-
// auf S3 sollen nicht ewig liegen.
|
|
18
|
+
// A final cleanup pass deletes downloadStorageKeys past
|
|
19
|
+
// expiresAt + exportStorageCleanupGraceHours, so expired ZIPs do not stay in
|
|
20
|
+
// storage indefinitely.
|
|
46
21
|
import { addDurationSpec } from "@cosmicdrift/kumiko-framework/compliance";
|
|
47
22
|
import { PII_ERASED_SENTINEL } from "@cosmicdrift/kumiko-framework/crypto";
|
|
48
23
|
import { createEventStoreExecutor, createTenantDb, fetchOne, selectMany, } from "@cosmicdrift/kumiko-framework/db";
|
|
@@ -68,28 +43,23 @@ export const EXPORT_CLEANUP_LAST_RUN_TIMESTAMP_METRIC = "export_cleanup_last_run
|
|
|
68
43
|
const crud = createEventStoreExecutor(exportJobsTable, exportJobEntity, {
|
|
69
44
|
entityName: "export-job",
|
|
70
45
|
});
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
// (Memory `feedback_no_fake_dispatcher`).
|
|
46
|
+
// Separate event-store executor for download tokens. `crud.create` emits
|
|
47
|
+
// `exportDownloadToken.created` and synchronously updates
|
|
48
|
+
// `read_export_download_tokens`; direct inserts would bypass that projection.
|
|
75
49
|
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
50
|
+
// Exported because the download handler uses tokenCrud.update for audit fields
|
|
51
|
+
// while preserving event-store consistency.
|
|
78
52
|
export const tokenCrud = createEventStoreExecutor(exportDownloadTokensTable, exportDownloadTokenEntity, { entityName: "export-download-token" });
|
|
79
53
|
export async function runExportJobs(args) {
|
|
80
54
|
const { db, registry, buildStorageProvider, now, sendExportReadyEmail, sendExportFailedEmail } = args;
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
// Runtime-Callback-Failures sind best-effort, Boot-Misconfig ist hard-
|
|
85
|
-
// fail (App-Author-Bug, kein Network-Hiccup).
|
|
55
|
+
// Validate this configuration before the pass loop so a missing URL is not
|
|
56
|
+
// swallowed by the per-job try/catch. Callback failures are best-effort, but
|
|
57
|
+
// this is a hard application configuration error.
|
|
86
58
|
if (sendExportReadyEmail && args.appExportDownloadUrl === undefined) {
|
|
87
59
|
throw new Error("user-data-rights: sendExportReadyEmail gesetzt aber appExportDownloadUrl fehlt — beide muessen zusammen konfiguriert sein");
|
|
88
60
|
}
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
// Wird VOR dem pickup-pass ausgefuehrt damit ein neuer Worker-Run
|
|
92
|
-
// den vorhergehenden Crash erstmal als failed markiert.
|
|
61
|
+
// Fail running jobs whose tenant-specific stale timeout has elapsed before
|
|
62
|
+
// pickup, so a new run recovers jobs left behind by an earlier crash.
|
|
93
63
|
const staleFailedJobIds = await staleDetectionPass({ db, now });
|
|
94
64
|
// Pass 2: Pickup pending jobs + Process them.
|
|
95
65
|
const completedJobIds = [];
|
|
@@ -110,20 +80,13 @@ export async function runExportJobs(args) {
|
|
|
110
80
|
});
|
|
111
81
|
if (outcome.kind === "done") {
|
|
112
82
|
completedJobIds.push(job.id);
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
// NICHT in Logs/Telemetry/jobRunsTable.
|
|
83
|
+
// Keep the plain token only in memory for downstream callbacks; never put it
|
|
84
|
+
// in logs, telemetry, or jobRunsTable.
|
|
116
85
|
tokenByJobId.set(job.id, outcome.tokenPlain);
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
//
|
|
121
|
-
// pending-Jobs muessen noch verarbeitet werden. Throw waere ein
|
|
122
|
-
// Bug: r.job-Wrap markiert den Run failed, retry findet keinen
|
|
123
|
-
// pending-Job mehr (Status=done) → silent miss + ZIP laeuft nach
|
|
124
|
-
// TTL ab ohne dass der User die Email je bekommt. console.warn ist
|
|
125
|
-
// die einzige Operator-Sichtbarkeit (runExportJobs-args fuehren
|
|
126
|
-
// AppContext.log nicht durch — pure-function-Pattern).
|
|
86
|
+
// A notification failure must not abort the batch: the job is already done,
|
|
87
|
+
// and retrying the worker would find no pending job and silently miss the
|
|
88
|
+
// notification. `console.warn` is the only available operator visibility
|
|
89
|
+
// because these arguments do not carry AppContext.log.
|
|
127
90
|
if (sendExportReadyEmail) {
|
|
128
91
|
try {
|
|
129
92
|
await fireExportReadyCallback({
|
|
@@ -145,10 +108,8 @@ export async function runExportJobs(args) {
|
|
|
145
108
|
else if (outcome.kind === "failed") {
|
|
146
109
|
failedJobIds.push(job.id);
|
|
147
110
|
errors.push(outcome.error);
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
// sendExportReadyEmail (Atom 5.fix3) — Email-Send-Throw darf den
|
|
151
|
-
// Batch nicht killen.
|
|
111
|
+
// A failed-job notification is best-effort so an email-send failure cannot
|
|
112
|
+
// abort processing of the remaining pending jobs.
|
|
152
113
|
if (sendExportFailedEmail) {
|
|
153
114
|
try {
|
|
154
115
|
await fireExportFailedCallback({
|
|
@@ -164,12 +125,11 @@ export async function runExportJobs(args) {
|
|
|
164
125
|
}
|
|
165
126
|
}
|
|
166
127
|
}
|
|
167
|
-
//
|
|
168
|
-
//
|
|
128
|
+
// A claim race means another worker already owns the job; skip it without
|
|
129
|
+
// reporting an error.
|
|
169
130
|
}
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
// + im DB-Row genullt.
|
|
131
|
+
// Storage cleanup: expired done jobs (expiresAt + grace < now) get their
|
|
132
|
+
// storage object deleted and downloadStorageKey cleared in the database.
|
|
173
133
|
const cleanedJobIds = await storageCleanupPass({
|
|
174
134
|
db,
|
|
175
135
|
buildStorageProvider,
|
|
@@ -194,19 +154,14 @@ async function fetchPendingJobs(db) {
|
|
|
194
154
|
async function processJob(args) {
|
|
195
155
|
const { db, registry, buildStorageProvider, now, job } = args;
|
|
196
156
|
const executor = createSystemUser(job.requestedFromTenantId);
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
// konsistent gehalten.
|
|
157
|
+
// The event-store stream lookup requires a TenantDb even though an export job
|
|
158
|
+
// is tenant-agnostic. System mode bypasses the automatic tenant filter while
|
|
159
|
+
// `requestedFromTenantId` preserves the stream identity.
|
|
201
160
|
const tdb = systemTenantDb(db, job.requestedFromTenantId);
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
//
|
|
206
|
-
// persistiert (deterministischer Pfad aus job.id). Begründung: wenn
|
|
207
|
-
// Worker zwischen ZIP-write und done-flip crashed, hat der Job-Row
|
|
208
|
-
// bereits den Pfad → storageCleanupPass kann den orphan-ZIP via
|
|
209
|
-
// failed-Job-Cleanup-Pass loeschen (sonst forever-leak im S3).
|
|
161
|
+
// Claim with a version check so a concurrent worker causes a conflict.
|
|
162
|
+
// Persist downloadStorageKey during the claim: if the worker crashes after
|
|
163
|
+
// writing the ZIP but before marking the job done, cleanup can find the
|
|
164
|
+
// orphaned object instead of leaving it in storage indefinitely.
|
|
210
165
|
const storageKey = buildExportStorageKey(job);
|
|
211
166
|
const claimResult = await crud.update({
|
|
212
167
|
id: job.id,
|
|
@@ -218,11 +173,11 @@ async function processJob(args) {
|
|
|
218
173
|
},
|
|
219
174
|
}, executor, tdb);
|
|
220
175
|
if (!claimResult.isSuccess) {
|
|
221
|
-
//
|
|
176
|
+
// Another worker won the claim race, so skip this job without logging an error.
|
|
222
177
|
return { kind: "skipped" };
|
|
223
178
|
}
|
|
224
|
-
//
|
|
225
|
-
//
|
|
179
|
+
// Build the bundle, stream the ZIP, and write it to storage. Errors before
|
|
180
|
+
// completion are materialized as a failed job.
|
|
226
181
|
try {
|
|
227
182
|
const bundle = await runUserExport({
|
|
228
183
|
db,
|
|
@@ -238,11 +193,10 @@ async function processJob(args) {
|
|
|
238
193
|
tenantId: job.requestedFromTenantId,
|
|
239
194
|
});
|
|
240
195
|
const ttl = profile.profile.userRights.exportDownloadTtl;
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
// ueber den fileRef.tenantId.
|
|
196
|
+
// Cross-tenant file references require a provider per tenant because storage
|
|
197
|
+
// configuration may differ. Cache each provider so a tenant is initialized
|
|
198
|
+
// only once; the requested tenant owns the write path, while reads use each
|
|
199
|
+
// file reference's tenantId.
|
|
246
200
|
const providerCache = new Map();
|
|
247
201
|
const cachedProvider = async (tenantId) => {
|
|
248
202
|
let p = providerCache.get(tenantId);
|
|
@@ -253,32 +207,26 @@ async function processJob(args) {
|
|
|
253
207
|
return p;
|
|
254
208
|
};
|
|
255
209
|
const writeProvider = await cachedProvider(job.requestedFromTenantId);
|
|
256
|
-
//
|
|
257
|
-
//
|
|
210
|
+
// The provider type requires both methods, so runtime optional checks are
|
|
211
|
+
// unnecessary.
|
|
258
212
|
const tracker = countingStream(createZipStream(bundleToZipEntries(bundle, now, cachedProvider)));
|
|
259
213
|
await writeProvider.writeStream(storageKey, tracker.stream, {
|
|
260
214
|
mimeType: "application/zip",
|
|
261
215
|
});
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
// VOR und done-flip NACH: tightes Race-Window in dem ein Worker-
|
|
267
|
-
// Crash zwischen den zwei calls einen Token + ZIP orphans. Beides
|
|
268
|
-
// catched die Stale-Detection im naechsten Pass — Job=failed,
|
|
269
|
-
// Storage-Cleanup-Pass clearted den ZIP (path-pre-claim oben).
|
|
216
|
+
// Create the token before the done transition: if token creation fails, the
|
|
217
|
+
// job is still running and the catch path marks it failed (monotonic status).
|
|
218
|
+
// A crash between the two calls can orphan a token and a ZIP; the next
|
|
219
|
+
// stale-detection pass fails the job and the cleanup pass removes the ZIP.
|
|
270
220
|
//
|
|
271
|
-
//
|
|
272
|
-
//
|
|
273
|
-
// ES via tokenCrud.create — kein direct-INSERT.
|
|
221
|
+
// The plain token stays in worker memory and reaches the notification
|
|
222
|
+
// through RunExportJobsResult; only its hash is stored.
|
|
274
223
|
//
|
|
275
|
-
// **
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
281
|
-
// (update mit frischem Hash + Reset der Audit-Felder).
|
|
224
|
+
// **Reruns:** when an operator flips a done or failed job back to pending
|
|
225
|
+
// (lost plain token, email failure), a token row already exists for the
|
|
226
|
+
// jobId. A second create would emit another created event on a fresh
|
|
227
|
+
// aggregate, and projection rebuilds would keep colliding on the
|
|
228
|
+
// one_per_job index. Rotate the token in place on the existing aggregate
|
|
229
|
+
// instead (fresh hash, audit fields reset).
|
|
282
230
|
const expiresAt = addDurationSpec(now, ttl);
|
|
283
231
|
const { plain: tokenPlain, hash: tokenHash } = await generateDownloadToken();
|
|
284
232
|
const existingToken = await fetchOne(db, exportDownloadTokensTable, { jobId: job.id });
|
|
@@ -301,39 +249,33 @@ async function processJob(args) {
|
|
|
301
249
|
tokenHash,
|
|
302
250
|
issuedAt: now,
|
|
303
251
|
expiresAt,
|
|
304
|
-
//
|
|
305
|
-
//
|
|
306
|
-
// Defensiv-Code.
|
|
252
|
+
// Set useCount explicitly to 0 so verification can increment it with
|
|
253
|
+
// `useCount + 1` without defensive COALESCE logic.
|
|
307
254
|
useCount: 0,
|
|
308
255
|
}, executor, tdb);
|
|
309
256
|
if (!tokenCreateResult.isSuccess) {
|
|
310
|
-
// Token
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
// persistiert).
|
|
257
|
+
// Token creation failed before the done transition. The catch path marks the
|
|
258
|
+
// job failed, and cleanup can remove the ZIP whose path was persisted at
|
|
259
|
+
// claim time.
|
|
314
260
|
throw new Error(`Job ${job.id}: Token-Creation failed before done-flip. ` +
|
|
315
261
|
`${tokenCreateResult.error?.code ?? "unknown"}`);
|
|
316
262
|
}
|
|
317
|
-
//
|
|
318
|
-
// hier persistiert (denormalisiert in beiden Tabellen).
|
|
263
|
+
// Persist the same expiresAt used for the token on the job row as well.
|
|
319
264
|
const doneResult = await crud.update({
|
|
320
265
|
id: job.id,
|
|
321
|
-
version: job.version + 1, // +1
|
|
266
|
+
version: job.version + 1, // +1 because the claim already performed one update
|
|
322
267
|
changes: {
|
|
323
268
|
status: EXPORT_JOB_STATUS.Done,
|
|
324
269
|
completedAt: now,
|
|
325
|
-
// downloadStorageKey
|
|
326
|
-
//
|
|
270
|
+
// downloadStorageKey was set during the claim, so it does not need to be
|
|
271
|
+
// written again.
|
|
327
272
|
expiresAt,
|
|
328
273
|
bytesWritten: tracker.bytes,
|
|
329
274
|
},
|
|
330
275
|
}, executor, tdb);
|
|
331
276
|
if (!doneResult.isSuccess) {
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
// failed damit der Operator das sieht. Token-Row bleibt orphan
|
|
335
|
-
// bis Atom-spaeter-Cleanup; Storage-ZIP wird via failed-Cleanup
|
|
336
|
-
// gecleared.
|
|
277
|
+
// The done transition failed after claiming. Mark the job failed so the
|
|
278
|
+
// operator can see the error; the token and ZIP remain for later cleanup.
|
|
337
279
|
throw new Error(`Job ${job.id}: failed to flip status=done after successful Token-Create. ` +
|
|
338
280
|
`${doneResult.error?.code ?? "unknown"}`);
|
|
339
281
|
}
|
|
@@ -341,9 +283,8 @@ async function processJob(args) {
|
|
|
341
283
|
}
|
|
342
284
|
catch (e) {
|
|
343
285
|
const message = e instanceof Error ? e.message : String(e);
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
// Worker-Run faengt ihn dann auf.
|
|
286
|
+
// Failure handling is best-effort. If the failure transition also fails, the
|
|
287
|
+
// job remains running and stale detection will recover it on a later run.
|
|
347
288
|
await crud
|
|
348
289
|
.update({
|
|
349
290
|
id: job.id,
|
|
@@ -370,12 +311,10 @@ async function processJob(args) {
|
|
|
370
311
|
}
|
|
371
312
|
async function staleDetectionPass(args) {
|
|
372
313
|
const { db, now } = args;
|
|
373
|
-
//
|
|
374
|
-
//
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
// 60s, zu jedem Zeitpunkt sind nur wenige Jobs in `running` —
|
|
378
|
-
// alle fetchen + profile-resolve im Loop ist bezahlbar + korrekt.
|
|
314
|
+
// Fetch all running jobs instead of applying a coarse one-hour filter:
|
|
315
|
+
// exportStaleTimeoutMinutes is tenant-specific and may be as low as five
|
|
316
|
+
// minutes, so a coarse filter could miss stale jobs. The expected number of
|
|
317
|
+
// running jobs makes resolving each tenant's profile practical.
|
|
379
318
|
const candidates = await selectMany(db, exportJobsTable, { status: EXPORT_JOB_STATUS.Running });
|
|
380
319
|
const failed = [];
|
|
381
320
|
for (const c of candidates) {
|
|
@@ -397,41 +336,21 @@ async function staleDetectionPass(args) {
|
|
|
397
336
|
}, createSystemUser(c.requestedFromTenantId), systemTenantDb(db, c.requestedFromTenantId));
|
|
398
337
|
if (result.isSuccess)
|
|
399
338
|
failed.push(c.id);
|
|
400
|
-
//
|
|
339
|
+
// Another worker completed the job, so skip it.
|
|
401
340
|
}
|
|
402
341
|
}
|
|
403
342
|
return failed;
|
|
404
343
|
}
|
|
405
344
|
async function storageCleanupPass(args) {
|
|
406
345
|
const { db, buildStorageProvider, now, metrics } = args;
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
//
|
|
414
|
-
//
|
|
415
|
-
// schiefgegangen (Token-Create-Fail, done-flip-Fail, Stale-
|
|
416
|
-
// Detection mid-write). Job=failed mit gesetztem downloadStorageKey =
|
|
417
|
-
// ZIP-Orphan in Storage, kein User-Pfad zum download.
|
|
418
|
-
//
|
|
419
|
-
// **Strategie failed-Jobs: SOFORTIGE Cleanup ohne Grace.**
|
|
420
|
-
//
|
|
421
|
-
// Trade-off (DSGVO vs Audit-Forensik):
|
|
422
|
-
// - DSGVO: User-Daten-ZIP einer failed-export-Anfrage hat keinen
|
|
423
|
-
// legitimen Aufbewahrungs-Grund — hier ueberwiegt das Recht auf
|
|
424
|
-
// Loeschung
|
|
425
|
-
// - Audit-Forensik: forensische Untersuchung ("was ist im ZIP gelandet,
|
|
426
|
-
// warum failed der Job") wuerde von einer Grace-Periode profitieren —
|
|
427
|
-
// ABER: Token wurde nie ausgegeben (kein User hat das ZIP gesehen),
|
|
428
|
-
// also keine User-Schaden-Forensik. Operator-Audit hat job.errorMessage
|
|
429
|
-
// + Stack-Trace, das reicht
|
|
430
|
-
// → Trade-off zugunsten DSGVO entschieden. Wenn ein Operator forensik
|
|
431
|
-
// braucht, muss er das vor dem Cleanup-Pass capturen (out-of-band).
|
|
432
|
-
//
|
|
433
|
-
// **SQL-Filter:** status IN (done, failed) + downloadStorageKey IS NOT NULL
|
|
434
|
-
// via selectMany (db/queries/export-jobs.ts).
|
|
346
|
+
// Done jobs are cleaned after expiresAt plus the tenant's grace period
|
|
347
|
+
// (default 24h after expiry, so re-downloads after a dropped connection still
|
|
348
|
+
// work). Failed jobs with a persisted downloadStorageKey are orphaned ZIPs:
|
|
349
|
+
// the worker wrote the ZIP but failed before the done flip, so no token was
|
|
350
|
+
// ever issued and no user has seen it. They are removed immediately, without
|
|
351
|
+
// grace: the right to erasure outweighs forensics here, and the job's
|
|
352
|
+
// errorMessage and stack trace are the operator's audit record. An operator
|
|
353
|
+
// who needs the ZIP itself must capture it out of band before this pass.
|
|
435
354
|
const candidates = await selectExportJobsForStorageCleanup(db, EXPORT_JOB_STATUS.Done, EXPORT_JOB_STATUS.Failed);
|
|
436
355
|
const cleaned = [];
|
|
437
356
|
// Candidates past their due-instant, keyed by that instant — used below to
|
|
@@ -539,11 +458,9 @@ function reportCleanupLastRun(metrics, now) {
|
|
|
539
458
|
console.warn(`[user-data-rights:run-export-jobs] metrics.set(${EXPORT_CLEANUP_LAST_RUN_TIMESTAMP_METRIC}) failed err=${err instanceof Error ? err.message : String(err)}`);
|
|
540
459
|
}
|
|
541
460
|
}
|
|
542
|
-
//
|
|
543
|
-
// (
|
|
544
|
-
//
|
|
545
|
-
// — wir nutzen `requestedFromTenantId` als Stream-Identity damit der
|
|
546
|
-
// Stream-Counter konsistent bleibt (Memory feedback_event_store_tenant_consistency).
|
|
461
|
+
// `crud.update` requires a TenantDb for event-store stream lookup. Export jobs are tenant-agnostic
|
|
462
|
+
// (one job per userId), so system mode bypasses the automatic tenant filter while
|
|
463
|
+
// `requestedFromTenantId` preserves stream identity and keeps the stream counter consistent.
|
|
547
464
|
// @wrapper-known semantic-alias
|
|
548
465
|
function systemTenantDb(db, tenantId) {
|
|
549
466
|
return createTenantDb({ db, tenantId, mode: "system" });
|
|
@@ -552,41 +469,31 @@ function buildExportStorageKey(job) {
|
|
|
552
469
|
return `${tenantExportPrefix(job.requestedFromTenantId)}${job.id}.zip`;
|
|
553
470
|
}
|
|
554
471
|
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
472
|
+
* Converts a UserExportBundle to an `AsyncIterable<ZipEntry>` for
|
|
473
|
+
* createZipStream. `bundle.json` contains the bundle metadata and file
|
|
474
|
+
* references; each file is streamed from the provider at its `zipPath`, so
|
|
475
|
+
* large files do not require an in-memory copy.
|
|
557
476
|
*
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
* + tenant-sections + flat
|
|
561
|
-
* fileRefs[] mit zipPath
|
|
562
|
-
* pro Datei
|
|
563
|
-
* files/<tenantId>/<fileRefId>-<name> — File-Binaries, Pfad
|
|
564
|
-
* identisch zu fileRef.zipPath
|
|
565
|
-
* im JSON. Bytes via
|
|
566
|
-
* provider.readStream — kein
|
|
567
|
-
* Memory-Spike, auch grosse
|
|
568
|
-
* PDFs streamen durch.
|
|
569
|
-
*
|
|
570
|
-
* `getProvider` ist ein async-cached Resolver pro tenantId. Caller
|
|
571
|
-
* (processJob) baut die Caching-Map damit jeder Tenant nur EINMAL
|
|
572
|
-
* via `buildStorageProvider` materialisiert wird.
|
|
477
|
+
* `getProvider` caches providers by tenantId, ensuring each tenant is built
|
|
478
|
+
* only once while processing cross-tenant file references.
|
|
573
479
|
*/
|
|
574
480
|
async function* bundleToZipEntries(bundle, mtime, getProvider) {
|
|
575
|
-
// bundle.json
|
|
481
|
+
// Emit bundle.json first so readers can parse the metadata before the file
|
|
482
|
+
// entries arrive.
|
|
576
483
|
const bundleJson = JSON.stringify(bundle, null, 2);
|
|
577
484
|
yield {
|
|
578
485
|
path: "bundle.json",
|
|
579
486
|
data: oneShot(new TextEncoder().encode(bundleJson)),
|
|
580
487
|
mtime,
|
|
581
488
|
};
|
|
582
|
-
// File
|
|
583
|
-
//
|
|
584
|
-
//
|
|
585
|
-
//
|
|
489
|
+
// File binaries: one ZIP entry per fileRef. `data` is the readStream directly,
|
|
490
|
+
// so the ZIP builder consumes chunks without an upfront memory spike. Even
|
|
491
|
+
// with 50 PDFs at 10MB each, at most about 64KB (default highWaterMark) is
|
|
492
|
+
// held in the heap at once.
|
|
586
493
|
for (const ref of bundle.fileRefs) {
|
|
587
494
|
const provider = await getProvider(ref.tenantId);
|
|
588
|
-
// readStream
|
|
589
|
-
//
|
|
495
|
+
// `readStream` is required by the FileStorageProvider type, so no runtime
|
|
496
|
+
// optional check is needed.
|
|
590
497
|
yield {
|
|
591
498
|
path: ref.zipPath,
|
|
592
499
|
data: provider.readStream(ref.storageKey),
|
|
@@ -598,9 +505,9 @@ async function* oneShot(bytes) {
|
|
|
598
505
|
yield bytes;
|
|
599
506
|
}
|
|
600
507
|
/**
|
|
601
|
-
* Wraps an `AsyncIterable<Uint8Array>`
|
|
602
|
-
* `for await
|
|
603
|
-
* bytesWritten
|
|
508
|
+
* Wraps an `AsyncIterable<Uint8Array>` and counts the total bytes. After
|
|
509
|
+
* `for await`, `tracker.bytes` contains the final value used for
|
|
510
|
+
* `bytesWritten` in the job row.
|
|
604
511
|
*/
|
|
605
512
|
function countingStream(source) {
|
|
606
513
|
const tracker = { bytes: 0 };
|
|
@@ -619,19 +526,14 @@ function countingStream(source) {
|
|
|
619
526
|
},
|
|
620
527
|
};
|
|
621
528
|
}
|
|
622
|
-
//
|
|
623
|
-
//
|
|
624
|
-
//
|
|
625
|
-
//
|
|
626
|
-
// runUserExport's Cross-Tenant-Iteration die ebenso direkt
|
|
627
|
-
// tenantMembershipsTable liest. Cross-feature-API via dispatcher
|
|
628
|
-
// haette einen JobContext-Wrapper gebraucht den der framework noch
|
|
629
|
-
// nicht hat — wenn das mal kommt, ist der hier Refactor-Kandidat.
|
|
529
|
+
// Reads directly from the database because the worker's AppContext has no
|
|
530
|
+
// queryAs, unlike HandlerContext in the request path. The cross-tenant
|
|
531
|
+
// export iteration uses the same approach; a dispatcher-based cross-feature
|
|
532
|
+
// API would require a JobContext wrapper that the framework does not provide.
|
|
630
533
|
//
|
|
631
|
-
//
|
|
632
|
-
//
|
|
633
|
-
//
|
|
634
|
-
// aus jedem Worker-Tenant-Context.
|
|
534
|
+
// userTable.id is globally unique. Even when a user belongs to multiple
|
|
535
|
+
// tenants, there is only one user row with its home tenant as tenantId, so a
|
|
536
|
+
// lookup without a tenantId filter works from any worker tenant context.
|
|
635
537
|
async function lookupUserContact(db, userId) {
|
|
636
538
|
// @cast-boundary db-row.
|
|
637
539
|
const row = (await fetchOne(db, userTable, { id: userId }));
|
|
@@ -642,23 +544,16 @@ async function lookupUserContact(db, userId) {
|
|
|
642
544
|
return null;
|
|
643
545
|
return { email, locale: row.locale ?? null };
|
|
644
546
|
}
|
|
645
|
-
//
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
// den Worker-Run erneut anstossen — der Job selbst bleibt done, das
|
|
649
|
-
// retry findet keinen pending-Job mehr aber die Audit-Log zeigt den
|
|
650
|
-
// Failure.
|
|
547
|
+
// The callback is best-effort: its failure must not abort the batch because
|
|
548
|
+
// the job is already done, and retrying the worker will not find it pending.
|
|
549
|
+
// The failure remains visible in the worker-run audit log.
|
|
651
550
|
async function fireExportReadyCallback(args) {
|
|
652
551
|
const contact = await lookupUserContact(args.db, args.job.userId);
|
|
653
552
|
if (!contact) {
|
|
654
|
-
//
|
|
655
|
-
//
|
|
656
|
-
//
|
|
657
|
-
//
|
|
658
|
-
// aktuell nicht durch (Worker-pure-function-Pattern). console.warn ist
|
|
659
|
-
// die einzige Operator-Sichtbarkeit fuer den missing-user-edge-case.
|
|
660
|
-
// Wenn jobs-Feature spaeter ctx.log threadet oder Worker-args erweitert
|
|
661
|
-
// werden, hier Refactor-Kandidat.
|
|
553
|
+
// If the user row is missing, skip the notification instead of throwing:
|
|
554
|
+
// the job remains done, and the token remains available through
|
|
555
|
+
// export-status.query. console.warn is used because the worker arguments
|
|
556
|
+
// do not currently carry AppContext.log.
|
|
662
557
|
// biome-ignore lint/suspicious/noConsole: operator-visibility for missing-user edge-case
|
|
663
558
|
console.warn(`[user-data-rights:run-export-jobs] userId=${args.job.userId} hat kein userEmail — sendExportReadyEmail skipped`);
|
|
664
559
|
// skip: missing user-email row; console.warn above provides operator visibility
|
|
@@ -680,7 +575,6 @@ async function fireExportReadyCallback(args) {
|
|
|
680
575
|
bytesWritten: args.bytesWritten,
|
|
681
576
|
});
|
|
682
577
|
}
|
|
683
|
-
// Atom 5 — Email-Notification beim failed-flip.
|
|
684
578
|
async function fireExportFailedCallback(args) {
|
|
685
579
|
const contact = await lookupUserContact(args.db, args.job.userId);
|
|
686
580
|
if (!contact) {
|