@cosmicdrift/kumiko-bundled-features 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/dist/agent-tools/tool-catalog.js +1 -1
- package/dist/agent-tools/tool-dispatch.d.ts +7 -7
- package/dist/agent-tools/tool-dispatch.js +9 -9
- package/dist/audit/handlers/audit-events.js +1 -1
- 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/feature-exports.d.ts +2 -2
- package/dist/auth-mfa/feature.js +8 -0
- 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 +8 -4
- 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-code-or-recovery-field.d.ts +11 -0
- package/dist/auth-mfa/web/mfa-code-or-recovery-field.js +12 -0
- package/dist/auth-mfa/web/mfa-disable-dialog.js +14 -3
- package/dist/auth-mfa/web/mfa-regenerate-recovery-dialog.js +14 -3
- package/dist/auth-mfa/web/mfa-setup-preauth-screen.js +10 -5
- package/dist/auth-mfa/web/mfa-verify-screen.js +32 -11
- package/dist/billing-foundation/aggregate-id.d.ts +5 -5
- package/dist/billing-foundation/aggregate-id.js +10 -11
- package/dist/billing-foundation/checkout-core.d.ts +9 -14
- package/dist/billing-foundation/checkout-core.js +24 -39
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +8 -2
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +1 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +1 -0
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +9 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +3 -1
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +4 -2
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +6 -5
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +5 -1
- package/dist/billing-foundation/consumer-protection/termination-record.js +27 -17
- package/dist/billing-foundation/consumer-protection/termination-request.js +12 -7
- package/dist/billing-foundation/consumer-protection/termination-revoke.js +12 -7
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/termination-texts.js +3 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +5 -2
- package/dist/billing-foundation/consumer-protection/withdrawal-period.js +8 -9
- package/dist/billing-foundation/entities.js +27 -27
- package/dist/billing-foundation/events.d.ts +1 -0
- package/dist/billing-foundation/events.js +19 -17
- package/dist/billing-foundation/feature.js +45 -44
- 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/billing-plans.query.js +35 -5
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +19 -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/plan-switch-preview.query.d.ts +13 -0
- package/dist/billing-foundation/handlers/plan-switch-preview.query.js +48 -0
- package/dist/billing-foundation/handlers/process-event.write.d.ts +6 -0
- package/dist/billing-foundation/handlers/process-event.write.js +28 -22
- package/dist/billing-foundation/handlers/start-plan-checkout.write.js +32 -5
- package/dist/billing-foundation/handlers/switch-plan.write.d.ts +2 -2
- package/dist/billing-foundation/handlers/switch-plan.write.js +56 -64
- package/dist/billing-foundation/handlers/sync-subscription.write.js +12 -1
- package/dist/billing-foundation/i18n.js +22 -0
- package/dist/billing-foundation/index.d.ts +1 -1
- package/dist/billing-foundation/plan-catalog.d.ts +21 -5
- package/dist/billing-foundation/plan-catalog.js +25 -16
- package/dist/billing-foundation/plan-switch.d.ts +12 -0
- package/dist/billing-foundation/plan-switch.js +60 -0
- 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 +97 -42
- package/dist/billing-foundation/types.js +29 -20
- package/dist/billing-foundation/validate-options.js +17 -0
- package/dist/billing-foundation/web/billing-plans-panel.js +82 -36
- package/dist/billing-foundation/web/checkout-consent-dialog.d.ts +17 -3
- package/dist/billing-foundation/web/checkout-consent-dialog.js +14 -10
- package/dist/billing-foundation/web/constants.d.ts +4 -0
- package/dist/billing-foundation/web/constants.js +4 -0
- package/dist/billing-foundation/web/plan-price.d.ts +15 -0
- package/dist/billing-foundation/web/plan-price.js +32 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.d.ts +15 -0
- package/dist/billing-foundation/web/plan-switch-confirm-dialog.js +66 -0
- package/dist/cap-counter/aggregate-id.js +8 -9
- package/dist/cap-counter/cap-reservation.js +5 -1
- 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 +49 -49
- 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 +8 -2
- package/dist/cap-counter/with-cap-enforcement.d.ts +3 -5
- package/dist/channel-email/feature.js +1 -1
- 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/cascade.query.d.ts +5 -0
- package/dist/config/handlers/cascade.query.js +4 -3
- 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/handlers/values.query.d.ts +5 -0
- package/dist/config/handlers/values.query.js +4 -3
- package/dist/config/resolver.js +4 -4
- package/dist/config/table.js +10 -10
- package/dist/config/write-helpers.d.ts +1 -0
- package/dist/config/write-helpers.js +19 -10
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +2 -2
- package/dist/crypto-shredding/handlers/forget-subject.write.js +25 -21
- 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/channel-context.js +6 -3
- package/dist/delivery/db/queries/preferences.js +2 -2
- package/dist/delivery/delivery-service.js +22 -7
- package/dist/delivery/feature.js +2 -2
- package/dist/delivery/handlers/log.query.js +1 -1
- package/dist/delivery/handlers/preferences.query.d.ts +1 -1
- package/dist/delivery/handlers/preferences.query.js +20 -1
- package/dist/delivery/handlers/unsubscribe-page-chrome.query.js +1 -1
- package/dist/delivery/tables.js +9 -8
- package/dist/delivery/unsubscribe-link.d.ts +3 -1
- package/dist/delivery/unsubscribe-page-strings.d.ts +1 -0
- package/dist/delivery/unsubscribe-page-strings.js +1 -0
- package/dist/delivery/unsubscribe-page.d.ts +2 -0
- package/dist/delivery/unsubscribe-page.js +5 -0
- package/dist/delivery/unsubscribe.js +29 -11
- 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-foundation/index.d.ts +1 -1
- package/dist/file-foundation/index.js +1 -1
- package/dist/file-foundation/read-file-ref-content.d.ts +3 -0
- package/dist/file-foundation/read-file-ref-content.js +6 -1
- package/dist/file-provider-inmemory/feature.d.ts +1 -1
- package/dist/file-provider-inmemory/feature.js +16 -16
- package/dist/file-provider-s3/env-named-feature.d.ts +0 -2
- 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 +3 -3
- package/dist/form-draft/db/queries/owned-file-refs.js +1 -1
- package/dist/form-draft/decrypt-draft.js +5 -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/host-routing/dispatch.js +11 -20
- 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.d.ts +2 -2
- package/dist/inbound-mail-foundation/handlers/update-account.write.js +22 -14
- package/dist/inbound-mail-foundation/i18n.js +0 -1
- 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/screens.js +0 -3
- package/dist/inbound-mail-foundation/tenant-destroy-hook.js +14 -16
- package/dist/inbound-mail-foundation/types.d.ts +49 -49
- 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 +85 -67
- 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.d.ts +1 -0
- package/dist/jobs/feature.js +13 -10
- 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/handlers/tenant-failures.query.d.ts +1 -1
- package/dist/jobs/handlers/tenant-failures.query.js +45 -41
- package/dist/jobs/handlers/tenant-runs.query.d.ts +1 -1
- package/dist/jobs/handlers/tenant-runs.query.js +54 -50
- package/dist/jobs/job-instant.js +1 -1
- package/dist/jobs/job-run-logger.js +15 -15
- 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 +5 -5
- package/dist/mail-foundation/feature.js +27 -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.d.ts +1 -1
- package/dist/personal-access-tokens/feature.js +9 -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/renderer-pdf/render-table-pdf.d.ts +1 -1
- package/dist/renderer-pdf/render-table-pdf.js +22 -10
- 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.d.ts +2 -2
- package/dist/sessions/feature.js +12 -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.d.ts +1 -0
- package/dist/shared/decrypt-stored-pii.js +7 -2
- package/dist/shared/encrypt-for-direct-write.js +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/mail-template-body.d.ts +6 -2
- package/dist/shared/mail-template-body.js +7 -4
- 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/step-dispatcher/feature.js +1 -0
- package/dist/subscription-dodo/client.d.ts +17 -0
- package/dist/subscription-dodo/client.js +31 -0
- package/dist/subscription-dodo/constants.d.ts +12 -0
- package/dist/subscription-dodo/constants.js +19 -0
- package/dist/subscription-dodo/feature.d.ts +37 -0
- package/dist/subscription-dodo/feature.js +144 -0
- package/dist/subscription-dodo/index.d.ts +2 -0
- package/dist/subscription-dodo/index.js +4 -0
- package/dist/subscription-dodo/plugin-methods.d.ts +23 -0
- package/dist/subscription-dodo/plugin-methods.js +260 -0
- package/dist/subscription-dodo/runtime.d.ts +27 -0
- package/dist/subscription-dodo/runtime.js +89 -0
- package/dist/subscription-dodo/verify-webhook.d.ts +37 -0
- package/dist/subscription-dodo/verify-webhook.js +189 -0
- 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 +36 -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 +8 -7
- 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.d.ts +3 -2
- package/dist/template-resolver/api.js +34 -12
- package/dist/template-resolver/compile-document-body.js +1 -0
- package/dist/template-resolver/feature.js +2 -2
- package/dist/template-resolver/handlers/bundle-import.js +3 -3
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +5 -1
- package/dist/template-resolver/handlers/copy-locale.write.js +22 -7
- package/dist/template-resolver/handlers/diff-to-system.query.js +3 -2
- package/dist/template-resolver/handlers/document-base.js +3 -2
- package/dist/template-resolver/handlers/find-by-id.query.js +8 -7
- package/dist/template-resolver/handlers/list.query.js +6 -7
- package/dist/template-resolver/handlers/overview.query.d.ts +3 -0
- package/dist/template-resolver/handlers/overview.query.js +6 -3
- package/dist/template-resolver/handlers/shared.js +2 -2
- package/dist/template-resolver/handlers/template-write.js +3 -3
- package/dist/template-resolver/handlers/tenant-override-counts.query.js +1 -3
- package/dist/template-resolver/handlers/tenant-override-selection.d.ts +2 -1
- package/dist/template-resolver/handlers/tenant-overrides.query.js +28 -5
- 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/handlers/usage.query.d.ts +2 -1
- package/dist/template-resolver/handlers/usage.query.js +26 -16
- package/dist/template-resolver/handlers/validate.query.js +7 -2
- package/dist/template-resolver/seeding.js +30 -14
- package/dist/template-resolver/store-template-resource.d.ts +1 -0
- package/dist/template-resolver/store-template-resource.js +19 -4
- package/dist/template-resolver/table.js +10 -11
- package/dist/template-resolver/template-delivery-policy.d.ts +0 -5
- package/dist/template-resolver/template-delivery-policy.js +0 -6
- package/dist/template-resolver/template-meta.js +7 -4
- package/dist/template-resolver/template-preview.js +4 -3
- package/dist/template-resolver/web/client-plugin.js +2 -2
- package/dist/template-resolver/web/constants.d.ts +2 -2
- package/dist/template-resolver/web/constants.js +3 -3
- package/dist/template-resolver/web/index.d.ts +1 -0
- package/dist/template-resolver/web/index.js +1 -0
- package/dist/template-resolver/web/mail-layout-constants.d.ts +5 -0
- package/dist/template-resolver/web/mail-layout-constants.js +7 -0
- package/dist/tenant/feature.js +7 -7
- package/dist/tenant/handlers/member-directory.query.d.ts +1 -0
- package/dist/tenant/handlers/member-directory.query.js +11 -4
- 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.d.ts +1 -0
- package/dist/tenant/handlers/tenant-directory.query.js +9 -5
- 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/constants.d.ts +1 -0
- package/dist/tenant-lifecycle/constants.js +4 -0
- package/dist/tenant-lifecycle/feature.js +1 -1
- package/dist/tenant-lifecycle/run-tenant-destroy.js +2 -2
- 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 +7 -8
- package/dist/tier-engine/handlers/set-tenant-tier.write.js +7 -4
- package/dist/tier-engine/tier-resolver.js +2 -2
- package/dist/tier-engine/trial.js +8 -8
- package/dist/tier-engine/web/constants.js +2 -2
- 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 +130 -237
- package/dist/user-data-rights/run-forget-cleanup.d.ts +16 -20
- package/dist/user-data-rights/run-forget-cleanup.js +65 -83
- 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/waitlist/handlers/export-purge.write.js +2 -1
- package/dist/waitlist/handlers/submit.write.js +5 -5
- package/dist/waitlist/import.d.ts +1 -0
- package/dist/waitlist/import.js +4 -0
- package/dist/waitlist/legacy-migrate.js +17 -6
- package/dist/waitlist/link-on-signup.js +18 -6
- package/dist/waitlist/options.d.ts +3 -0
- 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 +20 -10
- package/src/agent-tools/changes.json +6 -0
- package/src/audit/changes.json +12 -0
- package/src/auth-mfa/changes.json +6 -0
- package/src/billing-foundation/changes.json +55 -0
- package/src/cap-counter/changes.json +18 -0
- package/src/channel-email/changes.json +6 -0
- package/src/config/changes.json +12 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/delivery/changes.json +30 -0
- package/src/form-draft/changes.json +6 -0
- package/src/host-routing/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +18 -0
- package/src/jobs/changes.json +13 -0
- package/src/mail-foundation/changes.json +6 -0
- package/src/renderer-pdf/changes.json +6 -0
- package/src/step-dispatcher/changes.json +6 -0
- package/src/subscription-dodo/changes.json +14 -0
- package/src/subscription-stripe/changes.json +12 -0
- package/src/template-resolver/changes.json +48 -0
- package/src/tenant/changes.json +6 -0
- package/src/tenant-lifecycle/changes.json +6 -0
- package/src/tier-engine/changes.json +6 -0
- package/src/waitlist/changes.json +18 -0
- package/dist/billing-foundation/consumer-protection/stored-user-email.d.ts +0 -1
- package/dist/billing-foundation/consumer-protection/stored-user-email.js +0 -8
|
@@ -6,18 +6,15 @@ import { type SearchAdapter } from "@cosmicdrift/kumiko-framework/search";
|
|
|
6
6
|
import type { getTemporal } from "@cosmicdrift/kumiko-framework/time";
|
|
7
7
|
type Instant = InstanceType<ReturnType<typeof getTemporal>["Instant"]>;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* persistiert den failed-Run in jobRunsTable (siehe
|
|
12
|
-
* jobs/__tests__/jobs-feature.integration.ts Scenario 2 — der throw-
|
|
13
|
-
* Pfad eines r.job-handlers wird dort gepinnt).
|
|
9
|
+
* Callback for the forget-cleanup path. A callback failure propagates to the
|
|
10
|
+
* r.job wrapper so the jobs feature can persist the failed run.
|
|
14
11
|
*
|
|
15
|
-
* **executedAt:**
|
|
16
|
-
*
|
|
12
|
+
* **executedAt:** The time of the delete flip, passed as an ISO string so the
|
|
13
|
+
* app author can insert it into an email template.
|
|
17
14
|
*
|
|
18
|
-
* **tenantIds:**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
15
|
+
* **tenantIds:** All memberships the user had before deletion. An email
|
|
16
|
+
* template can use this to describe which tenant accounts were deleted. The
|
|
17
|
+
* list is empty for an orphan user with no memberships.
|
|
21
18
|
*/
|
|
22
19
|
export type SendDeletionExecutedEmailFn = (args: {
|
|
23
20
|
readonly userId: string;
|
|
@@ -32,13 +29,12 @@ export interface RunForgetCleanupArgs {
|
|
|
32
29
|
readonly db: DbRunner;
|
|
33
30
|
readonly registry: Registry;
|
|
34
31
|
/**
|
|
35
|
-
*
|
|
36
|
-
* Pattern aus data-retention/keep-for.ts (advisor-pinned).
|
|
32
|
+
* Injects the current time so tests can control it without mocking the date.
|
|
37
33
|
*/
|
|
38
34
|
readonly now: Instant;
|
|
39
|
-
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
35
|
+
/** Sends an optional email notification after the delete flip. Without a
|
|
36
|
+
* callback, the worker remains silent because the user already received the
|
|
37
|
+
* request-deletion email and grace-period reminder. */
|
|
42
38
|
readonly sendDeletionExecutedEmail?: SendDeletionExecutedEmailFn;
|
|
43
39
|
/**
|
|
44
40
|
* Per-tenant file-storage-provider resolver (the forget cron builds it from
|
|
@@ -56,8 +52,8 @@ export interface RunForgetCleanupArgs {
|
|
|
56
52
|
*/
|
|
57
53
|
readonly kms?: KmsAdapter;
|
|
58
54
|
/**
|
|
59
|
-
* Search adapter for purging derived Meili docs of searchable subject-PII
|
|
60
|
-
*
|
|
55
|
+
* Search adapter for purging derived Meili docs of searchable subject-PII.
|
|
56
|
+
* Same seam as forget-subject. Omitted → no search purge (apps
|
|
61
57
|
* without Meili).
|
|
62
58
|
*/
|
|
63
59
|
readonly searchAdapter?: SearchAdapter;
|
|
@@ -97,11 +93,11 @@ export interface ForgetCleanupIncomplete {
|
|
|
97
93
|
readonly rolledBack: boolean;
|
|
98
94
|
}
|
|
99
95
|
export interface RunForgetCleanupResult {
|
|
100
|
-
/**
|
|
96
|
+
/** Users flipped from DeletionRequested to Deleted during this run. */
|
|
101
97
|
readonly processedUserIds: readonly string[];
|
|
102
|
-
/**
|
|
98
|
+
/** Number of entity-hook calls that actually ran, whether successful or failed. */
|
|
103
99
|
readonly hookCallsAttempted: number;
|
|
104
|
-
/** Hook
|
|
100
|
+
/** Hook errors collected for operator visibility; the run continues. */
|
|
105
101
|
readonly errors: readonly ForgetCleanupError[];
|
|
106
102
|
/** Hooks that reported `{status:"incomplete"}` — partial success, user still Deleted. */
|
|
107
103
|
readonly incomplete: readonly ForgetCleanupIncomplete[];
|
|
@@ -1,39 +1,32 @@
|
|
|
1
1
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
2
|
-
// Forget-
|
|
2
|
+
// Forget-cleanup runner — pure-function pipeline.
|
|
3
3
|
//
|
|
4
|
-
// After the grace period
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// history from kumiko_events).
|
|
4
|
+
// After the grace period, this runner processes users in DeletionRequested
|
|
5
|
+
// state and triggers EXT_USER_DATA delete hooks in every tenant where the user
|
|
6
|
+
// is or was a member, using membership history from kumiko_events.
|
|
8
7
|
//
|
|
9
|
-
// **Cross-tenant iteration:** a
|
|
10
|
-
//
|
|
11
|
-
// the request — see
|
|
12
|
-
// docs/plans/architecture/user-data-rights.md "Cross-Tenant-Semantik".
|
|
8
|
+
// **Cross-tenant iteration:** a request in tenant A must remove the user's data
|
|
9
|
+
// in all of their tenants, including tenants they left before the request.
|
|
13
10
|
//
|
|
14
|
-
// **Strategy
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
11
|
+
// **Strategy dispatch:** each entity's data-retention policy determines whether
|
|
12
|
+
// to run "delete" or "anonymize". A blockDelete policy always produces
|
|
13
|
+
// "anonymize", leaving the data object in place while removing its association
|
|
14
|
+
// with the person.
|
|
18
15
|
//
|
|
19
|
-
// **Per-
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// Cleanup-Run beim ersten Hook-Throw zurueckrollen.
|
|
16
|
+
// **Per-user atomicity:** each user runs in its own sub-transaction
|
|
17
|
+
// (db.transaction → SAVEPOINT when an outer transaction is active, BEGIN
|
|
18
|
+
// otherwise). A failing hook rolls back only that user's transaction, while
|
|
19
|
+
// other users and previously completed status changes remain commit-able.
|
|
20
|
+
// Without this isolation, the outer dispatcher transaction would roll back the
|
|
21
|
+
// entire cleanup run on the first hook failure.
|
|
26
22
|
//
|
|
27
|
-
// **
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
// Lauf den User nicht mehr findet.
|
|
23
|
+
// **Idempotency:** hooks are designed to be idempotent, so rerunning after
|
|
24
|
+
// crash recovery is safe. Flipping the status to "Deleted" at the end keeps
|
|
25
|
+
// the user out of subsequent runs.
|
|
31
26
|
//
|
|
32
|
-
// **Error
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// gefailten Hooks bleibt im DeletionRequested-Status (next Lauf
|
|
36
|
-
// retried automatisch).
|
|
27
|
+
// **Error handling:** a throwing hook must not stop the run. Errors are
|
|
28
|
+
// collected and returned for operator visibility, while a user with failed
|
|
29
|
+
// hooks remains in DeletionRequested for automatic retry.
|
|
37
30
|
import { configuredPiiSubjectKms, subjectIdToKey, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
38
31
|
import { createTenantDb, entityEventName, executeRawQuery, fetchOne, nullBlindIndexesForSubject, selectMany, WritePurposes, } from "@cosmicdrift/kumiko-framework/db";
|
|
39
32
|
import { deleteStoredFileAndDerivatives } from "@cosmicdrift/kumiko-framework/derivatives";
|
|
@@ -85,8 +78,8 @@ export async function runForgetCleanup(args) {
|
|
|
85
78
|
if (dueUsers.length === 0) {
|
|
86
79
|
return { processedUserIds: [], hookCallsAttempted: 0, errors: [], incomplete: [] };
|
|
87
80
|
}
|
|
88
|
-
// Step 2:
|
|
89
|
-
// (entityName, deleteHook)
|
|
81
|
+
// Step 2: collect all EXT_USER_DATA usages once, i.e. the
|
|
82
|
+
// (entityName, deleteHook) pairs of all registered provider features.
|
|
90
83
|
const usages = registry.getExtensionUsages(EXT_USER_DATA);
|
|
91
84
|
const hookEntries = usages
|
|
92
85
|
.map((u) => {
|
|
@@ -112,7 +105,7 @@ export async function runForgetCleanup(args) {
|
|
|
112
105
|
const incomplete = [];
|
|
113
106
|
const processedUserIds = [];
|
|
114
107
|
let hookCallsAttempted = 0;
|
|
115
|
-
// Step 3:
|
|
108
|
+
// Step 3: iterate per user, each in its own sub-transaction (see header).
|
|
116
109
|
for (const user of dueUsers) {
|
|
117
110
|
const userResult = await processUser({
|
|
118
111
|
db,
|
|
@@ -132,17 +125,14 @@ export async function runForgetCleanup(args) {
|
|
|
132
125
|
incomplete.push(...userResult.incomplete);
|
|
133
126
|
if (userResult.success) {
|
|
134
127
|
processedUserIds.push(user.id);
|
|
135
|
-
//
|
|
136
|
-
//
|
|
128
|
+
// userEmail was cached BEFORE the transaction because the user hook
|
|
129
|
+
// anonymizes it inside the transaction.
|
|
137
130
|
//
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
// schon Deleted) → silent miss. console.warn ist die einzige
|
|
144
|
-
// Operator-Sichtbarkeit — runForgetCleanup-args fuehren AppContext.
|
|
145
|
-
// log aktuell nicht durch (pure-function-Pattern).
|
|
131
|
+
// The email is best-effort: a send failure must not abort the batch after the
|
|
132
|
+
// user's sub-transaction has committed. Otherwise the job would be marked
|
|
133
|
+
// failed, and a retry would find no eligible users, causing a silent miss.
|
|
134
|
+
// console.warn is the only operator-visible reporting path because the
|
|
135
|
+
// runForgetCleanup args do not carry AppContext.log (pure-function pattern).
|
|
146
136
|
if (sendDeletionExecutedEmail && userResult.userEmailBeforeDelete) {
|
|
147
137
|
try {
|
|
148
138
|
await sendDeletionExecutedEmail({
|
|
@@ -167,11 +157,9 @@ async function processUser(args) {
|
|
|
167
157
|
const errors = [];
|
|
168
158
|
const incomplete = [];
|
|
169
159
|
let hookCallsAttempted = 0;
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
// Memory-cache laesst Atom-5b-Callback nach success-flip den
|
|
174
|
-
// ORIGINAL-email an App-Author-Callback geben.
|
|
160
|
+
// Cache the email before the transaction because the user hook anonymizes
|
|
161
|
+
// email/displayName/passwordHash inside it. The cache lets the callback after
|
|
162
|
+
// a successful status flip receive the original email address.
|
|
175
163
|
const userPreTx = await fetchOne(db, userTable, {
|
|
176
164
|
id: userId,
|
|
177
165
|
});
|
|
@@ -179,13 +167,12 @@ async function processUser(args) {
|
|
|
179
167
|
? await decryptStoredPii(userPreTx.email, "email", "user-data-rights:forget-cleanup")
|
|
180
168
|
: null;
|
|
181
169
|
const userLocaleBeforeDelete = userPreTx?.locale ?? null;
|
|
182
|
-
// Memberships fuer diesen User holen — alle Tenants in denen er Mitglied ist.
|
|
183
170
|
const memberships = await selectMany(db, tenantMembershipsTable, {
|
|
184
171
|
userId,
|
|
185
172
|
});
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
173
|
+
// Capture memberships before the transaction deletes them so the email
|
|
174
|
+
// callback receives the original tenant list. Orphan users provide an empty
|
|
175
|
+
// list.
|
|
189
176
|
const tenantIdsBeforeDelete = memberships.map((m) => m.tenantId);
|
|
190
177
|
// Departed tenants: removeMember deletes only the projection row, so data
|
|
191
178
|
// the user left behind there (e.g. note mentions) must still be reached.
|
|
@@ -197,19 +184,17 @@ async function processUser(args) {
|
|
|
197
184
|
// nothing there and no-op. Without this path status=Deleted would be set
|
|
198
185
|
// while the original PII stays — compliant-looking, but not compliant.
|
|
199
186
|
const tenantList = hookTenantIds.length > 0 ? hookTenantIds : [SYSTEM_TENANT_ID_FOR_ORPHANS];
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
// (
|
|
204
|
-
// writeHandler bereits IN der Outer-Tx laeuft — via `.savepoint`
|
|
205
|
-
// (SAVEPOINT). Siehe runInSubTransaction.
|
|
187
|
+
// Hooks and the status flip run atomically per user. A hook failure rolls back
|
|
188
|
+
// only that user, while other users remain commit-able. runInSubTransaction
|
|
189
|
+
// uses `.begin` (BEGIN) for a top-level connection and `.savepoint`
|
|
190
|
+
// (SAVEPOINT) when the dispatcher is already inside an outer transaction.
|
|
206
191
|
let txSucceeded = false;
|
|
207
192
|
let currentTenantId = null;
|
|
208
193
|
let currentEntityName = null;
|
|
209
194
|
try {
|
|
210
195
|
await runInSubTransaction(db, async (tx) => {
|
|
211
196
|
// Discover + purge search docs for this subject BEFORE any delete
|
|
212
|
-
// hook runs
|
|
197
|
+
// hook runs. purgeSearchDocumentsForSubject finds candidate
|
|
213
198
|
// rows via a live SELECT on each entity's read-table — a hook that
|
|
214
199
|
// hard-deletes a row (UserDataDeleteStrategy "delete") makes that row
|
|
215
200
|
// invisible to a purge running afterward, permanently stranding its
|
|
@@ -289,7 +274,8 @@ async function processUser(args) {
|
|
|
289
274
|
// is what makes this crash-safe: eraseKey-throw → tx rollback → user
|
|
290
275
|
// stays DeletionRequested → next run retries; flip-throw after a
|
|
291
276
|
// successful erase → retry re-runs the idempotent hooks and eraseKey
|
|
292
|
-
// (
|
|
277
|
+
// (idempotent: a repeat erase keeps the original tombstone, an unknown
|
|
278
|
+
// subject gets one). Erasing after
|
|
293
279
|
// the flip instead would risk a permanent silent miss: user already
|
|
294
280
|
// Deleted, no run ever picks him up again, key stays live.
|
|
295
281
|
if (kms) {
|
|
@@ -298,24 +284,22 @@ async function processUser(args) {
|
|
|
298
284
|
userId,
|
|
299
285
|
eraseReason: "user-data-rights:forget",
|
|
300
286
|
});
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
//
|
|
304
|
-
//
|
|
287
|
+
// Null blind-index columns immediately after erasing the subject key; otherwise
|
|
288
|
+
// the deterministic HMAC would remain equality-matchable until the next
|
|
289
|
+
// rebuild. Keeping this in the sub-transaction makes the sweep rollback
|
|
290
|
+
// with the rest of the user's cleanup.
|
|
305
291
|
await nullBlindIndexesForSubject(tx, registry.features, subjectIdToKey({ kind: "user", userId }));
|
|
306
292
|
}
|
|
307
|
-
//
|
|
308
|
-
//
|
|
309
|
-
//
|
|
310
|
-
// Run retried.
|
|
293
|
+
// Flip the status in the same sub-transaction. If a hook throws, execution
|
|
294
|
+
// never reaches this point, so the user remains in DeletionRequested for
|
|
295
|
+
// the next retry.
|
|
311
296
|
await updateUserLifecycle(tx, userId, { status: USER_STATUS.Deleted }, { writePurpose: WritePurposes.erasure });
|
|
312
297
|
txSucceeded = true;
|
|
313
298
|
});
|
|
314
299
|
}
|
|
315
300
|
catch (e) {
|
|
316
|
-
//
|
|
317
|
-
//
|
|
318
|
-
// generisches "<sub-transaction>".
|
|
301
|
+
// Track the failing hook so operators see its tenant and entity instead of a
|
|
302
|
+
// generic sub-transaction error.
|
|
319
303
|
errors.push({
|
|
320
304
|
userId,
|
|
321
305
|
tenantId: currentTenantId ?? "",
|
|
@@ -357,10 +341,9 @@ function resolveForgetFileBytesDeleter(buildStorageProvider) {
|
|
|
357
341
|
}
|
|
358
342
|
};
|
|
359
343
|
}
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
// User-Row und ignorieren tenantId.
|
|
344
|
+
// Use a pseudo-tenant for users with no active or historical memberships.
|
|
345
|
+
// Tenant-scoped hooks find nothing there, while tenant-agnostic hooks such as
|
|
346
|
+
// the user hook still operate on the global user row and ignore tenantId.
|
|
364
347
|
const SYSTEM_TENANT_ID_FOR_ORPHANS = "00000000-0000-0000-0000-000000000000";
|
|
365
348
|
// "single-user" requires BOTH the app config AND a runtime sole-member check —
|
|
366
349
|
// the config asserts the deployment model, the count guards against a stray
|
|
@@ -380,7 +363,7 @@ async function resolveEffectiveTenantModel(db, tenantId, appTenantModel) {
|
|
|
380
363
|
// removeMemberWrite only deletes the tenant-membership PROJECTION row — the
|
|
381
364
|
// event history survives forever, and a departed member's own tenant-scoped
|
|
382
365
|
// data rows (contributor-style records with no per-user column) can
|
|
383
|
-
// outlive their membership
|
|
366
|
+
// outlive their membership. A live count of 1 is therefore not
|
|
384
367
|
// enough: a tenant that ever had >1 distinct member must stay multi-user for
|
|
385
368
|
// the forget path even after everyone but one is removed, or the sole
|
|
386
369
|
// remaining member's forget request wipes the departed co-member's leftover
|
|
@@ -390,7 +373,7 @@ async function resolveEffectiveTenantModel(db, tenantId, appTenantModel) {
|
|
|
390
373
|
// tenant SystemAdmin adds land under the actor's tenant_id.
|
|
391
374
|
//
|
|
392
375
|
// Counting created-ROWS against distinct userIds latched a tenant to
|
|
393
|
-
// multi-user as soon as its sole member was removed and re-added
|
|
376
|
+
// multi-user as soon as its sole member was removed and re-added —
|
|
394
377
|
// two created-events, one identity, no co-member ever. The fail-safe only
|
|
395
378
|
// needs to cover payloads whose userId cannot be READ, so the unreadable
|
|
396
379
|
// remainder is measured directly instead of inferred from the row count.
|
|
@@ -426,14 +409,13 @@ async function readHistoricalMembershipTenantIds(db, userId) {
|
|
|
426
409
|
AND NULLIF(payload->>'tenantId', '') IS NOT NULL`, [entityEventName("tenant-membership", "created"), userId]);
|
|
427
410
|
return rows.map((row) => row.tenant_id); // @cast-boundary db-row
|
|
428
411
|
}
|
|
429
|
-
//
|
|
430
|
-
// - "anonymize" / "blockDelete" → "anonymize"
|
|
431
|
-
//
|
|
432
|
-
// - "hardDelete" / "softDelete" / null → "delete" (
|
|
412
|
+
// Map retention.strategy to user-data-rights.UserDataDeleteStrategy.
|
|
413
|
+
// - "anonymize" / "blockDelete" → "anonymize": retention requirements keep
|
|
414
|
+
// the data physically present while removing its association with the person.
|
|
415
|
+
// - "hardDelete" / "softDelete" / null → "delete" (default).
|
|
433
416
|
//
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
// werden kann (siehe run-forget-cleanup.test.ts).
|
|
417
|
+
// Keeping this mapping in one function makes drift between retention strategies
|
|
418
|
+
// and user-data-rights hooks explicit and testable.
|
|
437
419
|
export function policyToStrategy(retentionStrategy) {
|
|
438
420
|
if (retentionStrategy === "anonymize" || retentionStrategy === "blockDelete") {
|
|
439
421
|
return "anonymize";
|
|
@@ -16,63 +16,64 @@ export interface UserExportFileRef {
|
|
|
16
16
|
readonly fileRefId: string;
|
|
17
17
|
readonly storageKey: string;
|
|
18
18
|
readonly fileName: string;
|
|
19
|
-
/** Tenant
|
|
19
|
+
/** Tenant containing the file; the same fileRefId cannot be shared across tenants. */
|
|
20
20
|
readonly tenantId: TenantId;
|
|
21
21
|
/**
|
|
22
|
-
* ZIP-internal
|
|
23
|
-
*
|
|
24
|
-
* fileRefs[]
|
|
25
|
-
* path
|
|
22
|
+
* ZIP-internal path under which the file is placed in the export ZIP.
|
|
23
|
+
* Reader tools (compliance audit, self-service portal) use this field
|
|
24
|
+
* to link bundle.json fileRefs[] to the files/-paths. Guaranteed to be
|
|
25
|
+
* safe from path traversal via sanitizeZipFilename.
|
|
26
26
|
*/
|
|
27
27
|
readonly zipPath: string;
|
|
28
28
|
}
|
|
29
29
|
export interface UserExportTenantSection {
|
|
30
30
|
readonly tenantId: TenantId;
|
|
31
|
-
/**
|
|
31
|
+
/** One snippet per entity ({entity, rows[]}); empty when the hook returns null. */
|
|
32
32
|
readonly entities: ReadonlyArray<UserDataExportSnippet>;
|
|
33
33
|
}
|
|
34
34
|
export interface UserExportBundle {
|
|
35
35
|
readonly userId: string;
|
|
36
|
-
/** ISO-8601
|
|
36
|
+
/** ISO-8601 generation timestamp for the audit trail. */
|
|
37
37
|
readonly generatedAt: string;
|
|
38
|
-
/**
|
|
38
|
+
/** One section for each tenant the user belongs to; orphan users have tenants=[]. */
|
|
39
39
|
readonly tenants: ReadonlyArray<UserExportTenantSection>;
|
|
40
40
|
/**
|
|
41
|
-
* Flat
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* Flat list of all fileRefs from every tenant section. The later ZIP
|
|
42
|
+
* construction job iterates over this list and retrieves binaries from
|
|
43
|
+
* the storage provider. Until then, it is the operator's manifest.
|
|
44
44
|
*/
|
|
45
45
|
readonly fileRefs: ReadonlyArray<UserExportFileRef>;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
* Pure function:
|
|
49
|
-
*
|
|
48
|
+
* Pure function: invokes all EXT_USER_DATA hooks for each tenant
|
|
49
|
+
* membership and collects snippets into a UserExportBundle.
|
|
50
50
|
*
|
|
51
|
-
* **Memory
|
|
51
|
+
* **Memory footprint:**
|
|
52
52
|
*
|
|
53
|
-
*
|
|
54
|
-
* - File
|
|
55
|
-
*
|
|
56
|
-
* - ZIP
|
|
57
|
-
* S3
|
|
58
|
-
* concurrent parts).
|
|
53
|
+
* The entire storage path is streaming-bound:
|
|
54
|
+
* - File binaries use provider.readStream and chunk streaming, scaling
|
|
55
|
+
* to arbitrary file sizes without a heap spike.
|
|
56
|
+
* - ZIP writing uses provider.writeStream; local storage uses
|
|
57
|
+
* fs.createWriteStream, while S3 uses lib-storage.Upload (multipart,
|
|
58
|
+
* with an approximately 20 MB heap bound at 4 concurrent parts).
|
|
59
59
|
*
|
|
60
|
-
* **
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
60
|
+
* **Only non-streaming path:** the bundle object itself (the bundle.json
|
|
61
|
+
* content). It is built entirely in memory before `bundleToZipEntries`
|
|
62
|
+
* yields it as a ZIP entry. Hooks return snippets as plain objects
|
|
63
|
+
* (see UserDataExportSnippet).
|
|
64
64
|
*
|
|
65
|
-
* **Threshold:**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* **Threshold:** A web app with approximately 500 table rows per user
|
|
66
|
+
* produces about 500 KB of JSON. 50k rows produce about 50 MB. 100k+
|
|
67
|
+
* rows per user (for example, long-running rental-portal logs) create
|
|
68
|
+
* noticeable heap pressure.
|
|
68
69
|
*
|
|
69
|
-
* **
|
|
70
|
-
*
|
|
71
|
-
* JSON
|
|
72
|
-
*
|
|
70
|
+
* **If this becomes a problem:** refactor runUserExport to use an
|
|
71
|
+
* AsyncIterable; hooks would yield snippets instead of returning objects,
|
|
72
|
+
* and the bundle schema would use JSON Lines. bundleToZipEntries would
|
|
73
|
+
* stream entries line by line. This is a non-trivial schema change.
|
|
73
74
|
*
|
|
74
|
-
* **Operator
|
|
75
|
-
*
|
|
75
|
+
* **Operator signal:** if bundle.json exceeds 100 MB in the ZIP, trigger
|
|
76
|
+
* telemetry and evaluate a schema refactor.
|
|
76
77
|
*/
|
|
77
78
|
export declare function runUserExport(args: RunUserExportArgs): Promise<UserExportBundle>;
|
|
78
79
|
export {};
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
2
|
-
// User
|
|
3
|
-
// Art. 20 (
|
|
2
|
+
// User data export pipeline — GDPR Art. 15 (access) +
|
|
3
|
+
// Art. 20 (data portability).
|
|
4
4
|
//
|
|
5
|
-
// Pure
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// Pure pipeline function: invokes all EXT_USER_DATA export hooks for
|
|
6
|
+
// all of a user's tenant memberships and collects the results into a
|
|
7
|
+
// structured bundle.
|
|
8
8
|
//
|
|
9
|
-
// **Async ZIP +
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
9
|
+
// **Async ZIP + storage deferred:** This foundation returns the bundle
|
|
10
|
+
// inline as a JSON object. Apps with large file collections need a job
|
|
11
|
+
// wrapper that writes the bundle to S3 or local storage and produces
|
|
12
|
+
// signed URLs; that can be added when a real use case produces more than
|
|
13
|
+
// 10 MB of output. Until then, inline output covers 99% of apps
|
|
14
|
+
// (user profiles, file metadata, and activity history).
|
|
15
15
|
//
|
|
16
|
-
// **Cross-
|
|
17
|
-
// Tenant
|
|
18
|
-
// docs/plans/architecture/user-data-rights.md "Cross-Tenant-Semantik".
|
|
16
|
+
// **Cross-tenant iteration:** As with the forget path, data from Tenant A
|
|
17
|
+
// and Tenant B is included in the same bundle.
|
|
19
18
|
//
|
|
20
|
-
// **PII
|
|
21
|
-
//
|
|
22
|
-
//
|
|
19
|
+
// **PII surface:** Hooks decide which fields enter the bundle.
|
|
20
|
+
// user-data-rights-defaults/hooks/user.userdata-hook explicitly exposes
|
|
21
|
+
// neither passwordHash nor roles (privileged columns).
|
|
23
22
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, isPiiCiphertext, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
24
23
|
import { collectEncryptedFieldNames, configuredEntityFieldEncryption, createTenantDb, decryptEntityFieldValues, selectMany, } from "@cosmicdrift/kumiko-framework/db";
|
|
25
24
|
import { EXT_USER_DATA, extensionUsageEscapeHatchReason, maskWriteOnlyFields, } from "@cosmicdrift/kumiko-framework/engine";
|
|
@@ -45,38 +44,38 @@ function buildHookDb(db, tenantId, entry, args) {
|
|
|
45
44
|
});
|
|
46
45
|
}
|
|
47
46
|
/**
|
|
48
|
-
* Pure function:
|
|
49
|
-
*
|
|
47
|
+
* Pure function: invokes all EXT_USER_DATA hooks for each tenant
|
|
48
|
+
* membership and collects snippets into a UserExportBundle.
|
|
50
49
|
*
|
|
51
|
-
* **Memory
|
|
50
|
+
* **Memory footprint:**
|
|
52
51
|
*
|
|
53
|
-
*
|
|
54
|
-
* - File
|
|
55
|
-
*
|
|
56
|
-
* - ZIP
|
|
57
|
-
* S3
|
|
58
|
-
* concurrent parts).
|
|
52
|
+
* The entire storage path is streaming-bound:
|
|
53
|
+
* - File binaries use provider.readStream and chunk streaming, scaling
|
|
54
|
+
* to arbitrary file sizes without a heap spike.
|
|
55
|
+
* - ZIP writing uses provider.writeStream; local storage uses
|
|
56
|
+
* fs.createWriteStream, while S3 uses lib-storage.Upload (multipart,
|
|
57
|
+
* with an approximately 20 MB heap bound at 4 concurrent parts).
|
|
59
58
|
*
|
|
60
|
-
* **
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* **Only non-streaming path:** the bundle object itself (the bundle.json
|
|
60
|
+
* content). It is built entirely in memory before `bundleToZipEntries`
|
|
61
|
+
* yields it as a ZIP entry. Hooks return snippets as plain objects
|
|
62
|
+
* (see UserDataExportSnippet).
|
|
64
63
|
*
|
|
65
|
-
* **Threshold:**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
64
|
+
* **Threshold:** A web app with approximately 500 table rows per user
|
|
65
|
+
* produces about 500 KB of JSON. 50k rows produce about 50 MB. 100k+
|
|
66
|
+
* rows per user (for example, long-running rental-portal logs) create
|
|
67
|
+
* noticeable heap pressure.
|
|
68
68
|
*
|
|
69
|
-
* **
|
|
70
|
-
*
|
|
71
|
-
* JSON
|
|
72
|
-
*
|
|
69
|
+
* **If this becomes a problem:** refactor runUserExport to use an
|
|
70
|
+
* AsyncIterable; hooks would yield snippets instead of returning objects,
|
|
71
|
+
* and the bundle schema would use JSON Lines. bundleToZipEntries would
|
|
72
|
+
* stream entries line by line. This is a non-trivial schema change.
|
|
73
73
|
*
|
|
74
|
-
* **Operator
|
|
75
|
-
*
|
|
74
|
+
* **Operator signal:** if bundle.json exceeds 100 MB in the ZIP, trigger
|
|
75
|
+
* telemetry and evaluate a schema refactor.
|
|
76
76
|
*/
|
|
77
77
|
export async function runUserExport(args) {
|
|
78
78
|
const { db, registry, userId, now } = args;
|
|
79
|
-
// Memberships → Tenant-Liste fuer Hook-Iteration.
|
|
80
79
|
const memberships = await selectMany(db, tenantMembershipsTable, {
|
|
81
80
|
userId,
|
|
82
81
|
});
|
|
@@ -126,15 +125,15 @@ export async function runUserExport(args) {
|
|
|
126
125
|
}
|
|
127
126
|
tenants.push({ tenantId, entities });
|
|
128
127
|
}
|
|
129
|
-
// Edge
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
//
|
|
128
|
+
// Edge case "0 memberships": tenant-agnostic hooks (for example, the
|
|
129
|
+
// user hook) would run multiple times during cross-tenant iteration.
|
|
130
|
+
// An orphan user with no membership would otherwise skip them entirely.
|
|
131
|
+
// As in the forget runner, add a special run with a pseudo-tenant so
|
|
132
|
+
// the global user row is included in the bundle. Tenant-scoped hooks
|
|
133
|
+
// are no-ops.
|
|
135
134
|
//
|
|
136
|
-
//
|
|
137
|
-
//
|
|
135
|
+
// Cross-tenant consistency must not silently break when memberships are
|
|
136
|
+
// empty.
|
|
138
137
|
if (tenantList.length === 0 && hookEntries.length > 0) {
|
|
139
138
|
const orphanEntities = [];
|
|
140
139
|
for (const entry of hookEntries) {
|
|
@@ -173,14 +172,14 @@ export async function runUserExport(args) {
|
|
|
173
172
|
fileRefs,
|
|
174
173
|
};
|
|
175
174
|
}
|
|
176
|
-
// Art. 20
|
|
177
|
-
//
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
175
|
+
// Art. 20 requires the data, not its ciphertext: export hooks read raw
|
|
176
|
+
// rows outside the executor's decryption layer, so this central pass
|
|
177
|
+
// decrypts encrypted entity fields after each hook. Without a configured
|
|
178
|
+
// cipher, it exports an explicit marker instead of presenting the base64
|
|
179
|
+
// blob as a value (leak by confusion).
|
|
181
180
|
const ENCRYPTED_UNAVAILABLE = "[encrypted:unavailable]";
|
|
182
181
|
// Shared decrypt sweep, used for both `snippet.rows` and `snippet.fileRefs`
|
|
183
|
-
//
|
|
182
|
+
// `encryptedFields` is keyed by field NAME, so it stays a no-op
|
|
184
183
|
// for today's `fileRef` entity but covers a future generically-encrypted
|
|
185
184
|
// side-channel field too.
|
|
186
185
|
async function decryptRecords(records, encryptedFields, kms) {
|
|
@@ -236,7 +235,6 @@ async function decryptSnippetFields(registry, hookEntityName, snippet) {
|
|
|
236
235
|
}));
|
|
237
236
|
return { ...snippet, rows, fileRefs };
|
|
238
237
|
}
|
|
239
|
-
// Pseudo-
|
|
240
|
-
//
|
|
241
|
-
// Hooks finden hier nichts (no-op).
|
|
238
|
+
// Pseudo-tenant for users without active memberships. It uses an
|
|
239
|
+
// RFC4122 null UUID; tenant-scoped hooks find no data here (no-op).
|
|
242
240
|
const SYSTEM_TENANT_ID_FOR_ORPHANS = "00000000-0000-0000-0000-000000000000";
|