@cosmicdrift/kumiko-samples 0.362.0 → 0.363.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 +17 -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/auth-email-password/changes.json +14 -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/invite-create.write.ts +4 -5
- 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/token-request-handler.ts +3 -12
- 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-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 +7 -0
- package/packages/bundled-features/src/billing-foundation/constants.ts +5 -4
- package/packages/bundled-features/src/billing-foundation/consumer-protection/confirmation-mail.ts +38 -44
- 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 +115 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/register-consent.ts +2 -0
- package/packages/bundled-features/src/billing-foundation/consumer-protection/termination-mail.ts +30 -30
- 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-request.ts +2 -2
- package/packages/bundled-features/src/billing-foundation/get-subscription-for-tenant.ts +2 -2
- 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 +2 -2
- package/packages/bundled-features/src/billing-foundation/handlers/process-payment-event.write.ts +1 -1
- package/packages/bundled-features/src/billing-foundation/subscription-tier-sync.ts +3 -6
- 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 +122 -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/handlers/set.write.ts +1 -5
- package/packages/bundled-features/src/custom-fields/db/queries/user-data-rights.ts +2 -2
- 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 +84 -0
- package/packages/bundled-features/src/delivery/changes.json +48 -0
- package/packages/bundled-features/src/delivery/channel-context.ts +129 -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 +17 -2
- package/packages/bundled-features/src/delivery/jobs.ts +48 -5
- 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 +28 -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-foundation/index.ts +7 -0
- package/packages/bundled-features/src/file-foundation/read-file-ref-content.ts +86 -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/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-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 +40 -25
- package/packages/bundled-features/src/inbound-provider-imap/imap-client.ts +4 -3
- package/packages/bundled-features/src/jobs/handlers/catalog.query.ts +1 -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 +132 -35
- package/packages/bundled-features/src/renderer-pdf/feature.ts +7 -1
- 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/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 +25 -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/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 +63 -7
- package/packages/bundled-features/src/template-resolver/api.ts +83 -8
- package/packages/bundled-features/src/template-resolver/changes.json +118 -0
- package/packages/bundled-features/src/template-resolver/compile-document-body.ts +214 -0
- package/packages/bundled-features/src/template-resolver/constants.ts +39 -11
- package/packages/bundled-features/src/template-resolver/document-sanitizer.ts +335 -0
- package/packages/bundled-features/src/template-resolver/document-schema.ts +323 -0
- package/packages/bundled-features/src/template-resolver/feature.ts +56 -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 +142 -0
- package/packages/bundled-features/src/template-resolver/handlers/diff-to-system.query.ts +72 -0
- package/packages/bundled-features/src/template-resolver/handlers/export-bundle.query.ts +83 -0
- package/packages/bundled-features/src/template-resolver/handlers/find-by-id.query.ts +3 -3
- 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 +108 -0
- package/packages/bundled-features/src/template-resolver/handlers/list.query.ts +5 -7
- package/packages/bundled-features/src/template-resolver/handlers/overview.query.ts +250 -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 +106 -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 +184 -0
- package/packages/bundled-features/src/template-resolver/handlers/template-write.ts +183 -0
- package/packages/bundled-features/src/template-resolver/handlers/tenant-overrides.query.ts +55 -0
- package/packages/bundled-features/src/template-resolver/handlers/toggle-status.write.ts +57 -10
- 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 +15 -76
- package/packages/bundled-features/src/template-resolver/handlers/usage.query.ts +17 -0
- package/packages/bundled-features/src/template-resolver/handlers/validate.query.ts +116 -0
- package/packages/bundled-features/src/template-resolver/index.ts +100 -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/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 +30 -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 +147 -17
- package/packages/bundled-features/src/template-resolver/store-template-resource.ts +416 -0
- package/packages/bundled-features/src/template-resolver/table.ts +14 -0
- 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-meta.ts +129 -0
- package/packages/bundled-features/src/template-resolver/template-preview.ts +573 -0
- package/packages/bundled-features/src/template-resolver/template-resources.ts +74 -0
- package/packages/bundled-features/src/template-resolver/template-usage.ts +48 -0
- package/packages/bundled-features/src/template-resolver/template-validation.ts +229 -0
- package/packages/bundled-features/src/template-resolver/testing.ts +5 -5
- package/packages/bundled-features/src/template-resolver/web/client-plugin.tsx +9 -6
- package/packages/bundled-features/src/template-resolver/web/document-editor-extensions.ts +162 -0
- package/packages/bundled-features/src/template-resolver/web/document-editor.tsx +28 -0
- package/packages/bundled-features/src/template-resolver/web/document-tiptap-editor.tsx +850 -0
- package/packages/bundled-features/src/template-resolver/web/document-toolbar-blocks.ts +61 -0
- package/packages/bundled-features/src/template-resolver/web/editor-handle.ts +26 -0
- package/packages/bundled-features/src/template-resolver/web/i18n.ts +100 -0
- package/packages/bundled-features/src/template-resolver/web/index.ts +19 -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 +169 -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/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/roles-input-schema.ts +2 -2
- package/packages/bundled-features/src/user-data-rights/changes.json +7 -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/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/workflow-runner/event-trigger.ts +1 -2
- package/samples/apps/ui-walkthrough/package.json +2 -0
- 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 +154 -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 +354 -0
- package/samples/apps/ui-walkthrough/src/features/billing/feature.ts +68 -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 +37 -1
- package/samples/apps/use-all-bundled/e2e-billing-gallery/gallery-features.ts +0 -2
- package/samples/apps/use-all-bundled/feature-manifest.json +115 -20
- 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
|
@@ -21,23 +21,7 @@ export type {
|
|
|
21
21
|
} from "./bootstrap.js";
|
|
22
22
|
export { BootstrapPlanError, BootstrapWriteError, bootstrapTenants } from "./bootstrap.js";
|
|
23
23
|
export { AUTH_EMAIL_PASSWORD_FEATURE, AuthErrors, AuthHandlers, AuthQueries } from "./constants.js";
|
|
24
|
-
|
|
25
|
-
// signup-activation, invite) emit structured AuthMailContent through delivery
|
|
26
|
-
// (ctx.notify).
|
|
27
|
-
export type {
|
|
28
|
-
AuthMailContent,
|
|
29
|
-
AuthMailLocale,
|
|
30
|
-
AuthMailSection,
|
|
31
|
-
RenderInviteEmailArgs,
|
|
32
|
-
RenderTokenContentArgs,
|
|
33
|
-
} from "./email-templates.js";
|
|
34
|
-
export {
|
|
35
|
-
renderActivationEmail,
|
|
36
|
-
renderInviteEmail,
|
|
37
|
-
renderResetPasswordEmail,
|
|
38
|
-
renderUnlockAccountEmail,
|
|
39
|
-
renderVerifyEmail,
|
|
40
|
-
} from "./email-templates.js";
|
|
24
|
+
export type { AuthMailLocale } from "./email-templates.js";
|
|
41
25
|
export type {
|
|
42
26
|
AccountLockoutOptions,
|
|
43
27
|
AccountUnlockOptions,
|
|
@@ -48,6 +32,7 @@ export type {
|
|
|
48
32
|
SignupOptions,
|
|
49
33
|
} from "./feature.js";
|
|
50
34
|
export { authEmailPasswordEnvSchema, createAuthEmailPasswordFeature } from "./feature.js";
|
|
35
|
+
export { AuthMailNotificationTypes } from "./mail-notifications.js";
|
|
51
36
|
export {
|
|
52
37
|
AUTH_SELF_REGISTRATION_FEATURE,
|
|
53
38
|
createAuthSelfRegistrationToggleFeature,
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
// Shared dispatch for the magic-link mails (password-reset, email-verification,
|
|
2
|
-
// signup-activation). All
|
|
3
|
-
// token to the app page
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
2
|
+
// account-unlock, signup-activation, invite). All follow the same delivery
|
|
3
|
+
// shape: append the token to the app page and notify the recipient email
|
|
4
|
+
// directly (route:{email} skips preferences — the recipient may not have a user
|
|
5
|
+
// account yet — and critical priority keeps a security mail non-unsubscribable).
|
|
6
|
+
// Each mail type is a trigger-less r.notification whose stored template renders
|
|
7
|
+
// from the variables passed here.
|
|
8
8
|
|
|
9
9
|
import { requestContext } from "@cosmicdrift/kumiko-framework/api";
|
|
10
10
|
import type { NotifyFn } from "@cosmicdrift/kumiko-framework/engine";
|
|
11
11
|
import { InternalError } from "@cosmicdrift/kumiko-framework/errors";
|
|
12
|
-
import { resolveMailLocale } from "@cosmicdrift/kumiko-framework/i18n";
|
|
13
|
-
import
|
|
12
|
+
import { mailT, resolveMailLocale } from "@cosmicdrift/kumiko-framework/i18n";
|
|
13
|
+
import { type AuthMailLocale, tokenMailExpiry } from "./email-templates.js";
|
|
14
|
+
import { authMailTemplateLocale } from "./mail-notifications.js";
|
|
14
15
|
|
|
15
|
-
// Per-flow constants: which notification type to dispatch
|
|
16
|
-
//
|
|
17
|
-
// renderActivationEmail), all unified on RenderTokenContentArgs → AuthMailContent.
|
|
16
|
+
// Per-flow constants: which notification type to dispatch. fallbackAppName
|
|
17
|
+
// replaces the translated default when the flow has no app name of its own.
|
|
18
18
|
export type MagicLinkMailSpec = {
|
|
19
19
|
readonly handlerName: string;
|
|
20
20
|
readonly notificationType: string;
|
|
21
|
-
readonly
|
|
21
|
+
readonly fallbackAppName?: string;
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
// Per-request values: the recipient + the app page that receives the token, plus
|
|
@@ -36,6 +36,8 @@ export type MagicLinkMailParams = {
|
|
|
36
36
|
readonly locale?: AuthMailLocale;
|
|
37
37
|
readonly issuedAt?: string;
|
|
38
38
|
readonly timeZone?: string;
|
|
39
|
+
// Invite mails name the role the recipient is invited as.
|
|
40
|
+
readonly role?: string;
|
|
39
41
|
};
|
|
40
42
|
|
|
41
43
|
function resolveAppUrl(appUrl: string | ((locale: string) => string), locale: string): string {
|
|
@@ -75,18 +77,24 @@ export async function dispatchMagicLinkMail(
|
|
|
75
77
|
// mailT already root-falls-back; appUrl must use the same negotiated tag
|
|
76
78
|
// or language-in-path apps 404 on e.g. /de-AT/activate.
|
|
77
79
|
const urlLocale = resolveMailLocale(locale);
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
const { validFor, validUntil } = tokenMailExpiry(
|
|
81
|
+
{
|
|
82
|
+
expiresAt: params.expiresAt,
|
|
83
|
+
...(params.issuedAt !== undefined && { issuedAt: params.issuedAt }),
|
|
84
|
+
...(params.timeZone !== undefined && { timeZone: params.timeZone }),
|
|
85
|
+
},
|
|
86
|
+
locale,
|
|
87
|
+
);
|
|
86
88
|
await notify(spec.notificationType, {
|
|
87
89
|
route: { email: params.email },
|
|
88
|
-
data:
|
|
89
|
-
|
|
90
|
+
data: {
|
|
91
|
+
appName: params.appName ?? spec.fallbackAppName ?? mailT(locale, "auth.mail.appNameDefault"),
|
|
92
|
+
actionUrl: appendToken(resolveAppUrl(params.appUrl, urlLocale), params.token),
|
|
93
|
+
validFor,
|
|
94
|
+
validUntil,
|
|
95
|
+
...(params.role !== undefined && { role: params.role }),
|
|
96
|
+
},
|
|
97
|
+
locale: authMailTemplateLocale(spec.notificationType, locale),
|
|
90
98
|
priority: "critical",
|
|
91
99
|
});
|
|
92
100
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type NotificationLocalizedTemplate,
|
|
3
|
+
QnTypes,
|
|
4
|
+
qn,
|
|
5
|
+
type TemplateVariables,
|
|
6
|
+
templateVariable,
|
|
7
|
+
} from "@cosmicdrift/kumiko-framework/engine";
|
|
8
|
+
import {
|
|
9
|
+
hasMailTranslationKey,
|
|
10
|
+
listMailLocales,
|
|
11
|
+
mailT,
|
|
12
|
+
resolveMailLocale,
|
|
13
|
+
} from "@cosmicdrift/kumiko-framework/i18n";
|
|
14
|
+
import { escapeHtml, escapeHtmlAttr } from "@cosmicdrift/kumiko-headless";
|
|
15
|
+
import { AUTH_EMAIL_PASSWORD_FEATURE } from "./constants.js";
|
|
16
|
+
|
|
17
|
+
export const AUTH_MAIL_KINDS = {
|
|
18
|
+
passwordReset: { name: "password-reset", translationKey: "reset" },
|
|
19
|
+
accountUnlock: { name: "account-unlock", translationKey: "unlock" },
|
|
20
|
+
emailVerification: { name: "email-verification", translationKey: "verify" },
|
|
21
|
+
signupActivation: { name: "signup-activation", translationKey: "activation" },
|
|
22
|
+
invite: { name: "invite", translationKey: "invite" },
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export type AuthMailKind = keyof typeof AUTH_MAIL_KINDS;
|
|
26
|
+
|
|
27
|
+
type MagicLinkFlowOptions = Partial<
|
|
28
|
+
Record<"passwordReset" | "emailVerification" | "signup" | "invite" | "accountUnlock", unknown>
|
|
29
|
+
>;
|
|
30
|
+
|
|
31
|
+
export function sendsMagicLinkMail(flows: MagicLinkFlowOptions): boolean {
|
|
32
|
+
return Boolean(
|
|
33
|
+
flows.passwordReset ||
|
|
34
|
+
flows.emailVerification ||
|
|
35
|
+
flows.signup ||
|
|
36
|
+
flows.invite ||
|
|
37
|
+
flows.accountUnlock,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function authMailType(kind: AuthMailKind): string {
|
|
42
|
+
return qn(AUTH_EMAIL_PASSWORD_FEATURE, QnTypes.notify, AUTH_MAIL_KINDS[kind].name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const AuthMailNotificationTypes: Readonly<Record<AuthMailKind, string>> = {
|
|
46
|
+
passwordReset: authMailType("passwordReset"),
|
|
47
|
+
accountUnlock: authMailType("accountUnlock"),
|
|
48
|
+
emailVerification: authMailType("emailVerification"),
|
|
49
|
+
signupActivation: authMailType("signupActivation"),
|
|
50
|
+
invite: authMailType("invite"),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function subjectKey(kind: AuthMailKind): string {
|
|
54
|
+
return `auth.mail.${AUTH_MAIL_KINDS[kind].translationKey}.subject`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ALL_AUTH_MAIL_KINDS: readonly AuthMailKind[] = [
|
|
58
|
+
"passwordReset",
|
|
59
|
+
"accountUnlock",
|
|
60
|
+
"emailVerification",
|
|
61
|
+
"signupActivation",
|
|
62
|
+
"invite",
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
const SUBJECT_KEY_BY_TYPE: ReadonlyMap<string, string> = new Map(
|
|
66
|
+
ALL_AUTH_MAIL_KINDS.map((kind) => [AuthMailNotificationTypes[kind], subjectKey(kind)]),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// A registered locale whose bundle lacks the subject key has no template (see
|
|
70
|
+
// authMailLocalizedTemplate); the renderer has no locale fallback, so the
|
|
71
|
+
// sender must pick a locale that has one or the mail would not go out at all.
|
|
72
|
+
export function authMailTemplateLocale(notificationType: string, requestedLocale: string): string {
|
|
73
|
+
const locale = resolveMailLocale(requestedLocale);
|
|
74
|
+
const key = SUBJECT_KEY_BY_TYPE.get(notificationType);
|
|
75
|
+
return key !== undefined && hasMailTranslationKey(locale, key) ? locale : "en";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const AUTH_MAIL_VARIABLES: TemplateVariables = {
|
|
79
|
+
appName: templateVariable.text({ required: true }),
|
|
80
|
+
actionUrl: templateVariable.link({ required: true }),
|
|
81
|
+
validFor: templateVariable.text({ required: true }),
|
|
82
|
+
validUntil: templateVariable.text({ required: true }),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const INVITE_MAIL_VARIABLES: TemplateVariables = {
|
|
86
|
+
...AUTH_MAIL_VARIABLES,
|
|
87
|
+
role: templateVariable.text({ required: true }),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Translation placeholder -> template variable. The markers survive HTML
|
|
91
|
+
// escaping, so only the translated text is escaped, never the placeholders.
|
|
92
|
+
const PLACEHOLDER_VARIABLES: Readonly<Record<string, string>> = {
|
|
93
|
+
app: "appName",
|
|
94
|
+
duration: "validFor",
|
|
95
|
+
when: "validUntil",
|
|
96
|
+
role: "role",
|
|
97
|
+
};
|
|
98
|
+
const MARKER_OPEN = "\uE000";
|
|
99
|
+
const MARKER_CLOSE = "\uE001";
|
|
100
|
+
const MARKER_PATTERN = new RegExp(`${MARKER_OPEN}(\\w+)${MARKER_CLOSE}`, "g");
|
|
101
|
+
|
|
102
|
+
function markers(): Readonly<Record<string, string>> {
|
|
103
|
+
return Object.fromEntries(
|
|
104
|
+
Object.keys(PLACEHOLDER_VARIABLES).map((name) => [
|
|
105
|
+
name,
|
|
106
|
+
`${MARKER_OPEN}${name}${MARKER_CLOSE}`,
|
|
107
|
+
]),
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function translatedHtml(locale: string, key: string): string {
|
|
112
|
+
return escapeHtml(mailT(locale, key, markers())).replace(MARKER_PATTERN, (_, name: string) => {
|
|
113
|
+
const variable = PLACEHOLDER_VARIABLES[name];
|
|
114
|
+
return variable === undefined
|
|
115
|
+
? ""
|
|
116
|
+
: `<span data-variable="${escapeHtmlAttr(variable)}"></span>`;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function mailBody(locale: string, translationKey: string): string {
|
|
121
|
+
const html = (part: string) => translatedHtml(locale, `auth.mail.${translationKey}.${part}`);
|
|
122
|
+
return [
|
|
123
|
+
`<p>${html("greeting")}</p>`,
|
|
124
|
+
`<p>${html("intro")}</p>`,
|
|
125
|
+
`<p><a data-button href="{{actionUrl}}">${html("button")}</a></p>`,
|
|
126
|
+
`<p>${html("expiry")}</p>`,
|
|
127
|
+
`<p>${html("ignore")}</p>`,
|
|
128
|
+
].join("");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Evaluated when the registry is built, after every locale feature has
|
|
132
|
+
// registered its mail translations. English always ships; another locale only
|
|
133
|
+
// gets a template when it has its own subject, so a missing translation never
|
|
134
|
+
// silently becomes an English mail in that locale's slot.
|
|
135
|
+
export function authMailLocalizedTemplate(kind: AuthMailKind): () => NotificationLocalizedTemplate {
|
|
136
|
+
const { translationKey } = AUTH_MAIL_KINDS[kind];
|
|
137
|
+
const kindSubjectKey = subjectKey(kind);
|
|
138
|
+
return () => {
|
|
139
|
+
const locales = [
|
|
140
|
+
"en",
|
|
141
|
+
...listMailLocales().filter(
|
|
142
|
+
(locale) => locale !== "en" && hasMailTranslationKey(locale, kindSubjectKey),
|
|
143
|
+
),
|
|
144
|
+
];
|
|
145
|
+
return Object.fromEntries(
|
|
146
|
+
locales.map((locale) => [
|
|
147
|
+
locale,
|
|
148
|
+
{
|
|
149
|
+
body: mailBody(locale, translationKey),
|
|
150
|
+
meta: {
|
|
151
|
+
subject: mailT(locale, kindSubjectKey, { app: "{{appName}}" }),
|
|
152
|
+
category: "transactional" as const,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
]),
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
}
|
|
@@ -9,15 +9,15 @@ import type { NotifyFn } from "@cosmicdrift/kumiko-framework/engine";
|
|
|
9
9
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
10
10
|
import type { Redis } from "ioredis";
|
|
11
11
|
import { AUTH_SIGNUP_DEFAULT_TTL_MINUTES } from "./constants.js";
|
|
12
|
-
import { renderActivationEmail } from "./email-templates.js";
|
|
13
12
|
import { dispatchMagicLinkMail } from "./magic-link-mail.js";
|
|
13
|
+
import { AuthMailNotificationTypes } from "./mail-notifications.js";
|
|
14
14
|
import {
|
|
15
15
|
invalidateExistingSignupToken,
|
|
16
16
|
normalizeEmail,
|
|
17
17
|
storeSignupToken,
|
|
18
18
|
} from "./signup-token-store.js";
|
|
19
19
|
|
|
20
|
-
export const SIGNUP_ACTIVATION_NOTIFICATION_TYPE =
|
|
20
|
+
export const SIGNUP_ACTIVATION_NOTIFICATION_TYPE = AuthMailNotificationTypes.signupActivation;
|
|
21
21
|
|
|
22
22
|
export type IssueSignupActivationArgs = {
|
|
23
23
|
readonly redis: Redis;
|
|
@@ -65,7 +65,6 @@ export async function issueSignupActivation(
|
|
|
65
65
|
{
|
|
66
66
|
handlerName: "signup-request",
|
|
67
67
|
notificationType: SIGNUP_ACTIVATION_NOTIFICATION_TYPE,
|
|
68
|
-
renderContent: renderActivationEmail,
|
|
69
68
|
},
|
|
70
69
|
{
|
|
71
70
|
email,
|
|
@@ -48,10 +48,10 @@ export type AuthProviderPlugin = {
|
|
|
48
48
|
|
|
49
49
|
export function isValidTokenShape(shape: unknown): shape is TokenShape {
|
|
50
50
|
if (typeof shape !== "object" || shape === null || !("kind" in shape)) return false;
|
|
51
|
-
const kind =
|
|
51
|
+
const kind = shape.kind;
|
|
52
52
|
if (kind === "jwt") return true;
|
|
53
53
|
if (kind === "prefix") {
|
|
54
|
-
const prefix = "prefix" in shape ?
|
|
54
|
+
const prefix = "prefix" in shape ? shape.prefix : undefined;
|
|
55
55
|
return typeof prefix === "string" && prefix.length > 0;
|
|
56
56
|
}
|
|
57
57
|
return false;
|
|
@@ -64,9 +64,9 @@ export function isAuthProviderPlugin(o: unknown): o is AuthProviderPlugin {
|
|
|
64
64
|
typeof o === "object" &&
|
|
65
65
|
o !== null &&
|
|
66
66
|
"build" in o &&
|
|
67
|
-
typeof
|
|
67
|
+
typeof o.build === "function" &&
|
|
68
68
|
"shape" in o &&
|
|
69
|
-
isValidTokenShape(
|
|
69
|
+
isValidTokenShape(o.shape)
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
@@ -105,12 +105,7 @@ export type SessionStoreProvider = {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
export function isSessionStoreProvider(o: unknown): o is SessionStoreProvider {
|
|
108
|
-
return
|
|
109
|
-
typeof o === "object" &&
|
|
110
|
-
o !== null &&
|
|
111
|
-
"build" in o &&
|
|
112
|
-
typeof (o as { build: unknown }).build === "function"
|
|
113
|
-
);
|
|
108
|
+
return typeof o === "object" && o !== null && "build" in o && typeof o.build === "function";
|
|
114
109
|
}
|
|
115
110
|
|
|
116
111
|
// Tenant-resolution extension points (#1373). Both are single-provider and
|
|
@@ -154,19 +149,14 @@ export function isTenantResolverProvider(o: unknown): o is TenantResolverProvide
|
|
|
154
149
|
typeof o === "object" &&
|
|
155
150
|
o !== null &&
|
|
156
151
|
"build" in o &&
|
|
157
|
-
typeof
|
|
152
|
+
typeof o.build === "function" &&
|
|
158
153
|
"trust" in o &&
|
|
159
|
-
isTenantResolverTrust(
|
|
154
|
+
isTenantResolverTrust(o.trust)
|
|
160
155
|
);
|
|
161
156
|
}
|
|
162
157
|
|
|
163
158
|
export function isTenantExistenceProvider(o: unknown): o is TenantExistenceProvider {
|
|
164
|
-
return
|
|
165
|
-
typeof o === "object" &&
|
|
166
|
-
o !== null &&
|
|
167
|
-
"build" in o &&
|
|
168
|
-
typeof (o as { build: unknown }).build === "function"
|
|
169
|
-
);
|
|
159
|
+
return typeof o === "object" && o !== null && "build" in o && typeof o.build === "function";
|
|
170
160
|
}
|
|
171
161
|
|
|
172
162
|
// r.useExtension options-shape for auth-foundation's four extension points.
|
|
@@ -53,11 +53,9 @@ export function bindMfaRevokeAllOtherSessionsFromFeature(
|
|
|
53
53
|
): BindMfaRevokeAllOtherSessions | undefined {
|
|
54
54
|
const exports = feature.exports;
|
|
55
55
|
if (exports && typeof exports === "object" && "bindRevokeAllOtherSessions" in exports) {
|
|
56
|
-
const { bindRevokeAllOtherSessions } = exports
|
|
57
|
-
bindRevokeAllOtherSessions: unknown;
|
|
58
|
-
};
|
|
56
|
+
const { bindRevokeAllOtherSessions } = exports;
|
|
59
57
|
if (typeof bindRevokeAllOtherSessions === "function") {
|
|
60
|
-
// @cast-boundary exports
|
|
58
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
61
59
|
return bindRevokeAllOtherSessions as BindMfaRevokeAllOtherSessions;
|
|
62
60
|
}
|
|
63
61
|
}
|
|
@@ -71,9 +69,9 @@ export function mfaStatusCheckerFromFeature(
|
|
|
71
69
|
): MfaStatusChecker | undefined {
|
|
72
70
|
const exports = feature.exports;
|
|
73
71
|
if (exports && typeof exports === "object" && "checkMfaStatus" in exports) {
|
|
74
|
-
const { checkMfaStatus } = exports
|
|
72
|
+
const { checkMfaStatus } = exports;
|
|
75
73
|
if (typeof checkMfaStatus === "function") {
|
|
76
|
-
// @cast-boundary exports
|
|
74
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
77
75
|
return checkMfaStatus as MfaStatusChecker;
|
|
78
76
|
}
|
|
79
77
|
}
|
|
@@ -87,9 +85,9 @@ export function mfaStatusCheckerFromFeature(
|
|
|
87
85
|
export function mfaVerifierFromFeature(feature: FeatureDefinition): MfaCodeVerifier | undefined {
|
|
88
86
|
const exports = feature.exports;
|
|
89
87
|
if (exports && typeof exports === "object" && "verifyMfaCode" in exports) {
|
|
90
|
-
const { verifyMfaCode } = exports
|
|
88
|
+
const { verifyMfaCode } = exports;
|
|
91
89
|
if (typeof verifyMfaCode === "function") {
|
|
92
|
-
// @cast-boundary exports
|
|
90
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
93
91
|
return verifyMfaCode as MfaCodeVerifier;
|
|
94
92
|
}
|
|
95
93
|
}
|
|
@@ -107,9 +105,9 @@ export function bindRevokeAllPatTokensFromFeature(
|
|
|
107
105
|
): BindRevokeAllPatTokens | undefined {
|
|
108
106
|
const exports = feature.exports;
|
|
109
107
|
if (exports && typeof exports === "object" && "bindRevokeAllPatTokens" in exports) {
|
|
110
|
-
const { bindRevokeAllPatTokens } = exports
|
|
108
|
+
const { bindRevokeAllPatTokens } = exports;
|
|
111
109
|
if (typeof bindRevokeAllPatTokens === "function") {
|
|
112
|
-
// @cast-boundary exports
|
|
110
|
+
// @cast-boundary feature-exports — feature.exports is untyped by design
|
|
113
111
|
return bindRevokeAllPatTokens as BindRevokeAllPatTokens;
|
|
114
112
|
}
|
|
115
113
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
|
|
4
5
|
function hmacSign(input: string, secret: string): string {
|
|
5
6
|
return createHmac("sha256", secret).update(input).digest("base64url");
|
|
@@ -20,11 +21,10 @@ export function createHmacTokenCodec<
|
|
|
20
21
|
function splitEncodedBody(
|
|
21
22
|
value: unknown,
|
|
22
23
|
): { readonly payload: TPayload; readonly expiresAtMs: number } | null {
|
|
23
|
-
if (
|
|
24
|
-
const
|
|
25
|
-
const expiresAtMs = v["expiresAtMs"];
|
|
24
|
+
if (!isPlainObject(value)) return null;
|
|
25
|
+
const expiresAtMs = value["expiresAtMs"];
|
|
26
26
|
if (typeof expiresAtMs !== "number") return null;
|
|
27
|
-
const { expiresAtMs: _drop, ...rest } =
|
|
27
|
+
const { expiresAtMs: _drop, ...rest } = value;
|
|
28
28
|
return isPayload(rest) ? { payload: rest, expiresAtMs } : null;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
import { createHmacTokenCodec, type HmacTokenVerifyResult } from "./hmac-token-codec.js";
|
|
4
5
|
|
|
5
6
|
export type MfaChallengePayload = {
|
|
@@ -8,9 +9,8 @@ export type MfaChallengePayload = {
|
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
function isPayload(value: unknown): value is MfaChallengePayload {
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
return typeof v["userId"] === "string" && typeof v["tenantId"] === "string";
|
|
12
|
+
if (!isPlainObject(value)) return false;
|
|
13
|
+
return typeof value["userId"] === "string" && typeof value["tenantId"] === "string";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// Domain-separated from mfa-preauth-setup (see hmac-token-codec). Single-use is
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
|
|
2
2
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
3
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
3
4
|
import { createHmacTokenCodec, type HmacTokenVerifyResult } from "./hmac-token-codec.js";
|
|
4
5
|
|
|
5
6
|
export type MfaPreauthSetupPayload = {
|
|
@@ -8,9 +9,8 @@ export type MfaPreauthSetupPayload = {
|
|
|
8
9
|
};
|
|
9
10
|
|
|
10
11
|
function isPayload(value: unknown): value is MfaPreauthSetupPayload {
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
return typeof v["userId"] === "string" && typeof v["tenantId"] === "string";
|
|
12
|
+
if (!isPlainObject(value)) return false;
|
|
13
|
+
return typeof value["userId"] === "string" && typeof value["tenantId"] === "string";
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// Domain "mfa-preauth-setup" must never equal the challenge domain: a setup
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// generated secret material, not just re-derive it from a lookup.
|
|
12
12
|
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
13
13
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
14
|
+
import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
|
|
14
15
|
|
|
15
16
|
export type MfaSetupPayload = {
|
|
16
17
|
readonly userId: string;
|
|
@@ -47,15 +48,14 @@ export type VerifyMfaSetupResult =
|
|
|
47
48
|
| { readonly ok: false; readonly reason: "malformed" | "bad_signature" | "expired" };
|
|
48
49
|
|
|
49
50
|
function isEncodedBody(value: unknown): value is EncodedBody {
|
|
50
|
-
if (
|
|
51
|
-
const v = value as Record<string, unknown>;
|
|
51
|
+
if (!isPlainObject(value)) return false;
|
|
52
52
|
return (
|
|
53
|
-
typeof
|
|
54
|
-
typeof
|
|
55
|
-
Array.isArray(
|
|
56
|
-
|
|
57
|
-
typeof
|
|
58
|
-
(
|
|
53
|
+
typeof value["userId"] === "string" &&
|
|
54
|
+
typeof value["totpSecretBase32"] === "string" &&
|
|
55
|
+
Array.isArray(value["recoveryCodeHashes"]) &&
|
|
56
|
+
value["recoveryCodeHashes"].every((h) => typeof h === "string") &&
|
|
57
|
+
typeof value["expiresAtMs"] === "number" &&
|
|
58
|
+
(value["tenantId"] === undefined || typeof value["tenantId"] === "string")
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "0.363.0",
|
|
4
|
+
"type": "breaking",
|
|
5
|
+
"title": "Contract confirmation and termination mails use template variables",
|
|
6
|
+
"detail": "contract-confirmation, contract-termination-receipt and contract-termination-operator-notice are r.notification declarations with variables and localized templates (de, en). The notification types are qualified names now (billing-foundation:notify:<name>). The notify data is the template variable object (subject, details, contractLines, termsParagraphs, operatorEmail), not sections.",
|
|
7
|
+
"migration": "Update delivery preferences and tests that referenced the old type strings, and anything that listens to the notification data. An app that mounts it without includeBundled needs template-resolver, renderer-foundation and @cosmicdrift/kumiko-renderer-mail-html at boot (composeRendererStack() from dev-server mounts all of them), plus a delivery with the channel-email feature; apps without the template tables need a schema migration (`kumiko schema generate`)."
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"version": "0.354.0",
|
|
4
11
|
"type": "breaking",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="temporal-polyfill/global" preserve="true" />
|
|
2
2
|
// @runtime client
|
|
3
3
|
import { Temporal } from "@cosmicdrift/kumiko-types/temporal";
|
|
4
|
+
import { isOneOf } from "@cosmicdrift/kumiko-types/type-guards";
|
|
4
5
|
|
|
5
6
|
// Feature name
|
|
6
7
|
export const BILLING_FOUNDATION_FEATURE = "billing-foundation" as const;
|
|
@@ -11,14 +12,14 @@ export const SUBSCRIPTION_PROVIDER_EXTENSION = "subscriptionProvider" as const;
|
|
|
11
12
|
|
|
12
13
|
/** delivery notification type of the § 312f contract confirmation mail. */
|
|
13
14
|
export const CONTRACT_CONFIRMATION_NOTIFICATION_TYPE =
|
|
14
|
-
"billing-foundation:contract-confirmation" as const;
|
|
15
|
+
"billing-foundation:notify:contract-confirmation" as const;
|
|
15
16
|
|
|
16
17
|
/** delivery notification types of the § 312k termination receipt and the
|
|
17
18
|
* operator notice. */
|
|
18
19
|
export const CONTRACT_TERMINATION_RECEIPT_NOTIFICATION_TYPE =
|
|
19
|
-
"billing-foundation:contract-termination-receipt" as const;
|
|
20
|
+
"billing-foundation:notify:contract-termination-receipt" as const;
|
|
20
21
|
export const CONTRACT_TERMINATION_OPERATOR_NOTIFICATION_TYPE =
|
|
21
|
-
"billing-foundation:contract-termination-operator-notice" as const;
|
|
22
|
+
"billing-foundation:notify:contract-termination-operator-notice" as const;
|
|
22
23
|
|
|
23
24
|
// Qualified write handler names (QN format: scope:type:name).
|
|
24
25
|
export const SubscriptionFoundationHandlers = {
|
|
@@ -230,5 +231,5 @@ export const TERMINATION_SCOPES = ["tenant-host", "platform"] as const;
|
|
|
230
231
|
export type TerminationScope = (typeof TERMINATION_SCOPES)[number];
|
|
231
232
|
|
|
232
233
|
export function isTerminationScope(value: unknown): value is TerminationScope {
|
|
233
|
-
return (TERMINATION_SCOPES
|
|
234
|
+
return isOneOf(TERMINATION_SCOPES, value);
|
|
234
235
|
}
|