@cosmicdrift/kumiko-bundled-features 0.362.0 → 0.364.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/dist/admin-shell/feature.d.ts +3 -0
- package/dist/admin-shell/feature.js +6 -1
- package/dist/agent-tools/agent-manifest.js +2 -1
- package/dist/agent-tools/filter-ops.d.ts +1 -1
- package/dist/agent-tools/filter-ops.js +1 -3
- package/dist/agent-tools/tool-dispatch.js +2 -1
- package/dist/audit/handlers/list.query.js +9 -5
- package/dist/auth-email-password/bootstrap.d.ts +8 -5
- package/dist/auth-email-password/bootstrap.js +12 -4
- package/dist/auth-email-password/email-rate-limits.d.ts +4 -0
- package/dist/auth-email-password/email-rate-limits.js +13 -0
- package/dist/auth-email-password/email-templates.d.ts +4 -26
- package/dist/auth-email-password/email-templates.js +9 -101
- package/dist/auth-email-password/feature.js +24 -6
- package/dist/auth-email-password/handlers/change-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/change-password.write.js +6 -2
- package/dist/auth-email-password/handlers/confirm-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept-with-login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-accept.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/invite-create.write.d.ts +2 -2
- package/dist/auth-email-password/handlers/invite-create.write.js +4 -4
- package/dist/auth-email-password/handlers/invite-signup-complete.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/login.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/logout.write.js +6 -2
- package/dist/auth-email-password/handlers/request-account-unlock.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-account-unlock.write.js +2 -4
- package/dist/auth-email-password/handlers/request-email-verification.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-email-verification.write.js +2 -4
- package/dist/auth-email-password/handlers/request-password-reset.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/request-password-reset.write.js +2 -4
- package/dist/auth-email-password/handlers/reset-password.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-confirm.write.js +3 -4
- package/dist/auth-email-password/handlers/signup-request.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/signup-request.write.js +2 -1
- package/dist/auth-email-password/handlers/switch-tenant-mfa-gate.write.d.ts +1 -1
- package/dist/auth-email-password/handlers/token-request-handler.d.ts +2 -3
- package/dist/auth-email-password/handlers/token-request-handler.js +2 -2
- package/dist/auth-email-password/handlers/verify-email.write.d.ts +1 -1
- package/dist/auth-email-password/index.d.ts +2 -2
- package/dist/auth-email-password/index.js +1 -1
- package/dist/auth-email-password/magic-link-mail.d.ts +3 -2
- package/dist/auth-email-password/magic-link-mail.js +19 -14
- package/dist/auth-email-password/mail-notifications.d.ts +32 -0
- package/dist/auth-email-password/mail-notifications.js +116 -0
- package/dist/auth-email-password/signup-activation.d.ts +1 -1
- package/dist/auth-email-password/signup-activation.js +2 -3
- package/dist/auth-email-password/web/auth-gate.js +16 -4
- package/dist/auth-foundation/types.js +2 -8
- package/dist/auth-mfa/feature.js +4 -4
- package/dist/auth-mfa/handlers/disable.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-confirm.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start-preauth.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/enable-start.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/regenerate-recovery.write.d.ts +1 -1
- package/dist/auth-mfa/handlers/verify.write.d.ts +1 -1
- package/dist/auth-mfa/hmac-token-codec.js +4 -4
- package/dist/auth-mfa/mfa-challenge-token.js +3 -3
- package/dist/auth-mfa/mfa-preauth-setup-token.js +3 -3
- package/dist/auth-mfa/mfa-setup-token.js +8 -8
- package/dist/billing-foundation/constants.d.ts +3 -3
- package/dist/billing-foundation/constants.js +5 -4
- package/dist/billing-foundation/consumer-protection/confirmation-mail.d.ts +32 -14
- package/dist/billing-foundation/consumer-protection/confirmation-mail.js +74 -32
- package/dist/billing-foundation/consumer-protection/consent-gate.d.ts +3 -0
- package/dist/billing-foundation/consumer-protection/consent-gate.js +21 -2
- package/dist/billing-foundation/consumer-protection/issue-confirmation.js +3 -3
- package/dist/billing-foundation/consumer-protection/mail-notifications.d.ts +8 -0
- package/dist/billing-foundation/consumer-protection/mail-notifications.js +95 -0
- package/dist/billing-foundation/consumer-protection/register-consent.js +2 -0
- package/dist/billing-foundation/consumer-protection/register-termination.js +11 -1
- package/dist/billing-foundation/consumer-protection/termination-mail.d.ts +25 -13
- package/dist/billing-foundation/consumer-protection/termination-mail.js +11 -23
- package/dist/billing-foundation/consumer-protection/termination-notify.js +2 -2
- package/dist/billing-foundation/consumer-protection/termination-pages.js +1 -3
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.d.ts +6 -0
- package/dist/billing-foundation/consumer-protection/termination-receipt-limit.js +34 -0
- package/dist/billing-foundation/consumer-protection/termination-record.d.ts +2 -0
- package/dist/billing-foundation/consumer-protection/termination-record.js +7 -1
- package/dist/billing-foundation/consumer-protection/termination-request.js +31 -21
- package/dist/billing-foundation/consumer-protection/termination-texts.d.ts +1 -1
- package/dist/billing-foundation/consumer-protection/termination-texts.js +6 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.d.ts +10 -0
- package/dist/billing-foundation/consumer-protection/withdrawal-period.js +26 -0
- package/dist/billing-foundation/handlers/create-checkout-session.write.js +11 -5
- package/dist/billing-foundation/handlers/process-event.write.js +12 -8
- package/dist/billing-foundation/handlers/process-payment-event.write.js +1 -1
- package/dist/billing-foundation/i18n.js +3 -0
- package/dist/billing-foundation/subscription-tier-sync.d.ts +7 -1
- package/dist/billing-foundation/subscription-tier-sync.js +16 -9
- package/dist/billing-foundation/types.d.ts +7 -2
- package/dist/cap-counter/calendar-cap-usage-reservation.js +2 -0
- package/dist/cap-counter/enforce-cap.d.ts +18 -1
- package/dist/cap-counter/enforce-cap.js +49 -6
- package/dist/cap-counter/index.d.ts +1 -1
- package/dist/cap-counter/index.js +1 -1
- package/dist/cap-counter/with-cap-enforcement.d.ts +2 -0
- package/dist/cap-overview/handlers/tenant-caps-list.query.js +2 -1
- package/dist/cap-overview/web/cap-usage-cell.js +5 -4
- package/dist/channel-email/email-channel.d.ts +2 -2
- package/dist/channel-email/email-channel.js +101 -26
- package/dist/channel-email/feature.js +3 -3
- package/dist/channel-email/index.d.ts +1 -1
- package/dist/channel-email/index.js +1 -1
- package/dist/channel-email/pii-guard.js +10 -0
- package/dist/channel-email/smtp-transport.js +13 -0
- package/dist/channel-email/types.d.ts +8 -0
- package/dist/channel-in-app/handlers/mark-all-read.write.d.ts +1 -1
- package/dist/channel-in-app/handlers/mark-read.write.d.ts +1 -1
- package/dist/compliance-profiles/handlers/set-profile.write.d.ts +1 -1
- package/dist/config/handlers/reset.write.d.ts +1 -1
- package/dist/config/handlers/set.write.d.ts +1 -1
- package/dist/config/handlers/set.write.js +3 -4
- package/dist/config/resolver.d.ts +1 -0
- package/dist/config/resolver.js +11 -14
- package/dist/config/write-helpers.d.ts +2 -0
- package/dist/config/write-helpers.js +19 -0
- package/dist/crypto-shredding/handlers/forget-subject.write.d.ts +1 -1
- package/dist/crypto-shredding/handlers/forget-subject.write.js +29 -19
- package/dist/custom-fields/handlers/update-tenant-field.write.js +0 -1
- package/dist/custom-fields/lib/value-schema.js +10 -11
- package/dist/custom-fields/wire-user-data-rights.js +7 -6
- package/dist/data-retention/index.d.ts +1 -0
- package/dist/data-retention/index.js +1 -0
- package/dist/data-retention/run-retention-cleanup.js +6 -6
- package/dist/delivery/attachment-ref.d.ts +41 -0
- package/dist/delivery/attachment-ref.js +78 -0
- package/dist/delivery/channel-context.d.ts +8 -4
- package/dist/delivery/channel-context.js +94 -2
- package/dist/delivery/constants.d.ts +1 -0
- package/dist/delivery/constants.js +1 -0
- package/dist/delivery/delivery-service.d.ts +4 -1
- package/dist/delivery/delivery-service.js +38 -32
- package/dist/delivery/feature.js +1 -1
- package/dist/delivery/handlers/resubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/resubscribe-user.write.d.ts +1 -1
- package/dist/delivery/handlers/set-preference.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-address.write.d.ts +1 -1
- package/dist/delivery/handlers/unsubscribe-user.write.d.ts +1 -1
- package/dist/delivery/index.d.ts +5 -2
- package/dist/delivery/index.js +4 -1
- package/dist/delivery/jobs.js +66 -14
- package/dist/delivery/notify-factory.js +4 -1
- package/dist/delivery/testing.d.ts +4 -1
- package/dist/delivery/types.d.ts +26 -14
- package/dist/delivery/unsubscribe.js +4 -1
- package/dist/derivatives-sharp/render.js +1 -18
- package/dist/document-ingest-foundation/pages.js +4 -4
- package/dist/feature-toggles/handlers/set.write.d.ts +1 -1
- package/dist/feature-toggles/toggle-sync-signal.js +4 -4
- package/dist/file-derivatives/handlers/public-variant.query.js +1 -1
- package/dist/file-foundation/index.d.ts +1 -0
- package/dist/file-foundation/index.js +1 -0
- package/dist/file-foundation/read-file-ref-content.d.ts +27 -0
- package/dist/file-foundation/read-file-ref-content.js +51 -0
- package/dist/files-tenant-data/hooks.js +4 -0
- package/dist/folders/web/folder-manager.js +3 -2
- package/dist/form-draft/db/queries/cleanup.js +4 -4
- package/dist/form-draft/db/queries/draft-count.js +2 -2
- package/dist/form-draft/db/queries/owned-file-refs.js +2 -2
- package/dist/form-draft/handlers/discard.write.d.ts +1 -1
- package/dist/form-draft/handlers/save.write.d.ts +1 -1
- package/dist/form-draft/release-file-refs.js +3 -2
- package/dist/inbound-mail-foundation/handlers/connect-account.write.js +1 -1
- package/dist/inbound-mail-foundation/handlers/ingest-message.write.js +6 -6
- package/dist/inbound-mail-foundation/handlers/scope-visibility.d.ts +1 -1
- package/dist/inbound-mail-foundation/oauth-access-token.js +8 -3
- package/dist/inbound-mail-foundation/oauth-state.js +7 -7
- package/dist/inbound-mail-foundation/types.js +4 -4
- package/dist/inbound-mail-foundation/watch-supervisor.js +24 -13
- package/dist/inbound-provider-imap/imap-client.js +4 -3
- package/dist/jobs/db/queries/stale-run-sweep.d.ts +1 -1
- package/dist/jobs/db/queries/stale-run-sweep.js +12 -5
- package/dist/jobs/feature.js +5 -3
- package/dist/jobs/handlers/list.query.js +3 -1
- package/dist/jobs/handlers/retry.write.d.ts +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.d.ts +1 -1
- package/dist/jobs/handlers/stale-run-sweep.job.js +2 -2
- package/dist/jobs/handlers/trigger.write.d.ts +1 -1
- package/dist/jobs/job-run-logger.js +32 -10
- package/dist/jobs/tenant-job-run-table.js +3 -2
- package/dist/ledger/entity.js +3 -1
- package/dist/ledger/recurring.js +1 -3
- package/dist/managed-pages/branding.d.ts +2 -0
- package/dist/managed-pages/branding.js +24 -2
- package/dist/managed-pages/feature.js +1 -0
- package/dist/managed-pages/handlers/set.write.d.ts +1 -1
- package/dist/managed-pages/i18n.js +1 -0
- package/dist/managed-pages/screens/branding-screen.js +8 -1
- package/dist/page-render/branding.d.ts +1 -0
- package/dist/page-render/branding.js +1 -0
- package/dist/page-render/markdown.js +1 -1
- package/dist/personal-access-tokens/feature.js +1 -1
- package/dist/personal-access-tokens/handlers/create.write.d.ts +1 -1
- package/dist/personal-access-tokens/handlers/list.query.js +2 -1
- package/dist/personal-access-tokens/handlers/revoke.write.d.ts +1 -1
- package/dist/personal-access-tokens/revoke-for-user.js +2 -1
- package/dist/renderer-foundation/api.d.ts +13 -13
- package/dist/renderer-foundation/api.js +1 -1
- package/dist/renderer-foundation/constants.js +2 -4
- package/dist/renderer-foundation/feature.js +7 -7
- package/dist/renderer-foundation/index.d.ts +2 -1
- package/dist/renderer-foundation/index.js +1 -0
- package/dist/renderer-foundation/types.d.ts +94 -32
- package/dist/renderer-pdf/feature.js +7 -1
- package/dist/renderer-pdf/render-table-pdf.d.ts +27 -33
- package/dist/renderer-pdf/render-table-pdf.js +95 -34
- package/dist/secrets/feature.js +4 -3
- package/dist/secrets/handlers/delete.write.d.ts +1 -1
- package/dist/secrets/handlers/set.write.d.ts +1 -1
- package/dist/secrets/secrets-context.js +15 -3
- package/dist/sessions/db/queries/cleanup.js +2 -2
- package/dist/sessions/feature.js +1 -1
- package/dist/sessions/handlers/list.query.js +2 -1
- package/dist/sessions/handlers/revoke-all-for-user.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke-all-others.write.d.ts +1 -1
- package/dist/sessions/handlers/revoke.write.d.ts +1 -1
- package/dist/shared/attachment-file-name.d.ts +3 -0
- package/dist/shared/attachment-file-name.js +35 -0
- package/dist/shared/chunked-entity-migration.d.ts +1 -0
- package/dist/shared/chunked-entity-migration.js +1 -1
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +3 -0
- package/dist/shared/is-tenant-db.js +1 -1
- package/dist/shared/mail-template-body.d.ts +7 -0
- package/dist/shared/mail-template-body.js +27 -0
- package/dist/shared/sharp-input-limits.d.ts +6 -0
- package/dist/shared/sharp-input-limits.js +22 -0
- package/dist/shared/signup-handover.js +2 -2
- package/dist/subscription-stripe/plugin-methods.js +18 -8
- package/dist/subscription-stripe/verify-webhook.d.ts +1 -1
- package/dist/tags/handlers/assign-tag.write.js +3 -1
- package/dist/tags/web/tag-chip.js +2 -0
- package/dist/template-resolver/api.d.ts +30 -2
- package/dist/template-resolver/api.js +42 -6
- package/dist/template-resolver/compile-document-body.d.ts +12 -0
- package/dist/template-resolver/compile-document-body.js +205 -0
- package/dist/template-resolver/constants.d.ts +13 -4
- package/dist/template-resolver/constants.js +32 -10
- package/dist/template-resolver/document-sanitizer.d.ts +32 -0
- package/dist/template-resolver/document-sanitizer.js +201 -0
- package/dist/template-resolver/document-schema.d.ts +21 -0
- package/dist/template-resolver/document-schema.js +316 -0
- package/dist/template-resolver/feature.d.ts +20 -0
- package/dist/template-resolver/feature.js +53 -6
- package/dist/template-resolver/handlers/bundle-import.d.ts +20 -0
- package/dist/template-resolver/handlers/bundle-import.js +147 -0
- package/dist/template-resolver/handlers/collection-set.write.d.ts +1 -1
- package/dist/template-resolver/handlers/collection-set.write.js +25 -2
- package/dist/template-resolver/handlers/copy-locale.write.d.ts +19 -0
- package/dist/template-resolver/handlers/copy-locale.write.js +134 -0
- package/dist/template-resolver/handlers/diff-to-system.query.d.ts +22 -0
- package/dist/template-resolver/handlers/diff-to-system.query.js +30 -0
- package/dist/template-resolver/handlers/discard-draft.write.d.ts +13 -0
- package/dist/template-resolver/handlers/discard-draft.write.js +37 -0
- package/dist/template-resolver/handlers/export-bundle.query.d.ts +35 -0
- package/dist/template-resolver/handlers/export-bundle.query.js +118 -0
- package/dist/template-resolver/handlers/find-by-id.query.d.ts +2 -0
- package/dist/template-resolver/handlers/find-by-id.query.js +9 -6
- package/dist/template-resolver/handlers/import-bundle-system.write.d.ts +39 -0
- package/dist/template-resolver/handlers/import-bundle-system.write.js +36 -0
- package/dist/template-resolver/handlers/import-bundle.write.d.ts +40 -0
- package/dist/template-resolver/handlers/import-bundle.write.js +19 -0
- package/dist/template-resolver/handlers/layout-source.query.d.ts +31 -0
- package/dist/template-resolver/handlers/layout-source.query.js +87 -0
- package/dist/template-resolver/handlers/list.query.d.ts +4 -1
- package/dist/template-resolver/handlers/list.query.js +9 -8
- package/dist/template-resolver/handlers/override-diff.d.ts +20 -0
- package/dist/template-resolver/handlers/override-diff.js +41 -0
- package/dist/template-resolver/handlers/overview.query.d.ts +65 -0
- package/dist/template-resolver/handlers/overview.query.js +205 -0
- package/dist/template-resolver/handlers/preview.query.d.ts +12 -0
- package/dist/template-resolver/handlers/preview.query.js +24 -0
- package/dist/template-resolver/handlers/reset-to-system.write.d.ts +8 -0
- package/dist/template-resolver/handlers/reset-to-system.write.js +50 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.d.ts +8 -0
- package/dist/template-resolver/handlers/resource-link-pdf.write.js +32 -0
- package/dist/template-resolver/handlers/resource-remove.write.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-remove.write.js +44 -0
- package/dist/template-resolver/handlers/resource-shared.d.ts +9 -0
- package/dist/template-resolver/handlers/resource-shared.js +14 -0
- package/dist/template-resolver/handlers/resource-upload.write.d.ts +13 -0
- package/dist/template-resolver/handlers/resource-upload.write.js +98 -0
- package/dist/template-resolver/handlers/send-test.write.d.ts +13 -0
- package/dist/template-resolver/handlers/send-test.write.js +108 -0
- package/dist/template-resolver/handlers/set.write.d.ts +5 -2
- package/dist/template-resolver/handlers/set.write.js +25 -3
- package/dist/template-resolver/handlers/shared.d.ts +9 -5
- package/dist/template-resolver/handlers/shared.js +6 -2
- package/dist/template-resolver/handlers/template-gates.d.ts +36 -0
- package/dist/template-resolver/handlers/template-gates.js +142 -0
- package/dist/template-resolver/handlers/template-write.d.ts +45 -0
- package/dist/template-resolver/handlers/template-write.js +141 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.d.ts +49 -0
- package/dist/template-resolver/handlers/tenant-overrides.query.js +112 -0
- package/dist/template-resolver/handlers/toggle-status.write.d.ts +27 -2
- package/dist/template-resolver/handlers/toggle-status.write.js +47 -15
- package/dist/template-resolver/handlers/upsert-system.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-system.write.js +14 -59
- package/dist/template-resolver/handlers/upsert-tenant.write.d.ts +5 -8
- package/dist/template-resolver/handlers/upsert-tenant.write.js +11 -62
- package/dist/template-resolver/handlers/usage.query.d.ts +17 -0
- package/dist/template-resolver/handlers/usage.query.js +24 -0
- package/dist/template-resolver/handlers/validate.query.d.ts +74 -0
- package/dist/template-resolver/handlers/validate.query.js +80 -0
- package/dist/template-resolver/index.d.ts +19 -1
- package/dist/template-resolver/index.js +17 -1
- package/dist/template-resolver/layout-appearance.d.ts +10 -0
- package/dist/template-resolver/layout-appearance.js +26 -0
- package/dist/template-resolver/layout-regions.d.ts +23 -0
- package/dist/template-resolver/layout-regions.js +125 -0
- package/dist/template-resolver/linked-resources.d.ts +17 -0
- package/dist/template-resolver/linked-resources.js +75 -0
- package/dist/template-resolver/pdf-constants.d.ts +7 -0
- package/dist/template-resolver/pdf-constants.js +8 -0
- package/dist/template-resolver/preview-display-html.d.ts +7 -0
- package/dist/template-resolver/preview-display-html.js +27 -0
- package/dist/template-resolver/print-document.d.ts +1 -0
- package/dist/template-resolver/print-document.js +55 -0
- package/dist/template-resolver/qualified-names.d.ts +34 -0
- package/dist/template-resolver/qualified-names.js +34 -0
- package/dist/template-resolver/record-mapping.d.ts +2 -0
- package/dist/template-resolver/record-mapping.js +21 -0
- package/dist/template-resolver/resource-image.d.ts +15 -0
- package/dist/template-resolver/resource-image.js +34 -0
- package/dist/template-resolver/seeding.d.ts +14 -1
- package/dist/template-resolver/seeding.js +125 -18
- package/dist/template-resolver/store-template-resource.d.ts +64 -0
- package/dist/template-resolver/store-template-resource.js +264 -0
- package/dist/template-resolver/table.d.ts +150 -8
- package/dist/template-resolver/table.js +20 -2
- package/dist/template-resolver/template-bundle.d.ts +36 -0
- package/dist/template-resolver/template-bundle.js +60 -0
- package/dist/template-resolver/template-diff.d.ts +7 -0
- package/dist/template-resolver/template-diff.js +68 -0
- package/dist/template-resolver/template-draft.d.ts +51 -0
- package/dist/template-resolver/template-draft.js +72 -0
- package/dist/template-resolver/template-meta.d.ts +85 -0
- package/dist/template-resolver/template-meta.js +145 -0
- package/dist/template-resolver/template-preview.d.ts +103 -0
- package/dist/template-resolver/template-preview.js +418 -0
- package/dist/template-resolver/template-resources.d.ts +19 -0
- package/dist/template-resolver/template-resources.js +73 -0
- package/dist/template-resolver/template-usage.d.ts +16 -0
- package/dist/template-resolver/template-usage.js +35 -0
- package/dist/template-resolver/template-validation.d.ts +22 -0
- package/dist/template-resolver/template-validation.js +173 -0
- package/dist/template-resolver/testing.js +5 -5
- package/dist/template-resolver/user-content-table.d.ts +14 -8
- package/dist/template-resolver/web/client-plugin.d.ts +1 -0
- package/dist/template-resolver/web/client-plugin.js +12 -7
- package/dist/template-resolver/web/document-editor-extensions.d.ts +18 -0
- package/dist/template-resolver/web/document-editor-extensions.js +145 -0
- package/dist/template-resolver/web/document-editor.d.ts +4 -0
- package/dist/template-resolver/web/document-editor.js +17 -0
- package/dist/template-resolver/web/document-tiptap-editor.d.ts +3 -0
- package/dist/template-resolver/web/document-tiptap-editor.js +492 -0
- package/dist/template-resolver/web/document-toolbar-blocks.d.ts +41 -0
- package/dist/template-resolver/web/document-toolbar-blocks.js +19 -0
- package/dist/template-resolver/web/editor-handle.d.ts +3 -0
- package/dist/template-resolver/web/editor-handle.js +24 -0
- package/dist/template-resolver/web/i18n.js +91 -0
- package/dist/template-resolver/web/index.d.ts +5 -0
- package/dist/template-resolver/web/index.js +5 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-line-editor-lazy.js +16 -0
- package/dist/template-resolver/web/placeholder-line-editor.d.ts +17 -0
- package/dist/template-resolver/web/placeholder-line-editor.js +94 -0
- package/dist/template-resolver/web/placeholder-text.d.ts +4 -0
- package/dist/template-resolver/web/placeholder-text.js +34 -0
- package/dist/template-resolver/web/use-stable-by-key.d.ts +1 -0
- package/dist/template-resolver/web/use-stable-by-key.js +13 -0
- package/dist/tenant/handlers/add-member.write.d.ts +1 -1
- package/dist/tenant/handlers/cancel-invitation.write.d.ts +1 -1
- package/dist/tenant/handlers/create.write.d.ts +1 -1
- package/dist/tenant/handlers/remove-member.write.d.ts +1 -1
- package/dist/tenant/handlers/team-list.query.js +3 -2
- package/dist/tenant/handlers/toggle-enabled.write.d.ts +2 -2
- package/dist/tenant/handlers/update-member-roles.write.d.ts +1 -1
- package/dist/tenant/handlers/update.write.d.ts +1 -1
- package/dist/tenant/seeding.js +31 -6
- package/dist/tenant-handover/handlers/claim.write.d.ts +1 -1
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.d.ts +2 -2
- package/dist/tenant-lifecycle/handlers/cancel-destruction.write.js +2 -1
- package/dist/tenant-lifecycle/handlers/request-destruction.write.d.ts +1 -1
- package/dist/tier-engine/feature.js +9 -3
- package/dist/tier-engine/handlers/set-tenant-tier.write.d.ts +1 -1
- package/dist/user/db/queries/stream-tenant-backfill.js +4 -4
- package/dist/user/handlers/create.write.d.ts +1 -1
- package/dist/user/handlers/detail.query.js +2 -1
- package/dist/user/handlers/update.write.d.ts +1 -1
- package/dist/user/resolve-display-names.js +5 -9
- package/dist/user/seeding.js +20 -1
- package/dist/user-data-rights/db/queries/forget-cleanup.d.ts +2 -2
- package/dist/user-data-rights/email-templates.d.ts +1 -29
- package/dist/user-data-rights/email-templates.js +5 -106
- package/dist/user-data-rights/feature.js +53 -35
- package/dist/user-data-rights/handlers/cancel-deletion.write.d.ts +2 -2
- package/dist/user-data-rights/handlers/cancel-deletion.write.js +2 -1
- package/dist/user-data-rights/handlers/confirm-deletion-by-token.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/lift-restriction.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion-by-email.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/request-deletion.write.js +5 -7
- package/dist/user-data-rights/handlers/request-export.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/restrict-account.write.d.ts +1 -1
- package/dist/user-data-rights/handlers/run-forget-cleanup.write.d.ts +1 -1
- package/dist/user-data-rights/lib/default-mailers.d.ts +15 -7
- package/dist/user-data-rights/lib/default-mailers.js +100 -48
- package/dist/user-data-rights/lib/update-user-lifecycle.js +1 -1
- package/dist/user-data-rights/mail-notifications.d.ts +13 -0
- package/dist/user-data-rights/mail-notifications.js +118 -0
- package/dist/user-data-rights/web/privacy-center-screen.js +2 -1
- package/dist/user-profile/handlers/change-email.write.d.ts +1 -1
- package/dist/user-profile/web/profile-screen.js +2 -1
- package/dist/waitlist/feature.js +10 -0
- package/dist/waitlist/handlers/invite.write.d.ts +1 -1
- package/dist/waitlist/handlers/reject.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.d.ts +1 -1
- package/dist/waitlist/handlers/submit.write.js +3 -4
- package/dist/waitlist/mail.d.ts +19 -6
- package/dist/waitlist/mail.js +50 -14
- package/dist/waitlist/options.js +1 -1
- package/dist/workflow-runner/event-trigger.js +2 -2
- package/dist/workflow-runner/handlers/resume-run.write.js +7 -2
- package/package.json +37 -17
- package/src/admin-shell/changes.json +6 -0
- package/src/audit/changes.json +6 -0
- package/src/auth-email-password/changes.json +51 -0
- package/src/billing-foundation/changes.json +43 -0
- package/src/cap-counter/changes.json +6 -0
- package/src/channel-email/changes.json +19 -0
- package/src/config/changes.json +6 -0
- package/src/crypto-shredding/changes.json +6 -0
- package/src/custom-fields/changes.json +6 -0
- package/src/data-retention/changes.json +6 -0
- package/src/delivery/changes.json +66 -0
- package/src/file-foundation/changes.json +6 -0
- package/src/files-tenant-data/changes.json +6 -0
- package/src/inbound-mail-foundation/changes.json +6 -0
- package/src/jobs/changes.json +19 -0
- package/src/ledger/changes.json +7 -0
- package/src/managed-pages/changes.json +6 -0
- package/src/renderer-foundation/changes.json +39 -0
- package/src/renderer-pdf/changes.json +12 -0
- package/src/secrets/changes.json +6 -0
- package/src/shared/changes.json +6 -0
- package/src/subscription-stripe/changes.json +6 -0
- package/src/template-resolver/changes.json +186 -0
- package/src/tier-engine/changes.json +6 -0
- package/src/user/changes.json +6 -0
- package/src/user-data-rights/changes.json +13 -0
- package/src/user-profile/changes.json +1 -1
- package/src/waitlist/changes.json +7 -0
- package/src/workflow-runner/changes.json +6 -0
- package/dist/renderer-simple/feature.d.ts +0 -10
- package/dist/renderer-simple/feature.js +0 -42
- package/dist/renderer-simple/index.d.ts +0 -3
- package/dist/renderer-simple/index.js +0 -2
- package/dist/renderer-simple/mail-blocks.d.ts +0 -21
- package/dist/renderer-simple/mail-blocks.js +0 -107
- package/dist/renderer-simple/resolve-variables.d.ts +0 -7
- package/dist/renderer-simple/resolve-variables.js +0 -47
- package/dist/renderer-simple/simple-renderer.d.ts +0 -24
- package/dist/renderer-simple/simple-renderer.js +0 -200
- package/src/renderer-simple/changes.json +0 -27
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// JSON.stringify(IngestPage[]) and readers must parse back. Centralising
|
|
4
4
|
// the codec here keeps liteparse + recipe consumers lockstep with the
|
|
5
5
|
// entity shape (kumiko-framework#1549/1).
|
|
6
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
6
7
|
export function writeIngestPages(pages) {
|
|
7
8
|
return JSON.stringify(pages);
|
|
8
9
|
}
|
|
@@ -25,8 +26,7 @@ function parseJson(raw) {
|
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
function isIngestPage(value) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return typeof page.pageNumber === "number" && typeof page.text === "string";
|
|
29
|
+
return (isPlainObject(value) &&
|
|
30
|
+
typeof value["pageNumber"] === "number" &&
|
|
31
|
+
typeof value["text"] === "string");
|
|
32
32
|
}
|
|
@@ -7,4 +7,4 @@ export declare function createSetWriteHandler(getRuntime: (() => GlobalFeatureTo
|
|
|
7
7
|
featureName: string;
|
|
8
8
|
enabled: boolean;
|
|
9
9
|
previousEnabled: boolean | null;
|
|
10
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
10
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { CACHE_SYNC_TOPICS, createRedisPubSubSignal, redisChannelPrefixFromEnv, } from "@cosmicdrift/kumiko-framework/redis";
|
|
2
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
2
3
|
// Single fixed channel — unlike the SSE broker there's no per-tenant/
|
|
3
4
|
// per-user variance to multiplex, every toggle flip is global.
|
|
4
5
|
const TOGGLE_SYNC_CHANNEL_NAME = "kumiko:feature-toggles:cache-sync";
|
|
5
6
|
function isTogglePayload(value) {
|
|
6
|
-
return (
|
|
7
|
-
value
|
|
8
|
-
typeof value
|
|
9
|
-
typeof value.enabled === "boolean");
|
|
7
|
+
return (isPlainObject(value) &&
|
|
8
|
+
typeof value["featureName"] === "string" &&
|
|
9
|
+
typeof value["enabled"] === "boolean");
|
|
10
10
|
}
|
|
11
11
|
// fw#2625: gives toggle-cache-sync the cross-replica transport its shared
|
|
12
12
|
// dispatcher cursor needs — without this, only the one process that won
|
|
@@ -99,7 +99,7 @@ export const publicVariantQuery = defineQueryHandler({
|
|
|
99
99
|
if (!spec)
|
|
100
100
|
return null;
|
|
101
101
|
const result = await derivatives.variant(query.payload.fileRefId, spec, query.payload.variant);
|
|
102
|
-
const bytes = await files.
|
|
102
|
+
const bytes = await files.refFromFileRef(result).read();
|
|
103
103
|
// ponytail: fully buffered (no streaming), +33% over the wire from
|
|
104
104
|
// base64 — HttpRouteHandlerDeps only exposes {app, systemQuery} (no
|
|
105
105
|
// direct ctx.files access from the Hono handler itself), and the local
|
|
@@ -1 +1,2 @@
|
|
|
1
1
|
export { createFileProviderForTenant, type FileProviderContext, type FileProviderPlugin, fileFoundationFeature, isFileProviderPlugin, } from "./feature.js";
|
|
2
|
+
export { type FileRefContent, FileRefTooLargeError, fileContextForTenant, fileContextFromResolver, readFileRefContent, } from "./read-file-ref-content.js";
|
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
// Public API of the file-foundation bundled-feature.
|
|
2
2
|
export { createFileProviderForTenant, fileFoundationFeature, isFileProviderPlugin, } from "./feature.js";
|
|
3
|
+
export { FileRefTooLargeError, fileContextForTenant, fileContextFromResolver, readFileRefContent, } from "./read-file-ref-content.js";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DbRunner, TenantDb } from "@cosmicdrift/kumiko-framework/db";
|
|
2
|
+
import { type FileContext, type FileProviderResolver } from "@cosmicdrift/kumiko-framework/files";
|
|
3
|
+
export type FileRefContent = {
|
|
4
|
+
readonly content: Uint8Array;
|
|
5
|
+
readonly contentType: string;
|
|
6
|
+
readonly tenantId: string;
|
|
7
|
+
readonly size: number;
|
|
8
|
+
};
|
|
9
|
+
export declare function fileContextForTenant(ctx: {
|
|
10
|
+
readonly _fileProviderResolver?: FileProviderResolver | undefined;
|
|
11
|
+
readonly files?: FileContext | undefined;
|
|
12
|
+
}, contextTenantId: string, tenantId: string): FileContext | undefined;
|
|
13
|
+
export declare function fileContextFromResolver(resolver: FileProviderResolver, tenantId: string): FileContext;
|
|
14
|
+
export declare class FileRefTooLargeError extends Error {
|
|
15
|
+
readonly size: number;
|
|
16
|
+
constructor(size: number);
|
|
17
|
+
}
|
|
18
|
+
export declare function readFileRefContent(args: {
|
|
19
|
+
readonly db: DbRunner | TenantDb;
|
|
20
|
+
readonly filesForTenant: (tenantId: string) => FileContext | undefined;
|
|
21
|
+
readonly fileRefId: string;
|
|
22
|
+
readonly allowedTenantIds: readonly string[];
|
|
23
|
+
/** Only accept this user's own upload that is not bound to any entity yet. */
|
|
24
|
+
readonly unclaimedUploadOf?: string;
|
|
25
|
+
/** Rejects before any bytes are read when the stored size exceeds the limit. */
|
|
26
|
+
readonly maxBytes?: number;
|
|
27
|
+
}): Promise<FileRefContent | null>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { selectMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
2
|
+
import { isUuid } from "@cosmicdrift/kumiko-framework/engine";
|
|
3
|
+
import { createFileContext, fileRefsTable, } from "@cosmicdrift/kumiko-framework/files";
|
|
4
|
+
// Tenant-bound on purpose: without a resolver `ctx.files` only reaches the storage of the
|
|
5
|
+
// context's own tenant (`contextTenantId`), so a request for another tenant's files is
|
|
6
|
+
// undefined instead of silently reading through the wrong tenant's storage.
|
|
7
|
+
export function fileContextForTenant(ctx, contextTenantId, tenantId) {
|
|
8
|
+
const resolver = ctx._fileProviderResolver;
|
|
9
|
+
if (resolver === undefined)
|
|
10
|
+
return tenantId === contextTenantId ? ctx.files : undefined;
|
|
11
|
+
return fileContextFromResolver(resolver, tenantId);
|
|
12
|
+
}
|
|
13
|
+
export function fileContextFromResolver(resolver, tenantId) {
|
|
14
|
+
// @cast-boundary engine-payload — tenantId comes from a trusted job payload or session
|
|
15
|
+
const brandedTenantId = tenantId;
|
|
16
|
+
return createFileContext(() => resolver(brandedTenantId), tenantId);
|
|
17
|
+
}
|
|
18
|
+
export class FileRefTooLargeError extends Error {
|
|
19
|
+
size;
|
|
20
|
+
constructor(size) {
|
|
21
|
+
super(`file ref exceeds the size limit (${size} bytes)`);
|
|
22
|
+
this.size = size;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// fileName of the row is never used: it is personal data and may be a PII ciphertext.
|
|
26
|
+
export async function readFileRefContent(args) {
|
|
27
|
+
if (!isUuid(args.fileRefId))
|
|
28
|
+
return null;
|
|
29
|
+
const [row] = await selectMany(args.db, fileRefsTable, {
|
|
30
|
+
id: args.fileRefId,
|
|
31
|
+
tenantId: { in: args.allowedTenantIds },
|
|
32
|
+
isDeleted: false,
|
|
33
|
+
...(args.unclaimedUploadOf !== undefined && {
|
|
34
|
+
insertedById: args.unclaimedUploadOf,
|
|
35
|
+
entityType: null,
|
|
36
|
+
entityId: null,
|
|
37
|
+
}),
|
|
38
|
+
});
|
|
39
|
+
if (row === undefined)
|
|
40
|
+
return null;
|
|
41
|
+
const size = Number(row.size);
|
|
42
|
+
if (args.maxBytes !== undefined && size > args.maxBytes) {
|
|
43
|
+
throw new FileRefTooLargeError(size);
|
|
44
|
+
}
|
|
45
|
+
const files = args.filesForTenant(row.tenantId);
|
|
46
|
+
if (files === undefined) {
|
|
47
|
+
throw new Error("readFileRefContent: no file storage is available for the file's tenant");
|
|
48
|
+
}
|
|
49
|
+
const content = await files.refFromFileRef(row).read();
|
|
50
|
+
return { content, contentType: row.mimeType, tenantId: row.tenantId, size };
|
|
51
|
+
}
|
|
@@ -97,6 +97,7 @@ export const fileRefTenantDestroyHook = async (ctx) => {
|
|
|
97
97
|
},
|
|
98
98
|
maxFailures: 1,
|
|
99
99
|
deadlineAt: ctx.deadlineAt ?? Number.POSITIVE_INFINITY,
|
|
100
|
+
minBatches: 1,
|
|
100
101
|
onRowError: (_row, err) => {
|
|
101
102
|
firstFailure = err instanceof Error ? err.message : String(err);
|
|
102
103
|
},
|
|
@@ -176,6 +177,9 @@ export const fileRefStorageDestroyHook = async (tenantId, ctx) => {
|
|
|
176
177
|
},
|
|
177
178
|
maxFailures: 1,
|
|
178
179
|
deadlineAt: ctx.deadlineAt ?? Number.POSITIVE_INFINITY,
|
|
180
|
+
// list() and the survivor lookup above can use up the whole budget; one chunk per tick
|
|
181
|
+
// still deletes, so the stage never reports "no progress".
|
|
182
|
+
minBatches: 1,
|
|
179
183
|
onRowError: (_key, err) => {
|
|
180
184
|
firstFailure = err instanceof Error ? err.message : String(err);
|
|
181
185
|
},
|
|
@@ -42,7 +42,8 @@ const loadCollapsed = () => {
|
|
|
42
42
|
return new Set();
|
|
43
43
|
try {
|
|
44
44
|
const raw = window.localStorage.getItem(COLLAPSED_KEY);
|
|
45
|
-
|
|
45
|
+
const parsed = raw ? JSON.parse(raw) : [];
|
|
46
|
+
return new Set(Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : []);
|
|
46
47
|
}
|
|
47
48
|
catch {
|
|
48
49
|
return new Set();
|
|
@@ -207,7 +208,7 @@ export function FolderManager({ renderMeta, filing, }) {
|
|
|
207
208
|
setDragOverKey(key);
|
|
208
209
|
},
|
|
209
210
|
onDragLeave: (e) => {
|
|
210
|
-
if (!e.currentTarget.contains(e.relatedTarget))
|
|
211
|
+
if (!(e.relatedTarget instanceof Node && e.currentTarget.contains(e.relatedTarget)))
|
|
211
212
|
setDragOverKey((cur) => (cur === key ? null : cur));
|
|
212
213
|
},
|
|
213
214
|
onDrop: (e) => {
|
|
@@ -12,10 +12,10 @@ import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
|
12
12
|
// rebuild must not resurrect a "deleted" draft). Ordered oldest-first so a
|
|
13
13
|
// batch is a stable, deterministic slice across the two queries.
|
|
14
14
|
export async function selectStaleDraftsBatch(db, olderThanDays, batchSize) {
|
|
15
|
-
const rows =
|
|
15
|
+
const rows = await unsafeReadRetrying(db, `SELECT "id", "tenant_id", "owner_id", "draft_key", "draft", "inserted_at" FROM "read_form_drafts"
|
|
16
16
|
WHERE COALESCE("modified_at", "inserted_at") < now() - ($1::int * interval '1 day')
|
|
17
17
|
ORDER BY COALESCE("modified_at", "inserted_at") ASC
|
|
18
|
-
LIMIT $2`, [olderThanDays, batchSize])
|
|
18
|
+
LIMIT $2`, [olderThanDays, batchSize]);
|
|
19
19
|
return rows.map((row) => ({
|
|
20
20
|
id: row.id,
|
|
21
21
|
tenantId: row.tenant_id, // @cast-boundary db-row
|
|
@@ -27,10 +27,10 @@ export async function selectStaleDraftsBatch(db, olderThanDays, batchSize) {
|
|
|
27
27
|
}
|
|
28
28
|
/** Re-check staleness after the select→release window so a freshly saved draft is not deleted. */
|
|
29
29
|
export async function isDraftStillStale(db, tenantId, id, olderThanDays) {
|
|
30
|
-
const rows =
|
|
30
|
+
const rows = await unsafeReadRetrying(db, `SELECT 1 FROM "read_form_drafts"
|
|
31
31
|
WHERE "id" = $1
|
|
32
32
|
AND "tenant_id" = $2
|
|
33
33
|
AND COALESCE("modified_at", "inserted_at") < now() - ($3::int * interval '1 day')
|
|
34
|
-
LIMIT 1`, [id, tenantId, olderThanDays])
|
|
34
|
+
LIMIT 1`, [id, tenantId, olderThanDays]);
|
|
35
35
|
return rows.length > 0;
|
|
36
36
|
}
|
|
@@ -4,7 +4,7 @@ import { unsafeReadRetrying } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
|
4
4
|
// (64KB); loading up to FORM_DRAFT_MAX_PER_OWNER full rows just to count them
|
|
5
5
|
// would be several MB of wasted transfer per save.
|
|
6
6
|
export async function countDraftsByOwner(db, tenantId, ownerId) {
|
|
7
|
-
const rows =
|
|
8
|
-
WHERE "tenant_id" = $1 AND "owner_id" = $2`, [tenantId, ownerId])
|
|
7
|
+
const rows = await unsafeReadRetrying(db, `SELECT count(*)::int AS "count" FROM "read_form_drafts"
|
|
8
|
+
WHERE "tenant_id" = $1 AND "owner_id" = $2`, [tenantId, ownerId]);
|
|
9
9
|
return rows[0]?.count ?? 0;
|
|
10
10
|
}
|
|
@@ -29,9 +29,9 @@ export async function filterOwnedStorageKeys(db, tenantId, ownerId, candidateKey
|
|
|
29
29
|
export async function filterOwnedFileRefs(db, tenantId, ownerId, candidateKeys, draftInsertedAt, isCreateMode) {
|
|
30
30
|
if (candidateKeys.length === 0)
|
|
31
31
|
return [];
|
|
32
|
-
const rows =
|
|
32
|
+
const rows = await unsafeReadRetrying(db, `SELECT "id", "storage_key" FROM "file_refs"
|
|
33
33
|
WHERE "tenant_id" = $1 AND "inserted_by_id" = $2
|
|
34
34
|
AND "storage_key" = ANY($3::text[]) AND "is_deleted" = false
|
|
35
|
-
AND ($5::boolean OR "inserted_at" > $4::timestamptz)`, [tenantId, ownerId, candidateKeys, draftInsertedAt.toString(), isCreateMode])
|
|
35
|
+
AND ($5::boolean OR "inserted_at" > $4::timestamptz)`, [tenantId, ownerId, candidateKeys, draftInsertedAt.toString(), isCreateMode]);
|
|
36
36
|
return rows.map((row) => ({ id: row.id, storageKey: row.storage_key }));
|
|
37
37
|
}
|
|
@@ -3,4 +3,4 @@ export declare const discardDraftWrite: import("@cosmicdrift/kumiko-types/define
|
|
|
3
3
|
releaseFiles: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
4
4
|
}, import("zod/v4/core").$strip>, {
|
|
5
5
|
discarded: boolean;
|
|
6
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
6
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -2,4 +2,4 @@ export declare const saveDraftWrite: import("@cosmicdrift/kumiko-types/define-ha
|
|
|
2
2
|
draftKey: import("zod").ZodString;
|
|
3
3
|
values: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodUnknown>;
|
|
4
4
|
stepIndex: import("zod").ZodNumber;
|
|
5
|
-
}, import("zod/v4/core").$strip>, import("@cosmicdrift/kumiko-types/hooks").SaveContext, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
5
|
+
}, import("zod/v4/core").$strip>, import("@cosmicdrift/kumiko-types/hooks").SaveContext, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
1
2
|
// The draft blob's `values` is free-form by design (issue #1889: form-draft
|
|
2
3
|
// never knows the consuming form's field schema, only { values, stepIndex,
|
|
3
4
|
// savedAt }). A FileRef pointer is therefore recognised purely by shape —
|
|
@@ -6,9 +7,9 @@
|
|
|
6
7
|
// step writes into the draft, whether under a single "file" field or nested
|
|
7
8
|
// in a "files"/"images" array.
|
|
8
9
|
function isFileRefPointer(value) {
|
|
9
|
-
if (
|
|
10
|
+
if (!isPlainObject(value))
|
|
10
11
|
return false;
|
|
11
|
-
const storageKey = value
|
|
12
|
+
const storageKey = value["storageKey"];
|
|
12
13
|
return typeof storageKey === "string" && storageKey.length > 0;
|
|
13
14
|
}
|
|
14
15
|
function collectStorageKeys(value, keys) {
|
|
@@ -70,7 +70,7 @@ export const connectAccountHandler = {
|
|
|
70
70
|
authMethod: payload.authMethod,
|
|
71
71
|
ownerUserId,
|
|
72
72
|
displayName: payload.displayName,
|
|
73
|
-
address: encryptedFields
|
|
73
|
+
address: encryptedFields.address,
|
|
74
74
|
// active ab Connect — der Supervisor greift den Account beim
|
|
75
75
|
// nächsten Tick auf und degradiert ihn bei Credential-Fehlern.
|
|
76
76
|
status: InboundMailAccountStatuses.active,
|
|
@@ -165,11 +165,11 @@ export const ingestMessageHandler = {
|
|
|
165
165
|
ownerUserId: payload.ownerUserId,
|
|
166
166
|
messageIdHeader: effectiveMessageIdHeader,
|
|
167
167
|
threadKey,
|
|
168
|
-
from: encryptedMessageFields
|
|
169
|
-
to: encryptedMessageFields
|
|
170
|
-
cc: encryptedMessageFields
|
|
171
|
-
subject: encryptedMessageFields
|
|
172
|
-
snippet: encryptedMessageFields
|
|
168
|
+
from: encryptedMessageFields.from,
|
|
169
|
+
to: encryptedMessageFields.to,
|
|
170
|
+
cc: encryptedMessageFields.cc,
|
|
171
|
+
subject: encryptedMessageFields.subject,
|
|
172
|
+
snippet: encryptedMessageFields.snippet,
|
|
173
173
|
receivedAtIso: payload.receivedAtIso,
|
|
174
174
|
bodyRef: payload.bodyRef,
|
|
175
175
|
scope: payload.scope,
|
|
@@ -246,7 +246,7 @@ export const ingestMessageHandler = {
|
|
|
246
246
|
});
|
|
247
247
|
const threadEventPayload = {
|
|
248
248
|
threadKey,
|
|
249
|
-
subject: encryptedThreadFields
|
|
249
|
+
subject: encryptedThreadFields.subject,
|
|
250
250
|
lastMessageAtIso: newLastAt,
|
|
251
251
|
messageCount,
|
|
252
252
|
};
|
|
@@ -82,14 +82,17 @@ export function createOAuthAccessTokenManager(deps) {
|
|
|
82
82
|
const secretKey = inboundCredentialSecretKey(account.id);
|
|
83
83
|
return transaction(deps.db, async (tx) => {
|
|
84
84
|
await acquireNamespacedAdvisoryLock(tx, OAUTH_REFRESH_LOCK_NAMESPACE, lockKey(account));
|
|
85
|
-
|
|
85
|
+
// Secret read/write join the lock transaction: a second pool connection per refresh
|
|
86
|
+
// would exhaust a small pool when many accounts refresh at once.
|
|
87
|
+
const secrets = deps.secrets.withRunner(tx);
|
|
88
|
+
const stored = await secrets.get(account.tenantId, secretKey);
|
|
86
89
|
if (!stored) {
|
|
87
90
|
throw new InboundAuthError("no refresh token stored for account");
|
|
88
91
|
}
|
|
89
92
|
const currentRefreshToken = stored.reveal();
|
|
90
93
|
const tokens = await refreshWithTimeout(plugin, account, currentRefreshToken);
|
|
91
94
|
if (tokens.refreshToken && tokens.refreshToken !== currentRefreshToken) {
|
|
92
|
-
await
|
|
95
|
+
await secrets.set(account.tenantId, secretKey, tokens.refreshToken, inboundRefreshTokenSecretOptions(account.id));
|
|
93
96
|
}
|
|
94
97
|
remember(account.id, tokens);
|
|
95
98
|
return tokens.accessToken;
|
|
@@ -117,7 +120,9 @@ export function createOAuthAccessTokenManager(deps) {
|
|
|
117
120
|
async storeRefreshToken(account, refreshToken) {
|
|
118
121
|
await transaction(deps.db, async (tx) => {
|
|
119
122
|
await acquireNamespacedAdvisoryLock(tx, OAUTH_REFRESH_LOCK_NAMESPACE, lockKey(account));
|
|
120
|
-
await deps.secrets
|
|
123
|
+
await deps.secrets
|
|
124
|
+
.withRunner(tx)
|
|
125
|
+
.set(account.tenantId, inboundCredentialSecretKey(account.id), refreshToken, inboundRefreshTokenSecretOptions(account.id));
|
|
121
126
|
});
|
|
122
127
|
cache.delete(account.id);
|
|
123
128
|
},
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// HMAC-Input, timing-safe compare, Expiry im Klartext-Segment).
|
|
12
12
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
13
13
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
14
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
14
15
|
const STATE_PURPOSE = "inbound-mail-oauth-connect";
|
|
15
16
|
function sign(input, secret) {
|
|
16
17
|
return createHmac("sha256", secret).update(input).digest("base64url");
|
|
@@ -60,12 +61,11 @@ export function verifyOAuthState(state, secret, now = Temporal.Now.instant()) {
|
|
|
60
61
|
return { ok: true, payload: parsed };
|
|
61
62
|
}
|
|
62
63
|
function isOAuthStatePayload(v) {
|
|
63
|
-
if (
|
|
64
|
+
if (!isPlainObject(v))
|
|
64
65
|
return false;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
typeof
|
|
69
|
-
typeof
|
|
70
|
-
typeof o["nonce"] === "string");
|
|
66
|
+
return (typeof v["tenantId"] === "string" &&
|
|
67
|
+
(v["ownerUserId"] === null || typeof v["ownerUserId"] === "string") &&
|
|
68
|
+
typeof v["providerKey"] === "string" &&
|
|
69
|
+
typeof v["mailbox"] === "string" &&
|
|
70
|
+
typeof v["nonce"] === "string");
|
|
71
71
|
}
|
|
@@ -64,14 +64,14 @@ export class InboundTransientError extends Error {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
export function isInboundAuthError(e) {
|
|
67
|
-
return e instanceof Error && e.kind === "inbound-auth-error";
|
|
67
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-auth-error";
|
|
68
68
|
}
|
|
69
69
|
export function isInboundRateLimitError(e) {
|
|
70
|
-
return e instanceof Error && e.kind === "inbound-rate-limit-error";
|
|
70
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-rate-limit-error";
|
|
71
71
|
}
|
|
72
72
|
export function isInboundCursorInvalidError(e) {
|
|
73
|
-
return e instanceof Error && e.kind === "inbound-cursor-invalid-error";
|
|
73
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-cursor-invalid-error";
|
|
74
74
|
}
|
|
75
75
|
export function isInboundTransientError(e) {
|
|
76
|
-
return e instanceof Error && e.kind === "inbound-transient-error";
|
|
76
|
+
return e instanceof Error && "kind" in e && e.kind === "inbound-transient-error";
|
|
77
77
|
}
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
import { fetchOne, insertOne, selectMany, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
47
47
|
import { configuredPiiSubjectKms, decryptPiiFieldValues, } from "@cosmicdrift/kumiko-framework/crypto";
|
|
48
48
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
49
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
49
50
|
import { InboundMailAccountStatuses, InboundMailFoundationHandlers } from "./constants.js";
|
|
50
51
|
import { MAIL_ACCOUNT_PII_FIELDS, syncCursorTable } from "./entities.js";
|
|
51
52
|
import { createOAuthAccessTokenManager, usesFoundationManagedOAuth, } from "./oauth-access-token.js";
|
|
@@ -111,20 +112,29 @@ export function createInboundMailSupervisor(deps) {
|
|
|
111
112
|
});
|
|
112
113
|
const piiKms = configuredPiiSubjectKms();
|
|
113
114
|
const records = [];
|
|
114
|
-
for (const
|
|
115
|
-
const
|
|
116
|
-
? await decryptPiiFieldValues(
|
|
117
|
-
|
|
115
|
+
for (const row of rows) {
|
|
116
|
+
const decrypted = piiKms
|
|
117
|
+
? await decryptPiiFieldValues(row, MAIL_ACCOUNT_PII_FIELDS, piiKms, {
|
|
118
|
+
requestId: "inbound-mail-foundation:supervisor:list-accounts",
|
|
119
|
+
})
|
|
120
|
+
: row;
|
|
121
|
+
const decryptedAddress = decrypted["address"];
|
|
122
|
+
// Falling back to row.address would hand the ciphertext to a provider.
|
|
123
|
+
// One undecryptable account must not stop polling of every other tenant's accounts.
|
|
124
|
+
if (typeof decryptedAddress !== "string") {
|
|
125
|
+
log(`inbound-mail: account ${row.id} skipped — address did not decrypt to text`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
118
128
|
records.push({
|
|
119
|
-
id: row
|
|
120
|
-
tenantId: row
|
|
121
|
-
provider: row
|
|
122
|
-
authMethod: row
|
|
123
|
-
ownerUserId: row
|
|
124
|
-
address:
|
|
125
|
-
displayName: row
|
|
129
|
+
id: row.id,
|
|
130
|
+
tenantId: row.tenantId,
|
|
131
|
+
provider: row.provider,
|
|
132
|
+
authMethod: row.authMethod,
|
|
133
|
+
ownerUserId: row.ownerUserId ?? null,
|
|
134
|
+
address: decryptedAddress,
|
|
135
|
+
displayName: row.displayName ?? "",
|
|
126
136
|
status: InboundMailAccountStatuses.active,
|
|
127
|
-
watchState: row
|
|
137
|
+
watchState: row.watchState ?? "idle",
|
|
128
138
|
});
|
|
129
139
|
}
|
|
130
140
|
return records;
|
|
@@ -140,7 +150,8 @@ export function createInboundMailSupervisor(deps) {
|
|
|
140
150
|
if (!row)
|
|
141
151
|
return null;
|
|
142
152
|
try {
|
|
143
|
-
|
|
153
|
+
const parsed = JSON.parse(row.cursor);
|
|
154
|
+
return isPlainObject(parsed) ? parsed : null;
|
|
144
155
|
}
|
|
145
156
|
catch {
|
|
146
157
|
return null; // korrupter Cursor = wie kein Cursor → Backfill, Dedup fängt Dubletten
|
|
@@ -6,6 +6,7 @@ import { InboundAuthError, InboundCursorInvalidError, InboundTransientError, } f
|
|
|
6
6
|
import { createFallbackLogger } from "@cosmicdrift/kumiko-framework/logging";
|
|
7
7
|
import { legacyDateToInstant } from "@cosmicdrift/kumiko-framework/time";
|
|
8
8
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
9
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
9
10
|
import { ImapFlow } from "imapflow";
|
|
10
11
|
import { simpleParser } from "mailparser";
|
|
11
12
|
export const IMAP_MAILBOX = "INBOX";
|
|
@@ -73,10 +74,10 @@ export function mapImapError(err, host) {
|
|
|
73
74
|
// imapflow-Fehler tragen die Server-Antwort in responseText, die
|
|
74
75
|
// message ist nur "Command failed" — beides in den Match einbeziehen;
|
|
75
76
|
// authenticationFailed ist das verlässliche Flag.
|
|
76
|
-
const carrier = err;
|
|
77
|
-
const responseText = typeof carrier?.responseText === "string" ? carrier
|
|
77
|
+
const carrier = isPlainObject(err) ? err : undefined;
|
|
78
|
+
const responseText = typeof carrier?.["responseText"] === "string" ? carrier["responseText"] : "";
|
|
78
79
|
const msg = `${err instanceof Error ? err.message : String(err)} ${responseText}`.trim();
|
|
79
|
-
if (carrier?.authenticationFailed === true ||
|
|
80
|
+
if (carrier?.["authenticationFailed"] === true ||
|
|
80
81
|
/auth(entication)? failed|invalid credentials|invalid login|LOGIN failed|AUTHENTICATIONFAILED/i.test(msg)) {
|
|
81
82
|
return new InboundAuthError(`IMAP auth failed at ${host}: ${msg}`);
|
|
82
83
|
}
|
|
@@ -5,4 +5,4 @@ export type StaleRunSweepResult = {
|
|
|
5
5
|
readonly tenantRunsMarkedFailed: number;
|
|
6
6
|
readonly tenantQueuedDropped: number;
|
|
7
7
|
};
|
|
8
|
-
export declare function markStaleJobRunsFailed(db: DbConnection, timeoutHours: number): Promise<StaleRunSweepResult>;
|
|
8
|
+
export declare function markStaleJobRunsFailed(db: DbConnection, timeoutHours: number, queuedRetentionDays?: number): Promise<StaleRunSweepResult>;
|
|
@@ -13,20 +13,26 @@
|
|
|
13
13
|
// (retry.write.ts only allows retry from "failed").
|
|
14
14
|
import { deleteManyBatched, selectMany, updateMany } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
15
15
|
import { mapWithConcurrency } from "../../../shared/index.js";
|
|
16
|
+
import { DEFAULT_JOB_RUN_RETENTION_DAYS } from "../../handlers/retention-cleanup.job.js";
|
|
16
17
|
import { encryptFailureError } from "../../job-run-logger.js";
|
|
17
18
|
import { jobRunsTable } from "../../job-run-table.js";
|
|
18
19
|
import { tenantJobRunsTable } from "../../tenant-job-run-table.js";
|
|
19
20
|
const KMS_POOL_CONCURRENCY = 4;
|
|
20
21
|
const STALE_DELETE_BATCH_SIZE = 500;
|
|
21
22
|
export const STALE_JOB_RUN_ERROR = "job run exceeded the stale-run timeout without a completion signal (likely a crashed worker process)";
|
|
22
|
-
|
|
23
|
+
// A queued tenant row can legitimately wait much longer than a run takes (backlog, delay/backoff,
|
|
24
|
+
// sequential requeue), so it is only treated as lost once it is older than the retention window,
|
|
25
|
+
// not after the running-timeout.
|
|
26
|
+
export async function markStaleJobRunsFailed(db, timeoutHours, queuedRetentionDays = DEFAULT_JOB_RUN_RETENTION_DAYS) {
|
|
23
27
|
const cutoff = Temporal.Now.instant().subtract({ hours: timeoutHours });
|
|
28
|
+
const queuedCutoff = Temporal.Now.instant().subtract({ hours: queuedRetentionDays * 24 });
|
|
24
29
|
const now = Temporal.Now.instant();
|
|
25
30
|
// Fetch the matched rows first: `error` carries the same per-triggering-
|
|
26
31
|
// user subject annotation as job-run-logger.ts's onJobFailed path
|
|
27
32
|
// (personal: { of: "triggeredById" } on job-run-table.ts), so it is encrypted
|
|
28
|
-
// under
|
|
29
|
-
//
|
|
33
|
+
// under the triggering user's DEK, or the system tenant key when
|
|
34
|
+
// triggeredById is null (whenAbsent: "tenant", fw#3654). Runs are grouped by
|
|
35
|
+
// that subject: one encrypt and one updateMany per subject instead of per run.
|
|
30
36
|
const stale = await selectMany(db, jobRunsTable, {
|
|
31
37
|
status: "running",
|
|
32
38
|
startedAt: { lt: cutoff },
|
|
@@ -61,9 +67,10 @@ export async function markStaleJobRunsFailed(db, timeoutHours) {
|
|
|
61
67
|
const runsMarkedFailed = updatedPerSubject.reduce((sum, count) => sum + count, 0);
|
|
62
68
|
// Tenant-visible run state follows the same clock: a running row whose
|
|
63
69
|
// worker died is a failure, a queued row that never started (job lost from
|
|
64
|
-
// Redis, enqueue hook without a run) would otherwise show "waiting" forever
|
|
70
|
+
// Redis, enqueue hook without a run) would otherwise show "waiting" forever;
|
|
71
|
+
// it is dropped once it has outlived the retention window.
|
|
65
72
|
const tenantRunsFailed = await updateMany(db, tenantJobRunsTable, { status: "failed", finishedAt: now, updatedAt: now }, { status: "running", startedAt: { lt: cutoff } });
|
|
66
|
-
const tenantQueuedStale = await deleteManyBatched(db, tenantJobRunsTable, { status: "queued", updatedAt: { lt:
|
|
73
|
+
const tenantQueuedStale = await deleteManyBatched(db, tenantJobRunsTable, { status: "queued", updatedAt: { lt: queuedCutoff } }, { limit: STALE_DELETE_BATCH_SIZE });
|
|
67
74
|
return {
|
|
68
75
|
runsMarkedFailed,
|
|
69
76
|
tenantRunsMarkedFailed: tenantRunsFailed.length,
|
package/dist/jobs/feature.js
CHANGED
|
@@ -19,7 +19,7 @@ export function createJobsFeature(options = {}) {
|
|
|
19
19
|
const retentionDays = options.retentionDays ?? DEFAULT_JOB_RUN_RETENTION_DAYS;
|
|
20
20
|
const staleRunTimeoutHours = options.staleRunTimeoutHours ?? DEFAULT_JOB_RUN_STALE_TIMEOUT_HOURS;
|
|
21
21
|
return defineFeature("jobs", (r) => {
|
|
22
|
-
r.describe("Persistence and operator tooling for background jobs registered via `r.job(...)`. Every job execution writes directly into `store_job_runs` (current status + duration) and `store_job_run_logs` (per-line log rows) from the BullMQ callbacks — no event stream in between (#2243). A daily `retention-cleanup` job deletes runs (and their logs) older than `retentionDays`; an hourly `stale-run-sweep` job marks runs stuck at status `running` past `staleRunTimeoutHours` as `failed` (#2246 — a crashed worker never fires the completion callback, so nothing else ever revisits the row). Exposes `jobs:write:trigger` (manual run) and `jobs:write:retry` (operator retry of a failed run), plus `jobs:query:list`, `jobs:query:details`, and `jobs:query:catalog` (manual jobs) for the operator UI. A job that declares `tenantVisibleFailure` also records its last failed attempt per tenant and subject in `store_tenant_job_failures`, which the tenant itself reads through `jobs:query:failures` — a translation key only, never the provider's message (fw#3079). A job that declares `tenantVisibleRun` also keeps its run state per tenant and subject in `store_tenant_job_runs` (queued, running, plus the latest completed or failed run, no payload, error text or logs), which the tenant reads through `jobs:query:tenant-runs` (fw#3616); the stale-run sweep fails its stuck `running` rows and drops
|
|
22
|
+
r.describe("Persistence and operator tooling for background jobs registered via `r.job(...)`. Every job execution writes directly into `store_job_runs` (current status + duration) and `store_job_run_logs` (per-line log rows) from the BullMQ callbacks — no event stream in between (#2243). A daily `retention-cleanup` job deletes runs (and their logs) older than `retentionDays`; an hourly `stale-run-sweep` job marks runs stuck at status `running` past `staleRunTimeoutHours` as `failed` (#2246 — a crashed worker never fires the completion callback, so nothing else ever revisits the row). Exposes `jobs:write:trigger` (manual run) and `jobs:write:retry` (operator retry of a failed run), plus `jobs:query:list`, `jobs:query:details`, and `jobs:query:catalog` (manual jobs) for the operator UI. A job that declares `tenantVisibleFailure` also records its last failed attempt per tenant and subject in `store_tenant_job_failures`, which the tenant itself reads through `jobs:query:failures` — a translation key only, never the provider's message (fw#3079). A job that declares `tenantVisibleRun` also keeps its run state per tenant and subject in `store_tenant_job_runs` (queued, running, plus the latest completed or failed run, no payload, error text or logs), which the tenant reads through `jobs:query:tenant-runs` (fw#3616); the stale-run sweep fails its stuck `running` rows and drops `queued` rows older than `retentionDays`.");
|
|
23
23
|
r.uiHints({
|
|
24
24
|
displayLabel: "Jobs · Audit & Operator UI",
|
|
25
25
|
category: "operations",
|
|
@@ -28,7 +28,9 @@ export function createJobsFeature(options = {}) {
|
|
|
28
28
|
r.systemScope();
|
|
29
29
|
r.storeTable(jobRunsTableMeta, {
|
|
30
30
|
reason: "direct_write.job_runs",
|
|
31
|
-
// payload/error carry personal:{of:"triggeredById"} (job-run-table.ts)
|
|
31
|
+
// payload/error carry personal:{of:"triggeredById"} (job-run-table.ts): encrypted under the
|
|
32
|
+
// triggering user's DEK, or the system tenant key when triggeredById is null
|
|
33
|
+
// (whenAbsent: "tenant", fw#3654).
|
|
32
34
|
// job-run-logger.ts manually encrypts both before every insert/update
|
|
33
35
|
// (encryptStartedPayload/encryptFailureError) since this store bypasses
|
|
34
36
|
// the executor's automatic PII pipeline (#2243).
|
|
@@ -63,7 +65,7 @@ export function createJobsFeature(options = {}) {
|
|
|
63
65
|
// frequent enough to catch a stuck run soon after it crosses the
|
|
64
66
|
// threshold, cheap enough that an (almost always empty) result set
|
|
65
67
|
// doesn't matter.
|
|
66
|
-
r.job("stale-run-sweep", { trigger: { cron: "0 * * * *" }, concurrency: "skip" }, createStaleRunSweepJob(staleRunTimeoutHours));
|
|
68
|
+
r.job("stale-run-sweep", { trigger: { cron: "0 * * * *" }, concurrency: "skip" }, createStaleRunSweepJob(staleRunTimeoutHours, retentionDays));
|
|
67
69
|
const handlers = {
|
|
68
70
|
trigger: r.writeHandler(triggerWrite),
|
|
69
71
|
retry: r.writeHandler(retryWrite),
|
|
@@ -7,6 +7,8 @@ import { decryptStoredPii, mapWithConcurrency } from "../../shared/index.js";
|
|
|
7
7
|
import { jobRunsTable } from "../job-run-table.js";
|
|
8
8
|
const KMS_POOL_CONCURRENCY = 4;
|
|
9
9
|
const DEFAULT_PAGE_SIZE = 50;
|
|
10
|
+
// The offset cursor re-reads everything up to the page end, so the page size bounds that read.
|
|
11
|
+
const MAX_LIST_LIMIT = 200;
|
|
10
12
|
const jobRunStatusSchema = z.enum(["queued", "running", "completed", "failed"]);
|
|
11
13
|
async function decryptRunRow(row) {
|
|
12
14
|
let result = row;
|
|
@@ -56,7 +58,7 @@ export const listQuery = defineQueryHandler({
|
|
|
56
58
|
.optional(),
|
|
57
59
|
sort: z.enum(["jobName", "status", "startedAt", "duration"]).optional(),
|
|
58
60
|
sortDirection: z.enum(["asc", "desc"]).optional(),
|
|
59
|
-
limit: z.number().int().min(1).optional(),
|
|
61
|
+
limit: z.number().int().min(1).max(MAX_LIST_LIMIT).optional(),
|
|
60
62
|
cursor: z.string().optional(),
|
|
61
63
|
totalCount: z.boolean().optional(),
|
|
62
64
|
}),
|
|
@@ -5,4 +5,4 @@ export declare const retryWrite: import("@cosmicdrift/kumiko-types/define-handle
|
|
|
5
5
|
jobName: string;
|
|
6
6
|
bullJobId: string;
|
|
7
7
|
retriedFromRunId: string;
|
|
8
|
-
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap>;
|
|
8
|
+
}, import("@cosmicdrift/kumiko-types/event-type-map").KumikoEventTypeMap, undefined>;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { JobHandlerFn } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
export declare const DEFAULT_JOB_RUN_STALE_TIMEOUT_HOURS = 24;
|
|
3
|
-
export declare function createStaleRunSweepJob(timeoutHours: number): JobHandlerFn;
|
|
3
|
+
export declare function createStaleRunSweepJob(timeoutHours: number, queuedRetentionDays: number): JobHandlerFn;
|
|
@@ -9,7 +9,7 @@ import { markStaleJobRunsFailed } from "../db/queries/stale-run-sweep.js";
|
|
|
9
9
|
// default has to clear any plausible real job, not just be "long".
|
|
10
10
|
// Configurable per-app via JobsFeatureOptions.staleRunTimeoutHours.
|
|
11
11
|
export const DEFAULT_JOB_RUN_STALE_TIMEOUT_HOURS = 24;
|
|
12
|
-
export function createStaleRunSweepJob(timeoutHours) {
|
|
12
|
+
export function createStaleRunSweepJob(timeoutHours, queuedRetentionDays) {
|
|
13
13
|
return async (_payload, ctx) => {
|
|
14
14
|
if (!ctx.systemDb) {
|
|
15
15
|
throw new InternalError({
|
|
@@ -17,7 +17,7 @@ export function createStaleRunSweepJob(timeoutHours) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
const db = ctx.systemDb.unsafeRaw("stale-run sweep marks stuck runs of every tenant as failed"); // @cast-boundary db-operator — DbRunner narrows to DbConnection, jobs never run inside a DbTx
|
|
20
|
-
const result = await markStaleJobRunsFailed(db, timeoutHours);
|
|
20
|
+
const result = await markStaleJobRunsFailed(db, timeoutHours, queuedRetentionDays);
|
|
21
21
|
ctx.log?.info?.(`[jobs:stale-run-sweep] complete: ${JSON.stringify(result)}`);
|
|
22
22
|
};
|
|
23
23
|
}
|