@cosmicdrift/kumiko-samples 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/package.json +1 -1
- package/packages/bundled-features/package.json +23 -6
- package/packages/bundled-features/src/admin-shell/changes.json +6 -0
- package/packages/bundled-features/src/admin-shell/feature.ts +8 -1
- package/packages/bundled-features/src/agent-tools/agent-manifest.ts +3 -4
- package/packages/bundled-features/src/agent-tools/filter-ops.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-catalog.ts +1 -3
- package/packages/bundled-features/src/agent-tools/tool-dispatch.ts +2 -1
- package/packages/bundled-features/src/audit/changes.json +6 -0
- package/packages/bundled-features/src/audit/handlers/list.query.ts +10 -5
- package/packages/bundled-features/src/auth-email-password/bootstrap.ts +29 -8
- package/packages/bundled-features/src/auth-email-password/changes.json +51 -0
- package/packages/bundled-features/src/auth-email-password/email-rate-limits.ts +16 -0
- package/packages/bundled-features/src/auth-email-password/email-templates.ts +12 -138
- package/packages/bundled-features/src/auth-email-password/feature.ts +30 -9
- package/packages/bundled-features/src/auth-email-password/handlers/change-password.write.ts +6 -2
- package/packages/bundled-features/src/auth-email-password/handlers/invite-create.write.ts +4 -5
- package/packages/bundled-features/src/auth-email-password/handlers/logout.write.ts +7 -2
- package/packages/bundled-features/src/auth-email-password/handlers/request-account-unlock.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-email-verification.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/request-password-reset.write.ts +2 -5
- package/packages/bundled-features/src/auth-email-password/handlers/signup-confirm.write.ts +3 -4
- package/packages/bundled-features/src/auth-email-password/handlers/signup-request.write.ts +2 -1
- package/packages/bundled-features/src/auth-email-password/handlers/token-request-handler.ts +5 -13
- package/packages/bundled-features/src/auth-email-password/index.ts +2 -17
- package/packages/bundled-features/src/auth-email-password/magic-link-mail.ts +30 -22
- package/packages/bundled-features/src/auth-email-password/mail-notifications.ts +158 -0
- package/packages/bundled-features/src/auth-email-password/signup-activation.ts +2 -3
- package/packages/bundled-features/src/auth-email-password/web/auth-gate.tsx +24 -5
- package/packages/bundled-features/src/auth-foundation/types.ts +8 -18
- package/packages/bundled-features/src/auth-mfa/feature.ts +8 -10
- package/packages/bundled-features/src/auth-mfa/hmac-token-codec.ts +4 -4
- package/packages/bundled-features/src/auth-mfa/mfa-challenge-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-preauth-setup-token.ts +3 -3
- package/packages/bundled-features/src/auth-mfa/mfa-setup-token.ts +8 -8
- package/packages/bundled-features/src/billing-foundation/changes.json +43 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +90 -44
- package/packages/bundled-features/src/billing-foundation/consumer-protection/consent-gate.ts +22 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/issue-confirmation.ts +3 -3
- package/packages/bundled-features/src/billing-foundation/consumer-protection/mail-notifications.ts +118 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-termination.ts +11 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +39 -33
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-notify.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-pages.ts +1 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-receipt-limit.ts +41 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-record.ts +10 -1
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-request.ts +36 -20
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-texts.ts +9 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/withdrawal-period.ts +35 -0
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/create-checkout-session.write.ts +11 -3
- package/packages/bundled-features/src/billing-foundation/handlers/create-portal-session.write.ts +4 -9
- package/packages/bundled-features/src/billing-foundation/handlers/list-subscriptions.query.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/handlers/process-event.write.ts +12 -6
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/i18n.ts +3 -0
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +20 -11
- package/packages/bundled-features/src/billing-foundation/types.ts +7 -2
- package/packages/bundled-features/src/cap-counter/calendar-cap-usage-reservation.ts +2 -0
- package/packages/bundled-features/src/cap-counter/changes.json +6 -0
- package/packages/bundled-features/src/cap-counter/enforce-cap.ts +75 -5
- package/packages/bundled-features/src/cap-counter/index.ts +1 -0
- package/packages/bundled-features/src/cap-counter/with-cap-enforcement.ts +3 -0
- package/packages/bundled-features/src/cap-overview/handlers/tenant-caps-list.query.ts +2 -1
- package/packages/bundled-features/src/cap-overview/web/cap-usage-cell.tsx +6 -4
- package/packages/bundled-features/src/channel-email/changes.json +19 -0
- package/packages/bundled-features/src/channel-email/email-channel.ts +135 -29
- package/packages/bundled-features/src/channel-email/feature.ts +6 -3
- package/packages/bundled-features/src/channel-email/index.ts +7 -1
- package/packages/bundled-features/src/channel-email/pii-guard.ts +13 -0
- package/packages/bundled-features/src/channel-email/smtp-transport.ts +15 -1
- package/packages/bundled-features/src/channel-email/types.ts +23 -0
- package/packages/bundled-features/src/channel-in-app/handlers/mark-read.write.ts +2 -2
- package/packages/bundled-features/src/config/changes.json +6 -0
- package/packages/bundled-features/src/config/handlers/set.write.ts +3 -7
- package/packages/bundled-features/src/config/resolver.ts +15 -12
- package/packages/bundled-features/src/config/write-helpers.ts +23 -0
- package/packages/bundled-features/src/crypto-shredding/changes.json +6 -0
- package/packages/bundled-features/src/crypto-shredding/handlers/forget-subject.write.ts +48 -27
- package/packages/bundled-features/src/custom-fields/changes.json +6 -0
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- package/packages/bundled-features/src/custom-fields/handlers/update-tenant-field.write.ts +0 -1
- package/packages/bundled-features/src/custom-fields/lib/value-schema.ts +10 -11
- package/packages/bundled-features/src/custom-fields/wire-user-data-rights.ts +7 -6
- package/packages/bundled-features/src/data-retention/changes.json +6 -0
- package/packages/bundled-features/src/data-retention/index.ts +1 -0
- package/packages/bundled-features/src/data-retention/run-retention-cleanup.ts +8 -6
- package/packages/bundled-features/src/delivery/attachment-ref.ts +105 -0
- package/packages/bundled-features/src/delivery/changes.json +66 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +152 -4
- package/packages/bundled-features/src/delivery/constants.ts +2 -0
- package/packages/bundled-features/src/delivery/delivery-service.ts +104 -26
- package/packages/bundled-features/src/delivery/feature.ts +1 -1
- package/packages/bundled-features/src/delivery/index.ts +19 -2
- package/packages/bundled-features/src/delivery/jobs.ts +97 -17
- package/packages/bundled-features/src/delivery/notify-factory.ts +14 -9
- package/packages/bundled-features/src/delivery/testing.ts +9 -1
- package/packages/bundled-features/src/delivery/types.ts +34 -18
- package/packages/bundled-features/src/delivery/unsubscribe.ts +5 -2
- package/packages/bundled-features/src/derivatives-sharp/render.ts +1 -22
- package/packages/bundled-features/src/document-ingest-foundation/pages.ts +6 -3
- package/packages/bundled-features/src/feature-toggles/toggle-sync-signal.ts +4 -4
- package/packages/bundled-features/src/file-derivatives/handlers/public-variant.query.ts +1 -1
- package/packages/bundled-features/src/file-foundation/changes.json +6 -0
- package/packages/bundled-features/src/file-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +89 -0
- package/packages/bundled-features/src/files-tenant-data/changes.json +6 -0
- package/packages/bundled-features/src/files-tenant-data/hooks.ts +4 -0
- package/packages/bundled-features/src/folders/web/folder-manager.tsx +5 -2
- package/packages/bundled-features/src/form-draft/db/queries/cleanup.ts +11 -11
- package/packages/bundled-features/src/form-draft/db/queries/draft-count.ts +2 -2
- package/packages/bundled-features/src/form-draft/db/queries/owned-file-refs.ts +2 -2
- package/packages/bundled-features/src/form-draft/release-file-refs.ts +3 -2
- package/packages/bundled-features/src/inbound-mail-foundation/changes.json +6 -0
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/connect-account.write.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/ingest-message.write.ts +6 -6
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-accounts.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/list-messages.query.ts +1 -1
- package/packages/bundled-features/src/inbound-mail-foundation/handlers/scope-visibility.ts +2 -2
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-access-token.ts +13 -8
- package/packages/bundled-features/src/inbound-mail-foundation/oauth-state.ts +7 -7
- package/packages/bundled-features/src/inbound-mail-foundation/retention-sweep.ts +2 -6
- package/packages/bundled-features/src/inbound-mail-foundation/tenant-destroy-hook.ts +5 -13
- package/packages/bundled-features/src/inbound-mail-foundation/types.ts +4 -6
- package/packages/bundled-features/src/inbound-mail-foundation/watch-supervisor.ts +42 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/changes.json +19 -0
- package/packages/bundled-features/src/jobs/db/queries/stale-run-sweep.ts +12 -4
- package/packages/bundled-features/src/jobs/feature.ts +7 -4
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -1
- package/packages/bundled-features/src/jobs/handlers/list.query.ts +3 -1
- package/packages/bundled-features/src/jobs/handlers/stale-run-sweep.job.ts +5 -2
- package/packages/bundled-features/src/jobs/job-run-logger.ts +52 -17
- package/packages/bundled-features/src/jobs/tenant-job-run-table.ts +7 -2
- package/packages/bundled-features/src/ledger/changes.json +7 -0
- package/packages/bundled-features/src/ledger/entity.ts +3 -1
- package/packages/bundled-features/src/ledger/recurring.ts +1 -3
- package/packages/bundled-features/src/managed-pages/branding.ts +33 -9
- package/packages/bundled-features/src/managed-pages/changes.json +6 -0
- package/packages/bundled-features/src/managed-pages/feature.ts +1 -0
- package/packages/bundled-features/src/managed-pages/i18n.ts +1 -0
- package/packages/bundled-features/src/managed-pages/screens/branding-screen.ts +8 -0
- package/packages/bundled-features/src/page-render/branding.ts +3 -0
- package/packages/bundled-features/src/page-render/markdown.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/feature.ts +2 -4
- package/packages/bundled-features/src/personal-access-tokens/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/personal-access-tokens/index.ts +1 -1
- package/packages/bundled-features/src/personal-access-tokens/revoke-for-user.ts +2 -1
- package/packages/bundled-features/src/renderer-foundation/README.md +11 -10
- package/packages/bundled-features/src/renderer-foundation/api.ts +14 -14
- package/packages/bundled-features/src/renderer-foundation/changes.json +39 -0
- package/packages/bundled-features/src/renderer-foundation/constants.ts +6 -8
- package/packages/bundled-features/src/renderer-foundation/feature.ts +7 -7
- package/packages/bundled-features/src/renderer-foundation/index.ts +13 -2
- package/packages/bundled-features/src/renderer-foundation/types.ts +134 -35
- package/packages/bundled-features/src/renderer-pdf/changes.json +12 -0
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- package/packages/bundled-features/src/renderer-pdf/render-table-pdf.ts +162 -82
- package/packages/bundled-features/src/secrets/changes.json +6 -0
- package/packages/bundled-features/src/secrets/feature.ts +4 -3
- package/packages/bundled-features/src/secrets/secrets-context.ts +23 -3
- package/packages/bundled-features/src/sessions/db/queries/cleanup.ts +2 -2
- package/packages/bundled-features/src/sessions/feature.ts +2 -4
- package/packages/bundled-features/src/sessions/handlers/list.query.ts +2 -1
- package/packages/bundled-features/src/shared/attachment-file-name.ts +39 -0
- package/packages/bundled-features/src/shared/changes.json +6 -0
- package/packages/bundled-features/src/shared/chunked-entity-migration.ts +4 -1
- package/packages/bundled-features/src/shared/index.ts +6 -0
- package/packages/bundled-features/src/shared/is-tenant-db.ts +1 -1
- package/packages/bundled-features/src/shared/mail-template-body.ts +42 -0
- package/packages/bundled-features/src/shared/sharp-input-limits.ts +26 -0
- package/packages/bundled-features/src/shared/signup-handover.ts +4 -4
- package/packages/bundled-features/src/subscription-stripe/changes.json +6 -0
- package/packages/bundled-features/src/subscription-stripe/plugin-methods.ts +19 -11
- package/packages/bundled-features/src/subscription-stripe/verify-webhook.ts +3 -1
- package/packages/bundled-features/src/tags/handlers/assign-tag.write.ts +5 -1
- package/packages/bundled-features/src/tags/web/tag-chip.tsx +2 -1
- package/packages/bundled-features/src/template-resolver/README.md +66 -8
- package/packages/bundled-features/src/template-resolver/api.ts +85 -9
- package/packages/bundled-features/src/template-resolver/changes.json +186 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +278 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +49 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +345 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +59 -6
- package/packages/bundled-features/src/template-resolver/handlers/bundle-import.ts +188 -0
- package/packages/bundled-features/src/template-resolver/handlers/collection-set.write.ts +30 -2
- package/packages/bundled-features/src/template-resolver/handlers/copy-locale.write.ts +160 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +33 -0
- package/packages/bundled-features/src/template-resolver/handlers/discard-draft.write.ts +52 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +138 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +9 -6
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle-system.write.ts +42 -0
- package/packages/bundled-features/src/template-resolver/handlers/import-bundle.write.ts +22 -0
- package/packages/bundled-features/src/template-resolver/handlers/layout-source.query.ts +151 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +19 -18
- package/packages/bundled-features/src/template-resolver/handlers/override-diff.ts +49 -0
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +323 -0
- package/packages/bundled-features/src/template-resolver/handlers/preview.query.ts +25 -0
- package/packages/bundled-features/src/template-resolver/handlers/reset-to-system.write.ts +58 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-link-pdf.write.ts +34 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-remove.write.ts +50 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-shared.ts +24 -0
- package/packages/bundled-features/src/template-resolver/handlers/resource-upload.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/send-test.write.ts +135 -0
- package/packages/bundled-features/src/template-resolver/handlers/set.write.ts +32 -3
- package/packages/bundled-features/src/template-resolver/handlers/shared.ts +7 -2
- package/packages/bundled-features/src/template-resolver/handlers/template-gates.ts +196 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +233 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +137 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +91 -17
- package/packages/bundled-features/src/template-resolver/handlers/upsert-system.write.ts +19 -71
- package/packages/bundled-features/src/template-resolver/handlers/upsert-tenant.write.ts +16 -77
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +31 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +111 -0
- package/packages/bundled-features/src/template-resolver/layout-appearance.ts +40 -0
- package/packages/bundled-features/src/template-resolver/layout-regions.ts +206 -0
- package/packages/bundled-features/src/template-resolver/linked-resources.ts +102 -0
- package/packages/bundled-features/src/template-resolver/pdf-constants.ts +12 -0
- package/packages/bundled-features/src/template-resolver/preview-display-html.ts +36 -0
- package/packages/bundled-features/src/template-resolver/print-document.ts +58 -0
- package/packages/bundled-features/src/template-resolver/qualified-names.ts +34 -0
- package/packages/bundled-features/src/template-resolver/record-mapping.ts +26 -0
- package/packages/bundled-features/src/template-resolver/resource-image.ts +47 -0
- package/packages/bundled-features/src/template-resolver/seeding.ts +171 -20
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +27 -1
- package/packages/bundled-features/src/template-resolver/template-bundle.ts +79 -0
- package/packages/bundled-features/src/template-resolver/template-diff.ts +78 -0
- package/packages/bundled-features/src/template-resolver/template-draft.ts +106 -0
- package/packages/bundled-features/src/template-resolver/template-meta.ts +192 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +676 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +114 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +68 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +230 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +20 -13
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +215 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +37 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +1065 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +72 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +121 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor-lazy.tsx +36 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-line-editor.tsx +176 -0
- package/packages/bundled-features/src/template-resolver/web/placeholder-text.ts +37 -0
- package/packages/bundled-features/src/template-resolver/web/use-stable-by-key.ts +14 -0
- package/packages/bundled-features/src/tenant/handlers/team-list.query.ts +3 -2
- package/packages/bundled-features/src/tenant/seeding.ts +41 -7
- package/packages/bundled-features/src/tenant-lifecycle/boot-checks.ts +1 -1
- package/packages/bundled-features/src/tenant-lifecycle/handlers/cancel-destruction.write.ts +2 -1
- package/packages/bundled-features/src/tier-engine/changes.json +6 -0
- package/packages/bundled-features/src/tier-engine/feature.ts +11 -3
- package/packages/bundled-features/src/user/changes.json +6 -0
- package/packages/bundled-features/src/user/db/queries/stream-tenant-backfill.ts +4 -4
- package/packages/bundled-features/src/user/handlers/detail.query.ts +3 -5
- package/packages/bundled-features/src/user/resolve-display-names.ts +8 -15
- package/packages/bundled-features/src/user/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user/seeding.ts +19 -5
- package/packages/bundled-features/src/user-data-rights/changes.json +13 -0
- package/packages/bundled-features/src/user-data-rights/db/queries/forget-cleanup.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/email-templates.ts +5 -149
- package/packages/bundled-features/src/user-data-rights/feature.ts +72 -35
- package/packages/bundled-features/src/user-data-rights/handlers/cancel-deletion.write.ts +2 -1
- package/packages/bundled-features/src/user-data-rights/handlers/request-deletion.write.ts +5 -11
- package/packages/bundled-features/src/user-data-rights/lib/default-mailers.ts +134 -53
- package/packages/bundled-features/src/user-data-rights/lib/update-user-lifecycle.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/mail-notifications.ts +157 -0
- package/packages/bundled-features/src/user-data-rights/run-forget-cleanup.ts +1 -1
- package/packages/bundled-features/src/user-data-rights/web/privacy-center-screen.tsx +2 -1
- package/packages/bundled-features/src/user-profile/changes.json +1 -1
- package/packages/bundled-features/src/user-profile/web/profile-screen.tsx +2 -1
- package/packages/bundled-features/src/waitlist/changes.json +7 -0
- package/packages/bundled-features/src/waitlist/feature.ts +17 -0
- package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +4 -5
- package/packages/bundled-features/src/waitlist/mail.ts +69 -18
- package/packages/bundled-features/src/waitlist/options.ts +1 -1
- package/packages/bundled-features/src/workflow-runner/changes.json +6 -0
- package/packages/bundled-features/src/workflow-runner/event-trigger.ts +3 -3
- package/packages/bundled-features/src/workflow-runner/handlers/resume-run.write.ts +7 -1
- package/samples/apps/ui-walkthrough/package.json +11 -0
- package/samples/apps/ui-walkthrough/playwright.config.ts +40 -7
- package/samples/apps/ui-walkthrough/src/app/auth-constants.ts +9 -0
- package/samples/apps/ui-walkthrough/src/app/client.tsx +6 -0
- package/samples/apps/ui-walkthrough/src/app/seed-templates.ts +239 -0
- package/samples/apps/ui-walkthrough/src/app/server.ts +5 -1
- package/samples/apps/ui-walkthrough/src/app/shell.tsx +12 -1
- package/samples/apps/ui-walkthrough/src/app/template-seed-data.ts +570 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +72 -0
- package/samples/apps/ui-walkthrough/src/features/billing/i18n.ts +18 -0
- package/samples/apps/ui-walkthrough/src/features/billing/index.ts +2 -0
- package/samples/apps/ui-walkthrough/src/features/billing/web/index.ts +7 -0
- package/samples/apps/ui-walkthrough/src/run-config.ts +41 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +118 -21
- package/samples/apps/use-all-bundled/kumiko/migrations/0045_template-resource-meta.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/kumiko/migrations/0046_pdf-rendered.rebuild.json +4 -0
- package/samples/apps/use-all-bundled/package.json +2 -0
- package/samples/apps/use-all-bundled/playwright.config.ts +38 -3
- package/samples/apps/use-all-bundled/schema/generate.ts +13 -3
- package/samples/apps/use-all-bundled/schema/schema.generated.ts +3 -0
- package/samples/apps/use-all-bundled/scripts/check-coverage.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/auth-constants.ts +3 -0
- package/samples/apps/use-all-bundled/src/app/client.tsx +4 -0
- package/samples/apps/use-all-bundled/src/app/seed-documents.ts +128 -0
- package/samples/apps/use-all-bundled/src/app/seed.ts +4 -0
- package/samples/apps/use-all-bundled/src/run-config.ts +11 -11
- package/samples/recipes/delivery-notifications/README.md +26 -25
- package/samples/recipes/delivery-notifications/package.json +2 -1
- package/samples/recipes/delivery-notifications/src/feature.ts +64 -58
- package/samples/recipes/seed-migration/README.md +1 -1
- package/packages/bundled-features/src/renderer-simple/changes.json +0 -27
- package/packages/bundled-features/src/renderer-simple/feature.ts +0 -66
- package/packages/bundled-features/src/renderer-simple/index.ts +0 -8
- package/packages/bundled-features/src/renderer-simple/mail-blocks.ts +0 -138
- package/packages/bundled-features/src/renderer-simple/resolve-variables.ts +0 -57
- package/packages/bundled-features/src/renderer-simple/simple-renderer.ts +0 -304
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-samples",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.364.0",
|
|
4
4
|
"description": "Source trees of the Kumiko sample recipes, sample apps and bundled features in repo layout, for tooling such as few-shot corpus builds.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cosmicdrift/kumiko-bundled-features",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.364.0",
|
|
4
4
|
"description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
|
|
@@ -106,6 +106,7 @@
|
|
|
106
106
|
"./shared/single-use-token-store": "./src/shared/single-use-token-store.ts",
|
|
107
107
|
"./shared/signed-token": "./src/shared/signed-token.ts",
|
|
108
108
|
"./shared/row-bound-grant": "./src/shared/row-bound-grant.ts",
|
|
109
|
+
"./shared/mail-template-body": "./src/shared/mail-template-body.ts",
|
|
109
110
|
"./delivery": "./src/delivery/index.ts",
|
|
110
111
|
"./delivery/web": "./src/delivery/web/index.ts",
|
|
111
112
|
"./channel-in-app": "./src/channel-in-app/index.ts",
|
|
@@ -115,7 +116,6 @@
|
|
|
115
116
|
"./channel-discord": "./src/channel-discord/index.ts",
|
|
116
117
|
"./channel-teams": "./src/channel-teams/index.ts",
|
|
117
118
|
"./channel-telegram": "./src/channel-telegram/index.ts",
|
|
118
|
-
"./renderer-simple": "./src/renderer-simple/index.ts",
|
|
119
119
|
"./renderer-pdf": "./src/renderer-pdf/index.ts",
|
|
120
120
|
"./files-provider-s3": "./src/files-provider-s3/index.ts",
|
|
121
121
|
"./rate-limiting": "./src/rate-limiting/index.ts",
|
|
@@ -131,6 +131,7 @@
|
|
|
131
131
|
"./feature-toggles": "./src/feature-toggles/index.ts",
|
|
132
132
|
"./feature-toggles/web": "./src/feature-toggles/web/index.ts",
|
|
133
133
|
"./template-resolver": "./src/template-resolver/index.ts",
|
|
134
|
+
"./template-resolver/document-schema": "./src/template-resolver/document-schema.ts",
|
|
134
135
|
"./template-resolver/seeding": "./src/template-resolver/seeding.ts",
|
|
135
136
|
"./template-resolver/testing": "./src/template-resolver/testing.ts",
|
|
136
137
|
"./template-resolver/web": "./src/template-resolver/web/index.ts",
|
|
@@ -156,9 +157,19 @@
|
|
|
156
157
|
"@cosmicdrift/kumiko-types": "workspace:*",
|
|
157
158
|
"@mollie/api-client": "^4.5.0",
|
|
158
159
|
"@node-rs/argon2": "^2.0.2",
|
|
160
|
+
"@tiptap/core": "3.31.3",
|
|
161
|
+
"@tiptap/extension-link": "3.31.3",
|
|
162
|
+
"@tiptap/extension-list": "3.31.3",
|
|
163
|
+
"@tiptap/extension-table": "3.31.3",
|
|
164
|
+
"@tiptap/html": "3.31.3",
|
|
165
|
+
"@tiptap/extensions": "3.31.3",
|
|
166
|
+
"@tiptap/pm": "3.31.3",
|
|
167
|
+
"@tiptap/react": "3.31.3",
|
|
168
|
+
"@tiptap/starter-kit": "3.31.3",
|
|
159
169
|
"@types/mailparser": "^3.4.6",
|
|
160
170
|
"@types/qrcode": "^1.5.5",
|
|
161
171
|
"clsx": "^2.1.1",
|
|
172
|
+
"happy-dom": "^20.11.2",
|
|
162
173
|
"hono": "^4.13.1",
|
|
163
174
|
"imapflow": "^1.7.8",
|
|
164
175
|
"ioredis": "^5.10.1",
|
|
@@ -170,6 +181,7 @@
|
|
|
170
181
|
"pdf-lib": "^1.17.1",
|
|
171
182
|
"qrcode": "^1.5.4",
|
|
172
183
|
"react": "^19.2.6",
|
|
184
|
+
"react-dom": "^19.2.6",
|
|
173
185
|
"sharp": "^0.35.5",
|
|
174
186
|
"stripe": "^22.6.2",
|
|
175
187
|
"tailwind-merge": "^3.6.0",
|
|
@@ -517,6 +529,10 @@
|
|
|
517
529
|
"types": "./dist/shared/row-bound-grant.d.ts",
|
|
518
530
|
"default": "./dist/shared/row-bound-grant.js"
|
|
519
531
|
},
|
|
532
|
+
"./shared/mail-template-body": {
|
|
533
|
+
"types": "./dist/shared/mail-template-body.d.ts",
|
|
534
|
+
"default": "./dist/shared/mail-template-body.js"
|
|
535
|
+
},
|
|
520
536
|
"./delivery": {
|
|
521
537
|
"types": "./dist/delivery/index.d.ts",
|
|
522
538
|
"default": "./dist/delivery/index.js"
|
|
@@ -553,10 +569,6 @@
|
|
|
553
569
|
"types": "./dist/channel-telegram/index.d.ts",
|
|
554
570
|
"default": "./dist/channel-telegram/index.js"
|
|
555
571
|
},
|
|
556
|
-
"./renderer-simple": {
|
|
557
|
-
"types": "./dist/renderer-simple/index.d.ts",
|
|
558
|
-
"default": "./dist/renderer-simple/index.js"
|
|
559
|
-
},
|
|
560
572
|
"./renderer-pdf": {
|
|
561
573
|
"types": "./dist/renderer-pdf/index.d.ts",
|
|
562
574
|
"default": "./dist/renderer-pdf/index.js"
|
|
@@ -617,6 +629,10 @@
|
|
|
617
629
|
"types": "./dist/template-resolver/index.d.ts",
|
|
618
630
|
"default": "./dist/template-resolver/index.js"
|
|
619
631
|
},
|
|
632
|
+
"./template-resolver/document-schema": {
|
|
633
|
+
"types": "./dist/template-resolver/document-schema.d.ts",
|
|
634
|
+
"default": "./dist/template-resolver/document-schema.js"
|
|
635
|
+
},
|
|
620
636
|
"./template-resolver/seeding": {
|
|
621
637
|
"types": "./dist/template-resolver/seeding.d.ts",
|
|
622
638
|
"default": "./dist/template-resolver/seeding.js"
|
|
@@ -689,6 +705,7 @@
|
|
|
689
705
|
"@testing-library/user-event": "^14.6.1",
|
|
690
706
|
"@cosmicdrift/kumiko-locale-de": "workspace:*",
|
|
691
707
|
"@cosmicdrift/kumiko-locale-es": "workspace:*",
|
|
708
|
+
"@cosmicdrift/kumiko-renderer-mail-html": "workspace:*",
|
|
692
709
|
"jsqr": "^1.4.0",
|
|
693
710
|
"unpdf": "^1.8.1"
|
|
694
711
|
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "improvement",
|
|
5
|
+
"title": "includeDocumentDesigner lists the template navs in the admin workspaces",
|
|
6
|
+
"detail": "With includeDocumentDesigner, the tenant workspace shows document-designer:nav:templates and the platform workspace document-designer:nav:platform-templates. The option requires the document-designer feature and defaults to false."
|
|
7
|
+
},
|
|
2
8
|
{
|
|
3
9
|
"version": "0.330.0",
|
|
4
10
|
"type": "breaking",
|
|
@@ -36,6 +36,9 @@ export type CreateAdminShellOptions = {
|
|
|
36
36
|
* Requires cap-overview mounted. Default false — apps that don't mount cap-overview
|
|
37
37
|
* must not get a nav entry pointing at a screen that doesn't exist. */
|
|
38
38
|
readonly includeCapOverview?: boolean;
|
|
39
|
+
/** Tenant nav → document-designer:nav:templates, platform nav → document-designer:nav:platform-templates.
|
|
40
|
+
* Requires document-designer mounted (a separate package, so no import here). Default false. */
|
|
41
|
+
readonly includeDocumentDesigner?: boolean;
|
|
39
42
|
/** When false, only overview screens + nav are registered — app owns r.workspace(). Default true. */
|
|
40
43
|
readonly registerWorkspaces?: boolean;
|
|
41
44
|
/** Metric list the overview panels are built from — pass the SAME list to
|
|
@@ -49,12 +52,13 @@ export function createAdminShellFeature(options: CreateAdminShellOptions = {}):
|
|
|
49
52
|
const platformWsId = options.workspaceIds?.platform ?? DEFAULT_PLATFORM_WORKSPACE_ID;
|
|
50
53
|
const includeTierAdmin = options.includeTierAdmin ?? true;
|
|
51
54
|
const includeCapOverview = options.includeCapOverview ?? false;
|
|
55
|
+
const includeDocumentDesigner = options.includeDocumentDesigner ?? false;
|
|
52
56
|
const registerWorkspaces = options.registerWorkspaces ?? true;
|
|
53
57
|
const metrics = options.metrics ?? DEFAULT_METRICS;
|
|
54
58
|
|
|
55
59
|
return defineFeature(ADMIN_SHELL_FEATURE, (r) => {
|
|
56
60
|
r.describe(
|
|
57
|
-
"Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `tenant:screen:tenant-list`, `jobs:screen:job-runs`, optional `tier-engine:screen:tier-admin`, optional `cap-overview:screen:my-caps`/`cap-overview:screen:tenant-cap-list`) and the two overview dashboards, built from the metrics list (`metrics` option, default DEFAULT_METRICS). Mount after user, tenant, audit, jobs, metrics and metrics-system; pass `workspaceIds` to match app URL conventions (e.g. Studio `d`/`s`, PublicStatus `admin`/`sysadmin`). Client: `adminShellClient()`, `tenantClient()`, `auditClient()`, `jobsClient()`, optional `tierEngineClient()`.",
|
|
61
|
+
"Registers tenant-admin and platform-admin workspaces with provider nav into owner-feature screens (`tenant:screen:members`, `audit:screen:audit-log`, `tenant:screen:tenant-list`, `jobs:screen:job-runs`, optional `tier-engine:screen:tier-admin`, optional `cap-overview:screen:my-caps`/`cap-overview:screen:tenant-cap-list`, optional `document-designer` template navs) and the two overview dashboards, built from the metrics list (`metrics` option, default DEFAULT_METRICS). Mount after user, tenant, audit, jobs, metrics and metrics-system; pass `workspaceIds` to match app URL conventions (e.g. Studio `d`/`s`, PublicStatus `admin`/`sysadmin`). Client: `adminShellClient()`, `tenantClient()`, `auditClient()`, `jobsClient()`, optional `tierEngineClient()`.",
|
|
58
62
|
);
|
|
59
63
|
r.uiHints({
|
|
60
64
|
displayLabel: "Admin Shell",
|
|
@@ -69,6 +73,7 @@ export function createAdminShellFeature(options: CreateAdminShellOptions = {}):
|
|
|
69
73
|
r.requires("metrics-system");
|
|
70
74
|
if (includeTierAdmin) r.requires("tier-engine");
|
|
71
75
|
if (includeCapOverview) r.requires("cap-overview");
|
|
76
|
+
if (includeDocumentDesigner) r.requires("document-designer");
|
|
72
77
|
|
|
73
78
|
r.translations({ keys: ADMIN_SHELL_I18N });
|
|
74
79
|
|
|
@@ -77,12 +82,14 @@ export function createAdminShellFeature(options: CreateAdminShellOptions = {}):
|
|
|
77
82
|
"tenant:nav:members",
|
|
78
83
|
"audit:nav:audit-log",
|
|
79
84
|
...(includeCapOverview ? (["admin-shell:nav:my-caps"] as const) : []),
|
|
85
|
+
...(includeDocumentDesigner ? (["document-designer:nav:templates"] as const) : []),
|
|
80
86
|
];
|
|
81
87
|
const platformNav: string[] = [
|
|
82
88
|
"admin-shell:nav:platform-overview",
|
|
83
89
|
"admin-shell:nav:tenants",
|
|
84
90
|
...(includeCapOverview ? (["admin-shell:nav:tenant-caps"] as const) : []),
|
|
85
91
|
"jobs:nav:job-runs",
|
|
92
|
+
...(includeDocumentDesigner ? (["document-designer:nav:platform-templates"] as const) : []),
|
|
86
93
|
...(includeTierAdmin ? (["admin-shell:nav:tier-admin"] as const) : []),
|
|
87
94
|
];
|
|
88
95
|
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
SYSTEM_ONLY_JSON_SCHEMA_KEY,
|
|
19
19
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
20
20
|
import { translationValueOtherText } from "@cosmicdrift/kumiko-framework/ui-types";
|
|
21
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
21
22
|
import * as z from "zod";
|
|
22
23
|
import { recordManifestDenyQns } from "./manifest-deny-qns.js";
|
|
23
24
|
import type {
|
|
@@ -199,7 +200,7 @@ export function systemOnlyPropertyNames(jsonSchema: Record<string, unknown>): re
|
|
|
199
200
|
if (typeof properties !== "object" || properties === null) return [];
|
|
200
201
|
return Object.entries(properties)
|
|
201
202
|
.filter(
|
|
202
|
-
([, property]) => (property
|
|
203
|
+
([, property]) => isPlainObject(property) && property[SYSTEM_ONLY_JSON_SCHEMA_KEY] === true,
|
|
203
204
|
)
|
|
204
205
|
.map(([name]) => name);
|
|
205
206
|
}
|
|
@@ -243,9 +244,7 @@ function buildHandlerEntry(
|
|
|
243
244
|
// A handler whose schema can't be expressed as JSON Schema (transforms,
|
|
244
245
|
// z.instanceof, ...) is unusable for an agent tool call — leave it out
|
|
245
246
|
// rather than ship a broken tool.
|
|
246
|
-
inputSchema = withoutSystemOnlyProperties(
|
|
247
|
-
z.toJSONSchema(def.schema, { io: "input" }) as Record<string, unknown>,
|
|
248
|
-
);
|
|
247
|
+
inputSchema = withoutSystemOnlyProperties(z.toJSONSchema(def.schema, { io: "input" }));
|
|
249
248
|
} catch {
|
|
250
249
|
return undefined;
|
|
251
250
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
export const FILTER_OPS = ["eq", "ne", "lt", "gt", "lte", "gte", "in"] as const;
|
|
2
2
|
|
|
3
|
-
export
|
|
4
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5
|
-
}
|
|
3
|
+
export { isPlainObject as isRecord } from "@cosmicdrift/kumiko-types/type-guards";
|
|
@@ -137,9 +137,7 @@ function addToolsForListHandler(
|
|
|
137
137
|
addTool(sink, tool, { kind: "search", entityName, qn });
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
for (const [fieldName, field] of Object.entries(
|
|
141
|
-
entity.fields as Record<string, FieldDefinition>,
|
|
142
|
-
)) {
|
|
140
|
+
for (const [fieldName, field] of Object.entries(entity.fields)) {
|
|
143
141
|
if (!isFilterable(field) || !isReadableWithoutRowCondition(field, roles)) continue;
|
|
144
142
|
const fieldSchema = jsonSchemaTypeForField(field);
|
|
145
143
|
if (!fieldSchema) continue;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { SessionUser, WriteResult } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { runAsDirectCallEntry } from "@cosmicdrift/kumiko-framework/internal/request-context";
|
|
3
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
import { FILTER_OPS, isRecord } from "./filter-ops.js";
|
|
4
5
|
import type { ToolDispatchDescriptor } from "./types.js";
|
|
5
6
|
|
|
@@ -48,7 +49,7 @@ type PayloadResult =
|
|
|
48
49
|
| { readonly ok: false; readonly error: string };
|
|
49
50
|
|
|
50
51
|
function isFilterOp(value: unknown): value is FilterOp {
|
|
51
|
-
return
|
|
52
|
+
return isOneOf(FILTER_OPS, value);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
function buildSearchPayload(
|
|
@@ -117,11 +117,16 @@ export const listQuery = defineQueryHandler({
|
|
|
117
117
|
createdAt: unknown;
|
|
118
118
|
createdBy: string;
|
|
119
119
|
}>(ctx.db, eventsTable, where, {
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
// The id cursor only resumes an id-ordered listing. created_at comes from the
|
|
121
|
+
// transaction start and can run against insert order, so ordering by it would let
|
|
122
|
+
// a page boundary skip rows; only the type sort leaves id order (and is one page).
|
|
123
|
+
orderBy:
|
|
124
|
+
p.sort === "type"
|
|
125
|
+
? [
|
|
126
|
+
{ col: "type", direction },
|
|
127
|
+
{ col: "id", direction },
|
|
128
|
+
]
|
|
129
|
+
: [{ col: "id", direction }],
|
|
125
130
|
limit: p.limit,
|
|
126
131
|
});
|
|
127
132
|
|
|
@@ -7,23 +7,26 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
makeDispatchSystemWrite,
|
|
10
|
-
|
|
10
|
+
makeDispatchWriteAs,
|
|
11
|
+
type RoleDispatchArgs,
|
|
11
12
|
} from "@cosmicdrift/kumiko-framework/api";
|
|
12
13
|
import { fetchOne } from "@cosmicdrift/kumiko-framework/bun-db";
|
|
13
14
|
import type { DbConnection } from "@cosmicdrift/kumiko-framework/db";
|
|
14
15
|
import {
|
|
15
16
|
ConfigScopes,
|
|
16
17
|
createSystemUser,
|
|
18
|
+
type Registry,
|
|
17
19
|
type TenantId,
|
|
18
20
|
type WriteResult,
|
|
19
21
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
20
22
|
import type { Dispatcher } from "@cosmicdrift/kumiko-framework/pipeline";
|
|
23
|
+
import type { EnvelopeCipher } from "@cosmicdrift/kumiko-framework/secrets";
|
|
21
24
|
import { parseRoles } from "@cosmicdrift/kumiko-framework/utils";
|
|
22
25
|
import type { Redis } from "ioredis";
|
|
23
26
|
// kumiko-lint-ignore cross-feature-import bootstrap sets per-tenant config through the config feature's write handler
|
|
24
27
|
import { ConfigHandlers } from "../config/constants.js";
|
|
25
28
|
// kumiko-lint-ignore cross-feature-import bootstrap compares against the stored config row to stay idempotent
|
|
26
|
-
import { findConfigRow } from "../config/write-helpers.js";
|
|
29
|
+
import { configRowHoldsValue, findConfigRow } from "../config/write-helpers.js";
|
|
27
30
|
// kumiko-lint-ignore cross-feature-import bootstrap provisions tenant + invitation rows owned by the tenant feature
|
|
28
31
|
import { TenantHandlers } from "../tenant/constants.js";
|
|
29
32
|
// kumiko-lint-ignore cross-feature-import bootstrap provisions tenant + invitation rows owned by the tenant feature
|
|
@@ -55,9 +58,7 @@ export type BootstrapSeedDeps = {
|
|
|
55
58
|
/** Writes as the system user with the given tenant roles in the seeded
|
|
56
59
|
* tenant; use it when the app gates tenant data on its own roles rather
|
|
57
60
|
* than SystemAdmin. */
|
|
58
|
-
readonly dispatchWriteAs: (
|
|
59
|
-
args: Pick<SystemDispatchArgs, "handlerQn" | "payload"> & { readonly roles: readonly string[] },
|
|
60
|
-
) => Promise<WriteResult>;
|
|
61
|
+
readonly dispatchWriteAs: (args: Omit<RoleDispatchArgs, "tenantId">) => Promise<WriteResult>;
|
|
61
62
|
};
|
|
62
63
|
|
|
63
64
|
export type BootstrapConfigValue = string | number | boolean;
|
|
@@ -96,6 +97,10 @@ export type BootstrapDeps = {
|
|
|
96
97
|
readonly db: DbConnection;
|
|
97
98
|
readonly redis: Redis;
|
|
98
99
|
readonly dispatcher: Dispatcher;
|
|
100
|
+
/** With these, a rerun can compare `encrypted`/`piiEncrypted` config keys by
|
|
101
|
+
* their decrypted value; without them such keys are rewritten on every run. */
|
|
102
|
+
readonly registry?: Registry;
|
|
103
|
+
readonly configCipher?: EnvelopeCipher;
|
|
99
104
|
};
|
|
100
105
|
|
|
101
106
|
export type BootstrapTenantOutcome = "created" | "exists";
|
|
@@ -216,7 +221,18 @@ async function applyTenantConfig(
|
|
|
216
221
|
const applied: string[] = [];
|
|
217
222
|
for (const [key, value] of Object.entries(tenant.config ?? {})) {
|
|
218
223
|
const existing = await findConfigRow(deps.db, key, tenant.id, null);
|
|
219
|
-
|
|
224
|
+
const serialized = JSON.stringify(value);
|
|
225
|
+
if (
|
|
226
|
+
await configRowHoldsValue(
|
|
227
|
+
existing,
|
|
228
|
+
deps.registry?.getConfigKey(key),
|
|
229
|
+
deps.configCipher,
|
|
230
|
+
key,
|
|
231
|
+
serialized,
|
|
232
|
+
)
|
|
233
|
+
) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
220
236
|
const result = await deps.dispatcher.write(
|
|
221
237
|
ConfigHandlers.set,
|
|
222
238
|
{ key, value, scope: ConfigScopes.tenant },
|
|
@@ -266,6 +282,11 @@ async function decideInvite(
|
|
|
266
282
|
return "role-mismatch";
|
|
267
283
|
}
|
|
268
284
|
if (invitation.status === INVITATION_STATUS.accepted) return "active";
|
|
285
|
+
// The plan dropped a global role (e.g. an admin moved to tenants[].invites):
|
|
286
|
+
// the stored invitation would still grant it on accept, so it is re-issued.
|
|
287
|
+
if (storedGlobalRoles.some((role) => !invite.globalRoles.some((planned) => planned === role))) {
|
|
288
|
+
return "resent";
|
|
289
|
+
}
|
|
269
290
|
if (await hasLiveInviteToken(deps.redis, invitation.id)) return "pending";
|
|
270
291
|
return "resent";
|
|
271
292
|
}
|
|
@@ -280,6 +301,7 @@ export async function bootstrapTenants(
|
|
|
280
301
|
): Promise<BootstrapReport> {
|
|
281
302
|
const plannedInvites = planInvites(plan);
|
|
282
303
|
const dispatchSystemWrite = makeDispatchSystemWrite(deps.dispatcher);
|
|
304
|
+
const dispatchWriteAsRoles = makeDispatchWriteAs(deps.dispatcher);
|
|
283
305
|
|
|
284
306
|
const tenants: Array<BootstrapReport["tenants"][number]> = [];
|
|
285
307
|
for (const tenant of plan.tenants) {
|
|
@@ -290,8 +312,7 @@ export async function bootstrapTenants(
|
|
|
290
312
|
tenantId: tenant.id,
|
|
291
313
|
db: deps.db,
|
|
292
314
|
dispatchSystemWrite,
|
|
293
|
-
dispatchWriteAs: ({
|
|
294
|
-
deps.dispatcher.write(handlerQn, payload, createSystemUser(tenant.id, roles)),
|
|
315
|
+
dispatchWriteAs: (args) => dispatchWriteAsRoles({ ...args, tenantId: tenant.id }),
|
|
295
316
|
});
|
|
296
317
|
}
|
|
297
318
|
const configApplied = await applyTenantConfig(deps, tenant);
|
|
@@ -1,4 +1,55 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.364.0",
|
|
4
|
+
"type": "fix",
|
|
5
|
+
"title": "A loginUrl builder that throws no longer crashes the app tree",
|
|
6
|
+
"detail": "The auth gate resolves the login redirect once per location change and, when the builder returns an invalid URL or throws, logs the error and shows the built-in login screen instead of throwing during render."
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"version": "0.364.0",
|
|
10
|
+
"type": "fix",
|
|
11
|
+
"title": "How to send an auth mail after the render*Email helpers were removed in 0.363.0",
|
|
12
|
+
"detail": "No runtime change. The 0.363.0 breaking entry lists renderActivationEmail, renderResetPasswordEmail, renderUnlockAccountEmail, renderVerifyEmail and renderInviteEmail as removed but does not say how app code that rendered one of these mails itself now sends it. This entry adds that recipe.",
|
|
13
|
+
"migration": "App code that rendered an auth mail itself and passed the result as `data`, for example `notify(\"auth-email-password:signup-activation\", { route: { email }, data: renderActivationEmail({ url, expiresAt, locale, appName }), priority: \"critical\" })`, breaks twice since 0.363.0: the helper is gone and the notification type is now `auth-email-password:notify:signup-activation`. The stored template renders subject and body; the app only passes template variables.\nFor a signup activation link, call `issueSignupActivation({ redis, notify: ctx.notify, email, appUrl, appName, locale, timeZone, tokenTtlMinutes, onTokenStored })` from `@cosmicdrift/kumiko-bundled-features/auth-email-password` (only redis, notify, email and appUrl are required). It drops any live signup token for the email, stores a new one under the keys signup-confirm reads and sends the mail. `appUrl` is the bare activation page; a function `(locale) => url` picks the page per locale.\nIf the app stores the token itself and only needs the mail, call `ctx.notify(AuthMailNotificationTypes.signupActivation, { route: { email }, data: { appName, actionUrl, validFor, validUntil }, locale, priority: \"critical\" })`. `AuthMailNotificationTypes` comes from `@cosmicdrift/kumiko-bundled-features/auth-email-password`. All four variables are required strings: ctx.notify checks them against the declared template variables and throws an InternalError on a missing or invalid one, which fails the write. `actionUrl` is the page URL with `token=<encodeURIComponent(token)>` appended as a query parameter and must be an http, https or mailto URL. `validFor` and `validUntil` come from `tokenMailExpiry({ expiresAt, issuedAt, timeZone }, locale)` in `@cosmicdrift/kumiko-bundled-features/auth-email-password/email-templates`. Only send a `locale` that has a template for the type, because the renderer has no locale fallback and a locale without a template sends nothing: use `resolveMailLocale(locale)` when `hasMailTranslationKey(resolveMailLocale(locale), \"auth.mail.activation.subject\")` is true (both from `@cosmicdrift/kumiko-framework/i18n`), otherwise `\"en\"`. The other mails use the subject keys `auth.mail.reset.subject`, `auth.mail.unlock.subject`, `auth.mail.verify.subject` and `auth.mail.invite.subject`.\nThe other auth mails work the same way with `AuthMailNotificationTypes.passwordReset`, `.accountUnlock`, `.emailVerification` and `.invite`; the invite mail also needs a `role` variable."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"version": "0.364.0",
|
|
17
|
+
"type": "fix",
|
|
18
|
+
"title": "seedAdminGuarded rewrites the admin email only when its blind index is missing or stale",
|
|
19
|
+
"detail": "rewriteSeededUserEmail returns early when the stored email blind index already matches the expected value, so repeated boots no longer re-encrypt the email and grow the user event stream. The row version is now checked with a type guard instead of a cast."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"version": "0.364.0",
|
|
23
|
+
"type": "fix",
|
|
24
|
+
"title": "Bootstrap reruns no longer rewrite encrypted config or restore dropped global roles",
|
|
25
|
+
"detail": "Declarative bootstrap compares encrypted and piiEncrypted config keys by their decrypted value, so a matching rerun writes nothing, and re-issues an invitation whose stored global roles are no longer in the plan. makeDispatchWriteAs is exported from the framework api."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"version": "0.364.0",
|
|
29
|
+
"type": "fix",
|
|
30
|
+
"title": "Email payload buckets ignore plus-tags and case; auth requests are capped at 3 per 15 minutes",
|
|
31
|
+
"detail": "additionalRateLimits accepts normalize: email, which lowercases and strips the plus-tag before hashing, and bucket keys are scoped by tenant and handler. Signup and token requests (reset, unlock, verification) and the waitlist default use it with a 3-request, 900-second window."
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"version": "0.364.0",
|
|
35
|
+
"type": "fix",
|
|
36
|
+
"title": "Password change and logout work for members with only an app role or TenantAdmin",
|
|
37
|
+
"detail": "Both handlers required User, Admin or SystemAdmin and answered 403 access_denied for invited members holding a single custom role. They are now open to every signed-in user, like the other self-service handlers; anonymous callers are still rejected."
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"version": "0.363.0",
|
|
41
|
+
"type": "breaking",
|
|
42
|
+
"title": "AuthMailContent is removed",
|
|
43
|
+
"detail": "The sections-based content type only fed the retired renderer-simple. Auth mails are template notifications with variables, so nothing builds or consumes AuthMailContent anymore.",
|
|
44
|
+
"migration": "Drop AuthMailContent imports; the auth mail text lives in the seeded templates and is overridden per tenant in the template editor."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"version": "0.363.0",
|
|
48
|
+
"type": "breaking",
|
|
49
|
+
"title": "Auth mails are notifications with seeded templates, render*Email is gone",
|
|
50
|
+
"detail": "password-reset, account-unlock, email-verification, signup-activation and invite are declared as r.notification with a localized template and variables, seeded as system templates at boot and rendered by renderer-mail-html, so a tenant can override the text in the template editor. A locale without its own subject key falls back to en, because the renderer has no locale fallback. AuthMailNotificationTypes is exported for the qualified names.",
|
|
51
|
+
"migration": "The notification types are now `auth-email-password:notify:<name>` (before: the plain names). Update delivery preferences, preference rows and tests that reference the old names. renderResetPasswordEmail, renderUnlockAccountEmail, renderVerifyEmail, renderActivationEmail, renderInviteEmail, AuthMailSection, RenderTokenContentArgs and RenderInviteEmailArgs are removed; AuthMailContent stays. An app that mounts auth-email-password now needs template-resolver, renderer-foundation and @cosmicdrift/kumiko-renderer-mail-html (composeRendererStack() from dev-server mounts all of them; with includeBundled, composeFeatures adds whichever is missing). Boot fails with a clear error otherwise, and apps without the template tables need a schema migration (`kumiko schema generate`). kumiko-schema-check counts the three as implicit features of every HAS_AUTH app, so its check fails until that migration exists. Tests that read the token from a mail use the text part (`mail.text`); the html part encodes `=` as `=`."
|
|
52
|
+
},
|
|
2
53
|
{
|
|
3
54
|
"version": "0.354.0",
|
|
4
55
|
"type": "breaking",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PayloadRateLimitOption } from "@cosmicdrift/kumiko-types/handlers";
|
|
2
|
+
|
|
3
|
+
// The bucket counts across all IPs, so a long window would let an attacker who
|
|
4
|
+
// knows an address lock its owner out of recovery/signup for the whole window.
|
|
5
|
+
// A short one still throttles mail bombing but clears within minutes.
|
|
6
|
+
export const AUTH_EMAIL_RATE_LIMIT_WINDOW_SECONDS = 15 * 60;
|
|
7
|
+
export const AUTH_EMAIL_RATE_LIMIT_MAX_REQUESTS = 3;
|
|
8
|
+
|
|
9
|
+
export const AUTH_EMAIL_PAYLOAD_RATE_LIMITS: readonly PayloadRateLimitOption[] = [
|
|
10
|
+
{
|
|
11
|
+
per: { payloadField: "email" },
|
|
12
|
+
limit: AUTH_EMAIL_RATE_LIMIT_MAX_REQUESTS,
|
|
13
|
+
windowSeconds: AUTH_EMAIL_RATE_LIMIT_WINDOW_SECONDS,
|
|
14
|
+
normalize: "email",
|
|
15
|
+
},
|
|
16
|
+
];
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// renderer-simple turns it into HTML. Apps wanting their own branding swap the
|
|
5
|
-
// renderer; these templates are deliberately plain so the renderer (and the
|
|
6
|
-
// operator reading the mailer log) can rely on them.
|
|
1
|
+
// English texts and expiry formatting for the transactional auth mails. The
|
|
2
|
+
// mail bodies are built from these keys into stored templates (see
|
|
3
|
+
// mail-notifications.ts).
|
|
7
4
|
//
|
|
8
5
|
// English ships here. Other languages register via @cosmicdrift/kumiko-locale-*
|
|
9
6
|
// (localeDe() / localeEs() call registerMailTranslations).
|
|
10
7
|
|
|
11
|
-
import {
|
|
8
|
+
import { registerMailTranslations } from "@cosmicdrift/kumiko-framework/i18n";
|
|
12
9
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
13
10
|
|
|
14
11
|
export type AuthMailLocale = string;
|
|
@@ -56,138 +53,14 @@ export const AUTH_MAIL_EN: Readonly<Record<string, string>> = {
|
|
|
56
53
|
|
|
57
54
|
registerMailTranslations("en", AUTH_MAIL_EN);
|
|
58
55
|
|
|
59
|
-
export type
|
|
60
|
-
readonly url: string;
|
|
56
|
+
export type TokenMailTiming = {
|
|
61
57
|
readonly expiresAt: string;
|
|
62
|
-
readonly locale?: AuthMailLocale;
|
|
63
|
-
/** Optional app name for subject + intro. Default from auth.mail.appNameDefault. */
|
|
64
|
-
readonly appName?: string;
|
|
65
58
|
/** ISO instant the token was issued; the validity duration is measured from it. Default: now. */
|
|
66
59
|
readonly issuedAt?: string;
|
|
67
60
|
/** IANA zone the expiry time is shown in. Default "UTC"; an invalid zone falls back to "UTC". */
|
|
68
61
|
readonly timeZone?: string;
|
|
69
62
|
};
|
|
70
63
|
|
|
71
|
-
export type RenderInviteEmailArgs = RenderTokenContentArgs & { readonly role: string };
|
|
72
|
-
|
|
73
|
-
export type AuthMailSection =
|
|
74
|
-
| { readonly text: string }
|
|
75
|
-
| { readonly button: { readonly label: string; readonly url: string } };
|
|
76
|
-
|
|
77
|
-
export type AuthMailContent = {
|
|
78
|
-
readonly subject: string;
|
|
79
|
-
readonly header: string;
|
|
80
|
-
readonly sections: readonly AuthMailSection[];
|
|
81
|
-
readonly footer: string;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
function tokenMailContent(spec: {
|
|
85
|
-
readonly subject: string;
|
|
86
|
-
readonly header: string;
|
|
87
|
-
readonly greeting: string;
|
|
88
|
-
readonly intro: string;
|
|
89
|
-
readonly buttonLabel: string;
|
|
90
|
-
readonly buttonUrl: string;
|
|
91
|
-
readonly expiry: string;
|
|
92
|
-
readonly ignore: string;
|
|
93
|
-
}): AuthMailContent {
|
|
94
|
-
return {
|
|
95
|
-
subject: spec.subject,
|
|
96
|
-
header: spec.header,
|
|
97
|
-
sections: [
|
|
98
|
-
{ text: spec.greeting },
|
|
99
|
-
{ text: spec.intro },
|
|
100
|
-
{ button: { label: spec.buttonLabel, url: spec.buttonUrl } },
|
|
101
|
-
{ text: spec.expiry },
|
|
102
|
-
],
|
|
103
|
-
footer: spec.ignore,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function t(locale: string, key: string, params?: Readonly<Record<string, string>>): string {
|
|
108
|
-
return mailT(locale, key, params);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function appNameFor(args: RenderTokenContentArgs): string {
|
|
112
|
-
const locale = args.locale ?? "en";
|
|
113
|
-
return args.appName ?? t(locale, "auth.mail.appNameDefault");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
export function renderResetPasswordEmail(args: RenderTokenContentArgs): AuthMailContent {
|
|
117
|
-
const locale = args.locale ?? "en";
|
|
118
|
-
const app = appNameFor(args);
|
|
119
|
-
return tokenMailContent({
|
|
120
|
-
subject: t(locale, "auth.mail.reset.subject", { app }),
|
|
121
|
-
header: t(locale, "auth.mail.reset.button"),
|
|
122
|
-
greeting: t(locale, "auth.mail.reset.greeting"),
|
|
123
|
-
intro: t(locale, "auth.mail.reset.intro", { app }),
|
|
124
|
-
buttonLabel: t(locale, "auth.mail.reset.button"),
|
|
125
|
-
buttonUrl: args.url,
|
|
126
|
-
expiry: t(locale, "auth.mail.reset.expiry", expiryParams(args, locale)),
|
|
127
|
-
ignore: t(locale, "auth.mail.reset.ignore"),
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function renderUnlockAccountEmail(args: RenderTokenContentArgs): AuthMailContent {
|
|
132
|
-
const locale = args.locale ?? "en";
|
|
133
|
-
const app = appNameFor(args);
|
|
134
|
-
return tokenMailContent({
|
|
135
|
-
subject: t(locale, "auth.mail.unlock.subject", { app }),
|
|
136
|
-
header: t(locale, "auth.mail.unlock.button"),
|
|
137
|
-
greeting: t(locale, "auth.mail.unlock.greeting"),
|
|
138
|
-
intro: t(locale, "auth.mail.unlock.intro", { app }),
|
|
139
|
-
buttonLabel: t(locale, "auth.mail.unlock.button"),
|
|
140
|
-
buttonUrl: args.url,
|
|
141
|
-
expiry: t(locale, "auth.mail.unlock.expiry", expiryParams(args, locale)),
|
|
142
|
-
ignore: t(locale, "auth.mail.unlock.ignore"),
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function renderVerifyEmail(args: RenderTokenContentArgs): AuthMailContent {
|
|
147
|
-
const locale = args.locale ?? "en";
|
|
148
|
-
const app = appNameFor(args);
|
|
149
|
-
return tokenMailContent({
|
|
150
|
-
subject: t(locale, "auth.mail.verify.subject", { app }),
|
|
151
|
-
header: t(locale, "auth.mail.verify.button"),
|
|
152
|
-
greeting: t(locale, "auth.mail.verify.greeting"),
|
|
153
|
-
intro: t(locale, "auth.mail.verify.intro", { app }),
|
|
154
|
-
buttonLabel: t(locale, "auth.mail.verify.button"),
|
|
155
|
-
buttonUrl: args.url,
|
|
156
|
-
expiry: t(locale, "auth.mail.verify.expiry", expiryParams(args, locale)),
|
|
157
|
-
ignore: t(locale, "auth.mail.verify.ignore"),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function renderActivationEmail(args: RenderTokenContentArgs): AuthMailContent {
|
|
162
|
-
const locale = args.locale ?? "en";
|
|
163
|
-
const app = appNameFor(args);
|
|
164
|
-
return tokenMailContent({
|
|
165
|
-
subject: t(locale, "auth.mail.activation.subject", { app }),
|
|
166
|
-
header: t(locale, "auth.mail.activation.button"),
|
|
167
|
-
greeting: t(locale, "auth.mail.activation.greeting"),
|
|
168
|
-
intro: t(locale, "auth.mail.activation.intro", { app }),
|
|
169
|
-
buttonLabel: t(locale, "auth.mail.activation.button"),
|
|
170
|
-
buttonUrl: args.url,
|
|
171
|
-
expiry: t(locale, "auth.mail.activation.expiry", expiryParams(args, locale)),
|
|
172
|
-
ignore: t(locale, "auth.mail.activation.ignore"),
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function renderInviteEmail(args: RenderInviteEmailArgs): AuthMailContent {
|
|
177
|
-
const locale = args.locale ?? "en";
|
|
178
|
-
const app = args.appName ?? "Workspace";
|
|
179
|
-
return tokenMailContent({
|
|
180
|
-
subject: t(locale, "auth.mail.invite.subject", { app }),
|
|
181
|
-
header: t(locale, "auth.mail.invite.button"),
|
|
182
|
-
greeting: t(locale, "auth.mail.invite.greeting"),
|
|
183
|
-
intro: t(locale, "auth.mail.invite.intro", { app, role: args.role }),
|
|
184
|
-
buttonLabel: t(locale, "auth.mail.invite.button"),
|
|
185
|
-
buttonUrl: args.url,
|
|
186
|
-
expiry: t(locale, "auth.mail.invite.expiry", expiryParams(args, locale)),
|
|
187
|
-
ignore: t(locale, "auth.mail.invite.ignore"),
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
|
|
191
64
|
const MS_PER_MINUTE = 60_000;
|
|
192
65
|
const MS_PER_HOUR = 60 * MS_PER_MINUTE;
|
|
193
66
|
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
@@ -241,18 +114,19 @@ function formatWhen(expiresAt: Temporal.Instant, locale: string, timeZone: strin
|
|
|
241
114
|
}
|
|
242
115
|
}
|
|
243
116
|
|
|
244
|
-
|
|
245
|
-
|
|
117
|
+
// Formatted as text here because the stored template only knows plain variables.
|
|
118
|
+
export function tokenMailExpiry(
|
|
119
|
+
args: TokenMailTiming,
|
|
246
120
|
locale: string,
|
|
247
|
-
): { readonly
|
|
121
|
+
): { readonly validFor: string; readonly validUntil: string } {
|
|
248
122
|
try {
|
|
249
123
|
const expiresAt = Temporal.Instant.from(args.expiresAt);
|
|
250
124
|
const issuedAt = args.issuedAt ? Temporal.Instant.from(args.issuedAt) : Temporal.Now.instant();
|
|
251
125
|
return {
|
|
252
|
-
|
|
253
|
-
|
|
126
|
+
validFor: formatDuration(expiresAt.epochMilliseconds - issuedAt.epochMilliseconds, locale),
|
|
127
|
+
validUntil: formatWhen(expiresAt, locale, args.timeZone ?? "UTC"),
|
|
254
128
|
};
|
|
255
129
|
} catch {
|
|
256
|
-
return {
|
|
130
|
+
return { validFor: args.expiresAt, validUntil: args.expiresAt };
|
|
257
131
|
}
|
|
258
132
|
}
|